ccddc9b9198542d5e411cbf49fee19d95e4642b8
[platform/upstream/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 <sys/types.h>
28
29 #define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
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(const char *interface)
39 {
40         GSList *list;
41
42         if (interface == NULL)
43                 return NULL;
44
45         for (list = device_list; list; list = list->next) {
46                 struct connman_device *device = list->data;
47                 const char *device_interface;
48
49                 device_interface = connman_device_get_interface(device);
50                 if (device_interface == NULL)
51                         continue;
52
53                 if (g_str_equal(device_interface, interface) == TRUE)
54                         return device;
55         }
56
57         return NULL;
58 }
59
60 static void add_device(struct udev_device *udev_device)
61 {
62         enum connman_device_type devtype = CONNMAN_DEVICE_TYPE_UNKNOWN;
63         struct connman_device *device;
64         struct udev_list_entry *entry;
65         const char *type = NULL, *interface = NULL;
66
67         DBG("");
68
69         entry = udev_device_get_properties_list_entry(udev_device);
70         while (entry) {
71                 const char *name = udev_list_entry_get_name(entry);
72
73                 if (g_str_has_prefix(name, "CONNMAN_TYPE") == TRUE)
74                         type = udev_list_entry_get_value(entry);
75                 else if (g_str_has_prefix(name, "CONNMAN_INTERFACE") == TRUE)
76                         interface = udev_list_entry_get_value(entry);
77
78                 entry = udev_list_entry_get_next(entry);
79         }
80
81         device = find_device(interface);
82         if (device != NULL)
83                 return;
84
85         if (type == NULL || interface == NULL)
86                 return;
87
88         if (g_str_equal(interface, "ttyUSB0") == FALSE &&
89                                 g_str_equal(interface, "noz0") == FALSE)
90                 return;
91
92         if (g_str_equal(type, "nozomi") == TRUE)
93                 devtype = CONNMAN_DEVICE_TYPE_NOZOMI;
94         else if (g_str_equal(type, "huawei") == TRUE)
95                 devtype = CONNMAN_DEVICE_TYPE_HUAWEI;
96         else if (g_str_equal(type, "novatel") == TRUE)
97                 devtype = CONNMAN_DEVICE_TYPE_NOVATEL;
98         else
99                 return;
100
101         device = connman_device_create(interface, devtype);
102         if (device == NULL)
103                 return;
104
105         connman_device_set_mode(device, CONNMAN_DEVICE_MODE_NETWORK_SINGLE);
106
107         connman_device_set_interface(device, interface);
108
109         if (connman_device_register(device) < 0) {
110                 connman_device_unref(device);
111                 return;
112         }
113
114         device_list = g_slist_append(device_list, device);
115 }
116
117 static void remove_device(struct udev_device *udev_device)
118 {
119         struct connman_device *device;
120         struct udev_list_entry *entry;
121         const char *interface = NULL;
122
123         DBG("");
124
125         entry = udev_device_get_properties_list_entry(udev_device);
126         while (entry) {
127                 const char *name = udev_list_entry_get_name(entry);
128
129                 if (g_str_has_prefix(name, "CONNMAN_INTERFACE") == TRUE)
130                         interface = udev_list_entry_get_value(entry);
131
132                 entry = udev_list_entry_get_next(entry);
133         }
134
135         device = find_device(interface);
136         if (device == NULL)
137                 return;
138
139         device_list = g_slist_remove(device_list, device);
140
141         connman_device_unregister(device);
142         connman_device_unref(device);
143 }
144
145 static void print_properties(struct udev_device *device, const char *prefix)
146 {
147         struct udev_list_entry *entry;
148
149         entry = udev_device_get_properties_list_entry(device);
150         while (entry) {
151                 const char *name = udev_list_entry_get_name(entry);
152                 const char *value = udev_list_entry_get_value(entry);
153
154                 if (g_str_has_prefix(name, "CONNMAN") == TRUE ||
155                                 g_str_has_prefix(name, "RFKILL") == TRUE ||
156                                 g_str_has_prefix(name, "ID_MODEM") == TRUE ||
157                                 g_str_equal(name, "ID_VENDOR") == TRUE ||
158                                 g_str_equal(name, "ID_MODEL") == TRUE ||
159                                 g_str_equal(name, "INTERFACE") == TRUE ||
160                                 g_str_equal(name, "IFINDEX") == TRUE ||
161                                 g_str_equal(name, "DEVNAME") == TRUE ||
162                                 g_str_equal(name, "DEVPATH") == TRUE)
163                         connman_debug("%s%s = %s", prefix, name, value);
164
165                 entry = udev_list_entry_get_next(entry);
166         }
167 }
168
169 static void print_device(struct udev_device *device, const char *action)
170 {
171         const char *subsystem, *devtype = NULL;
172         struct udev_device *parent;
173
174         connman_debug("=== %s ===", action);
175         print_properties(device, "");
176
177         parent = udev_device_get_parent(device);
178         if (parent == NULL)
179                 return;
180
181         subsystem = udev_device_get_subsystem(parent);
182
183         if (subsystem != NULL &&
184                         g_str_equal(subsystem, "usb-serial") == TRUE) {
185                 subsystem = "usb";
186                 devtype = "usb_device";
187         }
188
189         parent = udev_device_get_parent_with_subsystem_devtype(device,
190                                                         subsystem, devtype);
191         print_properties(parent, "    ");
192 }
193
194 static void enumerate_devices(struct udev *context)
195 {
196         struct udev_enumerate *enumerate;
197         struct udev_list_entry *entry;
198
199         enumerate = udev_enumerate_new(context);
200         if (enumerate == NULL)
201                 return;
202
203         udev_enumerate_add_match_property(enumerate, "CONNMAN_TYPE", "?*");
204
205         udev_enumerate_scan_devices(enumerate);
206
207         entry = udev_enumerate_get_list_entry(enumerate);
208         while (entry) {
209                 const char *syspath = udev_list_entry_get_name(entry);
210                 struct udev_device *device;
211
212                 device = udev_device_new_from_syspath(context, syspath);
213
214                 print_device(device, "coldplug");
215
216                 add_device(device);
217
218                 udev_device_unref(device);
219
220                 entry = udev_list_entry_get_next(entry);
221         }
222
223         udev_enumerate_unref(enumerate);
224 }
225
226 static gboolean udev_event(GIOChannel *channel,
227                                 GIOCondition condition, gpointer user_data)
228 {
229         struct udev_monitor *monitor = user_data;
230         struct udev_device *device;
231         const char *action;
232
233         device = udev_monitor_receive_device(monitor);
234         if (device == NULL)
235                 return TRUE;
236
237         action = udev_device_get_action(device);
238         if (action == NULL)
239                 goto done;
240
241         print_device(device, action);
242
243         if (g_str_equal(action, "add") == TRUE)
244                 add_device(device);
245         else if (g_str_equal(action, "remove") == TRUE)
246                 remove_device(device);
247
248 done:
249         udev_device_unref(device);
250
251         return TRUE;
252 }
253
254 static struct udev *udev_ctx;
255 static struct udev_monitor *udev_mon;
256 static guint udev_watch = 0;
257
258 char *__connman_udev_get_devtype(const char *ifname)
259 {
260         struct udev_device *device;
261         const char *devtype;
262         char syspath[128];
263
264         snprintf(syspath, sizeof(syspath) - 1, "/sys/class/net/%s", ifname);
265
266         device = udev_device_new_from_syspath(udev_ctx, syspath);
267         if (device == NULL)
268                 return NULL;
269
270         devtype = udev_device_get_devtype(device);
271         if (devtype == NULL)
272                 goto done;
273
274         connman_info("%s ==> %s", ifname, devtype);
275
276 done:
277         udev_device_unref(device);
278
279         return NULL;
280 }
281
282 int __connman_udev_init(void)
283 {
284         GIOChannel *channel;
285         int fd;
286
287         DBG("");
288
289         udev_ctx = udev_new();
290         if (udev_ctx == NULL) {
291                 connman_error("Failed to create udev context");
292                 return -1;
293         }
294
295         udev_mon = udev_monitor_new_from_netlink(udev_ctx, "udev");
296         if (udev_mon == NULL) {
297                 connman_error("Failed to create udev monitor");
298                 udev_unref(udev_ctx);
299                 udev_ctx = NULL;
300                 return -1;
301         }
302
303         if (udev_monitor_enable_receiving(udev_mon) < 0) {
304                 connman_error("Failed to enable udev monitor");
305                 udev_unref(udev_ctx);
306                 udev_ctx = NULL;
307                 udev_monitor_unref(udev_mon);
308                 return -1;
309         }
310
311         enumerate_devices(udev_ctx);
312
313         fd = udev_monitor_get_fd(udev_mon);
314
315         channel = g_io_channel_unix_new(fd);
316         if (channel == NULL)
317                 return 0;
318
319         udev_watch = g_io_add_watch(channel, G_IO_IN, udev_event, udev_mon);
320
321         g_io_channel_unref(channel);
322
323         return 0;
324 }
325
326 void __connman_udev_cleanup(void)
327 {
328         GSList *list;
329
330         DBG("");
331
332         if (udev_watch > 0)
333                 g_source_remove(udev_watch);
334
335         for (list = device_list; list; list = list->next) {
336                 struct connman_device *device = list->data;
337
338                 connman_device_unregister(device);
339                 connman_device_unref(device);
340         }
341
342         g_slist_free(device_list);
343         device_list = NULL;
344
345         if (udev_ctx == NULL)
346                 return;
347
348         udev_monitor_unref(udev_mon);
349         udev_unref(udev_ctx);
350 }