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