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