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