]> git.ipfire.org Git - thirdparty/systemd.git/blob - udevinfo.c
encode db-file names, instead of just replacing '/'
[thirdparty/systemd.git] / udevinfo.c
1 /*
2 * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 *
17 */
18
19 #include <stdlib.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <stddef.h>
23 #include <ctype.h>
24 #include <stdarg.h>
25 #include <unistd.h>
26 #include <dirent.h>
27 #include <errno.h>
28 #include <getopt.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31
32 #include "udev.h"
33
34
35 #ifdef USE_LOG
36 void log_message (int priority, const char *format, ...)
37 {
38 va_list args;
39
40 if (priority > udev_log_priority)
41 return;
42
43 va_start(args, format);
44 vsyslog(priority, format, args);
45 va_end(args);
46 }
47 #endif
48
49 static void print_all_attributes(const char *devpath, const char *key)
50 {
51 char path[PATH_SIZE];
52 DIR *dir;
53 struct dirent *dent;
54
55 strlcpy(path, sysfs_path, sizeof(path));
56 strlcat(path, devpath, sizeof(path));
57
58 dir = opendir(path);
59 if (dir != NULL) {
60 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
61 struct stat statbuf;
62 char filename[PATH_SIZE];
63 char *attr_value;
64 char value[NAME_SIZE];
65 size_t len;
66
67 if (dent->d_name[0] == '.')
68 continue;
69
70 strlcpy(filename, path, sizeof(filename));
71 strlcat(filename, "/", sizeof(filename));
72 strlcat(filename, dent->d_name, sizeof(filename));
73 if (lstat(filename, &statbuf) != 0)
74 continue;
75 if (S_ISLNK(statbuf.st_mode))
76 continue;
77
78 attr_value = sysfs_attr_get_value(devpath, dent->d_name);
79 if (attr_value == NULL)
80 continue;
81 len = strlcpy(value, attr_value, sizeof(value));
82 dbg("attr '%s'='%s'(%zi)", dent->d_name, value, len);
83
84 /* remove trailing newlines */
85 while (len && value[len-1] == '\n')
86 value[--len] = '\0';
87
88 /* skip nonprintable attributes */
89 while (len && isprint(value[len-1]))
90 len--;
91 if (len) {
92 dbg("attribute value of '%s' non-printable, skip", dent->d_name);
93 continue;
94 }
95
96 replace_untrusted_chars(value);
97 printf(" %s{%s}==\"%s\"\n", key, dent->d_name, value);
98 }
99 }
100 printf("\n");
101 }
102
103 static int print_device_chain(const char *devpath)
104 {
105 struct sysfs_device *dev;
106
107 dev = sysfs_device_get(devpath);
108 if (dev == NULL)
109 return -1;
110
111 printf("\n"
112 "Udevinfo starts with the device specified by the devpath and then\n"
113 "walks up the chain of parent devices. It prints for every device\n"
114 "found, all possible attributes in the udev rules key format.\n"
115 "A rule to match, can be composed by the attributes of the device\n"
116 "and the attributes from one single parent device.\n"
117 "\n");
118
119 printf(" looking at device '%s':\n", dev->devpath);
120 printf(" KERNEL==\"%s\"\n", dev->kernel);
121 printf(" SUBSYSTEM==\"%s\"\n", dev->subsystem);
122 printf(" DRIVER==\"%s\"\n", dev->driver);
123 print_all_attributes(dev->devpath, "ATTR");
124
125 /* walk up the chain of devices */
126 while (1) {
127 dev = sysfs_device_get_parent(dev);
128 if (dev == NULL)
129 break;
130 printf(" looking at parent device '%s':\n", dev->devpath);
131 printf(" KERNELS==\"%s\"\n", dev->kernel);
132 printf(" SUBSYSTEMS==\"%s\"\n", dev->subsystem);
133 printf(" DRIVERS==\"%s\"\n", dev->driver);
134
135 print_all_attributes(dev->devpath, "ATTRS");
136 }
137
138 return 0;
139 }
140
141 static void print_record(struct udevice *udev)
142 {
143 struct name_entry *name_loop;
144
145 printf("P: %s\n", udev->dev->devpath);
146 printf("N: %s\n", udev->name);
147 list_for_each_entry(name_loop, &udev->symlink_list, node)
148 printf("S: %s\n", name_loop->name);
149 list_for_each_entry(name_loop, &udev->env_list, node)
150 printf("E: %s\n", name_loop->name);
151 }
152
153 static void export_name_devpath(struct udevice *udev) {
154 printf("%s=%s/%s\n", udev->dev->devpath, udev_root, udev->name);
155 }
156
157 static void export_record(struct udevice *udev) {
158 print_record(udev);
159 printf("\n");
160 }
161
162 static void export_db(void fnct(struct udevice *udev)) {
163 LIST_HEAD(name_list);
164 struct name_entry *name_loop;
165
166 udev_db_get_all_entries(&name_list);
167 list_for_each_entry(name_loop, &name_list, node) {
168 struct udevice *udev_db;
169
170 udev_db = udev_device_init();
171 if (udev_db == NULL)
172 continue;
173 if (udev_db_get_device(udev_db, name_loop->name) == 0)
174 fnct(udev_db);
175 udev_device_cleanup(udev_db);
176 }
177 name_list_cleanup(&name_list);
178 }
179
180 int main(int argc, char *argv[], char *envp[])
181 {
182 int option;
183 struct udevice *udev;
184 int root = 0;
185
186 static const struct option options[] = {
187 { "name", 1, NULL, 'n' },
188 { "path", 1, NULL, 'p' },
189 { "query", 1, NULL, 'q' },
190 { "attribute-walk", 0, NULL, 'a' },
191 { "export-db", 0, NULL, 'e' },
192 { "root", 0, NULL, 'r' },
193 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
194 { "help", 0, NULL, 'h' },
195 {}
196 };
197
198 enum action_type {
199 ACTION_NONE,
200 ACTION_QUERY,
201 ACTION_ATTRIBUTE_WALK,
202 ACTION_ROOT,
203 } action = ACTION_NONE;
204
205 enum query_type {
206 QUERY_NONE,
207 QUERY_NAME,
208 QUERY_PATH,
209 QUERY_SYMLINK,
210 QUERY_ENV,
211 QUERY_ALL,
212 } query = QUERY_NONE;
213
214 char path[PATH_SIZE] = "";
215 char name[PATH_SIZE] = "";
216 struct name_entry *name_loop;
217 int rc = 0;
218
219 logging_init("udevinfo");
220 udev_config_init();
221 sysfs_init();
222
223 udev = udev_device_init();
224 if (udev == NULL) {
225 rc = 1;
226 goto exit;
227 }
228
229 /* get command line options */
230 while (1) {
231 option = getopt_long(argc, argv, "aden:p:q:rVh", options, NULL);
232 if (option == -1)
233 break;
234
235 dbg("option '%c'", option);
236 switch (option) {
237 case 'n':
238 /* remove /dev if given */
239 if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
240 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
241 else
242 strlcpy(name, optarg, sizeof(name));
243 dbg("name: %s\n", name);
244 break;
245 case 'p':
246 /* remove /sys if given */
247 if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
248 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
249 else
250 strlcpy(path, optarg, sizeof(path));
251 dbg("path: %s\n", path);
252 break;
253 case 'q':
254 dbg("udev query: %s\n", optarg);
255 action = ACTION_QUERY;
256 if (strcmp(optarg, "name") == 0) {
257 query = QUERY_NAME;
258 break;
259 }
260 if (strcmp(optarg, "symlink") == 0) {
261 query = QUERY_SYMLINK;
262 break;
263 }
264 if (strcmp(optarg, "path") == 0) {
265 query = QUERY_PATH;
266 break;
267 }
268 if (strcmp(optarg, "env") == 0) {
269 query = QUERY_ENV;
270 break;
271 }
272 if (strcmp(optarg, "all") == 0) {
273 query = QUERY_ALL;
274 break;
275 }
276 fprintf(stderr, "unknown query type\n");
277 rc = 2;
278 goto exit;
279 case 'r':
280 if (action == ACTION_NONE)
281 action = ACTION_ROOT;
282 root = 1;
283 break;
284 case 'a':
285 action = ACTION_ATTRIBUTE_WALK;
286 break;
287 case 'd':
288 export_db(export_name_devpath);
289 goto exit;
290 case 'e':
291 export_db(export_record);
292 goto exit;
293 case 1:
294 printf("%s\n", UDEV_VERSION);
295 goto exit;
296 case 'V':
297 printf("udevinfo, version %s\n", UDEV_VERSION);
298 goto exit;
299 case 'h':
300 printf("Usage: udevinfo OPTIONS\n"
301 " --query=<type> query database for the specified value:\n"
302 " name name of device node\n"
303 " symlink pointing to node\n"
304 " path sysfs device path\n"
305 " env the device related imported environment\n"
306 " all all values\n"
307 "\n"
308 " --path=<devpath> sysfs device path used for query or chain\n"
309 " --name=<name> node or symlink name used for query\n"
310 "\n"
311 " --root prepend to query result or print udev_root\n"
312 " --attribute-walk print all SYSFS_attributes along the device chain\n"
313 " --export-db export the content of the udev database\n"
314 " --help print this text\n"
315 "\n");
316 goto exit;
317 default:
318 goto exit;
319 }
320 }
321
322 /* run action */
323 switch (action) {
324 case ACTION_QUERY:
325 /* needs devpath or node/symlink name for query */
326 if (path[0] != '\0') {
327 if (udev_db_get_device(udev, path) != 0) {
328 fprintf(stderr, "no record for '%s' in database\n", path);
329 rc = 3;
330 goto exit;
331 }
332 } else if (name[0] != '\0') {
333 char devpath[PATH_SIZE];
334
335 if (udev_db_lookup_name(name, devpath, sizeof(devpath)) != 0) {
336 fprintf(stderr, "node name not found\n");
337 rc = 4;
338 goto exit;
339 }
340 udev_db_get_device(udev, devpath);
341 } else {
342 fprintf(stderr, "query needs --path or node --name specified\n");
343 rc = 4;
344 goto exit;
345 }
346
347 switch(query) {
348 case QUERY_NAME:
349 if (root)
350 printf("%s/%s\n", udev_root, udev->name);
351 else
352 printf("%s\n", udev->name);
353 break;
354 case QUERY_SYMLINK:
355 if (list_empty(&udev->symlink_list))
356 goto exit;
357 if (root)
358 list_for_each_entry(name_loop, &udev->symlink_list, node)
359 printf("%s/%s ", udev_root, name_loop->name);
360 else
361 list_for_each_entry(name_loop, &udev->symlink_list, node)
362 printf("%s ", name_loop->name);
363 printf("\n");
364 break;
365 case QUERY_PATH:
366 printf("%s\n", udev->dev->devpath);
367 goto exit;
368 case QUERY_ENV:
369 list_for_each_entry(name_loop, &udev->env_list, node)
370 printf("%s\n", name_loop->name);
371 break;
372 case QUERY_ALL:
373 print_record(udev);
374 break;
375 default:
376 fprintf(stderr, "unknown query type\n");
377 break;
378 }
379 break;
380 case ACTION_ATTRIBUTE_WALK:
381 if (path[0] != '\0') {
382 if (print_device_chain(path) != 0) {
383 fprintf(stderr, "device not found\n");
384 rc = 4;
385 goto exit;
386 }
387 } else if (name[0] != '\0') {
388 char devpath[PATH_SIZE];
389
390 if (udev_db_lookup_name(name, devpath, sizeof(devpath)) != 0) {
391 fprintf(stderr, "node name not found\n");
392 rc = 4;
393 goto exit;
394 }
395 if (print_device_chain(devpath) != 0) {
396 fprintf(stderr, "device not found\n");
397 rc = 4;
398 goto exit;
399 }
400 } else {
401 fprintf(stderr, "attribute walk needs --path or node --name specified\n");
402 rc = 5;
403 goto exit;
404 }
405 break;
406 case ACTION_ROOT:
407 printf("%s\n", udev_root);
408 break;
409 default:
410 fprintf(stderr, "missing option\n");
411 rc = 1;
412 break;
413 }
414
415 exit:
416 udev_device_cleanup(udev);
417 sysfs_cleanup();
418 logging_close();
419 return rc;
420 }