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