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