chiark / gitweb /
resolved: various bad memory access fixes to the cache
[elogind.git] / src / udev / udevadm-info.c
1 /*
2  * Copyright (C) 2004-2009 Kay Sievers <kay@vrfy.org>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <stdlib.h>
19 #include <string.h>
20 #include <stdio.h>
21 #include <stddef.h>
22 #include <ctype.h>
23 #include <stdarg.h>
24 #include <unistd.h>
25 #include <dirent.h>
26 #include <errno.h>
27 #include <getopt.h>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31
32 #include "udev.h"
33 #include "udev-util.h"
34
35 static bool skip_attribute(const char *name)
36 {
37         static const char* const skip[] = {
38                 "uevent",
39                 "dev",
40                 "modalias",
41                 "resource",
42                 "driver",
43                 "subsystem",
44                 "module",
45         };
46         unsigned int i;
47
48         for (i = 0; i < ELEMENTSOF(skip); i++)
49                 if (streq(name, skip[i]))
50                         return true;
51         return false;
52 }
53
54 static void print_all_attributes(struct udev_device *device, const char *key)
55 {
56         struct udev_list_entry *sysattr;
57
58         udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) {
59                 const char *name;
60                 const char *value;
61                 size_t len;
62
63                 name = udev_list_entry_get_name(sysattr);
64                 if (skip_attribute(name))
65                         continue;
66
67                 value = udev_device_get_sysattr_value(device, name);
68                 if (value == NULL)
69                         continue;
70
71                 /* skip any values that look like a path */
72                 if (value[0] == '/')
73                         continue;
74
75                 /* skip nonprintable attributes */
76                 len = strlen(value);
77                 while (len > 0 && isprint(value[len-1]))
78                         len--;
79                 if (len > 0)
80                         continue;
81
82                 printf("    %s{%s}==\"%s\"\n", key, name, value);
83         }
84         printf("\n");
85 }
86
87 static int print_device_chain(struct udev_device *device)
88 {
89         struct udev_device *device_parent;
90         const char *str;
91
92         printf("\n"
93                "Udevadm info starts with the device specified by the devpath and then\n"
94                "walks up the chain of parent devices. It prints for every device\n"
95                "found, all possible attributes in the udev rules key format.\n"
96                "A rule to match, can be composed by the attributes of the device\n"
97                "and the attributes from one single parent device.\n"
98                "\n");
99
100         printf("  looking at device '%s':\n", udev_device_get_devpath(device));
101         printf("    KERNEL==\"%s\"\n", udev_device_get_sysname(device));
102         str = udev_device_get_subsystem(device);
103         if (str == NULL)
104                 str = "";
105         printf("    SUBSYSTEM==\"%s\"\n", str);
106         str = udev_device_get_driver(device);
107         if (str == NULL)
108                 str = "";
109         printf("    DRIVER==\"%s\"\n", str);
110         print_all_attributes(device, "ATTR");
111
112         device_parent = device;
113         do {
114                 device_parent = udev_device_get_parent(device_parent);
115                 if (device_parent == NULL)
116                         break;
117                 printf("  looking at parent device '%s':\n", udev_device_get_devpath(device_parent));
118                 printf("    KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent));
119                 str = udev_device_get_subsystem(device_parent);
120                 if (str == NULL)
121                         str = "";
122                 printf("    SUBSYSTEMS==\"%s\"\n", str);
123                 str = udev_device_get_driver(device_parent);
124                 if (str == NULL)
125                         str = "";
126                 printf("    DRIVERS==\"%s\"\n", str);
127                 print_all_attributes(device_parent, "ATTRS");
128         } while (device_parent != NULL);
129
130         return 0;
131 }
132
133 static void print_record(struct udev_device *device)
134 {
135         const char *str;
136         int i;
137         struct udev_list_entry *list_entry;
138
139         printf("P: %s\n", udev_device_get_devpath(device));
140
141         str = udev_device_get_devnode(device);
142         if (str != NULL)
143                 printf("N: %s\n", str + strlen("/dev/"));
144
145         i = udev_device_get_devlink_priority(device);
146         if (i != 0)
147                 printf("L: %i\n", i);
148
149         udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device))
150                 printf("S: %s\n", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
151
152         udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
153                 printf("E: %s=%s\n",
154                        udev_list_entry_get_name(list_entry),
155                        udev_list_entry_get_value(list_entry));
156         printf("\n");
157 }
158
159 static int stat_device(const char *name, bool export, const char *prefix)
160 {
161         struct stat statbuf;
162
163         if (stat(name, &statbuf) != 0)
164                 return -1;
165
166         if (export) {
167                 if (prefix == NULL)
168                         prefix = "INFO_";
169                 printf("%sMAJOR=%d\n"
170                        "%sMINOR=%d\n",
171                        prefix, major(statbuf.st_dev),
172                        prefix, minor(statbuf.st_dev));
173         } else
174                 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
175         return 0;
176 }
177
178 static int export_devices(struct udev *udev)
179 {
180         struct udev_enumerate *udev_enumerate;
181         struct udev_list_entry *list_entry;
182
183         udev_enumerate = udev_enumerate_new(udev);
184         if (udev_enumerate == NULL)
185                 return -1;
186         udev_enumerate_scan_devices(udev_enumerate);
187         udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
188                 struct udev_device *device;
189
190                 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
191                 if (device != NULL) {
192                         print_record(device);
193                         udev_device_unref(device);
194                 }
195         }
196         udev_enumerate_unref(udev_enumerate);
197         return 0;
198 }
199
200 static void cleanup_dir(DIR *dir, mode_t mask, int depth)
201 {
202         struct dirent *dent;
203
204         if (depth <= 0)
205                 return;
206
207         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
208                 struct stat stats;
209
210                 if (dent->d_name[0] == '.')
211                         continue;
212                 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
213                         continue;
214                 if ((stats.st_mode & mask) != 0)
215                         continue;
216                 if (S_ISDIR(stats.st_mode)) {
217                         DIR *dir2;
218
219                         dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
220                         if (dir2 != NULL) {
221                                 cleanup_dir(dir2, mask, depth-1);
222                                 closedir(dir2);
223                         }
224                         unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
225                 } else {
226                         unlinkat(dirfd(dir), dent->d_name, 0);
227                 }
228         }
229 }
230
231 static void cleanup_db(struct udev *udev)
232 {
233         DIR *dir;
234
235         unlink("/run/udev/queue.bin");
236
237         dir = opendir("/run/udev/data");
238         if (dir != NULL) {
239                 cleanup_dir(dir, S_ISVTX, 1);
240                 closedir(dir);
241         }
242
243         dir = opendir("/run/udev/links");
244         if (dir != NULL) {
245                 cleanup_dir(dir, 0, 2);
246                 closedir(dir);
247         }
248
249         dir = opendir("/run/udev/tags");
250         if (dir != NULL) {
251                 cleanup_dir(dir, 0, 2);
252                 closedir(dir);
253         }
254
255         dir = opendir("/run/udev/static_node-tags");
256         if (dir != NULL) {
257                 cleanup_dir(dir, 0, 2);
258                 closedir(dir);
259         }
260
261         dir = opendir("/run/udev/watch");
262         if (dir != NULL) {
263                 cleanup_dir(dir, 0, 1);
264                 closedir(dir);
265         }
266 }
267
268 static struct udev_device *find_device(struct udev *udev, const char *id, const char *prefix)
269 {
270         char name[UTIL_PATH_SIZE];
271
272         if (prefix && !startswith(id, prefix)) {
273                 strscpyl(name, sizeof(name), prefix, id, NULL);
274                 id = name;
275         }
276
277         if (startswith(id, "/dev/")) {
278                 struct stat statbuf;
279                 char type;
280
281                 if (stat(id, &statbuf) < 0)
282                         return NULL;
283
284                 if (S_ISBLK(statbuf.st_mode))
285                         type = 'b';
286                 else if (S_ISCHR(statbuf.st_mode))
287                         type = 'c';
288                 else
289                         return NULL;
290
291                 return udev_device_new_from_devnum(udev, type, statbuf.st_rdev);
292         } else if (startswith(id, "/sys/"))
293                 return udev_device_new_from_syspath(udev, id);
294         else
295                 return NULL;
296 }
297
298 static int uinfo(struct udev *udev, int argc, char *argv[])
299 {
300         _cleanup_udev_device_unref_ struct udev_device *device = NULL;
301         bool root = 0;
302         bool export = 0;
303         const char *export_prefix = NULL;
304         char name[UTIL_PATH_SIZE];
305         struct udev_list_entry *list_entry;
306         int c;
307
308         static const struct option options[] = {
309                 { "name",              required_argument, NULL, 'n' },
310                 { "path",              required_argument, NULL, 'p' },
311                 { "query",             required_argument, NULL, 'q' },
312                 { "attribute-walk",    no_argument,       NULL, 'a' },
313                 { "cleanup-db",        no_argument,       NULL, 'c' },
314                 { "export-db",         no_argument,       NULL, 'e' },
315                 { "root",              no_argument,       NULL, 'r' },
316                 { "device-id-of-file", required_argument, NULL, 'd' },
317                 { "export",            no_argument,       NULL, 'x' },
318                 { "export-prefix",     required_argument, NULL, 'P' },
319                 { "version",           no_argument,       NULL, 'V' },
320                 { "help",              no_argument,       NULL, 'h' },
321                 {}
322         };
323
324         static const char *usage =
325                 "Usage: udevadm info [OPTIONS] [DEVPATH|FILE]\n"
326                 " -q,--query=TYPE             query device information:\n"
327                 "      name                     name of device node\n"
328                 "      symlink                  pointing to node\n"
329                 "      path                     sys device path\n"
330                 "      property                 the device properties\n"
331                 "      all                      all values\n"
332                 " -p,--path=SYSPATH           sys device path used for query or attribute walk\n"
333                 " -n,--name=NAME              node or symlink name used for query or attribute walk\n"
334                 " -r,--root                   prepend dev directory to path names\n"
335                 " -a,--attribute-walk         print all key matches walking along the chain\n"
336                 "                             of parent devices\n"
337                 " -d,--device-id-of-file=FILE print major:minor of device containing this file\n"
338                 " -x,--export                 export key/value pairs\n"
339                 " -P,--export-prefix          export the key name with a prefix\n"
340                 " -e,--export-db              export the content of the udev database\n"
341                 " -c,--cleanup-db             cleanup the udev database\n"
342                 "    --version                print version of the program\n"
343                 " -h,--help                   print this message\n";
344
345         enum action_type {
346                 ACTION_QUERY,
347                 ACTION_ATTRIBUTE_WALK,
348                 ACTION_DEVICE_ID_FILE,
349         } action = ACTION_QUERY;
350
351         enum query_type {
352                 QUERY_NAME,
353                 QUERY_PATH,
354                 QUERY_SYMLINK,
355                 QUERY_PROPERTY,
356                 QUERY_ALL,
357         } query = QUERY_ALL;
358
359         while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
360                 switch (c) {
361                 case 'n': {
362                         if (device != NULL) {
363                                 fprintf(stderr, "device already specified\n");
364                                 return 2;
365                         }
366
367                         device = find_device(udev, optarg, "/dev/");
368                         if (device == NULL) {
369                                 fprintf(stderr, "device node not found\n");
370                                 return 2;
371                         }
372                         break;
373                 }
374                 case 'p':
375                         if (device != NULL) {
376                                 fprintf(stderr, "device already specified\n");
377                                 return 2;
378                         }
379
380                         device = find_device(udev, optarg, "/sys");
381                         if (device == NULL) {
382                                 fprintf(stderr, "syspath not found\n");
383                                 return 2;
384                         }
385                         break;
386                 case 'q':
387                         action = ACTION_QUERY;
388                         if (streq(optarg, "property") || streq(optarg, "env"))
389                                 query = QUERY_PROPERTY;
390                         else if (streq(optarg, "name"))
391                                 query = QUERY_NAME;
392                         else if (streq(optarg, "symlink"))
393                                 query = QUERY_SYMLINK;
394                         else if (streq(optarg, "path"))
395                                 query = QUERY_PATH;
396                         else if (streq(optarg, "all"))
397                                 query = QUERY_ALL;
398                         else {
399                                 fprintf(stderr, "unknown query type\n");
400                                 return 3;
401                         }
402                         break;
403                 case 'r':
404                         root = true;
405                         break;
406                 case 'd':
407                         action = ACTION_DEVICE_ID_FILE;
408                         strscpy(name, sizeof(name), optarg);
409                         break;
410                 case 'a':
411                         action = ACTION_ATTRIBUTE_WALK;
412                         break;
413                 case 'e':
414                         export_devices(udev);
415                         return 0;
416                 case 'c':
417                         cleanup_db(udev);
418                         return 0;
419                 case 'x':
420                         export = true;
421                         break;
422                 case 'P':
423                         export_prefix = optarg;
424                         break;
425                 case 'V':
426                         printf("%s\n", VERSION);
427                         return 0;
428                 case 'h':
429                         printf("%s\n", usage);
430                         return 0;
431                 default:
432                         return 1;
433                 }
434
435         switch (action) {
436         case ACTION_QUERY:
437                 if (!device) {
438                         if (!argv[optind]) {
439                                 fprintf(stderr, "%s\n", usage);
440                                 return 2;
441                         }
442                         device = find_device(udev, argv[optind], NULL);
443                         if (!device) {
444                                 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
445                                 return 4;
446                         }
447                 }
448
449                 switch(query) {
450                 case QUERY_NAME: {
451                         const char *node = udev_device_get_devnode(device);
452
453                         if (node == NULL) {
454                                 fprintf(stderr, "no device node found\n");
455                                 return 5;
456                         }
457
458                         if (root)
459                                 printf("%s\n", udev_device_get_devnode(device));
460                         else
461                                 printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/"));
462                         break;
463                 }
464                 case QUERY_SYMLINK:
465                         list_entry = udev_device_get_devlinks_list_entry(device);
466                         while (list_entry != NULL) {
467                                 if (root)
468                                         printf("%s", udev_list_entry_get_name(list_entry));
469                                 else
470                                         printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
471                                 list_entry = udev_list_entry_get_next(list_entry);
472                                 if (list_entry != NULL)
473                                         printf(" ");
474                         }
475                         printf("\n");
476                         break;
477                 case QUERY_PATH:
478                         printf("%s\n", udev_device_get_devpath(device));
479                         return 0;
480                 case QUERY_PROPERTY:
481                         list_entry = udev_device_get_properties_list_entry(device);
482                         while (list_entry != NULL) {
483                                 if (export) {
484                                         const char *prefix = export_prefix;
485
486                                         if (prefix == NULL)
487                                                 prefix = "";
488                                         printf("%s%s='%s'\n", prefix,
489                                                udev_list_entry_get_name(list_entry),
490                                                udev_list_entry_get_value(list_entry));
491                                 } else {
492                                         printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
493                                 }
494                                 list_entry = udev_list_entry_get_next(list_entry);
495                         }
496                         break;
497                 case QUERY_ALL:
498                         print_record(device);
499                         break;
500                 default:
501                         assert_not_reached("unknown query type");
502                 }
503                 break;
504         case ACTION_ATTRIBUTE_WALK:
505                 if (!device && argv[optind]) {
506                         device = find_device(udev, argv[optind], NULL);
507                         if (!device) {
508                                 fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n");
509                                 return 4;
510                         }
511                 }
512                 if (!device) {
513                         fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
514                         return 4;
515                 }
516                 print_device_chain(device);
517                 break;
518         case ACTION_DEVICE_ID_FILE:
519                 if (stat_device(name, export, export_prefix) != 0)
520                         return 1;
521                 break;
522         }
523
524         return 0;
525 }
526
527 const struct udevadm_cmd udevadm_info = {
528         .name = "info",
529         .cmd = uinfo,
530         .help = "query sysfs or the udev database",
531 };