chiark / gitweb /
udevd: clarify worker exit status
[elogind.git] / 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_list_entry *sysattr;
56
57         udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) {
58                 struct udev *udev = udev_device_get_udev(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 nonprintable attributes */
73                 len = strlen(value);
74                 while (len > 0 && isprint(value[len-1]))
75                         len--;
76                 if (len > 0) {
77                         dbg(udev, "attribute value of '%s' non-printable, skip\n", name);
78                         continue;
79                 }
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         size_t len;
135         const char *str;
136         int i;
137         struct udev_list_entry *list_entry;
138
139         printf("P: %s\n", udev_device_get_devpath(device));
140
141         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
142         str = udev_device_get_devnode(device);
143         if (str != NULL)
144                 printf("N: %s\n", &str[len+1]);
145
146         i = udev_device_get_devlink_priority(device);
147         if (i != 0)
148                 printf("L: %i\n", i);
149
150         udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) {
151                 len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
152                 printf("S: %s\n", &udev_list_entry_get_name(list_entry)[len+1]);
153         }
154
155         udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
156                 printf("E: %s=%s\n",
157                        udev_list_entry_get_name(list_entry),
158                        udev_list_entry_get_value(list_entry));
159         printf("\n");
160 }
161
162 static int stat_device(const char *name, bool export, const char *prefix)
163 {
164         struct stat statbuf;
165
166         if (stat(name, &statbuf) != 0)
167                 return -1;
168
169         if (export) {
170                 if (prefix == NULL)
171                         prefix = "INFO_";
172                 printf("%sMAJOR=%d\n"
173                        "%sMINOR=%d\n",
174                        prefix, major(statbuf.st_dev),
175                        prefix, minor(statbuf.st_dev));
176         } else
177                 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
178         return 0;
179 }
180
181 static int export_devices(struct udev *udev)
182 {
183         struct udev_enumerate *udev_enumerate;
184         struct udev_list_entry *list_entry;
185
186         udev_enumerate = udev_enumerate_new(udev);
187         if (udev_enumerate == NULL)
188                 return -1;
189         udev_enumerate_scan_devices(udev_enumerate);
190         udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
191                 struct udev_device *device;
192
193                 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
194                 if (device != NULL) {
195                         print_record(device);
196                         udev_device_unref(device);
197                 }
198         }
199         udev_enumerate_unref(udev_enumerate);
200         return 0;
201 }
202
203 static void cleanup_dir(DIR *dir, mode_t mask, int depth)
204 {
205         struct dirent *dent;
206
207         if (depth <= 0)
208                 return;
209
210         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
211                 struct stat stats;
212
213                 if (dent->d_name[0] == '.')
214                         continue;
215                 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
216                         continue;
217                 if ((stats.st_mode & mask) != 0)
218                         continue;
219                 if (S_ISDIR(stats.st_mode)) {
220                         DIR *dir2;
221
222                         dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
223                         if (dir2 != NULL) {
224                                 cleanup_dir(dir2, mask, depth-1);
225                                 closedir(dir2);
226                         }
227                         unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
228                 } else {
229                         unlinkat(dirfd(dir), dent->d_name, 0);
230                 }
231         }
232 }
233
234 static void cleanup_db(struct udev *udev)
235 {
236         char filename[UTIL_PATH_SIZE];
237         DIR *dir;
238
239         util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/queue.bin", NULL);
240         unlink(filename);
241
242         util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data", NULL);
243         dir = opendir(filename);
244         if (dir != NULL) {
245                 cleanup_dir(dir, S_ISVTX, 1);
246                 closedir(dir);
247         }
248
249         util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/links", NULL);
250         dir = opendir(filename);
251         if (dir != NULL) {
252                 cleanup_dir(dir, 0, 2);
253                 closedir(dir);
254         }
255
256         util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/tags", NULL);
257         dir = opendir(filename);
258         if (dir != NULL) {
259                 cleanup_dir(dir, 0, 2);
260                 closedir(dir);
261         }
262
263         util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/watch", NULL);
264         dir = opendir(filename);
265         if (dir != NULL) {
266                 cleanup_dir(dir, 0, 1);
267                 closedir(dir);
268         }
269
270         util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/firmware-missing", NULL);
271         dir = opendir(filename);
272         if (dir != NULL) {
273                 cleanup_dir(dir, 0, 1);
274                 closedir(dir);
275         }
276 }
277
278 int udevadm_info(struct udev *udev, int argc, char *argv[])
279 {
280         struct udev_device *device = NULL;
281         bool root = 0;
282         bool export = 0;
283         const char *export_prefix = NULL;
284         char path[UTIL_PATH_SIZE];
285         char name[UTIL_PATH_SIZE];
286         struct udev_list_entry *list_entry;
287         int rc = 0;
288
289         static const struct option options[] = {
290                 { "name", required_argument, NULL, 'n' },
291                 { "path", required_argument, NULL, 'p' },
292                 { "query", required_argument, NULL, 'q' },
293                 { "attribute-walk", no_argument, NULL, 'a' },
294                 { "cleanup-db", no_argument, NULL, 'c' },
295                 { "export-db", no_argument, NULL, 'e' },
296                 { "root", no_argument, NULL, 'r' },
297                 { "run", no_argument, NULL, 'R' },
298                 { "device-id-of-file", required_argument, NULL, 'd' },
299                 { "export", no_argument, NULL, 'x' },
300                 { "export-prefix", required_argument, NULL, 'P' },
301                 { "version", no_argument, NULL, 'V' },
302                 { "help", no_argument, NULL, 'h' },
303                 {}
304         };
305
306         enum action_type {
307                 ACTION_NONE,
308                 ACTION_QUERY,
309                 ACTION_ATTRIBUTE_WALK,
310                 ACTION_ROOT,
311                 ACTION_DEVICE_ID_FILE,
312         } action = ACTION_NONE;
313
314         enum query_type {
315                 QUERY_NONE,
316                 QUERY_NAME,
317                 QUERY_PATH,
318                 QUERY_SYMLINK,
319                 QUERY_PROPERTY,
320                 QUERY_ALL,
321         } query = QUERY_NONE;
322
323         for (;;) {
324                 int option;
325                 struct stat statbuf;
326
327                 option = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL);
328                 if (option == -1)
329                         break;
330
331                 dbg(udev, "option '%c'\n", option);
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                         goto exit;
458                 }
459         }
460
461         switch (action) {
462         case ACTION_QUERY:
463                 if (device == NULL) {
464                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
465                         rc = 4;
466                         goto exit;
467                 }
468
469                 switch(query) {
470                 case QUERY_NAME: {
471                         const char *node = udev_device_get_devnode(device);
472
473                         if (node == NULL) {
474                                 fprintf(stderr, "no device node found\n");
475                                 rc = 5;
476                                 goto exit;
477                         }
478
479                         if (root) {
480                                 printf("%s\n", udev_device_get_devnode(device));
481                         } else {
482                                 size_t len = strlen(udev_get_dev_path(udev));
483
484                                 printf("%s\n", &udev_device_get_devnode(device)[len+1]);
485                         }
486                         break;
487                 }
488                 case QUERY_SYMLINK:
489                         list_entry = udev_device_get_devlinks_list_entry(device);
490                         while (list_entry != NULL) {
491                                 if (root) {
492                                         printf("%s", udev_list_entry_get_name(list_entry));
493                                 } else {
494                                         size_t len;
495
496                                         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
497                                         printf("%s", &udev_list_entry_get_name(list_entry)[len+1]);
498                                 }
499                                 list_entry = udev_list_entry_get_next(list_entry);
500                                 if (list_entry != NULL)
501                                         printf(" ");
502                         }
503                         printf("\n");
504                         break;
505                 case QUERY_PATH:
506                         printf("%s\n", udev_device_get_devpath(device));
507                         goto exit;
508                 case QUERY_PROPERTY:
509                         list_entry = udev_device_get_properties_list_entry(device);
510                         while (list_entry != NULL) {
511                                 if (export) {
512                                         const char *prefix = export_prefix;
513
514                                         if (prefix == NULL)
515                                                 prefix = "";
516                                         printf("%s%s='%s'\n", prefix,
517                                                udev_list_entry_get_name(list_entry),
518                                                udev_list_entry_get_value(list_entry));
519                                 } else {
520                                         printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
521                                 }
522                                 list_entry = udev_list_entry_get_next(list_entry);
523                         }
524                         break;
525                 case QUERY_ALL:
526                         print_record(device);
527                         break;
528                 default:
529                         fprintf(stderr, "unknown query type\n");
530                         break;
531                 }
532                 break;
533         case ACTION_ATTRIBUTE_WALK:
534                 if (device == NULL) {
535                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
536                         rc = 4;
537                         goto exit;
538                 }
539                 print_device_chain(device);
540                 break;
541         case ACTION_DEVICE_ID_FILE:
542                 if (stat_device(name, export, export_prefix) != 0)
543                         rc = 1;
544                 break;
545         case ACTION_ROOT:
546                 printf("%s\n", udev_get_dev_path(udev));
547                 break;
548         default:
549                 fprintf(stderr, "missing option\n");
550                 rc = 1;
551                 break;
552         }
553
554 exit:
555         udev_device_unref(device);
556         return rc;
557 }