chiark / gitweb /
remove redundant "remove trailing newlines" in udevadm info
[elogind.git] / udev / udevadm-info.c
1 /*
2  * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
3  *
4  *      This program is free software; you can redistribute it and/or modify it
5  *      under the terms of the GNU General Public License as published by the
6  *      Free Software Foundation version 2 of the License.
7  * 
8  *      This program is distributed in the hope that it will be useful, but
9  *      WITHOUT ANY WARRANTY; without even the implied warranty of
10  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  *      General Public License for more details.
12  * 
13  *      You should have received a copy of the GNU General Public License along
14  *      with this program; if not, write to the Free Software Foundation, Inc.,
15  *      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16  *
17  */
18
19 #include "config.h"
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <stddef.h>
25 #include <ctype.h>
26 #include <stdarg.h>
27 #include <unistd.h>
28 #include <dirent.h>
29 #include <errno.h>
30 #include <getopt.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33
34 #include "udev.h"
35
36 static void print_all_attributes(struct udev *udev, const char *devpath, const char *key)
37 {
38         char path[PATH_SIZE];
39         DIR *dir;
40         struct dirent *dent;
41
42         strlcpy(path, udev_get_sys_path(udev), sizeof(path));
43         strlcat(path, devpath, sizeof(path));
44
45         dir = opendir(path);
46         if (dir != NULL) {
47                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
48                         struct stat statbuf;
49                         char filename[PATH_SIZE];
50                         char *attr_value;
51                         char value[NAME_SIZE];
52                         size_t len;
53
54                         if (dent->d_name[0] == '.')
55                                 continue;
56
57                         if (strcmp(dent->d_name, "uevent") == 0)
58                                 continue;
59                         if (strcmp(dent->d_name, "dev") == 0)
60                                 continue;
61
62                         strlcpy(filename, path, sizeof(filename));
63                         strlcat(filename, "/", sizeof(filename));
64                         strlcat(filename, dent->d_name, sizeof(filename));
65                         if (lstat(filename, &statbuf) != 0)
66                                 continue;
67                         if (S_ISLNK(statbuf.st_mode))
68                                 continue;
69
70                         attr_value = sysfs_attr_get_value(udev, devpath, dent->d_name);
71                         if (attr_value == NULL)
72                                 continue;
73                         len = strlcpy(value, attr_value, sizeof(value));
74                         if(len >= sizeof(value))
75                                 len = sizeof(value) - 1;
76                         dbg(udev, "attr '%s'='%s'(%zi)\n", dent->d_name, value, len);
77
78                         /* skip nonprintable attributes */
79                         while (len && isprint(value[len-1]))
80                                 len--;
81                         if (len) {
82                                 dbg(udev, "attribute value of '%s' non-printable, skip\n", dent->d_name);
83                                 continue;
84                         }
85
86                         printf("    %s{%s}==\"%s\"\n", key, dent->d_name, value);
87                 }
88         }
89         printf("\n");
90 }
91
92 static int print_device_chain(struct udev *udev, const char *devpath)
93 {
94         struct sysfs_device *dev;
95
96         dev = sysfs_device_get(udev, devpath);
97         if (dev == NULL)
98                 return -1;
99
100         printf("\n"
101                "Udevinfo starts with the device specified by the devpath and then\n"
102                "walks up the chain of parent devices. It prints for every device\n"
103                "found, all possible attributes in the udev rules key format.\n"
104                "A rule to match, can be composed by the attributes of the device\n"
105                "and the attributes from one single parent device.\n"
106                "\n");
107
108         printf("  looking at device '%s':\n", dev->devpath);
109         printf("    KERNEL==\"%s\"\n", dev->kernel);
110         printf("    SUBSYSTEM==\"%s\"\n", dev->subsystem);
111         printf("    DRIVER==\"%s\"\n", dev->driver);
112         print_all_attributes(udev, dev->devpath, "ATTR");
113
114         /* walk up the chain of devices */
115         while (1) {
116                 dev = sysfs_device_get_parent(udev, dev);
117                 if (dev == NULL)
118                         break;
119                 printf("  looking at parent device '%s':\n", dev->devpath);
120                 printf("    KERNELS==\"%s\"\n", dev->kernel);
121                 printf("    SUBSYSTEMS==\"%s\"\n", dev->subsystem);
122                 printf("    DRIVERS==\"%s\"\n", dev->driver);
123
124                 print_all_attributes(udev, dev->devpath, "ATTRS");
125         }
126
127         return 0;
128 }
129
130 static void print_record(struct udevice *udevice)
131 {
132         struct name_entry *name_loop;
133
134         printf("P: %s\n", udevice->dev->devpath);
135         printf("N: %s\n", udevice->name);
136         list_for_each_entry(name_loop, &udevice->symlink_list, node)
137                 printf("S: %s\n", name_loop->name);
138         if (udevice->link_priority != 0)
139                 printf("L: %i\n", udevice->link_priority);
140         if (udevice->partitions != 0)
141                 printf("A:%u\n", udevice->partitions);
142         if (udevice->ignore_remove)
143                 printf("R:%u\n", udevice->ignore_remove);
144         list_for_each_entry(name_loop, &udevice->env_list, node)
145                 printf("E: %s\n", name_loop->name);
146 }
147
148 static void export_db(struct udev *udev)
149 {
150         LIST_HEAD(name_list);
151         struct name_entry *name_loop;
152
153         udev_db_get_all_entries(udev, &name_list);
154         list_for_each_entry(name_loop, &name_list, node) {
155                 struct udevice *udevice_db;
156
157                 udevice_db = udev_device_init(udev);
158                 if (udevice_db == NULL)
159                         continue;
160                 if (udev_db_get_device(udevice_db, name_loop->name) == 0)
161                         print_record(udevice_db);
162                         printf("\n");
163                 udev_device_cleanup(udevice_db);
164         }
165         name_list_cleanup(udev, &name_list);
166 }
167
168 static int lookup_device_by_name(struct udev *udev, struct udevice **udevice, const char *name)
169 {
170         LIST_HEAD(name_list);
171         int count;
172         struct name_entry *device;
173         int rc  = -1;
174
175         count = udev_db_get_devices_by_name(udev, name, &name_list);
176         if (count <= 0)
177                 goto out;
178
179         info(udev, "found %i devices for '%s'\n", count, name);
180
181         /* select the device that seems to match */
182         list_for_each_entry(device, &name_list, node) {
183                 struct udevice *udevice_loop;
184                 char filename[PATH_SIZE];
185                 struct stat statbuf;
186
187                 udevice_loop = udev_device_init(udev);
188                 if (udevice_loop == NULL)
189                         break;
190                 if (udev_db_get_device(udevice_loop, device->name) != 0)
191                         goto next;
192                 info(udev, "found db entry '%s'\n", device->name);
193
194                 /* make sure, we don't get a link of a different device */
195                 strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
196                 strlcat(filename, "/", sizeof(filename));
197                 strlcat(filename, name, sizeof(filename));
198                 if (stat(filename, &statbuf) != 0)
199                         goto next;
200                 if (major(udevice_loop->devt) > 0 && udevice_loop->devt != statbuf.st_rdev) {
201                         info(udev, "skip '%s', dev_t doesn't match\n", udevice_loop->name);
202                         goto next;
203                 }
204                 rc = 0;
205                 *udevice = udevice_loop;
206                 break;
207 next:
208                 udev_device_cleanup(udevice_loop);
209         }
210 out:
211         name_list_cleanup(udev, &name_list);
212         return rc;
213 }
214
215 static int stat_device(const char *name, int export, const char *prefix)
216 {
217         struct stat statbuf;
218
219         if (stat(name, &statbuf) != 0)
220                 return -1;
221
222         if (export) {
223                 if (prefix == NULL)
224                         prefix = "INFO_";
225                 printf("%sMAJOR=%d\n"
226                        "%sMINOR=%d\n",
227                        prefix, major(statbuf.st_dev),
228                        prefix, minor(statbuf.st_dev));
229         } else
230                 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
231         return 0;
232 }
233
234 int udevadm_info(struct udev *udev, int argc, char *argv[])
235 {
236         struct udevice *udevice = NULL;
237         int root = 0;
238         int export = 0;
239         const char *export_prefix = NULL;
240
241         static const struct option options[] = {
242                 { "name", 1, NULL, 'n' },
243                 { "path", 1, NULL, 'p' },
244                 { "query", 1, NULL, 'q' },
245                 { "attribute-walk", 0, NULL, 'a' },
246                 { "export-db", 0, NULL, 'e' },
247                 { "root", 0, NULL, 'r' },
248                 { "device-id-of-file", 1, NULL, 'd' },
249                 { "export", 0, NULL, 'x' },
250                 { "export-prefix", 1, NULL, 'P' },
251                 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
252                 { "help", 0, NULL, 'h' },
253                 {}
254         };
255
256         enum action_type {
257                 ACTION_NONE,
258                 ACTION_QUERY,
259                 ACTION_ATTRIBUTE_WALK,
260                 ACTION_ROOT,
261                 ACTION_DEVICE_ID_FILE,
262         } action = ACTION_NONE;
263
264         enum query_type {
265                 QUERY_NONE,
266                 QUERY_NAME,
267                 QUERY_PATH,
268                 QUERY_SYMLINK,
269                 QUERY_ENV,
270                 QUERY_ALL,
271         } query = QUERY_NONE;
272
273         char path[PATH_SIZE] = "";
274         char name[PATH_SIZE] = "";
275         struct name_entry *name_loop;
276         int rc = 0;
277
278         while (1) {
279                 int option;
280
281                 option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL);
282                 if (option == -1)
283                         break;
284
285                 dbg(udev, "option '%c'\n", option);
286                 switch (option) {
287                 case 'n':
288                         /* remove /dev if given */
289                         if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) == 0)
290                                 strlcpy(name, &optarg[strlen(udev_get_dev_path(udev))+1], sizeof(name));
291                         else
292                                 strlcpy(name, optarg, sizeof(name));
293                         remove_trailing_chars(name, '/');
294                         dbg(udev, "name: %s\n", name);
295                         break;
296                 case 'p':
297                         /* remove /sys if given */
298                         if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) == 0)
299                                 strlcpy(path, &optarg[strlen(udev_get_sys_path(udev))], sizeof(path));
300                         else
301                                 strlcpy(path, optarg, sizeof(path));
302                         remove_trailing_chars(path, '/');
303
304                         /* possibly resolve to real devpath */
305                         if (sysfs_resolve_link(udev, path, sizeof(path)) != 0) {
306                                 char temp[PATH_SIZE];
307                                 char *pos;
308
309                                 /* also check if the parent is a link */
310                                 strlcpy(temp, path, sizeof(temp));
311                                 pos = strrchr(temp, '/');
312                                 if (pos != 0) {
313                                         char tail[PATH_SIZE];
314
315                                         strlcpy(tail, pos, sizeof(tail));
316                                         pos[0] = '\0';
317                                         if (sysfs_resolve_link(udev, temp, sizeof(temp)) == 0) {
318                                                 strlcpy(path, temp, sizeof(path));
319                                                 strlcat(path, tail, sizeof(path));
320                                         }
321                                 }
322                         }
323                         dbg(udev, "path: %s\n", path);
324                         break;
325                 case 'q':
326                         action = ACTION_QUERY;
327                         if (strcmp(optarg, "name") == 0) {
328                                 query = QUERY_NAME;
329                                 break;
330                         }
331                         if (strcmp(optarg, "symlink") == 0) {
332                                 query = QUERY_SYMLINK;
333                                 break;
334                         }
335                         if (strcmp(optarg, "path") == 0) {
336                                 query = QUERY_PATH;
337                                 break;
338                         }
339                         if (strcmp(optarg, "env") == 0) {
340                                 query = QUERY_ENV;
341                                 break;
342                         }
343                         if (strcmp(optarg, "all") == 0) {
344                                 query = QUERY_ALL;
345                                 break;
346                         }
347                         fprintf(stderr, "unknown query type\n");
348                         rc = 2;
349                         goto exit;
350                 case 'r':
351                         if (action == ACTION_NONE)
352                                 action = ACTION_ROOT;
353                         root = 1;
354                         break;
355                 case 'd':
356                         action = ACTION_DEVICE_ID_FILE;
357                         strlcpy(name, optarg, sizeof(name));
358                         break;
359                 case 'a':
360                         action = ACTION_ATTRIBUTE_WALK;
361                         break;
362                 case 'e':
363                         export_db(udev);
364                         goto exit;
365                 case 'x':
366                         export = 1;
367                         break;
368                 case 'P':
369                         export_prefix = optarg;
370                         break;
371                 case 1:
372                         printf("%s\n", VERSION);
373                         goto exit;
374                 case 'V':
375                         printf("udevinfo, version %s\n", VERSION);
376                         goto exit;
377                 case 'h':
378                         printf("Usage: udevadm info OPTIONS\n"
379                                "  --query=<type>             query database for the specified value:\n"
380                                "      name                     name of device node\n"
381                                "      symlink                  pointing to node\n"
382                                "      path                     sysfs device path\n"
383                                "      env                      the device related imported environment\n"
384                                "      all                      all values\n"
385                                "  --path=<devpath>           sysfs device path used for query or chain\n"
386                                "  --name=<name>              node or symlink name used for query\n"
387                                "  --root                     prepend to query result or print udev_root\n"
388                                "  --attribute-walk           print all key matches while walking along chain\n"
389                                "                             of parent devices\n"
390                                "  --device-id-of-file=<file> print major/minor of underlying device\n"
391                                "  --export-db                export the content of the udev database\n"
392                                "  --help                     print this text\n"
393                                "\n");
394                         goto exit;
395                 default:
396                         goto exit;
397                 }
398         }
399
400         /* run action */
401         switch (action) {
402         case ACTION_QUERY:
403                 /* needs devpath or node/symlink name for query */
404                 if (path[0] != '\0') {
405                         udevice = udev_device_init(udev);
406                         if (udevice == NULL) {
407                                 rc = 1;
408                                 goto exit;
409                         }
410                         if (udev_db_get_device(udevice, path) != 0) {
411                                 fprintf(stderr, "no record for '%s' in database\n", path);
412                                 rc = 3;
413                                 goto exit;
414                         }
415                 } else if (name[0] != '\0') {
416                         if (lookup_device_by_name(udev, &udevice, name) != 0) {
417                                 fprintf(stderr, "node name not found\n");
418                                 rc = 4;
419                                 goto exit;
420                         }
421                 } else {
422                         fprintf(stderr, "query needs --path or node --name specified\n");
423                         rc = 4;
424                         goto exit;
425                 }
426
427                 switch(query) {
428                 case QUERY_NAME:
429                         if (root)
430                                 printf("%s/%s\n", udev_get_dev_path(udev), udevice->name);
431                         else
432                                 printf("%s\n", udevice->name);
433                         break;
434                 case QUERY_SYMLINK:
435                         list_for_each_entry(name_loop, &udevice->symlink_list, node) {
436                                 char c = name_loop->node.next != &udevice->symlink_list ? ' ' : '\n';
437
438                                 if (root)
439                                         printf("%s/%s%c", udev_get_dev_path(udev), name_loop->name, c);
440                                 else
441                                         printf("%s%c", name_loop->name, c);
442                         }
443                         break;
444                 case QUERY_PATH:
445                         printf("%s\n", udevice->dev->devpath);
446                         goto exit;
447                 case QUERY_ENV:
448                         list_for_each_entry(name_loop, &udevice->env_list, node)
449                                 printf("%s\n", name_loop->name);
450                         break;
451                 case QUERY_ALL:
452                         print_record(udevice);
453                         break;
454                 default:
455                         fprintf(stderr, "unknown query type\n");
456                         break;
457                 }
458                 break;
459         case ACTION_ATTRIBUTE_WALK:
460                 if (path[0] != '\0') {
461                         if (print_device_chain(udev, path) != 0) {
462                                 fprintf(stderr, "no valid sysfs device found\n");
463                                 rc = 4;
464                                 goto exit;
465                         }
466                 } else if (name[0] != '\0') {
467                         if (lookup_device_by_name(udev, &udevice, name) != 0) {
468                                 fprintf(stderr, "node name not found\n");
469                                 rc = 4;
470                                 goto exit;
471                         }
472                         if (print_device_chain(udev, udevice->dev->devpath) != 0) {
473                                 fprintf(stderr, "no valid sysfs device found\n");
474                                 rc = 4;
475                                 goto exit;
476                         }
477                 } else {
478                         fprintf(stderr, "attribute walk needs --path or node --name specified\n");
479                         rc = 5;
480                         goto exit;
481                 }
482                 break;
483         case ACTION_DEVICE_ID_FILE:
484                 if (stat_device(name, export, export_prefix) != 0)
485                         rc = 6;
486                 break;
487         case ACTION_ROOT:
488                 printf("%s\n", udev_get_dev_path(udev));
489                 break;
490         default:
491                 fprintf(stderr, "missing option\n");
492                 rc = 1;
493                 break;
494         }
495
496 exit:
497         udev_device_cleanup(udevice);
498         return rc;
499 }