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