Add function to get index value from DHCP structure
[framework/connectivity/connman.git] / src / udev.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2009  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29
30 #include <libudev.h>
31
32 #include <glib.h>
33
34 #include "connman.h"
35
36 static gboolean rfkill_processing = FALSE;
37
38 static GSList *device_list = NULL;
39
40 static struct connman_device *find_device(int index)
41 {
42         GSList *list;
43
44         if (index < 0)
45                 return NULL;
46
47         for (list = device_list; list; list = list->next) {
48                 struct connman_device *device = list->data;
49
50                 if (connman_device_get_index(device) == index)
51                         return device;
52         }
53
54         return NULL;
55 }
56
57 static void add_net_device(struct udev_device *udev_device)
58 {
59         struct udev_list_entry *entry;
60         struct connman_device *device;
61         enum connman_device_type devtype;
62         const char *value, *systype;
63         int index = -1;
64
65         DBG("");
66
67         systype = udev_device_get_sysattr_value(udev_device, "type");
68         if (systype == NULL || atoi(systype) != 1)
69                 return;
70
71         entry = udev_device_get_properties_list_entry(udev_device);
72         while (entry) {
73                 const char *name = udev_list_entry_get_name(entry);
74
75                 if (g_str_has_prefix(name, "IFINDEX") == TRUE) {
76                         const char *value = udev_list_entry_get_value(entry);
77                         if (value != NULL)
78                                 index = atoi(value);
79                 }
80
81                 entry = udev_list_entry_get_next(entry);
82         }
83
84         if (index < 0)
85                 return;
86
87         devtype = __connman_inet_get_device_type(index);
88
89         switch (devtype) {
90         case CONNMAN_DEVICE_TYPE_UNKNOWN:
91         case CONNMAN_DEVICE_TYPE_VENDOR:
92         case CONNMAN_DEVICE_TYPE_WIMAX:
93         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
94         case CONNMAN_DEVICE_TYPE_CELLULAR:
95         case CONNMAN_DEVICE_TYPE_GPS:
96                 return;
97         case CONNMAN_DEVICE_TYPE_ETHERNET:
98         case CONNMAN_DEVICE_TYPE_WIFI:
99         case CONNMAN_DEVICE_TYPE_MBM:
100         case CONNMAN_DEVICE_TYPE_HSO:
101                 break;
102         }
103
104         device = find_device(index);
105         if (device != NULL)
106                 return;
107
108         device = connman_inet_create_device(index);
109         if (device == NULL)
110                 return;
111
112         value = udev_device_get_sysattr_value(udev_device, "phy80211/index");
113         if (value != NULL)
114                 __connman_device_set_phyindex(device, atoi(value));
115
116         if (connman_device_register(device) < 0) {
117                 connman_device_unref(device);
118                 return;
119         }
120
121         device_list = g_slist_append(device_list, device);
122 }
123
124 static void remove_net_device(struct udev_device *udev_device)
125 {
126         struct udev_list_entry *entry;
127         struct connman_device *device;
128         int index = -1;
129
130         DBG("");
131
132         entry = udev_device_get_properties_list_entry(udev_device);
133         while (entry) {
134                 const char *name = udev_list_entry_get_name(entry);
135
136                 if (g_str_has_prefix(name, "IFINDEX") == TRUE) {
137                         const char *value = udev_list_entry_get_value(entry);
138                         if (value != NULL)
139                                 index = atoi(value);
140                 }
141
142                 entry = udev_list_entry_get_next(entry);
143         }
144
145         if (index < 0)
146                 return;
147
148         device = find_device(index);
149         if (device == NULL)
150                 return;
151
152         device_list = g_slist_remove(device_list, device);
153
154         connman_device_unregister(device);
155         connman_device_unref(device);
156 }
157
158 static void phyindex_rfkill(int phyindex, connman_bool_t blocked)
159 {
160         GSList *list;
161
162         if (phyindex < 0)
163                 return;
164
165         for (list = device_list; list; list = list->next) {
166                 struct connman_device *device = list->data;
167
168                 if (__connman_device_get_phyindex(device) == phyindex)
169                         __connman_device_set_blocked(device, blocked);
170         }
171 }
172
173 static void change_rfkill_device(struct udev_device *device)
174 {
175         struct udev_device *parent;
176         struct udev_list_entry *entry;
177         connman_bool_t blocked;
178         const char *value, *type = NULL;
179         int state = -1;
180
181         if (rfkill_processing == FALSE)
182                 return;
183
184         entry = udev_device_get_properties_list_entry(device);
185         while (entry) {
186                 const char *name = udev_list_entry_get_name(entry);
187
188                 if (g_str_has_prefix(name, "RFKILL_STATE") == TRUE) {
189                         value = udev_list_entry_get_value(entry);
190                         if (value != NULL)
191                                 state = atoi(value);
192                 } else if (g_str_has_prefix(name, "RFKILL_TYPE") == TRUE)
193                         type = udev_list_entry_get_value(entry);
194
195                 entry = udev_list_entry_get_next(entry);
196         }
197
198         if (type == NULL || state < 0)
199                 return;
200
201         if (g_str_equal(type, "wlan") == FALSE)
202                 return;
203
204         parent = udev_device_get_parent(device);
205         if (parent == NULL)
206                 return;
207
208         value = udev_device_get_sysattr_value(parent, "index");
209         if (value == NULL)
210                 return;
211
212         blocked = (state != 1) ? TRUE : FALSE;
213
214         phyindex_rfkill(atoi(value), blocked);
215 }
216
217 static void add_rfkill_device(struct udev_device *device)
218 {
219         change_rfkill_device(device);
220 }
221
222 static void print_properties(struct udev_device *device, const char *prefix)
223 {
224         struct udev_list_entry *entry;
225
226         entry = udev_device_get_properties_list_entry(device);
227         while (entry) {
228                 const char *name = udev_list_entry_get_name(entry);
229                 const char *value = udev_list_entry_get_value(entry);
230
231                 if (g_str_has_prefix(name, "CONNMAN") == TRUE ||
232                                 g_str_has_prefix(name, "RFKILL") == TRUE ||
233                                 g_str_has_prefix(name, "ID_MODEM") == TRUE ||
234                                 g_str_equal(name, "ID_VENDOR") == TRUE ||
235                                 g_str_equal(name, "ID_MODEL") == TRUE ||
236                                 g_str_equal(name, "INTERFACE") == TRUE ||
237                                 g_str_equal(name, "IFINDEX") == TRUE ||
238                                 g_str_equal(name, "DEVNAME") == TRUE ||
239                                 g_str_equal(name, "DEVPATH") == TRUE)
240                         DBG("%s%s = %s", prefix, name, value);
241
242                 entry = udev_list_entry_get_next(entry);
243         }
244 }
245
246 static void print_device(struct udev_device *device, const char *action)
247 {
248         const char *subsystem, *sysname, *driver, *devtype = NULL;
249         struct udev_device *parent;
250
251         DBG("=== %s ===", action);
252         print_properties(device, "");
253
254         parent = udev_device_get_parent(device);
255         if (parent == NULL)
256                 return;
257
258         subsystem = udev_device_get_subsystem(parent);
259
260         if (subsystem != NULL &&
261                         g_str_equal(subsystem, "usb-serial") == TRUE) {
262                 subsystem = "usb";
263                 devtype = "usb_device";
264         }
265
266         parent = udev_device_get_parent_with_subsystem_devtype(device,
267                                                         subsystem, devtype);
268         print_properties(parent, "    ");
269
270         driver = udev_device_get_driver(device);
271         if (driver == NULL) {
272                 driver = udev_device_get_driver(parent);
273                 if (driver == NULL)
274                         return;
275         }
276
277         devtype = udev_device_get_devtype(device);
278         sysname = udev_device_get_sysname(device);
279
280         driver = udev_device_get_driver(parent);
281 }
282
283 static void enumerate_devices(struct udev *context)
284 {
285         struct udev_enumerate *enumerate;
286         struct udev_list_entry *entry;
287
288         enumerate = udev_enumerate_new(context);
289         if (enumerate == NULL)
290                 return;
291
292         udev_enumerate_add_match_subsystem(enumerate, "net");
293         udev_enumerate_add_match_subsystem(enumerate, "rfkill");
294
295         udev_enumerate_scan_devices(enumerate);
296
297         entry = udev_enumerate_get_list_entry(enumerate);
298         while (entry) {
299                 const char *syspath = udev_list_entry_get_name(entry);
300                 struct udev_device *device;
301
302                 device = udev_device_new_from_syspath(context, syspath);
303                 if (device != NULL) {
304                         const char *subsystem;
305
306                         print_device(device, "coldplug");
307
308                         subsystem = udev_device_get_subsystem(device);
309
310                         if (g_strcmp0(subsystem, "net") == 0)
311                                 add_net_device(device);
312                         else if (g_strcmp0(subsystem, "rfkill") == 0)
313                                 add_rfkill_device(device);
314
315                         udev_device_unref(device);
316                 }
317
318                 entry = udev_list_entry_get_next(entry);
319         }
320
321         udev_enumerate_unref(enumerate);
322 }
323
324 static gboolean udev_event(GIOChannel *channel,
325                                 GIOCondition condition, gpointer user_data)
326 {
327         struct udev_monitor *monitor = user_data;
328         struct udev_device *device;
329         const char *subsystem, *action;
330
331         device = udev_monitor_receive_device(monitor);
332         if (device == NULL)
333                 return TRUE;
334
335         subsystem = udev_device_get_subsystem(device);
336         if (subsystem == NULL)
337                 goto done;
338
339         action = udev_device_get_action(device);
340         if (action == NULL)
341                 goto done;
342
343         print_device(device, action);
344
345         if (g_str_equal(action, "add") == TRUE) {
346                 if (g_str_equal(subsystem, "net") == TRUE)
347                         add_net_device(device);
348                 else if (g_str_equal(subsystem, "rfkill") == TRUE)
349                         add_rfkill_device(device);
350         } else if (g_str_equal(action, "remove") == TRUE) {
351                 if (g_str_equal(subsystem, "net") == TRUE)
352                         remove_net_device(device);
353         } else if (g_str_equal(action, "change") == TRUE) {
354                 if (g_str_equal(subsystem, "rfkill") == TRUE)
355                         change_rfkill_device(device);
356         }
357
358 done:
359         udev_device_unref(device);
360
361         return TRUE;
362 }
363
364 static struct udev *udev_ctx;
365 static struct udev_monitor *udev_mon;
366 static guint udev_watch = 0;
367
368 void __connman_udev_enable_rfkill_processing(void)
369 {
370         rfkill_processing = TRUE;
371
372         connman_warn("Enabling udev based RFKILL processing");
373
374         enumerate_devices(udev_ctx);
375 }
376
377 char *__connman_udev_get_devtype(const char *ifname)
378 {
379         struct udev_device *device;
380         const char *devtype;
381
382         device = udev_device_new_from_subsystem_sysname(udev_ctx,
383                                                         "net", ifname);
384         if (device == NULL)
385                 return NULL;
386
387         devtype = udev_device_get_devtype(device);
388         if (devtype == NULL)
389                 goto done;
390
391 done:
392         udev_device_unref(device);
393
394         return NULL;
395 }
396
397 char *__connman_udev_get_mbm_devnode(const char *ifname)
398 {
399         struct udev_device *device, *parent, *control;
400         const char *driver, *devpath1, *devpath2;
401         char *devnode = NULL;
402         unsigned int i;
403
404         device = udev_device_new_from_subsystem_sysname(udev_ctx,
405                                                         "net", ifname);
406         if (device == NULL)
407                 return NULL;
408
409         parent = udev_device_get_parent(device);
410         if (parent == NULL)
411                 goto done;
412
413         driver = udev_device_get_driver(parent);
414         if (g_strcmp0(driver, "cdc_ether") != 0)
415                 goto done;
416
417         parent = udev_device_get_parent_with_subsystem_devtype(device,
418                                                         "usb", "usb_device");
419         if (parent == NULL)
420                 goto done;
421
422         devpath1 = udev_device_get_devpath(parent);
423
424         for (i = 0; i < 64; i++) {
425                 char sysname[16];
426
427                 snprintf(sysname, sizeof(sysname) - 1, "ttyACM%d", i);
428
429                 control = udev_device_new_from_subsystem_sysname(udev_ctx,
430                                                         "tty", sysname);
431                 if (control == NULL)
432                         continue;
433
434                 parent = udev_device_get_parent_with_subsystem_devtype(control,
435                                                         "usb", "usb_device");
436                 if (parent == NULL)
437                         continue;
438
439                 devpath2 = udev_device_get_devpath(parent);
440
441                 if (g_strcmp0(devpath1, devpath2) == 0) {
442                         devnode = g_strdup(udev_device_get_devnode(control));
443                         break;
444                 }
445         }
446
447 done:
448         udev_device_unref(device);
449
450         return devnode;
451 }
452
453 void __connman_udev_rfkill(const char *sysname, connman_bool_t blocked)
454 {
455         struct udev_device *device, *parent;
456         const char *value;
457
458         device = udev_device_new_from_subsystem_sysname(udev_ctx,
459                                                         "rfkill", sysname);
460         if (device == NULL)
461                 return;
462
463         parent = udev_device_get_parent(device);
464         if (parent == NULL)
465                 return;
466
467         value = udev_device_get_sysattr_value(parent, "index");
468         if (value == NULL)
469                 return;
470
471         phyindex_rfkill(atoi(value), blocked);
472 }
473
474 int __connman_udev_init(void)
475 {
476         DBG("");
477
478         udev_ctx = udev_new();
479         if (udev_ctx == NULL) {
480                 connman_error("Failed to create udev context");
481                 return -1;
482         }
483
484         udev_mon = udev_monitor_new_from_netlink(udev_ctx, "udev");
485         if (udev_mon == NULL) {
486                 connman_error("Failed to create udev monitor");
487                 udev_unref(udev_ctx);
488                 udev_ctx = NULL;
489                 return -1;
490         }
491
492         udev_monitor_filter_add_match_subsystem_devtype(udev_mon,
493                                                         "net", NULL);
494         udev_monitor_filter_add_match_subsystem_devtype(udev_mon,
495                                                         "rfkill", NULL);
496
497         udev_monitor_filter_update(udev_mon);
498
499         return 0;
500 }
501
502 void __connman_udev_start(void)
503 {
504         GIOChannel *channel;
505         int fd;
506
507         DBG("");
508
509         if (udev_monitor_enable_receiving(udev_mon) < 0) {
510                 connman_error("Failed to enable udev monitor");
511                 return;
512         }
513
514         enumerate_devices(udev_ctx);
515
516         fd = udev_monitor_get_fd(udev_mon);
517
518         channel = g_io_channel_unix_new(fd);
519         if (channel == NULL)
520                 return;
521
522         udev_watch = g_io_add_watch(channel, G_IO_IN, udev_event, udev_mon);
523
524         g_io_channel_unref(channel);
525 }
526
527 void __connman_udev_cleanup(void)
528 {
529         GSList *list;
530
531         DBG("");
532
533         if (udev_watch > 0)
534                 g_source_remove(udev_watch);
535
536         for (list = device_list; list; list = list->next) {
537                 struct connman_device *device = list->data;
538
539                 connman_device_unregister(device);
540                 connman_device_unref(device);
541         }
542
543         g_slist_free(device_list);
544         device_list = NULL;
545
546         if (udev_ctx == NULL)
547                 return;
548
549         udev_monitor_filter_remove(udev_mon);
550
551         udev_monitor_unref(udev_mon);
552         udev_unref(udev_ctx);
553 }