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