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