chiark / gitweb /
sysusers: add a new RPM macro for creating users directly from data passed in via...
[elogind.git] / src / udev / udevadm-info.c
1 /*
2  * Copyright (C) 2004-2009 Kay Sievers <kay@vrfy.org>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <stdlib.h>
19 #include <string.h>
20 #include <stdio.h>
21 #include <stddef.h>
22 #include <ctype.h>
23 #include <stdarg.h>
24 #include <unistd.h>
25 #include <dirent.h>
26 #include <errno.h>
27 #include <getopt.h>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31
32 #include "udev.h"
33 #include "udev-util.h"
34
35 static bool skip_attribute(const char *name) {
36         static const char* const skip[] = {
37                 "uevent",
38                 "dev",
39                 "modalias",
40                 "resource",
41                 "driver",
42                 "subsystem",
43                 "module",
44         };
45         unsigned int i;
46
47         for (i = 0; i < ELEMENTSOF(skip); i++)
48                 if (streq(name, skip[i]))
49                         return true;
50         return false;
51 }
52
53 static void print_all_attributes(struct udev_device *device, const char *key) {
54         struct udev_list_entry *sysattr;
55
56         udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) {
57                 const char *name;
58                 const char *value;
59                 size_t len;
60
61                 name = udev_list_entry_get_name(sysattr);
62                 if (skip_attribute(name))
63                         continue;
64
65                 value = udev_device_get_sysattr_value(device, name);
66                 if (value == NULL)
67                         continue;
68
69                 /* skip any values that look like a path */
70                 if (value[0] == '/')
71                         continue;
72
73                 /* skip nonprintable attributes */
74                 len = strlen(value);
75                 while (len > 0 && isprint(value[len-1]))
76                         len--;
77                 if (len > 0)
78                         continue;
79
80                 printf("    %s{%s}==\"%s\"\n", key, name, value);
81         }
82         printf("\n");
83 }
84
85 static int print_device_chain(struct udev_device *device) {
86         struct udev_device *device_parent;
87         const char *str;
88
89         printf("\n"
90                "Udevadm info starts with the device specified by the devpath and then\n"
91                "walks up the chain of parent devices. It prints for every device\n"
92                "found, all possible attributes in the udev rules key format.\n"
93                "A rule to match, can be composed by the attributes of the device\n"
94                "and the attributes from one single parent device.\n"
95                "\n");
96
97         printf("  looking at device '%s':\n", udev_device_get_devpath(device));
98         printf("    KERNEL==\"%s\"\n", udev_device_get_sysname(device));
99         str = udev_device_get_subsystem(device);
100         if (str == NULL)
101                 str = "";
102         printf("    SUBSYSTEM==\"%s\"\n", str);
103         str = udev_device_get_driver(device);
104         if (str == NULL)
105                 str = "";
106         printf("    DRIVER==\"%s\"\n", str);
107         print_all_attributes(device, "ATTR");
108
109         device_parent = device;
110         do {
111                 device_parent = udev_device_get_parent(device_parent);
112                 if (device_parent == NULL)
113                         break;
114                 printf("  looking at parent device '%s':\n", udev_device_get_devpath(device_parent));
115                 printf("    KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent));
116                 str = udev_device_get_subsystem(device_parent);
117                 if (str == NULL)
118                         str = "";
119                 printf("    SUBSYSTEMS==\"%s\"\n", str);
120                 str = udev_device_get_driver(device_parent);
121                 if (str == NULL)
122                         str = "";
123                 printf("    DRIVERS==\"%s\"\n", str);
124                 print_all_attributes(device_parent, "ATTRS");
125         } while (device_parent != NULL);
126
127         return 0;
128 }
129
130 static void print_record(struct udev_device *device) {
131         const char *str;
132         int i;
133         struct udev_list_entry *list_entry;
134
135         printf("P: %s\n", udev_device_get_devpath(device));
136
137         str = udev_device_get_devnode(device);
138         if (str != NULL)
139                 printf("N: %s\n", str + strlen("/dev/"));
140
141         i = udev_device_get_devlink_priority(device);
142         if (i != 0)
143                 printf("L: %i\n", i);
144
145         udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device))
146                 printf("S: %s\n", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
147
148         udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
149                 printf("E: %s=%s\n",
150                        udev_list_entry_get_name(list_entry),
151                        udev_list_entry_get_value(list_entry));
152         printf("\n");
153 }
154
155 static int stat_device(const char *name, bool export, const char *prefix) {
156         struct stat statbuf;
157
158         if (stat(name, &statbuf) != 0)
159                 return -1;
160
161         if (export) {
162                 if (prefix == NULL)
163                         prefix = "INFO_";
164                 printf("%sMAJOR=%d\n"
165                        "%sMINOR=%d\n",
166                        prefix, major(statbuf.st_dev),
167                        prefix, minor(statbuf.st_dev));
168         } else
169                 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
170         return 0;
171 }
172
173 static int export_devices(struct udev *udev) {
174         struct udev_enumerate *udev_enumerate;
175         struct udev_list_entry *list_entry;
176
177         udev_enumerate = udev_enumerate_new(udev);
178         if (udev_enumerate == NULL)
179                 return -1;
180         udev_enumerate_scan_devices(udev_enumerate);
181         udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
182                 struct udev_device *device;
183
184                 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
185                 if (device != NULL) {
186                         print_record(device);
187                         udev_device_unref(device);
188                 }
189         }
190         udev_enumerate_unref(udev_enumerate);
191         return 0;
192 }
193
194 static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
195         struct dirent *dent;
196
197         if (depth <= 0)
198                 return;
199
200         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
201                 struct stat stats;
202
203                 if (dent->d_name[0] == '.')
204                         continue;
205                 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
206                         continue;
207                 if ((stats.st_mode & mask) != 0)
208                         continue;
209                 if (S_ISDIR(stats.st_mode)) {
210                         DIR *dir2;
211
212                         dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
213                         if (dir2 != NULL) {
214                                 cleanup_dir(dir2, mask, depth-1);
215                                 closedir(dir2);
216                         }
217                         unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
218                 } else {
219                         unlinkat(dirfd(dir), dent->d_name, 0);
220                 }
221         }
222 }
223
224 static void cleanup_db(struct udev *udev) {
225         DIR *dir;
226
227         unlink("/run/udev/queue.bin");
228
229         dir = opendir("/run/udev/data");
230         if (dir != NULL) {
231                 cleanup_dir(dir, S_ISVTX, 1);
232                 closedir(dir);
233         }
234
235         dir = opendir("/run/udev/links");
236         if (dir != NULL) {
237                 cleanup_dir(dir, 0, 2);
238                 closedir(dir);
239         }
240
241         dir = opendir("/run/udev/tags");
242         if (dir != NULL) {
243                 cleanup_dir(dir, 0, 2);
244                 closedir(dir);
245         }
246
247         dir = opendir("/run/udev/static_node-tags");
248         if (dir != NULL) {
249                 cleanup_dir(dir, 0, 2);
250                 closedir(dir);
251         }
252
253         dir = opendir("/run/udev/watch");
254         if (dir != NULL) {
255                 cleanup_dir(dir, 0, 1);
256                 closedir(dir);
257         }
258 }
259
260 static struct udev_device *find_device(struct udev *udev, const char *id, const char *prefix) {
261         char name[UTIL_PATH_SIZE];
262
263         if (prefix && !startswith(id, prefix)) {
264                 strscpyl(name, sizeof(name), prefix, id, NULL);
265                 id = name;
266         }
267
268         if (startswith(id, "/dev/")) {
269                 struct stat statbuf;
270                 char type;
271
272                 if (stat(id, &statbuf) < 0)
273                         return NULL;
274
275                 if (S_ISBLK(statbuf.st_mode))
276                         type = 'b';
277                 else if (S_ISCHR(statbuf.st_mode))
278                         type = 'c';
279                 else
280                         return NULL;
281
282                 return udev_device_new_from_devnum(udev, type, statbuf.st_rdev);
283         } else if (startswith(id, "/sys/"))
284                 return udev_device_new_from_syspath(udev, id);
285         else
286                 return NULL;
287 }
288
289 static int uinfo(struct udev *udev, int argc, char *argv[]) {
290         _cleanup_udev_device_unref_ struct udev_device *device = NULL;
291         bool root = 0;
292         bool export = 0;
293         const char *export_prefix = NULL;
294         char name[UTIL_PATH_SIZE];
295         struct udev_list_entry *list_entry;
296         int c;
297
298         static const struct option options[] = {
299                 { "name",              required_argument, NULL, 'n' },
300                 { "path",              required_argument, NULL, 'p' },
301                 { "query",             required_argument, NULL, 'q' },
302                 { "attribute-walk",    no_argument,       NULL, 'a' },
303                 { "cleanup-db",        no_argument,       NULL, 'c' },
304                 { "export-db",         no_argument,       NULL, 'e' },
305                 { "root",              no_argument,       NULL, 'r' },
306                 { "device-id-of-file", required_argument, NULL, 'd' },
307                 { "export",            no_argument,       NULL, 'x' },
308                 { "export-prefix",     required_argument, NULL, 'P' },
309                 { "version",           no_argument,       NULL, 'V' },
310                 { "help",              no_argument,       NULL, 'h' },
311                 {}
312         };
313
314         static const char *usage =
315                 "Usage: udevadm info [OPTIONS] [DEVPATH|FILE]\n"
316                 " -q,--query=TYPE             query device information:\n"
317                 "      name                     name of device node\n"
318                 "      symlink                  pointing to node\n"
319                 "      path                     sys device path\n"
320                 "      property                 the device properties\n"
321                 "      all                      all values\n"
322                 " -p,--path=SYSPATH           sys device path used for query or attribute walk\n"
323                 " -n,--name=NAME              node or symlink name used for query or attribute walk\n"
324                 " -r,--root                   prepend dev directory to path names\n"
325                 " -a,--attribute-walk         print all key matches walking along the chain\n"
326                 "                             of parent devices\n"
327                 " -d,--device-id-of-file=FILE print major:minor of device containing this file\n"
328                 " -x,--export                 export key/value pairs\n"
329                 " -P,--export-prefix          export the key name with a prefix\n"
330                 " -e,--export-db              export the content of the udev database\n"
331                 " -c,--cleanup-db             cleanup the udev database\n"
332                 "    --version                print version of the program\n"
333                 " -h,--help                   print this message\n";
334
335         enum action_type {
336                 ACTION_QUERY,
337                 ACTION_ATTRIBUTE_WALK,
338                 ACTION_DEVICE_ID_FILE,
339         } action = ACTION_QUERY;
340
341         enum query_type {
342                 QUERY_NAME,
343                 QUERY_PATH,
344                 QUERY_SYMLINK,
345                 QUERY_PROPERTY,
346                 QUERY_ALL,
347         } query = QUERY_ALL;
348
349         while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
350                 switch (c) {
351                 case 'n': {
352                         if (device != NULL) {
353                                 fprintf(stderr, "device already specified\n");
354                                 return 2;
355                         }
356
357                         device = find_device(udev, optarg, "/dev/");
358                         if (device == NULL) {
359                                 fprintf(stderr, "device node not found\n");
360                                 return 2;
361                         }
362                         break;
363                 }
364                 case 'p':
365                         if (device != NULL) {
366                                 fprintf(stderr, "device already specified\n");
367                                 return 2;
368                         }
369
370                         device = find_device(udev, optarg, "/sys");
371                         if (device == NULL) {
372                                 fprintf(stderr, "syspath not found\n");
373                                 return 2;
374                         }
375                         break;
376                 case 'q':
377                         action = ACTION_QUERY;
378                         if (streq(optarg, "property") || streq(optarg, "env"))
379                                 query = QUERY_PROPERTY;
380                         else if (streq(optarg, "name"))
381                                 query = QUERY_NAME;
382                         else if (streq(optarg, "symlink"))
383                                 query = QUERY_SYMLINK;
384                         else if (streq(optarg, "path"))
385                                 query = QUERY_PATH;
386                         else if (streq(optarg, "all"))
387                                 query = QUERY_ALL;
388                         else {
389                                 fprintf(stderr, "unknown query type\n");
390                                 return 3;
391                         }
392                         break;
393                 case 'r':
394                         root = true;
395                         break;
396                 case 'd':
397                         action = ACTION_DEVICE_ID_FILE;
398                         strscpy(name, sizeof(name), optarg);
399                         break;
400                 case 'a':
401                         action = ACTION_ATTRIBUTE_WALK;
402                         break;
403                 case 'e':
404                         export_devices(udev);
405                         return 0;
406                 case 'c':
407                         cleanup_db(udev);
408                         return 0;
409                 case 'x':
410                         export = true;
411                         break;
412                 case 'P':
413                         export_prefix = optarg;
414                         break;
415                 case 'V':
416                         printf("%s\n", VERSION);
417                         return 0;
418                 case 'h':
419                         printf("%s\n", usage);
420                         return 0;
421                 default:
422                         return 1;
423                 }
424
425         switch (action) {
426         case ACTION_QUERY:
427                 if (!device) {
428                         if (!argv[optind]) {
429                                 fprintf(stderr, "%s\n", usage);
430                                 return 2;
431                         }
432                         device = find_device(udev, argv[optind], NULL);
433                         if (!device) {
434                                 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
435                                 return 4;
436                         }
437                 }
438
439                 switch(query) {
440                 case QUERY_NAME: {
441                         const char *node = udev_device_get_devnode(device);
442
443                         if (node == NULL) {
444                                 fprintf(stderr, "no device node found\n");
445                                 return 5;
446                         }
447
448                         if (root)
449                                 printf("%s\n", udev_device_get_devnode(device));
450                         else
451                                 printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/"));
452                         break;
453                 }
454                 case QUERY_SYMLINK:
455                         list_entry = udev_device_get_devlinks_list_entry(device);
456                         while (list_entry != NULL) {
457                                 if (root)
458                                         printf("%s", udev_list_entry_get_name(list_entry));
459                                 else
460                                         printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
461                                 list_entry = udev_list_entry_get_next(list_entry);
462                                 if (list_entry != NULL)
463                                         printf(" ");
464                         }
465                         printf("\n");
466                         break;
467                 case QUERY_PATH:
468                         printf("%s\n", udev_device_get_devpath(device));
469                         return 0;
470                 case QUERY_PROPERTY:
471                         list_entry = udev_device_get_properties_list_entry(device);
472                         while (list_entry != NULL) {
473                                 if (export) {
474                                         const char *prefix = export_prefix;
475
476                                         if (prefix == NULL)
477                                                 prefix = "";
478                                         printf("%s%s='%s'\n", prefix,
479                                                udev_list_entry_get_name(list_entry),
480                                                udev_list_entry_get_value(list_entry));
481                                 } else {
482                                         printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
483                                 }
484                                 list_entry = udev_list_entry_get_next(list_entry);
485                         }
486                         break;
487                 case QUERY_ALL:
488                         print_record(device);
489                         break;
490                 default:
491                         assert_not_reached("unknown query type");
492                 }
493                 break;
494         case ACTION_ATTRIBUTE_WALK:
495                 if (!device && argv[optind]) {
496                         device = find_device(udev, argv[optind], NULL);
497                         if (!device) {
498                                 fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n");
499                                 return 4;
500                         }
501                 }
502                 if (!device) {
503                         fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
504                         return 4;
505                 }
506                 print_device_chain(device);
507                 break;
508         case ACTION_DEVICE_ID_FILE:
509                 if (stat_device(name, export, export_prefix) != 0)
510                         return 1;
511                 break;
512         }
513
514         return 0;
515 }
516
517 const struct udevadm_cmd udevadm_info = {
518         .name = "info",
519         .cmd = uinfo,
520         .help = "query sysfs or the udev database",
521 };