chiark / gitweb /
udev: use startswith() and streq()
[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 < ELEMENTSOF(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                 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/watch");
255         if (dir != NULL) {
256                 cleanup_dir(dir, 0, 1);
257                 closedir(dir);
258         }
259
260         dir = opendir("/run/udev/firmware-missing");
261         if (dir != NULL) {
262                 cleanup_dir(dir, 0, 1);
263                 closedir(dir);
264         }
265 }
266
267 static int uinfo(struct udev *udev, int argc, char *argv[])
268 {
269         struct udev_device *device = NULL;
270         bool root = 0;
271         bool export = 0;
272         const char *export_prefix = NULL;
273         char path[UTIL_PATH_SIZE];
274         char name[UTIL_PATH_SIZE];
275         struct udev_list_entry *list_entry;
276         int rc = 0;
277
278         static const struct option options[] = {
279                 { "name", required_argument, NULL, 'n' },
280                 { "path", required_argument, NULL, 'p' },
281                 { "query", required_argument, NULL, 'q' },
282                 { "attribute-walk", no_argument, NULL, 'a' },
283                 { "cleanup-db", no_argument, NULL, 'c' },
284                 { "export-db", no_argument, NULL, 'e' },
285                 { "root", no_argument, NULL, 'r' },
286                 { "run", no_argument, NULL, 'R' },
287                 { "device-id-of-file", required_argument, NULL, 'd' },
288                 { "export", no_argument, NULL, 'x' },
289                 { "export-prefix", required_argument, NULL, 'P' },
290                 { "version", no_argument, NULL, 'V' },
291                 { "help", no_argument, NULL, 'h' },
292                 {}
293         };
294
295         enum action_type {
296                 ACTION_NONE,
297                 ACTION_QUERY,
298                 ACTION_ATTRIBUTE_WALK,
299                 ACTION_ROOT,
300                 ACTION_DEVICE_ID_FILE,
301         } action = ACTION_NONE;
302
303         enum query_type {
304                 QUERY_NONE,
305                 QUERY_NAME,
306                 QUERY_PATH,
307                 QUERY_SYMLINK,
308                 QUERY_PROPERTY,
309                 QUERY_ALL,
310         } query = QUERY_NONE;
311
312         for (;;) {
313                 int option;
314                 struct stat statbuf;
315
316                 option = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL);
317                 if (option == -1)
318                         break;
319
320                 switch (option) {
321                 case 'n':
322                         if (device != NULL) {
323                                 fprintf(stderr, "device already specified\n");
324                                 rc = 2;
325                                 goto exit;
326                         }
327                         /* add /dev if not given */
328                         if (!startswith(optarg, "/dev"))
329                                 util_strscpyl(name, sizeof(name), "/dev/", optarg, NULL);
330                         else
331                                 util_strscpy(name, sizeof(name), optarg);
332                         util_remove_trailing_chars(name, '/');
333                         if (stat(name, &statbuf) < 0) {
334                                 fprintf(stderr, "device node not found\n");
335                                 rc = 2;
336                                 goto exit;
337                         } else {
338                                 char type;
339
340                                 if (S_ISBLK(statbuf.st_mode)) {
341                                         type = 'b';
342                                 } else if (S_ISCHR(statbuf.st_mode)) {
343                                         type = 'c';
344                                 } else {
345                                         fprintf(stderr, "device node has wrong file type\n");
346                                         rc = 2;
347                                         goto exit;
348                                 }
349                                 device = udev_device_new_from_devnum(udev, type, statbuf.st_rdev);
350                                 if (device == NULL) {
351                                         fprintf(stderr, "device node not found\n");
352                                         rc = 2;
353                                         goto exit;
354                                 }
355                         }
356                         break;
357                 case 'p':
358                         if (device != NULL) {
359                                 fprintf(stderr, "device already specified\n");
360                                 rc = 2;
361                                 goto exit;
362                         }
363                         /* add sys dir if needed */
364                         if (!startswith(optarg, "/sys"))
365                                 util_strscpyl(path, sizeof(path), "/sys", optarg, NULL);
366                         else
367                                 util_strscpy(path, sizeof(path), optarg);
368                         util_remove_trailing_chars(path, '/');
369                         device = udev_device_new_from_syspath(udev, path);
370                         if (device == NULL) {
371                                 fprintf(stderr, "device path not found\n");
372                                 rc = 2;
373                                 goto exit;
374                         }
375                         break;
376                 case 'q':
377                         action = ACTION_QUERY;
378                         if (strcmp(optarg, "property") == 0 || strcmp(optarg, "env") == 0) {
379                                 query = QUERY_PROPERTY;
380                         } else if (strcmp(optarg, "name") == 0) {
381                                 query = QUERY_NAME;
382                         } else if (strcmp(optarg, "symlink") == 0) {
383                                 query = QUERY_SYMLINK;
384                         } else if (strcmp(optarg, "path") == 0) {
385                                 query = QUERY_PATH;
386                         } else if (strcmp(optarg, "all") == 0) {
387                                 query = QUERY_ALL;
388                         } else {
389                                 fprintf(stderr, "unknown query type\n");
390                                 rc = 3;
391                                 goto exit;
392                         }
393                         break;
394                 case 'r':
395                         if (action == ACTION_NONE)
396                                 action = ACTION_ROOT;
397                         root = true;
398                         break;
399                 case 'R':
400                         printf("/run/udev\n");
401                         goto exit;
402                 case 'd':
403                         action = ACTION_DEVICE_ID_FILE;
404                         util_strscpy(name, sizeof(name), optarg);
405                         break;
406                 case 'a':
407                         action = ACTION_ATTRIBUTE_WALK;
408                         break;
409                 case 'e':
410                         export_devices(udev);
411                         goto exit;
412                 case 'c':
413                         cleanup_db(udev);
414                         goto exit;
415                 case 'x':
416                         export = true;
417                         break;
418                 case 'P':
419                         export_prefix = optarg;
420                         break;
421                 case 'V':
422                         printf("%s\n", VERSION);
423                         goto exit;
424                 case 'h':
425                         printf("Usage: udevadm info OPTIONS\n"
426                                "  --query=<type>             query device information:\n"
427                                "      name                     name of device node\n"
428                                "      symlink                  pointing to node\n"
429                                "      path                     sys device path\n"
430                                "      property                 the device properties\n"
431                                "      all                      all values\n"
432                                "  --path=<syspath>           sys device path used for query or attribute walk\n"
433                                "  --name=<name>              node or symlink name used for query or attribute walk\n"
434                                "  --root                     prepend dev directory to path names\n"
435                                "  --attribute-walk           print all key matches while walking along the chain\n"
436                                "                             of parent devices\n"
437                                "  --device-id-of-file=<file> print major:minor of device containing this file\n"
438                                "  --export                   export key/value pairs\n"
439                                "  --export-prefix            export the key name with a prefix\n"
440                                "  --export-db                export the content of the udev database\n"
441                                "  --cleanup-db               cleanup the udev database\n"
442                                "  --help\n\n");
443                         goto exit;
444                 default:
445                         rc = 1;
446                         goto exit;
447                 }
448         }
449
450         switch (action) {
451         case ACTION_QUERY:
452                 if (device == NULL) {
453                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
454                         rc = 4;
455                         goto exit;
456                 }
457
458                 switch(query) {
459                 case QUERY_NAME: {
460                         const char *node = udev_device_get_devnode(device);
461
462                         if (node == NULL) {
463                                 fprintf(stderr, "no device node found\n");
464                                 rc = 5;
465                                 goto exit;
466                         }
467
468                         if (root)
469                                 printf("%s\n", udev_device_get_devnode(device));
470                         else
471                                 printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/"));
472                         break;
473                 }
474                 case QUERY_SYMLINK:
475                         list_entry = udev_device_get_devlinks_list_entry(device);
476                         while (list_entry != NULL) {
477                                 if (root)
478                                         printf("%s", udev_list_entry_get_name(list_entry));
479                                 else
480                                         printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
481                                 list_entry = udev_list_entry_get_next(list_entry);
482                                 if (list_entry != NULL)
483                                         printf(" ");
484                         }
485                         printf("\n");
486                         break;
487                 case QUERY_PATH:
488                         printf("%s\n", udev_device_get_devpath(device));
489                         goto exit;
490                 case QUERY_PROPERTY:
491                         list_entry = udev_device_get_properties_list_entry(device);
492                         while (list_entry != NULL) {
493                                 if (export) {
494                                         const char *prefix = export_prefix;
495
496                                         if (prefix == NULL)
497                                                 prefix = "";
498                                         printf("%s%s='%s'\n", prefix,
499                                                udev_list_entry_get_name(list_entry),
500                                                udev_list_entry_get_value(list_entry));
501                                 } else {
502                                         printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
503                                 }
504                                 list_entry = udev_list_entry_get_next(list_entry);
505                         }
506                         break;
507                 case QUERY_ALL:
508                         print_record(device);
509                         break;
510                 default:
511                         fprintf(stderr, "unknown query type\n");
512                         break;
513                 }
514                 break;
515         case ACTION_ATTRIBUTE_WALK:
516                 if (device == NULL) {
517                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
518                         rc = 4;
519                         goto exit;
520                 }
521                 print_device_chain(device);
522                 break;
523         case ACTION_DEVICE_ID_FILE:
524                 if (stat_device(name, export, export_prefix) != 0)
525                         rc = 1;
526                 break;
527         case ACTION_ROOT:
528                 printf("/dev\n");
529                 break;
530         default:
531                 fprintf(stderr, "missing option\n");
532                 rc = 1;
533                 break;
534         }
535
536 exit:
537         udev_device_unref(device);
538         return rc;
539 }
540
541 const struct udevadm_cmd udevadm_info = {
542         .name = "info",
543         .cmd = uinfo,
544         .help = "query sysfs or the udev database",
545 };