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