chiark / gitweb /
libudev: udev_device - add attribute cache
[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 *udev,
169                          const char *devpath, const char *subsystem, const char *name,
170                          void *data)
171 {
172         struct udev_device *device;
173
174         device = udev_device_new_from_devpath(udev, devpath);
175         if (device == NULL)
176                 return 0;
177         if (udev_device_get_devname(device) != NULL)
178                 print_record(device);
179         udev_device_unref(device);
180         return 0;
181 }
182
183 static struct udev_device *lookup_device_by_name(struct udev *udev, const char *name)
184 {
185 #if 0
186         /* FIXME */
187         LIST_HEAD(name_list);
188         int count;
189         struct name_entry *device;
190         int rc  = -1;
191
192         count = udev_db_get_devices_by_name(udev, name, &name_list);
193         if (count <= 0)
194                 goto out;
195
196         info(udev, "found %i devices for '%s'\n", count, name);
197
198         /* select the device that seems to match */
199         list_for_each_entry(device, &name_list, node) {
200                 struct udevice *udevice_loop;
201                 char filename[UTIL_PATH_SIZE];
202                 struct stat statbuf;
203
204                 udevice_loop = udev_device_init(udev);
205                 if (udevice_loop == NULL)
206                         break;
207                 if (udev_db_get_device(udevice_loop, device->name) != 0)
208                         goto next;
209                 info(udev, "found db entry '%s'\n", device->name);
210
211                 /* make sure, we don't get a link of a different device */
212                 util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
213                 util_strlcat(filename, "/", sizeof(filename));
214                 util_strlcat(filename, name, sizeof(filename));
215                 if (stat(filename, &statbuf) != 0)
216                         goto next;
217                 if (major(udevice_loop->devt) > 0 && udevice_loop->devt != statbuf.st_rdev) {
218                         info(udev, "skip '%s', dev_t doesn't match\n", udevice_loop->name);
219                         goto next;
220                 }
221                 rc = 0;
222                 *udevice = udevice_loop;
223                 break;
224 next:
225                 udev_device_cleanup(udevice_loop);
226         }
227 out:
228         name_list_cleanup(udev, &name_list);
229         return rc;
230 #endif
231         return NULL;
232 }
233
234 static int add_devlink_cb(struct udev_device *device, const char *value, void *data)
235 {
236         char **links = data;
237
238         if (*links == NULL) {
239                 *links = strdup(value);
240         } else {
241                 char *str;
242
243                 asprintf(&str, "%s %s", *links, value);
244                 free(*links);
245                 *links = str;
246         }
247         return 0;
248 }
249
250 static int add_devlink_noroot_cb(struct udev_device *device, const char *value, void *data)
251 {
252         size_t len;
253
254         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
255         value = &value[len+1];
256         return add_devlink_cb(device, value, data);
257 }
258
259 static int print_property_cb(struct udev_device *device, const char *key, const char *value, void *data)
260 {
261         printf("%s=%s\n", key, value);
262         return 0;
263 }
264
265 static int stat_device(const char *name, int export, const char *prefix)
266 {
267         struct stat statbuf;
268
269         if (stat(name, &statbuf) != 0)
270                 return -1;
271
272         if (export) {
273                 if (prefix == NULL)
274                         prefix = "INFO_";
275                 printf("%sMAJOR=%d\n"
276                        "%sMINOR=%d\n",
277                        prefix, major(statbuf.st_dev),
278                        prefix, minor(statbuf.st_dev));
279         } else
280                 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
281         return 0;
282 }
283
284 int udevadm_info(struct udev *udev, int argc, char *argv[])
285 {
286         struct udev_device *device = NULL;
287         int root = 0;
288         int export = 0;
289         const char *export_prefix = NULL;
290         char path[UTIL_PATH_SIZE];
291         char name[UTIL_PATH_SIZE];
292         char *links;
293         int rc = 0;
294
295         static const struct option options[] = {
296                 { "name", 1, NULL, 'n' },
297                 { "path", 1, NULL, 'p' },
298                 { "query", 1, NULL, 'q' },
299                 { "attribute-walk", 0, NULL, 'a' },
300                 { "export-db", 0, NULL, 'e' },
301                 { "root", 0, NULL, 'r' },
302                 { "device-id-of-file", 1, NULL, 'd' },
303                 { "export", 0, NULL, 'x' },
304                 { "export-prefix", 1, NULL, 'P' },
305                 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
306                 { "help", 0, NULL, 'h' },
307                 {}
308         };
309
310         enum action_type {
311                 ACTION_NONE,
312                 ACTION_QUERY,
313                 ACTION_ATTRIBUTE_WALK,
314                 ACTION_ROOT,
315                 ACTION_DEVICE_ID_FILE,
316         } action = ACTION_NONE;
317
318         enum query_type {
319                 QUERY_NONE,
320                 QUERY_NAME,
321                 QUERY_PATH,
322                 QUERY_SYMLINK,
323                 QUERY_ENV,
324                 QUERY_ALL,
325         } query = QUERY_NONE;
326
327         while (1) {
328                 int option;
329
330                 option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL);
331                 if (option == -1)
332                         break;
333
334                 dbg(udev, "option '%c'\n", option);
335                 switch (option) {
336                 case 'n':
337                         if (device != NULL) {
338                                 fprintf(stderr, "device already specified\n");
339                                 rc = 2;
340                                 goto exit;
341                         }
342                         /* remove /dev if given */
343                         if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) == 0)
344                                 util_strlcpy(name, &optarg[strlen(udev_get_dev_path(udev))+1], sizeof(name));
345                         else
346                                 util_strlcpy(name, optarg, sizeof(name));
347                         util_remove_trailing_chars(name, '/');
348                         device = lookup_device_by_name(udev, name);
349                         break;
350                 case 'p':
351                         if (device != NULL) {
352                                 fprintf(stderr, "device already specified\n");
353                                 rc = 2;
354                                 goto exit;
355                         }
356                         /* remove /sys if given */
357                         if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) == 0)
358                                 util_strlcpy(path, &optarg[strlen(udev_get_sys_path(udev))], sizeof(path));
359                         else
360                                 util_strlcpy(path, optarg, sizeof(path));
361                         util_remove_trailing_chars(path, '/');
362                         device = udev_device_new_from_devpath(udev, path);
363                         break;
364                 case 'q':
365                         action = ACTION_QUERY;
366                         if (strcmp(optarg, "name") == 0) {
367                                 query = QUERY_NAME;
368                                 break;
369                         }
370                         if (strcmp(optarg, "symlink") == 0) {
371                                 query = QUERY_SYMLINK;
372                                 break;
373                         }
374                         if (strcmp(optarg, "path") == 0) {
375                                 query = QUERY_PATH;
376                                 break;
377                         }
378                         if (strcmp(optarg, "env") == 0) {
379                                 query = QUERY_ENV;
380                                 break;
381                         }
382                         if (strcmp(optarg, "all") == 0) {
383                                 query = QUERY_ALL;
384                                 break;
385                         }
386                         fprintf(stderr, "unknown query type\n");
387                         rc = 2;
388                         goto exit;
389                 case 'r':
390                         if (action == ACTION_NONE)
391                                 action = ACTION_ROOT;
392                         root = 1;
393                         break;
394                 case 'd':
395                         action = ACTION_DEVICE_ID_FILE;
396                         util_strlcpy(name, optarg, sizeof(name));
397                         break;
398                 case 'a':
399                         action = ACTION_ATTRIBUTE_WALK;
400                         break;
401                 case 'e':
402                         udev_enumerate_devices(udev, NULL, export_all_cb, NULL);
403                         goto exit;
404                 case 'x':
405                         export = 1;
406                         break;
407                 case 'P':
408                         export_prefix = optarg;
409                         break;
410                 case 1:
411                         printf("%s\n", VERSION);
412                         goto exit;
413                 case 'V':
414                         printf("udevinfo, version %s\n", VERSION);
415                         goto exit;
416                 case 'h':
417                         printf("Usage: udevadm info OPTIONS\n"
418                                "  --query=<type>             query database for the specified value:\n"
419                                "      name                     name of device node\n"
420                                "      symlink                  pointing to node\n"
421                                "      path                     sysfs device path\n"
422                                "      env                      the device related imported environment\n"
423                                "      all                      all values\n"
424                                "  --path=<devpath>           sysfs device path used for query or chain\n"
425                                "  --name=<name>              node or symlink name used for query\n"
426                                "  --root                     prepend to query result or print udev_root\n"
427                                "  --attribute-walk           print all key matches while walking along chain\n"
428                                "                             of parent devices\n"
429                                "  --device-id-of-file=<file> print major/minor of underlying device\n"
430                                "  --export-db                export the content of the udev database\n"
431                                "  --help                     print this text\n"
432                                "\n");
433                         goto exit;
434                 default:
435                         goto exit;
436                 }
437         }
438
439         switch (action) {
440         case ACTION_QUERY:
441                 if (device == NULL) {
442                         fprintf(stderr, "query needs --path= or node --name= specified\n");
443                         rc = 4;
444                         goto exit;
445                 }
446
447                 switch(query) {
448                 case QUERY_NAME:
449                         if (root) {
450                                 printf("%s\n", udev_device_get_devname(device));
451                         } else {
452                                 size_t len;
453
454                                 len = strlen(udev_get_dev_path(udev));
455                                 printf("%s\n", &udev_device_get_devname(device)[len+1]);
456                         }
457                         break;
458                 case QUERY_SYMLINK:
459                         links = NULL;
460                         if (root)
461                                 udev_device_get_devlinks(device, add_devlink_cb, &links);
462                         else
463                                 udev_device_get_devlinks(device, add_devlink_noroot_cb, &links);
464                         printf("%s\n", links);
465                         free(links);
466                         break;
467                 case QUERY_PATH:
468                         printf("%s\n", udev_device_get_devpath(device));
469                         goto exit;
470                 case QUERY_ENV:
471                         udev_device_get_properties(device, print_property_cb, NULL);
472                         break;
473                 case QUERY_ALL:
474                         print_record(device);
475                         break;
476                 default:
477                         fprintf(stderr, "unknown query type\n");
478                         break;
479                 }
480                 break;
481         case ACTION_ATTRIBUTE_WALK:
482                 if (device == NULL) {
483                         fprintf(stderr, "attribute walk needs --path or node --name specified\n");
484                         rc = 5;
485                         goto exit;
486                 }
487                 print_device_chain(device);
488                 break;
489         case ACTION_DEVICE_ID_FILE:
490                 if (stat_device(name, export, export_prefix) != 0)
491                         rc = 6;
492                 break;
493         case ACTION_ROOT:
494                 printf("%s\n", udev_get_dev_path(udev));
495                 break;
496         default:
497                 fprintf(stderr, "missing option\n");
498                 rc = 1;
499                 break;
500         }
501
502 exit:
503         udev_device_unref(device);
504         return rc;
505 }