chiark / gitweb /
release 107
[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                         strlcpy(filename, path, sizeof(filename));
71                         strlcat(filename, "/", sizeof(filename));
72                         strlcat(filename, dent->d_name, sizeof(filename));
73                         if (lstat(filename, &statbuf) != 0)
74                                 continue;
75                         if (S_ISLNK(statbuf.st_mode))
76                                 continue;
77
78                         attr_value = sysfs_attr_get_value(devpath, dent->d_name);
79                         if (attr_value == NULL)
80                                 continue;
81                         len = strlcpy(value, attr_value, sizeof(value));
82                         dbg("attr '%s'='%s'(%zi)", dent->d_name, value, len);
83
84                         /* remove trailing newlines */
85                         while (len && value[len-1] == '\n')
86                                 value[--len] = '\0';
87
88                         /* skip nonprintable attributes */
89                         while (len && isprint(value[len-1]))
90                                 len--;
91                         if (len) {
92                                 dbg("attribute value of '%s' non-printable, skip", dent->d_name);
93                                 continue;
94                         }
95
96                         replace_untrusted_chars(value);
97                         printf("    %s{%s}==\"%s\"\n", key, dent->d_name, value);
98                 }
99         }
100         printf("\n");
101 }
102
103 static int print_device_chain(const char *devpath)
104 {
105         struct sysfs_device *dev;
106
107         dev = sysfs_device_get(devpath);
108         if (dev == NULL)
109                 return -1;
110
111         printf("\n"
112                "Udevinfo starts with the device specified by the devpath and then\n"
113                "walks up the chain of parent devices. It prints for every device\n"
114                "found, all possible attributes in the udev rules key format.\n"
115                "A rule to match, can be composed by the attributes of the device\n"
116                "and the attributes from one single parent device.\n"
117                "\n");
118
119         printf("  looking at device '%s':\n", dev->devpath);
120         printf("    KERNEL==\"%s\"\n", dev->kernel);
121         printf("    SUBSYSTEM==\"%s\"\n", dev->subsystem);
122         printf("    DRIVER==\"%s\"\n", dev->driver);
123         print_all_attributes(dev->devpath, "ATTR");
124
125         /* walk up the chain of devices */
126         while (1) {
127                 dev = sysfs_device_get_parent(dev);
128                 if (dev == NULL)
129                         break;
130                 printf("  looking at parent device '%s':\n", dev->devpath);
131                 printf("    KERNELS==\"%s\"\n", dev->kernel);
132                 printf("    SUBSYSTEMS==\"%s\"\n", dev->subsystem);
133                 printf("    DRIVERS==\"%s\"\n", dev->driver);
134
135                 print_all_attributes(dev->devpath, "ATTRS");
136         }
137
138         return 0;
139 }
140
141 static void print_record(struct udevice *udev)
142 {
143         struct name_entry *name_loop;
144
145         printf("P: %s\n", udev->dev->devpath);
146         printf("N: %s\n", udev->name);
147         printf("L: %i\n", udev->link_priority);
148         list_for_each_entry(name_loop, &udev->symlink_list, node)
149                 printf("S: %s\n", name_loop->name);
150         list_for_each_entry(name_loop, &udev->env_list, node)
151                 printf("E: %s\n", name_loop->name);
152 }
153
154 static void export_db(void) {
155         LIST_HEAD(name_list);
156         struct name_entry *name_loop;
157
158         udev_db_get_all_entries(&name_list);
159         list_for_each_entry(name_loop, &name_list, node) {
160                 struct udevice *udev_db;
161
162                 udev_db = udev_device_init(NULL);
163                 if (udev_db == NULL)
164                         continue;
165                 if (udev_db_get_device(udev_db, name_loop->name) == 0)
166                         print_record(udev_db);
167                         printf("\n");
168                 udev_device_cleanup(udev_db);
169         }
170         name_list_cleanup(&name_list);
171 }
172
173 static int lookup_device_by_name(struct udevice *udev, const char *name)
174 {
175         LIST_HEAD(name_list);
176         struct name_entry *device;
177         int rc  = -1;
178
179         if (udev_db_get_devices_by_name(name, &name_list) <= 0)
180                 goto out;
181
182         /* select the device that matches the dev_t of name */
183         list_for_each_entry(device, &name_list, node) {
184                 char filename[PATH_SIZE];
185                 struct stat statbuf;
186
187                 udev_device_init(udev);
188                 if (udev_db_get_device(udev, device->name) != 0)
189                         continue;
190                 info("found db entry '%s'", device->name);
191
192                 strlcpy(filename, udev_root, sizeof(filename));
193                 strlcat(filename, "/", sizeof(filename));
194                 strlcat(filename, name, sizeof(filename));
195                 if (stat(filename, &statbuf) != 0)
196                         continue;
197                 if (statbuf.st_rdev == udev->devt) {
198                         info("found '%s', dev_t matches", udev->name);
199                         rc = 0;
200                         break;
201                 }
202         }
203 out:
204         name_list_cleanup(&name_list);
205         return rc;
206 }
207
208 int main(int argc, char *argv[], char *envp[])
209 {
210         int option;
211         struct udevice *udev;
212         int root = 0;
213
214         static const struct option options[] = {
215                 { "name", 1, NULL, 'n' },
216                 { "path", 1, NULL, 'p' },
217                 { "query", 1, NULL, 'q' },
218                 { "attribute-walk", 0, NULL, 'a' },
219                 { "export-db", 0, NULL, 'e' },
220                 { "root", 0, NULL, 'r' },
221                 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
222                 { "help", 0, NULL, 'h' },
223                 {}
224         };
225
226         enum action_type {
227                 ACTION_NONE,
228                 ACTION_QUERY,
229                 ACTION_ATTRIBUTE_WALK,
230                 ACTION_ROOT,
231         } action = ACTION_NONE;
232
233         enum query_type {
234                 QUERY_NONE,
235                 QUERY_NAME,
236                 QUERY_PATH,
237                 QUERY_SYMLINK,
238                 QUERY_ENV,
239                 QUERY_ALL,
240         } query = QUERY_NONE;
241
242         char path[PATH_SIZE] = "";
243         char name[PATH_SIZE] = "";
244         struct name_entry *name_loop;
245         int rc = 0;
246
247         logging_init("udevinfo");
248         udev_config_init();
249         sysfs_init();
250
251         udev = udev_device_init(NULL);
252         if (udev == NULL) {
253                 rc = 1;
254                 goto exit;
255         }
256
257         /* get command line options */
258         while (1) {
259                 option = getopt_long(argc, argv, "aen:p:q:rVh", options, NULL);
260                 if (option == -1)
261                         break;
262
263                 dbg("option '%c'", option);
264                 switch (option) {
265                 case 'n':
266                         /* remove /dev if given */
267                         if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
268                                 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
269                         else
270                                 strlcpy(name, optarg, sizeof(name));
271                         dbg("name: %s\n", name);
272                         break;
273                 case 'p':
274                         /* remove /sys if given */
275                         if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
276                                 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
277                         else
278                                 strlcpy(path, optarg, sizeof(path));
279                         dbg("path: %s\n", path);
280                         break;
281                 case 'q':
282                         dbg("udev query: %s\n", optarg);
283                         action = ACTION_QUERY;
284                         if (strcmp(optarg, "name") == 0) {
285                                 query = QUERY_NAME;
286                                 break;
287                         }
288                         if (strcmp(optarg, "symlink") == 0) {
289                                 query = QUERY_SYMLINK;
290                                 break;
291                         }
292                         if (strcmp(optarg, "path") == 0) {
293                                 query = QUERY_PATH;
294                                 break;
295                         }
296                         if (strcmp(optarg, "env") == 0) {
297                                 query = QUERY_ENV;
298                                 break;
299                         }
300                         if (strcmp(optarg, "all") == 0) {
301                                 query = QUERY_ALL;
302                                 break;
303                         }
304                         fprintf(stderr, "unknown query type\n");
305                         rc = 2;
306                         goto exit;
307                 case 'r':
308                         if (action == ACTION_NONE)
309                                 action = ACTION_ROOT;
310                         root = 1;
311                         break;
312                 case 'a':
313                         action = ACTION_ATTRIBUTE_WALK;
314                         break;
315                 case 'e':
316                         export_db();
317                         goto exit;
318                 case 1:
319                         printf("%s\n", UDEV_VERSION);
320                         goto exit;
321                 case 'V':
322                         printf("udevinfo, version %s\n", UDEV_VERSION);
323                         goto exit;
324                 case 'h':
325                         printf("Usage: udevinfo OPTIONS\n"
326                                "  --query=<type>    query database for the specified value:\n"
327                                "    name            name of device node\n"
328                                "    symlink         pointing to node\n"
329                                "    path            sysfs device path\n"
330                                "    env             the device related imported environment\n"
331                                "    all             all values\n"
332                                "\n"
333                                "  --path=<devpath>  sysfs device path used for query or chain\n"
334                                "  --name=<name>     node or symlink name used for query\n"
335                                "\n"
336                                "  --root            prepend to query result or print udev_root\n"
337                                "  --attribute-walk  print all SYSFS_attributes along the device chain\n"
338                                "  --export-db       export the content of the udev database\n"
339                                "  --help            print this text\n"
340                                "\n");
341                         goto exit;
342                 default:
343                         goto exit;
344                 }
345         }
346
347         /* run action */
348         switch (action) {
349         case ACTION_QUERY:
350                 /* needs devpath or node/symlink name for query */
351                 if (path[0] != '\0') {
352                         if (udev_db_get_device(udev, path) != 0) {
353                                 fprintf(stderr, "no record for '%s' in database\n", path);
354                                 rc = 3;
355                                 goto exit;
356                         }
357                 } else if (name[0] != '\0') {
358                         if (lookup_device_by_name(udev, name) != 0) {
359                                 fprintf(stderr, "node name not found\n");
360                                 rc = 4;
361                                 goto exit;
362                         }
363                 } else {
364                         fprintf(stderr, "query needs --path or node --name specified\n");
365                         rc = 4;
366                         goto exit;
367                 }
368
369                 switch(query) {
370                 case QUERY_NAME:
371                         if (root)
372                                 printf("%s/%s\n", udev_root, udev->name);
373                         else
374                                 printf("%s\n", udev->name);
375                         break;
376                 case QUERY_SYMLINK:
377                         if (list_empty(&udev->symlink_list))
378                                 goto exit;
379                         if (root)
380                                 list_for_each_entry(name_loop, &udev->symlink_list, node)
381                                         printf("%s/%s ", udev_root, name_loop->name);
382                         else
383                                 list_for_each_entry(name_loop, &udev->symlink_list, node)
384                                         printf("%s ", name_loop->name);
385                         printf("\n");
386                         break;
387                 case QUERY_PATH:
388                         printf("%s\n", udev->dev->devpath);
389                         goto exit;
390                 case QUERY_ENV:
391                         list_for_each_entry(name_loop, &udev->env_list, node)
392                                 printf("%s\n", name_loop->name);
393                         break;
394                 case QUERY_ALL:
395                         print_record(udev);
396                         break;
397                 default:
398                         fprintf(stderr, "unknown query type\n");
399                         break;
400                 }
401                 break;
402         case ACTION_ATTRIBUTE_WALK:
403                 if (path[0] != '\0') {
404                         if (print_device_chain(path) != 0) {
405                                 fprintf(stderr, "device not found\n");
406                                 rc = 4;
407                                 goto exit;
408                         }
409                 } else if (name[0] != '\0') {
410                         if (lookup_device_by_name(udev, name) != 0) {
411                                 fprintf(stderr, "node name not found\n");
412                                 rc = 4;
413                                 goto exit;
414                         }
415                         if (print_device_chain(udev->dev->devpath) != 0) {
416                                 fprintf(stderr, "device not found\n");
417                                 rc = 4;
418                                 goto exit;
419                         }
420                 } else {
421                         fprintf(stderr, "attribute walk needs --path or node --name specified\n");
422                         rc = 5;
423                         goto exit;
424                 }
425                 break;
426         case ACTION_ROOT:
427                 printf("%s\n", udev_root);
428                 break;
429         default:
430                 fprintf(stderr, "missing option\n");
431                 rc = 1;
432                 break;
433         }
434
435 exit:
436         udev_device_cleanup(udev);
437         sysfs_cleanup();
438         logging_close();
439         return rc;
440 }