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