shared: Replace device_notifier_type with the libsyscommon
[platform/core/system/deviced.git] / src / core / udev.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2012 - 2015 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19
20 #include <stdio.h>
21 #include <errno.h>
22 #include <assert.h>
23 #include <libsyscommon/libgdbus.h>
24 #include <libsyscommon/list.h>
25
26 #include "log.h"
27 #include "shared/device-notifier.h"
28 #include "shared/devices.h"
29 #include "udev.h"
30
31 #define KERNEL          "kernel"
32 #define UDEV            "udev"
33
34 #define UDEV_MONITOR_BUFFER_SIZE   (128*1024)
35
36 struct uevent_info {
37         struct udev_monitor *mon;
38         guint fdh;
39         GList *event_list;
40 };
41
42 /* Uevent */
43 static struct udev *udev;
44
45 static struct uevent_info kevent = {.mon = NULL, .fdh = 0, .event_list = 0}; /* kernel */
46 static struct uevent_info uevent = {.mon = NULL, .fdh = 0, .event_list = 0}; /* udev */
47
48 static gboolean uevent_control_cb(gint fd, GIOCondition cond, void *data)
49 {
50         struct uevent_info *info = data;
51         struct udev_device *dev;
52         struct uevent_handler *l;
53         GList *elem;
54         const char *subsystem;
55         int len;
56
57         assert(info);
58
59         dev = udev_monitor_receive_device(info->mon);
60         if (!dev)
61                 return G_SOURCE_CONTINUE;
62
63         subsystem = udev_device_get_subsystem(dev);
64         if (!subsystem)
65                 goto out;
66
67         len = strlen(subsystem);
68         SYS_G_LIST_FOREACH(info->event_list, elem, l) {
69                 if (!strncmp(l->subsystem, subsystem, len) &&
70                     l->uevent_func)
71                         l->uevent_func(dev);
72         }
73
74 out:
75         udev_device_unref(dev);
76         return G_SOURCE_CONTINUE;
77 }
78
79 static int uevent_control_stop(struct uevent_info *info)
80 {
81         struct udev_device *dev;
82
83         if (!info)
84                 return -EINVAL;
85
86         if (info->fdh) {
87                 g_source_remove(info->fdh);
88                 info->fdh = 0;
89         }
90         if (info->mon) {
91                 dev = udev_monitor_receive_device(info->mon);
92                 if (dev)
93                         udev_device_unref(dev);
94                 udev_monitor_unref(info->mon);
95                 info->mon = NULL;
96         }
97         if (udev)
98                 udev = udev_unref(udev);
99         return 0;
100 }
101
102 static int uevent_control_start(const char *type,
103                 struct uevent_info *info)
104 {
105         struct uevent_handler *l;
106         GList *elem;
107         int fd;
108         int ret;
109
110         if (!info)
111                 return -EINVAL;
112
113         if (info->mon) {
114                 _E("%s Uevent control routine is alreay started.", type);
115                 return -EINVAL;
116         }
117
118         if (!udev) {
119                 udev = udev_new();
120                 if (!udev) {
121                         _E("Failed to create udev.");
122                         return -EINVAL;
123                 }
124         } else
125                 udev = udev_ref(udev);
126
127         info->mon = udev_monitor_new_from_netlink(udev, type);
128         if (info->mon == NULL) {
129                 _E("Failed to create udev_monitor.");
130                 goto stop;
131         }
132
133         _I("Set udev monitor buffer size(%d).", UDEV_MONITOR_BUFFER_SIZE);
134         ret = udev_monitor_set_receive_buffer_size(info->mon,
135                         UDEV_MONITOR_BUFFER_SIZE);
136         if (ret != 0) {
137                 _E("Failed to set receive buffer size.");
138                 goto stop;
139         }
140
141         SYS_G_LIST_FOREACH(info->event_list, elem, l) {
142                 ret = udev_monitor_filter_add_match_subsystem_devtype(
143                                 info->mon,
144                                 l->subsystem, NULL);
145                 if (ret < 0) {
146                         _E("Failed to apply subsystem filter.");
147                         goto stop;
148                 }
149         }
150
151         ret = udev_monitor_filter_update(info->mon);
152         if (ret < 0)
153                 _E("Failed to call 'udev_monitor_filter_update'.");
154
155         fd = udev_monitor_get_fd(info->mon);
156         if (fd == -1) {
157                 _E("Failed to call 'udev_monitor_get_fd'.");
158                 goto stop;
159         }
160
161         info->fdh = g_unix_fd_add(fd, G_IO_IN,
162                         uevent_control_cb, info);
163         if (!info->fdh) {
164                 _E("Failed to call 'error g_unix_fd_add'.");
165                 goto stop;
166         }
167
168         if (udev_monitor_enable_receiving(info->mon) < 0) {
169                 _E("Failed to call 'udev_monitor_enable_receiving'.");
170                 goto stop;
171         }
172
173         return 0;
174 stop:
175         uevent_control_stop(info);
176         return -EINVAL;
177 }
178
179 static int register_uevent_control(struct uevent_info *info,
180                 const struct uevent_handler *uh)
181 {
182         struct uevent_handler *l;
183         GList *elem;
184         int r;
185         bool matched = false;
186         int len;
187
188         if (!info || !uh || !uh->subsystem)
189                 return -EINVAL;
190
191         /* if udev is not initialized, it just will be added list */
192         if (!udev || !info->mon)
193                 goto add_list;
194
195         len = strlen(uh->subsystem);
196         /* check if the same subsystem is already added */
197         SYS_G_LIST_FOREACH(info->event_list, elem, l) {
198                 if (!strncmp(l->subsystem, uh->subsystem, len)) {
199                         matched = true;
200                         break;
201                 }
202         }
203
204         /* the first request to add subsystem */
205         if (!matched) {
206                 r = udev_monitor_filter_add_match_subsystem_devtype(info->mon,
207                                 uh->subsystem, NULL);
208                 if (r < 0) {
209                         _E("Failed to add subsystem(%s): %d", uh->subsystem, r);
210                         return -EPERM;
211                 }
212         }
213
214         r = udev_monitor_filter_update(info->mon);
215         if (r < 0)
216                 _E("Failed to update udev monitor filter: %d", r);
217
218 add_list:
219         SYS_G_LIST_APPEND(info->event_list, uh);
220         return 0;
221 }
222
223 static int unregister_uevent_control(struct uevent_info *info,
224                 const struct uevent_handler *uh)
225 {
226         struct uevent_handler *l;
227         GList *n, *next;
228         int len;
229
230         if (!info || !uh || !uh->subsystem)
231                 return -EINVAL;
232
233         len = strlen(uh->subsystem);
234         SYS_G_LIST_FOREACH_SAFE(info->event_list, n, next, l) {
235                 if (!strncmp(l->subsystem, uh->subsystem, len) &&
236                     l->uevent_func == uh->uevent_func) {
237                         SYS_G_LIST_REMOVE(info->event_list, l);
238                         return 0;
239                 }
240         }
241
242         return -ENOENT;
243 }
244
245 int register_kernel_uevent_control(const struct uevent_handler *uh)
246 {
247         return register_uevent_control(&kevent, uh);
248 }
249
250 int unregister_kernel_uevent_control(const struct uevent_handler *uh)
251 {
252         return unregister_uevent_control(&kevent, uh);
253 }
254
255 int register_udev_uevent_control(const struct uevent_handler *uh)
256 {
257         return register_uevent_control(&uevent, uh);
258 }
259
260 int unregister_udev_uevent_control(const struct uevent_handler *uh)
261 {
262         return unregister_uevent_control(&uevent, uh);
263 }
264
265 static int device_event_handler(void *data)
266 {
267         device_notifier_state_e state = *(device_notifier_state_e *)data;
268
269         if (state == DEVICED_NOTIFIER_STATE_START) {
270                 uevent_control_start(KERNEL, &kevent);
271                 uevent_control_start(UDEV, &uevent);
272         } else if (state == DEVICED_NOTIFIER_STATE_STOP) {
273                 uevent_control_stop(&kevent);
274                 uevent_control_stop(&uevent);
275         }
276
277         return 0;
278 }
279
280 static int device_change_poweroff(void *data)
281 {
282         uevent_control_stop(&kevent);
283         uevent_control_stop(&uevent);
284         return 0;
285 }
286
287 static void udev_init(void *data)
288 {
289         syscommon_notifier_subscribe_notify(DEVICED_NOTIFIER_POWEROFF, device_change_poweroff);
290         syscommon_notifier_subscribe_notify(DEVICED_NOTIFIER_EVENT_HANDLER, device_event_handler);
291
292         if (uevent_control_start(KERNEL, &kevent) != 0)
293                 _E("Failed to init uevent kernel control.");
294
295         if (uevent_control_start(UDEV, &uevent) != 0)
296                 _E("Failed to init uevent udev control.");
297 }
298
299 static void udev_exit(void *data)
300 {
301         syscommon_notifier_unsubscribe_notify(DEVICED_NOTIFIER_EVENT_HANDLER, device_event_handler);
302         syscommon_notifier_unsubscribe_notify(DEVICED_NOTIFIER_POWEROFF, device_change_poweroff);
303 }
304
305 static const struct device_ops udev_device_ops = {
306         .priority = DEVICE_PRIORITY_NORMAL,
307         DECLARE_NAME_LEN("udev"),
308         .init     = udev_init,
309         .exit     = udev_exit,
310 };
311
312 DEVICE_OPS_REGISTER(&udev_device_ops)