chiark / gitweb /
Merge gregkh@ehlo.org:/home/kay/public_html/pub/scm/linux/hotplug/udev-kay
[elogind.git] / udevinfo.c
1 /*
2  * udevinfo - fetches attributes for a device
3  *
4  * Copyright (C) 2004 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 int 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
92         return 0;
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("    SUBSYSTEM==\"%s\"\n", class_dev->classname);
126
127         attr_list = sysfs_get_classdev_attributes(class_dev);
128         if (attr_list == NULL) {
129                 fprintf(stderr, "couldn't open class device directory\n");
130                 retval = -1;
131                 goto exit;
132         }
133         print_all_attributes(attr_list);
134
135         /* get the device link (if parent exists look here) */
136         class_dev_parent = sysfs_get_classdev_parent(class_dev);
137         if (class_dev_parent != NULL)
138                 sysfs_dev = sysfs_get_classdev_device(class_dev_parent);
139         else 
140                 sysfs_dev = sysfs_get_classdev_device(class_dev);
141         
142         if (sysfs_dev != NULL)
143                 printf("follow the \"device\"-link to the physical device:\n");
144
145         /* look the device chain upwards */
146         while (sysfs_dev != NULL) {
147                 printf("  looking at the device chain at '%s':\n", sysfs_dev->path);
148                 printf("    BUS==\"%s\"\n", sysfs_dev->bus);
149                 printf("    ID==\"%s\"\n", sysfs_dev->bus_id);
150                 printf("    DRIVER==\"%s\"\n", sysfs_dev->driver_name);
151
152                 attr_list = sysfs_get_device_attributes(sysfs_dev);
153                 if (attr_list != NULL)
154                         print_all_attributes(attr_list);
155                 else
156                         printf("\n");
157
158                 sysfs_dev = sysfs_get_device_parent(sysfs_dev);
159                 if (sysfs_dev == NULL)
160                         break;
161         }
162
163 exit:
164         sysfs_close_class_device(class_dev);
165         return retval;
166 }
167
168 static int print_dump(const char *devpath, const char *name) {
169         printf("%s=%s/%s\n", devpath, udev_root, name);
170         return 0;
171 }
172
173 int main(int argc, char *argv[], char *envp[])
174 {
175         static const char short_options[] = "adn:p:q:rVh";
176         int option;
177         struct udevice udev;
178         int root = 0;
179         int attributes = 0;
180         enum query_type {
181                 QUERY_NONE,
182                 QUERY_NAME,
183                 QUERY_PATH,
184                 QUERY_SYMLINK,
185                 QUERY_ENV,
186                 QUERY_ALL,
187         } query = QUERY_NONE;
188         char path[PATH_SIZE] = "";
189         char name[PATH_SIZE] = "";
190         char temp[PATH_SIZE];
191         struct name_entry *name_loop;
192         char *pos;
193         int retval = 0;
194
195         logging_init("udevinfo");
196
197         udev_init_config();
198         udev_init_device(&udev, NULL, NULL, NULL);
199
200         /* get command line options */
201         while (1) {
202                 option = getopt(argc, argv, short_options);
203                 if (option == -1)
204                         break;
205
206                 dbg("option '%c'", option);
207                 switch (option) {
208                 case 'n':
209                         dbg("udev name: %s\n", optarg);
210                         strlcpy(name, optarg, sizeof(name));
211                         break;
212
213                 case 'p':
214                         dbg("udev path: %s\n", optarg);
215                         strlcpy(path, optarg, sizeof(path));
216                         break;
217
218                 case 'q':
219                         dbg("udev query: %s\n", optarg);
220
221                         if (strcmp(optarg, "name") == 0) {
222                                 query = QUERY_NAME;
223                                 break;
224                         }
225
226                         if (strcmp(optarg, "symlink") == 0) {
227                                 query = QUERY_SYMLINK;
228                                 break;
229                         }
230
231                         if (strcmp(optarg, "path") == 0) {
232                                 query = QUERY_PATH;
233                                 break;
234                         }
235
236                         if (strcmp(optarg, "env") == 0) {
237                                 query = QUERY_ENV;
238                                 break;
239                         }
240
241                         if (strcmp(optarg, "all") == 0) {
242                                 query = QUERY_ALL;
243                                 break;
244                         }
245
246                         fprintf(stderr, "unknown query type\n");
247                         retval = 1;
248                         goto exit;
249
250                 case 'r':
251                         root = 1;
252                         break;
253
254                 case 'a':
255                         attributes = 1;
256                         break;
257
258                 case 'd':
259                         udev_db_dump_names(print_dump);
260                         goto exit;
261
262                 case 'V':
263                         printf("udevinfo, version %s\n", UDEV_VERSION);
264                         goto exit;
265
266                 case 'h':
267                         retval = 2;
268                 case '?':
269                 default:
270                         goto help;
271                 }
272         }
273
274         /* process options */
275         if (query != QUERY_NONE) {
276                 if (path[0] != '\0') {
277                         /* remove sysfs_path if given */
278                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) == 0) {
279                                 pos = path + strlen(sysfs_path);
280                         } else {
281                                 if (path[0] != '/') {
282                                         /* prepend '/' if missing */
283                                         strcpy(temp, "/");
284                                         strlcpy(temp, path, sizeof(temp));
285                                         pos = temp;
286                                 } else {
287                                         pos = path;
288                                 }
289                         }
290                         retval = udev_db_get_device(&udev, pos);
291                         if (retval != 0) {
292                                 fprintf(stderr, "device not found in database\n");
293                                 goto exit;
294                         }
295                         goto print;
296                 }
297
298                 if (name[0] != '\0') {
299                         char devpath[PATH_SIZE];
300                         int len;
301
302                         /* remove udev_root if given */
303                         len = strlen(udev_root);
304                         if (strncmp(name, udev_root, len) == 0) {
305                                 pos = &name[len+1];
306                         } else
307                                 pos = name;
308
309                         retval = udev_db_search_name(devpath, sizeof(devpath), pos);
310                         if (retval != 0) {
311                                 fprintf(stderr, "device not found in database\n");
312                                 goto exit;
313                         }
314                         udev_db_get_device(&udev, devpath);
315                         goto print;
316                 }
317
318                 fprintf(stderr, "query needs device path(-p) or node name(-n) specified\n");
319                 retval = 3;
320                 goto exit;
321
322 print:
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                         goto exit;
330                 case QUERY_SYMLINK:
331                         if (list_empty(&udev.symlink_list))
332                                 break;
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                         goto exit;
341                 case QUERY_PATH:
342                         printf("%s\n", udev.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                         goto exit;
348                 case QUERY_ALL:
349                         print_record(&udev);
350                         goto exit;
351                 default:
352                         goto help;
353                 }
354         }
355
356         if (attributes) {
357                 if (path[0] == '\0') {
358                         fprintf(stderr, "attribute walk on device chain needs path(-p) specified\n");
359                         retval = 4;
360                         goto exit;
361                 } else {
362                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) != 0) {
363                                 /* prepend sysfs mountpoint if not given */
364                                 snprintf(temp, sizeof(temp), "%s%s", sysfs_path, path);
365                                 temp[sizeof(temp)-1] = '\0';
366                                 strlcpy(path, temp, sizeof(temp));
367                         }
368                         print_device_chain(path);
369                         goto exit;
370                 }
371         }
372
373         if (root) {
374                 printf("%s\n", udev_root);
375                 goto exit;
376         }
377
378 help:
379         fprintf(stderr, "Usage: udevinfo [-anpqrVh]\n"
380                "  -q TYPE  query database for the specified value:\n"
381                "             'name'    name of device node\n"
382                "             'symlink' pointing to node\n"
383                "             'path'    sysfs device path\n"
384                "             'env'     the device related imported environment\n"
385                "             'all'     all values\n"
386                "\n"
387                "  -p PATH  sysfs device path used for query or chain\n"
388                "  -n NAME  node/symlink name used for query\n"
389                "\n"
390                "  -r       print udev root\n"
391                "  -a       print all SYSFS_attributes along the device chain\n"
392                "  -d       print the relationship of devpath and the node name for all\n"
393                "           devices available in the database\n"
394                "  -V       print udev version\n"
395                "  -h       print this help text\n"
396                "\n");
397
398 exit:
399         udev_cleanup_device(&udev);
400         logging_close();
401         return retval;
402 }