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