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