chiark / gitweb /
udevd: kill hanging event processes after 30 seconds
[elogind.git] / src / libudev.c
1 /*
2  * libudev - interface to udev device information
3  *
4  * Copyright (C) 2008-2010 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stddef.h>
15 #include <stdarg.h>
16 #include <unistd.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include <time.h>
21
22 #include "libudev.h"
23 #include "libudev-private.h"
24
25 /**
26  * SECTION:libudev
27  * @short_description: libudev context
28  *
29  * The context contains the default values read from the udev config file,
30  * and is passed to all library operations.
31  */
32
33 /**
34  * udev:
35  *
36  * Opaque object representing the library context.
37  */
38 struct udev {
39         int refcount;
40         void (*log_fn)(struct udev *udev,
41                        int priority, const char *file, int line, const char *fn,
42                        const char *format, va_list args);
43         void *userdata;
44         char *sys_path;
45         char *dev_path;
46         char *rules_path[4];
47         unsigned long long rules_path_ts[4];
48         int rules_path_count;
49         char *run_path;
50         struct udev_list properties_list;
51         int log_priority;
52 };
53
54 void udev_log(struct udev *udev,
55               int priority, const char *file, int line, const char *fn,
56               const char *format, ...)
57 {
58         va_list args;
59
60         va_start(args, format);
61         udev->log_fn(udev, priority, file, line, fn, format, args);
62         va_end(args);
63 }
64
65 static void log_stderr(struct udev *udev,
66                        int priority, const char *file, int line, const char *fn,
67                        const char *format, va_list args)
68 {
69         fprintf(stderr, "libudev: %s: ", fn);
70         vfprintf(stderr, format, args);
71 }
72
73 /**
74  * udev_get_userdata:
75  * @udev: udev library context
76  *
77  * Retrieve stored data pointer from library context. This might be useful
78  * to access from callbacks like a custom logging function.
79  *
80  * Returns: stored userdata
81  **/
82 UDEV_EXPORT void *udev_get_userdata(struct udev *udev)
83 {
84         if (udev == NULL)
85                 return NULL;
86         return udev->userdata;
87 }
88
89 /**
90  * udev_set_userdata:
91  * @udev: udev library context
92  * @userdata: data pointer
93  *
94  * Store custom @userdata in the library context.
95  **/
96 UDEV_EXPORT void udev_set_userdata(struct udev *udev, void *userdata)
97 {
98         if (udev == NULL)
99                 return;
100         udev->userdata = userdata;
101 }
102
103 static char *set_value(char **s, const char *v)
104 {
105         free(*s);
106         *s = strdup(v);
107         util_remove_trailing_chars(*s, '/');
108         return *s;
109 }
110
111 /**
112  * udev_new:
113  *
114  * Create udev library context. This reads the udev configuration
115  * file, and fills in the default values.
116  *
117  * The initial refcount is 1, and needs to be decremented to
118  * release the resources of the udev library context.
119  *
120  * Returns: a new udev library context
121  **/
122 UDEV_EXPORT struct udev *udev_new(void)
123 {
124         struct udev *udev;
125         const char *env;
126         char *config_file = NULL;
127         FILE *f;
128
129         udev = calloc(1, sizeof(struct udev));
130         if (udev == NULL)
131                 return NULL;
132         udev->refcount = 1;
133         udev->log_fn = log_stderr;
134         udev->log_priority = LOG_ERR;
135         udev_list_init(udev, &udev->properties_list, true);
136
137         /* custom config file */
138         env = getenv("UDEV_CONFIG_FILE");
139         if (env != NULL) {
140                 if (set_value(&config_file, env) == NULL)
141                         goto err;
142                 udev_add_property(udev, "UDEV_CONFIG_FILE", config_file);
143         }
144
145         /* default config file */
146         if (config_file == NULL)
147                 config_file = strdup(SYSCONFDIR "/udev/udev.conf");
148         if (config_file == NULL)
149                 goto err;
150
151         f = fopen(config_file, "re");
152         if (f != NULL) {
153                 char line[UTIL_LINE_SIZE];
154                 int line_nr = 0;
155
156                 while (fgets(line, sizeof(line), f)) {
157                         size_t len;
158                         char *key;
159                         char *val;
160
161                         line_nr++;
162
163                         /* find key */
164                         key = line;
165                         while (isspace(key[0]))
166                                 key++;
167
168                         /* comment or empty line */
169                         if (key[0] == '#' || key[0] == '\0')
170                                 continue;
171
172                         /* split key/value */
173                         val = strchr(key, '=');
174                         if (val == NULL) {
175                                 err(udev, "missing <key>=<value> in '%s'[%i], skip line\n", config_file, line_nr);
176                                 continue;
177                         }
178                         val[0] = '\0';
179                         val++;
180
181                         /* find value */
182                         while (isspace(val[0]))
183                                 val++;
184
185                         /* terminate key */
186                         len = strlen(key);
187                         if (len == 0)
188                                 continue;
189                         while (isspace(key[len-1]))
190                                 len--;
191                         key[len] = '\0';
192
193                         /* terminate value */
194                         len = strlen(val);
195                         if (len == 0)
196                                 continue;
197                         while (isspace(val[len-1]))
198                                 len--;
199                         val[len] = '\0';
200
201                         if (len == 0)
202                                 continue;
203
204                         /* unquote */
205                         if (val[0] == '"' || val[0] == '\'') {
206                                 if (val[len-1] != val[0]) {
207                                         err(udev, "inconsistent quoting in '%s'[%i], skip line\n", config_file, line_nr);
208                                         continue;
209                                 }
210                                 val[len-1] = '\0';
211                                 val++;
212                         }
213
214                         if (strcmp(key, "udev_log") == 0) {
215                                 udev_set_log_priority(udev, util_log_priority(val));
216                                 continue;
217                         }
218                         if (strcmp(key, "udev_root") == 0) {
219                                 set_value(&udev->dev_path, val);
220                                 continue;
221                         }
222                         if (strcmp(key, "udev_run") == 0) {
223                                 set_value(&udev->run_path, val);
224                                 continue;
225                         }
226                         if (strcmp(key, "udev_sys") == 0) {
227                                 set_value(&udev->sys_path, val);
228                                 continue;
229                         }
230                         if (strcmp(key, "udev_rules") == 0) {
231                                 set_value(&udev->rules_path[0], val);
232                                 udev->rules_path_count = 1;
233                                 continue;
234                         }
235                 }
236                 fclose(f);
237         }
238
239         /* environment overwrites config */
240         env = getenv("UDEV_LOG");
241         if (env != NULL)
242                 udev_set_log_priority(udev, util_log_priority(env));
243
244         /* set defaults */
245         if (udev->dev_path == NULL)
246                 if (set_value(&udev->dev_path, "/dev") == NULL)
247                         goto err;
248
249         if (udev->sys_path == NULL)
250                 if (set_value(&udev->sys_path, "/sys") == NULL)
251                         goto err;
252
253         if (udev->run_path == NULL)
254                 if (set_value(&udev->run_path, "/run/udev") == NULL)
255                         goto err;
256
257         if (udev->rules_path[0] == NULL) {
258                 /* /usr/lib/udev -- system rules */
259                 udev->rules_path[0] = strdup(PKGLIBEXECDIR "/rules.d");
260                 if (!udev->rules_path[0])
261                         goto err;
262
263                 /* /etc/udev -- local administration rules */
264                 udev->rules_path[1] = strdup(SYSCONFDIR "/udev/rules.d");
265                 if (!udev->rules_path[1])
266                         goto err;
267
268                 /* /run/udev -- runtime rules */
269                 if (asprintf(&udev->rules_path[2], "%s/rules.d", udev->run_path) < 0)
270                         goto err;
271
272                 udev->rules_path_count = 3;
273         }
274
275         dbg(udev, "context %p created\n", udev);
276         dbg(udev, "log_priority=%d\n", udev->log_priority);
277         dbg(udev, "config_file='%s'\n", config_file);
278         dbg(udev, "dev_path='%s'\n", udev->dev_path);
279         dbg(udev, "sys_path='%s'\n", udev->sys_path);
280         dbg(udev, "run_path='%s'\n", udev->run_path);
281         dbg(udev, "rules_path='%s':'%s':'%s'\n", udev->rules_path[0], udev->rules_path[1], udev->rules_path[2]);
282         free(config_file);
283         return udev;
284 err:
285         free(config_file);
286         err(udev, "context creation failed\n");
287         udev_unref(udev);
288         return NULL;
289 }
290
291 /**
292  * udev_ref:
293  * @udev: udev library context
294  *
295  * Take a reference of the udev library context.
296  *
297  * Returns: the passed udev library context
298  **/
299 UDEV_EXPORT struct udev *udev_ref(struct udev *udev)
300 {
301         if (udev == NULL)
302                 return NULL;
303         udev->refcount++;
304         return udev;
305 }
306
307 /**
308  * udev_unref:
309  * @udev: udev library context
310  *
311  * Drop a reference of the udev library context. If the refcount
312  * reaches zero, the resources of the context will be released.
313  *
314  **/
315 UDEV_EXPORT void udev_unref(struct udev *udev)
316 {
317         if (udev == NULL)
318                 return;
319         udev->refcount--;
320         if (udev->refcount > 0)
321                 return;
322         udev_list_cleanup(&udev->properties_list);
323         free(udev->dev_path);
324         free(udev->sys_path);
325         free(udev->rules_path[0]);
326         free(udev->rules_path[1]);
327         free(udev->rules_path[2]);
328         free(udev->run_path);
329         dbg(udev, "context %p released\n", udev);
330         free(udev);
331 }
332
333 /**
334  * udev_set_log_fn:
335  * @udev: udev library context
336  * @log_fn: function to be called for logging messages
337  *
338  * The built-in logging writes to stderr. It can be
339  * overridden by a custom function, to plug log messages
340  * into the users' logging functionality.
341  *
342  **/
343 UDEV_EXPORT void udev_set_log_fn(struct udev *udev,
344                      void (*log_fn)(struct udev *udev,
345                                     int priority, const char *file, int line, const char *fn,
346                                     const char *format, va_list args))
347 {
348         udev->log_fn = log_fn;
349         info(udev, "custom logging function %p registered\n", log_fn);
350 }
351
352 /**
353  * udev_get_log_priority:
354  * @udev: udev library context
355  *
356  * The initial logging priority is read from the udev config file
357  * at startup.
358  *
359  * Returns: the current logging priority
360  **/
361 UDEV_EXPORT int udev_get_log_priority(struct udev *udev)
362 {
363         return udev->log_priority;
364 }
365
366 /**
367  * udev_set_log_priority:
368  * @udev: udev library context
369  * @priority: the new logging priority
370  *
371  * Set the current logging priority. The value controls which messages
372  * are logged.
373  **/
374 UDEV_EXPORT void udev_set_log_priority(struct udev *udev, int priority)
375 {
376         char num[32];
377
378         udev->log_priority = priority;
379         snprintf(num, sizeof(num), "%u", udev->log_priority);
380         udev_add_property(udev, "UDEV_LOG", num);
381 }
382
383 int udev_get_rules_path(struct udev *udev, char **path[], unsigned long long *stamp_usec[])
384 {
385         *path = udev->rules_path;
386         if (stamp_usec)
387                 *stamp_usec = udev->rules_path_ts;
388         return udev->rules_path_count;
389 }
390
391 /**
392  * udev_get_sys_path:
393  * @udev: udev library context
394  *
395  * Retrieve the sysfs mount point. The default is "/sys". For
396  * testing purposes, it can be overridden with udev_sys=
397  * in the udev configuration file.
398  *
399  * Returns: the sys mount point
400  **/
401 UDEV_EXPORT const char *udev_get_sys_path(struct udev *udev)
402 {
403         if (udev == NULL)
404                 return NULL;
405         return udev->sys_path;
406 }
407
408 /**
409  * udev_get_dev_path:
410  * @udev: udev library context
411  *
412  * Retrieve the device directory path. The default value is "/dev",
413  * the actual value may be overridden in the udev configuration
414  * file.
415  *
416  * Returns: the device directory path
417  **/
418 UDEV_EXPORT const char *udev_get_dev_path(struct udev *udev)
419 {
420         if (udev == NULL)
421                 return NULL;
422         return udev->dev_path;
423 }
424
425 /**
426  * udev_get_run_path:
427  * @udev: udev library context
428  *
429  * Retrieve the udev runtime directory path. The default is "/run/udev".
430  *
431  * Returns: the runtime directory path
432  **/
433 UDEV_EXPORT const char *udev_get_run_path(struct udev *udev)
434 {
435         if (udev == NULL)
436                 return NULL;
437         return udev->run_path;
438 }
439
440 struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value)
441 {
442         if (value == NULL) {
443                 struct udev_list_entry *list_entry;
444
445                 list_entry = udev_get_properties_list_entry(udev);
446                 list_entry = udev_list_entry_get_by_name(list_entry, key);
447                 if (list_entry != NULL)
448                         udev_list_entry_delete(list_entry);
449                 return NULL;
450         }
451         return udev_list_entry_add(&udev->properties_list, key, value);
452 }
453
454 struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev)
455 {
456         return udev_list_get_entry(&udev->properties_list);
457 }