Include a configuration setting to specify the interval among wireless scans
[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 void WirelessInterface_ScanItem(struct wireless_scan* ap, ScanInfo **scaninfo) {
68
69                         *scaninfo = (ScanInfo*) malloc( sizeof(ScanInfo) );
70                         memset(*scaninfo, 0, sizeof(ScanInfo) );
71
72                         iw_sawap_ntop( &ap->ap_addr, (*scaninfo)->mac);
73
74                         (*scaninfo)->rssi = ap->stats.qual.level - 0x100;
75                         (*scaninfo)->noise = ap->stats.qual.level - 0x100;
76
77 }
78
79 /* ---------------------------------------------------------------------- */
80
81 void* makeScan(WirelessInterface* wlan, gchar *message) {
82         wireless_scan_head context;
83         iwrange        range;
84         int delay = 1;
85
86         ScanInfo        **scan = NULL;
87
88         iw_get_range_info(wlan->sock, wlan->ifname, &range);
89
90         /* We don't call iw_scan to allow fine control of delays and timeouts */
91         context.result = NULL;
92         context.retry = 0;
93
94         while(delay>0) {
95                 delay = iw_process_scan(wlan->sock, wlan->ifname, range.we_version_compiled, &context);
96                 if(delay < 0) break;
97                 usleep(delay * 1000);
98                 }
99
100         if ( delay == 0 ) {
101
102                 int i = 1; // To acount for the final NULL
103                 struct wireless_scan *ap = context.result;
104                 while ( ap != NULL ) {
105                                         ap = ap->next;
106                                         i++;
107                         }
108                 scan = (ScanInfo**) malloc( i * sizeof(ScanInfo *) );
109                 memset(scan, 0, i * sizeof(ScanInfo *) );
110
111                 ScanInfo **sc = scan;
112
113                 /* Extract values */
114                 ap = context.result;
115                 while ( ap != NULL ) {
116                                 WirelessInterface_ScanItem(ap, sc++);
117                                         ap = ap->next;
118                         }
119
120         } else {
121                 message = "Unknown error";
122         }
123
124         return scan;
125 }
126
127 static void refresh(WirelessInterface* coso) {
128         struct iwreq wrq;
129         
130         iw_get_basic_config(coso->sock, coso->ifname, &(coso->info.b));
131         iw_get_range_info(coso->sock, coso->ifname, &(coso->info.range));
132
133         iw_get_ext(coso->sock, coso->ifname, SIOCGIWRATE, &wrq);
134         memcpy(&(coso->info.bitrate), &wrq.u.bitrate, sizeof(iwparam));
135
136         iw_get_ext(coso->sock, coso->ifname, SIOCGIWAP, &wrq);
137         memcpy(&(coso->info.ap_addr), &wrq.u.ap_addr, sizeof (sockaddr));
138
139         iw_get_stats(
140                 coso->sock, coso->ifname, &(coso->info.stats), 
141                 &(coso->info.range), coso->info.has_range
142         );
143 }
144
145 /* Application UI data struct */
146 typedef struct _AppData AppData;
147 struct _AppData {
148     WirelessInterface iface;
149     osso_context_t *osso_context;
150 };
151
152 #ifdef HAVE_LIBOSSO
153 static GMainLoop *event_loop = NULL;
154 #endif
155 static short int start_flags = 0;
156
157 /* Callback for normal D-BUS messages */
158 gint dbus_req_handler(const gchar * interface, const gchar * method,
159                       GArray * arguments, gpointer data,
160                       osso_rpc_t * retval)
161 {
162     AppData *appdata;
163     appdata = (AppData *) data;
164
165     retval->type = DBUS_TYPE_STRING;
166     retval->value.s = (gchar*) malloc( sizeof(gchar *) );
167     retval->value.s[0] = '\0';
168
169 #ifndef HAVE_LIBOSSO
170     if ( strcmp(method,"Introspect")==0 ) {
171         retval->value.s = (gchar *) realloc(retval->value.s,630*sizeof(gchar *));
172         snprintf(retval->value.s,strlen(WIFISCAN_INTROSPECTION),WIFISCAN_INTROSPECTION);
173         return OSSO_OK;
174     }
175 #endif
176
177     if ( strcmp(method,"wakeup")==0 ) {
178         retval->value.s = (gchar *) realloc(retval->value.s,16*sizeof(gchar *));
179         snprintf(retval->value.s,16,"WifiScand ready");
180         return OSSO_OK;
181     }
182
183     if ( strcmp(method,"start")==0 ) {
184
185         if( (appdata->iface.sock=iw_sockets_open()) < 0) {
186             retval->value.s = (gchar *) realloc(retval->value.s,33*sizeof(gchar *));
187             snprintf(retval->value.s,33,"Failure in socket initialization");
188             return OSSO_ERROR;
189         }
190
191         /* Get range stuff */
192         iwrange        range;
193         int has_range = (iw_get_range_info(appdata->iface.sock, appdata->iface.ifname, &range) >= 0);
194
195         /* Check if the interface could support scanning. */
196         if((!has_range) || (range.we_version_compiled < 14)) {
197             retval->value.s = (gchar *) realloc(retval->value.s,35*sizeof(gchar *));
198             snprintf(retval->value.s,35,"Interface doesn't support scanning");
199             return OSSO_ERROR;
200         }
201
202         struct ifreq frq;
203         strncpy(frq.ifr_name, appdata->iface.ifname, IFNAMSIZ);
204         if(ioctl(appdata->iface.sock, SIOCGIFFLAGS, &frq)) {
205             retval->value.s = (gchar *) realloc(retval->value.s,28*sizeof(gchar *));
206             snprintf(retval->value.s,28,"Cannot get interface status");
207             return OSSO_ERROR;
208         }
209
210         start_flags = frq.ifr_flags;
211         frq.ifr_flags |= IFF_UP | IFF_RUNNING;
212
213         if(ioctl(appdata->iface.sock, SIOCSIFFLAGS, &frq)) {
214             retval->value.s = (gchar *) realloc(retval->value.s,27*sizeof(gchar *));
215             snprintf(retval->value.s,27,"Cannot set interface state");
216             return OSSO_ERROR;
217         }
218
219         refresh(&appdata->iface);
220         retval->value.s = (gchar *) realloc(retval->value.s,22*sizeof(gchar *));
221         snprintf(retval->value.s,22,"Interface initialized");
222         return OSSO_OK;
223     }
224
225     if ( strcmp(method,"stop")==0 ) {
226         struct ifreq frq;
227         strncpy(frq.ifr_name, appdata->iface.ifname, IFNAMSIZ);
228         if(!ioctl(appdata->iface.sock, SIOCGIFFLAGS, &frq)) {
229             frq.ifr_flags = start_flags;
230             if(!ioctl(appdata->iface.sock, SIOCSIFFLAGS, &frq))
231                 refresh(&appdata->iface);
232         }
233         iw_sockets_close(appdata->iface.sock);
234         appdata->iface.sock = 0;
235 #ifdef HAVE_LIBOSSO
236         osso_deinitialize(appdata->osso_context);
237         /* Instead of exiting, signaling finish to main loop could be better
238         retval->value.s = (gchar *) realloc(retval->value.s,34*sizeof(gchar *));
239         snprintf(retval->value.s,34,"Interface moved to original state");
240         */
241         exit(0);
242 #else
243         return OSSO_OK;
244 #endif
245     }
246
247     if ( strcmp(method,"scan")==0 ) {
248
249         ScanInfo **scan = (ScanInfo **) makeScan(&appdata->iface,retval->value.s);
250         if(scan == NULL) {
251             retval->value.s = (gchar *) realloc(retval->value.s,64*sizeof(gchar *));
252             snprintf(retval->value.s,64,"ERROR");
253             return OSSO_ERROR;
254         }
255
256         int i = 0;
257         while ( *(scan+i) != NULL ) i++;
258         retval->value.s = (gchar *) malloc( (22*i+1) * sizeof(gchar *) );
259             if ( retval->value.s == NULL ) {
260                 retval->value.s = "Error allocating memory";
261                 return OSSO_ERROR;
262                 }
263         memset(retval->value.s, '\0', (22*i+1) * sizeof(gchar *) );
264         i = 0;
265         while ( *scan != NULL ) {
266             sprintf(retval->value.s+(22*i),"%s:%d ",(*scan)->mac,(*scan)->rssi);
267             scan++;
268             i++;
269         }
270
271         retval->value.s[strlen(retval->value.s)-1]  = '\0';
272         return OSSO_OK;
273     }
274
275     retval->value.s = (gchar *) realloc(retval->value.s,64*sizeof(gchar *));
276     snprintf(retval->value.s,64,"Unknown method");
277     return OSSO_ERROR;
278 }
279
280 #ifndef HAVE_LIBOSSO
281
282 dbus_bool_t stopped_service = FALSE;
283
284 static DBusObjectPathVTable *vtable = NULL;
285
286 int dbus_set_cb_f( DBusConnection *context,
287                 const char *service, const char *object,
288                 const char *interface, void *handler, // FIXME : Set proper type for handler
289                 void *user_data) {
290
291         /* First, we prepare the complex dbus handler structures */
292         DBusObjectPathVTable *vtable = malloc( sizeof(DBusObjectPathVTable) );
293         memset(vtable , '\0', sizeof(DBusObjectPathVTable) );
294         vtable->message_function = (DBusObjectPathMessageFunction) handler; // FIXME : Aqui va el nuevo handler/wrapper
295
296         if (!dbus_connection_register_fallback(context, object, vtable, user_data)) {
297                 return OSSO_ERROR;
298                 }
299
300         DBusError error;
301         dbus_error_init(&error);
302
303         dbus_bus_request_name (context, service, 0, &error);
304         if ( dbus_error_is_set(&error) ) {
305                 dbus_error_free (&error);
306                 return OSSO_ERROR;
307                 }
308
309         dbus_error_free (&error);
310         return OSSO_OK;
311 }
312
313 void dbus_deinitialize( DBusConnection *context ) {
314         free(vtable); vtable = NULL;
315         dbus_connection_unref(context);
316         dbus_shutdown();
317 }
318
319 static DBusHandlerResult handler_wrapper (DBusConnection  *connection,
320                    DBusMessage     *message,
321                    void            *data) {
322
323         gint retcode;
324         osso_rpc_t *retval = malloc(sizeof(osso_rpc_t));
325
326         if ( dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL ) {
327                 retcode = dbus_req_handler(dbus_message_get_interface(message),
328                                 dbus_message_get_member(message),
329                                 NULL, data, retval);
330
331                 if ( strcmp(dbus_message_get_member(message),"stop")==0 )
332                         stopped_service = TRUE;
333
334                 if ( retval->value.s != NULL ) {
335                         DBusMessage *reply = dbus_message_new_method_return (message);
336                         if (reply == NULL)
337                                 exit(0);
338                         if (!dbus_message_append_args (reply, DBUS_TYPE_STRING, &retval->value.s, DBUS_TYPE_INVALID))
339                                 exit(0);
340                         if (!dbus_connection_send (connection, reply, NULL))
341                                 exit(0);
342                         dbus_message_unref (reply);
343                         }
344
345                 }
346
347         return DBUS_HANDLER_RESULT_HANDLED;
348 }
349
350 #endif
351
352 int main( void ) {
353
354         osso_context_t *osso_context;
355 #ifndef HAVE_LIBOSSO
356         DBusError error;
357 #endif
358
359         /* Initialize maemo application */
360 #ifdef HAVE_LIBOSSO
361         osso_context = osso_initialize(OSSO_NAME, WIFISCAND_VERSION_STRING, TRUE, NULL);
362 #else
363         dbus_error_init(&error);
364         osso_context = dbus_bus_get(DBUS_BUS_SESSION, &error);
365 #endif
366
367         /* Check that initialization was ok */
368         if (osso_context == NULL) {
369 #ifndef HAVE_LIBOSSO
370                 fprintf (stderr, "*** Failed to open connection to activating message bus: %s\n", error.message);
371                 dbus_error_free (&error);
372 #endif
373                 exit(OSSO_ERROR);
374                 }
375
376         /* Create AppData */
377         AppData *appdata;
378         appdata = g_new0(AppData, 1);
379         appdata->osso_context = osso_context;
380
381         memset(&(appdata->iface.info), 0, sizeof(wireless_info));
382         appdata->iface.ifname = "wlan0";
383         appdata->iface.sock   = 0;
384
385         /* Add handler for hello D-BUS messages */
386 #ifdef HAVE_LIBOSSO
387         osso_return_t result = osso_rpc_set_cb_f(osso_context, OSSO_SERVICE, OSSO_OBJECT, OSSO_IFACE, dbus_req_handler, appdata);
388 #else
389         int result = dbus_set_cb_f(osso_context, OSSO_SERVICE, OSSO_OBJECT, OSSO_IFACE, handler_wrapper, appdata);
390 #endif
391
392         if (result != OSSO_OK) {
393 #ifdef HAVE_LIBOSSO
394                 osso_system_note_infoprint(appdata->osso_context, "Failure while setting OSSO callback", NULL);
395 #endif
396                 return OSSO_ERROR;
397         }
398
399         /* INITIALIZATION FINISH */
400
401 #ifdef HAVE_LIBOSSO
402         event_loop = g_main_loop_new(NULL, FALSE);
403         g_main_loop_run(event_loop);
404 #else
405         while (dbus_connection_read_write_dispatch(osso_context, -1) && stopped_service==FALSE) {}
406 #endif
407
408         /* Deinitialize OSSO */
409 #ifdef HAVE_LIBOSSO
410         osso_deinitialize(osso_context);
411 #else
412         dbus_deinitialize(osso_context);
413 #endif
414
415         exit(0);
416 }
417