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