chiark / gitweb /
788dd519a86e97cdf7e2d8d6cbac398189773a6a
[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 enum query_type {
96         NONE,
97         NAME,
98         PATH,
99         SYMLINK,
100         ALL,
101 };
102
103 static int print_device_chain(const char *path)
104 {
105         struct sysfs_class_device *class_dev;
106         struct sysfs_class_device *class_dev_parent;
107         struct sysfs_attribute *attr;
108         struct sysfs_device *sysfs_dev;
109         struct dlist *attr_list;
110         int retval = 0;
111
112         /*  get the class dev */
113         class_dev = sysfs_open_class_device_path(path);
114         if (class_dev == NULL) {
115                 fprintf(stderr, "couldn't get the class device\n");
116                 return -1;
117         }
118
119         printf("\nudevinfo starts with the device the node belongs to and then walks up the\n"
120                "device chain, to print for every device found, all possibly useful attributes\n"
121                "in the udev key format.\n"
122                "Only attributes within one device section may be used together in one rule,\n"
123                "to match the device for which the node will be created.\n"
124                "\n");
125
126         /* look for the 'dev' file */
127         attr = sysfs_get_classdev_attr(class_dev, "dev");
128         if (attr != NULL)
129                 printf("device '%s' has major:minor %s", class_dev->path, attr->value);
130
131         /* open sysfs class device directory and print all attributes */
132         printf("  looking at class device '%s':\n", class_dev->path);
133         printf("    SUBSYSTEM==\"%s\"\n", class_dev->classname);
134
135         attr_list = sysfs_get_classdev_attributes(class_dev);
136         if (attr_list == NULL) {
137                 fprintf(stderr, "couldn't open class device directory\n");
138                 retval = -1;
139                 goto exit;
140         }
141         print_all_attributes(attr_list);
142
143         /* get the device link (if parent exists look here) */
144         class_dev_parent = sysfs_get_classdev_parent(class_dev);
145         if (class_dev_parent != NULL)
146                 sysfs_dev = sysfs_get_classdev_device(class_dev_parent);
147         else 
148                 sysfs_dev = sysfs_get_classdev_device(class_dev);
149         
150         if (sysfs_dev != NULL)
151                 printf("follow the \"device\"-link to the physical device:\n");
152
153         /* look the device chain upwards */
154         while (sysfs_dev != NULL) {
155                 attr_list = sysfs_get_device_attributes(sysfs_dev);
156                 if (attr_list == NULL) {
157                         fprintf(stderr, "couldn't open device directory\n");
158                         retval = -1;
159                         goto exit;
160                 }
161
162                 printf("  looking at the device chain at '%s':\n", sysfs_dev->path);
163                 printf("    BUS==\"%s\"\n", sysfs_dev->bus);
164                 printf("    ID==\"%s\"\n", sysfs_dev->bus_id);
165                 printf("    DRIVER==\"%s\"\n", sysfs_dev->driver_name);
166
167                 print_all_attributes(attr_list);
168
169                 sysfs_dev = sysfs_get_device_parent(sysfs_dev);
170                 if (sysfs_dev == NULL)
171                         break;
172         }
173
174 exit:
175         sysfs_close_class_device(class_dev);
176         return retval;
177 }
178
179 static int print_dump(const char *devpath, const char *name) {
180         printf("%s=%s/%s\n", devpath, udev_root, name);
181         return 0;
182 }
183
184 int main(int argc, char *argv[], char *envp[])
185 {
186         static const char short_options[] = "adn:p:q:rVh";
187         int option;
188         struct udevice udev;
189         int root = 0;
190         int attributes = 0;
191         enum query_type query = NONE;
192         char path[PATH_SIZE] = "";
193         char name[PATH_SIZE] = "";
194         char temp[PATH_SIZE];
195         struct name_entry *name_loop;
196         char *pos;
197         int retval = 0;
198
199         logging_init("udevinfo");
200
201         udev_init_config();
202         udev_init_device(&udev, NULL, NULL);
203
204         /* get command line options */
205         while (1) {
206                 option = getopt(argc, argv, short_options);
207                 if (option == -1)
208                         break;
209
210                 dbg("option '%c'", option);
211                 switch (option) {
212                 case 'n':
213                         dbg("udev name: %s\n", optarg);
214                         strlcpy(name, optarg, sizeof(name));
215                         break;
216
217                 case 'p':
218                         dbg("udev path: %s\n", optarg);
219                         strlcpy(path, optarg, sizeof(path));
220                         break;
221
222                 case 'q':
223                         dbg("udev query: %s\n", optarg);
224
225                         if (strcmp(optarg, "name") == 0) {
226                                 query = NAME;
227                                 break;
228                         }
229
230                         if (strcmp(optarg, "symlink") == 0) {
231                                 query = SYMLINK;
232                                 break;
233                         }
234
235                         if (strcmp(optarg, "path") == 0) {
236                                 query = PATH;
237                                 break;
238                         }
239
240                         if (strcmp(optarg, "all") == 0) {
241                                 query = ALL;
242                                 break;
243                         }
244
245                         fprintf(stderr, "unknown query type\n");
246                         retval = 1;
247                         goto exit;
248
249                 case 'r':
250                         root = 1;
251                         break;
252
253                 case 'a':
254                         attributes = 1;
255                         break;
256
257                 case 'd':
258                         udev_db_dump_names(print_dump);
259                         goto exit;
260
261                 case 'V':
262                         printf("udevinfo, version %s\n", UDEV_VERSION);
263                         goto exit;
264
265                 case 'h':
266                         retval = 2;
267                 case '?':
268                 default:
269                         goto help;
270                 }
271         }
272
273         /* process options */
274         if (query != NONE) {
275                 if (path[0] != '\0') {
276                         /* remove sysfs_path if given */
277                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) == 0) {
278                                 pos = path + strlen(sysfs_path);
279                         } else {
280                                 if (path[0] != '/') {
281                                         /* prepend '/' if missing */
282                                         strcpy(temp, "/");
283                                         strlcpy(temp, path, sizeof(temp));
284                                         pos = temp;
285                                 } else {
286                                         pos = path;
287                                 }
288                         }
289                         retval = udev_db_get_device(&udev, pos);
290                         if (retval != 0) {
291                                 fprintf(stderr, "device not found in database\n");
292                                 goto exit;
293                         }
294                         goto print;
295                 }
296
297                 if (name[0] != '\0') {
298                         char devpath[PATH_SIZE];
299                         int len;
300
301                         /* remove udev_root if given */
302                         len = strlen(udev_root);
303                         if (strncmp(name, udev_root, len) == 0) {
304                                 pos = &name[len+1];
305                         } else
306                                 pos = name;
307
308                         retval = udev_db_search_name(devpath, sizeof(devpath), pos);
309                         if (retval != 0) {
310                                 fprintf(stderr, "device not found in database\n");
311                                 goto exit;
312                         }
313                         udev_db_get_device(&udev, devpath);
314                         goto print;
315                 }
316
317                 fprintf(stderr, "query needs device path(-p) or node name(-n) specified\n");
318                 retval = 3;
319                 goto exit;
320
321 print:
322                 switch(query) {
323                 case NAME:
324                         if (root)
325                                 printf("%s/%s\n", udev_root, udev.name);
326                         else
327                                 printf("%s\n", udev.name);
328                         goto exit;
329                 case SYMLINK:
330                         if (list_empty(&udev.symlink_list))
331                                 break;
332                         if (root)
333                                 list_for_each_entry(name_loop, &udev.symlink_list, node)
334                                         printf("%s/%s ", udev_root, name_loop->name);
335                         else
336                                 list_for_each_entry(name_loop, &udev.symlink_list, node)
337                                         printf("%s ", name_loop->name);
338                         printf("\n");
339                         goto exit;
340                 case PATH:
341                         printf("%s\n", udev.devpath);
342                         goto exit;
343                 case ALL:
344                         print_record(&udev);
345                         goto exit;
346                 default:
347                         goto help;
348                 }
349         }
350
351         if (attributes) {
352                 if (path[0] == '\0') {
353                         fprintf(stderr, "attribute walk on device chain needs path(-p) specified\n");
354                         retval = 4;
355                         goto exit;
356                 } else {
357                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) != 0) {
358                                 /* prepend sysfs mountpoint if not given */
359                                 snprintf(temp, sizeof(temp), "%s%s", sysfs_path, path);
360                                 temp[sizeof(temp)-1] = '\0';
361                                 strlcpy(path, temp, sizeof(temp));
362                         }
363                         print_device_chain(path);
364                         goto exit;
365                 }
366         }
367
368         if (root) {
369                 printf("%s\n", udev_root);
370                 goto exit;
371         }
372
373 help:
374         fprintf(stderr, "Usage: udevinfo [-anpqrVh]\n"
375                "  -q TYPE  query database for the specified value:\n"
376                "             'name'    name of device node\n"
377                "             'symlink' pointing to node\n"
378                "             'path'    sysfs device path\n"
379                "             'all'     all values\n"
380                "\n"
381                "  -p PATH  sysfs device path used for query or chain\n"
382                "  -n NAME  node/symlink name used for query\n"
383                "\n"
384                "  -r       print udev root\n"
385                "  -a       print all SYSFS_attributes along the device chain\n"
386                "  -d       print the relationship of devpath and the node name for all\n"
387                "           devices available in the database\n"
388                "  -V       print udev version\n"
389                "  -h       print this help text\n"
390                "\n");
391
392 exit:
393         udev_cleanup_device(&udev);
394         logging_close();
395         return retval;
396 }