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