chiark / gitweb /
Also merge into the top-level Makefile.am the simpler extras.
[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'(%zi)\n", dent->d_name, value, len);
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         i = udev_device_get_num_fake_partitions(device);
146         if (i != 0)
147                 printf("A: %u\n", i);
148
149         i = udev_device_get_ignore_remove(device);
150         if (i != 0)
151                 printf("R: %u\n", i);
152
153         i = udev_device_get_watch_handle(device);
154         if (i >= 0)
155                 printf("W: %u\n", i);
156
157         udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) {
158                 len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
159                 printf("S: %s\n", &udev_list_entry_get_name(list_entry)[len+1]);
160         }
161
162         udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
163                 printf("E: %s=%s\n",
164                        udev_list_entry_get_name(list_entry),
165                        udev_list_entry_get_value(list_entry));
166         printf("\n");
167 }
168
169 static int stat_device(const char *name, int export, const char *prefix)
170 {
171         struct stat statbuf;
172
173         if (stat(name, &statbuf) != 0)
174                 return -1;
175
176         if (export) {
177                 if (prefix == NULL)
178                         prefix = "INFO_";
179                 printf("%sMAJOR=%d\n"
180                        "%sMINOR=%d\n",
181                        prefix, major(statbuf.st_dev),
182                        prefix, minor(statbuf.st_dev));
183         } else
184                 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
185         return 0;
186 }
187
188 static int export_devices(struct udev *udev)
189 {
190         struct udev_enumerate *udev_enumerate;
191         struct udev_list_entry *list_entry;
192
193         udev_enumerate = udev_enumerate_new(udev);
194         if (udev_enumerate == NULL)
195                 return -1;
196         udev_enumerate_scan_devices(udev_enumerate);
197         udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
198                 struct udev_device *device;
199
200                 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
201                 if (device != NULL) {
202                         print_record(device);
203                         udev_device_unref(device);
204                 }
205         }
206         udev_enumerate_unref(udev_enumerate);
207         return 0;
208 }
209
210 int udevadm_info(struct udev *udev, int argc, char *argv[])
211 {
212         struct udev_device *device = NULL;
213         int root = 0;
214         int export = 0;
215         const char *export_prefix = NULL;
216         char path[UTIL_PATH_SIZE];
217         char name[UTIL_PATH_SIZE];
218         struct udev_list_entry *list_entry;
219         int rc = 0;
220
221         static const struct option options[] = {
222                 { "name", required_argument, NULL, 'n' },
223                 { "path", required_argument, NULL, 'p' },
224                 { "query", required_argument, NULL, 'q' },
225                 { "attribute-walk", no_argument, NULL, 'a' },
226                 { "export-db", no_argument, NULL, 'e' },
227                 { "root", no_argument, NULL, 'r' },
228                 { "device-id-of-file", required_argument, NULL, 'd' },
229                 { "export", no_argument, NULL, 'x' },
230                 { "export-prefix", required_argument, NULL, 'P' },
231                 { "version", no_argument, NULL, 'V' },
232                 { "help", no_argument, NULL, 'h' },
233                 {}
234         };
235
236         enum action_type {
237                 ACTION_NONE,
238                 ACTION_QUERY,
239                 ACTION_ATTRIBUTE_WALK,
240                 ACTION_ROOT,
241                 ACTION_DEVICE_ID_FILE,
242         } action = ACTION_NONE;
243
244         enum query_type {
245                 QUERY_NONE,
246                 QUERY_NAME,
247                 QUERY_PATH,
248                 QUERY_SYMLINK,
249                 QUERY_PROPERTY,
250                 QUERY_ALL,
251         } query = QUERY_NONE;
252
253         while (1) {
254                 int option;
255                 struct stat statbuf;
256
257                 option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL);
258                 if (option == -1)
259                         break;
260
261                 dbg(udev, "option '%c'\n", option);
262                 switch (option) {
263                 case 'n':
264                         if (device != NULL) {
265                                 fprintf(stderr, "device already specified\n");
266                                 rc = 2;
267                                 goto exit;
268                         }
269                         /* remove /dev if given */
270                         if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) != 0)
271                                 util_strscpyl(name, sizeof(name), udev_get_dev_path(udev), "/", optarg, NULL);
272                         else
273                                 util_strscpy(name, sizeof(name), optarg);
274                         util_remove_trailing_chars(name, '/');
275                         if (stat(name, &statbuf) < 0) {
276                                 fprintf(stderr, "device node not found\n");
277                                 rc = 2;
278                                 goto exit;
279                         } else {
280                                 char type;
281
282                                 if (S_ISBLK(statbuf.st_mode)) {
283                                         type = 'b';
284                                 } else if (S_ISCHR(statbuf.st_mode)) {
285                                         type = 'c';
286                                 } else {
287                                         fprintf(stderr, "device node has wrong file type\n");
288                                         rc = 2;
289                                         goto exit;
290                                 }
291                                 device = udev_device_new_from_devnum(udev, type, statbuf.st_rdev);
292                                 if (device == NULL) {
293                                         fprintf(stderr, "device node not found\n");
294                                         rc = 2;
295                                         goto exit;
296                                 }
297                         }
298                         break;
299                 case 'p':
300                         if (device != NULL) {
301                                 fprintf(stderr, "device already specified\n");
302                                 rc = 2;
303                                 goto exit;
304                         }
305                         /* add sys dir if needed */
306                         if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0)
307                                 util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), optarg, NULL);
308                         else
309                                 util_strscpy(path, sizeof(path), optarg);
310                         util_remove_trailing_chars(path, '/');
311                         device = udev_device_new_from_syspath(udev, path);
312                         if (device == NULL) {
313                                 fprintf(stderr, "device path not found\n");
314                                 rc = 2;
315                                 goto exit;
316                         }
317                         break;
318                 case 'q':
319                         action = ACTION_QUERY;
320                         if (strcmp(optarg, "property") == 0 || strcmp(optarg, "env") == 0) {
321                                 query = QUERY_PROPERTY;
322                         } else if (strcmp(optarg, "name") == 0) {
323                                 query = QUERY_NAME;
324                         } else if (strcmp(optarg, "symlink") == 0) {
325                                 query = QUERY_SYMLINK;
326                         } else if (strcmp(optarg, "path") == 0) {
327                                 query = QUERY_PATH;
328                         } else if (strcmp(optarg, "all") == 0) {
329                                 query = QUERY_ALL;
330                         } else {
331                                 fprintf(stderr, "unknown query type\n");
332                                 rc = 3;
333                                 goto exit;
334                         }
335                         break;
336                 case 'r':
337                         if (action == ACTION_NONE)
338                                 action = ACTION_ROOT;
339                         root = 1;
340                         break;
341                 case 'd':
342                         action = ACTION_DEVICE_ID_FILE;
343                         util_strscpy(name, sizeof(name), optarg);
344                         break;
345                 case 'a':
346                         action = ACTION_ATTRIBUTE_WALK;
347                         break;
348                 case 'e':
349                         export_devices(udev);
350                         goto exit;
351                 case 'x':
352                         export = 1;
353                         break;
354                 case 'P':
355                         export_prefix = optarg;
356                         break;
357                 case 'V':
358                         printf("%s\n", VERSION);
359                         goto exit;
360                 case 'h':
361                         printf("Usage: udevadm info OPTIONS\n"
362                                "  --query=<type>             query device information:\n"
363                                "      name                     name of device node\n"
364                                "      symlink                  pointing to node\n"
365                                "      path                     sys device path\n"
366                                "      property                 the device properties\n"
367                                "      all                      all values\n"
368                                "  --path=<syspath>           sys device path used for query or attribute walk\n"
369                                "  --name=<name>              node or symlink name used for query or attribute walk\n"
370                                "  --root                     prepend dev directory to path names\n"
371                                "  --attribute-walk           print all key matches while walking along the chain\n"
372                                "                             of parent devices\n"
373                                "  --device-id-of-file=<file> print major:minor of device containing this file\n"
374                                "  --export-db                export the content of the udev database\n"
375                                "  --help\n\n");
376                         goto exit;
377                 default:
378                         goto exit;
379                 }
380         }
381
382         switch (action) {
383         case ACTION_QUERY:
384                 if (device == NULL) {
385                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
386                         rc = 4;
387                         goto exit;
388                 }
389
390                 switch(query) {
391                 case QUERY_NAME:
392                         if (root) {
393                                 printf("%s\n", udev_device_get_devnode(device));
394                         } else {
395                                 size_t len;
396                                 const char *node;
397
398                                 len = strlen(udev_get_dev_path(udev));
399                                 node = udev_device_get_devnode(device);
400                                 if (node == NULL) {
401                                         fprintf(stderr, "no device node found\n");
402                                         rc = 5;
403                                         goto exit;
404                                 }
405                                         printf("%s\n", &udev_device_get_devnode(device)[len+1]);
406                         }
407                         break;
408                 case QUERY_SYMLINK:
409                         list_entry = udev_device_get_devlinks_list_entry(device);
410                         while (list_entry != NULL) {
411                                 if (root) {
412                                         printf("%s", udev_list_entry_get_name(list_entry));
413                                 } else {
414                                         size_t len;
415
416                                         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
417                                         printf("%s", &udev_list_entry_get_name(list_entry)[len+1]);
418                                 }
419                                 list_entry = udev_list_entry_get_next(list_entry);
420                                 if (list_entry != NULL)
421                                         printf(" ");
422                         }
423                         printf("\n");
424                         break;
425                 case QUERY_PATH:
426                         printf("%s\n", udev_device_get_devpath(device));
427                         goto exit;
428                 case QUERY_PROPERTY:
429                         list_entry = udev_device_get_properties_list_entry(device);
430                         while (list_entry != NULL) {
431                                 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
432                                 list_entry = udev_list_entry_get_next(list_entry);
433                         }
434                         break;
435                 case QUERY_ALL:
436                         print_record(device);
437                         break;
438                 default:
439                         fprintf(stderr, "unknown query type\n");
440                         break;
441                 }
442                 break;
443         case ACTION_ATTRIBUTE_WALK:
444                 if (device == NULL) {
445                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
446                         rc = 4;
447                         goto exit;
448                 }
449                 print_device_chain(device);
450                 break;
451         case ACTION_DEVICE_ID_FILE:
452                 if (stat_device(name, export, export_prefix) != 0)
453                         rc = 1;
454                 break;
455         case ACTION_ROOT:
456                 printf("%s\n", udev_get_dev_path(udev));
457                 break;
458         default:
459                 fprintf(stderr, "missing option\n");
460                 rc = 1;
461                 break;
462         }
463
464 exit:
465         udev_device_unref(device);
466         return rc;
467 }