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