chiark / gitweb /
libudev: monitor - export filter_update()
[elogind.git] / udev / lib / libudev.c
1 /*
2  * libudev - interface to udev device information
3  *
4  * Copyright (C) 2008 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 struct udev {
25         int refcount;
26         void (*log_fn)(struct udev *udev,
27                        int priority, const char *file, int line, const char *fn,
28                        const char *format, va_list args);
29         void *userdata;
30         char *sys_path;
31         char *dev_path;
32         char *rules_path;
33         struct udev_list_node properties_list;
34         int log_priority;
35         int run;
36 };
37
38 void udev_log(struct udev *udev,
39               int priority, const char *file, int line, const char *fn,
40               const char *format, ...)
41 {
42         va_list args;
43
44         if (priority > udev->log_priority)
45                 return;
46
47         va_start(args, format);
48         udev->log_fn(udev, priority, file, line, fn, format, args);
49         va_end(args);
50 }
51
52 static void log_stderr(struct udev *udev,
53                        int priority, const char *file, int line, const char *fn,
54                        const char *format, va_list args)
55 {
56         fprintf(stderr, "libudev: %s: ", fn);
57         vfprintf(stderr, format, args);
58 }
59
60 void *udev_get_userdata(struct udev *udev)
61 {
62         if (udev == NULL)
63                 return NULL;
64         return udev->userdata;
65 }
66
67 void udev_set_userdata(struct udev *udev, void *userdata)
68 {
69         if (udev == NULL)
70                 return;
71         udev->userdata = userdata;
72 }
73
74 /**
75  * udev_new:
76  *
77  * Create udev library context.
78  *
79  * The initial refcount is 1, and needs to be decremented to
80  * release the resources of the udev library context.
81  *
82  * Returns: a new udev library context
83  **/
84 struct udev *udev_new(void)
85 {
86         struct udev *udev;
87         const char *env;
88         char *config_file;
89         FILE *f;
90
91         udev = calloc(1, sizeof(struct udev));
92         if (udev == NULL)
93                 return NULL;
94         udev->refcount = 1;
95         udev->log_fn = log_stderr;
96         udev->log_priority = LOG_ERR;
97         udev_list_init(&udev->properties_list);
98         udev->run = 1;
99         udev->dev_path = strdup(UDEV_PREFIX "/dev");
100         udev->sys_path = strdup("/sys");
101         config_file = strdup(SYSCONFDIR "/udev/udev.conf");
102         if (udev->dev_path == NULL ||
103             udev->sys_path == NULL ||
104             config_file == NULL)
105                 goto err;
106
107         /* settings by environment and config file */
108         env = getenv("SYSFS_PATH");
109         if (env != NULL) {
110                 free(udev->sys_path);
111                 udev->sys_path = strdup(env);
112                 util_remove_trailing_chars(udev->sys_path, '/');
113                 udev_add_property(udev, "SYSFS_PATH", udev->sys_path);
114         }
115
116         env = getenv("UDEV_RUN");
117         if (env != NULL && strcmp(env, "0") == 0)
118                 udev->run = 0;
119
120         env = getenv("UDEV_CONFIG_FILE");
121         if (env != NULL) {
122                 free(config_file);
123                 config_file = strdup(env);
124                 util_remove_trailing_chars(config_file, '/');
125         }
126         if (config_file == NULL)
127                 goto err;
128         f = fopen(config_file, "r");
129         if (f != NULL) {
130                 char line[UTIL_LINE_SIZE];
131                 int line_nr = 0;
132
133                 while (fgets(line, sizeof(line), f)) {
134                         size_t len;
135                         char *key;
136                         char *val;
137
138                         line_nr++;
139
140                         /* find key */
141                         key = line;
142                         while (isspace(key[0]))
143                                 key++;
144
145                         /* comment or empty line */
146                         if (key[0] == '#' || key[0] == '\0')
147                                 continue;
148
149                         /* split key/value */
150                         val = strchr(key, '=');
151                         if (val == NULL) {
152                                 err(udev, "missing <key>=<value> in '%s'[%i], skip line\n", config_file, line_nr);
153                                 continue;
154                         }
155                         val[0] = '\0';
156                         val++;
157
158                         /* find value */
159                         while (isspace(val[0]))
160                                 val++;
161
162                         /* terminate key */
163                         len = strlen(key);
164                         if (len == 0)
165                                 continue;
166                         while (isspace(key[len-1]))
167                                 len--;
168                         key[len] = '\0';
169
170                         /* terminate value */
171                         len = strlen(val);
172                         if (len == 0)
173                                 continue;
174                         while (isspace(val[len-1]))
175                                 len--;
176                         val[len] = '\0';
177
178                         if (len == 0)
179                                 continue;
180
181                         /* unquote */
182                         if (val[0] == '"' || val[0] == '\'') {
183                                 if (val[len-1] != val[0]) {
184                                         err(udev, "inconsistent quoting in '%s'[%i], skip line\n", config_file, line_nr);
185                                         continue;
186                                 }
187                                 val[len-1] = '\0';
188                                 val++;
189                         }
190
191                         if (strcasecmp(key, "udev_log") == 0) {
192                                 udev_set_log_priority(udev, util_log_priority(val));
193                                 continue;
194                         }
195                         if (strcasecmp(key, "udev_root") == 0) {
196                                 free(udev->dev_path);
197                                 udev->dev_path = strdup(val);
198                                 util_remove_trailing_chars(udev->dev_path, '/');
199                                 continue;
200                         }
201                         if (strcasecmp(key, "udev_rules") == 0) {
202                                 free(udev->rules_path);
203                                 udev->rules_path = strdup(val);
204                                 util_remove_trailing_chars(udev->rules_path, '/');
205                                 continue;
206                         }
207                 }
208                 fclose(f);
209         }
210
211         env = getenv("UDEV_ROOT");
212         if (env != NULL) {
213                 free(udev->dev_path);
214                 udev->dev_path = strdup(env);
215                 util_remove_trailing_chars(udev->dev_path, '/');
216                 udev_add_property(udev, "UDEV_ROOT", udev->dev_path);
217         }
218
219         env = getenv("UDEV_LOG");
220         if (env != NULL)
221                 udev_set_log_priority(udev, util_log_priority(env));
222
223         if (udev->dev_path == NULL || udev->sys_path == NULL)
224                 goto err;
225         dbg(udev, "context %p created\n", udev);
226         dbg(udev, "log_priority=%d\n", udev->log_priority);
227         dbg(udev, "config_file='%s'\n", config_file);
228         dbg(udev, "dev_path='%s'\n", udev->dev_path);
229         dbg(udev, "sys_path='%s'\n", udev->sys_path);
230         if (udev->rules_path != NULL)
231                 dbg(udev, "rules_path='%s'\n", udev->rules_path);
232         free(config_file);
233         return udev;
234 err:
235         free(config_file);
236         err(udev, "context creation failed\n");
237         udev_unref(udev);
238         return NULL;
239 }
240
241 /**
242  * udev_ref:
243  * @udev: udev library context
244  *
245  * Take a reference of the udev library context.
246  *
247  * Returns: the passed udev library context
248  **/
249 struct udev *udev_ref(struct udev *udev)
250 {
251         if (udev == NULL)
252                 return NULL;
253         udev->refcount++;
254         return udev;
255 }
256
257 /**
258  * udev_unref:
259  * @udev: udev library context
260  *
261  * Drop a reference of the udev library context. If the refcount
262  * reaches zero, the resources of the context will be released.
263  *
264  **/
265 void udev_unref(struct udev *udev)
266 {
267         if (udev == NULL)
268                 return;
269         udev->refcount--;
270         if (udev->refcount > 0)
271                 return;
272         udev_list_cleanup_entries(udev, &udev->properties_list);
273         free(udev->dev_path);
274         free(udev->sys_path);
275         free(udev->rules_path);
276         dbg(udev, "context %p released\n", udev);
277         free(udev);
278 }
279
280 /**
281  * udev_set_log_fn:
282  * @udev: udev library context
283  * @log_fn: function to be called for logging messages
284  *
285  * The built-in logging writes to stderr. It can be
286  * overridden by a custom function, to plug log messages
287  * into the users' logging functionality.
288  *
289  **/
290 void udev_set_log_fn(struct udev *udev,
291                      void (*log_fn)(struct udev *udev,
292                                     int priority, const char *file, int line, const char *fn,
293                                     const char *format, va_list args))
294 {
295         udev->log_fn = log_fn;
296         info(udev, "custom logging function %p registered\n", udev);
297 }
298
299 int udev_get_log_priority(struct udev *udev)
300 {
301         return udev->log_priority;
302 }
303
304 void udev_set_log_priority(struct udev *udev, int priority)
305 {
306         char num[32];
307
308         udev->log_priority = priority;
309         snprintf(num, sizeof(num), "%u", udev->log_priority);
310         udev_add_property(udev, "UDEV_LOG", num);
311 }
312
313 const char *udev_get_rules_path(struct udev *udev)
314 {
315         return udev->rules_path;
316 }
317
318 int udev_get_run(struct udev *udev)
319 {
320         return udev->run;
321 }
322
323 /**
324  * udev_get_sys_path:
325  * @udev: udev library context
326  *
327  * Retrieve the sysfs mount point. The default is "/sys". For
328  * testing purposes, it can be overridden with the environment
329  * variable SYSFS_PATH.
330  *
331  * Returns: the sys mount point
332  **/
333 const char *udev_get_sys_path(struct udev *udev)
334 {
335         if (udev == NULL)
336                 return NULL;
337         return udev->sys_path;
338 }
339
340 /**
341  * udev_get_dev_path:
342  * @udev: udev library context
343  *
344  * Retrieve the device directory path. The default value is "/dev",
345  * the actual value may be overridden in the udev configuration
346  * file.
347  *
348  * Returns: the device directory path
349  **/
350 const char *udev_get_dev_path(struct udev *udev)
351 {
352         if (udev == NULL)
353                 return NULL;
354         return udev->dev_path;
355 }
356
357 struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value)
358 {
359         if (value == NULL) {
360                 struct udev_list_entry *list_entry;
361
362                 list_entry = udev_get_properties_list_entry(udev);
363                 list_entry = udev_list_entry_get_by_name(list_entry, key);
364                 if (list_entry != NULL)
365                         udev_list_entry_delete(list_entry);
366                 return NULL;
367         }
368         return udev_list_entry_add(udev, &udev->properties_list, key, value, 1, 0);
369 }
370
371 struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev)
372 {
373         return udev_list_get_entry(&udev->properties_list);
374 }