chiark / gitweb /
9ce83aabd884abecd04ab769b08b86180141504c
[elogind.git] / udev / udevadm-info.c
1 /*
2  * Copyright (C) 2004-2008 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 "config.h"
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include <stddef.h>
24 #include <ctype.h>
25 #include <stdarg.h>
26 #include <unistd.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #include <getopt.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32
33 #include "udev.h"
34
35 static void print_all_attributes(struct udev_device *device, const char *key)
36 {
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                         char filename[UTIL_PATH_SIZE];
45                         const char *value;
46                         size_t len;
47
48                         if (dent->d_name[0] == '.')
49                                 continue;
50
51                         if (strcmp(dent->d_name, "uevent") == 0)
52                                 continue;
53                         if (strcmp(dent->d_name, "dev") == 0)
54                                 continue;
55
56                         util_strlcpy(filename, udev_device_get_syspath(device), sizeof(filename));
57                         util_strlcat(filename, "/", sizeof(filename));
58                         util_strlcat(filename, dent->d_name, sizeof(filename));
59                         if (lstat(filename, &statbuf) != 0)
60                                 continue;
61                         if (S_ISLNK(statbuf.st_mode))
62                                 continue;
63
64                         value = udev_device_get_attr_value(device, dent->d_name);
65                         if (value == NULL)
66                                 continue;
67                         dbg(udev, "attr '%s'='%s'(%zi)\n", dent->d_name, value, len);
68
69                         /* skip nonprintable attributes */
70                         len = strlen(value);
71                         while (len > 0 && isprint(value[len-1]))
72                                 len--;
73                         if (len > 0) {
74                                 dbg(info, "attribute value of '%s' non-printable, skip\n", dent->d_name);
75                                 continue;
76                         }
77
78                         printf("    %s{%s}==\"%s\"\n", key, dent->d_name, value);
79                 }
80                 closedir(dir);
81         }
82         printf("\n");
83 }
84
85 static int print_device_chain(struct udev_device *device)
86 {
87         struct udev_device *device_parent;
88         const char *str;
89
90         printf("\n"
91                "Udevinfo starts with the device specified by the devpath and then\n"
92                "walks up the chain of parent devices. It prints for every device\n"
93                "found, all possible attributes in the udev rules key format.\n"
94                "A rule to match, can be composed by the attributes of the device\n"
95                "and the attributes from one single parent device.\n"
96                "\n");
97
98         printf("  looking at device '%s':\n", udev_device_get_devpath(device));
99         printf("    KERNEL==\"%s\"\n", udev_device_get_sysname(device));
100         str = udev_device_get_subsystem(device);
101         if (str == NULL)
102                 str = "";
103         printf("    SUBSYSTEM==\"%s\"\n", str);
104         str = udev_device_get_driver(device);
105         if (str == NULL)
106                 str = "";
107         printf("    DRIVER==\"%s\"\n", str);
108         print_all_attributes(device, "ATTR");
109
110         device_parent = device;
111         do {
112                 device_parent = udev_device_get_parent(device_parent);
113                 if (device_parent == NULL)
114                         break;
115                 printf("  looking at parent device '%s':\n", udev_device_get_devpath(device_parent));
116                 printf("    KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent));
117                 str = udev_device_get_subsystem(device_parent);
118                 if (str == NULL)
119                         str = "";
120                 printf("    SUBSYSTEMS==\"%s\"\n", str);
121                 str = udev_device_get_driver(device_parent);
122                 if (str == NULL)
123                         str = "";
124                 printf("    DRIVERS==\"%s\"\n", str);
125                 print_all_attributes(device_parent, "ATTRS");
126         } while (device_parent != NULL);
127
128         return 0;
129 }
130
131 static int print_record_devlinks_cb(struct udev_device *device, const char *value, void *data)
132 {
133         size_t len;
134
135         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
136         printf("S: %s\n", &value[len+1]);
137         return 0;
138 }
139
140 static int print_record_properties_cb(struct udev_device *device, const char *key, const char *value, void *data)
141 {
142         printf("E: %s=%s\n", key, value);
143         return 0;
144 }
145
146 static void print_record(struct udev_device *device)
147 {
148         size_t len;
149         int i;
150
151         printf("P: %s\n", udev_device_get_devpath(device));
152         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
153         printf("N: %s\n", &udev_device_get_devname(device)[len+1]);
154         i = device_get_devlink_priority(device);
155         if (i != 0)
156                 printf("L: %i\n", i);
157         i = device_get_num_fake_partitions(device);
158         if (i != 0)
159                 printf("A:%u\n", i);
160         i = device_get_ignore_remove(device);
161         if (i != 0)
162                 printf("R:%u\n", i);
163         udev_device_get_devlinks(device, print_record_devlinks_cb, NULL);
164         udev_device_get_properties(device, print_record_properties_cb, NULL);
165         printf("\n");
166 }
167
168 static int export_all_cb(struct udev *udev,
169                          const char *syspath, const char *subsystem, const char *name,
170                          void *data)
171 {
172         struct udev_device *device;
173
174         device = udev_device_new_from_syspath(udev, syspath);
175         if (device == NULL)
176                 return 0;
177         if (udev_device_get_devname(device) != NULL)
178                 print_record(device);
179         udev_device_unref(device);
180         return 0;
181 }
182
183 static struct udev_device *lookup_device_by_name(struct udev *udev, const char *name)
184 {
185         struct udev_device *udev_device = NULL;
186         LIST_HEAD(name_list);
187         int count;
188         struct name_entry *device;
189
190         count = udev_db_get_devices_by_name(udev, name, &name_list);
191         if (count <= 0)
192                 goto out;
193
194         info(udev, "found %i devices for '%s'\n", count, name);
195
196         /* select the device that matches */
197         list_for_each_entry(device, &name_list, node) {
198                 struct udevice *udevice_loop;
199                 char filename[UTIL_PATH_SIZE];
200                 struct stat statbuf;
201
202                 udevice_loop = udev_device_init(udev);
203                 if (udevice_loop == NULL)
204                         break;
205                 if (udev_db_get_device(udevice_loop, device->name) != 0)
206                         goto next;
207                 info(udev, "found db entry '%s'\n", device->name);
208                 /* make sure, we don't get a link of a different device */
209                 util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
210                 util_strlcat(filename, "/", sizeof(filename));
211                 util_strlcat(filename, name, sizeof(filename));
212                 if (stat(filename, &statbuf) != 0)
213                         goto next;
214                 if (major(udevice_loop->devt) > 0 && udevice_loop->devt != statbuf.st_rdev) {
215                         info(udev, "skip '%s', dev_t doesn't match\n", udevice_loop->name);
216                         goto next;
217                 }
218                 util_strlcpy(filename, udev_get_sys_path(udev), sizeof(filename));
219                 util_strlcat(filename,  udevice_loop->dev->devpath, sizeof(filename));
220                 udev_device = udev_device_new_from_syspath(udev, filename);
221                 udev_device_cleanup(udevice_loop);
222                 break;
223 next:
224                 udev_device_cleanup(udevice_loop);
225         }
226 out:
227         name_list_cleanup(udev, &name_list);
228         return udev_device;
229 }
230
231 static int add_devlink_cb(struct udev_device *device, const char *value, void *data)
232 {
233         char **links = data;
234
235         if (*links == NULL) {
236                 *links = strdup(value);
237         } else {
238                 char *str;
239
240                 asprintf(&str, "%s %s", *links, value);
241                 free(*links);
242                 *links = str;
243         }
244         return 0;
245 }
246
247 static int add_devlink_noroot_cb(struct udev_device *device, const char *value, void *data)
248 {
249         size_t len;
250
251         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
252         value = &value[len+1];
253         return add_devlink_cb(device, value, data);
254 }
255
256 static int print_property_cb(struct udev_device *device, const char *key, const char *value, void *data)
257 {
258         printf("%s=%s\n", key, value);
259         return 0;
260 }
261
262 static int stat_device(const char *name, int export, const char *prefix)
263 {
264         struct stat statbuf;
265
266         if (stat(name, &statbuf) != 0)
267                 return -1;
268
269         if (export) {
270                 if (prefix == NULL)
271                         prefix = "INFO_";
272                 printf("%sMAJOR=%d\n"
273                        "%sMINOR=%d\n",
274                        prefix, major(statbuf.st_dev),
275                        prefix, minor(statbuf.st_dev));
276         } else
277                 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
278         return 0;
279 }
280
281 int udevadm_info(struct udev *udev, int argc, char *argv[])
282 {
283         struct udev_device *device = NULL;
284         int root = 0;
285         int export = 0;
286         const char *export_prefix = NULL;
287         char path[UTIL_PATH_SIZE];
288         char name[UTIL_PATH_SIZE];
289         char *links;
290         int rc = 0;
291
292         static const struct option options[] = {
293                 { "name", 1, NULL, 'n' },
294                 { "path", 1, NULL, 'p' },
295                 { "query", 1, NULL, 'q' },
296                 { "attribute-walk", 0, NULL, 'a' },
297                 { "export-db", 0, NULL, 'e' },
298                 { "root", 0, NULL, 'r' },
299                 { "device-id-of-file", 1, NULL, 'd' },
300                 { "export", 0, NULL, 'x' },
301                 { "export-prefix", 1, NULL, 'P' },
302                 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
303                 { "help", 0, NULL, 'h' },
304                 {}
305         };
306
307         enum action_type {
308                 ACTION_NONE,
309                 ACTION_QUERY,
310                 ACTION_ATTRIBUTE_WALK,
311                 ACTION_ROOT,
312                 ACTION_DEVICE_ID_FILE,
313         } action = ACTION_NONE;
314
315         enum query_type {
316                 QUERY_NONE,
317                 QUERY_NAME,
318                 QUERY_PATH,
319                 QUERY_SYMLINK,
320                 QUERY_ENV,
321                 QUERY_ALL,
322         } query = QUERY_NONE;
323
324         while (1) {
325                 int option;
326
327                 option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL);
328                 if (option == -1)
329                         break;
330
331                 dbg(udev, "option '%c'\n", option);
332                 switch (option) {
333                 case 'n':
334                         if (device != NULL) {
335                                 fprintf(stderr, "device already specified\n");
336                                 rc = 2;
337                                 goto exit;
338                         }
339                         /* remove /dev if given */
340                         if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) == 0)
341                                 util_strlcpy(name, &optarg[strlen(udev_get_dev_path(udev))+1], sizeof(name));
342                         else
343                                 util_strlcpy(name, optarg, sizeof(name));
344                         util_remove_trailing_chars(name, '/');
345                         device = lookup_device_by_name(udev, name);
346                         break;
347                 case 'p':
348                         if (device != NULL) {
349                                 fprintf(stderr, "device already specified\n");
350                                 rc = 2;
351                                 goto exit;
352                         }
353                         /* add /sys if needed */
354                         if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) {
355                                 util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
356                                 util_strlcat(path, optarg, sizeof(path));
357                         } else {
358                                 util_strlcpy(path, optarg, sizeof(path));
359                         }
360                         util_remove_trailing_chars(path, '/');
361                         device = udev_device_new_from_syspath(udev, path);
362                         break;
363                 case 'q':
364                         action = ACTION_QUERY;
365                         if (strcmp(optarg, "name") == 0) {
366                                 query = QUERY_NAME;
367                                 break;
368                         }
369                         if (strcmp(optarg, "symlink") == 0) {
370                                 query = QUERY_SYMLINK;
371                                 break;
372                         }
373                         if (strcmp(optarg, "path") == 0) {
374                                 query = QUERY_PATH;
375                                 break;
376                         }
377                         if (strcmp(optarg, "env") == 0) {
378                                 query = QUERY_ENV;
379                                 break;
380                         }
381                         if (strcmp(optarg, "all") == 0) {
382                                 query = QUERY_ALL;
383                                 break;
384                         }
385                         fprintf(stderr, "unknown query type\n");
386                         rc = 2;
387                         goto exit;
388                 case 'r':
389                         if (action == ACTION_NONE)
390                                 action = ACTION_ROOT;
391                         root = 1;
392                         break;
393                 case 'd':
394                         action = ACTION_DEVICE_ID_FILE;
395                         util_strlcpy(name, optarg, sizeof(name));
396                         break;
397                 case 'a':
398                         action = ACTION_ATTRIBUTE_WALK;
399                         break;
400                 case 'e':
401                         udev_enumerate_devices(udev, NULL, export_all_cb, NULL);
402                         goto exit;
403                 case 'x':
404                         export = 1;
405                         break;
406                 case 'P':
407                         export_prefix = optarg;
408                         break;
409                 case 1:
410                         printf("%s\n", VERSION);
411                         goto exit;
412                 case 'V':
413                         printf("udevinfo, version %s\n", VERSION);
414                         goto exit;
415                 case 'h':
416                         printf("Usage: udevadm info OPTIONS\n"
417                                "  --query=<type>             query database for the specified value:\n"
418                                "      name                     name of device node\n"
419                                "      symlink                  pointing to node\n"
420                                "      path                     sysfs device path\n"
421                                "      env                      the device related imported environment\n"
422                                "      all                      all values\n"
423                                "  --path=<devpath>           sysfs device path used for query or chain\n"
424                                "  --name=<name>              node or symlink name used for query\n"
425                                "  --root                     prepend to query result or print udev_root\n"
426                                "  --attribute-walk           print all key matches while walking along chain\n"
427                                "                             of parent devices\n"
428                                "  --device-id-of-file=<file> print major/minor of underlying device\n"
429                                "  --export-db                export the content of the udev database\n"
430                                "  --help                     print this text\n"
431                                "\n");
432                         goto exit;
433                 default:
434                         goto exit;
435                 }
436         }
437
438         switch (action) {
439         case ACTION_QUERY:
440                 if (device == NULL) {
441                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
442                         rc = 4;
443                         goto exit;
444                 }
445
446                 switch(query) {
447                 case QUERY_NAME:
448                         if (root) {
449                                 printf("%s\n", udev_device_get_devname(device));
450                         } else {
451                                 size_t len;
452
453                                 len = strlen(udev_get_dev_path(udev));
454                                 printf("%s\n", &udev_device_get_devname(device)[len+1]);
455                         }
456                         break;
457                 case QUERY_SYMLINK:
458                         links = NULL;
459                         if (root)
460                                 udev_device_get_devlinks(device, add_devlink_cb, &links);
461                         else
462                                 udev_device_get_devlinks(device, add_devlink_noroot_cb, &links);
463                         printf("%s\n", links);
464                         free(links);
465                         break;
466                 case QUERY_PATH:
467                         printf("%s\n", udev_device_get_devpath(device));
468                         goto exit;
469                 case QUERY_ENV:
470                         udev_device_get_properties(device, print_property_cb, NULL);
471                         break;
472                 case QUERY_ALL:
473                         print_record(device);
474                         break;
475                 default:
476                         fprintf(stderr, "unknown query type\n");
477                         break;
478                 }
479                 break;
480         case ACTION_ATTRIBUTE_WALK:
481                 if (device == NULL) {
482                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
483                         rc = 5;
484                         goto exit;
485                 }
486                 print_device_chain(device);
487                 break;
488         case ACTION_DEVICE_ID_FILE:
489                 if (stat_device(name, export, export_prefix) != 0)
490                         rc = 6;
491                 break;
492         case ACTION_ROOT:
493                 printf("%s\n", udev_get_dev_path(udev));
494                 break;
495         default:
496                 fprintf(stderr, "missing option\n");
497                 rc = 1;
498                 break;
499         }
500
501 exit:
502         udev_device_unref(device);
503         return rc;
504 }