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