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