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