chiark / gitweb /
af36cc44524c9f788f26d803afd4435f20a61400
[elogind.git] / src / libudev / libudev.c
1 /*
2  * libudev - interface to udev device information
3  *
4  * Copyright (C) 2008-2010 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stddef.h>
15 #include <stdarg.h>
16 #include <unistd.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include <time.h>
21
22 #include "libudev.h"
23 #include "libudev-private.h"
24 #include "missing.h"
25
26 /**
27  * SECTION:libudev
28  * @short_description: libudev context
29  *
30  * The context contains the default values read from the udev config file,
31  * and is passed to all library operations.
32  */
33
34 /**
35  * udev:
36  *
37  * Opaque object representing the library context.
38  */
39 struct udev {
40         int refcount;
41         void (*log_fn)(struct udev *udev,
42                        int priority, const char *file, int line, const char *fn,
43                        const char *format, va_list args);
44         void *userdata;
45         struct udev_list properties_list;
46         int log_priority;
47 };
48
49 void udev_log(struct udev *udev,
50               int priority, const char *file, int line, const char *fn,
51               const char *format, ...)
52 {
53         va_list args;
54
55         va_start(args, format);
56         udev->log_fn(udev, priority, file, line, fn, format, args);
57         va_end(args);
58 }
59
60 static void log_stderr(struct udev *udev,
61                        int priority, const char *file, int line, const char *fn,
62                        const char *format, va_list args)
63 {
64         fprintf(stderr, "libudev: %s: ", fn);
65         vfprintf(stderr, format, args);
66 }
67
68 /**
69  * udev_get_userdata:
70  * @udev: udev library context
71  *
72  * Retrieve stored data pointer from library context. This might be useful
73  * to access from callbacks like a custom logging function.
74  *
75  * Returns: stored userdata
76  **/
77 _public_ void *udev_get_userdata(struct udev *udev)
78 {
79         if (udev == NULL)
80                 return NULL;
81         return udev->userdata;
82 }
83
84 /**
85  * udev_set_userdata:
86  * @udev: udev library context
87  * @userdata: data pointer
88  *
89  * Store custom @userdata in the library context.
90  **/
91 _public_ void udev_set_userdata(struct udev *udev, void *userdata)
92 {
93         if (udev == NULL)
94                 return;
95         udev->userdata = userdata;
96 }
97
98 /**
99  * udev_new:
100  *
101  * Create udev library context. This reads the udev configuration
102  * file, and fills in the default values.
103  *
104  * The initial refcount is 1, and needs to be decremented to
105  * release the resources of the udev library context.
106  *
107  * Returns: a new udev library context
108  **/
109 _public_ struct udev *udev_new(void)
110 {
111         struct udev *udev;
112         const char *env;
113         FILE *f;
114
115         udev = calloc(1, sizeof(struct udev));
116         if (udev == NULL)
117                 return NULL;
118         udev->refcount = 1;
119         udev->log_fn = log_stderr;
120         udev->log_priority = LOG_ERR;
121         udev_list_init(udev, &udev->properties_list, true);
122
123         f = fopen(SYSCONFDIR "/udev/udev.conf", "re");
124         if (f != NULL) {
125                 char line[UTIL_LINE_SIZE];
126                 int line_nr = 0;
127
128                 while (fgets(line, sizeof(line), f)) {
129                         size_t len;
130                         char *key;
131                         char *val;
132
133                         line_nr++;
134
135                         /* find key */
136                         key = line;
137                         while (isspace(key[0]))
138                                 key++;
139
140                         /* comment or empty line */
141                         if (key[0] == '#' || key[0] == '\0')
142                                 continue;
143
144                         /* split key/value */
145                         val = strchr(key, '=');
146                         if (val == NULL) {
147                                 udev_err(udev, "missing <key>=<value> in " SYSCONFDIR "/udev/udev.conf[%i]; skip line\n", line_nr);
148                                 continue;
149                         }
150                         val[0] = '\0';
151                         val++;
152
153                         /* find value */
154                         while (isspace(val[0]))
155                                 val++;
156
157                         /* terminate key */
158                         len = strlen(key);
159                         if (len == 0)
160                                 continue;
161                         while (isspace(key[len-1]))
162                                 len--;
163                         key[len] = '\0';
164
165                         /* terminate value */
166                         len = strlen(val);
167                         if (len == 0)
168                                 continue;
169                         while (isspace(val[len-1]))
170                                 len--;
171                         val[len] = '\0';
172
173                         if (len == 0)
174                                 continue;
175
176                         /* unquote */
177                         if (val[0] == '"' || val[0] == '\'') {
178                                 if (val[len-1] != val[0]) {
179                                         udev_err(udev, "inconsistent quoting in " SYSCONFDIR "/udev/udev.conf[%i]; skip line\n", line_nr);
180                                         continue;
181                                 }
182                                 val[len-1] = '\0';
183                                 val++;
184                         }
185
186                         if (strcmp(key, "udev_log") == 0) {
187                                 udev_set_log_priority(udev, util_log_priority(val));
188                                 continue;
189                         }
190                 }
191                 fclose(f);
192         }
193
194         /* environment overrides config */
195         env = secure_getenv("UDEV_LOG");
196         if (env != NULL)
197                 udev_set_log_priority(udev, util_log_priority(env));
198
199         return udev;
200 }
201
202 /**
203  * udev_ref:
204  * @udev: udev library context
205  *
206  * Take a reference of the udev library context.
207  *
208  * Returns: the passed udev library context
209  **/
210 _public_ struct udev *udev_ref(struct udev *udev)
211 {
212         if (udev == NULL)
213                 return NULL;
214         udev->refcount++;
215         return udev;
216 }
217
218 /**
219  * udev_unref:
220  * @udev: udev library context
221  *
222  * Drop a reference of the udev library context. If the refcount
223  * reaches zero, the resources of the context will be released.
224  *
225  * Returns: the passed udev library context if it has still an active reference, or #NULL otherwise.
226  **/
227 _public_ struct udev *udev_unref(struct udev *udev)
228 {
229         if (udev == NULL)
230                 return NULL;
231         udev->refcount--;
232         if (udev->refcount > 0)
233                 return udev;
234         udev_list_cleanup(&udev->properties_list);
235         free(udev);
236         return NULL;
237 }
238
239 /**
240  * udev_set_log_fn:
241  * @udev: udev library context
242  * @log_fn: function to be called for logging messages
243  *
244  * The built-in logging writes to stderr. It can be
245  * overridden by a custom function, to plug log messages
246  * into the users' logging functionality.
247  *
248  **/
249 _public_ void udev_set_log_fn(struct udev *udev,
250                      void (*log_fn)(struct udev *udev,
251                                     int priority, const char *file, int line, const char *fn,
252                                     const char *format, va_list args))
253 {
254         udev->log_fn = log_fn;
255         udev_dbg(udev, "custom logging function %p registered\n", log_fn);
256 }
257
258 /**
259  * udev_get_log_priority:
260  * @udev: udev library context
261  *
262  * The initial logging priority is read from the udev config file
263  * at startup.
264  *
265  * Returns: the current logging priority
266  **/
267 _public_ int udev_get_log_priority(struct udev *udev)
268 {
269         return udev->log_priority;
270 }
271
272 /**
273  * udev_set_log_priority:
274  * @udev: udev library context
275  * @priority: the new logging priority
276  *
277  * Set the current logging priority. The value controls which messages
278  * are logged.
279  **/
280 _public_ void udev_set_log_priority(struct udev *udev, int priority)
281 {
282         char num[32];
283
284         udev->log_priority = priority;
285         snprintf(num, sizeof(num), "%u", udev->log_priority);
286         udev_add_property(udev, "UDEV_LOG", num);
287 }
288
289 struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value)
290 {
291         if (value == NULL) {
292                 struct udev_list_entry *list_entry;
293
294                 list_entry = udev_get_properties_list_entry(udev);
295                 list_entry = udev_list_entry_get_by_name(list_entry, key);
296                 if (list_entry != NULL)
297                         udev_list_entry_delete(list_entry);
298                 return NULL;
299         }
300         return udev_list_entry_add(&udev->properties_list, key, value);
301 }
302
303 struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev)
304 {
305         return udev_list_get_entry(&udev->properties_list);
306 }