First implementation of standard 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/gtypes.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 #endif
251
252 /* Callback for normal D-BUS messages */
253 #ifdef HAVE_LIBOSSO
254 gint dbus_req_handler(const gchar * interface, const gchar * method,
255                       GArray * arguments, gpointer data,
256                       osso_rpc_t * retval)
257 #else
258 static DBusHandlerResult dbus_req_handler (DBusConnection  *connection,
259                    DBusMessage     *message,
260                    void            *data)
261 #endif
262 {
263 #ifndef HAVE_LIBOSSO
264     osso_rpc_t *retval = malloc(sizeof(osso_rpc_t));
265 #endif
266
267     AppData *appdata;
268     appdata = (AppData *) data;
269
270     retval->type = DBUS_TYPE_STRING;
271     retval->value.s = (gchar*) malloc( sizeof(gchar *) );
272     retval->value.s[0] = '\0';
273
274 #ifdef HAVE_LIBOSSO
275     if ( strcmp(method,"wakeup")==0 ) {
276 #else
277     if (dbus_message_is_method_call(message, OSSO_IFACE, "wakeup")) {
278 #endif
279         retval->value.s = (gchar *) realloc(retval->value.s,16*sizeof(gchar *));
280         snprintf(retval->value.s,16,"WifiScand ready");
281 #ifdef HAVE_LIBOSSO
282         return OSSO_OK;
283 #else
284         return submit_reply_message(connection, message, retval->value.s);
285 #endif
286     }
287
288 #ifdef HAVE_LIBOSSO
289     if ( strcmp(method,"start")==0 ) {
290 #else
291     if (dbus_message_is_method_call(message, OSSO_IFACE, "start")) {
292 #endif
293
294         if( (appdata->iface.sock=iw_sockets_open()) < 0) {
295             retval->value.s = (gchar *) realloc(retval->value.s,33*sizeof(gchar *));
296             snprintf(retval->value.s,33,"Failure in socket initialization");
297 #ifdef HAVE_LIBOSSO
298             return OSSO_ERROR;
299 #else
300             return submit_reply_message(connection, message, retval->value.s);
301 #endif
302         }
303
304         struct ifreq frq;
305         strncpy(frq.ifr_name, appdata->iface.ifname, IFNAMSIZ);
306         if(ioctl(appdata->iface.sock, SIOCGIFFLAGS, &frq)) {
307             retval->value.s = (gchar *) realloc(retval->value.s,28*sizeof(gchar *));
308             snprintf(retval->value.s,28,"Cannot get interface status");
309 #ifdef HAVE_LIBOSSO
310             return OSSO_ERROR;
311 #else
312             return submit_reply_message(connection, message, retval->value.s);
313 #endif
314         }
315
316         start_flags = frq.ifr_flags;
317         frq.ifr_flags |= IFF_UP | IFF_RUNNING;
318
319         if(ioctl(appdata->iface.sock, SIOCSIFFLAGS, &frq)) {
320             retval->value.s = (gchar *) realloc(retval->value.s,27*sizeof(gchar *));
321             snprintf(retval->value.s,27,"Cannot set interface state");
322 #ifdef HAVE_LIBOSSO
323             return OSSO_ERROR;
324 #else
325             return submit_reply_message(connection, message, retval->value.s);
326 #endif
327         }
328
329         refresh(&appdata->iface);
330         retval->value.s = (gchar *) realloc(retval->value.s,22*sizeof(gchar *));
331         snprintf(retval->value.s,22,"Interface initialized");
332 #ifdef HAVE_LIBOSSO
333         return OSSO_OK;
334 #else
335         return submit_reply_message(connection, message, retval->value.s);
336 #endif
337     }
338
339 #ifdef HAVE_LIBOSSO
340     if ( strcmp(method,"stop")==0 ) {
341 #else
342     if (dbus_message_is_method_call(message, OSSO_IFACE, "stop")) {
343 #endif
344         struct ifreq frq;
345         strncpy(frq.ifr_name, appdata->iface.ifname, IFNAMSIZ);
346         if(!ioctl(appdata->iface.sock, SIOCGIFFLAGS, &frq)) {
347             frq.ifr_flags = start_flags;
348             if(!ioctl(appdata->iface.sock, SIOCSIFFLAGS, &frq))
349                 refresh(&appdata->iface);
350         }
351         iw_sockets_close(appdata->iface.sock);
352         appdata->iface.sock = 0;
353 #ifdef HAVE_LIBOSSO
354         osso_deinitialize(appdata->osso_context);
355 #else
356         dbus_connection_unref(appdata->osso_context);
357         dbus_shutdown();
358 #endif
359         /* Instead of exiting, signaling finish to main loop could be better
360         retval->value.s = (gchar *) realloc(retval->value.s,34*sizeof(gchar *));
361         snprintf(retval->value.s,34,"Interface moved to original state");
362         */
363         exit(0);
364     }
365
366 #ifdef HAVE_LIBOSSO
367     if ( strcmp(method,"scan")==0 ) {
368 #else
369     if (dbus_message_is_method_call(message, OSSO_IFACE, "scan")) {
370 #endif
371
372         ScanInfo **scan = (ScanInfo **) makeScan(&appdata->iface,retval->value.s);
373         if(scan == NULL) {
374             retval->value.s = (gchar *) realloc(retval->value.s,64*sizeof(gchar *));
375             snprintf(retval->value.s,64,"ERROR");
376 #ifdef HAVE_LIBOSSO
377             return OSSO_ERROR;
378 #else
379             return submit_reply_message(connection, message, retval->value.s);
380 #endif
381         }
382
383         int i;
384         for (i=0; i<25&&*(scan+i)!=NULL; i++) {
385             ScanInfo* sc = *(scan+i);
386             retval->value.s = (gchar *) realloc(retval->value.s,23*(i+1)*sizeof(gchar *));
387             if ( retval->value.s == NULL ) {
388                 retval->value.s = "Error allocating memory";
389 #ifdef HAVE_LIBOSSO
390                 return OSSO_ERROR;
391 #else
392                 return submit_reply_message(connection, message, retval->value.s);
393 #endif
394             }
395             sprintf(retval->value.s+strlen(retval->value.s),"%s:%d ",sc->mac,sc->rssi);
396         }
397
398         retval->value.s[strlen(retval->value.s)-1]  = '\0';
399 #ifdef HAVE_LIBOSSO
400         return OSSO_OK;
401 #else
402         return submit_reply_message(connection, message, retval->value.s);
403 #endif
404     }
405
406     retval->value.s = (gchar *) realloc(retval->value.s,64*sizeof(gchar *));
407     snprintf(retval->value.s,64,"Unknown method");
408 #ifdef HAVE_LIBOSSO
409     return OSSO_ERROR;
410 #else
411     return submit_reply_message(connection, message, retval->value.s);
412 #endif
413 }
414
415 #ifndef HAVE_LIBOSSO
416 static DBusObjectPathVTable
417 dbus_req_handler_vtable = {
418   NULL,
419   dbus_req_handler,
420   NULL,
421 };
422 #endif
423
424 int main( void ) {
425
426         osso_context_t *osso_context;
427 #ifndef HAVE_LIBOSSO
428         DBusError error;
429 #endif
430
431         /* Initialize maemo application */
432 #ifdef HAVE_LIBOSSO
433         osso_context = osso_initialize(OSSO_NAME, WIFISCAND_VERSION_STRING, TRUE, NULL);
434 #else
435         dbus_error_init(&error);
436         osso_context = dbus_bus_get(DBUS_BUS_STARTER, &error);
437 #endif
438
439         /* Check that initialization was ok */
440         if (osso_context == NULL) {
441 #ifndef HAVE_LIBOSSO
442                 fprintf (stderr, "*** Failed to open connection to activating message bus: %s\n", error.message);
443                 dbus_error_free (&error);
444 #endif
445                 exit(OSSO_ERROR);
446                 }
447
448         /* Create AppData */
449         AppData *appdata;
450 #ifdef HAVE_LIBOSSO
451         appdata = g_new0(AppData, 1);
452 #else
453         appdata = malloc(sizeof(AppData));
454 #endif
455         appdata->osso_context = osso_context;
456
457         memset(&(appdata->iface.info), 0, sizeof(wireless_info));
458         appdata->iface.ifname = "wlan0";
459         appdata->iface.sock   = 0;
460
461         /* Add handler for hello D-BUS messages */
462 #ifdef HAVE_LIBOSSO
463         osso_return_t result = osso_rpc_set_cb_f(osso_context, OSSO_SERVICE, OSSO_OBJECT, OSSO_IFACE, dbus_req_handler, appdata);
464 #else
465         int result = dbus_connection_register_fallback(osso_context, OSSO_OBJECT, &dbus_req_handler_vtable, appdata);
466 #endif
467 // NOTE : valid return codes do not match from both functions
468 #ifdef HAVE_LIBOSSO
469         if (result != OSSO_OK) {
470 #else
471         if (!result) {
472 #endif
473 #ifdef HAVE_LIBOSSO
474                 osso_system_note_infoprint(appdata->osso_context, "Failure while setting OSSO callback", NULL);
475 #endif
476                 return OSSO_ERROR;
477         }
478
479 #ifndef HAVE_LIBOSSO
480         result = dbus_bus_request_name (osso_context, OSSO_SERVICE, 0, &error);
481         if (dbus_error_is_set (&error)) {
482                 fprintf (stderr, "Error %s\n", error.message);
483                 dbus_error_free (&error);
484                 exit (1);
485                 }
486 #endif
487
488         /* INITIALIZATION FINISH */
489
490 #ifdef HAVE_LIBOSSO
491         event_loop = g_main_loop_new(NULL, FALSE);
492         g_main_loop_run(event_loop);
493 #else
494         while (dbus_connection_read_write_dispatch (osso_context, -1)) {}
495 #endif
496
497         /* Deinitialize OSSO */
498 #ifdef HAVE_LIBOSSO
499         osso_deinitialize(osso_context);
500 #else
501         dbus_connection_unref(osso_context);
502         dbus_shutdown();
503 #endif
504
505         exit(0);
506 }
507