chiark / gitweb /
keymap: Explain how to end the program
[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 void print_all_attributes(struct udev_device *device, const char *key)
35 {
36         struct udev *udev = udev_device_get_udev(device);
37         DIR *dir;
38         struct dirent *dent;
39
40         dir = opendir(udev_device_get_syspath(device));
41         if (dir != NULL) {
42                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
43                         struct stat statbuf;
44                         const char *value;
45                         size_t len;
46
47                         if (dent->d_name[0] == '.')
48                                 continue;
49
50                         if (strcmp(dent->d_name, "uevent") == 0)
51                                 continue;
52                         if (strcmp(dent->d_name, "dev") == 0)
53                                 continue;
54
55                         if (fstatat(dirfd(dir), dent->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) != 0)
56                                 continue;
57                         if (S_ISLNK(statbuf.st_mode))
58                                 continue;
59
60                         value = udev_device_get_sysattr_value(device, dent->d_name);
61                         if (value == NULL)
62                                 continue;
63                         dbg(udev, "attr '%s'='%s'\n", dent->d_name, value);
64
65                         /* skip nonprintable attributes */
66                         len = strlen(value);
67                         while (len > 0 && isprint(value[len-1]))
68                                 len--;
69                         if (len > 0) {
70                                 dbg(udev, "attribute value of '%s' non-printable, skip\n", dent->d_name);
71                                 continue;
72                         }
73
74                         printf("    %s{%s}==\"%s\"\n", key, dent->d_name, value);
75                 }
76                 closedir(dir);
77         }
78         printf("\n");
79 }
80
81 static int print_device_chain(struct udev_device *device)
82 {
83         struct udev_device *device_parent;
84         const char *str;
85
86         printf("\n"
87                "Udevadm info starts with the device specified by the devpath and then\n"
88                "walks up the chain of parent devices. It prints for every device\n"
89                "found, all possible attributes in the udev rules key format.\n"
90                "A rule to match, can be composed by the attributes of the device\n"
91                "and the attributes from one single parent device.\n"
92                "\n");
93
94         printf("  looking at device '%s':\n", udev_device_get_devpath(device));
95         printf("    KERNEL==\"%s\"\n", udev_device_get_sysname(device));
96         str = udev_device_get_subsystem(device);
97         if (str == NULL)
98                 str = "";
99         printf("    SUBSYSTEM==\"%s\"\n", str);
100         str = udev_device_get_driver(device);
101         if (str == NULL)
102                 str = "";
103         printf("    DRIVER==\"%s\"\n", str);
104         print_all_attributes(device, "ATTR");
105
106         device_parent = device;
107         do {
108                 device_parent = udev_device_get_parent(device_parent);
109                 if (device_parent == NULL)
110                         break;
111                 printf("  looking at parent device '%s':\n", udev_device_get_devpath(device_parent));
112                 printf("    KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent));
113                 str = udev_device_get_subsystem(device_parent);
114                 if (str == NULL)
115                         str = "";
116                 printf("    SUBSYSTEMS==\"%s\"\n", str);
117                 str = udev_device_get_driver(device_parent);
118                 if (str == NULL)
119                         str = "";
120                 printf("    DRIVERS==\"%s\"\n", str);
121                 print_all_attributes(device_parent, "ATTRS");
122         } while (device_parent != NULL);
123
124         return 0;
125 }
126
127 static void print_record(struct udev_device *device)
128 {
129         size_t len;
130         const char *str;
131         int i;
132         struct udev_list_entry *list_entry;
133
134         printf("P: %s\n", udev_device_get_devpath(device));
135
136         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
137         str = udev_device_get_devnode(device);
138         if (str != NULL)
139                 printf("N: %s\n", &str[len+1]);
140
141         i = udev_device_get_devlink_priority(device);
142         if (i != 0)
143                 printf("L: %i\n", i);
144
145         udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) {
146                 len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
147                 printf("S: %s\n", &udev_list_entry_get_name(list_entry)[len+1]);
148         }
149
150         udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
151                 printf("E: %s=%s\n",
152                        udev_list_entry_get_name(list_entry),
153                        udev_list_entry_get_value(list_entry));
154         printf("\n");
155 }
156
157 static int stat_device(const char *name, bool export, const char *prefix)
158 {
159         struct stat statbuf;
160
161         if (stat(name, &statbuf) != 0)
162                 return -1;
163
164         if (export) {
165                 if (prefix == NULL)
166                         prefix = "INFO_";
167                 printf("%sMAJOR=%d\n"
168                        "%sMINOR=%d\n",
169                        prefix, major(statbuf.st_dev),
170                        prefix, minor(statbuf.st_dev));
171         } else
172                 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
173         return 0;
174 }
175
176 static int export_devices(struct udev *udev)
177 {
178         struct udev_enumerate *udev_enumerate;
179         struct udev_list_entry *list_entry;
180
181         udev_enumerate = udev_enumerate_new(udev);
182         if (udev_enumerate == NULL)
183                 return -1;
184         udev_enumerate_scan_devices(udev_enumerate);
185         udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
186                 struct udev_device *device;
187
188                 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
189                 if (device != NULL) {
190                         print_record(device);
191                         udev_device_unref(device);
192                 }
193         }
194         udev_enumerate_unref(udev_enumerate);
195         return 0;
196 }
197
198 static int convert_db(struct udev *udev)
199 {
200         struct udev_enumerate *udev_enumerate;
201         struct udev_list_entry *list_entry;
202
203         udev_enumerate = udev_enumerate_new(udev);
204         if (udev_enumerate == NULL)
205                 return -1;
206         udev_enumerate_scan_devices(udev_enumerate);
207         udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
208                 struct udev_device *device;
209
210                 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
211                 if (device != NULL) {
212                         const char *id;
213                         struct stat stats;
214                         char to[UTIL_PATH_SIZE];
215                         char devpath[UTIL_PATH_SIZE];
216                         char from[UTIL_PATH_SIZE];
217
218                         id = udev_device_get_id_filename(device);
219                         if (id == NULL) {
220                                 udev_device_unref(device);
221                                 continue;
222                         }
223                         util_strscpyl(to, sizeof(to), udev_get_dev_path(udev), "/.udev/db/", id, NULL);
224
225                         /* find old database with $subsys:$sysname */
226                         util_strscpyl(from, sizeof(from), udev_get_dev_path(udev),
227                                      "/.udev/db/", udev_device_get_subsystem(device), ":",
228                                      udev_device_get_sysname(device), NULL);
229                         if (lstat(from, &stats) == 0) {
230                                 if (lstat(to, &stats) == 0)
231                                         unlink(from);
232                                 else
233                                         rename(from, to);
234                         }
235
236                         /* find old database with the encoded devpath */
237                         util_path_encode(udev_device_get_devpath(device), devpath, sizeof(devpath));
238                         util_strscpyl(from, sizeof(from), udev_get_dev_path(udev),
239                                       "/.udev/db/", devpath, NULL);
240                         if (lstat(from, &stats) == 0) {
241                                 if (lstat(to, &stats) == 0)
242                                         unlink(from);
243                                 else
244                                         rename(from, to);
245                         }
246
247                         /* read the old database, and write out a new one */
248                         udev_device_read_db(device);
249                         udev_device_update_db(device);
250
251                         udev_device_unref(device);
252                 }
253         }
254         udev_enumerate_unref(udev_enumerate);
255         return 0;
256 }
257
258 int udevadm_info(struct udev *udev, int argc, char *argv[])
259 {
260         struct udev_device *device = NULL;
261         bool root = 0;
262         bool export = 0;
263         const char *export_prefix = NULL;
264         char path[UTIL_PATH_SIZE];
265         char name[UTIL_PATH_SIZE];
266         struct udev_list_entry *list_entry;
267         int rc = 0;
268
269         static const struct option options[] = {
270                 { "name", required_argument, NULL, 'n' },
271                 { "path", required_argument, NULL, 'p' },
272                 { "query", required_argument, NULL, 'q' },
273                 { "attribute-walk", no_argument, NULL, 'a' },
274                 { "export-db", no_argument, NULL, 'e' },
275                 { "convert-db", no_argument, NULL, 'C' },
276                 { "root", no_argument, NULL, 'r' },
277                 { "device-id-of-file", required_argument, NULL, 'd' },
278                 { "export", no_argument, NULL, 'x' },
279                 { "export-prefix", required_argument, NULL, 'P' },
280                 { "version", no_argument, NULL, 'V' },
281                 { "help", no_argument, NULL, 'h' },
282                 {}
283         };
284
285         enum action_type {
286                 ACTION_NONE,
287                 ACTION_QUERY,
288                 ACTION_ATTRIBUTE_WALK,
289                 ACTION_ROOT,
290                 ACTION_DEVICE_ID_FILE,
291         } action = ACTION_NONE;
292
293         enum query_type {
294                 QUERY_NONE,
295                 QUERY_NAME,
296                 QUERY_PATH,
297                 QUERY_SYMLINK,
298                 QUERY_PROPERTY,
299                 QUERY_ALL,
300         } query = QUERY_NONE;
301
302         for (;;) {
303                 int option;
304                 struct stat statbuf;
305
306                 option = getopt_long(argc, argv, "aed:n:p:q:rxP:Vh", options, NULL);
307                 if (option == -1)
308                         break;
309
310                 dbg(udev, "option '%c'\n", option);
311                 switch (option) {
312                 case 'n':
313                         if (device != NULL) {
314                                 fprintf(stderr, "device already specified\n");
315                                 rc = 2;
316                                 goto exit;
317                         }
318                         /* remove /dev if given */
319                         if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) != 0)
320                                 util_strscpyl(name, sizeof(name), udev_get_dev_path(udev), "/", optarg, NULL);
321                         else
322                                 util_strscpy(name, sizeof(name), optarg);
323                         util_remove_trailing_chars(name, '/');
324                         if (stat(name, &statbuf) < 0) {
325                                 fprintf(stderr, "device node not found\n");
326                                 rc = 2;
327                                 goto exit;
328                         } else {
329                                 char type;
330
331                                 if (S_ISBLK(statbuf.st_mode)) {
332                                         type = 'b';
333                                 } else if (S_ISCHR(statbuf.st_mode)) {
334                                         type = 'c';
335                                 } else {
336                                         fprintf(stderr, "device node has wrong file type\n");
337                                         rc = 2;
338                                         goto exit;
339                                 }
340                                 device = udev_device_new_from_devnum(udev, type, statbuf.st_rdev);
341                                 if (device == NULL) {
342                                         fprintf(stderr, "device node not found\n");
343                                         rc = 2;
344                                         goto exit;
345                                 }
346                         }
347                         break;
348                 case 'p':
349                         if (device != NULL) {
350                                 fprintf(stderr, "device already specified\n");
351                                 rc = 2;
352                                 goto exit;
353                         }
354                         /* add sys dir if needed */
355                         if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0)
356                                 util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), optarg, NULL);
357                         else
358                                 util_strscpy(path, sizeof(path), optarg);
359                         util_remove_trailing_chars(path, '/');
360                         device = udev_device_new_from_syspath(udev, path);
361                         if (device == NULL) {
362                                 fprintf(stderr, "device path not found\n");
363                                 rc = 2;
364                                 goto exit;
365                         }
366                         break;
367                 case 'q':
368                         action = ACTION_QUERY;
369                         if (strcmp(optarg, "property") == 0 || strcmp(optarg, "env") == 0) {
370                                 query = QUERY_PROPERTY;
371                         } else if (strcmp(optarg, "name") == 0) {
372                                 query = QUERY_NAME;
373                         } else if (strcmp(optarg, "symlink") == 0) {
374                                 query = QUERY_SYMLINK;
375                         } else if (strcmp(optarg, "path") == 0) {
376                                 query = QUERY_PATH;
377                         } else if (strcmp(optarg, "all") == 0) {
378                                 query = QUERY_ALL;
379                         } else {
380                                 fprintf(stderr, "unknown query type\n");
381                                 rc = 3;
382                                 goto exit;
383                         }
384                         break;
385                 case 'r':
386                         if (action == ACTION_NONE)
387                                 action = ACTION_ROOT;
388                         root = true;
389                         break;
390                 case 'd':
391                         action = ACTION_DEVICE_ID_FILE;
392                         util_strscpy(name, sizeof(name), optarg);
393                         break;
394                 case 'a':
395                         action = ACTION_ATTRIBUTE_WALK;
396                         break;
397                 case 'e':
398                         export_devices(udev);
399                         goto exit;
400                 case 'C':
401                         convert_db(udev);
402                         goto exit;
403                 case 'x':
404                         export = true;
405                         break;
406                 case 'P':
407                         export_prefix = optarg;
408                         break;
409                 case 'V':
410                         printf("%s\n", VERSION);
411                         goto exit;
412                 case 'h':
413                         printf("Usage: udevadm info OPTIONS\n"
414                                "  --query=<type>             query device information:\n"
415                                "      name                     name of device node\n"
416                                "      symlink                  pointing to node\n"
417                                "      path                     sys device path\n"
418                                "      property                 the device properties\n"
419                                "      all                      all values\n"
420                                "  --path=<syspath>           sys device path used for query or attribute walk\n"
421                                "  --name=<name>              node or symlink name used for query or attribute walk\n"
422                                "  --root                     prepend dev directory to path names\n"
423                                "  --attribute-walk           print all key matches while walking along the chain\n"
424                                "                             of parent devices\n"
425                                "  --device-id-of-file=<file> print major:minor of device containing this file\n"
426                                "  --export                   export key/value pairs\n"
427                                "  --export-prefix            export the key name with a prefix\n"
428                                "  --export-db                export the content of the udev database\n"
429                                "  --convert-db               convert older version of database without a reboot\n"
430                                "  --help\n\n");
431                         goto exit;
432                 default:
433                         goto exit;
434                 }
435         }
436
437         switch (action) {
438         case ACTION_QUERY:
439                 if (device == NULL) {
440                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
441                         rc = 4;
442                         goto exit;
443                 }
444
445                 switch(query) {
446                 case QUERY_NAME: {
447                         const char *node = udev_device_get_devnode(device);
448
449                         if (node == NULL) {
450                                 fprintf(stderr, "no device node found\n");
451                                 rc = 5;
452                                 goto exit;
453                         }
454
455                         if (root) {
456                                 printf("%s\n", udev_device_get_devnode(device));
457                         } else {
458                                 size_t len = strlen(udev_get_dev_path(udev));
459
460                                 printf("%s\n", &udev_device_get_devnode(device)[len+1]);
461                         }
462                         break;
463                 }
464                 case QUERY_SYMLINK:
465                         list_entry = udev_device_get_devlinks_list_entry(device);
466                         while (list_entry != NULL) {
467                                 if (root) {
468                                         printf("%s", udev_list_entry_get_name(list_entry));
469                                 } else {
470                                         size_t len;
471
472                                         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
473                                         printf("%s", &udev_list_entry_get_name(list_entry)[len+1]);
474                                 }
475                                 list_entry = udev_list_entry_get_next(list_entry);
476                                 if (list_entry != NULL)
477                                         printf(" ");
478                         }
479                         printf("\n");
480                         break;
481                 case QUERY_PATH:
482                         printf("%s\n", udev_device_get_devpath(device));
483                         goto exit;
484                 case QUERY_PROPERTY:
485                         list_entry = udev_device_get_properties_list_entry(device);
486                         while (list_entry != NULL) {
487                                 if (export) {
488                                         const char *prefix = export_prefix;
489
490                                         if (prefix == NULL)
491                                                 prefix = "";
492                                         printf("%s%s='%s'\n", prefix,
493                                                udev_list_entry_get_name(list_entry),
494                                                udev_list_entry_get_value(list_entry));
495                                 } else {
496                                         printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
497                                 }
498                                 list_entry = udev_list_entry_get_next(list_entry);
499                         }
500                         break;
501                 case QUERY_ALL:
502                         print_record(device);
503                         break;
504                 default:
505                         fprintf(stderr, "unknown query type\n");
506                         break;
507                 }
508                 break;
509         case ACTION_ATTRIBUTE_WALK:
510                 if (device == NULL) {
511                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
512                         rc = 4;
513                         goto exit;
514                 }
515                 print_device_chain(device);
516                 break;
517         case ACTION_DEVICE_ID_FILE:
518                 if (stat_device(name, export, export_prefix) != 0)
519                         rc = 1;
520                 break;
521         case ACTION_ROOT:
522                 printf("%s\n", udev_get_dev_path(udev));
523                 break;
524         default:
525                 fprintf(stderr, "missing option\n");
526                 rc = 1;
527                 break;
528         }
529
530 exit:
531         udev_device_unref(device);
532         return rc;
533 }