chiark / gitweb /
a0a17c40b4d1828e31eaa6c4f8318fe577a0b906
[elogind.git] / udevinfo.c
1 /*
2  * udevinfo.c - fetches stored device information or sysfs attributes
3  *
4  * Copyright (C) 2004-2005 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  *      This program is free software; you can redistribute it and/or modify it
7  *      under the terms of the GNU General Public License as published by the
8  *      Free Software Foundation version 2 of the License.
9  * 
10  *      This program is distributed in the hope that it will be useful, but
11  *      WITHOUT ANY WARRANTY; without even the implied warranty of
12  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *      General Public License for more details.
14  * 
15  *      You should have received a copy of the GNU General Public License along
16  *      with this program; if not, write to the Free Software Foundation, Inc.,
17  *      675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  */
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
31 #include "udev.h"
32
33
34 #ifdef USE_LOG
35 void log_message (int priority, const char *format, ...)
36 {
37         va_list args;
38
39         if (priority > udev_log_priority)
40                 return;
41
42         va_start(args, format);
43         vsyslog(priority, format, args);
44         va_end(args);
45 }
46 #endif
47
48 static void print_all_attributes(const char *devpath, const char *key)
49 {
50         char path[PATH_SIZE];
51         DIR *dir;
52         struct dirent *dent;
53
54         strlcpy(path, sysfs_path, sizeof(path));
55         strlcat(path, devpath, sizeof(path));
56
57         dir = opendir(path);
58         if (dir != NULL) {
59                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
60                         char *attr_value;
61                         char value[NAME_SIZE];
62                         size_t len;
63
64                         attr_value = sysfs_attr_get_value(devpath, dent->d_name);
65                         if (attr_value == NULL)
66                                 continue;
67                         len = strlcpy(value, attr_value, sizeof(value));
68                         dbg("attr '%s'='%s'(%zi)", dent->d_name, value, len);
69
70                         /* remove trailing newlines */
71                         while (len && value[len-1] == '\n')
72                                 value[--len] = '\0';
73
74                         /* skip nonprintable attributes */
75                         while (len && isprint(value[len-1]))
76                                 len--;
77                         if (len) {
78                                 dbg("attribute value of '%s' non-printable, skip", dent->d_name);
79                                 continue;
80                         }
81
82                         replace_untrusted_chars(value);
83                         printf("    %s{%s}==\"%s\"\n", key, dent->d_name, value);
84                 }
85         }
86         printf("\n");
87 }
88
89 static int print_device_chain(const char *devpath)
90 {
91         struct sysfs_device *dev;
92
93         printf("\n"
94                "Udevinfo starts with the device specified by the devpath and then\n"
95                "walks up the chain of parent devices. It prints for every device\n"
96                "found, all possible attributes in the udev rules key format.\n"
97                "A rule to match, can be composed by the attributes of the device\n"
98                "and the attributes from one single parent device.\n"
99                "\n");
100
101         dev = sysfs_device_get(devpath);
102         if (dev == NULL)
103                 return -1;
104
105         printf("  looking at device '%s':\n", dev->devpath);
106         printf("    KERNEL==\"%s\"\n", dev->kernel);
107         printf("    SUBSYSTEM==\"%s\"\n", dev->subsystem);
108         printf("    DRIVER==\"%s\"\n", dev->driver);
109         print_all_attributes(dev->devpath, "ATTR");
110
111         /* walk up the chain of devices */
112         while (1) {
113                 dev = sysfs_device_get_parent(dev);
114                 if (dev == NULL)
115                         break;
116                 printf("  looking at parent device '%s':\n", dev->devpath);
117                 printf("    KERNELS==\"%s\"\n", dev->kernel);
118                 printf("    SUBSYTEMS==\"%s\"\n", dev->subsystem);
119                 printf("    DRIVERS==\"%s\"\n", dev->driver);
120
121                 print_all_attributes(dev->devpath, "ATTRS");
122         }
123
124         return 0;
125 }
126
127 static void print_record(struct udevice *udev)
128 {
129         struct name_entry *name_loop;
130
131         printf("P: %s\n", udev->dev->devpath);
132         printf("N: %s\n", udev->name);
133         list_for_each_entry(name_loop, &udev->symlink_list, node)
134                 printf("S: %s\n", name_loop->name);
135         list_for_each_entry(name_loop, &udev->env_list, node)
136                 printf("E: %s\n", name_loop->name);
137 }
138
139 static void export_name_devpath(struct udevice *udev) {
140         printf("%s=%s/%s\n", udev->dev->devpath, udev_root, udev->name);
141 }
142
143 static void export_record(struct udevice *udev) {
144         print_record(udev);
145         printf("\n");
146 }
147
148 static void export_db(void fnct(struct udevice *udev)) {
149         LIST_HEAD(name_list);
150         struct name_entry *name_loop;
151
152         udev_db_get_all_entries(&name_list);
153         list_for_each_entry(name_loop, &name_list, node) {
154                 struct udevice *udev_db;
155
156                 udev_db = udev_device_init();
157                 if (udev_db == NULL)
158                         continue;
159                 if (udev_db_get_device(udev_db, name_loop->name) == 0)
160                         fnct(udev_db);
161                 udev_device_cleanup(udev_db);
162         }
163         name_list_cleanup(&name_list);
164 }
165
166 int main(int argc, char *argv[], char *envp[])
167 {
168         int option;
169         struct udevice *udev;
170         int root = 0;
171
172         enum action_type {
173                 ACTION_NONE,
174                 ACTION_QUERY,
175                 ACTION_ATTRIBUTE_WALK,
176                 ACTION_ROOT,
177         } action = ACTION_NONE;
178
179         enum query_type {
180                 QUERY_NONE,
181                 QUERY_NAME,
182                 QUERY_PATH,
183                 QUERY_SYMLINK,
184                 QUERY_ENV,
185                 QUERY_ALL,
186         } query = QUERY_NONE;
187
188         char path[PATH_SIZE] = "";
189         char name[PATH_SIZE] = "";
190         struct name_entry *name_loop;
191         int rc = 0;
192
193         logging_init("udevinfo");
194         udev_config_init();
195         sysfs_init();
196
197         udev = udev_device_init();
198         if (udev == NULL) {
199                 rc = 1;
200                 goto exit;
201         }
202
203         /* get command line options */
204         opterr = 0;
205         while (1) {
206                 option = getopt(argc, argv, ":aden:p:q:rVh");
207                 if (option == -1)
208                         break;
209
210                 dbg("option '%c'", option);
211                 switch (option) {
212                 case 'n':
213                         /* remove /dev if given */
214                         if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
215                                 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
216                         else
217                                 strlcpy(name, optarg, sizeof(name));
218                         dbg("name: %s\n", name);
219                         break;
220                 case 'p':
221                         /* remove /sys if given */
222                         if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
223                                 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
224                         else
225                                 strlcpy(path, optarg, sizeof(path));
226                         dbg("path: %s\n", path);
227                         break;
228                 case 'q':
229                         dbg("udev query: %s\n", optarg);
230                         action = ACTION_QUERY;
231                         if (strcmp(optarg, "name") == 0) {
232                                 query = QUERY_NAME;
233                                 break;
234                         }
235                         if (strcmp(optarg, "symlink") == 0) {
236                                 query = QUERY_SYMLINK;
237                                 break;
238                         }
239                         if (strcmp(optarg, "path") == 0) {
240                                 query = QUERY_PATH;
241                                 break;
242                         }
243                         if (strcmp(optarg, "env") == 0) {
244                                 query = QUERY_ENV;
245                                 break;
246                         }
247                         if (strcmp(optarg, "all") == 0) {
248                                 query = QUERY_ALL;
249                                 break;
250                         }
251                         fprintf(stderr, "unknown query type\n");
252                         rc = 2;
253                         goto exit;
254                 case 'r':
255                         if (action == ACTION_NONE)
256                                 action = ACTION_ROOT;
257                         root = 1;
258                         break;
259                 case 'a':
260                         action = ACTION_ATTRIBUTE_WALK;
261                         break;
262                 case 'd':
263                         export_db(export_name_devpath);
264                         goto exit;
265                 case 'e':
266                         export_db(export_record);
267                         goto exit;
268                 case 'V':
269                         printf("udevinfo, version %s\n", UDEV_VERSION);
270                         goto exit;
271                 case 'h':
272                         printf("Usage: udevinfo [-anpqrVh]\n"
273                                "  -q TYPE  query database for the specified value:\n"
274                                "             'name'    name of device node\n"
275                                "             'symlink' pointing to node\n"
276                                "             'path'    sysfs device path\n"
277                                "             'env'     the device related imported environment\n"
278                                "             'all'     all values\n"
279                                "\n"
280                                "  -p PATH  sysfs device path used for query or chain\n"
281                                "  -n NAME  node/symlink name used for query\n"
282                                "\n"
283                                "  -r       prepend to query result or print udev_root\n"
284                                "  -a       print all SYSFS_attributes along the device chain\n"
285                                "  -e       export the content of the udev database\n"
286                                "  -V       print udev version\n"
287                                "  -h       print this help text\n"
288                                "\n");
289                         goto exit;
290                 case ':':
291                         fprintf(stderr, "missing argument for '%c'\n", optopt);
292                         goto exit;
293                 case '?':
294                 default:
295                         fprintf(stderr, "unrecognized option '%c'\n", optopt);
296                         goto exit;
297                 }
298         }
299
300         /* run action */
301         switch (action) {
302         case ACTION_QUERY:
303                 /* needs devpath or node/symlink name for query */
304                 if (path[0] != '\0') {
305                         if (udev_db_get_device(udev, path) != 0) {
306                                 fprintf(stderr, "no record for '%s' in database\n", path);
307                                 rc = 3;
308                                 goto exit;
309                         }
310                 } else if (name[0] != '\0') {
311                         char devpath[PATH_SIZE];
312
313                         if (udev_db_lookup_name(name, devpath, sizeof(devpath)) != 0) {
314                                 fprintf(stderr, "node name not found\n");
315                                 rc = 4;
316                                 goto exit;
317                         }
318                         udev_db_get_device(udev, devpath);
319                 } else {
320                         fprintf(stderr, "query needs device path(-p) or node name(-n) specified\n");
321                         rc = 4;
322                         goto exit;
323                 }
324
325                 switch(query) {
326                 case QUERY_NAME:
327                         if (root)
328                                 printf("%s/%s\n", udev_root, udev->name);
329                         else
330                                 printf("%s\n", udev->name);
331                         break;
332                 case QUERY_SYMLINK:
333                         if (list_empty(&udev->symlink_list))
334                                 goto exit;
335                         if (root)
336                                 list_for_each_entry(name_loop, &udev->symlink_list, node)
337                                         printf("%s/%s ", udev_root, name_loop->name);
338                         else
339                                 list_for_each_entry(name_loop, &udev->symlink_list, node)
340                                         printf("%s ", name_loop->name);
341                         printf("\n");
342                         break;
343                 case QUERY_PATH:
344                         printf("%s\n", udev->dev->devpath);
345                         goto exit;
346                 case QUERY_ENV:
347                         list_for_each_entry(name_loop, &udev->env_list, node)
348                                 printf("%s\n", name_loop->name);
349                         break;
350                 case QUERY_ALL:
351                         print_record(udev);
352                         break;
353                 default:
354                         fprintf(stderr, "unknown query type\n");
355                         break;
356                 }
357                 break;
358         case ACTION_ATTRIBUTE_WALK:
359                 if (path[0] != '\0') {
360                         print_device_chain(path);
361                 } else if (name[0] != '\0') {
362                         char devpath[PATH_SIZE];
363
364                         if (udev_db_lookup_name(name, devpath, sizeof(devpath)) != 0) {
365                                 fprintf(stderr, "node name not found\n");
366                                 rc = 4;
367                                 goto exit;
368                         }
369                         print_device_chain(devpath);
370                 } else {
371                         fprintf(stderr, "attribute walk needs device path(-p) or node name(-n) specified\n");
372                         rc = 5;
373                         goto exit;
374                 }
375                 break;
376         case ACTION_ROOT:
377                 printf("%s\n", udev_root);
378                 break;
379         default:
380                 fprintf(stderr, "missing option\n");
381                 rc = 1;
382                 break;
383         }
384
385 exit:
386         udev_device_cleanup(udev);
387         sysfs_cleanup();
388         logging_close();
389         return rc;
390 }