chiark / gitweb /
libudev: switch to "udev_device_get_parent"
[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[UTIL_PATH_SIZE];
38         DIR *dir;
39         struct dirent *dent;
40
41         util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
42         util_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[UTIL_PATH_SIZE];
49                         char *attr_value;
50                         char value[UTIL_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                         util_strlcpy(filename, path, sizeof(filename));
62                         util_strlcat(filename, "/", sizeof(filename));
63                         util_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 = util_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                 closedir(dir);
88         }
89         printf("\n");
90 }
91
92 static int print_device_chain(struct udev *udev, const char *devpath)
93 {
94         struct udev_device *device;
95         struct udev_device *device_parent;
96         const char *str;
97
98         device = udev_device_new_from_devpath(udev, devpath);
99         if (device == 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", udev_device_get_devpath(device));
111         printf("    KERNEL==\"%s\"\n", udev_device_get_sysname(device));
112         str = udev_device_get_subsystem(device);
113         if (str == NULL)
114                 str = "";
115         printf("    SUBSYSTEM==\"%s\"\n", str);
116         str = udev_device_get_driver(device);
117         if (str == NULL)
118                 str = "";
119         printf("    DRIVER==\"%s\"\n", str);
120         print_all_attributes(udev, udev_device_get_devpath(device), "ATTR");
121
122         device_parent = device;
123         do {
124                 device_parent = udev_device_get_parent(device_parent);
125                 if (device_parent == NULL)
126                         break;
127                 printf("  looking at parent device '%s':\n", udev_device_get_devpath(device_parent));
128                 printf("    KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent));
129                 str = udev_device_get_subsystem(device_parent);
130                 if (str == NULL)
131                         str = "";
132                 printf("    SUBSYSTEMS==\"%s\"\n", str);
133                 str = udev_device_get_driver(device_parent);
134                 if (str == NULL)
135                         str = "";
136                 printf("    DRIVERS==\"%s\"\n", str);
137                 print_all_attributes(udev, udev_device_get_devpath(device_parent), "ATTRS");
138         } while (device_parent != NULL);
139
140         udev_device_unref(device);
141         return 0;
142 }
143
144 static void print_record(struct udevice *udevice)
145 {
146         struct name_entry *name_loop;
147
148         printf("P: %s\n", udevice->dev->devpath);
149         printf("N: %s\n", udevice->name);
150         list_for_each_entry(name_loop, &udevice->symlink_list, node)
151                 printf("S: %s\n", name_loop->name);
152         if (udevice->link_priority != 0)
153                 printf("L: %i\n", udevice->link_priority);
154         if (udevice->partitions != 0)
155                 printf("A:%u\n", udevice->partitions);
156         if (udevice->ignore_remove)
157                 printf("R:%u\n", udevice->ignore_remove);
158         list_for_each_entry(name_loop, &udevice->env_list, node)
159                 printf("E: %s\n", name_loop->name);
160 }
161
162 static void export_db(struct udev *udev)
163 {
164         LIST_HEAD(name_list);
165         struct name_entry *name_loop;
166
167         udev_db_get_all_entries(udev, &name_list);
168         list_for_each_entry(name_loop, &name_list, node) {
169                 struct udevice *udevice_db;
170
171                 udevice_db = udev_device_init(udev);
172                 if (udevice_db == NULL)
173                         continue;
174                 if (udev_db_get_device(udevice_db, name_loop->name) == 0)
175                         print_record(udevice_db);
176                         printf("\n");
177                 udev_device_cleanup(udevice_db);
178         }
179         name_list_cleanup(udev, &name_list);
180 }
181
182 static int lookup_device_by_name(struct udev *udev, struct udevice **udevice, const char *name)
183 {
184         LIST_HEAD(name_list);
185         int count;
186         struct name_entry *device;
187         int rc  = -1;
188
189         count = udev_db_get_devices_by_name(udev, name, &name_list);
190         if (count <= 0)
191                 goto out;
192
193         info(udev, "found %i devices for '%s'\n", count, name);
194
195         /* select the device that seems to match */
196         list_for_each_entry(device, &name_list, node) {
197                 struct udevice *udevice_loop;
198                 char filename[UTIL_PATH_SIZE];
199                 struct stat statbuf;
200
201                 udevice_loop = udev_device_init(udev);
202                 if (udevice_loop == NULL)
203                         break;
204                 if (udev_db_get_device(udevice_loop, device->name) != 0)
205                         goto next;
206                 info(udev, "found db entry '%s'\n", device->name);
207
208                 /* make sure, we don't get a link of a different device */
209                 util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
210                 util_strlcat(filename, "/", sizeof(filename));
211                 util_strlcat(filename, name, sizeof(filename));
212                 if (stat(filename, &statbuf) != 0)
213                         goto next;
214                 if (major(udevice_loop->devt) > 0 && udevice_loop->devt != statbuf.st_rdev) {
215                         info(udev, "skip '%s', dev_t doesn't match\n", udevice_loop->name);
216                         goto next;
217                 }
218                 rc = 0;
219                 *udevice = udevice_loop;
220                 break;
221 next:
222                 udev_device_cleanup(udevice_loop);
223         }
224 out:
225         name_list_cleanup(udev, &name_list);
226         return rc;
227 }
228
229 static int stat_device(const char *name, int export, const char *prefix)
230 {
231         struct stat statbuf;
232
233         if (stat(name, &statbuf) != 0)
234                 return -1;
235
236         if (export) {
237                 if (prefix == NULL)
238                         prefix = "INFO_";
239                 printf("%sMAJOR=%d\n"
240                        "%sMINOR=%d\n",
241                        prefix, major(statbuf.st_dev),
242                        prefix, minor(statbuf.st_dev));
243         } else
244                 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
245         return 0;
246 }
247
248 int udevadm_info(struct udev *udev, int argc, char *argv[])
249 {
250         struct udevice *udevice = NULL;
251         int root = 0;
252         int export = 0;
253         const char *export_prefix = NULL;
254
255         static const struct option options[] = {
256                 { "name", 1, NULL, 'n' },
257                 { "path", 1, NULL, 'p' },
258                 { "query", 1, NULL, 'q' },
259                 { "attribute-walk", 0, NULL, 'a' },
260                 { "export-db", 0, NULL, 'e' },
261                 { "root", 0, NULL, 'r' },
262                 { "device-id-of-file", 1, NULL, 'd' },
263                 { "export", 0, NULL, 'x' },
264                 { "export-prefix", 1, NULL, 'P' },
265                 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
266                 { "help", 0, NULL, 'h' },
267                 {}
268         };
269
270         enum action_type {
271                 ACTION_NONE,
272                 ACTION_QUERY,
273                 ACTION_ATTRIBUTE_WALK,
274                 ACTION_ROOT,
275                 ACTION_DEVICE_ID_FILE,
276         } action = ACTION_NONE;
277
278         enum query_type {
279                 QUERY_NONE,
280                 QUERY_NAME,
281                 QUERY_PATH,
282                 QUERY_SYMLINK,
283                 QUERY_ENV,
284                 QUERY_ALL,
285         } query = QUERY_NONE;
286
287         char path[UTIL_PATH_SIZE] = "";
288         char name[UTIL_PATH_SIZE] = "";
289         struct name_entry *name_loop;
290         int rc = 0;
291
292         while (1) {
293                 int option;
294
295                 option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL);
296                 if (option == -1)
297                         break;
298
299                 dbg(udev, "option '%c'\n", option);
300                 switch (option) {
301                 case 'n':
302                         /* remove /dev if given */
303                         if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) == 0)
304                                 util_strlcpy(name, &optarg[strlen(udev_get_dev_path(udev))+1], sizeof(name));
305                         else
306                                 util_strlcpy(name, optarg, sizeof(name));
307                         util_remove_trailing_chars(name, '/');
308                         dbg(udev, "name: %s\n", name);
309                         break;
310                 case 'p':
311                         /* remove /sys if given */
312                         if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) == 0)
313                                 util_strlcpy(path, &optarg[strlen(udev_get_sys_path(udev))], sizeof(path));
314                         else
315                                 util_strlcpy(path, optarg, sizeof(path));
316                         util_remove_trailing_chars(path, '/');
317
318                         /* possibly resolve to real devpath */
319                         if (util_resolve_sys_link(udev, path, sizeof(path)) != 0) {
320                                 char temp[UTIL_PATH_SIZE];
321                                 char *pos;
322
323                                 /* also check if the parent is a link */
324                                 util_strlcpy(temp, path, sizeof(temp));
325                                 pos = strrchr(temp, '/');
326                                 if (pos != 0) {
327                                         char tail[UTIL_PATH_SIZE];
328
329                                         util_strlcpy(tail, pos, sizeof(tail));
330                                         pos[0] = '\0';
331                                         if (util_resolve_sys_link(udev, temp, sizeof(temp)) == 0) {
332                                                 util_strlcpy(path, temp, sizeof(path));
333                                                 util_strlcat(path, tail, sizeof(path));
334                                         }
335                                 }
336                         }
337                         dbg(udev, "path: %s\n", path);
338                         break;
339                 case 'q':
340                         action = ACTION_QUERY;
341                         if (strcmp(optarg, "name") == 0) {
342                                 query = QUERY_NAME;
343                                 break;
344                         }
345                         if (strcmp(optarg, "symlink") == 0) {
346                                 query = QUERY_SYMLINK;
347                                 break;
348                         }
349                         if (strcmp(optarg, "path") == 0) {
350                                 query = QUERY_PATH;
351                                 break;
352                         }
353                         if (strcmp(optarg, "env") == 0) {
354                                 query = QUERY_ENV;
355                                 break;
356                         }
357                         if (strcmp(optarg, "all") == 0) {
358                                 query = QUERY_ALL;
359                                 break;
360                         }
361                         fprintf(stderr, "unknown query type\n");
362                         rc = 2;
363                         goto exit;
364                 case 'r':
365                         if (action == ACTION_NONE)
366                                 action = ACTION_ROOT;
367                         root = 1;
368                         break;
369                 case 'd':
370                         action = ACTION_DEVICE_ID_FILE;
371                         util_strlcpy(name, optarg, sizeof(name));
372                         break;
373                 case 'a':
374                         action = ACTION_ATTRIBUTE_WALK;
375                         break;
376                 case 'e':
377                         export_db(udev);
378                         goto exit;
379                 case 'x':
380                         export = 1;
381                         break;
382                 case 'P':
383                         export_prefix = optarg;
384                         break;
385                 case 1:
386                         printf("%s\n", VERSION);
387                         goto exit;
388                 case 'V':
389                         printf("udevinfo, version %s\n", VERSION);
390                         goto exit;
391                 case 'h':
392                         printf("Usage: udevadm info OPTIONS\n"
393                                "  --query=<type>             query database for the specified value:\n"
394                                "      name                     name of device node\n"
395                                "      symlink                  pointing to node\n"
396                                "      path                     sysfs device path\n"
397                                "      env                      the device related imported environment\n"
398                                "      all                      all values\n"
399                                "  --path=<devpath>           sysfs device path used for query or chain\n"
400                                "  --name=<name>              node or symlink name used for query\n"
401                                "  --root                     prepend to query result or print udev_root\n"
402                                "  --attribute-walk           print all key matches while walking along chain\n"
403                                "                             of parent devices\n"
404                                "  --device-id-of-file=<file> print major/minor of underlying device\n"
405                                "  --export-db                export the content of the udev database\n"
406                                "  --help                     print this text\n"
407                                "\n");
408                         goto exit;
409                 default:
410                         goto exit;
411                 }
412         }
413
414         /* run action */
415         switch (action) {
416         case ACTION_QUERY:
417                 /* needs devpath or node/symlink name for query */
418                 if (path[0] != '\0') {
419                         udevice = udev_device_init(udev);
420                         if (udevice == NULL) {
421                                 rc = 1;
422                                 goto exit;
423                         }
424                         if (udev_db_get_device(udevice, path) != 0) {
425                                 fprintf(stderr, "no record for '%s' in database\n", path);
426                                 rc = 3;
427                                 goto exit;
428                         }
429                 } else if (name[0] != '\0') {
430                         if (lookup_device_by_name(udev, &udevice, name) != 0) {
431                                 fprintf(stderr, "node name not found\n");
432                                 rc = 4;
433                                 goto exit;
434                         }
435                 } else {
436                         fprintf(stderr, "query needs --path or node --name specified\n");
437                         rc = 4;
438                         goto exit;
439                 }
440
441                 switch(query) {
442                 case QUERY_NAME:
443                         if (root)
444                                 printf("%s/%s\n", udev_get_dev_path(udev), udevice->name);
445                         else
446                                 printf("%s\n", udevice->name);
447                         break;
448                 case QUERY_SYMLINK:
449                         list_for_each_entry(name_loop, &udevice->symlink_list, node) {
450                                 char c = name_loop->node.next != &udevice->symlink_list ? ' ' : '\n';
451
452                                 if (root)
453                                         printf("%s/%s%c", udev_get_dev_path(udev), name_loop->name, c);
454                                 else
455                                         printf("%s%c", name_loop->name, c);
456                         }
457                         break;
458                 case QUERY_PATH:
459                         printf("%s\n", udevice->dev->devpath);
460                         goto exit;
461                 case QUERY_ENV:
462                         list_for_each_entry(name_loop, &udevice->env_list, node)
463                                 printf("%s\n", name_loop->name);
464                         break;
465                 case QUERY_ALL:
466                         print_record(udevice);
467                         break;
468                 default:
469                         fprintf(stderr, "unknown query type\n");
470                         break;
471                 }
472                 break;
473         case ACTION_ATTRIBUTE_WALK:
474                 if (path[0] != '\0') {
475                         if (print_device_chain(udev, path) != 0) {
476                                 fprintf(stderr, "no valid sysfs device found\n");
477                                 rc = 4;
478                                 goto exit;
479                         }
480                 } else if (name[0] != '\0') {
481                         if (lookup_device_by_name(udev, &udevice, name) != 0) {
482                                 fprintf(stderr, "node name not found\n");
483                                 rc = 4;
484                                 goto exit;
485                         }
486                         if (print_device_chain(udev, udevice->dev->devpath) != 0) {
487                                 fprintf(stderr, "no valid sysfs device found\n");
488                                 rc = 4;
489                                 goto exit;
490                         }
491                 } else {
492                         fprintf(stderr, "attribute walk needs --path or node --name specified\n");
493                         rc = 5;
494                         goto exit;
495                 }
496                 break;
497         case ACTION_DEVICE_ID_FILE:
498                 if (stat_device(name, export, export_prefix) != 0)
499                         rc = 6;
500                 break;
501         case ACTION_ROOT:
502                 printf("%s\n", udev_get_dev_path(udev));
503                 break;
504         default:
505                 fprintf(stderr, "missing option\n");
506                 rc = 1;
507                 break;
508         }
509
510 exit:
511         udev_device_cleanup(udevice);
512         return rc;
513 }