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