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