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