chiark / gitweb /
f1b74fc8f6c98899b08eb5ab1e4887cbd1b9c7ba
[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/block devices with major/minor, physical device and bus*/
201 static void print_sysfs_devices(void)
202 {
203         struct dlist *subsyslist;
204         char *class;
205
206         subsyslist = sysfs_open_subsystem_list("class");
207         if (!subsyslist)
208                 exit(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
216                 cls = sysfs_open_class(class);
217                 if (!cls)
218                         continue;
219
220                 class_devices = sysfs_get_class_devices(cls);
221                 if (!class_devices)
222                         continue;
223
224                 dlist_for_each_data(class_devices, class_dev, struct sysfs_class_device) {
225                         struct sysfs_attribute *attr;
226
227                         printf("\n");
228                         printf("DEVPATH        '%s'\n", class_dev->path);
229                         printf("SUBSYSTEM      '%s'\n", class_dev->classname);
230                         printf("NAME           '%s'\n", class_dev->name);
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("MAJORMINOR     '%s'\n", attr->value);
240                         }
241
242                         phys_dev = sysfs_get_classdev_device(class_dev);
243                         if (phys_dev) {
244                                 printf("PHYSDEVPATH    '%s'\n", phys_dev->path);
245                                 if (phys_dev->bus[0] != '\0')
246                                         printf("PHYSDEVPATHBUS '%s'\n", phys_dev->bus);
247                                 if (phys_dev->driver_name[0] != '\0')
248                                         printf("DRIVER         '%s'\n", phys_dev->driver_name);
249                         }
250                 }
251
252                 sysfs_close_class(cls);
253         }
254
255         sysfs_close_list(subsyslist);
256 }
257
258 static int process_options(void)
259 {
260         static const char short_options[] = "adn:p:q:rsVh";
261         int option;
262         int retval = 1;
263         struct udevice udev;
264         int root = 0;
265         int attributes = 0;
266         enum query_type query = NONE;
267         char result[NAME_SIZE] = "";
268         char path[NAME_SIZE] = "";
269         char name[NAME_SIZE] = "";
270         char temp[NAME_SIZE];
271         char *pos;
272
273         /* get command line options */
274         while (1) {
275                 option = getopt(main_argc, main_argv, short_options);
276                 if (option == -1)
277                         break;
278
279                 dbg("option '%c'", option);
280                 switch (option) {
281                 case 'n':
282                         dbg("udev name: %s\n", optarg);
283                         strfieldcpy(name, optarg);
284                         break;
285
286                 case 'p':
287                         dbg("udev path: %s\n", optarg);
288                         strfieldcpy(path, optarg);
289                         break;
290
291                 case 'q':
292                         dbg("udev query: %s\n", optarg);
293
294                         if (strcmp(optarg, "name") == 0) {
295                                 query = NAME;
296                                 break;
297                         }
298
299                         if (strcmp(optarg, "symlink") == 0) {
300                                 query = SYMLINK;
301                                 break;
302                         }
303
304                         if (strcmp(optarg, "path") == 0) {
305                                 query = PATH;
306                                 break;
307                         }
308
309                         if (strcmp(optarg, "all") == 0) {
310                                 query = ALL;
311                                 break;
312                         }
313
314                         printf("unknown query type\n");
315                         exit(1);
316
317                 case 'r':
318                         root = 1;
319                         break;
320
321                 case 's':
322                         print_sysfs_devices();
323                         exit(0);
324
325                 case 'a':
326                         attributes = 1;
327                         break;
328
329                 case 'V':
330                         printf("udevinfo, version %s\n", UDEV_VERSION);
331                         exit(0);
332
333                 case 'h':
334                         retval = 0;
335                 case '?':
336                 default:
337                         goto help;
338                 }
339         }
340
341         /* process options */
342         if (query != NONE) {
343                 if (path[0] != '\0') {
344                         /* remove sysfs_path if given */
345                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) == 0) {
346                                 pos = path + strlen(sysfs_path);
347                         } else {
348                                 if (path[0] != '/') {
349                                         /* prepend '/' if missing */
350                                         strfieldcat(temp, "/");
351                                         strfieldcat(temp, path);
352                                         pos = temp;
353                                 } else {
354                                         pos = path;
355                                 }
356                         }
357                         memset(&udev, 0x00, sizeof(struct udevice));
358                         strfieldcpy(udev.devpath, pos);
359                         retval = udevdb_get_dev(&udev);
360                         if (retval != 0) {
361                                 printf("device not found in database\n");
362                                 goto exit;
363                         }
364                         goto print;
365                 }
366
367                 if (name[0] != '\0') {
368                         /* remove udev_root if given */
369                         int len = strlen(udev_root);
370
371                         if (strncmp(name, udev_root, len) == 0) {
372                                 pos = &name[len+1];
373                         } else
374                                 pos = name;
375
376                         memset(&udev, 0x00, sizeof(struct udevice));
377                         strfieldcpy(udev.name, pos);
378                         retval = udevdb_get_dev_byname(&udev, pos);
379                         if (retval != 0) {
380                                 printf("device not found in database\n");
381                                 goto exit;
382                         }
383
384                         goto print;
385                 }
386
387                 printf("query needs device path(-p) or node name(-n) specified\n");
388                 goto exit;
389
390 print:
391                 switch(query) {
392                 case NAME:
393                         if (root) {
394                                 snprintf(result, NAME_SIZE-1, "%s/%s", udev_root, udev.name);
395                                 result[NAME_SIZE-1] = '\0';
396                         } else {
397                                 strfieldcpy(result, udev.name);
398                         }
399                         break;
400
401                 case SYMLINK:
402                         strfieldcpy(result, udev.symlink);
403                         break;
404
405                 case PATH:
406                         strfieldcpy(result, path);
407                         break;
408
409                 case ALL:
410                         print_record(&udev);
411                         goto exit;
412
413                 default:
414                         goto exit;
415                 }
416                 printf("%s\n", result);
417
418 exit:
419                 return retval;
420         }
421
422         if (attributes) {
423                 if (path[0] == '\0') {
424                         printf("attribute walk on device chain needs path(-p) specified\n");
425                         return -EINVAL;
426                 } else {
427                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) != 0) {
428                                 /* prepend sysfs mountpoint if not given */
429                                 strfieldcpy(temp, path);
430                                 strfieldcpy(path, sysfs_path);
431                                 strfieldcat(path, temp);
432                         }
433                         print_device_chain(path);
434                         return 0;
435                 }
436         }
437
438         if (root) {
439                 printf("%s\n", udev_root);
440                 return 0;
441         }
442
443 help:
444         printf("Usage: [-anpqrdVh]\n"
445                "  -q TYPE  query database for the specified value:\n"
446                "             'name'    name of device node\n"
447                "             'symlink' pointing to node\n"
448                "             'path'    sysfs device path\n"
449                "             'all'     all values\n"
450                "\n"
451                "  -p PATH  sysfs device path used for query or chain\n"
452                "  -n NAME  node/symlink name used for query\n"
453                "\n"
454                "  -r       print udev root\n"
455                "  -a       print all SYSFS_attributes along the device chain\n"
456                "  -s       print all sysfs devices with major/minor, physical device and bus\n"
457                "  -V       print udev version\n"
458                "  -h       print this help text\n"
459                "\n");
460         return retval;
461 }
462
463 int main(int argc, char *argv[], char *envp[])
464 {
465         int rc = 0;
466
467         main_argv = argv;
468         main_argc = argc;
469
470         logging_init("udevinfo");
471
472         /* initialize our configuration */
473         udev_init_config();
474
475         rc = process_options();
476
477         logging_close();
478         exit(rc);
479 }