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