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