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