chiark / gitweb /
b395ad93b65c581405b8fbc56b2f83cef41a192a
[elogind.git] / udev / udevadm-info.c
1 /*
2  * Copyright (C) 2004-2009 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 <stdlib.h>
19 #include <string.h>
20 #include <stdio.h>
21 #include <stddef.h>
22 #include <ctype.h>
23 #include <stdarg.h>
24 #include <unistd.h>
25 #include <dirent.h>
26 #include <errno.h>
27 #include <getopt.h>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31
32 #include "udev.h"
33
34 static void print_all_attributes(struct udev_device *device, const char *key)
35 {
36         struct udev *udev = udev_device_get_udev(device);
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                         const char *value;
45                         size_t len;
46
47                         if (dent->d_name[0] == '.')
48                                 continue;
49
50                         if (strcmp(dent->d_name, "uevent") == 0)
51                                 continue;
52                         if (strcmp(dent->d_name, "dev") == 0)
53                                 continue;
54
55                         if (fstatat(dirfd(dir), dent->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) != 0)
56                                 continue;
57                         if (S_ISLNK(statbuf.st_mode))
58                                 continue;
59
60                         value = udev_device_get_sysattr_value(device, dent->d_name);
61                         if (value == NULL)
62                                 continue;
63                         dbg(udev, "attr '%s'='%s'\n", dent->d_name, value);
64
65                         /* skip nonprintable attributes */
66                         len = strlen(value);
67                         while (len > 0 && isprint(value[len-1]))
68                                 len--;
69                         if (len > 0) {
70                                 dbg(udev, "attribute value of '%s' non-printable, skip\n", dent->d_name);
71                                 continue;
72                         }
73
74                         printf("    %s{%s}==\"%s\"\n", key, dent->d_name, value);
75                 }
76                 closedir(dir);
77         }
78         printf("\n");
79 }
80
81 static int print_device_chain(struct udev_device *device)
82 {
83         struct udev_device *device_parent;
84         const char *str;
85
86         printf("\n"
87                "Udevadm info starts with the device specified by the devpath and then\n"
88                "walks up the chain of parent devices. It prints for every device\n"
89                "found, all possible attributes in the udev rules key format.\n"
90                "A rule to match, can be composed by the attributes of the device\n"
91                "and the attributes from one single parent device.\n"
92                "\n");
93
94         printf("  looking at device '%s':\n", udev_device_get_devpath(device));
95         printf("    KERNEL==\"%s\"\n", udev_device_get_sysname(device));
96         str = udev_device_get_subsystem(device);
97         if (str == NULL)
98                 str = "";
99         printf("    SUBSYSTEM==\"%s\"\n", str);
100         str = udev_device_get_driver(device);
101         if (str == NULL)
102                 str = "";
103         printf("    DRIVER==\"%s\"\n", str);
104         print_all_attributes(device, "ATTR");
105
106         device_parent = device;
107         do {
108                 device_parent = udev_device_get_parent(device_parent);
109                 if (device_parent == NULL)
110                         break;
111                 printf("  looking at parent device '%s':\n", udev_device_get_devpath(device_parent));
112                 printf("    KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent));
113                 str = udev_device_get_subsystem(device_parent);
114                 if (str == NULL)
115                         str = "";
116                 printf("    SUBSYSTEMS==\"%s\"\n", str);
117                 str = udev_device_get_driver(device_parent);
118                 if (str == NULL)
119                         str = "";
120                 printf("    DRIVERS==\"%s\"\n", str);
121                 print_all_attributes(device_parent, "ATTRS");
122         } while (device_parent != NULL);
123
124         return 0;
125 }
126
127 static void print_record(struct udev_device *device)
128 {
129         size_t len;
130         const char *str;
131         int i;
132         struct udev_list_entry *list_entry;
133
134         printf("P: %s\n", udev_device_get_devpath(device));
135
136         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
137         str = udev_device_get_devnode(device);
138         if (str != NULL)
139                 printf("N: %s\n", &str[len+1]);
140
141         i = udev_device_get_devlink_priority(device);
142         if (i != 0)
143                 printf("L: %i\n", i);
144
145         udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) {
146                 len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
147                 printf("S: %s\n", &udev_list_entry_get_name(list_entry)[len+1]);
148         }
149
150         udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
151                 printf("E: %s=%s\n",
152                        udev_list_entry_get_name(list_entry),
153                        udev_list_entry_get_value(list_entry));
154         printf("\n");
155 }
156
157 static int stat_device(const char *name, int export, const char *prefix)
158 {
159         struct stat statbuf;
160
161         if (stat(name, &statbuf) != 0)
162                 return -1;
163
164         if (export) {
165                 if (prefix == NULL)
166                         prefix = "INFO_";
167                 printf("%sMAJOR=%d\n"
168                        "%sMINOR=%d\n",
169                        prefix, major(statbuf.st_dev),
170                        prefix, minor(statbuf.st_dev));
171         } else
172                 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
173         return 0;
174 }
175
176 static int export_devices(struct udev *udev)
177 {
178         struct udev_enumerate *udev_enumerate;
179         struct udev_list_entry *list_entry;
180
181         udev_enumerate = udev_enumerate_new(udev);
182         if (udev_enumerate == NULL)
183                 return -1;
184         udev_enumerate_scan_devices(udev_enumerate);
185         udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
186                 struct udev_device *device;
187
188                 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
189                 if (device != NULL) {
190                         print_record(device);
191                         udev_device_unref(device);
192                 }
193         }
194         udev_enumerate_unref(udev_enumerate);
195         return 0;
196 }
197
198 int udevadm_info(struct udev *udev, int argc, char *argv[])
199 {
200         struct udev_device *device = NULL;
201         int root = 0;
202         int export = 0;
203         const char *export_prefix = NULL;
204         char path[UTIL_PATH_SIZE];
205         char name[UTIL_PATH_SIZE];
206         struct udev_list_entry *list_entry;
207         int rc = 0;
208
209         static const struct option options[] = {
210                 { "name", required_argument, NULL, 'n' },
211                 { "path", required_argument, NULL, 'p' },
212                 { "query", required_argument, NULL, 'q' },
213                 { "attribute-walk", no_argument, NULL, 'a' },
214                 { "export-db", no_argument, NULL, 'e' },
215                 { "root", no_argument, NULL, 'r' },
216                 { "device-id-of-file", required_argument, NULL, 'd' },
217                 { "export", no_argument, NULL, 'x' },
218                 { "export-prefix", required_argument, NULL, 'P' },
219                 { "version", no_argument, NULL, 'V' },
220                 { "help", no_argument, NULL, 'h' },
221                 {}
222         };
223
224         enum action_type {
225                 ACTION_NONE,
226                 ACTION_QUERY,
227                 ACTION_ATTRIBUTE_WALK,
228                 ACTION_ROOT,
229                 ACTION_DEVICE_ID_FILE,
230         } action = ACTION_NONE;
231
232         enum query_type {
233                 QUERY_NONE,
234                 QUERY_NAME,
235                 QUERY_PATH,
236                 QUERY_SYMLINK,
237                 QUERY_PROPERTY,
238                 QUERY_ALL,
239         } query = QUERY_NONE;
240
241         while (1) {
242                 int option;
243                 struct stat statbuf;
244
245                 option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL);
246                 if (option == -1)
247                         break;
248
249                 dbg(udev, "option '%c'\n", option);
250                 switch (option) {
251                 case 'n':
252                         if (device != NULL) {
253                                 fprintf(stderr, "device already specified\n");
254                                 rc = 2;
255                                 goto exit;
256                         }
257                         /* remove /dev if given */
258                         if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) != 0)
259                                 util_strscpyl(name, sizeof(name), udev_get_dev_path(udev), "/", optarg, NULL);
260                         else
261                                 util_strscpy(name, sizeof(name), optarg);
262                         util_remove_trailing_chars(name, '/');
263                         if (stat(name, &statbuf) < 0) {
264                                 fprintf(stderr, "device node not found\n");
265                                 rc = 2;
266                                 goto exit;
267                         } else {
268                                 char type;
269
270                                 if (S_ISBLK(statbuf.st_mode)) {
271                                         type = 'b';
272                                 } else if (S_ISCHR(statbuf.st_mode)) {
273                                         type = 'c';
274                                 } else {
275                                         fprintf(stderr, "device node has wrong file type\n");
276                                         rc = 2;
277                                         goto exit;
278                                 }
279                                 device = udev_device_new_from_devnum(udev, type, statbuf.st_rdev);
280                                 if (device == NULL) {
281                                         fprintf(stderr, "device node not found\n");
282                                         rc = 2;
283                                         goto exit;
284                                 }
285                         }
286                         break;
287                 case 'p':
288                         if (device != NULL) {
289                                 fprintf(stderr, "device already specified\n");
290                                 rc = 2;
291                                 goto exit;
292                         }
293                         /* add sys dir if needed */
294                         if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0)
295                                 util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), optarg, NULL);
296                         else
297                                 util_strscpy(path, sizeof(path), optarg);
298                         util_remove_trailing_chars(path, '/');
299                         device = udev_device_new_from_syspath(udev, path);
300                         if (device == NULL) {
301                                 fprintf(stderr, "device path not found\n");
302                                 rc = 2;
303                                 goto exit;
304                         }
305                         break;
306                 case 'q':
307                         action = ACTION_QUERY;
308                         if (strcmp(optarg, "property") == 0 || strcmp(optarg, "env") == 0) {
309                                 query = QUERY_PROPERTY;
310                         } else if (strcmp(optarg, "name") == 0) {
311                                 query = QUERY_NAME;
312                         } else if (strcmp(optarg, "symlink") == 0) {
313                                 query = QUERY_SYMLINK;
314                         } else if (strcmp(optarg, "path") == 0) {
315                                 query = QUERY_PATH;
316                         } else if (strcmp(optarg, "all") == 0) {
317                                 query = QUERY_ALL;
318                         } else {
319                                 fprintf(stderr, "unknown query type\n");
320                                 rc = 3;
321                                 goto exit;
322                         }
323                         break;
324                 case 'r':
325                         if (action == ACTION_NONE)
326                                 action = ACTION_ROOT;
327                         root = 1;
328                         break;
329                 case 'd':
330                         action = ACTION_DEVICE_ID_FILE;
331                         util_strscpy(name, sizeof(name), optarg);
332                         break;
333                 case 'a':
334                         action = ACTION_ATTRIBUTE_WALK;
335                         break;
336                 case 'e':
337                         export_devices(udev);
338                         goto exit;
339                 case 'x':
340                         export = 1;
341                         break;
342                 case 'P':
343                         export_prefix = optarg;
344                         break;
345                 case 'V':
346                         printf("%s\n", VERSION);
347                         goto exit;
348                 case 'h':
349                         printf("Usage: udevadm info OPTIONS\n"
350                                "  --query=<type>             query device information:\n"
351                                "      name                     name of device node\n"
352                                "      symlink                  pointing to node\n"
353                                "      path                     sys device path\n"
354                                "      property                 the device properties\n"
355                                "      all                      all values\n"
356                                "  --path=<syspath>           sys device path used for query or attribute walk\n"
357                                "  --name=<name>              node or symlink name used for query or attribute walk\n"
358                                "  --root                     prepend dev directory to path names\n"
359                                "  --attribute-walk           print all key matches while walking along the chain\n"
360                                "                             of parent devices\n"
361                                "  --device-id-of-file=<file> print major:minor of device containing this file\n"
362                                "  --export-db                export the content of the udev database\n"
363                                "  --help\n\n");
364                         goto exit;
365                 default:
366                         goto exit;
367                 }
368         }
369
370         switch (action) {
371         case ACTION_QUERY:
372                 if (device == NULL) {
373                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
374                         rc = 4;
375                         goto exit;
376                 }
377
378                 switch(query) {
379                 case QUERY_NAME: {
380                         const char *node = udev_device_get_devnode(device);
381
382                         if (node == NULL) {
383                                 fprintf(stderr, "no device node found\n");
384                                 rc = 5;
385                                 goto exit;
386                         }
387
388                         if (root) {
389                                 printf("%s\n", udev_device_get_devnode(device));
390                         } else {
391                                 size_t len = strlen(udev_get_dev_path(udev));
392
393                                 printf("%s\n", &udev_device_get_devnode(device)[len+1]);
394                         }
395                         break;
396                 }
397                 case QUERY_SYMLINK:
398                         list_entry = udev_device_get_devlinks_list_entry(device);
399                         while (list_entry != NULL) {
400                                 if (root) {
401                                         printf("%s", udev_list_entry_get_name(list_entry));
402                                 } else {
403                                         size_t len;
404
405                                         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
406                                         printf("%s", &udev_list_entry_get_name(list_entry)[len+1]);
407                                 }
408                                 list_entry = udev_list_entry_get_next(list_entry);
409                                 if (list_entry != NULL)
410                                         printf(" ");
411                         }
412                         printf("\n");
413                         break;
414                 case QUERY_PATH:
415                         printf("%s\n", udev_device_get_devpath(device));
416                         goto exit;
417                 case QUERY_PROPERTY:
418                         list_entry = udev_device_get_properties_list_entry(device);
419                         while (list_entry != NULL) {
420                                 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
421                                 list_entry = udev_list_entry_get_next(list_entry);
422                         }
423                         break;
424                 case QUERY_ALL:
425                         print_record(device);
426                         break;
427                 default:
428                         fprintf(stderr, "unknown query type\n");
429                         break;
430                 }
431                 break;
432         case ACTION_ATTRIBUTE_WALK:
433                 if (device == NULL) {
434                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
435                         rc = 4;
436                         goto exit;
437                 }
438                 print_device_chain(device);
439                 break;
440         case ACTION_DEVICE_ID_FILE:
441                 if (stat_device(name, export, export_prefix) != 0)
442                         rc = 1;
443                 break;
444         case ACTION_ROOT:
445                 printf("%s\n", udev_get_dev_path(udev));
446                 break;
447         default:
448                 fprintf(stderr, "missing option\n");
449                 rc = 1;
450                 break;
451         }
452
453 exit:
454         udev_device_unref(device);
455         return rc;
456 }