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