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