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