chiark / gitweb /
test-udev: cleanup libudev context and overridden rules file string
[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         udev->refcount = 1;
95         udev->log_fn = log_stderr;
96         udev->log_priority = LOG_ERR;
97         udev->run = 1;
98         udev->dev_path = strdup(UDEV_PREFIX "/dev");
99         udev->sys_path = strdup("/sys");
100         config_file = strdup(SYSCONFDIR "/udev/udev.conf");
101
102         if (udev->dev_path == NULL ||
103             udev->sys_path == NULL ||
104             config_file == NULL)
105                 goto err;
106
107         /* settings by environment and config file */
108         env = getenv("SYSFS_PATH");
109         if (env != NULL) {
110                 free(udev->sys_path);
111                 udev->sys_path = strdup(env);
112                 remove_trailing_chars(udev->sys_path, '/');
113         }
114
115         env = getenv("UDEV_RUN");
116         if (env != NULL && !string_is_true(env))
117                 udev->run = 0;
118
119         env = getenv("UDEV_CONFIG_FILE");
120         if (env != NULL) {
121                 free(config_file);
122                 config_file = strdup(env);
123                 remove_trailing_chars(config_file, '/');
124         }
125         if (config_file == NULL)
126                 goto err;
127         f = fopen(config_file, "r");
128         if (f != NULL) {
129                 char line[LINE_SIZE];
130                 int line_nr = 0;
131
132                 while (fgets(line, sizeof(line), f)) {
133                         size_t len;
134                         char *key;
135                         char *val;
136
137                         line_nr++;
138
139                         /* find key */
140                         key = line;
141                         while (isspace(key[0]))
142                                 key++;
143
144                         /* comment or empty line */
145                         if (key[0] == '#' || key[0] == '\0')
146                                 continue;
147
148                         /* split key/value */
149                         val = strchr(key, '=');
150                         if (val == NULL) {
151                                 err(udev, "missing <key>=<value> in '%s'[%i], skip line\n", config_file, line_nr);
152                                 continue;
153                         }
154                         val[0] = '\0';
155                         val++;
156
157                         /* find value */
158                         while (isspace(val[0]))
159                                 val++;
160
161                         /* terminate key */
162                         len = strlen(key);
163                         if (len == 0)
164                                 continue;
165                         while (isspace(key[len-1]))
166                                 len--;
167                         key[len] = '\0';
168
169                         /* terminate value */
170                         len = strlen(val);
171                         if (len == 0)
172                                 continue;
173                         while (isspace(val[len-1]))
174                                 len--;
175                         val[len] = '\0';
176
177                         if (len == 0)
178                                 continue;
179
180                         /* unquote */
181                         if (val[0] == '"' || val[0] == '\'') {
182                                 if (val[len-1] != val[0]) {
183                                         err(udev, "inconsistent quoting in '%s'[%i], skip line\n", config_file, line_nr);
184                                         continue;
185                                 }
186                                 val[len-1] = '\0';
187                                 val++;
188                         }
189
190                         if (strcasecmp(key, "udev_log") == 0) {
191                                 udev->log_priority = log_priority(val);
192                                 continue;
193                         }
194                         if (strcasecmp(key, "udev_root") == 0) {
195                                 free(udev->dev_path);
196                                 udev->dev_path = strdup(val);
197                                 remove_trailing_chars(udev->dev_path, '/');
198                                 continue;
199                         }
200                         if (strcasecmp(key, "udev_rules") == 0) {
201                                 free(udev->rules_path);
202                                 udev->rules_path = strdup(val);
203                                 remove_trailing_chars(udev->rules_path, '/');
204                                 continue;
205                         }
206                 }
207                 fclose(f);
208         }
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, "config_file='%s'\n", config_file);
227         info(udev, "dev_path='%s'\n", udev->dev_path);
228         info(udev, "sys_path='%s'\n", udev->sys_path);
229         if (udev->rules_path != NULL)
230                 info(udev, "rules_path='%s'\n", udev->rules_path);
231
232         free(config_file);
233         return udev;
234 err:
235         free(config_file);
236         err(udev, "context creation failed\n");
237         udev_unref(udev);
238         return NULL;
239 }
240
241 /**
242  * udev_ref:
243  * @udev: udev library context
244  *
245  * Take a reference of the udev library context.
246  *
247  * Returns: the passed udev library context
248  **/
249 struct udev *udev_ref(struct udev *udev)
250 {
251         if (udev == NULL)
252                 return NULL;
253         udev->refcount++;
254         return udev;
255 }
256
257 /**
258  * udev_unref:
259  * @udev: udev library context
260  *
261  * Drop a reference of the udev library context. If the refcount
262  * reaches zero, the ressources of the context will be released.
263  *
264  **/
265 void udev_unref(struct udev *udev)
266 {
267         if (udev == NULL)
268                 return;
269         udev->refcount--;
270         if (udev->refcount > 0)
271                 return;
272         sysfs_cleanup();
273         free(udev->dev_path);
274         free(udev->sys_path);
275         free(udev->rules_path);
276         info(udev, "context %p released\n", udev);
277         free(udev);
278 }
279
280 /**
281  * udev_set_log_fn:
282  * @udev: udev library context
283  * @log_fn: function to be called for logging messages
284  *
285  * The built-in logging, which writes to stderr if the
286  * LIBUDEV_DEBUG environment variable is set, can be
287  * overridden by a custom function, to plug log messages
288  * into the users logging functionality.
289  *
290  **/
291 void udev_set_log_fn(struct udev *udev,
292                      void (*log_fn)(struct udev *udev,
293                                     int priority, const char *file, int line, const char *fn,
294                                     const char *format, va_list args))
295 {
296         udev->log_fn = log_fn;
297         info(udev, "custom logging function %p registered\n", udev);
298 }
299
300 int udev_get_log_priority(struct udev *udev)
301 {
302         return udev->log_priority;
303 }
304
305 void udev_set_log_priority(struct udev *udev, int priority)
306 {
307         udev->log_priority = priority;
308 }
309
310 const char *udev_get_rules_path(struct udev *udev)
311 {
312         return udev->rules_path;
313 }
314
315 int udev_get_run(struct udev *udev)
316 {
317         return udev->run;
318 }
319
320 /**
321  * udev_get_sys_path:
322  * @udev: udev library context
323  *
324  * Retrieve the sysfs mount point. The default is "/sys". For
325  * testing purposes, it can be overridden with the environment
326  * variable SYSFS_PATH.
327  *
328  * Returns: the sys mount point
329  **/
330 const char *udev_get_sys_path(struct udev *udev)
331 {
332         if (udev == NULL)
333                 return NULL;
334         return udev->sys_path;
335 }
336
337 /**
338  * udev_get_dev_path:
339  * @udev: udev library context
340  *
341  * Retrieve the device directory path. The default value is "/dev",
342  * the actual value may be overridden in the udev configuration
343  * file.
344  *
345  * Returns: the device directory path
346  **/
347 const char *udev_get_dev_path(struct udev *udev)
348 {
349         if (udev == NULL)
350                 return NULL;
351         return udev->dev_path;
352 }