chiark / gitweb /
a2bc0136913070d569a62cf87a77d07181ab8977
[elogind.git] / udevinfo.c
1 /*
2  * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
3  *
4  *      This program is free software; you can redistribute it and/or modify it
5  *      under the terms of the GNU General Public License as published by the
6  *      Free Software Foundation version 2 of the License.
7  * 
8  *      This program is distributed in the hope that it will be useful, but
9  *      WITHOUT ANY WARRANTY; without even the implied warranty of
10  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  *      General Public License for more details.
12  * 
13  *      You should have received a copy of the GNU General Public License along
14  *      with this program; if not, write to the Free Software Foundation, Inc.,
15  *      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16  *
17  */
18
19 #include <stdlib.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <stddef.h>
23 #include <ctype.h>
24 #include <stdarg.h>
25 #include <unistd.h>
26 #include <dirent.h>
27 #include <errno.h>
28 #include <getopt.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31
32 #include "udev.h"
33
34
35 #ifdef USE_LOG
36 void log_message (int priority, const char *format, ...)
37 {
38         va_list args;
39
40         if (priority > udev_log_priority)
41                 return;
42
43         va_start(args, format);
44         vsyslog(priority, format, args);
45         va_end(args);
46 }
47 #endif
48
49 static void print_all_attributes(const char *devpath, const char *key)
50 {
51         char path[PATH_SIZE];
52         DIR *dir;
53         struct dirent *dent;
54
55         strlcpy(path, sysfs_path, sizeof(path));
56         strlcat(path, devpath, sizeof(path));
57
58         dir = opendir(path);
59         if (dir != NULL) {
60                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
61                         struct stat statbuf;
62                         char filename[PATH_SIZE];
63                         char *attr_value;
64                         char value[NAME_SIZE];
65                         size_t len;
66
67                         if (dent->d_name[0] == '.')
68                                 continue;
69
70                         if (strcmp(dent->d_name, "uevent") == 0)
71                                 continue;
72
73                         strlcpy(filename, path, sizeof(filename));
74                         strlcat(filename, "/", sizeof(filename));
75                         strlcat(filename, dent->d_name, sizeof(filename));
76                         if (lstat(filename, &statbuf) != 0)
77                                 continue;
78                         if (S_ISLNK(statbuf.st_mode))
79                                 continue;
80
81                         attr_value = sysfs_attr_get_value(devpath, dent->d_name);
82                         if (attr_value == NULL)
83                                 continue;
84                         len = strlcpy(value, attr_value, sizeof(value));
85                         if(len >= sizeof(value))
86                                 len = sizeof(value) - 1;
87                         dbg("attr '%s'='%s'(%zi)", dent->d_name, value, len);
88
89                         /* remove trailing newlines */
90                         while (len && value[len-1] == '\n')
91                                 value[--len] = '\0';
92
93                         /* skip nonprintable attributes */
94                         while (len && isprint(value[len-1]))
95                                 len--;
96                         if (len) {
97                                 dbg("attribute value of '%s' non-printable, skip", dent->d_name);
98                                 continue;
99                         }
100
101                         replace_chars(value, ALLOWED_CHARS_INPUT);
102                         printf("    %s{%s}==\"%s\"\n", key, dent->d_name, value);
103                 }
104         }
105         printf("\n");
106 }
107
108 static int print_device_chain(const char *devpath)
109 {
110         struct sysfs_device *dev;
111
112         dev = sysfs_device_get(devpath);
113         if (dev == NULL)
114                 return -1;
115
116         printf("\n"
117                "Udevinfo starts with the device specified by the devpath and then\n"
118                "walks up the chain of parent devices. It prints for every device\n"
119                "found, all possible attributes in the udev rules key format.\n"
120                "A rule to match, can be composed by the attributes of the device\n"
121                "and the attributes from one single parent device.\n"
122                "\n");
123
124         printf("  looking at device '%s':\n", dev->devpath);
125         printf("    KERNEL==\"%s\"\n", dev->kernel);
126         printf("    SUBSYSTEM==\"%s\"\n", dev->subsystem);
127         printf("    DRIVER==\"%s\"\n", dev->driver);
128         print_all_attributes(dev->devpath, "ATTR");
129
130         /* walk up the chain of devices */
131         while (1) {
132                 dev = sysfs_device_get_parent(dev);
133                 if (dev == NULL)
134                         break;
135                 printf("  looking at parent device '%s':\n", dev->devpath);
136                 printf("    KERNELS==\"%s\"\n", dev->kernel);
137                 printf("    SUBSYSTEMS==\"%s\"\n", dev->subsystem);
138                 printf("    DRIVERS==\"%s\"\n", dev->driver);
139
140                 print_all_attributes(dev->devpath, "ATTRS");
141         }
142
143         return 0;
144 }
145
146 static void print_record(struct udevice *udev)
147 {
148         struct name_entry *name_loop;
149
150         printf("P: %s\n", udev->dev->devpath);
151         printf("N: %s\n", udev->name);
152         list_for_each_entry(name_loop, &udev->symlink_list, node)
153                 printf("S: %s\n", name_loop->name);
154         if (udev->link_priority != 0)
155                 printf("L: %i\n", udev->link_priority);
156         if (udev->partitions != 0)
157                 printf("A:%u\n", udev->partitions);
158         if (udev->ignore_remove)
159                 printf("R:%u\n", udev->ignore_remove);
160         list_for_each_entry(name_loop, &udev->env_list, node)
161                 printf("E: %s\n", name_loop->name);
162 }
163
164 static void export_db(void) {
165         LIST_HEAD(name_list);
166         struct name_entry *name_loop;
167
168         udev_db_get_all_entries(&name_list);
169         list_for_each_entry(name_loop, &name_list, node) {
170                 struct udevice *udev_db;
171
172                 udev_db = udev_device_init(NULL);
173                 if (udev_db == NULL)
174                         continue;
175                 if (udev_db_get_device(udev_db, name_loop->name) == 0)
176                         print_record(udev_db);
177                         printf("\n");
178                 udev_device_cleanup(udev_db);
179         }
180         name_list_cleanup(&name_list);
181 }
182
183 static int lookup_device_by_name(struct udevice *udev, const char *name)
184 {
185         LIST_HEAD(name_list);
186         int count;
187         struct name_entry *device;
188         int rc  = -1;
189
190         count = udev_db_get_devices_by_name(name, &name_list);
191         if (count <= 0)
192                 goto out;
193
194         info("found %i devices for '%s'", count, name);
195
196         /* select the device that seems to match */
197         list_for_each_entry(device, &name_list, node) {
198                 char filename[PATH_SIZE];
199                 struct stat statbuf;
200
201                 udev_device_init(udev);
202                 if (udev_db_get_device(udev, device->name) != 0)
203                         continue;
204                 info("found db entry '%s'", device->name);
205
206                 /* make sure, we don't get a link of a differnt device */
207                 strlcpy(filename, udev_root, sizeof(filename));
208                 strlcat(filename, "/", sizeof(filename));
209                 strlcat(filename, name, sizeof(filename));
210                 if (stat(filename, &statbuf) != 0)
211                         continue;
212                 if (major(udev->devt) > 0 && udev->devt != statbuf.st_rdev) {
213                         info("skip '%s', dev_t doesn't match", udev->name);
214                         continue;
215                 }
216                 rc = 0;
217                 break;
218         }
219 out:
220         name_list_cleanup(&name_list);
221         return rc;
222 }
223
224 int main(int argc, char *argv[], char *envp[])
225 {
226         int option;
227         struct udevice *udev;
228         int root = 0;
229
230         static const struct option options[] = {
231                 { "name", 1, NULL, 'n' },
232                 { "path", 1, NULL, 'p' },
233                 { "query", 1, NULL, 'q' },
234                 { "attribute-walk", 0, NULL, 'a' },
235                 { "export-db", 0, NULL, 'e' },
236                 { "root", 0, NULL, 'r' },
237                 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
238                 { "help", 0, NULL, 'h' },
239                 {}
240         };
241
242         enum action_type {
243                 ACTION_NONE,
244                 ACTION_QUERY,
245                 ACTION_ATTRIBUTE_WALK,
246                 ACTION_ROOT,
247         } action = ACTION_NONE;
248
249         enum query_type {
250                 QUERY_NONE,
251                 QUERY_NAME,
252                 QUERY_PATH,
253                 QUERY_SYMLINK,
254                 QUERY_ENV,
255                 QUERY_ALL,
256         } query = QUERY_NONE;
257
258         char path[PATH_SIZE] = "";
259         char name[PATH_SIZE] = "";
260         struct name_entry *name_loop;
261         int rc = 0;
262
263         logging_init("udevinfo");
264         udev_config_init();
265         sysfs_init();
266
267         udev = udev_device_init(NULL);
268         if (udev == NULL) {
269                 rc = 1;
270                 goto exit;
271         }
272
273         while (1) {
274                 option = getopt_long(argc, argv, "aen:p:q:rVh", options, NULL);
275                 if (option == -1)
276                         break;
277
278                 dbg("option '%c'", option);
279                 switch (option) {
280                 case 'n':
281                         /* remove /dev if given */
282                         if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
283                                 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
284                         else
285                                 strlcpy(name, optarg, sizeof(name));
286                         dbg("name: %s", name);
287                         break;
288                 case 'p':
289                         /* remove /sys if given */
290                         if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
291                                 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
292                         else
293                                 strlcpy(path, optarg, sizeof(path));
294                         dbg("path: %s", path);
295                         break;
296                 case 'q':
297                         action = ACTION_QUERY;
298                         if (strcmp(optarg, "name") == 0) {
299                                 query = QUERY_NAME;
300                                 break;
301                         }
302                         if (strcmp(optarg, "symlink") == 0) {
303                                 query = QUERY_SYMLINK;
304                                 break;
305                         }
306                         if (strcmp(optarg, "path") == 0) {
307                                 query = QUERY_PATH;
308                                 break;
309                         }
310                         if (strcmp(optarg, "env") == 0) {
311                                 query = QUERY_ENV;
312                                 break;
313                         }
314                         if (strcmp(optarg, "all") == 0) {
315                                 query = QUERY_ALL;
316                                 break;
317                         }
318                         fprintf(stderr, "unknown query type\n");
319                         rc = 2;
320                         goto exit;
321                 case 'r':
322                         if (action == ACTION_NONE)
323                                 action = ACTION_ROOT;
324                         root = 1;
325                         break;
326                 case 'a':
327                         action = ACTION_ATTRIBUTE_WALK;
328                         break;
329                 case 'e':
330                         export_db();
331                         goto exit;
332                 case 1:
333                         printf("%s\n", UDEV_VERSION);
334                         goto exit;
335                 case 'V':
336                         printf("udevinfo, version %s\n", UDEV_VERSION);
337                         goto exit;
338                 case 'h':
339                         printf("Usage: udevinfo OPTIONS\n"
340                                "  --query=<type>    query database for the specified value:\n"
341                                "    name            name of device node\n"
342                                "    symlink         pointing to node\n"
343                                "    path            sysfs device path\n"
344                                "    env             the device related imported environment\n"
345                                "    all             all values\n"
346                                "\n"
347                                "  --path=<devpath>  sysfs device path used for query or chain\n"
348                                "  --name=<name>     node or symlink name used for query\n"
349                                "\n"
350                                "  --root            prepend to query result or print udev_root\n"
351                                "  --attribute-walk  print all SYSFS_attributes along the device chain\n"
352                                "  --export-db       export the content of the udev database\n"
353                                "  --help            print this text\n"
354                                "\n");
355                         goto exit;
356                 default:
357                         goto exit;
358                 }
359         }
360
361         /* run action */
362         switch (action) {
363         case ACTION_QUERY:
364                 /* needs devpath or node/symlink name for query */
365                 if (path[0] != '\0') {
366                         if (udev_db_get_device(udev, path) != 0) {
367                                 fprintf(stderr, "no record for '%s' in database\n", path);
368                                 rc = 3;
369                                 goto exit;
370                         }
371                 } else if (name[0] != '\0') {
372                         if (lookup_device_by_name(udev, name) != 0) {
373                                 fprintf(stderr, "node name not found\n");
374                                 rc = 4;
375                                 goto exit;
376                         }
377                 } else {
378                         fprintf(stderr, "query needs --path or node --name specified\n");
379                         rc = 4;
380                         goto exit;
381                 }
382
383                 switch(query) {
384                 case QUERY_NAME:
385                         if (root)
386                                 printf("%s/%s\n", udev_root, udev->name);
387                         else
388                                 printf("%s\n", udev->name);
389                         break;
390                 case QUERY_SYMLINK:
391                         list_for_each_entry(name_loop, &udev->symlink_list, node) {
392                                 char c = name_loop->node.next != &udev->symlink_list ? ' ' : '\n';
393
394                                 if (root)
395                                         printf("%s/%s%c", udev_root, name_loop->name, c);
396                                 else
397                                         printf("%s%c", name_loop->name, c);
398                         }
399                         break;
400                 case QUERY_PATH:
401                         printf("%s\n", udev->dev->devpath);
402                         goto exit;
403                 case QUERY_ENV:
404                         list_for_each_entry(name_loop, &udev->env_list, node)
405                                 printf("%s\n", name_loop->name);
406                         break;
407                 case QUERY_ALL:
408                         print_record(udev);
409                         break;
410                 default:
411                         fprintf(stderr, "unknown query type\n");
412                         break;
413                 }
414                 break;
415         case ACTION_ATTRIBUTE_WALK:
416                 if (path[0] != '\0') {
417                         if (print_device_chain(path) != 0) {
418                                 fprintf(stderr, "no valid sysfs device found\n");
419                                 rc = 4;
420                                 goto exit;
421                         }
422                 } else if (name[0] != '\0') {
423                         if (lookup_device_by_name(udev, name) != 0) {
424                                 fprintf(stderr, "node name not found\n");
425                                 rc = 4;
426                                 goto exit;
427                         }
428                         if (print_device_chain(udev->dev->devpath) != 0) {
429                                 fprintf(stderr, "no valid sysfs device found\n");
430                                 rc = 4;
431                                 goto exit;
432                         }
433                 } else {
434                         fprintf(stderr, "attribute walk needs --path or node --name specified\n");
435                         rc = 5;
436                         goto exit;
437                 }
438                 break;
439         case ACTION_ROOT:
440                 printf("%s\n", udev_root);
441                 break;
442         default:
443                 fprintf(stderr, "missing option\n");
444                 rc = 1;
445                 break;
446         }
447
448 exit:
449         udev_device_cleanup(udev);
450         sysfs_cleanup();
451         logging_close();
452         return rc;
453 }