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