chiark / gitweb /
[PATCH] udev_volume_id: add Reiser4 support
[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 void log_message (int level, const char *format, ...)
43 {
44         va_list args;
45
46         va_start(args, format);
47         vsyslog(level, format, args);
48         va_end(args);
49 }
50 #endif
51
52 static int print_all_attributes(const char *path)
53 {
54         struct dlist *attributes;
55         struct sysfs_attribute *attr;
56         struct sysfs_directory *sysfs_dir;
57         char value[SYSFS_VALUE_SIZE];
58         int len;
59         int retval = 0;
60
61         sysfs_dir = sysfs_open_directory(path);
62         if (sysfs_dir == NULL)
63                 return -1;
64
65         attributes = sysfs_get_dir_attributes(sysfs_dir);
66         if (attributes == NULL) {
67                 retval = -1;
68                 goto exit;
69         }
70
71         dlist_for_each_data(attributes, attr, struct sysfs_attribute) {
72                 if (attr->value != NULL) {
73                         strfieldcpy(value, attr->value);
74                         len = strlen(value);
75                         if (len == 0)
76                                 continue;
77
78                         /* remove trailing newline */
79                         if (value[len-1] == '\n') {
80                                 value[len-1] = '\0';
81                                 len--;
82                         }
83
84                         /* skip nonprintable values */
85                         while (len) {
86                                 if (isprint(value[len-1]) == 0)
87                                         break;
88                                 len--;
89                         }
90                         if (len == 0)
91                                 printf("    SYSFS{%s}=\"%s\"\n", attr->name, value);
92                 }
93         }
94         printf("\n");
95
96 exit:
97         sysfs_close_directory(sysfs_dir);
98
99         return retval;
100 }
101
102 static int print_record(struct udevice *udev)
103 {
104         printf("P: %s\n", udev->devpath);
105         printf("N: %s\n", udev->name);
106         printf("S: %s\n", udev->symlink);
107         printf("\n");
108         return 0;
109 }
110
111 enum query_type {
112         NONE,
113         NAME,
114         PATH,
115         SYMLINK,
116         ALL
117 };
118
119 static int print_device_chain(const char *path)
120 {
121         struct sysfs_class_device *class_dev;
122         struct sysfs_class_device *class_dev_parent;
123         struct sysfs_attribute *attr;
124         struct sysfs_device *sysfs_dev;
125         struct sysfs_device *sysfs_dev_parent;
126         int retval = 0;
127
128         /*  get the class dev */
129         class_dev = sysfs_open_class_device_path(path);
130         if (class_dev == NULL) {
131                 printf("couldn't get the class device\n");
132                 return -1;
133         }
134
135         printf("\nudevinfo starts with the device the node belongs to and then walks up the\n"
136                "device chain, to print for every device found, all possibly useful attributes\n"
137                "in the udev key format.\n"
138                "Only attributes within one device section may be used together in one rule,\n"
139                "to match the device for which the node will be created.\n"
140                "\n");
141
142         /* look for the 'dev' file */
143         attr = sysfs_get_classdev_attr(class_dev, "dev");
144         if (attr != NULL)
145                 printf("device '%s' has major:minor %s", class_dev->path, attr->value);
146
147         /* open sysfs class device directory and print all attributes */
148         printf("  looking at class device '%s':\n", class_dev->path);
149         printf("    SUBSYSTEM=\"%s\"\n", class_dev->classname);
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                 printf("    DRIVER=\"%s\"\n", sysfs_dev->driver_name);
172
173                 /* open sysfs device directory and print all attributes */
174                 print_all_attributes(sysfs_dev->path);
175
176                 sysfs_dev_parent = sysfs_get_device_parent(sysfs_dev);
177                 if (sysfs_dev_parent == NULL)
178                         break;
179
180                 sysfs_dev = sysfs_dev_parent;
181         }
182
183 exit:
184         sysfs_close_class_device(class_dev);
185         return retval;
186 }
187
188 /* print all class/main block devices with major/minor, physical device, driver and bus */
189 static int print_sysfs_devices(void)
190 {
191         struct dlist *subsyslist;
192         char *class;
193
194         subsyslist = sysfs_open_subsystem_list("class");
195         if (!subsyslist)
196                 return -1;
197
198         dlist_for_each_data(subsyslist, class, char) {
199                 struct sysfs_class *cls;
200                 struct dlist *class_devices;
201                 struct sysfs_class_device *class_dev;
202                 struct sysfs_device *phys_dev;
203                 unsigned int major, minor;
204
205                 cls = sysfs_open_class(class);
206                 if (!cls)
207                         continue;
208
209                 class_devices = sysfs_get_class_devices(cls);
210                 if (!class_devices)
211                         continue;
212
213                 dlist_for_each_data(class_devices, class_dev, struct sysfs_class_device) {
214                         struct sysfs_attribute *attr;
215
216                         printf("\n");
217                         printf("DEVPATH        '%s'\n", class_dev->path);
218                         printf("SUBSYSTEM      '%s'\n", class_dev->classname);
219
220                         attr = sysfs_get_classdev_attr(class_dev, "dev");
221                         if (attr) {
222                                 sscanf(attr->value, "%u:%u", &major, &minor);
223                                 printf("MAJOR          %u\n", major);
224                                 printf("MINOR          %u\n", minor);
225                         }
226
227                         phys_dev = sysfs_get_classdev_device(class_dev);
228                         if (phys_dev) {
229                                 printf("PHYSDEVPATH    '%s'\n", phys_dev->path);
230                                 if (phys_dev->bus[0] != '\0')
231                                         printf("PHYSDEVBUS     '%s'\n", phys_dev->bus);
232
233                                 if (phys_dev->driver_name[0] != '\0')
234                                         printf("PHYSDEVDRIVER  '%s'\n", phys_dev->driver_name);
235                         }
236                 }
237                 sysfs_close_class(cls);
238         }
239         sysfs_close_list(subsyslist);
240
241         return 0;
242 }
243
244 static int process_options(int argc, char *argv[])
245 {
246         static const char short_options[] = "an:p:q:rsVh";
247         int option;
248         int retval = 1;
249         struct udevice udev;
250         int root = 0;
251         int attributes = 0;
252         enum query_type query = NONE;
253         char result[1024] = "";
254         char path[NAME_SIZE] = "";
255         char name[NAME_SIZE] = "";
256         char temp[NAME_SIZE];
257         char *pos;
258
259         /* get command line options */
260         while (1) {
261                 option = getopt(argc, argv, short_options);
262                 if (option == -1)
263                         break;
264
265                 dbg("option '%c'", option);
266                 switch (option) {
267                 case 'n':
268                         dbg("udev name: %s\n", optarg);
269                         strfieldcpy(name, optarg);
270                         break;
271
272                 case 'p':
273                         dbg("udev path: %s\n", optarg);
274                         strfieldcpy(path, optarg);
275                         break;
276
277                 case 'q':
278                         dbg("udev query: %s\n", optarg);
279
280                         if (strcmp(optarg, "name") == 0) {
281                                 query = NAME;
282                                 break;
283                         }
284
285                         if (strcmp(optarg, "symlink") == 0) {
286                                 query = SYMLINK;
287                                 break;
288                         }
289
290                         if (strcmp(optarg, "path") == 0) {
291                                 query = PATH;
292                                 break;
293                         }
294
295                         if (strcmp(optarg, "all") == 0) {
296                                 query = ALL;
297                                 break;
298                         }
299
300                         printf("unknown query type\n");
301                         exit(1);
302
303                 case 'r':
304                         root = 1;
305                         break;
306
307                 case 's':
308                         print_sysfs_devices();
309                         exit(0);
310
311                 case 'a':
312                         attributes = 1;
313                         break;
314
315                 case 'V':
316                         printf("udevinfo, version %s\n", UDEV_VERSION);
317                         exit(0);
318
319                 case 'h':
320                         retval = 0;
321                 case '?':
322                 default:
323                         goto help;
324                 }
325         }
326
327         /* process options */
328         if (query != NONE) {
329                 if (path[0] != '\0') {
330                         /* remove sysfs_path if given */
331                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) == 0) {
332                                 pos = path + strlen(sysfs_path);
333                         } else {
334                                 if (path[0] != '/') {
335                                         /* prepend '/' if missing */
336                                         strfieldcat(temp, "/");
337                                         strfieldcat(temp, path);
338                                         pos = temp;
339                                 } else {
340                                         pos = path;
341                                 }
342                         }
343                         memset(&udev, 0x00, sizeof(struct udevice));
344                         retval = udev_db_get_device_by_devpath(&udev, pos);
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: udevinfo [-anpqrVh]\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 }