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