chiark / gitweb /
814bd1e90af94a83fca8be047dc863ecd7764fbb
[elogind.git] / udevinfo.c
1 /*
2  * udevinfo.c - 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 void dump_names(void) {
169         LIST_HEAD(name_list);
170         struct name_entry *name_loop;
171         struct name_entry *tmp_loop;
172
173         udev_db_get_all_entries(&name_list);
174         list_for_each_entry_safe(name_loop, tmp_loop, &name_list, node) {
175                 struct udevice udev_db;
176
177                 udev_init_device(&udev_db, NULL, NULL, NULL);
178                 if (udev_db_get_device(&udev_db, name_loop->name) == 0) {
179                         printf("%s=%s/%s\n", udev_db.devpath, udev_root, udev_db.name);
180                         free(name_loop);
181                 }
182                 udev_cleanup_device(&udev_db);
183         }
184 }
185
186 int main(int argc, char *argv[], char *envp[])
187 {
188         static const char short_options[] = "adn:p:q:rVh";
189         int option;
190         struct udevice udev;
191         int root = 0;
192         int attributes = 0;
193         enum query_type {
194                 QUERY_NONE,
195                 QUERY_NAME,
196                 QUERY_PATH,
197                 QUERY_SYMLINK,
198                 QUERY_ENV,
199                 QUERY_ALL,
200         } query = QUERY_NONE;
201         char path[PATH_SIZE] = "";
202         char name[PATH_SIZE] = "";
203         char temp[PATH_SIZE];
204         struct name_entry *name_loop;
205         char *pos;
206         int retval = 0;
207
208         logging_init("udevinfo");
209
210         udev_init_config();
211         udev_init_device(&udev, NULL, NULL, NULL);
212
213         /* get command line options */
214         while (1) {
215                 option = getopt(argc, argv, short_options);
216                 if (option == -1)
217                         break;
218
219                 dbg("option '%c'", option);
220                 switch (option) {
221                 case 'n':
222                         dbg("udev name: %s\n", optarg);
223                         strlcpy(name, optarg, sizeof(name));
224                         break;
225
226                 case 'p':
227                         dbg("udev path: %s\n", optarg);
228                         strlcpy(path, optarg, sizeof(path));
229                         break;
230
231                 case 'q':
232                         dbg("udev query: %s\n", optarg);
233
234                         if (strcmp(optarg, "name") == 0) {
235                                 query = QUERY_NAME;
236                                 break;
237                         }
238
239                         if (strcmp(optarg, "symlink") == 0) {
240                                 query = QUERY_SYMLINK;
241                                 break;
242                         }
243
244                         if (strcmp(optarg, "path") == 0) {
245                                 query = QUERY_PATH;
246                                 break;
247                         }
248
249                         if (strcmp(optarg, "env") == 0) {
250                                 query = QUERY_ENV;
251                                 break;
252                         }
253
254                         if (strcmp(optarg, "all") == 0) {
255                                 query = QUERY_ALL;
256                                 break;
257                         }
258
259                         fprintf(stderr, "unknown query type\n");
260                         retval = 1;
261                         goto exit;
262
263                 case 'r':
264                         root = 1;
265                         break;
266
267                 case 'a':
268                         attributes = 1;
269                         break;
270
271                 case 'd':
272                         dump_names();
273                         goto exit;
274
275                 case 'V':
276                         printf("udevinfo, version %s\n", UDEV_VERSION);
277                         goto exit;
278
279                 case 'h':
280                         retval = 2;
281                 case '?':
282                 default:
283                         goto help;
284                 }
285         }
286
287         /* process options */
288         if (query != QUERY_NONE) {
289                 if (path[0] != '\0') {
290                         /* remove sysfs_path if given */
291                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) == 0) {
292                                 pos = path + strlen(sysfs_path);
293                         } else {
294                                 if (path[0] != '/') {
295                                         /* prepend '/' if missing */
296                                         strcpy(temp, "/");
297                                         strlcpy(temp, path, sizeof(temp));
298                                         pos = temp;
299                                 } else {
300                                         pos = path;
301                                 }
302                         }
303                         retval = udev_db_get_device(&udev, pos);
304                         if (retval != 0) {
305                                 fprintf(stderr, "no record for '%s' in database\n", pos);
306                                 goto exit;
307                         }
308                         goto print;
309                 }
310
311                 if (name[0] != '\0') {
312                         char devpath[PATH_SIZE];
313                         int len;
314
315                         /* remove udev_root if given */
316                         len = strlen(udev_root);
317                         if (strncmp(name, udev_root, len) == 0) {
318                                 pos = &name[len+1];
319                         } else
320                                 pos = name;
321
322                         retval = udev_db_search_name(devpath, sizeof(devpath), pos);
323                         if (retval != 0) {
324                                 fprintf(stderr, "device not found in database\n");
325                                 goto exit;
326                         }
327                         udev_db_get_device(&udev, devpath);
328                         goto print;
329                 }
330
331                 fprintf(stderr, "query needs device path(-p) or node name(-n) specified\n");
332                 retval = 3;
333                 goto exit;
334
335 print:
336                 switch(query) {
337                 case QUERY_NAME:
338                         if (root)
339                                 printf("%s/%s\n", udev_root, udev.name);
340                         else
341                                 printf("%s\n", udev.name);
342                         goto exit;
343                 case QUERY_SYMLINK:
344                         if (list_empty(&udev.symlink_list))
345                                 goto exit;
346                         if (root)
347                                 list_for_each_entry(name_loop, &udev.symlink_list, node)
348                                         printf("%s/%s ", udev_root, name_loop->name);
349                         else
350                                 list_for_each_entry(name_loop, &udev.symlink_list, node)
351                                         printf("%s ", name_loop->name);
352                         printf("\n");
353                         goto exit;
354                 case QUERY_PATH:
355                         printf("%s\n", udev.devpath);
356                         goto exit;
357                 case QUERY_ENV:
358                         list_for_each_entry(name_loop, &udev.env_list, node)
359                                 printf("%s\n", name_loop->name);
360                         goto exit;
361                 case QUERY_ALL:
362                         print_record(&udev);
363                         goto exit;
364                 default:
365                         goto help;
366                 }
367         }
368
369         if (attributes) {
370                 if (path[0] == '\0') {
371                         fprintf(stderr, "attribute walk on device chain needs path(-p) specified\n");
372                         retval = 4;
373                         goto exit;
374                 } else {
375                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) != 0) {
376                                 /* prepend sysfs mountpoint if not given */
377                                 snprintf(temp, sizeof(temp), "%s%s", sysfs_path, path);
378                                 temp[sizeof(temp)-1] = '\0';
379                                 strlcpy(path, temp, sizeof(temp));
380                         }
381                         print_device_chain(path);
382                         goto exit;
383                 }
384         }
385
386         if (root) {
387                 printf("%s\n", udev_root);
388                 goto exit;
389         }
390
391 help:
392         fprintf(stderr, "Usage: udevinfo [-anpqrVh]\n"
393                "  -q TYPE  query database for the specified value:\n"
394                "             'name'    name of device node\n"
395                "             'symlink' pointing to node\n"
396                "             'path'    sysfs device path\n"
397                "             'env'     the device related imported environment\n"
398                "             'all'     all values\n"
399                "\n"
400                "  -p PATH  sysfs device path used for query or chain\n"
401                "  -n NAME  node/symlink name used for query\n"
402                "\n"
403                "  -r       print udev root\n"
404                "  -a       print all SYSFS_attributes along the device chain\n"
405                "  -d       print the relationship of devpath and the node name for all\n"
406                "           devices available in the database\n"
407                "  -V       print udev version\n"
408                "  -h       print this help text\n"
409                "\n");
410
411 exit:
412         udev_cleanup_device(&udev);
413         logging_close();
414         return retval;
415 }