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