chiark / gitweb /
cdrom_id: add error message if open() fails
[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 <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_ENV,
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, "name") == 0) {
321                                 query = QUERY_NAME;
322                                 break;
323                         }
324                         if (strcmp(optarg, "symlink") == 0) {
325                                 query = QUERY_SYMLINK;
326                                 break;
327                         }
328                         if (strcmp(optarg, "path") == 0) {
329                                 query = QUERY_PATH;
330                                 break;
331                         }
332                         if (strcmp(optarg, "env") == 0) {
333                                 query = QUERY_ENV;
334                                 break;
335                         }
336                         if (strcmp(optarg, "all") == 0) {
337                                 query = QUERY_ALL;
338                                 break;
339                         }
340                         fprintf(stderr, "unknown query type\n");
341                         rc = 3;
342                         goto exit;
343                 case 'r':
344                         if (action == ACTION_NONE)
345                                 action = ACTION_ROOT;
346                         root = 1;
347                         break;
348                 case 'd':
349                         action = ACTION_DEVICE_ID_FILE;
350                         util_strscpy(name, sizeof(name), optarg);
351                         break;
352                 case 'a':
353                         action = ACTION_ATTRIBUTE_WALK;
354                         break;
355                 case 'e':
356                         export_devices(udev);
357                         goto exit;
358                 case 'x':
359                         export = 1;
360                         break;
361                 case 'P':
362                         export_prefix = optarg;
363                         break;
364                 case 'V':
365                         printf("%s\n", VERSION);
366                         goto exit;
367                 case 'h':
368                         printf("Usage: udevadm info OPTIONS\n"
369                                "  --query=<type>             query device information:\n"
370                                "      name                     name of device node\n"
371                                "      symlink                  pointing to node\n"
372                                "      path                     sys device path\n"
373                                "      env                      the device related imported environment\n"
374                                "      all                      all values\n"
375                                "  --path=<syspath>           sys device path used for query or attribute walk\n"
376                                "  --name=<name>              node or symlink name used for query or attribute walk\n"
377                                "  --root                     prepend dev directory to path names\n"
378                                "  --attribute-walk           print all key matches while walking along the chain\n"
379                                "                             of parent devices\n"
380                                "  --device-id-of-file=<file> print major:minor of device containing this file\n"
381                                "  --export-db                export the content of the udev database\n"
382                                "  --help\n\n");
383                         goto exit;
384                 default:
385                         goto exit;
386                 }
387         }
388
389         switch (action) {
390         case ACTION_QUERY:
391                 if (device == NULL) {
392                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
393                         rc = 4;
394                         goto exit;
395                 }
396
397                 switch(query) {
398                 case QUERY_NAME:
399                         if (root) {
400                                 printf("%s\n", udev_device_get_devnode(device));
401                         } else {
402                                 size_t len;
403                                 const char *node;
404
405                                 len = strlen(udev_get_dev_path(udev));
406                                 node = udev_device_get_devnode(device);
407                                 if (node == NULL) {
408                                         fprintf(stderr, "no device node found\n");
409                                         rc = 5;
410                                         goto exit;
411                                 }
412                                         printf("%s\n", &udev_device_get_devnode(device)[len+1]);
413                         }
414                         break;
415                 case QUERY_SYMLINK:
416                         list_entry = udev_device_get_devlinks_list_entry(device);
417                         while (list_entry != NULL) {
418                                 if (root) {
419                                         printf("%s", udev_list_entry_get_name(list_entry));
420                                 } else {
421                                         size_t len;
422
423                                         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
424                                         printf("%s", &udev_list_entry_get_name(list_entry)[len+1]);
425                                 }
426                                 list_entry = udev_list_entry_get_next(list_entry);
427                                 if (list_entry != NULL)
428                                         printf(" ");
429                         }
430                         printf("\n");
431                         break;
432                 case QUERY_PATH:
433                         printf("%s\n", udev_device_get_devpath(device));
434                         goto exit;
435                 case QUERY_ENV:
436                         list_entry = udev_device_get_properties_list_entry(device);
437                         while (list_entry != NULL) {
438                                 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
439                                 list_entry = udev_list_entry_get_next(list_entry);
440                         }
441                         break;
442                 case QUERY_ALL:
443                         print_record(device);
444                         break;
445                 default:
446                         fprintf(stderr, "unknown query type\n");
447                         break;
448                 }
449                 break;
450         case ACTION_ATTRIBUTE_WALK:
451                 if (device == NULL) {
452                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
453                         rc = 4;
454                         goto exit;
455                 }
456                 print_device_chain(device);
457                 break;
458         case ACTION_DEVICE_ID_FILE:
459                 if (stat_device(name, export, export_prefix) != 0)
460                         rc = 1;
461                 break;
462         case ACTION_ROOT:
463                 printf("%s\n", udev_get_dev_path(udev));
464                 break;
465         default:
466                 fprintf(stderr, "missing option\n");
467                 rc = 1;
468                 break;
469         }
470
471 exit:
472         udev_device_unref(device);
473         return rc;
474 }