chiark / gitweb /
udevadm,scsi_id: add short options to help strings and to the man page
[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, c;
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] [DEVPATH|FILE]\n"
325                 " -q,--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                 " -p,--path=SYSPATH           sys device path used for query or attribute walk\n"
332                 " -n,--name=NAME              node or symlink name used for query or attribute walk\n"
333                 " -r,--root                   prepend dev directory to path names\n"
334                 " -a,--attribute-walk         print all key matches walking along the chain\n"
335                 "                             of parent devices\n"
336                 " -d,--device-id-of-file=FILE print major:minor of device containing this file\n"
337                 " -x,--export                 export key/value pairs\n"
338                 " -P,--export-prefix          export the key name with a prefix\n"
339                 " -e,--export-db              export the content of the udev database\n"
340                 " -c,--cleanup-db             cleanup the udev database\n"
341                 "    --version                print version of the program\n"
342                 " -h,--help                   print this message\n";
343
344         enum action_type {
345                 ACTION_QUERY,
346                 ACTION_ATTRIBUTE_WALK,
347                 ACTION_DEVICE_ID_FILE,
348         } action = ACTION_QUERY;
349
350         enum query_type {
351                 QUERY_NAME,
352                 QUERY_PATH,
353                 QUERY_SYMLINK,
354                 QUERY_PROPERTY,
355                 QUERY_ALL,
356         } query = QUERY_ALL;
357
358         while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
359                 switch (c) {
360                 case 'n': {
361                         if (device != NULL) {
362                                 fprintf(stderr, "device already specified\n");
363                                 rc = 2;
364                                 goto exit;
365                         }
366
367                         device = find_device(udev, optarg, "/dev/");
368                         if (device == NULL) {
369                                 fprintf(stderr, "device node not found\n");
370                                 rc = 2;
371                                 goto exit;
372                         }
373                         break;
374                 }
375                 case 'p':
376                         if (device != NULL) {
377                                 fprintf(stderr, "device already specified\n");
378                                 rc = 2;
379                                 goto exit;
380                         }
381
382                         device = find_device(udev, optarg, "/sys");
383                         if (device == NULL) {
384                                 fprintf(stderr, "syspath not found\n");
385                                 rc = 2;
386                                 goto exit;
387                         }
388                         break;
389                 case 'q':
390                         action = ACTION_QUERY;
391                         if (streq(optarg, "property") || streq(optarg, "env")) {
392                                 query = QUERY_PROPERTY;
393                         } else if (streq(optarg, "name")) {
394                                 query = QUERY_NAME;
395                         } else if (streq(optarg, "symlink")) {
396                                 query = QUERY_SYMLINK;
397                         } else if (streq(optarg, "path")) {
398                                 query = QUERY_PATH;
399                         } else if (streq(optarg, "all")) {
400                                 query = QUERY_ALL;
401                         } else {
402                                 fprintf(stderr, "unknown query type\n");
403                                 rc = 3;
404                                 goto exit;
405                         }
406                         break;
407                 case 'r':
408                         root = true;
409                         break;
410                 case 'd':
411                         action = ACTION_DEVICE_ID_FILE;
412                         strscpy(name, sizeof(name), optarg);
413                         break;
414                 case 'a':
415                         action = ACTION_ATTRIBUTE_WALK;
416                         break;
417                 case 'e':
418                         export_devices(udev);
419                         goto exit;
420                 case 'c':
421                         cleanup_db(udev);
422                         goto exit;
423                 case 'x':
424                         export = true;
425                         break;
426                 case 'P':
427                         export_prefix = optarg;
428                         break;
429                 case 'V':
430                         printf("%s\n", VERSION);
431                         goto exit;
432                 case 'h':
433                         printf("%s\n", usage);
434                         goto exit;
435                 default:
436                         rc = 1;
437                         goto exit;
438                 }
439
440         switch (action) {
441         case ACTION_QUERY:
442                 if (!device) {
443                         if (!argv[optind]) {
444                                 fprintf(stderr, "%s\n", usage);
445                                 rc = 2;
446                                 goto exit;
447                         }
448                         device = find_device(udev, argv[optind], NULL);
449                         if (!device) {
450                                 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
451                                 rc = 4;
452                                 goto exit;
453                         }
454                 }
455
456                 switch(query) {
457                 case QUERY_NAME: {
458                         const char *node = udev_device_get_devnode(device);
459
460                         if (node == NULL) {
461                                 fprintf(stderr, "no device node found\n");
462                                 rc = 5;
463                                 goto exit;
464                         }
465
466                         if (root)
467                                 printf("%s\n", udev_device_get_devnode(device));
468                         else
469                                 printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/"));
470                         break;
471                 }
472                 case QUERY_SYMLINK:
473                         list_entry = udev_device_get_devlinks_list_entry(device);
474                         while (list_entry != NULL) {
475                                 if (root)
476                                         printf("%s", udev_list_entry_get_name(list_entry));
477                                 else
478                                         printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
479                                 list_entry = udev_list_entry_get_next(list_entry);
480                                 if (list_entry != NULL)
481                                         printf(" ");
482                         }
483                         printf("\n");
484                         break;
485                 case QUERY_PATH:
486                         printf("%s\n", udev_device_get_devpath(device));
487                         goto exit;
488                 case QUERY_PROPERTY:
489                         list_entry = udev_device_get_properties_list_entry(device);
490                         while (list_entry != NULL) {
491                                 if (export) {
492                                         const char *prefix = export_prefix;
493
494                                         if (prefix == NULL)
495                                                 prefix = "";
496                                         printf("%s%s='%s'\n", prefix,
497                                                udev_list_entry_get_name(list_entry),
498                                                udev_list_entry_get_value(list_entry));
499                                 } else {
500                                         printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
501                                 }
502                                 list_entry = udev_list_entry_get_next(list_entry);
503                         }
504                         break;
505                 case QUERY_ALL:
506                         print_record(device);
507                         break;
508                 default:
509                         fprintf(stderr, "unknown query type\n");
510                         break;
511                 }
512                 break;
513         case ACTION_ATTRIBUTE_WALK:
514                 if (!device && argv[optind]) {
515                         device = find_device(udev, argv[optind], NULL);
516                         if (!device) {
517                                 fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n");
518                                 rc = 4;
519                                 goto exit;
520                         }
521                 }
522                 if (!device) {
523                         fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
524                         rc = 4;
525                         goto exit;
526                 }
527                 print_device_chain(device);
528                 break;
529         case ACTION_DEVICE_ID_FILE:
530                 if (stat_device(name, export, export_prefix) != 0)
531                         rc = 1;
532                 break;
533         }
534
535 exit:
536         udev_device_unref(device);
537         return rc;
538 }
539
540 const struct udevadm_cmd udevadm_info = {
541         .name = "info",
542         .cmd = uinfo,
543         .help = "query sysfs or the udev database",
544 };