chiark / gitweb /
only install *.rules
[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                         if(len >= sizeof(value))
83                                 len = sizeof(value) - 1;
84                         dbg("attr '%s'='%s'(%zi)", dent->d_name, value, len);
85
86                         /* remove trailing newlines */
87                         while (len && value[len-1] == '\n')
88                                 value[--len] = '\0';
89
90                         /* skip nonprintable attributes */
91                         while (len && isprint(value[len-1]))
92                                 len--;
93                         if (len) {
94                                 dbg("attribute value of '%s' non-printable, skip", dent->d_name);
95                                 continue;
96                         }
97
98                         replace_chars(value, ALLOWED_CHARS_INPUT);
99                         printf("    %s{%s}==\"%s\"\n", key, dent->d_name, value);
100                 }
101         }
102         printf("\n");
103 }
104
105 static int print_device_chain(const char *devpath)
106 {
107         struct sysfs_device *dev;
108
109         dev = sysfs_device_get(devpath);
110         if (dev == NULL)
111                 return -1;
112
113         printf("\n"
114                "Udevinfo starts with the device specified by the devpath and then\n"
115                "walks up the chain of parent devices. It prints for every device\n"
116                "found, all possible attributes in the udev rules key format.\n"
117                "A rule to match, can be composed by the attributes of the device\n"
118                "and the attributes from one single parent device.\n"
119                "\n");
120
121         printf("  looking at device '%s':\n", dev->devpath);
122         printf("    KERNEL==\"%s\"\n", dev->kernel);
123         printf("    SUBSYSTEM==\"%s\"\n", dev->subsystem);
124         printf("    DRIVER==\"%s\"\n", dev->driver);
125         print_all_attributes(dev->devpath, "ATTR");
126
127         /* walk up the chain of devices */
128         while (1) {
129                 dev = sysfs_device_get_parent(dev);
130                 if (dev == NULL)
131                         break;
132                 printf("  looking at parent device '%s':\n", dev->devpath);
133                 printf("    KERNELS==\"%s\"\n", dev->kernel);
134                 printf("    SUBSYSTEMS==\"%s\"\n", dev->subsystem);
135                 printf("    DRIVERS==\"%s\"\n", dev->driver);
136
137                 print_all_attributes(dev->devpath, "ATTRS");
138         }
139
140         return 0;
141 }
142
143 static void print_record(struct udevice *udev)
144 {
145         struct name_entry *name_loop;
146
147         printf("P: %s\n", udev->dev->devpath);
148         printf("N: %s\n", udev->name);
149         list_for_each_entry(name_loop, &udev->symlink_list, node)
150                 printf("S: %s\n", name_loop->name);
151         if (udev->link_priority != 0)
152                 printf("L: %i\n", udev->link_priority);
153         if (udev->partitions != 0)
154                 printf("A:%u\n", udev->partitions);
155         if (udev->ignore_remove)
156                 printf("R:%u\n", udev->ignore_remove);
157         list_for_each_entry(name_loop, &udev->env_list, node)
158                 printf("E: %s\n", name_loop->name);
159 }
160
161 static void export_db(void) {
162         LIST_HEAD(name_list);
163         struct name_entry *name_loop;
164
165         udev_db_get_all_entries(&name_list);
166         list_for_each_entry(name_loop, &name_list, node) {
167                 struct udevice *udev_db;
168
169                 udev_db = udev_device_init(NULL);
170                 if (udev_db == NULL)
171                         continue;
172                 if (udev_db_get_device(udev_db, name_loop->name) == 0)
173                         print_record(udev_db);
174                         printf("\n");
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         int count;
184         struct name_entry *device;
185         int rc  = -1;
186
187         count = udev_db_get_devices_by_name(name, &name_list);
188         if (count <= 0)
189                 goto out;
190
191         info("found %i devices for '%s'", count, name);
192
193         /* select the device that seems to match */
194         list_for_each_entry(device, &name_list, node) {
195                 char filename[PATH_SIZE];
196                 struct stat statbuf;
197
198                 udev_device_init(udev);
199                 if (udev_db_get_device(udev, device->name) != 0)
200                         continue;
201                 info("found db entry '%s'", device->name);
202
203                 /* make sure, we don't get a link of a differnt device */
204                 strlcpy(filename, udev_root, sizeof(filename));
205                 strlcat(filename, "/", sizeof(filename));
206                 strlcat(filename, name, sizeof(filename));
207                 if (stat(filename, &statbuf) != 0)
208                         continue;
209                 if (major(udev->devt) > 0 && udev->devt != statbuf.st_rdev) {
210                         info("skip '%s', dev_t doesn't match", udev->name);
211                         continue;
212                 }
213                 rc = 0;
214                 break;
215         }
216 out:
217         name_list_cleanup(&name_list);
218         return rc;
219 }
220
221 int main(int argc, char *argv[], char *envp[])
222 {
223         int option;
224         struct udevice *udev;
225         int root = 0;
226
227         static const struct option options[] = {
228                 { "name", 1, NULL, 'n' },
229                 { "path", 1, NULL, 'p' },
230                 { "query", 1, NULL, 'q' },
231                 { "attribute-walk", 0, NULL, 'a' },
232                 { "export-db", 0, NULL, 'e' },
233                 { "root", 0, NULL, 'r' },
234                 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
235                 { "help", 0, NULL, 'h' },
236                 {}
237         };
238
239         enum action_type {
240                 ACTION_NONE,
241                 ACTION_QUERY,
242                 ACTION_ATTRIBUTE_WALK,
243                 ACTION_ROOT,
244         } action = ACTION_NONE;
245
246         enum query_type {
247                 QUERY_NONE,
248                 QUERY_NAME,
249                 QUERY_PATH,
250                 QUERY_SYMLINK,
251                 QUERY_ENV,
252                 QUERY_ALL,
253         } query = QUERY_NONE;
254
255         char path[PATH_SIZE] = "";
256         char name[PATH_SIZE] = "";
257         struct name_entry *name_loop;
258         int rc = 0;
259
260         logging_init("udevinfo");
261         udev_config_init();
262         sysfs_init();
263
264         udev = udev_device_init(NULL);
265         if (udev == NULL) {
266                 rc = 1;
267                 goto exit;
268         }
269
270         while (1) {
271                 option = getopt_long(argc, argv, "aen:p:q:rVh", options, NULL);
272                 if (option == -1)
273                         break;
274
275                 dbg("option '%c'", option);
276                 switch (option) {
277                 case 'n':
278                         /* remove /dev if given */
279                         if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
280                                 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
281                         else
282                                 strlcpy(name, optarg, sizeof(name));
283                         dbg("name: %s", name);
284                         break;
285                 case 'p':
286                         /* remove /sys if given */
287                         if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
288                                 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
289                         else
290                                 strlcpy(path, optarg, sizeof(path));
291                         dbg("path: %s", path);
292                         break;
293                 case 'q':
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, "no valid sysfs device 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, "no valid sysfs device 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 }