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