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