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