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