chiark / gitweb /
udevadm: move init from commands to udevadm
[elogind.git] / udev / udevadm-info.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 udevadm_info(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         while (1) {
283                 option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL);
284                 if (option == -1)
285                         break;
286
287                 dbg("option '%c'\n", option);
288                 switch (option) {
289                 case 'n':
290                         /* remove /dev if given */
291                         if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
292                                 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
293                         else
294                                 strlcpy(name, optarg, sizeof(name));
295                         remove_trailing_chars(name, '/');
296                         dbg("name: %s\n", name);
297                         break;
298                 case 'p':
299                         /* remove /sys if given */
300                         if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
301                                 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
302                         else
303                                 strlcpy(path, optarg, sizeof(path));
304                         remove_trailing_chars(path, '/');
305
306                         /* possibly resolve to real devpath */
307                         if (sysfs_resolve_link(path, sizeof(path)) != 0) {
308                                 char temp[PATH_SIZE];
309                                 char *pos;
310
311                                 /* also check if the parent is a link */
312                                 strlcpy(temp, path, sizeof(temp));
313                                 pos = strrchr(temp, '/');
314                                 if (pos != 0) {
315                                         char tail[PATH_SIZE];
316
317                                         strlcpy(tail, pos, sizeof(tail));
318                                         pos[0] = '\0';
319                                         if (sysfs_resolve_link(temp, sizeof(temp)) == 0) {
320                                                 strlcpy(path, temp, sizeof(path));
321                                                 strlcat(path, tail, sizeof(path));
322                                         }
323                                 }
324                         }
325                         dbg("path: %s\n", path);
326                         break;
327                 case 'q':
328                         action = ACTION_QUERY;
329                         if (strcmp(optarg, "name") == 0) {
330                                 query = QUERY_NAME;
331                                 break;
332                         }
333                         if (strcmp(optarg, "symlink") == 0) {
334                                 query = QUERY_SYMLINK;
335                                 break;
336                         }
337                         if (strcmp(optarg, "path") == 0) {
338                                 query = QUERY_PATH;
339                                 break;
340                         }
341                         if (strcmp(optarg, "env") == 0) {
342                                 query = QUERY_ENV;
343                                 break;
344                         }
345                         if (strcmp(optarg, "all") == 0) {
346                                 query = QUERY_ALL;
347                                 break;
348                         }
349                         fprintf(stderr, "unknown query type\n");
350                         rc = 2;
351                         goto exit;
352                 case 'r':
353                         if (action == ACTION_NONE)
354                                 action = ACTION_ROOT;
355                         root = 1;
356                         break;
357                 case 'd':
358                         action = ACTION_DEVICE_ID_FILE;
359                         strlcpy(name, optarg, sizeof(name));
360                         break;
361                 case 'a':
362                         action = ACTION_ATTRIBUTE_WALK;
363                         break;
364                 case 'e':
365                         export_db();
366                         goto exit;
367                 case 'x':
368                         export = 1;
369                         break;
370                 case 'P':
371                         export_prefix = optarg;
372                         break;
373                 case 1:
374                         printf("%s\n", VERSION);
375                         goto exit;
376                 case 'V':
377                         printf("udevinfo, version %s\n", VERSION);
378                         goto exit;
379                 case 'h':
380                         printf("Usage: udevadm info OPTIONS\n"
381                                "  --query=<type>             query database for the specified value:\n"
382                                "      name                     name of device node\n"
383                                "      symlink                  pointing to node\n"
384                                "      path                     sysfs device path\n"
385                                "      env                      the device related imported environment\n"
386                                "      all                      all values\n"
387                                "  --path=<devpath>           sysfs device path used for query or chain\n"
388                                "  --name=<name>              node or symlink name used for query\n"
389                                "  --root                     prepend to query result or print udev_root\n"
390                                "  --attribute-walk           print all key matches while walking along chain\n"
391                                "                             of parent devices\n"
392                                "  --device-id-of-file=<file> print major/minor of underlying device\n"
393                                "  --export-db                export the content of the udev database\n"
394                                "  --help                     print this text\n"
395                                "\n");
396                         goto exit;
397                 default:
398                         goto exit;
399                 }
400         }
401
402         /* run action */
403         switch (action) {
404         case ACTION_QUERY:
405                 /* needs devpath or node/symlink name for query */
406                 if (path[0] != '\0') {
407                         udev = udev_device_init();
408                         if (udev == NULL) {
409                                 rc = 1;
410                                 goto exit;
411                         }
412                         if (udev_db_get_device(udev, path) != 0) {
413                                 fprintf(stderr, "no record for '%s' in database\n", path);
414                                 rc = 3;
415                                 goto exit;
416                         }
417                 } else if (name[0] != '\0') {
418                         if (lookup_device_by_name(&udev, name) != 0) {
419                                 fprintf(stderr, "node name not found\n");
420                                 rc = 4;
421                                 goto exit;
422                         }
423                 } else {
424                         fprintf(stderr, "query needs --path or node --name specified\n");
425                         rc = 4;
426                         goto exit;
427                 }
428
429                 switch(query) {
430                 case QUERY_NAME:
431                         if (root)
432                                 printf("%s/%s\n", udev_root, udev->name);
433                         else
434                                 printf("%s\n", udev->name);
435                         break;
436                 case QUERY_SYMLINK:
437                         list_for_each_entry(name_loop, &udev->symlink_list, node) {
438                                 char c = name_loop->node.next != &udev->symlink_list ? ' ' : '\n';
439
440                                 if (root)
441                                         printf("%s/%s%c", udev_root, name_loop->name, c);
442                                 else
443                                         printf("%s%c", name_loop->name, c);
444                         }
445                         break;
446                 case QUERY_PATH:
447                         printf("%s\n", udev->dev->devpath);
448                         goto exit;
449                 case QUERY_ENV:
450                         list_for_each_entry(name_loop, &udev->env_list, node)
451                                 printf("%s\n", name_loop->name);
452                         break;
453                 case QUERY_ALL:
454                         print_record(udev);
455                         break;
456                 default:
457                         fprintf(stderr, "unknown query type\n");
458                         break;
459                 }
460                 break;
461         case ACTION_ATTRIBUTE_WALK:
462                 if (path[0] != '\0') {
463                         if (print_device_chain(path) != 0) {
464                                 fprintf(stderr, "no valid sysfs device found\n");
465                                 rc = 4;
466                                 goto exit;
467                         }
468                 } else if (name[0] != '\0') {
469                         if (lookup_device_by_name(&udev, name) != 0) {
470                                 fprintf(stderr, "node name not found\n");
471                                 rc = 4;
472                                 goto exit;
473                         }
474                         if (print_device_chain(udev->dev->devpath) != 0) {
475                                 fprintf(stderr, "no valid sysfs device found\n");
476                                 rc = 4;
477                                 goto exit;
478                         }
479                 } else {
480                         fprintf(stderr, "attribute walk needs --path or node --name specified\n");
481                         rc = 5;
482                         goto exit;
483                 }
484                 break;
485         case ACTION_DEVICE_ID_FILE:
486                 if (stat_device(name, export, export_prefix) != 0)
487                         rc = 6;
488                 break;
489         case ACTION_ROOT:
490                 printf("%s\n", udev_root);
491                 break;
492         default:
493                 fprintf(stderr, "missing option\n");
494                 rc = 1;
495                 break;
496         }
497
498 exit:
499         udev_device_cleanup(udev);
500         return rc;
501 }