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