chiark / gitweb /
udevadm: info - make attribute array static and const
[elogind.git] / udev / udevadm-info.c
1 /*
2  * Copyright (C) 2004-2009 Kay Sievers <kay.sievers@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
34 static bool skip_attribute(const char *name)
35 {
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 < ARRAY_SIZE(skip); i++)
48                 if (strcmp(name, skip[i]) == 0)
49                         return true;
50         return false;
51 }
52
53 static void print_all_attributes(struct udev_device *device, const char *key)
54 {
55         struct udev_list_entry *sysattr;
56
57         udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) {
58                 struct udev *udev = udev_device_get_udev(device);
59                 const char *name;
60                 const char *value;
61                 size_t len;
62
63                 name = udev_list_entry_get_name(sysattr);
64                 if (skip_attribute(name))
65                         continue;
66
67                 value = udev_device_get_sysattr_value(device, name);
68                 if (value == NULL)
69                         continue;
70                 dbg(udev, "attr '%s'='%s'\n", name, value);
71
72                 /* skip nonprintable attributes */
73                 len = strlen(value);
74                 while (len > 0 && isprint(value[len-1]))
75                         len--;
76                 if (len > 0) {
77                         dbg(udev, "attribute value of '%s' non-printable, skip\n", name);
78                         continue;
79                 }
80
81                 printf("    %s{%s}==\"%s\"\n", key, name, value);
82         }
83         printf("\n");
84 }
85
86 static int print_device_chain(struct udev_device *device)
87 {
88         struct udev_device *device_parent;
89         const char *str;
90
91         printf("\n"
92                "Udevadm info starts with the device specified by the devpath and then\n"
93                "walks up the chain of parent devices. It prints for every device\n"
94                "found, all possible attributes in the udev rules key format.\n"
95                "A rule to match, can be composed by the attributes of the device\n"
96                "and the attributes from one single parent device.\n"
97                "\n");
98
99         printf("  looking at device '%s':\n", udev_device_get_devpath(device));
100         printf("    KERNEL==\"%s\"\n", udev_device_get_sysname(device));
101         str = udev_device_get_subsystem(device);
102         if (str == NULL)
103                 str = "";
104         printf("    SUBSYSTEM==\"%s\"\n", str);
105         str = udev_device_get_driver(device);
106         if (str == NULL)
107                 str = "";
108         printf("    DRIVER==\"%s\"\n", str);
109         print_all_attributes(device, "ATTR");
110
111         device_parent = device;
112         do {
113                 device_parent = udev_device_get_parent(device_parent);
114                 if (device_parent == NULL)
115                         break;
116                 printf("  looking at parent device '%s':\n", udev_device_get_devpath(device_parent));
117                 printf("    KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent));
118                 str = udev_device_get_subsystem(device_parent);
119                 if (str == NULL)
120                         str = "";
121                 printf("    SUBSYSTEMS==\"%s\"\n", str);
122                 str = udev_device_get_driver(device_parent);
123                 if (str == NULL)
124                         str = "";
125                 printf("    DRIVERS==\"%s\"\n", str);
126                 print_all_attributes(device_parent, "ATTRS");
127         } while (device_parent != NULL);
128
129         return 0;
130 }
131
132 static void print_record(struct udev_device *device)
133 {
134         size_t len;
135         const char *str;
136         int i;
137         struct udev_list_entry *list_entry;
138
139         printf("P: %s\n", udev_device_get_devpath(device));
140
141         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
142         str = udev_device_get_devnode(device);
143         if (str != NULL)
144                 printf("N: %s\n", &str[len+1]);
145
146         i = udev_device_get_devlink_priority(device);
147         if (i != 0)
148                 printf("L: %i\n", i);
149
150         udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) {
151                 len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
152                 printf("S: %s\n", &udev_list_entry_get_name(list_entry)[len+1]);
153         }
154
155         udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
156                 printf("E: %s=%s\n",
157                        udev_list_entry_get_name(list_entry),
158                        udev_list_entry_get_value(list_entry));
159         printf("\n");
160 }
161
162 static int stat_device(const char *name, bool export, const char *prefix)
163 {
164         struct stat statbuf;
165
166         if (stat(name, &statbuf) != 0)
167                 return -1;
168
169         if (export) {
170                 if (prefix == NULL)
171                         prefix = "INFO_";
172                 printf("%sMAJOR=%d\n"
173                        "%sMINOR=%d\n",
174                        prefix, major(statbuf.st_dev),
175                        prefix, minor(statbuf.st_dev));
176         } else
177                 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
178         return 0;
179 }
180
181 static int export_devices(struct udev *udev)
182 {
183         struct udev_enumerate *udev_enumerate;
184         struct udev_list_entry *list_entry;
185
186         udev_enumerate = udev_enumerate_new(udev);
187         if (udev_enumerate == NULL)
188                 return -1;
189         udev_enumerate_scan_devices(udev_enumerate);
190         udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
191                 struct udev_device *device;
192
193                 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
194                 if (device != NULL) {
195                         print_record(device);
196                         udev_device_unref(device);
197                 }
198         }
199         udev_enumerate_unref(udev_enumerate);
200         return 0;
201 }
202
203 static int convert_db(struct udev *udev)
204 {
205         struct udev_enumerate *udev_enumerate;
206         struct udev_list_entry *list_entry;
207
208         udev_enumerate = udev_enumerate_new(udev);
209         if (udev_enumerate == NULL)
210                 return -1;
211         udev_enumerate_scan_devices(udev_enumerate);
212         udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
213                 struct udev_device *device;
214
215                 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
216                 if (device != NULL) {
217                         const char *id;
218                         struct stat stats;
219                         char to[UTIL_PATH_SIZE];
220                         char devpath[UTIL_PATH_SIZE];
221                         char from[UTIL_PATH_SIZE];
222
223                         id = udev_device_get_id_filename(device);
224                         if (id == NULL) {
225                                 udev_device_unref(device);
226                                 continue;
227                         }
228                         util_strscpyl(to, sizeof(to), udev_get_dev_path(udev), "/.udev/db/", id, NULL);
229
230                         /* find old database with $subsys:$sysname */
231                         util_strscpyl(from, sizeof(from), udev_get_dev_path(udev),
232                                      "/.udev/db/", udev_device_get_subsystem(device), ":",
233                                      udev_device_get_sysname(device), NULL);
234                         if (lstat(from, &stats) == 0) {
235                                 if (lstat(to, &stats) == 0)
236                                         unlink(from);
237                                 else
238                                         rename(from, to);
239                         }
240
241                         /* find old database with the encoded devpath */
242                         util_path_encode(udev_device_get_devpath(device), devpath, sizeof(devpath));
243                         util_strscpyl(from, sizeof(from), udev_get_dev_path(udev),
244                                       "/.udev/db/", devpath, NULL);
245                         if (lstat(from, &stats) == 0) {
246                                 if (lstat(to, &stats) == 0)
247                                         unlink(from);
248                                 else
249                                         rename(from, to);
250                         }
251
252                         /* read the old database, and write out a new one */
253                         udev_device_read_db(device);
254                         udev_device_update_db(device);
255
256                         udev_device_unref(device);
257                 }
258         }
259         udev_enumerate_unref(udev_enumerate);
260         return 0;
261 }
262
263 int udevadm_info(struct udev *udev, int argc, char *argv[])
264 {
265         struct udev_device *device = NULL;
266         bool root = 0;
267         bool export = 0;
268         const char *export_prefix = NULL;
269         char path[UTIL_PATH_SIZE];
270         char name[UTIL_PATH_SIZE];
271         struct udev_list_entry *list_entry;
272         int rc = 0;
273
274         static const struct option options[] = {
275                 { "name", required_argument, NULL, 'n' },
276                 { "path", required_argument, NULL, 'p' },
277                 { "query", required_argument, NULL, 'q' },
278                 { "attribute-walk", no_argument, NULL, 'a' },
279                 { "export-db", no_argument, NULL, 'e' },
280                 { "convert-db", no_argument, NULL, 'C' },
281                 { "root", no_argument, NULL, 'r' },
282                 { "device-id-of-file", required_argument, NULL, 'd' },
283                 { "export", no_argument, NULL, 'x' },
284                 { "export-prefix", required_argument, NULL, 'P' },
285                 { "version", no_argument, NULL, 'V' },
286                 { "help", no_argument, NULL, 'h' },
287                 {}
288         };
289
290         enum action_type {
291                 ACTION_NONE,
292                 ACTION_QUERY,
293                 ACTION_ATTRIBUTE_WALK,
294                 ACTION_ROOT,
295                 ACTION_DEVICE_ID_FILE,
296         } action = ACTION_NONE;
297
298         enum query_type {
299                 QUERY_NONE,
300                 QUERY_NAME,
301                 QUERY_PATH,
302                 QUERY_SYMLINK,
303                 QUERY_PROPERTY,
304                 QUERY_ALL,
305         } query = QUERY_NONE;
306
307         for (;;) {
308                 int option;
309                 struct stat statbuf;
310
311                 option = getopt_long(argc, argv, "aed:n:p:q:rxP:Vh", options, NULL);
312                 if (option == -1)
313                         break;
314
315                 dbg(udev, "option '%c'\n", option);
316                 switch (option) {
317                 case 'n':
318                         if (device != NULL) {
319                                 fprintf(stderr, "device already specified\n");
320                                 rc = 2;
321                                 goto exit;
322                         }
323                         /* remove /dev if given */
324                         if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) != 0)
325                                 util_strscpyl(name, sizeof(name), udev_get_dev_path(udev), "/", optarg, NULL);
326                         else
327                                 util_strscpy(name, sizeof(name), optarg);
328                         util_remove_trailing_chars(name, '/');
329                         if (stat(name, &statbuf) < 0) {
330                                 fprintf(stderr, "device node not found\n");
331                                 rc = 2;
332                                 goto exit;
333                         } else {
334                                 char type;
335
336                                 if (S_ISBLK(statbuf.st_mode)) {
337                                         type = 'b';
338                                 } else if (S_ISCHR(statbuf.st_mode)) {
339                                         type = 'c';
340                                 } else {
341                                         fprintf(stderr, "device node has wrong file type\n");
342                                         rc = 2;
343                                         goto exit;
344                                 }
345                                 device = udev_device_new_from_devnum(udev, type, statbuf.st_rdev);
346                                 if (device == NULL) {
347                                         fprintf(stderr, "device node not found\n");
348                                         rc = 2;
349                                         goto exit;
350                                 }
351                         }
352                         break;
353                 case 'p':
354                         if (device != NULL) {
355                                 fprintf(stderr, "device already specified\n");
356                                 rc = 2;
357                                 goto exit;
358                         }
359                         /* add sys dir if needed */
360                         if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0)
361                                 util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), optarg, NULL);
362                         else
363                                 util_strscpy(path, sizeof(path), optarg);
364                         util_remove_trailing_chars(path, '/');
365                         device = udev_device_new_from_syspath(udev, path);
366                         if (device == NULL) {
367                                 fprintf(stderr, "device path not found\n");
368                                 rc = 2;
369                                 goto exit;
370                         }
371                         break;
372                 case 'q':
373                         action = ACTION_QUERY;
374                         if (strcmp(optarg, "property") == 0 || strcmp(optarg, "env") == 0) {
375                                 query = QUERY_PROPERTY;
376                         } else if (strcmp(optarg, "name") == 0) {
377                                 query = QUERY_NAME;
378                         } else if (strcmp(optarg, "symlink") == 0) {
379                                 query = QUERY_SYMLINK;
380                         } else if (strcmp(optarg, "path") == 0) {
381                                 query = QUERY_PATH;
382                         } else if (strcmp(optarg, "all") == 0) {
383                                 query = QUERY_ALL;
384                         } else {
385                                 fprintf(stderr, "unknown query type\n");
386                                 rc = 3;
387                                 goto exit;
388                         }
389                         break;
390                 case 'r':
391                         if (action == ACTION_NONE)
392                                 action = ACTION_ROOT;
393                         root = true;
394                         break;
395                 case 'd':
396                         action = ACTION_DEVICE_ID_FILE;
397                         util_strscpy(name, sizeof(name), optarg);
398                         break;
399                 case 'a':
400                         action = ACTION_ATTRIBUTE_WALK;
401                         break;
402                 case 'e':
403                         export_devices(udev);
404                         goto exit;
405                 case 'C':
406                         convert_db(udev);
407                         goto exit;
408                 case 'x':
409                         export = true;
410                         break;
411                 case 'P':
412                         export_prefix = optarg;
413                         break;
414                 case 'V':
415                         printf("%s\n", VERSION);
416                         goto exit;
417                 case 'h':
418                         printf("Usage: udevadm info OPTIONS\n"
419                                "  --query=<type>             query device information:\n"
420                                "      name                     name of device node\n"
421                                "      symlink                  pointing to node\n"
422                                "      path                     sys device path\n"
423                                "      property                 the device properties\n"
424                                "      all                      all values\n"
425                                "  --path=<syspath>           sys device path used for query or attribute walk\n"
426                                "  --name=<name>              node or symlink name used for query or attribute walk\n"
427                                "  --root                     prepend dev directory to path names\n"
428                                "  --attribute-walk           print all key matches while walking along the chain\n"
429                                "                             of parent devices\n"
430                                "  --device-id-of-file=<file> print major:minor of device containing this file\n"
431                                "  --export                   export key/value pairs\n"
432                                "  --export-prefix            export the key name with a prefix\n"
433                                "  --export-db                export the content of the udev database\n"
434                                "  --convert-db               convert older version of database without a reboot\n"
435                                "  --help\n\n");
436                         goto exit;
437                 default:
438                         goto exit;
439                 }
440         }
441
442         switch (action) {
443         case ACTION_QUERY:
444                 if (device == NULL) {
445                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
446                         rc = 4;
447                         goto exit;
448                 }
449
450                 switch(query) {
451                 case QUERY_NAME: {
452                         const char *node = udev_device_get_devnode(device);
453
454                         if (node == NULL) {
455                                 fprintf(stderr, "no device node found\n");
456                                 rc = 5;
457                                 goto exit;
458                         }
459
460                         if (root) {
461                                 printf("%s\n", udev_device_get_devnode(device));
462                         } else {
463                                 size_t len = strlen(udev_get_dev_path(udev));
464
465                                 printf("%s\n", &udev_device_get_devnode(device)[len+1]);
466                         }
467                         break;
468                 }
469                 case QUERY_SYMLINK:
470                         list_entry = udev_device_get_devlinks_list_entry(device);
471                         while (list_entry != NULL) {
472                                 if (root) {
473                                         printf("%s", udev_list_entry_get_name(list_entry));
474                                 } else {
475                                         size_t len;
476
477                                         len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
478                                         printf("%s", &udev_list_entry_get_name(list_entry)[len+1]);
479                                 }
480                                 list_entry = udev_list_entry_get_next(list_entry);
481                                 if (list_entry != NULL)
482                                         printf(" ");
483                         }
484                         printf("\n");
485                         break;
486                 case QUERY_PATH:
487                         printf("%s\n", udev_device_get_devpath(device));
488                         goto exit;
489                 case QUERY_PROPERTY:
490                         list_entry = udev_device_get_properties_list_entry(device);
491                         while (list_entry != NULL) {
492                                 if (export) {
493                                         const char *prefix = export_prefix;
494
495                                         if (prefix == NULL)
496                                                 prefix = "";
497                                         printf("%s%s='%s'\n", prefix,
498                                                udev_list_entry_get_name(list_entry),
499                                                udev_list_entry_get_value(list_entry));
500                                 } else {
501                                         printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
502                                 }
503                                 list_entry = udev_list_entry_get_next(list_entry);
504                         }
505                         break;
506                 case QUERY_ALL:
507                         print_record(device);
508                         break;
509                 default:
510                         fprintf(stderr, "unknown query type\n");
511                         break;
512                 }
513                 break;
514         case ACTION_ATTRIBUTE_WALK:
515                 if (device == NULL) {
516                         fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
517                         rc = 4;
518                         goto exit;
519                 }
520                 print_device_chain(device);
521                 break;
522         case ACTION_DEVICE_ID_FILE:
523                 if (stat_device(name, export, export_prefix) != 0)
524                         rc = 1;
525                 break;
526         case ACTION_ROOT:
527                 printf("%s\n", udev_get_dev_path(udev));
528                 break;
529         default:
530                 fprintf(stderr, "missing option\n");
531                 rc = 1;
532                 break;
533         }
534
535 exit:
536         udev_device_unref(device);
537         return rc;
538 }