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