chiark / gitweb /
use libudev code, unify logging, pass udev context around everywhere
[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 "config.h"
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stddef.h>
25 #include <stdarg.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <ctype.h>
30
31 #include "libudev.h"
32 #include "libudev-private.h"
33 #include "../udev.h"
34
35 struct udev {
36         int refcount;
37         void (*log_fn)(struct udev *udev,
38                        int priority, const char *file, int line, const char *fn,
39                        const char *format, va_list args);
40         char *sys_path;
41         char *dev_path;
42         char *rules_path;
43         int log_priority;
44         int run:1;
45 };
46
47 void udev_log(struct udev *udev,
48               int priority, const char *file, int line, const char *fn,
49               const char *format, ...)
50 {
51         va_list args;
52
53         if (priority > udev->log_priority)
54                 return;
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_new:
71  *
72  * Create udev library context.
73  *
74  * The initial refcount is 1, and needs to be decremented to
75  * release the ressources of the udev library context.
76  *
77  * Returns: a new udev library context
78  **/
79 struct udev *udev_new(void)
80 {
81         struct udev *udev;
82         const char *env;
83         char *config_file;
84         FILE *f;
85
86         udev = malloc(sizeof(struct udev));
87         if (udev == NULL)
88                 return NULL;
89         memset(udev, 0x00, (sizeof(struct udev)));
90
91         sysfs_init();
92
93         /* defaults */
94         config_file = NULL;
95         udev->refcount = 1;
96         udev->log_fn = log_stderr;
97         udev->log_priority = LOG_ERR;
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
103         if (udev->dev_path == NULL || udev->sys_path == NULL)
104                 goto err;
105
106         /* settings by environment and config file */
107         env = getenv("SYSFS_PATH");
108         if (env != NULL) {
109                 free(udev->sys_path);
110                 udev->sys_path = strdup(env);
111                 remove_trailing_chars(udev->sys_path, '/');
112         }
113
114         env = getenv("UDEV_RUN");
115         if (env != NULL && !string_is_true(env))
116                 udev->run = 0;
117
118         env = getenv("UDEV_CONFIG_FILE");
119         if (env != NULL) {
120                 free(config_file);
121                 config_file = strdup(env);
122                 remove_trailing_chars(config_file, '/');
123         }
124         if (config_file == NULL)
125                 goto err;
126         f = fopen(config_file, "r");
127         if (f != NULL) {
128                 char line[LINE_SIZE];
129                 int line_nr = 0;
130
131                 while (fgets(line, sizeof(line), f)) {
132                         size_t len;
133                         char *key;
134                         char *val;
135
136                         line_nr++;
137
138                         /* find key */
139                         key = line;
140                         while (isspace(key[0]))
141                                 key++;
142
143                         /* comment or empty line */
144                         if (key[0] == '#' || key[0] == '\0')
145                                 continue;
146
147                         /* split key/value */
148                         val = strchr(key, '=');
149                         if (val == NULL) {
150                                 err(udev, "missing <key>=<value> in '%s'[%i], skip line\n", config_file, line_nr);
151                                 continue;
152                         }
153                         val[0] = '\0';
154                         val++;
155
156                         /* find value */
157                         while (isspace(val[0]))
158                                 val++;
159
160                         /* terminate key */
161                         len = strlen(key);
162                         if (len == 0)
163                                 continue;
164                         while (isspace(key[len-1]))
165                                 len--;
166                         key[len] = '\0';
167
168                         /* terminate value */
169                         len = strlen(val);
170                         if (len == 0)
171                                 continue;
172                         while (isspace(val[len-1]))
173                                 len--;
174                         val[len] = '\0';
175
176                         if (len == 0)
177                                 continue;
178
179                         /* unquote */
180                         if (val[0] == '"' || val[0] == '\'') {
181                                 if (val[len-1] != val[0]) {
182                                         err(udev, "inconsistent quoting in '%s'[%i], skip line\n", config_file, line_nr);
183                                         continue;
184                                 }
185                                 val[len-1] = '\0';
186                                 val++;
187                         }
188
189                         if (strcasecmp(key, "udev_log") == 0) {
190                                 udev->log_priority = log_priority(val);
191                                 continue;
192                         }
193                         if (strcasecmp(key, "udev_root") == 0) {
194                                 free(udev->dev_path);
195                                 udev->dev_path = strdup(val);
196                                 remove_trailing_chars(udev->dev_path, '/');
197                                 continue;
198                         }
199                         if (strcasecmp(key, "udev_rules") == 0) {
200                                 free(udev->rules_path);
201                                 udev->rules_path = strdup(val);
202                                 remove_trailing_chars(udev->rules_path, '/');
203                                 continue;
204                         }
205                 }
206                 fclose(f);
207         }
208         free(config_file);
209
210         env = getenv("UDEV_ROOT");
211         if (env != NULL) {
212                 free(udev->dev_path);
213                 udev->dev_path = strdup(env);
214                 remove_trailing_chars(udev->dev_path, '/');
215         }
216
217         env = getenv("UDEV_LOG");
218         if (env != NULL)
219                 udev->log_priority = log_priority(env);
220
221         if (udev->dev_path == NULL || udev->sys_path == NULL)
222                 goto err;
223
224         info(udev, "context %p created\n", udev);
225         info(udev, "log_priority=%d\n", udev->log_priority);
226         info(udev, "dev_path='%s'\n", udev->dev_path);
227         if (udev->rules_path != NULL)
228                 info(udev, "rules_path='%s'\n", udev->rules_path);
229
230         return udev;
231 err:
232         err(udev, "context creation failed\n");
233         udev_unref(udev);
234         return NULL;
235 }
236
237 /**
238  * udev_ref:
239  * @udev: udev library context
240  *
241  * Take a reference of the udev library context.
242  *
243  * Returns: the passed udev library context
244  **/
245 struct udev *udev_ref(struct udev *udev)
246 {
247         if (udev == NULL)
248                 return NULL;
249         udev->refcount++;
250         return udev;
251 }
252
253 /**
254  * udev_unref:
255  * @udev: udev library context
256  *
257  * Drop a reference of the udev library context. If the refcount
258  * reaches zero, the ressources of the context will be released.
259  *
260  **/
261 void udev_unref(struct udev *udev)
262 {
263         if (udev == NULL)
264                 return;
265         udev->refcount--;
266         if (udev->refcount > 0)
267                 return;
268         sysfs_cleanup();
269         free(udev->dev_path);
270         free(udev->sys_path);
271         info(udev, "context %p released\n", udev);
272         free(udev);
273 }
274
275 /**
276  * udev_set_log_fn:
277  * @udev: udev library context
278  * @log_fn: function to be called for logging messages
279  *
280  * The built-in logging, which writes to stderr if the
281  * LIBUDEV_DEBUG environment variable is set, can be
282  * overridden by a custom function, to plug log messages
283  * into the users logging functionality.
284  *
285  **/
286 void udev_set_log_fn(struct udev *udev,
287                      void (*log_fn)(struct udev *udev,
288                                     int priority, const char *file, int line, const char *fn,
289                                     const char *format, va_list args))
290 {
291         udev->log_fn = log_fn;
292         info(udev, "custom logging function %p registered\n", udev);
293 }
294
295 int udev_get_log_priority(struct udev *udev)
296 {
297         return udev->log_priority;
298 }
299
300 void udev_set_log_priority(struct udev *udev, int priority)
301 {
302         udev->log_priority = priority;
303 }
304
305 const char *udev_get_rules_path(struct udev *udev)
306 {
307         return udev->rules_path;
308 }
309
310 int udev_get_run(struct udev *udev)
311 {
312         return udev->run;
313 }
314
315 /**
316  * udev_get_sys_path:
317  * @udev: udev library context
318  *
319  * Retrieve the sysfs mount point. The default is "/sys". For
320  * testing purposes, it can be overridden with the environment
321  * variable SYSFS_PATH.
322  *
323  * Returns: the sys mount point
324  **/
325 const char *udev_get_sys_path(struct udev *udev)
326 {
327         if (udev == NULL)
328                 return NULL;
329         return udev->sys_path;
330 }
331
332 /**
333  * udev_get_dev_path:
334  * @udev: udev library context
335  *
336  * Retrieve the device directory path. The default value is "/dev",
337  * the actual value may be overridden in the udev configuration
338  * file.
339  *
340  * Returns: the device directory path
341  **/
342 const char *udev_get_dev_path(struct udev *udev)
343 {
344         if (udev == NULL)
345                 return NULL;
346         return udev->dev_path;
347 }