chiark / gitweb /
libudev: enumerate - split new() and scan()
[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 *udev_enumerate;
187         struct udev_list_entry *list_entry;
188
189         udev_enumerate = udev_enumerate_new(udev);
190         if (udev_enumerate == NULL)
191                 return -1;
192         udev_enumerate_scan_devices(udev_enumerate, NULL);
193         udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
194                 struct udev_device *device;
195
196                 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
197                 if (device != NULL) {
198                         if (udev_device_get_devnode(device) != NULL)
199                                 print_record(device);
200                         udev_device_unref(device);
201                 }
202         }
203         udev_enumerate_unref(udev_enumerate);
204         return 0;
205 }
206
207 int udevadm_info(struct udev *udev, int argc, char *argv[])
208 {
209         struct udev_device *device = NULL;
210         int root = 0;
211         int export = 0;
212         const char *export_prefix = NULL;
213         char path[UTIL_PATH_SIZE];
214         char name[UTIL_PATH_SIZE];
215         struct udev_list_entry *list_entry;
216         int rc = 0;
217
218         static const struct option options[] = {
219                 { "name", 1, NULL, 'n' },
220                 { "path", 1, NULL, 'p' },
221                 { "query", 1, NULL, 'q' },
222                 { "attribute-walk", 0, NULL, 'a' },
223                 { "export-db", 0, NULL, 'e' },
224                 { "root", 0, NULL, 'r' },
225                 { "device-id-of-file", 1, NULL, 'd' },
226                 { "export", 0, NULL, 'x' },
227                 { "export-prefix", 1, NULL, 'P' },
228                 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
229                 { "help", 0, NULL, 'h' },
230                 {}
231         };
232
233         enum action_type {
234                 ACTION_NONE,
235                 ACTION_QUERY,
236                 ACTION_ATTRIBUTE_WALK,
237                 ACTION_ROOT,
238                 ACTION_DEVICE_ID_FILE,
239         } action = ACTION_NONE;
240
241         enum query_type {
242                 QUERY_NONE,
243                 QUERY_NAME,
244                 QUERY_PATH,
245                 QUERY_SYMLINK,
246                 QUERY_ENV,
247                 QUERY_ALL,
248         } query = QUERY_NONE;
249
250         while (1) {
251                 int option;
252                 struct stat statbuf;
253
254                 option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL);
255                 if (option == -1)
256                         break;
257
258                 dbg(udev, "option '%c'\n", option);
259                 switch (option) {
260                 case 'n':
261                         if (device != NULL) {
262                                 fprintf(stderr, "device already specified\n");
263                                 rc = 2;
264                                 goto exit;
265                         }
266                         /* remove /dev if given */
267                         if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) != 0) {
268                                 util_strlcpy(name, udev_get_dev_path(udev), sizeof(name));
269                                 util_strlcat(name, "/", sizeof(name));
270                                 util_strlcat(name, optarg, sizeof(name));
271                         } else {
272                                 util_strlcpy(name, optarg, sizeof(name));
273                         }
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_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
308                                 util_strlcat(path, optarg, sizeof(path));
309                         } else {
310                                 util_strlcpy(path, optarg, sizeof(path));
311                         }
312                         util_remove_trailing_chars(path, '/');
313                         device = udev_device_new_from_syspath(udev, path);
314                         if (device == NULL) {
315                                 fprintf(stderr, "device path not found\n");
316                                 rc = 2;
317                                 goto exit;
318                         }
319                         break;
320                 case 'q':
321                         action = ACTION_QUERY;
322                         if (strcmp(optarg, "name") == 0) {
323                                 query = QUERY_NAME;
324                                 break;
325                         }
326                         if (strcmp(optarg, "symlink") == 0) {
327                                 query = QUERY_SYMLINK;
328                                 break;
329                         }
330                         if (strcmp(optarg, "path") == 0) {
331                                 query = QUERY_PATH;
332                                 break;
333                         }
334                         if (strcmp(optarg, "env") == 0) {
335                                 query = QUERY_ENV;
336                                 break;
337                         }
338                         if (strcmp(optarg, "all") == 0) {
339                                 query = QUERY_ALL;
340                                 break;
341                         }
342                         fprintf(stderr, "unknown query type\n");
343                         rc = 2;
344                         goto exit;
345                 case 'r':
346                         if (action == ACTION_NONE)
347                                 action = ACTION_ROOT;
348                         root = 1;
349                         break;
350                 case 'd':
351                         action = ACTION_DEVICE_ID_FILE;
352                         util_strlcpy(name, optarg, sizeof(name));
353                         break;
354                 case 'a':
355                         action = ACTION_ATTRIBUTE_WALK;
356                         break;
357                 case 'e':
358                         export_devices(udev);
359                         goto exit;
360                 case 'x':
361                         export = 1;
362                         break;
363                 case 'P':
364                         export_prefix = optarg;
365                         break;
366                 case 1:
367                         printf("%s\n", VERSION);
368                         goto exit;
369                 case 'V':
370                         printf("udevinfo, version %s\n", VERSION);
371                         goto exit;
372                 case 'h':
373                         printf("Usage: udevadm info OPTIONS\n"
374                                "  --query=<type>             query device information:\n"
375                                "      name                     name of device node\n"
376                                "      symlink                  pointing to node\n"
377                                "      path                     sys device path\n"
378                                "      env                      the device related imported environment\n"
379                                "      all                      all values\n"
380                                "  --path=<syspath>           sys device path used for query or attribute walk\n"
381                                "  --name=<name>              node or symlink name used for query or attribute walk\n"
382                                "  --root                     prepend dev directory to path names\n"
383                                "  --attribute-walk           print all key matches while walking along the chain\n"
384                                "                             of parent devices\n"
385                                "  --device-id-of-file=<file> print major:minor of device containing this file\n"
386                                "  --export-db                export the content of the udev database\n"
387                                "  --help                     print this text\n"
388                                "\n");
389                         goto exit;
390                 default:
391                         goto exit;
392                 }
393         }
394
395         switch (action) {
396         case ACTION_QUERY:
397                 if (device == NULL) {
398                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
399                         rc = 4;
400                         goto exit;
401                 }
402
403                 switch(query) {
404                 case QUERY_NAME:
405                         if (root) {
406                                 printf("%s\n", udev_device_get_devnode(device));
407                         } else {
408                                 size_t len;
409
410                                 len = strlen(udev_get_dev_path(udev));
411                                 printf("%s\n", &udev_device_get_devnode(device)[len+1]);
412                         }
413                         break;
414                 case QUERY_SYMLINK:
415                         list_entry = udev_device_get_devlinks_list_entry(device);
416                         while (list_entry != NULL) {
417                                 if (root) {
418                                         printf("%s", udev_list_entry_get_name(list_entry));
419                                 } else {
420                                         size_t len;
421
422                                         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
423                                         printf("%s", &udev_list_entry_get_name(list_entry)[len+1]);
424                                 }
425                                 list_entry = udev_list_entry_get_next(list_entry);
426                                 if (list_entry != NULL)
427                                         printf(" ");
428                         }
429                         printf("\n");
430                         break;
431                 case QUERY_PATH:
432                         printf("%s\n", udev_device_get_devpath(device));
433                         goto exit;
434                 case QUERY_ENV:
435                         list_entry = udev_device_get_properties_list_entry(device);
436                         while (list_entry != NULL) {
437                                 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
438                                 list_entry = udev_list_entry_get_next(list_entry);
439                         }
440                         break;
441                 case QUERY_ALL:
442                         print_record(device);
443                         break;
444                 default:
445                         fprintf(stderr, "unknown query type\n");
446                         break;
447                 }
448                 break;
449         case ACTION_ATTRIBUTE_WALK:
450                 if (device == NULL) {
451                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
452                         rc = 5;
453                         goto exit;
454                 }
455                 print_device_chain(device);
456                 break;
457         case ACTION_DEVICE_ID_FILE:
458                 if (stat_device(name, export, export_prefix) != 0)
459                         rc = 6;
460                 break;
461         case ACTION_ROOT:
462                 printf("%s\n", udev_get_dev_path(udev));
463                 break;
464         default:
465                 fprintf(stderr, "missing option\n");
466                 rc = 1;
467                 break;
468         }
469
470 exit:
471         udev_device_unref(device);
472         return rc;
473 }