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