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