chiark / gitweb /
edd_id: move persistent rules to its own file
[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_chars(value, ALLOWED_CHARS_INPUT);
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         while (1) {
269                 option = getopt_long(argc, argv, "aen:p:q:rVh", options, NULL);
270                 if (option == -1)
271                         break;
272
273                 dbg("option '%c'", option);
274                 switch (option) {
275                 case 'n':
276                         /* remove /dev if given */
277                         if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
278                                 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
279                         else
280                                 strlcpy(name, optarg, sizeof(name));
281                         dbg("name: %s", name);
282                         break;
283                 case 'p':
284                         /* remove /sys if given */
285                         if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
286                                 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
287                         else
288                                 strlcpy(path, optarg, sizeof(path));
289                         dbg("path: %s", path);
290                         break;
291                 case 'q':
292                         action = ACTION_QUERY;
293                         if (strcmp(optarg, "name") == 0) {
294                                 query = QUERY_NAME;
295                                 break;
296                         }
297                         if (strcmp(optarg, "symlink") == 0) {
298                                 query = QUERY_SYMLINK;
299                                 break;
300                         }
301                         if (strcmp(optarg, "path") == 0) {
302                                 query = QUERY_PATH;
303                                 break;
304                         }
305                         if (strcmp(optarg, "env") == 0) {
306                                 query = QUERY_ENV;
307                                 break;
308                         }
309                         if (strcmp(optarg, "all") == 0) {
310                                 query = QUERY_ALL;
311                                 break;
312                         }
313                         fprintf(stderr, "unknown query type\n");
314                         rc = 2;
315                         goto exit;
316                 case 'r':
317                         if (action == ACTION_NONE)
318                                 action = ACTION_ROOT;
319                         root = 1;
320                         break;
321                 case 'a':
322                         action = ACTION_ATTRIBUTE_WALK;
323                         break;
324                 case 'e':
325                         export_db();
326                         goto exit;
327                 case 1:
328                         printf("%s\n", UDEV_VERSION);
329                         goto exit;
330                 case 'V':
331                         printf("udevinfo, version %s\n", UDEV_VERSION);
332                         goto exit;
333                 case 'h':
334                         printf("Usage: udevinfo OPTIONS\n"
335                                "  --query=<type>    query database for the specified value:\n"
336                                "    name            name of device node\n"
337                                "    symlink         pointing to node\n"
338                                "    path            sysfs device path\n"
339                                "    env             the device related imported environment\n"
340                                "    all             all values\n"
341                                "\n"
342                                "  --path=<devpath>  sysfs device path used for query or chain\n"
343                                "  --name=<name>     node or symlink name used for query\n"
344                                "\n"
345                                "  --root            prepend to query result or print udev_root\n"
346                                "  --attribute-walk  print all SYSFS_attributes along the device chain\n"
347                                "  --export-db       export the content of the udev database\n"
348                                "  --help            print this text\n"
349                                "\n");
350                         goto exit;
351                 default:
352                         goto exit;
353                 }
354         }
355
356         /* run action */
357         switch (action) {
358         case ACTION_QUERY:
359                 /* needs devpath or node/symlink name for query */
360                 if (path[0] != '\0') {
361                         if (udev_db_get_device(udev, path) != 0) {
362                                 fprintf(stderr, "no record for '%s' in database\n", path);
363                                 rc = 3;
364                                 goto exit;
365                         }
366                 } else if (name[0] != '\0') {
367                         if (lookup_device_by_name(udev, name) != 0) {
368                                 fprintf(stderr, "node name not found\n");
369                                 rc = 4;
370                                 goto exit;
371                         }
372                 } else {
373                         fprintf(stderr, "query needs --path or node --name specified\n");
374                         rc = 4;
375                         goto exit;
376                 }
377
378                 switch(query) {
379                 case QUERY_NAME:
380                         if (root)
381                                 printf("%s/%s\n", udev_root, udev->name);
382                         else
383                                 printf("%s\n", udev->name);
384                         break;
385                 case QUERY_SYMLINK:
386                         if (list_empty(&udev->symlink_list))
387                                 goto exit;
388                         if (root)
389                                 list_for_each_entry(name_loop, &udev->symlink_list, node)
390                                         printf("%s/%s ", udev_root, name_loop->name);
391                         else
392                                 list_for_each_entry(name_loop, &udev->symlink_list, node)
393                                         printf("%s ", name_loop->name);
394                         printf("\n");
395                         break;
396                 case QUERY_PATH:
397                         printf("%s\n", udev->dev->devpath);
398                         goto exit;
399                 case QUERY_ENV:
400                         list_for_each_entry(name_loop, &udev->env_list, node)
401                                 printf("%s\n", name_loop->name);
402                         break;
403                 case QUERY_ALL:
404                         print_record(udev);
405                         break;
406                 default:
407                         fprintf(stderr, "unknown query type\n");
408                         break;
409                 }
410                 break;
411         case ACTION_ATTRIBUTE_WALK:
412                 if (path[0] != '\0') {
413                         if (print_device_chain(path) != 0) {
414                                 fprintf(stderr, "no valid sysfs device found\n");
415                                 rc = 4;
416                                 goto exit;
417                         }
418                 } else if (name[0] != '\0') {
419                         if (lookup_device_by_name(udev, name) != 0) {
420                                 fprintf(stderr, "node name not found\n");
421                                 rc = 4;
422                                 goto exit;
423                         }
424                         if (print_device_chain(udev->dev->devpath) != 0) {
425                                 fprintf(stderr, "no valid sysfs device found\n");
426                                 rc = 4;
427                                 goto exit;
428                         }
429                 } else {
430                         fprintf(stderr, "attribute walk needs --path or node --name specified\n");
431                         rc = 5;
432                         goto exit;
433                 }
434                 break;
435         case ACTION_ROOT:
436                 printf("%s\n", udev_root);
437                 break;
438         default:
439                 fprintf(stderr, "missing option\n");
440                 rc = 1;
441                 break;
442         }
443
444 exit:
445         udev_device_cleanup(udev);
446         sysfs_cleanup();
447         logging_close();
448         return rc;
449 }