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