chiark / gitweb /
93f2d8f1b5d6d9f58553629e4e9d1ae898430224
[elogind.git] / libudev / 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_rules") == 0) {
227                                 set_value(&udev->rules_path[0], val);
228                                 udev->rules_path_count = 1;
229                                 continue;
230                         }
231                 }
232                 fclose(f);
233         }
234
235         /* environment overwrites config */
236         env = getenv("UDEV_LOG");
237         if (env != NULL)
238                 udev_set_log_priority(udev, util_log_priority(env));
239
240         env = getenv("UDEV_ROOT");
241         if (env != NULL) {
242                 set_value(&udev->dev_path, env);
243                 udev_add_property(udev, "UDEV_ROOT", udev->dev_path);
244         }
245
246         env = getenv("SYSFS_PATH");
247         if (env != NULL) {
248                 set_value(&udev->sys_path, env);
249                 udev_add_property(udev, "SYSFS_PATH", udev->sys_path);
250         }
251
252         /* set defaults */
253         if (udev->dev_path == NULL)
254                 if (set_value(&udev->dev_path, "/dev") == NULL)
255                         goto err;
256
257         if (udev->sys_path == NULL)
258                 if (set_value(&udev->sys_path, "/sys") == NULL)
259                         goto err;
260
261         if (udev->run_path == NULL)
262                 if (set_value(&udev->run_path, "/run/udev") == NULL)
263                         goto err;
264
265         if (udev->rules_path[0] == NULL) {
266                 /* /usr/lib/udev -- system rules */
267                 udev->rules_path[0] = strdup(LIBEXECDIR "/rules.d");
268                 if (!udev->rules_path[0])
269                         goto err;
270
271                 /* /etc/udev -- local administration rules */
272                 udev->rules_path[1] = strdup(SYSCONFDIR "/udev/rules.d");
273                 if (!udev->rules_path[1])
274                         goto err;
275
276                 /* /run/udev -- runtime rules */
277                 if (asprintf(&udev->rules_path[2], "%s/rules.d", udev->run_path) < 0)
278                         goto err;
279
280                 udev->rules_path_count = 3;
281         }
282
283         dbg(udev, "context %p created\n", udev);
284         dbg(udev, "log_priority=%d\n", udev->log_priority);
285         dbg(udev, "config_file='%s'\n", config_file);
286         dbg(udev, "dev_path='%s'\n", udev->dev_path);
287         dbg(udev, "sys_path='%s'\n", udev->sys_path);
288         dbg(udev, "run_path='%s'\n", udev->run_path);
289         dbg(udev, "rules_path='%s':'%s':'%s'\n", udev->rules_path[0], udev->rules_path[1], udev->rules_path[2]);
290         free(config_file);
291         return udev;
292 err:
293         free(config_file);
294         err(udev, "context creation failed\n");
295         udev_unref(udev);
296         return NULL;
297 }
298
299 /**
300  * udev_ref:
301  * @udev: udev library context
302  *
303  * Take a reference of the udev library context.
304  *
305  * Returns: the passed udev library context
306  **/
307 UDEV_EXPORT struct udev *udev_ref(struct udev *udev)
308 {
309         if (udev == NULL)
310                 return NULL;
311         udev->refcount++;
312         return udev;
313 }
314
315 /**
316  * udev_unref:
317  * @udev: udev library context
318  *
319  * Drop a reference of the udev library context. If the refcount
320  * reaches zero, the resources of the context will be released.
321  *
322  **/
323 UDEV_EXPORT void udev_unref(struct udev *udev)
324 {
325         if (udev == NULL)
326                 return;
327         udev->refcount--;
328         if (udev->refcount > 0)
329                 return;
330         udev_list_cleanup(&udev->properties_list);
331         free(udev->dev_path);
332         free(udev->sys_path);
333         free(udev->rules_path[0]);
334         free(udev->rules_path[1]);
335         free(udev->rules_path[2]);
336         free(udev->run_path);
337         dbg(udev, "context %p released\n", udev);
338         free(udev);
339 }
340
341 /**
342  * udev_set_log_fn:
343  * @udev: udev library context
344  * @log_fn: function to be called for logging messages
345  *
346  * The built-in logging writes to stderr. It can be
347  * overridden by a custom function, to plug log messages
348  * into the users' logging functionality.
349  *
350  **/
351 UDEV_EXPORT void udev_set_log_fn(struct udev *udev,
352                      void (*log_fn)(struct udev *udev,
353                                     int priority, const char *file, int line, const char *fn,
354                                     const char *format, va_list args))
355 {
356         udev->log_fn = log_fn;
357         info(udev, "custom logging function %p registered\n", log_fn);
358 }
359
360 /**
361  * udev_get_log_priority:
362  * @udev: udev library context
363  *
364  * The initial logging priority is read from the udev config file
365  * at startup.
366  *
367  * Returns: the current logging priority
368  **/
369 UDEV_EXPORT int udev_get_log_priority(struct udev *udev)
370 {
371         return udev->log_priority;
372 }
373
374 /**
375  * udev_set_log_priority:
376  * @udev: udev library context
377  * @priority: the new logging priority
378  *
379  * Set the current logging priority. The value controls which messages
380  * are logged.
381  **/
382 UDEV_EXPORT void udev_set_log_priority(struct udev *udev, int priority)
383 {
384         char num[32];
385
386         udev->log_priority = priority;
387         snprintf(num, sizeof(num), "%u", udev->log_priority);
388         udev_add_property(udev, "UDEV_LOG", num);
389 }
390
391 int udev_get_rules_path(struct udev *udev, char **path[], unsigned long long *stamp_usec[])
392 {
393         *path = udev->rules_path;
394         if (stamp_usec)
395                 *stamp_usec = udev->rules_path_ts;
396         return udev->rules_path_count;
397 }
398
399 /**
400  * udev_get_sys_path:
401  * @udev: udev library context
402  *
403  * Retrieve the sysfs mount point. The default is "/sys". For
404  * testing purposes, it can be overridden with the environment
405  * variable SYSFS_PATH.
406  *
407  * Returns: the sys mount point
408  **/
409 UDEV_EXPORT const char *udev_get_sys_path(struct udev *udev)
410 {
411         if (udev == NULL)
412                 return NULL;
413         return udev->sys_path;
414 }
415
416 /**
417  * udev_get_dev_path:
418  * @udev: udev library context
419  *
420  * Retrieve the device directory path. The default value is "/dev",
421  * the actual value may be overridden in the udev configuration
422  * file.
423  *
424  * Returns: the device directory path
425  **/
426 UDEV_EXPORT const char *udev_get_dev_path(struct udev *udev)
427 {
428         if (udev == NULL)
429                 return NULL;
430         return udev->dev_path;
431 }
432
433 /**
434  * udev_get_run_path:
435  * @udev: udev library context
436  *
437  * Retrieve the udev runtime directory path. The default is "/run/udev".
438  *
439  * Returns: the runtime directory path
440  **/
441 UDEV_EXPORT const char *udev_get_run_path(struct udev *udev)
442 {
443         if (udev == NULL)
444                 return NULL;
445         return udev->run_path;
446 }
447
448 struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value)
449 {
450         if (value == NULL) {
451                 struct udev_list_entry *list_entry;
452
453                 list_entry = udev_get_properties_list_entry(udev);
454                 list_entry = udev_list_entry_get_by_name(list_entry, key);
455                 if (list_entry != NULL)
456                         udev_list_entry_delete(list_entry);
457                 return NULL;
458         }
459         return udev_list_entry_add(&udev->properties_list, key, value);
460 }
461
462 struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev)
463 {
464         return udev_list_get_entry(&udev->properties_list);
465 }