chiark / gitweb /
Merge branch 'master' of gregkh@master.kernel.org:/pub/scm/linux/hotplug/udev
[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 <ctype.h>
25 #include <stdarg.h>
26 #include <unistd.h>
27 #include <errno.h>
28
29 #include "libsysfs/sysfs/libsysfs.h"
30 #include "udev_libc_wrapper.h"
31 #include "udev.h"
32 #include "udev_utils.h"
33 #include "udev_version.h"
34 #include "udev_db.h"
35 #include "logging.h"
36
37
38 #ifdef USE_LOG
39 void log_message (int priority, const char *format, ...)
40 {
41         va_list args;
42
43         if (priority > udev_log_priority)
44                 return;
45
46         va_start(args, format);
47         vsyslog(priority, format, args);
48         va_end(args);
49 }
50 #endif
51
52 static void print_all_attributes(struct dlist *attr_list)
53 {
54         struct sysfs_attribute *attr;
55         char value[VALUE_SIZE];
56         size_t len;
57
58         dlist_for_each_data(attr_list, attr, struct sysfs_attribute) {
59                 if (attr->value == NULL)
60                         continue;
61                 len = strlcpy(value, attr->value, sizeof(value));
62                 if (len >= sizeof(value)) {
63                         dbg("attribute value of '%s' too long, skip", attr->name);
64                         continue;
65                 }
66
67                 /* remove trailing newlines */
68                 while (len && value[len-1] == '\n')
69                         value[--len] = '\0';
70                 /* skip nonprintable attributes */
71                 while (len && isprint(value[len-1]))
72                         len--;
73                 if (len) {
74                         dbg("attribute value of '%s' non-printable, skip", attr->name);
75                         continue;
76                 }
77                 replace_untrusted_chars(value);
78                 printf("    SYSFS{%s}==\"%s\"\n", attr->name, value);
79         }
80         printf("\n");
81 }
82
83 static void print_record(struct udevice *udev)
84 {
85         struct name_entry *name_loop;
86
87         printf("P: %s\n", udev->devpath);
88         printf("N: %s\n", udev->name);
89         list_for_each_entry(name_loop, &udev->symlink_list, node)
90                 printf("S: %s\n", name_loop->name);
91         list_for_each_entry(name_loop, &udev->env_list, node)
92                 printf("E: %s\n", name_loop->name);
93 }
94
95 static int print_device_chain(const char *path)
96 {
97         struct sysfs_class_device *class_dev;
98         struct sysfs_class_device *class_dev_parent;
99         struct sysfs_attribute *attr;
100         struct sysfs_device *sysfs_dev;
101         struct dlist *attr_list;
102         int retval = 0;
103
104         /*  get the class dev */
105         class_dev = sysfs_open_class_device_path(path);
106         if (class_dev == NULL) {
107                 fprintf(stderr, "couldn't get the class device\n");
108                 return -1;
109         }
110
111         printf("\nudevinfo starts with the device the node belongs to and then walks up the\n"
112                "device chain, to print for every device found, all possibly useful attributes\n"
113                "in the udev key format.\n"
114                "Only attributes within one device section may be used together in one rule,\n"
115                "to match the device for which the node will be created.\n"
116                "\n");
117
118         /* look for the 'dev' file */
119         attr = sysfs_get_classdev_attr(class_dev, "dev");
120         if (attr != NULL)
121                 printf("device '%s' has major:minor %s", class_dev->path, attr->value);
122
123         /* open sysfs class device directory and print all attributes */
124         printf("  looking at class device '%s':\n", class_dev->path);
125         printf("    KERNEL==\"%s\"\n", class_dev->name);
126         printf("    SUBSYSTEM==\"%s\"\n", class_dev->classname);
127
128         attr_list = sysfs_get_classdev_attributes(class_dev);
129         if (attr_list == NULL) {
130                 fprintf(stderr, "couldn't open class device directory\n");
131                 retval = -1;
132                 goto exit;
133         }
134         print_all_attributes(attr_list);
135
136         /* get the device link (if parent exists look here) */
137         class_dev_parent = sysfs_get_classdev_parent(class_dev);
138         if (class_dev_parent != NULL)
139                 sysfs_dev = sysfs_get_classdev_device(class_dev_parent);
140         else 
141                 sysfs_dev = sysfs_get_classdev_device(class_dev);
142         
143         if (sysfs_dev != NULL)
144                 printf("follow the \"device\"-link to the physical device:\n");
145
146         /* look the device chain upwards */
147         while (sysfs_dev != NULL) {
148                 printf("  looking at the device chain at '%s':\n", sysfs_dev->path);
149                 printf("    BUS==\"%s\"\n", sysfs_dev->bus);
150                 printf("    ID==\"%s\"\n", sysfs_dev->bus_id);
151                 printf("    DRIVER==\"%s\"\n", sysfs_dev->driver_name);
152
153                 attr_list = sysfs_get_device_attributes(sysfs_dev);
154                 if (attr_list != NULL)
155                         print_all_attributes(attr_list);
156                 else
157                         printf("\n");
158
159                 sysfs_dev = sysfs_get_device_parent(sysfs_dev);
160                 if (sysfs_dev == NULL)
161                         break;
162         }
163
164 exit:
165         sysfs_close_class_device(class_dev);
166         return retval;
167 }
168
169 static void export_name_devpath(struct udevice *udev) {
170         printf("%s=%s/%s\n", udev->devpath, udev_root, udev->name);
171 }
172
173 static void export_record(struct udevice *udev) {
174         print_record(udev);
175         printf("\n");
176 }
177
178 static void export_db(void fnct(struct udevice *udev)) {
179         LIST_HEAD(name_list);
180         struct name_entry *name_loop;
181
182         udev_db_get_all_entries(&name_list);
183         list_for_each_entry(name_loop, &name_list, node) {
184                 struct udevice udev_db;
185
186                 udev_init_device(&udev_db, NULL, NULL, NULL);
187                 if (udev_db_get_device(&udev_db, name_loop->name) == 0)
188                         fnct(&udev_db);
189                 udev_cleanup_device(&udev_db);
190         }
191         name_list_cleanup(&name_list);
192 }
193
194 static void print_help(void)
195 {
196         fprintf(stderr, "Usage: udevinfo [-anpqrVh]\n"
197                "  -q TYPE  query database for the specified value:\n"
198                "             'name'    name of device node\n"
199                "             'symlink' pointing to node\n"
200                "             'path'    sysfs device path\n"
201                "             'env'     the device related imported environment\n"
202                "             'all'     all values\n"
203                "\n"
204                "  -p PATH  sysfs device path used for query or chain\n"
205                "  -n NAME  node/symlink name used for query\n"
206                "\n"
207                "  -r       prepend to query result or print udev_root\n"
208                "  -a       print all SYSFS_attributes along the device chain\n"
209                "  -e       export the content of the udev database\n"
210                "  -V       print udev version\n"
211                "  -h       print this help text\n"
212                "\n");
213 }
214
215 int main(int argc, char *argv[], char *envp[])
216 {
217         static const char short_options[] = "aden:p:q:rVh";
218         int option;
219         struct udevice udev;
220         int root = 0;
221
222         enum action_type {
223                 ACTION_NONE,
224                 ACTION_QUERY,
225                 ACTION_ATTRIBUTE_WALK,
226                 ACTION_ROOT,
227         } action = ACTION_NONE;
228
229         enum query_type {
230                 QUERY_NONE,
231                 QUERY_NAME,
232                 QUERY_PATH,
233                 QUERY_SYMLINK,
234                 QUERY_ENV,
235                 QUERY_ALL,
236         } query = QUERY_NONE;
237
238         char path[PATH_SIZE] = "";
239         char name[PATH_SIZE] = "";
240         char temp[PATH_SIZE];
241         struct name_entry *name_loop;
242         char *pos;
243         int retval = 0;
244
245         logging_init("udevinfo");
246
247         udev_init_config();
248         udev_init_device(&udev, NULL, NULL, NULL);
249
250         /* get command line options */
251         while (1) {
252                 option = getopt(argc, argv, short_options);
253                 if (option == -1)
254                         break;
255
256                 dbg("option '%c'", option);
257                 switch (option) {
258                 case 'n':
259                         dbg("udev name: %s\n", optarg);
260                         strlcpy(name, optarg, sizeof(name));
261                         break;
262                 case 'p':
263                         dbg("udev path: %s\n", optarg);
264                         strlcpy(path, optarg, sizeof(path));
265                         break;
266                 case 'q':
267                         dbg("udev query: %s\n", optarg);
268                         action = ACTION_QUERY;
269                         if (strcmp(optarg, "name") == 0) {
270                                 query = QUERY_NAME;
271                                 break;
272                         }
273                         if (strcmp(optarg, "symlink") == 0) {
274                                 query = QUERY_SYMLINK;
275                                 break;
276                         }
277                         if (strcmp(optarg, "path") == 0) {
278                                 query = QUERY_PATH;
279                                 break;
280                         }
281                         if (strcmp(optarg, "env") == 0) {
282                                 query = QUERY_ENV;
283                                 break;
284                         }
285                         if (strcmp(optarg, "all") == 0) {
286                                 query = QUERY_ALL;
287                                 break;
288                         }
289                         fprintf(stderr, "unknown query type\n");
290                         retval = 2;
291                         goto exit;
292                 case 'r':
293                         if (action == ACTION_NONE)
294                                 action = ACTION_ROOT;
295                         root = 1;
296                         break;
297                 case 'a':
298                         action = ACTION_ATTRIBUTE_WALK;
299                         break;
300                 case 'd':
301                         export_db(export_name_devpath);
302                         goto exit;
303                 case 'e':
304                         export_db(export_record);
305                         goto exit;
306                 case 'V':
307                         printf("udevinfo, version %s\n", UDEV_VERSION);
308                         goto exit;
309                 case 'h':
310                 case '?':
311                 default:
312                         print_help();
313                         goto exit;
314                 }
315         }
316
317         /* run action */
318         switch (action) {
319         case ACTION_QUERY:
320                 /* need devpath or node/symlink name for query */
321                 if (path[0] != '\0') {
322                         /* remove sysfs_path if given */
323                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) == 0) {
324                                 pos = path + strlen(sysfs_path);
325                         } else {
326                                 if (path[0] != '/') {
327                                         /* prepend '/' if missing */
328                                         strcpy(temp, "/");
329                                         strlcpy(temp, path, sizeof(temp));
330                                         pos = temp;
331                                 } else {
332                                         pos = path;
333                                 }
334                         }
335                         retval = udev_db_get_device(&udev, pos);
336                         if (retval != 0) {
337                                 fprintf(stderr, "no record for '%s' in database\n", pos);
338                                 retval = 3;
339                                 goto exit;
340                         }
341                 } else if (name[0] != '\0') {
342                         char devpath[PATH_SIZE];
343                         int len;
344
345                         /* remove udev_root if given */
346                         len = strlen(udev_root);
347                         if (strncmp(name, udev_root, len) == 0) {
348                                 pos = &name[len+1];
349                         } else
350                                 pos = name;
351
352                         retval = udev_db_lookup_name(pos, devpath, sizeof(devpath));
353                         if (retval != 0) {
354                                 fprintf(stderr, "no record for '%s' in database\n", pos);
355                                 retval = 3;
356                                 goto exit;
357                         }
358                         udev_db_get_device(&udev, devpath);
359                 } else {
360                         fprintf(stderr, "query needs device path(-p) or node name(-n) specified\n");
361                         retval = 4;
362                         goto exit;
363                 }
364
365                 switch(query) {
366                 case QUERY_NAME:
367                         if (root)
368                                 printf("%s/%s\n", udev_root, udev.name);
369                         else
370                                 printf("%s\n", udev.name);
371                         break;
372                 case QUERY_SYMLINK:
373                         if (list_empty(&udev.symlink_list))
374                                 goto exit;
375                         if (root)
376                                 list_for_each_entry(name_loop, &udev.symlink_list, node)
377                                         printf("%s/%s ", udev_root, name_loop->name);
378                         else
379                                 list_for_each_entry(name_loop, &udev.symlink_list, node)
380                                         printf("%s ", name_loop->name);
381                         printf("\n");
382                         break;
383                 case QUERY_PATH:
384                         printf("%s\n", udev.devpath);
385                         goto exit;
386                 case QUERY_ENV:
387                         list_for_each_entry(name_loop, &udev.env_list, node)
388                                 printf("%s\n", name_loop->name);
389                         break;
390                 case QUERY_ALL:
391                         print_record(&udev);
392                         break;
393                 default:
394                         print_help();
395                         break;
396                 }
397                 break;
398         case ACTION_ATTRIBUTE_WALK:
399                 if (path[0] == '\0') {
400                         fprintf(stderr, "attribute walk on device chain needs path(-p) specified\n");
401                         retval = 4;
402                         goto exit;
403                 } else {
404                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) != 0) {
405                                 /* prepend sysfs mountpoint if not given */
406                                 snprintf(temp, sizeof(temp), "%s%s", sysfs_path, path);
407                                 temp[sizeof(temp)-1] = '\0';
408                                 strlcpy(path, temp, sizeof(temp));
409                         }
410                         print_device_chain(path);
411                 }
412                 break;
413         case ACTION_ROOT:
414                 printf("%s\n", udev_root);
415                 break;
416         default:
417                 print_help();
418                 retval = 1;
419                 break;
420         }
421
422 exit:
423         udev_cleanup_device(&udev);
424         logging_close();
425         return retval;
426 }