Fixed stopping of DBUS service
[wifihood] / wifiscand / wifiscand.c
1
2 #include <iwlib.h>
3
4 #ifdef HAVE_LIBOSSO
5 #include <libosso.h>
6 #else
7 #include <glib.h>
8 #include <dbus/dbus.h>
9
10 #define OSSO_OK 0
11 #define OSSO_ERROR 1
12
13 #define osso_context_t DBusConnection
14 typedef struct osso_rpc_t_values {
15   int i;
16   char *s;
17   } osso_rpc_t_values;
18 typedef struct osso_rpc_t {
19   int type;
20   osso_rpc_t_values value;
21   } osso_rpc_t;
22 #endif
23
24 #define WIFISCAND_VERSION_STRING  "1.1"
25
26 #define OSSO_NAME    "wifiscan"
27 #define OSSO_SERVICE "org.javiplx."OSSO_NAME
28 #define OSSO_OBJECT  "/org/javiplx/"OSSO_NAME
29 #define OSSO_IFACE   "org.javiplx."OSSO_NAME
30
31
32 typedef struct {
33         char          mac[18];
34         int           rssi;
35         int           noise;
36 } ScanInfo;
37
38
39 /* --------------------------------------------------------------------------- WirelessInterface */
40 typedef struct {
41         wireless_info info;
42         char*         ifname;
43         int           sock;
44 } WirelessInterface;
45
46 /* ------------------------------------------------------------------ WirelessInterface_ScanItem */
47
48 static int WirelessInterface_ScanItem(struct iw_event* event, ScanInfo **scaninfo) {
49
50         switch(event->cmd) {
51                                   
52                 case SIOCGIWAP: {
53
54                         *scaninfo = (ScanInfo*) malloc( sizeof(ScanInfo) );
55                         memset(*scaninfo, 0, sizeof(ScanInfo) );
56
57                         iw_ether_ntop( (const struct ether_addr*)(event->u.ap_addr.sa_data), (*scaninfo)->mac);
58                         return 1;
59                 }
60
61                 case IWEVQUAL: {
62                         (*scaninfo)->rssi = event->u.qual.level - 0x100;
63                         return 0;
64                 }
65
66                 default: {
67                          return 0;
68                  }
69         }
70 }
71
72 /* ---------------------------------------------------------------------- */
73
74 void* makeScan(WirelessInterface* coso, gchar *message) {
75         struct iwreq          wrq;
76         unsigned char* buffer     = NULL;
77         int            buflen     = IW_SCAN_MAX_DATA;
78         iwrange        range;
79         int            has_range;
80         struct timeval        tv;
81         int            timeout    = 10000000;
82
83         ScanInfo        **scan = NULL;
84
85         has_range = (iw_get_range_info(coso->sock, coso->ifname, &range) >= 0);
86
87         tv.tv_sec          = 0;
88         tv.tv_usec         = 250000;
89         wrq.u.data.pointer = NULL;
90         wrq.u.data.flags   = 0;
91         wrq.u.data.length  = 0;
92
93         if(iw_set_ext(coso->sock, coso->ifname, SIOCSIWSCAN, &wrq) < 0) {
94                 if(errno != EPERM) {
95                         message = "Interface doesn't support scanning";
96                         return NULL;
97                 }
98                 tv.tv_usec = 0;
99         }
100         timeout -= tv.tv_usec;
101
102         while(1) {
103                 fd_set rfds;
104                 int    last_fd;
105                 int    ret;
106
107                 FD_ZERO(&rfds);
108                 last_fd = -1;
109
110                 ret = select(last_fd + 1, &rfds, NULL, NULL, &tv);
111
112                 if(ret < 0) {
113                         if(errno == EAGAIN || errno == EINTR) continue;
114                         
115                         else {
116                                 message = "Unknown scanning error";
117                                 return NULL;
118                         }
119                 }
120
121                 if(!ret) {
122                         unsigned char* newbuf;
123
124                         realloc:
125                         newbuf = (unsigned char*) realloc(buffer, buflen);
126                         
127                         if(!newbuf) {
128                                 if(buffer) free(buffer);
129                                 message = "Memory allocation failure in scan";
130                                 return NULL;
131                         }
132                         
133                         buffer = newbuf;
134
135                         wrq.u.data.pointer = buffer;
136                         wrq.u.data.flags   = 0;
137                         wrq.u.data.length  = buflen;
138                         
139                         if(iw_get_ext(coso->sock, coso->ifname, SIOCGIWSCAN, &wrq) < 0) {
140                                 if((errno == E2BIG)) {
141                                         if(wrq.u.data.length > buflen) buflen = wrq.u.data.length;
142                                         else buflen *= 2;
143
144                                         goto realloc;
145                                 }
146
147                                 if(errno == EAGAIN) {
148                                         tv.tv_sec   = 0;
149                                         tv.tv_usec  = 100000;
150                                         timeout    -= tv.tv_usec;
151                                         
152                                         if(timeout > 0) continue;
153                                 }
154
155                                 free(buffer);
156                                 
157                                 message = "Unable to read scan data";
158
159                                 return NULL;
160                         }
161                         
162                         else break;
163                 }
164         }
165
166
167         if(wrq.u.data.length)   {
168                 struct iw_event      iwe;
169                 stream_descr stream;
170                 int          ret;
171                 void*    scan_dict = NULL;
172                 
173                 iw_init_event_stream(&stream, (char*)(buffer), wrq.u.data.length);
174                 
175                 scan = (ScanInfo**) malloc( 25 * sizeof(ScanInfo *) );
176                 memset(scan, 0, 25 * sizeof(ScanInfo *) );
177                 *scan = NULL;
178                 int i;
179                 for (i=0; i<25; i++) *(scan+i) = NULL;
180
181                 i = 0;
182                 ScanInfo *sc = NULL;
183                 do {
184                         ret = iw_extract_event_stream(&stream, &iwe, range.we_version_compiled);
185                 
186                         if(ret > 0) {
187                                 int sr = WirelessInterface_ScanItem(&iwe, &sc);
188                                 if(sr && i<25) {
189                                         *(scan+i) = sc;
190                                         i++;
191                                 }
192                         }
193                 }
194                 
195                 while(ret > 0);
196
197         } else {
198                 message = "Unknown error";
199                 free(buffer);
200                 return NULL;
201         }
202
203         free(buffer);
204         return scan;
205 }
206
207 static void refresh(WirelessInterface* coso) {
208         struct iwreq wrq;
209         
210         iw_get_basic_config(coso->sock, coso->ifname, &(coso->info.b));
211         iw_get_range_info(coso->sock, coso->ifname, &(coso->info.range));
212
213         iw_get_ext(coso->sock, coso->ifname, SIOCGIWRATE, &wrq);
214         memcpy(&(coso->info.bitrate), &wrq.u.bitrate, sizeof(iwparam));
215
216         iw_get_ext(coso->sock, coso->ifname, SIOCGIWAP, &wrq);
217         memcpy(&(coso->info.ap_addr), &wrq.u.ap_addr, sizeof (sockaddr));
218
219         iw_get_stats(
220                 coso->sock, coso->ifname, &(coso->info.stats), 
221                 &(coso->info.range), coso->info.has_range
222         );
223 }
224
225 /* Application UI data struct */
226 typedef struct _AppData AppData;
227 struct _AppData {
228     WirelessInterface iface;
229     osso_context_t *osso_context;
230 };
231
232 #ifdef HAVE_LIBOSSO
233 static GMainLoop *event_loop = NULL;
234 #endif
235 static short int start_flags = 0;
236
237 /* Callback for normal D-BUS messages */
238 gint dbus_req_handler(const gchar * interface, const gchar * method,
239                       GArray * arguments, gpointer data,
240                       osso_rpc_t * retval)
241 {
242     AppData *appdata;
243     appdata = (AppData *) data;
244
245     retval->type = DBUS_TYPE_STRING;
246     retval->value.s = (gchar*) malloc( sizeof(gchar *) );
247     retval->value.s[0] = '\0';
248
249     if ( strcmp(method,"wakeup")==0 ) {
250         retval->value.s = (gchar *) realloc(retval->value.s,16*sizeof(gchar *));
251         snprintf(retval->value.s,16,"WifiScand ready");
252         return OSSO_OK;
253     }
254
255     if ( strcmp(method,"start")==0 ) {
256
257         if( (appdata->iface.sock=iw_sockets_open()) < 0) {
258             retval->value.s = (gchar *) realloc(retval->value.s,33*sizeof(gchar *));
259             snprintf(retval->value.s,33,"Failure in socket initialization");
260             return OSSO_ERROR;
261         }
262
263         struct ifreq frq;
264         strncpy(frq.ifr_name, appdata->iface.ifname, IFNAMSIZ);
265         if(ioctl(appdata->iface.sock, SIOCGIFFLAGS, &frq)) {
266             retval->value.s = (gchar *) realloc(retval->value.s,28*sizeof(gchar *));
267             snprintf(retval->value.s,28,"Cannot get interface status");
268             return OSSO_ERROR;
269         }
270
271         start_flags = frq.ifr_flags;
272         frq.ifr_flags |= IFF_UP | IFF_RUNNING;
273
274         if(ioctl(appdata->iface.sock, SIOCSIFFLAGS, &frq)) {
275             retval->value.s = (gchar *) realloc(retval->value.s,27*sizeof(gchar *));
276             snprintf(retval->value.s,27,"Cannot set interface state");
277             return OSSO_ERROR;
278         }
279
280         refresh(&appdata->iface);
281         retval->value.s = (gchar *) realloc(retval->value.s,22*sizeof(gchar *));
282         snprintf(retval->value.s,22,"Interface initialized");
283         return OSSO_OK;
284     }
285
286     if ( strcmp(method,"stop")==0 ) {
287         struct ifreq frq;
288         strncpy(frq.ifr_name, appdata->iface.ifname, IFNAMSIZ);
289         if(!ioctl(appdata->iface.sock, SIOCGIFFLAGS, &frq)) {
290             frq.ifr_flags = start_flags;
291             if(!ioctl(appdata->iface.sock, SIOCSIFFLAGS, &frq))
292                 refresh(&appdata->iface);
293         }
294         iw_sockets_close(appdata->iface.sock);
295         appdata->iface.sock = 0;
296 #ifdef HAVE_LIBOSSO
297         osso_deinitialize(appdata->osso_context);
298         /* Instead of exiting, signaling finish to main loop could be better
299         retval->value.s = (gchar *) realloc(retval->value.s,34*sizeof(gchar *));
300         snprintf(retval->value.s,34,"Interface moved to original state");
301         */
302         exit(0);
303 #else
304         return OSSO_OK;
305 #endif
306     }
307
308     if ( strcmp(method,"scan")==0 ) {
309
310         ScanInfo **scan = (ScanInfo **) makeScan(&appdata->iface,retval->value.s);
311         if(scan == NULL) {
312             retval->value.s = (gchar *) realloc(retval->value.s,64*sizeof(gchar *));
313             snprintf(retval->value.s,64,"ERROR");
314             return OSSO_ERROR;
315         }
316
317         int i;
318         for (i=0; i<25&&*(scan+i)!=NULL; i++) {
319             ScanInfo* sc = *(scan+i);
320             retval->value.s = (gchar *) realloc(retval->value.s,23*(i+1)*sizeof(gchar *));
321             if ( retval->value.s == NULL ) {
322                 retval->value.s = "Error allocating memory";
323                 return OSSO_ERROR;
324             }
325             sprintf(retval->value.s+strlen(retval->value.s),"%s:%d ",sc->mac,sc->rssi);
326         }
327
328         retval->value.s[strlen(retval->value.s)-1]  = '\0';
329         return OSSO_OK;
330     }
331
332     retval->value.s = (gchar *) realloc(retval->value.s,64*sizeof(gchar *));
333     snprintf(retval->value.s,64,"Unknown method");
334     return OSSO_ERROR;
335 }
336
337 #ifndef HAVE_LIBOSSO
338
339 dbus_bool_t stopped_service = FALSE;
340
341 static DBusObjectPathVTable *vtable = NULL;
342
343 int dbus_set_cb_f( DBusConnection *context,
344                 const char *service, const char *object,
345                 const char *interface, void *handler, // FIXME : Set proper type for handler
346                 void *user_data) {
347
348         /* First, we prepare the complex dbus handler structures */
349         DBusObjectPathVTable *vtable = malloc( sizeof(DBusObjectPathVTable) );
350         memset(vtable , '\0', sizeof(DBusObjectPathVTable) );
351         vtable->message_function = (DBusObjectPathMessageFunction) handler; // FIXME : Aqui va el nuevo handler/wrapper
352
353         if (!dbus_connection_register_fallback(context, object, vtable, user_data)) {
354                 return OSSO_ERROR;
355                 }
356
357         DBusError error;
358         dbus_error_init(&error);
359
360         dbus_bus_request_name (context, service, 0, &error);
361         if ( dbus_error_is_set(&error) ) {
362                 dbus_error_free (&error);
363                 return OSSO_ERROR;
364                 }
365
366         dbus_error_free (&error);
367         return OSSO_OK;
368 }
369
370 void dbus_deinitialize( DBusConnection *context ) {
371         free(vtable); vtable = NULL;
372         dbus_connection_unref(context);
373         dbus_shutdown();
374 }
375
376 static DBusHandlerResult handler_wrapper (DBusConnection  *connection,
377                    DBusMessage     *message,
378                    void            *data) {
379
380         gint retcode;
381         osso_rpc_t *retval = malloc(sizeof(osso_rpc_t));
382
383         if ( dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL ) {
384                 retcode = dbus_req_handler(dbus_message_get_interface(message),
385                                 dbus_message_get_member(message),
386                                 NULL, data, retval);
387
388                 if ( strcmp(dbus_message_get_member(message),"stop")==0 )
389                         stopped_service = TRUE;
390
391                 if ( retval->value.s != NULL ) {
392                         DBusMessage *reply = dbus_message_new_method_return (message);
393                         if (reply == NULL)
394                                 exit(0);
395                         if (!dbus_message_append_args (reply, DBUS_TYPE_STRING, &retval->value.s, DBUS_TYPE_INVALID))
396                                 exit(0);
397                         if (!dbus_connection_send (connection, reply, NULL))
398                                 exit(0);
399                         dbus_message_unref (reply);
400                         }
401
402                 }
403
404         return DBUS_HANDLER_RESULT_HANDLED;
405 }
406
407 #endif
408
409 int main( void ) {
410
411         osso_context_t *osso_context;
412 #ifndef HAVE_LIBOSSO
413         DBusError error;
414 #endif
415
416         /* Initialize maemo application */
417 #ifdef HAVE_LIBOSSO
418         osso_context = osso_initialize(OSSO_NAME, WIFISCAND_VERSION_STRING, TRUE, NULL);
419 #else
420         dbus_error_init(&error);
421         osso_context = dbus_bus_get(DBUS_BUS_STARTER, &error);
422 #endif
423
424         /* Check that initialization was ok */
425         if (osso_context == NULL) {
426 #ifndef HAVE_LIBOSSO
427                 fprintf (stderr, "*** Failed to open connection to activating message bus: %s\n", error.message);
428                 dbus_error_free (&error);
429 #endif
430                 exit(OSSO_ERROR);
431                 }
432
433         /* Create AppData */
434         AppData *appdata;
435         appdata = g_new0(AppData, 1);
436         appdata->osso_context = osso_context;
437
438         memset(&(appdata->iface.info), 0, sizeof(wireless_info));
439         appdata->iface.ifname = "wlan0";
440         appdata->iface.sock   = 0;
441
442         /* Add handler for hello D-BUS messages */
443 #ifdef HAVE_LIBOSSO
444         osso_return_t result = osso_rpc_set_cb_f(osso_context, OSSO_SERVICE, OSSO_OBJECT, OSSO_IFACE, dbus_req_handler, appdata);
445 #else
446         int result = dbus_set_cb_f(osso_context, OSSO_SERVICE, OSSO_OBJECT, OSSO_IFACE, handler_wrapper, appdata);
447 #endif
448
449         if (result != OSSO_OK) {
450 #ifdef HAVE_LIBOSSO
451                 osso_system_note_infoprint(appdata->osso_context, "Failure while setting OSSO callback", NULL);
452 #endif
453                 return OSSO_ERROR;
454         }
455
456         /* INITIALIZATION FINISH */
457
458 #ifdef HAVE_LIBOSSO
459         event_loop = g_main_loop_new(NULL, FALSE);
460         g_main_loop_run(event_loop);
461 #else
462         while (dbus_connection_read_write_dispatch(osso_context, -1) && stopped_service==FALSE) {}
463 #endif
464
465         /* Deinitialize OSSO */
466 #ifdef HAVE_LIBOSSO
467         osso_deinitialize(osso_context);
468 #else
469         dbus_deinitialize(osso_context);
470 #endif
471
472         exit(0);
473 }
474