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