chiark / gitweb /
udevadm: info - fix lookup-by-name
[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 *devpath, const char *subsystem, const char *name,
170                          void *data)
171 {
172         struct udev_device *device;
173
174         device = udev_device_new_from_devpath(udev, devpath);
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                 udev_device = udev_device_new_from_devpath(udev, udevice_loop->dev->devpath);
219                 udev_device_cleanup(udevice_loop);
220                 break;
221 next:
222                 udev_device_cleanup(udevice_loop);
223         }
224 out:
225         name_list_cleanup(udev, &name_list);
226         return udev_device;
227 }
228
229 static int add_devlink_cb(struct udev_device *device, const char *value, void *data)
230 {
231         char **links = data;
232
233         if (*links == NULL) {
234                 *links = strdup(value);
235         } else {
236                 char *str;
237
238                 asprintf(&str, "%s %s", *links, value);
239                 free(*links);
240                 *links = str;
241         }
242         return 0;
243 }
244
245 static int add_devlink_noroot_cb(struct udev_device *device, const char *value, void *data)
246 {
247         size_t len;
248
249         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
250         value = &value[len+1];
251         return add_devlink_cb(device, value, data);
252 }
253
254 static int print_property_cb(struct udev_device *device, const char *key, const char *value, void *data)
255 {
256         printf("%s=%s\n", key, value);
257         return 0;
258 }
259
260 static int stat_device(const char *name, int export, const char *prefix)
261 {
262         struct stat statbuf;
263
264         if (stat(name, &statbuf) != 0)
265                 return -1;
266
267         if (export) {
268                 if (prefix == NULL)
269                         prefix = "INFO_";
270                 printf("%sMAJOR=%d\n"
271                        "%sMINOR=%d\n",
272                        prefix, major(statbuf.st_dev),
273                        prefix, minor(statbuf.st_dev));
274         } else
275                 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
276         return 0;
277 }
278
279 int udevadm_info(struct udev *udev, int argc, char *argv[])
280 {
281         struct udev_device *device = NULL;
282         int root = 0;
283         int export = 0;
284         const char *export_prefix = NULL;
285         char path[UTIL_PATH_SIZE];
286         char name[UTIL_PATH_SIZE];
287         char *links;
288         int rc = 0;
289
290         static const struct option options[] = {
291                 { "name", 1, NULL, 'n' },
292                 { "path", 1, NULL, 'p' },
293                 { "query", 1, NULL, 'q' },
294                 { "attribute-walk", 0, NULL, 'a' },
295                 { "export-db", 0, NULL, 'e' },
296                 { "root", 0, NULL, 'r' },
297                 { "device-id-of-file", 1, NULL, 'd' },
298                 { "export", 0, NULL, 'x' },
299                 { "export-prefix", 1, NULL, 'P' },
300                 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
301                 { "help", 0, NULL, 'h' },
302                 {}
303         };
304
305         enum action_type {
306                 ACTION_NONE,
307                 ACTION_QUERY,
308                 ACTION_ATTRIBUTE_WALK,
309                 ACTION_ROOT,
310                 ACTION_DEVICE_ID_FILE,
311         } action = ACTION_NONE;
312
313         enum query_type {
314                 QUERY_NONE,
315                 QUERY_NAME,
316                 QUERY_PATH,
317                 QUERY_SYMLINK,
318                 QUERY_ENV,
319                 QUERY_ALL,
320         } query = QUERY_NONE;
321
322         while (1) {
323                 int option;
324
325                 option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL);
326                 if (option == -1)
327                         break;
328
329                 dbg(udev, "option '%c'\n", option);
330                 switch (option) {
331                 case 'n':
332                         if (device != NULL) {
333                                 fprintf(stderr, "device already specified\n");
334                                 rc = 2;
335                                 goto exit;
336                         }
337                         /* remove /dev if given */
338                         if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) == 0)
339                                 util_strlcpy(name, &optarg[strlen(udev_get_dev_path(udev))+1], sizeof(name));
340                         else
341                                 util_strlcpy(name, optarg, sizeof(name));
342                         util_remove_trailing_chars(name, '/');
343                         device = lookup_device_by_name(udev, name);
344                         break;
345                 case 'p':
346                         if (device != NULL) {
347                                 fprintf(stderr, "device already specified\n");
348                                 rc = 2;
349                                 goto exit;
350                         }
351                         /* remove /sys if given */
352                         if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) == 0)
353                                 util_strlcpy(path, &optarg[strlen(udev_get_sys_path(udev))], sizeof(path));
354                         else
355                                 util_strlcpy(path, optarg, sizeof(path));
356                         util_remove_trailing_chars(path, '/');
357                         device = udev_device_new_from_devpath(udev, path);
358                         break;
359                 case 'q':
360                         action = ACTION_QUERY;
361                         if (strcmp(optarg, "name") == 0) {
362                                 query = QUERY_NAME;
363                                 break;
364                         }
365                         if (strcmp(optarg, "symlink") == 0) {
366                                 query = QUERY_SYMLINK;
367                                 break;
368                         }
369                         if (strcmp(optarg, "path") == 0) {
370                                 query = QUERY_PATH;
371                                 break;
372                         }
373                         if (strcmp(optarg, "env") == 0) {
374                                 query = QUERY_ENV;
375                                 break;
376                         }
377                         if (strcmp(optarg, "all") == 0) {
378                                 query = QUERY_ALL;
379                                 break;
380                         }
381                         fprintf(stderr, "unknown query type\n");
382                         rc = 2;
383                         goto exit;
384                 case 'r':
385                         if (action == ACTION_NONE)
386                                 action = ACTION_ROOT;
387                         root = 1;
388                         break;
389                 case 'd':
390                         action = ACTION_DEVICE_ID_FILE;
391                         util_strlcpy(name, optarg, sizeof(name));
392                         break;
393                 case 'a':
394                         action = ACTION_ATTRIBUTE_WALK;
395                         break;
396                 case 'e':
397                         udev_enumerate_devices(udev, NULL, export_all_cb, NULL);
398                         goto exit;
399                 case 'x':
400                         export = 1;
401                         break;
402                 case 'P':
403                         export_prefix = optarg;
404                         break;
405                 case 1:
406                         printf("%s\n", VERSION);
407                         goto exit;
408                 case 'V':
409                         printf("udevinfo, version %s\n", VERSION);
410                         goto exit;
411                 case 'h':
412                         printf("Usage: udevadm info OPTIONS\n"
413                                "  --query=<type>             query database for the specified value:\n"
414                                "      name                     name of device node\n"
415                                "      symlink                  pointing to node\n"
416                                "      path                     sysfs device path\n"
417                                "      env                      the device related imported environment\n"
418                                "      all                      all values\n"
419                                "  --path=<devpath>           sysfs device path used for query or chain\n"
420                                "  --name=<name>              node or symlink name used for query\n"
421                                "  --root                     prepend to query result or print udev_root\n"
422                                "  --attribute-walk           print all key matches while walking along chain\n"
423                                "                             of parent devices\n"
424                                "  --device-id-of-file=<file> print major/minor of underlying device\n"
425                                "  --export-db                export the content of the udev database\n"
426                                "  --help                     print this text\n"
427                                "\n");
428                         goto exit;
429                 default:
430                         goto exit;
431                 }
432         }
433
434         switch (action) {
435         case ACTION_QUERY:
436                 if (device == NULL) {
437                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
438                         rc = 4;
439                         goto exit;
440                 }
441
442                 switch(query) {
443                 case QUERY_NAME:
444                         if (root) {
445                                 printf("%s\n", udev_device_get_devname(device));
446                         } else {
447                                 size_t len;
448
449                                 len = strlen(udev_get_dev_path(udev));
450                                 printf("%s\n", &udev_device_get_devname(device)[len+1]);
451                         }
452                         break;
453                 case QUERY_SYMLINK:
454                         links = NULL;
455                         if (root)
456                                 udev_device_get_devlinks(device, add_devlink_cb, &links);
457                         else
458                                 udev_device_get_devlinks(device, add_devlink_noroot_cb, &links);
459                         printf("%s\n", links);
460                         free(links);
461                         break;
462                 case QUERY_PATH:
463                         printf("%s\n", udev_device_get_devpath(device));
464                         goto exit;
465                 case QUERY_ENV:
466                         udev_device_get_properties(device, print_property_cb, NULL);
467                         break;
468                 case QUERY_ALL:
469                         print_record(device);
470                         break;
471                 default:
472                         fprintf(stderr, "unknown query type\n");
473                         break;
474                 }
475                 break;
476         case ACTION_ATTRIBUTE_WALK:
477                 if (device == NULL) {
478                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
479                         rc = 5;
480                         goto exit;
481                 }
482                 print_device_chain(device);
483                 break;
484         case ACTION_DEVICE_ID_FILE:
485                 if (stat_device(name, export, export_prefix) != 0)
486                         rc = 6;
487                 break;
488         case ACTION_ROOT:
489                 printf("%s\n", udev_get_dev_path(udev));
490                 break;
491         default:
492                 fprintf(stderr, "missing option\n");
493                 rc = 1;
494                 break;
495         }
496
497 exit:
498         udev_device_unref(device);
499         return rc;
500 }