chiark / gitweb /
[PATCH] added RFC-dev.d document detailing how /etc/dev.d/ works.
[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  *
7  *      This program is free software; you can redistribute it and/or modify it
8  *      under the terms of the GNU General Public License as published by the
9  *      Free Software Foundation version 2 of the License.
10  * 
11  *      This program is distributed in the hope that it will be useful, but
12  *      WITHOUT ANY WARRANTY; without even the implied warranty of
13  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *      General Public License for more details.
15  * 
16  *      You should have received a copy of the GNU General Public License along
17  *      with this program; if not, write to the Free Software Foundation, Inc.,
18  *      675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  */
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <stdarg.h>
27 #include <unistd.h>
28 #include <errno.h>
29
30 #include "libsysfs/sysfs/libsysfs.h"
31 #include "libsysfs/dlist.h"
32 #include "udev.h"
33 #include "udev_lib.h"
34 #include "udev_version.h"
35 #include "logging.h"
36 #include "udevdb.h"
37
38
39 # define SYSFS_VALUE_MAX 200
40
41 char **main_argv;
42 int main_argc;
43
44 #ifdef LOG
45 unsigned char logname[LOGNAME_SIZE];
46 void log_message (int level, const char *format, ...)
47 {
48         va_list args;
49
50         va_start(args, format);
51         vsyslog(level, format, args);
52         va_end(args);
53 }
54 #endif
55
56 static int print_all_attributes(const char *path)
57 {
58         struct dlist *attributes;
59         struct sysfs_attribute *attr;
60         struct sysfs_directory *sysfs_dir;
61         char value[SYSFS_VALUE_MAX];
62         int len;
63         int retval = 0;
64
65         sysfs_dir = sysfs_open_directory(path);
66         if (sysfs_dir == NULL)
67                 return -1;
68
69         attributes = sysfs_get_dir_attributes(sysfs_dir);
70         if (attributes == NULL) {
71                 retval = -1;
72                 goto exit;
73         }
74
75         dlist_for_each_data(attributes, attr, struct sysfs_attribute) {
76                 if (attr->value != NULL) {
77                         strfieldcpy(value, attr->value);
78                         len = strlen(value);
79                         if (len == 0)
80                                 continue;
81
82                         /* remove trailing newline */
83                         if (value[len-1] == '\n') {
84                                 value[len-1] = '\0';
85                                 len--;
86                         }
87
88                         /* skip nonprintable values */
89                         while (len) {
90                                 if (isprint(value[len-1]) == 0)
91                                         break;
92                                 len--;
93                         }
94                         if (len == 0)
95                                 printf("    SYSFS{%s}=\"%s\"\n", attr->name, value);
96                 }
97         }
98         printf("\n");
99
100 exit:
101         sysfs_close_directory(sysfs_dir);
102
103         return retval;
104 }
105
106 /* callback for database dump */
107 static int print_record(char *path, struct udevice *dev)
108 {
109         printf("P: %s\n", path);
110         printf("N: %s\n", dev->name);
111         printf("M: %#o\n", dev->mode);
112         printf("S: %s\n", dev->symlink);
113         printf("O: %s\n", dev->owner);
114         printf("G: %s\n", dev->group);
115         printf("F: %s\n", dev->config_file);
116         printf("L: %i\n", dev->config_line);
117         printf("T: %li\n", dev->config_time);
118         printf("\n");
119         return 0;
120 }
121
122 enum query_type {
123         NONE,
124         NAME,
125         PATH,
126         SYMLINK,
127         MODE,
128         OWNER,
129         GROUP,
130         ALL
131 };
132
133 static int print_device_chain(const char *path)
134 {
135         struct sysfs_class_device *class_dev;
136         struct sysfs_class_device *class_dev_parent;
137         struct sysfs_attribute *attr;
138         struct sysfs_device *sysfs_dev;
139         struct sysfs_device *sysfs_dev_parent;
140         int retval = 0;
141
142         /*  get the class dev */
143         class_dev = sysfs_open_class_device_path(path);
144         if (class_dev == NULL) {
145                 printf("couldn't get the class device\n");
146                 return -1;
147         }
148
149         /* read the 'dev' file for major/minor*/
150         attr = sysfs_get_classdev_attr(class_dev, "dev");
151         if (attr == NULL) {
152                 printf("couldn't get the \"dev\" file\n");
153                 retval = -1;
154                 goto exit;
155         }
156
157         printf("\nudevinfo starts with the device the node belongs to and then walks up the\n"
158                "device chain, to print for every device found, all possibly useful attributes\n"
159                "in the udev key format.\n"
160                "Only attributes within one device section may be used together in one rule,\n"
161                "to match the device for which the node will be created.\n"
162                "\n");
163         printf("device '%s' has major:minor %s", class_dev->path, attr->value);
164
165         /* open sysfs class device directory and print all attributes */
166         printf("  looking at class device '%s':\n", class_dev->path);
167         if (print_all_attributes(class_dev->path) != 0) {
168                 printf("couldn't open class device directory\n");
169                 retval = -1;
170                 goto exit;
171         }
172
173         /* get the device link (if parent exists look here) */
174         class_dev_parent = sysfs_get_classdev_parent(class_dev);
175         if (class_dev_parent != NULL) 
176                 sysfs_dev = sysfs_get_classdev_device(class_dev_parent);
177         else 
178                 sysfs_dev = sysfs_get_classdev_device(class_dev);
179         
180         if (sysfs_dev != NULL)
181                 printf("follow the class device's \"device\"\n");
182
183         /* look the device chain upwards */
184         while (sysfs_dev != NULL) {
185                 printf("  looking at the device chain at '%s':\n", sysfs_dev->path);
186                 printf("    BUS=\"%s\"\n", sysfs_dev->bus);
187                 printf("    ID=\"%s\"\n", sysfs_dev->bus_id);
188
189                 /* open sysfs device directory and print all attributes */
190                 print_all_attributes(sysfs_dev->path);
191
192                 sysfs_dev_parent = sysfs_get_device_parent(sysfs_dev);
193                 if (sysfs_dev_parent == NULL)
194                         break;
195
196                 sysfs_dev = sysfs_dev_parent;
197         }
198
199 exit:
200         sysfs_close_class_device(class_dev);
201         return retval;
202 }
203
204 static int process_options(void)
205 {
206         static const char short_options[] = "adn:p:q:rVh";
207         int option;
208         int retval = 1;
209         struct udevice dev;
210         int root = 0;
211         int attributes = 0;
212         enum query_type query = NONE;
213         char result[NAME_SIZE] = "";
214         char path[NAME_SIZE] = "";
215         char name[NAME_SIZE] = "";
216         char temp[NAME_SIZE];
217         char *pos;
218
219         /* get command line options */
220         while (1) {
221                 option = getopt(main_argc, main_argv, short_options);
222                 if (option == -1)
223                         break;
224
225                 dbg("option '%c'", option);
226                 switch (option) {
227                 case 'n':
228                         dbg("udev name: %s\n", optarg);
229                         strfieldcpy(name, optarg);
230                         break;
231
232                 case 'p':
233                         dbg("udev path: %s\n", optarg);
234                         strfieldcpy(path, optarg);
235                         break;
236
237                 case 'q':
238                         dbg("udev query: %s\n", optarg);
239
240                         if (strcmp(optarg, "name") == 0) {
241                                 query = NAME;
242                                 break;
243                         }
244
245                         if (strcmp(optarg, "symlink") == 0) {
246                                 query = SYMLINK;
247                                 break;
248                         }
249
250                         if (strcmp(optarg, "mode") == 0) {
251                                 query = MODE;
252                                 break;
253                         }
254
255                         if (strcmp(optarg, "owner") == 0) {
256                                 query = OWNER;
257                                 break;
258                         }
259
260                         if (strcmp(optarg, "group") == 0) {
261                                 query = GROUP;
262                                 break;
263                         }
264
265                         if (strcmp(optarg, "path") == 0) {
266                                 query = PATH;
267                                 break;
268                         }
269
270                         if (strcmp(optarg, "all") == 0) {
271                                 query = ALL;
272                                 break;
273                         }
274
275                         printf("unknown query type\n");
276                         exit(1);
277
278                 case 'r':
279                         root = 1;
280                         break;
281
282                 case 'a':
283                         attributes = 1;
284                         break;
285
286                 case 'd':
287                         retval = udevdb_open_ro();
288                         if (retval != 0) {
289                                 printf("unable to open udev database\n");
290                                 exit(2);
291                         }
292                         udevdb_call_foreach(print_record);
293                         udevdb_exit();
294                         exit(0);
295
296                 case 'V':
297                         printf("udevinfo, version %s\n", UDEV_VERSION);
298                         exit(0);
299
300                 case 'h':
301                         retval = 0;
302                 case '?':
303                 default:
304                         goto help;
305                 }
306         }
307
308         /* process options */
309         if (query != NONE) {
310                 retval = udevdb_open_ro();
311                 if (retval != 0) {
312                         printf("unable to open udev database\n");
313                         return -EACCES;
314                 }
315
316                 if (path[0] != '\0') {
317                         /* remove sysfs_path if given */
318                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) == 0) {
319                                 pos = path + strlen(sysfs_path);
320                         } else {
321                                 if (path[0] != '/') {
322                                         /* prepend '/' if missing */
323                                         strfieldcat(temp, "/");
324                                         strfieldcat(temp, path);
325                                         pos = temp;
326                                 } else {
327                                         pos = path;
328                                 }
329                         }
330                         retval = udevdb_get_dev(pos, &dev);
331                         if (retval != 0) {
332                                 printf("device not found in database\n");
333                                 goto exit;
334                         }
335                         goto print;
336                 }
337
338                 if (name[0] != '\0') {
339                         /* remove udev_root if given */
340                         if (strncmp(name, udev_root, strlen(udev_root)) == 0) {
341                                 pos = name + strlen(udev_root);
342                         } else
343                                 pos = name;
344                         retval = udevdb_get_dev_byname(pos, path, &dev);
345                         if (retval != 0) {
346                                 printf("device not found in database\n");
347                                 goto exit;
348                         }
349                         goto print;
350                 }
351
352                 printf("query needs device path(-p) or node name(-n) specified\n");
353                 goto exit;
354
355 print:
356                 switch(query) {
357                 case NAME:
358                         if (root)
359                                 strfieldcpy(result, udev_root);
360                         strfieldcat(result, dev.name);
361                         break;
362
363                 case SYMLINK:
364                         strfieldcpy(result, dev.symlink);
365                         break;
366
367                 case MODE:
368                         sprintf(result, "%#o", dev.mode);
369                         break;
370
371                 case GROUP:
372                         strfieldcpy(result, dev.group);
373                         break;
374
375                 case OWNER:
376                         strfieldcpy(result, dev.owner);
377                         break;
378
379                 case PATH:
380                         strfieldcpy(result, path);
381                         break;
382
383                 case ALL:
384                         print_record(path, &dev);
385                         goto exit;
386
387                 default:
388                         goto exit;
389                 }
390                 printf("%s\n", result);
391
392 exit:
393                 udevdb_exit();
394                 return retval;
395         }
396
397         if (attributes) {
398                 if (path[0] == '\0') {
399                         printf("attribute walk on device chain needs path(-p) specified\n");
400                         return -EINVAL;
401                 } else {
402                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) != 0) {
403                                 /* prepend sysfs mountpoint if not given */
404                                 strfieldcpy(temp, path);
405                                 strfieldcpy(path, sysfs_path);
406                                 strfieldcat(path, temp);
407                         }
408                         print_device_chain(path);
409                         return 0;
410                 }
411         }
412
413         if (root) {
414                 printf("%s\n", udev_root);
415                 return 0;
416         }
417
418 help:
419         printf("Usage: [-anpqrdVh]\n"
420                "  -q TYPE  query database for the specified value:\n"
421                "             'name'    name of device node\n"
422                "             'symlink' pointing to node\n"
423                "             'mode'    permissions of node\n"
424                "             'owner'   of node\n"
425                "             'group'   of node\n"
426                "             'path'    sysfs device path\n"
427                "             'all'     all values\n"
428                "\n"
429                "  -p PATH  sysfs device path used for query or chain\n"
430                "  -n NAME  node/symlink name used for query\n"
431                "\n"
432                "  -r       print udev root\n"
433                "  -a       print all SYSFS_attributes along the device chain\n"
434                "  -d       dump whole database\n"
435                "  -V       print udev version\n"
436                "  -h       print this help text\n"
437                "\n");
438         return retval;
439 }
440
441 int main(int argc, char *argv[], char *envp[])
442 {
443         int retval;
444
445         main_argv = argv;
446         main_argc = argc;
447
448         init_logging("udevinfo");
449
450         /* initialize our configuration */
451         udev_init_config();
452
453         retval = process_options();
454         if (retval != 0)
455                 exit(1);
456         exit(0);
457 }