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