chiark / gitweb /
udevtest: add --force mode
[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         list_for_each_entry(name_loop, &udev->symlink_list, node)
148                 printf("S: %s\n", name_loop->name);
149         list_for_each_entry(name_loop, &udev->env_list, node)
150                 printf("E: %s\n", name_loop->name);
151 }
152
153 static void export_db(void) {
154         LIST_HEAD(name_list);
155         struct name_entry *name_loop;
156
157         udev_db_get_all_entries(&name_list);
158         list_for_each_entry(name_loop, &name_list, node) {
159                 struct udevice *udev_db;
160
161                 udev_db = udev_device_init(NULL);
162                 if (udev_db == NULL)
163                         continue;
164                 if (udev_db_get_device(udev_db, name_loop->name) == 0)
165                         print_record(udev_db);
166                         printf("\n");
167                 udev_device_cleanup(udev_db);
168         }
169         name_list_cleanup(&name_list);
170 }
171
172 static int lookup_device_by_name(struct udevice *udev, const char *name)
173 {
174         LIST_HEAD(name_list);
175         struct name_entry *device;
176         int rc  = -1;
177
178         if (udev_db_get_devices_by_name(name, &name_list) <= 0)
179                 goto out;
180
181         /* select the device that matches the dev_t of name */
182         list_for_each_entry(device, &name_list, node) {
183                 char filename[PATH_SIZE];
184                 struct stat statbuf;
185
186                 udev_device_init(udev);
187                 if (udev_db_get_device(udev, device->name) != 0)
188                         continue;
189                 info("found db entry '%s'", device->name);
190
191                 strlcpy(filename, udev_root, sizeof(filename));
192                 strlcat(filename, "/", sizeof(filename));
193                 strlcat(filename, name, sizeof(filename));
194                 if (stat(filename, &statbuf) != 0)
195                         continue;
196                 if (statbuf.st_rdev == udev->devt) {
197                         info("found '%s', dev_t matches", udev->name);
198                         rc = 0;
199                         break;
200                 }
201         }
202 out:
203         name_list_cleanup(&name_list);
204         return rc;
205 }
206
207 int main(int argc, char *argv[], char *envp[])
208 {
209         int option;
210         struct udevice *udev;
211         int root = 0;
212
213         static const struct option options[] = {
214                 { "name", 1, NULL, 'n' },
215                 { "path", 1, NULL, 'p' },
216                 { "query", 1, NULL, 'q' },
217                 { "attribute-walk", 0, NULL, 'a' },
218                 { "export-db", 0, NULL, 'e' },
219                 { "root", 0, NULL, 'r' },
220                 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
221                 { "help", 0, NULL, 'h' },
222                 {}
223         };
224
225         enum action_type {
226                 ACTION_NONE,
227                 ACTION_QUERY,
228                 ACTION_ATTRIBUTE_WALK,
229                 ACTION_ROOT,
230         } action = ACTION_NONE;
231
232         enum query_type {
233                 QUERY_NONE,
234                 QUERY_NAME,
235                 QUERY_PATH,
236                 QUERY_SYMLINK,
237                 QUERY_ENV,
238                 QUERY_ALL,
239         } query = QUERY_NONE;
240
241         char path[PATH_SIZE] = "";
242         char name[PATH_SIZE] = "";
243         struct name_entry *name_loop;
244         int rc = 0;
245
246         logging_init("udevinfo");
247         udev_config_init();
248         sysfs_init();
249
250         udev = udev_device_init(NULL);
251         if (udev == NULL) {
252                 rc = 1;
253                 goto exit;
254         }
255
256         /* get command line options */
257         while (1) {
258                 option = getopt_long(argc, argv, "aen:p:q:rVh", options, NULL);
259                 if (option == -1)
260                         break;
261
262                 dbg("option '%c'", option);
263                 switch (option) {
264                 case 'n':
265                         /* remove /dev if given */
266                         if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
267                                 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
268                         else
269                                 strlcpy(name, optarg, sizeof(name));
270                         dbg("name: %s\n", name);
271                         break;
272                 case 'p':
273                         /* remove /sys if given */
274                         if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
275                                 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
276                         else
277                                 strlcpy(path, optarg, sizeof(path));
278                         dbg("path: %s\n", path);
279                         break;
280                 case 'q':
281                         dbg("udev query: %s\n", optarg);
282                         action = ACTION_QUERY;
283                         if (strcmp(optarg, "name") == 0) {
284                                 query = QUERY_NAME;
285                                 break;
286                         }
287                         if (strcmp(optarg, "symlink") == 0) {
288                                 query = QUERY_SYMLINK;
289                                 break;
290                         }
291                         if (strcmp(optarg, "path") == 0) {
292                                 query = QUERY_PATH;
293                                 break;
294                         }
295                         if (strcmp(optarg, "env") == 0) {
296                                 query = QUERY_ENV;
297                                 break;
298                         }
299                         if (strcmp(optarg, "all") == 0) {
300                                 query = QUERY_ALL;
301                                 break;
302                         }
303                         fprintf(stderr, "unknown query type\n");
304                         rc = 2;
305                         goto exit;
306                 case 'r':
307                         if (action == ACTION_NONE)
308                                 action = ACTION_ROOT;
309                         root = 1;
310                         break;
311                 case 'a':
312                         action = ACTION_ATTRIBUTE_WALK;
313                         break;
314                 case 'e':
315                         export_db();
316                         goto exit;
317                 case 1:
318                         printf("%s\n", UDEV_VERSION);
319                         goto exit;
320                 case 'V':
321                         printf("udevinfo, version %s\n", UDEV_VERSION);
322                         goto exit;
323                 case 'h':
324                         printf("Usage: udevinfo OPTIONS\n"
325                                "  --query=<type>    query database for the specified value:\n"
326                                "    name            name of device node\n"
327                                "    symlink         pointing to node\n"
328                                "    path            sysfs device path\n"
329                                "    env             the device related imported environment\n"
330                                "    all             all values\n"
331                                "\n"
332                                "  --path=<devpath>  sysfs device path used for query or chain\n"
333                                "  --name=<name>     node or symlink name used for query\n"
334                                "\n"
335                                "  --root            prepend to query result or print udev_root\n"
336                                "  --attribute-walk  print all SYSFS_attributes along the device chain\n"
337                                "  --export-db       export the content of the udev database\n"
338                                "  --help            print this text\n"
339                                "\n");
340                         goto exit;
341                 default:
342                         goto exit;
343                 }
344         }
345
346         /* run action */
347         switch (action) {
348         case ACTION_QUERY:
349                 /* needs devpath or node/symlink name for query */
350                 if (path[0] != '\0') {
351                         if (udev_db_get_device(udev, path) != 0) {
352                                 fprintf(stderr, "no record for '%s' in database\n", path);
353                                 rc = 3;
354                                 goto exit;
355                         }
356                 } else if (name[0] != '\0') {
357                         if (lookup_device_by_name(udev, name) != 0) {
358                                 fprintf(stderr, "node name not found\n");
359                                 rc = 4;
360                                 goto exit;
361                         }
362                 } else {
363                         fprintf(stderr, "query needs --path or node --name specified\n");
364                         rc = 4;
365                         goto exit;
366                 }
367
368                 switch(query) {
369                 case QUERY_NAME:
370                         if (root)
371                                 printf("%s/%s\n", udev_root, udev->name);
372                         else
373                                 printf("%s\n", udev->name);
374                         break;
375                 case QUERY_SYMLINK:
376                         if (list_empty(&udev->symlink_list))
377                                 goto exit;
378                         if (root)
379                                 list_for_each_entry(name_loop, &udev->symlink_list, node)
380                                         printf("%s/%s ", udev_root, name_loop->name);
381                         else
382                                 list_for_each_entry(name_loop, &udev->symlink_list, node)
383                                         printf("%s ", name_loop->name);
384                         printf("\n");
385                         break;
386                 case QUERY_PATH:
387                         printf("%s\n", udev->dev->devpath);
388                         goto exit;
389                 case QUERY_ENV:
390                         list_for_each_entry(name_loop, &udev->env_list, node)
391                                 printf("%s\n", name_loop->name);
392                         break;
393                 case QUERY_ALL:
394                         print_record(udev);
395                         break;
396                 default:
397                         fprintf(stderr, "unknown query type\n");
398                         break;
399                 }
400                 break;
401         case ACTION_ATTRIBUTE_WALK:
402                 if (path[0] != '\0') {
403                         if (print_device_chain(path) != 0) {
404                                 fprintf(stderr, "device not found\n");
405                                 rc = 4;
406                                 goto exit;
407                         }
408                 } else if (name[0] != '\0') {
409                         if (lookup_device_by_name(udev, name) != 0) {
410                                 fprintf(stderr, "node name not found\n");
411                                 rc = 4;
412                                 goto exit;
413                         }
414                         if (print_device_chain(udev->dev->devpath) != 0) {
415                                 fprintf(stderr, "device not found\n");
416                                 rc = 4;
417                                 goto exit;
418                         }
419                 } else {
420                         fprintf(stderr, "attribute walk needs --path or node --name specified\n");
421                         rc = 5;
422                         goto exit;
423                 }
424                 break;
425         case ACTION_ROOT:
426                 printf("%s\n", udev_root);
427                 break;
428         default:
429                 fprintf(stderr, "missing option\n");
430                 rc = 1;
431                 break;
432         }
433
434 exit:
435         udev_device_cleanup(udev);
436         sysfs_cleanup();
437         logging_close();
438         return rc;
439 }