Add support for setting proxy configuration method
[platform/upstream/connman.git] / src / udev.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  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 GSList *device_list = NULL;
37
38 static struct connman_device *find_device(int index)
39 {
40         GSList *list;
41
42         if (index < 0)
43                 return NULL;
44
45         for (list = device_list; list; list = list->next) {
46                 struct connman_device *device = list->data;
47
48                 if (connman_device_get_index(device) == index)
49                         return device;
50         }
51
52         return NULL;
53 }
54
55 static enum connman_device_type string2devtype(const char *devtype)
56 {
57         if (g_strcmp0(devtype, "wlan") == 0)
58                 return CONNMAN_DEVICE_TYPE_WIFI;
59         else if (g_strcmp0(devtype, "wimax") == 0)
60                 return CONNMAN_DEVICE_TYPE_WIMAX;
61         else if (g_strcmp0(devtype, "wwan") == 0)
62                 return CONNMAN_DEVICE_TYPE_CELLULAR;
63
64         return CONNMAN_DEVICE_TYPE_UNKNOWN;
65 }
66
67 static enum connman_device_type get_device_type(
68                 struct udev_device *udev_device, int index)
69 {
70         const char *devtype;
71
72         devtype = udev_device_get_devtype(udev_device);
73         if (devtype == NULL)
74                 return __connman_inet_get_device_type(index);
75
76         return string2devtype(devtype);
77 }
78
79 static void add_net_device(struct udev_device *udev_device)
80 {
81         struct udev_list_entry *entry;
82         struct connman_device *device;
83         enum connman_device_type devtype;
84         const char *value, *systype;
85         int index = -1;
86
87         DBG("");
88
89         systype = udev_device_get_sysattr_value(udev_device, "type");
90         if (systype == NULL || atoi(systype) != 1)
91                 return;
92
93         entry = udev_device_get_properties_list_entry(udev_device);
94         while (entry) {
95                 const char *name = udev_list_entry_get_name(entry);
96
97                 if (g_str_has_prefix(name, "IFINDEX") == TRUE) {
98                         const char *value = udev_list_entry_get_value(entry);
99                         if (value != NULL)
100                                 index = atoi(value);
101                 }
102
103                 entry = udev_list_entry_get_next(entry);
104         }
105
106         if (index < 0)
107                 return;
108
109         devtype = get_device_type(udev_device, index);
110
111         DBG("devtype %d", devtype);
112
113         switch (devtype) {
114         case CONNMAN_DEVICE_TYPE_UNKNOWN:
115         case CONNMAN_DEVICE_TYPE_VENDOR:
116         case CONNMAN_DEVICE_TYPE_WIMAX:
117         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
118         case CONNMAN_DEVICE_TYPE_CELLULAR:
119         case CONNMAN_DEVICE_TYPE_GPS:
120                 return;
121         case CONNMAN_DEVICE_TYPE_ETHERNET:
122         case CONNMAN_DEVICE_TYPE_WIFI:
123                 break;
124         }
125
126         device = find_device(index);
127         if (device != NULL)
128                 return;
129
130         device = connman_inet_create_device(index);
131         if (device == NULL)
132                 return;
133
134         value = udev_device_get_sysattr_value(udev_device, "phy80211/index");
135         if (value != NULL)
136                 __connman_device_set_phyindex(device, atoi(value));
137
138         if (connman_device_register(device) < 0) {
139                 connman_device_unref(device);
140                 return;
141         }
142
143         device_list = g_slist_append(device_list, device);
144 }
145
146 static void remove_net_device(struct udev_device *udev_device)
147 {
148         struct udev_list_entry *entry;
149         struct connman_device *device;
150         int index = -1;
151
152         DBG("");
153
154         entry = udev_device_get_properties_list_entry(udev_device);
155         while (entry) {
156                 const char *name = udev_list_entry_get_name(entry);
157
158                 if (g_str_has_prefix(name, "IFINDEX") == TRUE) {
159                         const char *value = udev_list_entry_get_value(entry);
160                         if (value != NULL)
161                                 index = atoi(value);
162                 }
163
164                 entry = udev_list_entry_get_next(entry);
165         }
166
167         if (index < 0)
168                 return;
169
170         device = find_device(index);
171         if (device == NULL)
172                 return;
173
174         device_list = g_slist_remove(device_list, device);
175
176         connman_device_unregister(device);
177         connman_device_unref(device);
178 }
179
180 static GSList *rfkill_list = NULL;
181
182 struct rfkill_data {
183         int phyindex;
184         connman_bool_t blocked;
185 };
186
187 connman_bool_t __connman_udev_get_blocked(int phyindex)
188 {
189         GSList *list;
190
191         if (phyindex < 0)
192                 return FALSE;
193
194         for (list = rfkill_list; list; list = rfkill_list->next) {
195                 struct rfkill_data *block = list->data;
196
197                 if (block->phyindex == phyindex)
198                         return block->blocked;
199         }
200
201         return FALSE;
202 }
203
204 static void update_rfkill_state(int phyindex, connman_bool_t blocked)
205 {
206         GSList *list;
207         struct rfkill_data *block;
208
209         DBG("index %d blocked %d", phyindex, blocked);
210
211         for (list = rfkill_list; list; list = rfkill_list->next) {
212                 block = list->data;
213
214                 if (block->phyindex == phyindex) {
215                         block->blocked = blocked;
216                         return;
217                 }
218         }
219
220         block = g_try_new0(struct rfkill_data, 1);
221         if (block == NULL)
222                 return;
223
224         block->phyindex = phyindex;
225         block->blocked = blocked;
226
227         rfkill_list = g_slist_prepend(rfkill_list, block);
228 }
229
230 static void phyindex_rfkill(int phyindex, connman_bool_t blocked)
231 {
232         GSList *list;
233
234         if (phyindex < 0)
235                 return;
236
237         update_rfkill_state(phyindex, blocked);
238
239         for (list = device_list; list; list = list->next) {
240                 struct connman_device *device = list->data;
241
242                 if (__connman_device_get_phyindex(device) == phyindex)
243                         __connman_device_set_blocked(device, blocked);
244         }
245 }
246
247 static void print_properties(struct udev_device *device, const char *prefix)
248 {
249         struct udev_list_entry *entry;
250
251         entry = udev_device_get_properties_list_entry(device);
252         while (entry) {
253                 const char *name = udev_list_entry_get_name(entry);
254                 const char *value = udev_list_entry_get_value(entry);
255
256                 if (g_str_has_prefix(name, "CONNMAN") == TRUE ||
257                                 g_str_has_prefix(name, "RFKILL") == TRUE ||
258                                 g_str_has_prefix(name, "ID_MODEM") == TRUE ||
259                                 g_str_equal(name, "ID_VENDOR") == TRUE ||
260                                 g_str_equal(name, "ID_MODEL") == TRUE ||
261                                 g_str_equal(name, "INTERFACE") == TRUE ||
262                                 g_str_equal(name, "IFINDEX") == TRUE ||
263                                 g_str_equal(name, "DEVNAME") == TRUE ||
264                                 g_str_equal(name, "DEVPATH") == TRUE)
265                         DBG("%s%s = %s", prefix, name, value);
266
267                 entry = udev_list_entry_get_next(entry);
268         }
269 }
270
271 static void print_device(struct udev_device *device, const char *action)
272 {
273         const char *subsystem, *sysname, *driver, *devtype = NULL;
274         struct udev_device *parent;
275
276         DBG("=== %s ===", action);
277         print_properties(device, "");
278
279         parent = udev_device_get_parent(device);
280         if (parent == NULL)
281                 return;
282
283         subsystem = udev_device_get_subsystem(parent);
284
285         if (subsystem != NULL &&
286                         g_str_equal(subsystem, "usb-serial") == TRUE) {
287                 subsystem = "usb";
288                 devtype = "usb_device";
289         }
290
291         parent = udev_device_get_parent_with_subsystem_devtype(device,
292                                                         subsystem, devtype);
293         print_properties(parent, "    ");
294
295         driver = udev_device_get_driver(device);
296         if (driver == NULL) {
297                 driver = udev_device_get_driver(parent);
298                 if (driver == NULL)
299                         return;
300         }
301
302         devtype = udev_device_get_devtype(device);
303
304         DBG("devtype %s", devtype);
305
306         sysname = udev_device_get_sysname(device);
307
308         driver = udev_device_get_driver(parent);
309 }
310
311 static void enumerate_devices(struct udev *context)
312 {
313         struct udev_enumerate *enumerate;
314         struct udev_list_entry *entry;
315
316         enumerate = udev_enumerate_new(context);
317         if (enumerate == NULL)
318                 return;
319
320         udev_enumerate_add_match_subsystem(enumerate, "net");
321
322         udev_enumerate_scan_devices(enumerate);
323
324         entry = udev_enumerate_get_list_entry(enumerate);
325         while (entry) {
326                 const char *syspath = udev_list_entry_get_name(entry);
327                 struct udev_device *device;
328
329                 device = udev_device_new_from_syspath(context, syspath);
330                 if (device != NULL) {
331                         const char *subsystem;
332
333                         print_device(device, "coldplug");
334
335                         subsystem = udev_device_get_subsystem(device);
336
337                         if (g_strcmp0(subsystem, "net") == 0)
338                                 add_net_device(device);
339
340                         udev_device_unref(device);
341                 }
342
343                 entry = udev_list_entry_get_next(entry);
344         }
345
346         udev_enumerate_unref(enumerate);
347 }
348
349 static gboolean udev_event(GIOChannel *channel,
350                                 GIOCondition condition, gpointer user_data)
351 {
352         struct udev_monitor *monitor = user_data;
353         struct udev_device *device;
354         const char *subsystem, *action;
355
356         device = udev_monitor_receive_device(monitor);
357         if (device == NULL)
358                 return TRUE;
359
360         subsystem = udev_device_get_subsystem(device);
361         if (subsystem == NULL)
362                 goto done;
363
364         action = udev_device_get_action(device);
365         if (action == NULL)
366                 goto done;
367
368         print_device(device, action);
369
370         if (g_str_equal(action, "add") == TRUE) {
371                 if (g_str_equal(subsystem, "net") == TRUE)
372                         add_net_device(device);
373         } else if (g_str_equal(action, "remove") == TRUE) {
374                 if (g_str_equal(subsystem, "net") == TRUE)
375                         remove_net_device(device);
376         }
377
378 done:
379         udev_device_unref(device);
380
381         return TRUE;
382 }
383
384 static struct udev *udev_ctx;
385 static struct udev_monitor *udev_mon;
386 static guint udev_watch = 0;
387
388 char *__connman_udev_get_devtype(const char *ifname)
389 {
390         struct udev_device *device;
391         const char *devtype;
392
393         device = udev_device_new_from_subsystem_sysname(udev_ctx,
394                                                         "net", ifname);
395         if (device == NULL)
396                 return NULL;
397
398         devtype = udev_device_get_devtype(device);
399         if (devtype == NULL)
400                 goto done;
401
402 done:
403         udev_device_unref(device);
404
405         return NULL;
406 }
407
408 void __connman_udev_rfkill(const char *sysname, connman_bool_t blocked)
409 {
410         struct udev_device *device, *parent;
411         const char *value;
412
413         device = udev_device_new_from_subsystem_sysname(udev_ctx,
414                                                         "rfkill", sysname);
415         if (device == NULL)
416                 return;
417
418         parent = udev_device_get_parent(device);
419         if (parent == NULL)
420                 return;
421
422         value = udev_device_get_sysattr_value(parent, "index");
423         if (value == NULL)
424                 return;
425
426         phyindex_rfkill(atoi(value), blocked);
427 }
428
429 int __connman_udev_init(void)
430 {
431         DBG("");
432
433         udev_ctx = udev_new();
434         if (udev_ctx == NULL) {
435                 connman_error("Failed to create udev context");
436                 return -1;
437         }
438
439         udev_mon = udev_monitor_new_from_netlink(udev_ctx, "udev");
440         if (udev_mon == NULL) {
441                 connman_error("Failed to create udev monitor");
442                 udev_unref(udev_ctx);
443                 udev_ctx = NULL;
444                 return -1;
445         }
446
447         udev_monitor_filter_add_match_subsystem_devtype(udev_mon,
448                                                         "net", NULL);
449
450         udev_monitor_filter_update(udev_mon);
451
452         return 0;
453 }
454
455 void __connman_udev_start(void)
456 {
457         GIOChannel *channel;
458         int fd;
459
460         DBG("");
461
462         if (udev_monitor_enable_receiving(udev_mon) < 0) {
463                 connman_error("Failed to enable udev monitor");
464                 return;
465         }
466
467         enumerate_devices(udev_ctx);
468
469         fd = udev_monitor_get_fd(udev_mon);
470
471         channel = g_io_channel_unix_new(fd);
472         if (channel == NULL)
473                 return;
474
475         udev_watch = g_io_add_watch(channel, G_IO_IN, udev_event, udev_mon);
476
477         g_io_channel_unref(channel);
478 }
479
480 void __connman_udev_cleanup(void)
481 {
482         GSList *list;
483
484         DBG("");
485
486         if (udev_watch > 0)
487                 g_source_remove(udev_watch);
488
489         for (list = device_list; list; list = list->next) {
490                 struct connman_device *device = list->data;
491
492                 connman_device_unregister(device);
493                 connman_device_unref(device);
494         }
495
496         g_slist_free(device_list);
497         device_list = NULL;
498
499         for (list = rfkill_list; list; list = list->next) {
500                 struct rfkill_data *block = list->data;
501                 g_free(block);
502         }
503
504         g_slist_free(rfkill_list);
505         rfkill_list = NULL;
506
507         if (udev_ctx == NULL)
508                 return;
509
510         udev_monitor_filter_remove(udev_mon);
511
512         udev_monitor_unref(udev_mon);
513         udev_unref(udev_ctx);
514 }