chiark / gitweb /
4e258bd2ce6e735d14b06a5a45689dc0c460d2ac
[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         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
170         selinux_init(udev);
171         sysfs_init();
172         udev->refcount = 1;
173         udev->log_fn = log_stderr;
174         udev->log_priority = LOG_ERR;
175         udev->run = 1;
176         udev->dev_path = strdup(UDEV_PREFIX "/dev");
177         udev->sys_path = strdup("/sys");
178         config_file = strdup(SYSCONFDIR "/udev/udev.conf");
179
180         if (udev->dev_path == NULL ||
181             udev->sys_path == NULL ||
182             config_file == NULL)
183                 goto err;
184
185         /* settings by environment and config file */
186         env = getenv("SYSFS_PATH");
187         if (env != NULL) {
188                 free(udev->sys_path);
189                 udev->sys_path = strdup(env);
190                 remove_trailing_chars(udev->sys_path, '/');
191         }
192
193         env = getenv("UDEV_RUN");
194         if (env != NULL && !string_is_true(env))
195                 udev->run = 0;
196
197         env = getenv("UDEV_CONFIG_FILE");
198         if (env != NULL) {
199                 free(config_file);
200                 config_file = strdup(env);
201                 remove_trailing_chars(config_file, '/');
202         }
203         if (config_file == NULL)
204                 goto err;
205         f = fopen(config_file, "r");
206         if (f != NULL) {
207                 char line[LINE_SIZE];
208                 int line_nr = 0;
209
210                 while (fgets(line, sizeof(line), f)) {
211                         size_t len;
212                         char *key;
213                         char *val;
214
215                         line_nr++;
216
217                         /* find key */
218                         key = line;
219                         while (isspace(key[0]))
220                                 key++;
221
222                         /* comment or empty line */
223                         if (key[0] == '#' || key[0] == '\0')
224                                 continue;
225
226                         /* split key/value */
227                         val = strchr(key, '=');
228                         if (val == NULL) {
229                                 err(udev, "missing <key>=<value> in '%s'[%i], skip line\n", config_file, line_nr);
230                                 continue;
231                         }
232                         val[0] = '\0';
233                         val++;
234
235                         /* find value */
236                         while (isspace(val[0]))
237                                 val++;
238
239                         /* terminate key */
240                         len = strlen(key);
241                         if (len == 0)
242                                 continue;
243                         while (isspace(key[len-1]))
244                                 len--;
245                         key[len] = '\0';
246
247                         /* terminate value */
248                         len = strlen(val);
249                         if (len == 0)
250                                 continue;
251                         while (isspace(val[len-1]))
252                                 len--;
253                         val[len] = '\0';
254
255                         if (len == 0)
256                                 continue;
257
258                         /* unquote */
259                         if (val[0] == '"' || val[0] == '\'') {
260                                 if (val[len-1] != val[0]) {
261                                         err(udev, "inconsistent quoting in '%s'[%i], skip line\n", config_file, line_nr);
262                                         continue;
263                                 }
264                                 val[len-1] = '\0';
265                                 val++;
266                         }
267
268                         if (strcasecmp(key, "udev_log") == 0) {
269                                 udev->log_priority = log_priority(val);
270                                 continue;
271                         }
272                         if (strcasecmp(key, "udev_root") == 0) {
273                                 free(udev->dev_path);
274                                 udev->dev_path = strdup(val);
275                                 remove_trailing_chars(udev->dev_path, '/');
276                                 continue;
277                         }
278                         if (strcasecmp(key, "udev_rules") == 0) {
279                                 free(udev->rules_path);
280                                 udev->rules_path = strdup(val);
281                                 remove_trailing_chars(udev->rules_path, '/');
282                                 continue;
283                         }
284                 }
285                 fclose(f);
286         }
287
288         env = getenv("UDEV_ROOT");
289         if (env != NULL) {
290                 free(udev->dev_path);
291                 udev->dev_path = strdup(env);
292                 remove_trailing_chars(udev->dev_path, '/');
293         }
294
295         env = getenv("UDEV_LOG");
296         if (env != NULL)
297                 udev->log_priority = log_priority(env);
298
299         if (udev->dev_path == NULL || udev->sys_path == NULL)
300                 goto err;
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 }