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