chiark / gitweb /
remove redundant string copy in udev_rules_apply_format()
[elogind.git] / udev / udevadm-info.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 "config.h"
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 #include <getopt.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33
34 #include "udev.h"
35
36 static void print_all_attributes(struct udev *udev, const char *devpath, const char *key)
37 {
38         char path[PATH_SIZE];
39         DIR *dir;
40         struct dirent *dent;
41
42         strlcpy(path, udev_get_sys_path(udev), sizeof(path));
43         strlcat(path, devpath, sizeof(path));
44
45         dir = opendir(path);
46         if (dir != NULL) {
47                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
48                         struct stat statbuf;
49                         char filename[PATH_SIZE];
50                         char *attr_value;
51                         char value[NAME_SIZE];
52                         size_t len;
53
54                         if (dent->d_name[0] == '.')
55                                 continue;
56
57                         if (strcmp(dent->d_name, "uevent") == 0)
58                                 continue;
59                         if (strcmp(dent->d_name, "dev") == 0)
60                                 continue;
61
62                         strlcpy(filename, path, sizeof(filename));
63                         strlcat(filename, "/", sizeof(filename));
64                         strlcat(filename, dent->d_name, sizeof(filename));
65                         if (lstat(filename, &statbuf) != 0)
66                                 continue;
67                         if (S_ISLNK(statbuf.st_mode))
68                                 continue;
69
70                         attr_value = sysfs_attr_get_value(udev, devpath, dent->d_name);
71                         if (attr_value == NULL)
72                                 continue;
73                         len = strlcpy(value, attr_value, sizeof(value));
74                         if(len >= sizeof(value))
75                                 len = sizeof(value) - 1;
76                         dbg(udev, "attr '%s'='%s'(%zi)\n", dent->d_name, value, len);
77
78                         /* remove trailing newlines */
79                         while (len && value[len-1] == '\n')
80                                 value[--len] = '\0';
81
82                         /* skip nonprintable attributes */
83                         while (len && isprint(value[len-1]))
84                                 len--;
85                         if (len) {
86                                 dbg(udev, "attribute value of '%s' non-printable, skip\n", dent->d_name);
87                                 continue;
88                         }
89
90                         printf("    %s{%s}==\"%s\"\n", key, dent->d_name, value);
91                 }
92         }
93         printf("\n");
94 }
95
96 static int print_device_chain(struct udev *udev, const char *devpath)
97 {
98         struct sysfs_device *dev;
99
100         dev = sysfs_device_get(udev, devpath);
101         if (dev == NULL)
102                 return -1;
103
104         printf("\n"
105                "Udevinfo starts with the device specified by the devpath and then\n"
106                "walks up the chain of parent devices. It prints for every device\n"
107                "found, all possible attributes in the udev rules key format.\n"
108                "A rule to match, can be composed by the attributes of the device\n"
109                "and the attributes from one single parent device.\n"
110                "\n");
111
112         printf("  looking at device '%s':\n", dev->devpath);
113         printf("    KERNEL==\"%s\"\n", dev->kernel);
114         printf("    SUBSYSTEM==\"%s\"\n", dev->subsystem);
115         printf("    DRIVER==\"%s\"\n", dev->driver);
116         print_all_attributes(udev, dev->devpath, "ATTR");
117
118         /* walk up the chain of devices */
119         while (1) {
120                 dev = sysfs_device_get_parent(udev, dev);
121                 if (dev == NULL)
122                         break;
123                 printf("  looking at parent device '%s':\n", dev->devpath);
124                 printf("    KERNELS==\"%s\"\n", dev->kernel);
125                 printf("    SUBSYSTEMS==\"%s\"\n", dev->subsystem);
126                 printf("    DRIVERS==\"%s\"\n", dev->driver);
127
128                 print_all_attributes(udev, dev->devpath, "ATTRS");
129         }
130
131         return 0;
132 }
133
134 static void print_record(struct udevice *udevice)
135 {
136         struct name_entry *name_loop;
137
138         printf("P: %s\n", udevice->dev->devpath);
139         printf("N: %s\n", udevice->name);
140         list_for_each_entry(name_loop, &udevice->symlink_list, node)
141                 printf("S: %s\n", name_loop->name);
142         if (udevice->link_priority != 0)
143                 printf("L: %i\n", udevice->link_priority);
144         if (udevice->partitions != 0)
145                 printf("A:%u\n", udevice->partitions);
146         if (udevice->ignore_remove)
147                 printf("R:%u\n", udevice->ignore_remove);
148         list_for_each_entry(name_loop, &udevice->env_list, node)
149                 printf("E: %s\n", name_loop->name);
150 }
151
152 static void export_db(struct udev *udev)
153 {
154         LIST_HEAD(name_list);
155         struct name_entry *name_loop;
156
157         udev_db_get_all_entries(udev, &name_list);
158         list_for_each_entry(name_loop, &name_list, node) {
159                 struct udevice *udevice_db;
160
161                 udevice_db = udev_device_init(udev);
162                 if (udevice_db == NULL)
163                         continue;
164                 if (udev_db_get_device(udevice_db, name_loop->name) == 0)
165                         print_record(udevice_db);
166                         printf("\n");
167                 udev_device_cleanup(udevice_db);
168         }
169         name_list_cleanup(udev, &name_list);
170 }
171
172 static int lookup_device_by_name(struct udev *udev, struct udevice **udevice, const char *name)
173 {
174         LIST_HEAD(name_list);
175         int count;
176         struct name_entry *device;
177         int rc  = -1;
178
179         count = udev_db_get_devices_by_name(udev, name, &name_list);
180         if (count <= 0)
181                 goto out;
182
183         info(udev, "found %i devices for '%s'\n", count, name);
184
185         /* select the device that seems to match */
186         list_for_each_entry(device, &name_list, node) {
187                 struct udevice *udevice_loop;
188                 char filename[PATH_SIZE];
189                 struct stat statbuf;
190
191                 udevice_loop = udev_device_init(udev);
192                 if (udevice_loop == NULL)
193                         break;
194                 if (udev_db_get_device(udevice_loop, device->name) != 0)
195                         goto next;
196                 info(udev, "found db entry '%s'\n", device->name);
197
198                 /* make sure, we don't get a link of a different device */
199                 strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
200                 strlcat(filename, "/", sizeof(filename));
201                 strlcat(filename, name, sizeof(filename));
202                 if (stat(filename, &statbuf) != 0)
203                         goto next;
204                 if (major(udevice_loop->devt) > 0 && udevice_loop->devt != statbuf.st_rdev) {
205                         info(udev, "skip '%s', dev_t doesn't match\n", udevice_loop->name);
206                         goto next;
207                 }
208                 rc = 0;
209                 *udevice = udevice_loop;
210                 break;
211 next:
212                 udev_device_cleanup(udevice_loop);
213         }
214 out:
215         name_list_cleanup(udev, &name_list);
216         return rc;
217 }
218
219 static int stat_device(const char *name, int export, const char *prefix)
220 {
221         struct stat statbuf;
222
223         if (stat(name, &statbuf) != 0)
224                 return -1;
225
226         if (export) {
227                 if (prefix == NULL)
228                         prefix = "INFO_";
229                 printf("%sMAJOR=%d\n"
230                        "%sMINOR=%d\n",
231                        prefix, major(statbuf.st_dev),
232                        prefix, minor(statbuf.st_dev));
233         } else
234                 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
235         return 0;
236 }
237
238 int udevadm_info(struct udev *udev, int argc, char *argv[])
239 {
240         struct udevice *udevice = NULL;
241         int root = 0;
242         int export = 0;
243         const char *export_prefix = NULL;
244
245         static const struct option options[] = {
246                 { "name", 1, NULL, 'n' },
247                 { "path", 1, NULL, 'p' },
248                 { "query", 1, NULL, 'q' },
249                 { "attribute-walk", 0, NULL, 'a' },
250                 { "export-db", 0, NULL, 'e' },
251                 { "root", 0, NULL, 'r' },
252                 { "device-id-of-file", 1, NULL, 'd' },
253                 { "export", 0, NULL, 'x' },
254                 { "export-prefix", 1, NULL, 'P' },
255                 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
256                 { "help", 0, NULL, 'h' },
257                 {}
258         };
259
260         enum action_type {
261                 ACTION_NONE,
262                 ACTION_QUERY,
263                 ACTION_ATTRIBUTE_WALK,
264                 ACTION_ROOT,
265                 ACTION_DEVICE_ID_FILE,
266         } action = ACTION_NONE;
267
268         enum query_type {
269                 QUERY_NONE,
270                 QUERY_NAME,
271                 QUERY_PATH,
272                 QUERY_SYMLINK,
273                 QUERY_ENV,
274                 QUERY_ALL,
275         } query = QUERY_NONE;
276
277         char path[PATH_SIZE] = "";
278         char name[PATH_SIZE] = "";
279         struct name_entry *name_loop;
280         int rc = 0;
281
282         while (1) {
283                 int option;
284
285                 option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL);
286                 if (option == -1)
287                         break;
288
289                 dbg(udev, "option '%c'\n", option);
290                 switch (option) {
291                 case 'n':
292                         /* remove /dev if given */
293                         if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) == 0)
294                                 strlcpy(name, &optarg[strlen(udev_get_dev_path(udev))+1], sizeof(name));
295                         else
296                                 strlcpy(name, optarg, sizeof(name));
297                         remove_trailing_chars(name, '/');
298                         dbg(udev, "name: %s\n", name);
299                         break;
300                 case 'p':
301                         /* remove /sys if given */
302                         if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) == 0)
303                                 strlcpy(path, &optarg[strlen(udev_get_sys_path(udev))], sizeof(path));
304                         else
305                                 strlcpy(path, optarg, sizeof(path));
306                         remove_trailing_chars(path, '/');
307
308                         /* possibly resolve to real devpath */
309                         if (sysfs_resolve_link(udev, path, sizeof(path)) != 0) {
310                                 char temp[PATH_SIZE];
311                                 char *pos;
312
313                                 /* also check if the parent is a link */
314                                 strlcpy(temp, path, sizeof(temp));
315                                 pos = strrchr(temp, '/');
316                                 if (pos != 0) {
317                                         char tail[PATH_SIZE];
318
319                                         strlcpy(tail, pos, sizeof(tail));
320                                         pos[0] = '\0';
321                                         if (sysfs_resolve_link(udev, temp, sizeof(temp)) == 0) {
322                                                 strlcpy(path, temp, sizeof(path));
323                                                 strlcat(path, tail, sizeof(path));
324                                         }
325                                 }
326                         }
327                         dbg(udev, "path: %s\n", path);
328                         break;
329                 case 'q':
330                         action = ACTION_QUERY;
331                         if (strcmp(optarg, "name") == 0) {
332                                 query = QUERY_NAME;
333                                 break;
334                         }
335                         if (strcmp(optarg, "symlink") == 0) {
336                                 query = QUERY_SYMLINK;
337                                 break;
338                         }
339                         if (strcmp(optarg, "path") == 0) {
340                                 query = QUERY_PATH;
341                                 break;
342                         }
343                         if (strcmp(optarg, "env") == 0) {
344                                 query = QUERY_ENV;
345                                 break;
346                         }
347                         if (strcmp(optarg, "all") == 0) {
348                                 query = QUERY_ALL;
349                                 break;
350                         }
351                         fprintf(stderr, "unknown query type\n");
352                         rc = 2;
353                         goto exit;
354                 case 'r':
355                         if (action == ACTION_NONE)
356                                 action = ACTION_ROOT;
357                         root = 1;
358                         break;
359                 case 'd':
360                         action = ACTION_DEVICE_ID_FILE;
361                         strlcpy(name, optarg, sizeof(name));
362                         break;
363                 case 'a':
364                         action = ACTION_ATTRIBUTE_WALK;
365                         break;
366                 case 'e':
367                         export_db(udev);
368                         goto exit;
369                 case 'x':
370                         export = 1;
371                         break;
372                 case 'P':
373                         export_prefix = optarg;
374                         break;
375                 case 1:
376                         printf("%s\n", VERSION);
377                         goto exit;
378                 case 'V':
379                         printf("udevinfo, version %s\n", VERSION);
380                         goto exit;
381                 case 'h':
382                         printf("Usage: udevadm info OPTIONS\n"
383                                "  --query=<type>             query database for the specified value:\n"
384                                "      name                     name of device node\n"
385                                "      symlink                  pointing to node\n"
386                                "      path                     sysfs device path\n"
387                                "      env                      the device related imported environment\n"
388                                "      all                      all values\n"
389                                "  --path=<devpath>           sysfs device path used for query or chain\n"
390                                "  --name=<name>              node or symlink name used for query\n"
391                                "  --root                     prepend to query result or print udev_root\n"
392                                "  --attribute-walk           print all key matches while walking along chain\n"
393                                "                             of parent devices\n"
394                                "  --device-id-of-file=<file> print major/minor of underlying device\n"
395                                "  --export-db                export the content of the udev database\n"
396                                "  --help                     print this text\n"
397                                "\n");
398                         goto exit;
399                 default:
400                         goto exit;
401                 }
402         }
403
404         /* run action */
405         switch (action) {
406         case ACTION_QUERY:
407                 /* needs devpath or node/symlink name for query */
408                 if (path[0] != '\0') {
409                         udevice = udev_device_init(udev);
410                         if (udevice == NULL) {
411                                 rc = 1;
412                                 goto exit;
413                         }
414                         if (udev_db_get_device(udevice, path) != 0) {
415                                 fprintf(stderr, "no record for '%s' in database\n", path);
416                                 rc = 3;
417                                 goto exit;
418                         }
419                 } else if (name[0] != '\0') {
420                         if (lookup_device_by_name(udev, &udevice, name) != 0) {
421                                 fprintf(stderr, "node name not found\n");
422                                 rc = 4;
423                                 goto exit;
424                         }
425                 } else {
426                         fprintf(stderr, "query needs --path or node --name specified\n");
427                         rc = 4;
428                         goto exit;
429                 }
430
431                 switch(query) {
432                 case QUERY_NAME:
433                         if (root)
434                                 printf("%s/%s\n", udev_get_dev_path(udev), udevice->name);
435                         else
436                                 printf("%s\n", udevice->name);
437                         break;
438                 case QUERY_SYMLINK:
439                         list_for_each_entry(name_loop, &udevice->symlink_list, node) {
440                                 char c = name_loop->node.next != &udevice->symlink_list ? ' ' : '\n';
441
442                                 if (root)
443                                         printf("%s/%s%c", udev_get_dev_path(udev), name_loop->name, c);
444                                 else
445                                         printf("%s%c", name_loop->name, c);
446                         }
447                         break;
448                 case QUERY_PATH:
449                         printf("%s\n", udevice->dev->devpath);
450                         goto exit;
451                 case QUERY_ENV:
452                         list_for_each_entry(name_loop, &udevice->env_list, node)
453                                 printf("%s\n", name_loop->name);
454                         break;
455                 case QUERY_ALL:
456                         print_record(udevice);
457                         break;
458                 default:
459                         fprintf(stderr, "unknown query type\n");
460                         break;
461                 }
462                 break;
463         case ACTION_ATTRIBUTE_WALK:
464                 if (path[0] != '\0') {
465                         if (print_device_chain(udev, path) != 0) {
466                                 fprintf(stderr, "no valid sysfs device found\n");
467                                 rc = 4;
468                                 goto exit;
469                         }
470                 } else if (name[0] != '\0') {
471                         if (lookup_device_by_name(udev, &udevice, name) != 0) {
472                                 fprintf(stderr, "node name not found\n");
473                                 rc = 4;
474                                 goto exit;
475                         }
476                         if (print_device_chain(udev, udevice->dev->devpath) != 0) {
477                                 fprintf(stderr, "no valid sysfs device found\n");
478                                 rc = 4;
479                                 goto exit;
480                         }
481                 } else {
482                         fprintf(stderr, "attribute walk needs --path or node --name specified\n");
483                         rc = 5;
484                         goto exit;
485                 }
486                 break;
487         case ACTION_DEVICE_ID_FILE:
488                 if (stat_device(name, export, export_prefix) != 0)
489                         rc = 6;
490                 break;
491         case ACTION_ROOT:
492                 printf("%s\n", udev_get_dev_path(udev));
493                 break;
494         default:
495                 fprintf(stderr, "missing option\n");
496                 rc = 1;
497                 break;
498         }
499
500 exit:
501         udev_device_cleanup(udevice);
502         return rc;
503 }