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