chiark / gitweb /
keymap: Add Logitech USB (iTouch)
[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/watch");
255         if (dir != NULL) {
256                 cleanup_dir(dir, 0, 1);
257                 closedir(dir);
258         }
259 }
260
261 static struct udev_device *find_device(struct udev *udev, const char *id, const char *prefix)
262 {
263         char name[UTIL_PATH_SIZE];
264
265         if (prefix && !startswith(id, prefix)) {
266                 strscpyl(name, sizeof(name), prefix, id, NULL);
267                 id = name;
268         }
269
270         if (startswith(id, "/dev/")) {
271                 struct stat statbuf;
272                 char type;
273
274                 if (stat(id, &statbuf) < 0)
275                         return NULL;
276
277                 if (S_ISBLK(statbuf.st_mode))
278                         type = 'b';
279                 else if (S_ISCHR(statbuf.st_mode))
280                         type = 'c';
281                 else
282                         return NULL;
283
284                 return udev_device_new_from_devnum(udev, type, statbuf.st_rdev);
285         } else if (startswith(id, "/sys/"))
286                 return udev_device_new_from_syspath(udev, id);
287         else
288                 return NULL;
289 }
290
291 static int uinfo(struct udev *udev, int argc, char *argv[])
292 {
293         struct udev_device *device = NULL;
294         bool root = 0;
295         bool export = 0;
296         const char *export_prefix = NULL;
297         char name[UTIL_PATH_SIZE];
298         struct udev_list_entry *list_entry;
299         int rc = 0;
300
301         static const struct option options[] = {
302                 { "name", required_argument, NULL, 'n' },
303                 { "path", required_argument, NULL, 'p' },
304                 { "query", required_argument, NULL, 'q' },
305                 { "attribute-walk", no_argument, NULL, 'a' },
306                 { "cleanup-db", no_argument, NULL, 'c' },
307                 { "export-db", no_argument, NULL, 'e' },
308                 { "root", no_argument, NULL, 'r' },
309                 { "device-id-of-file", required_argument, NULL, 'd' },
310                 { "export", no_argument, NULL, 'x' },
311                 { "export-prefix", required_argument, NULL, 'P' },
312                 { "version", no_argument, NULL, 'V' },
313                 { "help", no_argument, NULL, 'h' },
314                 {}
315         };
316
317         static const char *usage =
318                 "Usage: udevadm info OPTIONS\n"
319                 "  --query=<type>             query device information:\n"
320                 "      name                     name of device node\n"
321                 "      symlink                  pointing to node\n"
322                 "      path                     sys device path\n"
323                 "      property                 the device properties\n"
324                 "      all                      all values\n"
325                 "  --path=<syspath>           sys device path used for query or attribute walk\n"
326                 "  --name=<name>              node or symlink name used for query or attribute walk\n"
327                 "  --root                     prepend dev directory to path names\n"
328                 "  --attribute-walk           print all key matches while walking along the chain\n"
329                 "                             of parent devices\n"
330                 "  --device-id-of-file=<file> print major:minor of device containing this file\n"
331                 "  --export                   export key/value pairs\n"
332                 "  --export-prefix            export the key name with a prefix\n"
333                 "  --export-db                export the content of the udev database\n"
334                 "  --cleanup-db               cleanup the udev database\n"
335                 "  --help\n";
336
337         enum action_type {
338                 ACTION_QUERY,
339                 ACTION_ATTRIBUTE_WALK,
340                 ACTION_DEVICE_ID_FILE,
341         } action = ACTION_QUERY;
342
343         enum query_type {
344                 QUERY_NAME,
345                 QUERY_PATH,
346                 QUERY_SYMLINK,
347                 QUERY_PROPERTY,
348                 QUERY_ALL,
349         } query = QUERY_ALL;
350
351         for (;;) {
352                 int option;
353
354                 option = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL);
355                 if (option == -1)
356                         break;
357
358                 switch (option) {
359                 case 'n': {
360                         if (device != NULL) {
361                                 fprintf(stderr, "device already specified\n");
362                                 rc = 2;
363                                 goto exit;
364                         }
365
366                         device = find_device(udev, optarg, "/dev/");
367                         if (device == NULL) {
368                                 fprintf(stderr, "device node not found\n");
369                                 rc = 2;
370                                 goto exit;
371                         }
372                         break;
373                 }
374                 case 'p':
375                         if (device != NULL) {
376                                 fprintf(stderr, "device already specified\n");
377                                 rc = 2;
378                                 goto exit;
379                         }
380
381                         device = find_device(udev, optarg, "/sys");
382                         if (device == NULL) {
383                                 fprintf(stderr, "syspath not found\n");
384                                 rc = 2;
385                                 goto exit;
386                         }
387                         break;
388                 case 'q':
389                         action = ACTION_QUERY;
390                         if (streq(optarg, "property") || streq(optarg, "env")) {
391                                 query = QUERY_PROPERTY;
392                         } else if (streq(optarg, "name")) {
393                                 query = QUERY_NAME;
394                         } else if (streq(optarg, "symlink")) {
395                                 query = QUERY_SYMLINK;
396                         } else if (streq(optarg, "path")) {
397                                 query = QUERY_PATH;
398                         } else if (streq(optarg, "all")) {
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                         root = true;
408                         break;
409                 case 'd':
410                         action = ACTION_DEVICE_ID_FILE;
411                         strscpy(name, sizeof(name), optarg);
412                         break;
413                 case 'a':
414                         action = ACTION_ATTRIBUTE_WALK;
415                         break;
416                 case 'e':
417                         export_devices(udev);
418                         goto exit;
419                 case 'c':
420                         cleanup_db(udev);
421                         goto exit;
422                 case 'x':
423                         export = true;
424                         break;
425                 case 'P':
426                         export_prefix = optarg;
427                         break;
428                 case 'V':
429                         printf("%s\n", VERSION);
430                         goto exit;
431                 case 'h':
432                         printf("%s\n", usage);
433                         goto exit;
434                 default:
435                         rc = 1;
436                         goto exit;
437                 }
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 };