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