chiark / gitweb /
290a09e880aee1dc7c95c03417fab4ee39e4eea6
[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 static int print_record(struct udevice *udev)
107 {
108         printf("P: %s\n", udev->devpath);
109         printf("N: %s\n", udev->name);
110         printf("S: %s\n", udev->symlink);
111         printf("\n");
112         return 0;
113 }
114
115 enum query_type {
116         NONE,
117         NAME,
118         PATH,
119         SYMLINK,
120         ALL
121 };
122
123 static int print_device_chain(const char *path)
124 {
125         struct sysfs_class_device *class_dev;
126         struct sysfs_class_device *class_dev_parent;
127         struct sysfs_attribute *attr;
128         struct sysfs_device *sysfs_dev;
129         struct sysfs_device *sysfs_dev_parent;
130         int retval = 0;
131         char type;
132
133         type = get_device_type(path, "");
134         dbg("device type is %c", type);
135
136         /*  get the class dev */
137         class_dev = sysfs_open_class_device_path(path);
138         if (class_dev == NULL) {
139                 printf("couldn't get the class device\n");
140                 return -1;
141         }
142
143         printf("\nudevinfo starts with the device the node belongs to and then walks up the\n"
144                "device chain, to print for every device found, all possibly useful attributes\n"
145                "in the udev key format.\n"
146                "Only attributes within one device section may be used together in one rule,\n"
147                "to match the device for which the node will be created.\n"
148                "\n");
149
150         if (type == 'b' || type =='c') {
151                 /* read the 'dev' file for major/minor*/
152                 attr = sysfs_get_classdev_attr(class_dev, "dev");
153                 if (attr == NULL) {
154                         printf("couldn't get the \"dev\" file\n");
155                         retval = -1;
156                         goto exit;
157                 }
158                 printf("device '%s' has major:minor %s", class_dev->path, attr->value);
159         }
160
161         /* open sysfs class device directory and print all attributes */
162         printf("  looking at class device '%s':\n", class_dev->path);
163         if (print_all_attributes(class_dev->path) != 0) {
164                 printf("couldn't open class device directory\n");
165                 retval = -1;
166                 goto exit;
167         }
168
169         /* get the device link (if parent exists look here) */
170         class_dev_parent = sysfs_get_classdev_parent(class_dev);
171         if (class_dev_parent != NULL) 
172                 sysfs_dev = sysfs_get_classdev_device(class_dev_parent);
173         else 
174                 sysfs_dev = sysfs_get_classdev_device(class_dev);
175         
176         if (sysfs_dev != NULL)
177                 printf("follow the class device's \"device\"\n");
178
179         /* look the device chain upwards */
180         while (sysfs_dev != NULL) {
181                 printf("  looking at the device chain at '%s':\n", sysfs_dev->path);
182                 printf("    BUS=\"%s\"\n", sysfs_dev->bus);
183                 printf("    ID=\"%s\"\n", sysfs_dev->bus_id);
184
185                 /* open sysfs device directory and print all attributes */
186                 print_all_attributes(sysfs_dev->path);
187
188                 sysfs_dev_parent = sysfs_get_device_parent(sysfs_dev);
189                 if (sysfs_dev_parent == NULL)
190                         break;
191
192                 sysfs_dev = sysfs_dev_parent;
193         }
194
195 exit:
196         sysfs_close_class_device(class_dev);
197         return retval;
198 }
199
200 /* print all class/main block devices with major/minor, physical device, driver and bus */
201 static int print_sysfs_devices(void)
202 {
203         struct dlist *subsyslist;
204         char *class;
205
206         subsyslist = sysfs_open_subsystem_list("class");
207         if (!subsyslist)
208                 return -1;
209
210         dlist_for_each_data(subsyslist, class, char) {
211                 struct sysfs_class *cls;
212                 struct dlist *class_devices;
213                 struct sysfs_class_device *class_dev;
214                 struct sysfs_device *phys_dev;
215                 struct sysfs_driver *driver;
216
217                 cls = sysfs_open_class(class);
218                 if (!cls)
219                         continue;
220
221                 class_devices = sysfs_get_class_devices(cls);
222                 if (!class_devices)
223                         continue;
224
225                 dlist_for_each_data(class_devices, class_dev, struct sysfs_class_device) {
226                         struct sysfs_attribute *attr;
227
228                         printf("\n");
229                         printf("DEVPATH        '%s'\n", class_dev->path);
230                         printf("SUBSYSTEM      '%s'\n", class_dev->classname);
231
232                         attr = sysfs_get_classdev_attr(class_dev, "dev");
233                         if (attr) {
234                                 char *pos = &(attr->value[strlen(attr->value)-1]);
235
236                                 if  (pos[0] == '\n')
237                                         pos[0] = '\0';
238
239                                 printf("DEVMAJORMINOR  '%s'\n", attr->value);
240                         }
241
242                         driver = sysfs_get_classdev_driver(class_dev);
243                         if (driver)
244                                 printf("DEVDRIVER      '%s'\n", driver->name);
245
246                         phys_dev = sysfs_get_classdev_device(class_dev);
247                         if (phys_dev) {
248                                 printf("PHYSDEVPATH    '%s'\n", phys_dev->path);
249                                 if (phys_dev->bus[0] != '\0')
250                                         printf("PHYSDEVBUS     '%s'\n", phys_dev->bus);
251                         }
252                 }
253                 sysfs_close_class(cls);
254         }
255         sysfs_close_list(subsyslist);
256
257         return 0;
258 }
259
260 static int process_options(void)
261 {
262         static const char short_options[] = "adn:p:q:rsVh";
263         int option;
264         int retval = 1;
265         struct udevice udev;
266         int root = 0;
267         int attributes = 0;
268         enum query_type query = NONE;
269         char result[1024] = "";
270         char path[NAME_SIZE] = "";
271         char name[NAME_SIZE] = "";
272         char temp[NAME_SIZE];
273         char *pos;
274
275         /* get command line options */
276         while (1) {
277                 option = getopt(main_argc, main_argv, short_options);
278                 if (option == -1)
279                         break;
280
281                 dbg("option '%c'", option);
282                 switch (option) {
283                 case 'n':
284                         dbg("udev name: %s\n", optarg);
285                         strfieldcpy(name, optarg);
286                         break;
287
288                 case 'p':
289                         dbg("udev path: %s\n", optarg);
290                         strfieldcpy(path, optarg);
291                         break;
292
293                 case 'q':
294                         dbg("udev query: %s\n", optarg);
295
296                         if (strcmp(optarg, "name") == 0) {
297                                 query = NAME;
298                                 break;
299                         }
300
301                         if (strcmp(optarg, "symlink") == 0) {
302                                 query = SYMLINK;
303                                 break;
304                         }
305
306                         if (strcmp(optarg, "path") == 0) {
307                                 query = PATH;
308                                 break;
309                         }
310
311                         if (strcmp(optarg, "all") == 0) {
312                                 query = ALL;
313                                 break;
314                         }
315
316                         printf("unknown query type\n");
317                         exit(1);
318
319                 case 'r':
320                         root = 1;
321                         break;
322
323                 case 's':
324                         print_sysfs_devices();
325                         exit(0);
326
327                 case 'a':
328                         attributes = 1;
329                         break;
330
331                 case 'V':
332                         printf("udevinfo, version %s\n", UDEV_VERSION);
333                         exit(0);
334
335                 case 'h':
336                         retval = 0;
337                 case '?':
338                 default:
339                         goto help;
340                 }
341         }
342
343         /* process options */
344         if (query != NONE) {
345                 if (path[0] != '\0') {
346                         /* remove sysfs_path if given */
347                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) == 0) {
348                                 pos = path + strlen(sysfs_path);
349                         } else {
350                                 if (path[0] != '/') {
351                                         /* prepend '/' if missing */
352                                         strfieldcat(temp, "/");
353                                         strfieldcat(temp, path);
354                                         pos = temp;
355                                 } else {
356                                         pos = path;
357                                 }
358                         }
359                         memset(&udev, 0x00, sizeof(struct udevice));
360                         strfieldcpy(udev.devpath, pos);
361                         retval = udevdb_get_dev(&udev);
362                         if (retval != 0) {
363                                 printf("device not found in database\n");
364                                 goto exit;
365                         }
366                         goto print;
367                 }
368
369                 if (name[0] != '\0') {
370                         /* remove udev_root if given */
371                         int len = strlen(udev_root);
372
373                         if (strncmp(name, udev_root, len) == 0) {
374                                 pos = &name[len+1];
375                         } else
376                                 pos = name;
377
378                         memset(&udev, 0x00, sizeof(struct udevice));
379                         strfieldcpy(udev.name, pos);
380                         retval = udevdb_get_dev_byname(&udev, pos);
381                         if (retval != 0) {
382                                 printf("device not found in database\n");
383                                 goto exit;
384                         }
385
386                         goto print;
387                 }
388
389                 printf("query needs device path(-p) or node name(-n) specified\n");
390                 goto exit;
391
392 print:
393                 switch(query) {
394                 case NAME:
395                         if (root) {
396                                 snprintf(result, NAME_SIZE-1, "%s/%s", udev_root, udev.name);
397                                 result[NAME_SIZE-1] = '\0';
398                         } else {
399                                 strfieldcpy(result, udev.name);
400                         }
401                         break;
402
403                 case SYMLINK:
404                         if (root) {
405                                 int slen;
406                                 char *spos;
407                                 char slink[NAME_SIZE];
408
409                                 pos = result;
410                                 foreach_strpart(udev.symlink, " \n\r", spos, slen) {
411                                         strncpy(slink, spos, slen);
412                                         slink[slen] = '\0';
413                                         pos += sprintf(pos, "%s/%s ", udev_root, slink);
414                                 }
415                         } else {
416                                 strfieldcpy(result, udev.symlink);
417                         }
418                         break;
419
420                 case PATH:
421                         strfieldcpy(result, path);
422                         break;
423
424                 case ALL:
425                         print_record(&udev);
426                         goto exit;
427
428                 default:
429                         goto exit;
430                 }
431                 printf("%s\n", result);
432
433 exit:
434                 return retval;
435         }
436
437         if (attributes) {
438                 if (path[0] == '\0') {
439                         printf("attribute walk on device chain needs path(-p) specified\n");
440                         return -EINVAL;
441                 } else {
442                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) != 0) {
443                                 /* prepend sysfs mountpoint if not given */
444                                 strfieldcpy(temp, path);
445                                 strfieldcpy(path, sysfs_path);
446                                 strfieldcat(path, temp);
447                         }
448                         print_device_chain(path);
449                         return 0;
450                 }
451         }
452
453         if (root) {
454                 printf("%s\n", udev_root);
455                 return 0;
456         }
457
458 help:
459         printf("Usage: [-anpqrdVh]\n"
460                "  -q TYPE  query database for the specified value:\n"
461                "             'name'    name of device node\n"
462                "             'symlink' pointing to node\n"
463                "             'path'    sysfs device path\n"
464                "             'all'     all values\n"
465                "\n"
466                "  -p PATH  sysfs device path used for query or chain\n"
467                "  -n NAME  node/symlink name used for query\n"
468                "\n"
469                "  -r       print udev root\n"
470                "  -a       print all SYSFS_attributes along the device chain\n"
471                "  -s       print all sysfs devices with major/minor, physical device and bus\n"
472                "  -V       print udev version\n"
473                "  -h       print this help text\n"
474                "\n");
475         return retval;
476 }
477
478 int main(int argc, char *argv[], char *envp[])
479 {
480         int rc = 0;
481
482         main_argv = argv;
483         main_argc = argc;
484
485         logging_init("udevinfo");
486
487         /* initialize our configuration */
488         udev_init_config();
489
490         rc = process_options();
491
492         logging_close();
493         exit(rc);
494 }