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