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