chiark / gitweb /
[PATCH] introduce OPTIONS=ignore_device, ignore_remove, all_partitions" key
[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                         strfieldcpy(udev.devpath, pos);
345                         retval = udev_db_get_device(&udev);
346                         if (retval != 0) {
347                                 printf("device not found in database\n");
348                                 goto exit;
349                         }
350                         goto print;
351                 }
352
353                 if (name[0] != '\0') {
354                         /* remove udev_root if given */
355                         int len = strlen(udev_root);
356
357                         if (strncmp(name, udev_root, len) == 0) {
358                                 pos = &name[len+1];
359                         } else
360                                 pos = name;
361
362                         memset(&udev, 0x00, sizeof(struct udevice));
363                         strfieldcpy(udev.name, pos);
364                         retval = udev_db_get_device_byname(&udev, pos);
365                         if (retval != 0) {
366                                 printf("device not found in database\n");
367                                 goto exit;
368                         }
369
370                         goto print;
371                 }
372
373                 printf("query needs device path(-p) or node name(-n) specified\n");
374                 goto exit;
375
376 print:
377                 switch(query) {
378                 case NAME:
379                         if (root) {
380                                 snprintf(result, NAME_SIZE-1, "%s/%s", udev_root, udev.name);
381                                 result[NAME_SIZE-1] = '\0';
382                         } else {
383                                 strfieldcpy(result, udev.name);
384                         }
385                         break;
386
387                 case SYMLINK:
388                         if (root) {
389                                 int slen;
390                                 char *spos;
391                                 char slink[NAME_SIZE];
392
393                                 pos = result;
394                                 foreach_strpart(udev.symlink, " \n\r", spos, slen) {
395                                         strncpy(slink, spos, slen);
396                                         slink[slen] = '\0';
397                                         pos += sprintf(pos, "%s/%s ", udev_root, slink);
398                                 }
399                         } else {
400                                 strfieldcpy(result, udev.symlink);
401                         }
402                         break;
403
404                 case PATH:
405                         strfieldcpy(result, udev.devpath);
406                         break;
407
408                 case ALL:
409                         print_record(&udev);
410                         goto exit;
411
412                 default:
413                         goto exit;
414                 }
415                 printf("%s\n", result);
416
417 exit:
418                 return retval;
419         }
420
421         if (attributes) {
422                 if (path[0] == '\0') {
423                         printf("attribute walk on device chain needs path(-p) specified\n");
424                         return -EINVAL;
425                 } else {
426                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) != 0) {
427                                 /* prepend sysfs mountpoint if not given */
428                                 strfieldcpy(temp, path);
429                                 strfieldcpy(path, sysfs_path);
430                                 strfieldcat(path, temp);
431                         }
432                         print_device_chain(path);
433                         return 0;
434                 }
435         }
436
437         if (root) {
438                 printf("%s\n", udev_root);
439                 return 0;
440         }
441
442 help:
443         printf("Usage: udevinfo [-anpqrVh]\n"
444                "  -q TYPE  query database for the specified value:\n"
445                "             'name'    name of device node\n"
446                "             'symlink' pointing to node\n"
447                "             'path'    sysfs device path\n"
448                "             'all'     all values\n"
449                "\n"
450                "  -p PATH  sysfs device path used for query or chain\n"
451                "  -n NAME  node/symlink name used for query\n"
452                "\n"
453                "  -r       print udev root\n"
454                "  -a       print all SYSFS_attributes along the device chain\n"
455                "  -s       print all sysfs devices with major/minor, physical device and bus\n"
456                "  -V       print udev version\n"
457                "  -h       print this help text\n"
458                "\n");
459         return retval;
460 }
461
462 int main(int argc, char *argv[], char *envp[])
463 {
464         int rc = 0;
465
466         logging_init("udevinfo");
467
468         /* initialize our configuration */
469         udev_init_config();
470
471         rc = process_options(argc, argv);
472
473         logging_close();
474         exit(rc);
475 }