chiark / gitweb /
update slackware rules
[elogind.git] / udevinfo.c
1 /*
2  * udevinfo.c - fetches stored device information or sysfs attributes
3  *
4  * Copyright (C) 2004-2005 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  *      This program is free software; you can redistribute it and/or modify it
7  *      under the terms of the GNU General Public License as published by the
8  *      Free Software Foundation version 2 of the License.
9  * 
10  *      This program is distributed in the hope that it will be useful, but
11  *      WITHOUT ANY WARRANTY; without even the implied warranty of
12  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *      General Public License for more details.
14  * 
15  *      You should have received a copy of the GNU General Public License along
16  *      with this program; if not, write to the Free Software Foundation, Inc.,
17  *      675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  */
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <stddef.h>
25 #include <ctype.h>
26 #include <stdarg.h>
27 #include <unistd.h>
28 #include <dirent.h>
29 #include <errno.h>
30
31 #include "udev.h"
32
33
34 #ifdef USE_LOG
35 void log_message (int priority, const char *format, ...)
36 {
37         va_list args;
38
39         if (priority > udev_log_priority)
40                 return;
41
42         va_start(args, format);
43         vsyslog(priority, format, args);
44         va_end(args);
45 }
46 #endif
47
48 static void print_all_attributes(const char *devpath)
49 {
50         char path[PATH_SIZE];
51         DIR *dir;
52         struct dirent *dent;
53
54         strlcpy(path, sysfs_path, sizeof(path));
55         strlcat(path, devpath, sizeof(path));
56
57         dir = opendir(path);
58         if (dir != NULL) {
59                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
60                         char *attr_value;
61                         char value[NAME_SIZE];
62                         size_t len;
63
64                         attr_value = sysfs_attr_get_value(devpath, dent->d_name);
65                         if (attr_value == NULL)
66                                 continue;
67                         len = strlcpy(value, attr_value, sizeof(value));
68                         dbg("attr '%s'='%s'(%zi)", dent->d_name, value, len);
69
70                         /* remove trailing newlines */
71                         while (len && value[len-1] == '\n')
72                                 value[--len] = '\0';
73
74                         /* skip nonprintable attributes */
75                         while (len && isprint(value[len-1]))
76                                 len--;
77                         if (len) {
78                                 dbg("attribute value of '%s' non-printable, skip", dent->d_name);
79                                 continue;
80                         }
81
82                         replace_untrusted_chars(value);
83                         printf("    SYSFS{%s}==\"%s\"\n", dent->d_name, value);
84                 }
85         }
86         printf("\n");
87 }
88
89 static int print_device_chain(const char *devpath)
90 {
91         struct sysfs_device *dev;
92
93         printf("\n"
94                "Udevinfo starts with the device specified by the devpath and then\n"
95                "walks up the chain of parent devices. It prints for every device\n"
96                "found, all possible attributes in the udev rules key format.\n"
97                "A rule to match, can be composed by the attributes of the device\n"
98                "and the attributes from one single parent device.\n"
99                "\n");
100
101         dev = sysfs_device_get(devpath);
102         if (dev == NULL)
103                 return -1;
104
105         printf("  looking at device '%s':\n", dev->devpath);
106         printf("    KERNEL==\"%s\"\n", dev->kernel_name);
107         printf("    SUBSYSTEM==\"%s\"\n", dev->subsystem);
108         printf("    DRIVER==\"%s\"\n", dev->driver);
109         print_all_attributes(dev->devpath);
110
111         /* walk up the chain of devices */
112         while (1) {
113                 dev = sysfs_device_get_parent(dev);
114                 if (dev == NULL)
115                         break;
116                 printf("  looking at parent device '%s':\n", dev->devpath);
117                 printf("    ID==\"%s\"\n", dev->kernel_name);
118                 printf("    BUS==\"%s\"\n", dev->subsystem);
119                 printf("    DRIVER==\"%s\"\n", dev->driver);
120
121                 print_all_attributes(dev->devpath);
122         }
123
124         return 0;
125 }
126
127 static void print_record(struct udevice *udev)
128 {
129         struct name_entry *name_loop;
130
131         printf("P: %s\n", udev->dev->devpath);
132         printf("N: %s\n", udev->name);
133         list_for_each_entry(name_loop, &udev->symlink_list, node)
134                 printf("S: %s\n", name_loop->name);
135         list_for_each_entry(name_loop, &udev->env_list, node)
136                 printf("E: %s\n", name_loop->name);
137 }
138
139 static void export_name_devpath(struct udevice *udev) {
140         printf("%s=%s/%s\n", udev->dev->devpath, udev_root, udev->name);
141 }
142
143 static void export_record(struct udevice *udev) {
144         print_record(udev);
145         printf("\n");
146 }
147
148 static void export_db(void fnct(struct udevice *udev)) {
149         LIST_HEAD(name_list);
150         struct name_entry *name_loop;
151
152         udev_db_get_all_entries(&name_list);
153         list_for_each_entry(name_loop, &name_list, node) {
154                 struct udevice *udev_db;
155
156                 udev_db = udev_device_init();
157                 if (udev_db == NULL)
158                         continue;
159                 if (udev_db_get_device(udev_db, name_loop->name) == 0)
160                         fnct(udev_db);
161                 udev_device_cleanup(udev_db);
162         }
163         name_list_cleanup(&name_list);
164 }
165
166 static void print_help(void)
167 {
168         fprintf(stderr, "Usage: udevinfo [-anpqrVh]\n"
169                "  -q TYPE  query database for the specified value:\n"
170                "             'name'    name of device node\n"
171                "             'symlink' pointing to node\n"
172                "             'path'    sysfs device path\n"
173                "             'env'     the device related imported environment\n"
174                "             'all'     all values\n"
175                "\n"
176                "  -p PATH  sysfs device path used for query or chain\n"
177                "  -n NAME  node/symlink name used for query\n"
178                "\n"
179                "  -r       prepend to query result or print udev_root\n"
180                "  -a       print all SYSFS_attributes along the device chain\n"
181                "  -e       export the content of the udev database\n"
182                "  -V       print udev version\n"
183                "  -h       print this help text\n"
184                "\n");
185 }
186
187 int main(int argc, char *argv[], char *envp[])
188 {
189         static const char short_options[] = "aden:p:q:rVh";
190         int option;
191         struct udevice *udev;
192         int root = 0;
193
194         enum action_type {
195                 ACTION_NONE,
196                 ACTION_QUERY,
197                 ACTION_ATTRIBUTE_WALK,
198                 ACTION_ROOT,
199         } action = ACTION_NONE;
200
201         enum query_type {
202                 QUERY_NONE,
203                 QUERY_NAME,
204                 QUERY_PATH,
205                 QUERY_SYMLINK,
206                 QUERY_ENV,
207                 QUERY_ALL,
208         } query = QUERY_NONE;
209
210         char path[PATH_SIZE] = "";
211         char name[PATH_SIZE] = "";
212         char temp[PATH_SIZE];
213         struct name_entry *name_loop;
214         char *pos;
215         int rc = 0;
216
217         logging_init("udevinfo");
218
219         udev_config_init();
220         sysfs_init();
221
222         udev = udev_device_init();
223         if (udev == NULL) {
224                 rc = 1;
225                 goto exit;
226         }
227
228         /* get command line options */
229         while (1) {
230                 option = getopt(argc, argv, short_options);
231                 if (option == -1)
232                         break;
233
234                 dbg("option '%c'", option);
235                 switch (option) {
236                 case 'n':
237                         dbg("udev name: %s\n", optarg);
238                         strlcpy(name, optarg, sizeof(name));
239                         break;
240                 case 'p':
241                         dbg("udev path: %s\n", optarg);
242                         /* remove sysfs mountpoint if not given */
243                         if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
244                                 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
245                         else
246                                 strlcpy(path, optarg, sizeof(path));
247                         break;
248                 case 'q':
249                         dbg("udev query: %s\n", optarg);
250                         action = ACTION_QUERY;
251                         if (strcmp(optarg, "name") == 0) {
252                                 query = QUERY_NAME;
253                                 break;
254                         }
255                         if (strcmp(optarg, "symlink") == 0) {
256                                 query = QUERY_SYMLINK;
257                                 break;
258                         }
259                         if (strcmp(optarg, "path") == 0) {
260                                 query = QUERY_PATH;
261                                 break;
262                         }
263                         if (strcmp(optarg, "env") == 0) {
264                                 query = QUERY_ENV;
265                                 break;
266                         }
267                         if (strcmp(optarg, "all") == 0) {
268                                 query = QUERY_ALL;
269                                 break;
270                         }
271                         fprintf(stderr, "unknown query type\n");
272                         rc = 2;
273                         goto exit;
274                 case 'r':
275                         if (action == ACTION_NONE)
276                                 action = ACTION_ROOT;
277                         root = 1;
278                         break;
279                 case 'a':
280                         action = ACTION_ATTRIBUTE_WALK;
281                         break;
282                 case 'd':
283                         export_db(export_name_devpath);
284                         goto exit;
285                 case 'e':
286                         export_db(export_record);
287                         goto exit;
288                 case 'V':
289                         printf("udevinfo, version %s\n", UDEV_VERSION);
290                         goto exit;
291                 case 'h':
292                 case '?':
293                 default:
294                         print_help();
295                         goto exit;
296                 }
297         }
298
299         /* run action */
300         switch (action) {
301         case ACTION_QUERY:
302                 /* need devpath or node/symlink name for query */
303                 if (path[0] != '\0') {
304                         /* remove sysfs_path if given */
305                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) == 0) {
306                                 pos = path + strlen(sysfs_path);
307                         } else {
308                                 if (path[0] != '/') {
309                                         /* prepend '/' if missing */
310                                         strcpy(temp, "/");
311                                         strlcpy(temp, path, sizeof(temp));
312                                         pos = temp;
313                                 } else {
314                                         pos = path;
315                                 }
316                         }
317                         if (udev_db_get_device(udev, pos) != 0) {
318                                 fprintf(stderr, "no record for '%s' in database\n", pos);
319                                 rc = 3;
320                                 goto exit;
321                         }
322                 } else if (name[0] != '\0') {
323                         char devpath[PATH_SIZE];
324                         int len;
325
326                         /* remove udev_root if given */
327                         len = strlen(udev_root);
328                         if (strncmp(name, udev_root, len) == 0) {
329                                 pos = &name[len+1];
330                         } else
331                                 pos = name;
332
333                         if (udev_db_lookup_name(pos, devpath, sizeof(devpath)) != 0) {
334                                 fprintf(stderr, "no record for '%s' in database\n", pos);
335                                 rc = 3;
336                                 goto exit;
337                         }
338                         udev_db_get_device(udev, devpath);
339                 } else {
340                         fprintf(stderr, "query needs device path(-p) or node name(-n) specified\n");
341                         rc = 4;
342                         goto exit;
343                 }
344
345                 switch(query) {
346                 case QUERY_NAME:
347                         if (root)
348                                 printf("%s/%s\n", udev_root, udev->name);
349                         else
350                                 printf("%s\n", udev->name);
351                         break;
352                 case QUERY_SYMLINK:
353                         if (list_empty(&udev->symlink_list))
354                                 goto exit;
355                         if (root)
356                                 list_for_each_entry(name_loop, &udev->symlink_list, node)
357                                         printf("%s/%s ", udev_root, name_loop->name);
358                         else
359                                 list_for_each_entry(name_loop, &udev->symlink_list, node)
360                                         printf("%s ", name_loop->name);
361                         printf("\n");
362                         break;
363                 case QUERY_PATH:
364                         printf("%s\n", udev->dev->devpath);
365                         goto exit;
366                 case QUERY_ENV:
367                         list_for_each_entry(name_loop, &udev->env_list, node)
368                                 printf("%s\n", name_loop->name);
369                         break;
370                 case QUERY_ALL:
371                         print_record(udev);
372                         break;
373                 default:
374                         print_help();
375                         break;
376                 }
377                 break;
378         case ACTION_ATTRIBUTE_WALK:
379                 if (path[0] == '\0') {
380                         fprintf(stderr, "attribute walk on device chain needs path(-p) specified\n");
381                         rc = 4;
382                         goto exit;
383                 } else
384                         print_device_chain(path);
385                 break;
386         case ACTION_ROOT:
387                 printf("%s\n", udev_root);
388                 break;
389         default:
390                 print_help();
391                 rc = 1;
392                 break;
393         }
394
395 exit:
396         udev_device_cleanup(udev);
397         sysfs_cleanup();
398         logging_close();
399         return rc;
400 }