]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-builtin-hwdb.c
b95465b3e3906a88f11d9c6910715eb87b86944e
[thirdparty/systemd.git] / src / udev / udev-builtin-hwdb.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2012 Kay Sievers <kay@vrfy.org>
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <fnmatch.h>
22 #include <getopt.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #include "sd-hwdb.h"
27
28 #include "alloc-util.h"
29 #include "hwdb-util.h"
30 #include "parse-util.h"
31 #include "string-util.h"
32 #include "udev-util.h"
33 #include "udev.h"
34
35 static sd_hwdb *hwdb;
36
37 int udev_builtin_hwdb_lookup(struct udev_device *dev,
38 const char *prefix, const char *modalias,
39 const char *filter, bool test) {
40 _cleanup_free_ char *lookup = NULL;
41 const char *key, *value;
42 int n = 0;
43
44 if (!hwdb)
45 return -ENOENT;
46
47 if (prefix) {
48 lookup = strjoin(prefix, modalias);
49 if (!lookup)
50 return -ENOMEM;
51 modalias = lookup;
52 }
53
54 SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value) {
55 if (filter && fnmatch(filter, key, FNM_NOESCAPE) != 0)
56 continue;
57
58 if (udev_builtin_add_property(dev, test, key, value) < 0)
59 return -ENOMEM;
60 n++;
61 }
62 return n;
63 }
64
65 static const char *modalias_usb(struct udev_device *dev, char *s, size_t size) {
66 const char *v, *p;
67 uint16_t vn, pn;
68
69 v = udev_device_get_sysattr_value(dev, "idVendor");
70 if (!v)
71 return NULL;
72 p = udev_device_get_sysattr_value(dev, "idProduct");
73 if (!p)
74 return NULL;
75 if (safe_atoux16(v, &vn) < 0)
76 return NULL;
77 if (safe_atoux16(p, &pn) < 0)
78 return NULL;
79 snprintf(s, size, "usb:v%04Xp%04X*", vn, pn);
80 return s;
81 }
82
83 static int udev_builtin_hwdb_search(struct udev_device *dev, struct udev_device *srcdev,
84 const char *subsystem, const char *prefix,
85 const char *filter, bool test) {
86 struct udev_device *d;
87 char s[16];
88 bool last = false;
89 int r = 0;
90
91 assert(dev);
92
93 if (!srcdev)
94 srcdev = dev;
95
96 for (d = srcdev; d && !last; d = udev_device_get_parent(d)) {
97 const char *dsubsys;
98 const char *modalias = NULL;
99
100 dsubsys = udev_device_get_subsystem(d);
101 if (!dsubsys)
102 continue;
103
104 /* look only at devices of a specific subsystem */
105 if (subsystem && !streq(dsubsys, subsystem))
106 continue;
107
108 modalias = udev_device_get_property_value(d, "MODALIAS");
109
110 if (streq(dsubsys, "usb") && streq_ptr(udev_device_get_devtype(d), "usb_device")) {
111 /* if the usb_device does not have a modalias, compose one */
112 if (!modalias)
113 modalias = modalias_usb(d, s, sizeof(s));
114
115 /* avoid looking at any parent device, they are usually just a USB hub */
116 last = true;
117 }
118
119 if (!modalias)
120 continue;
121
122 r = udev_builtin_hwdb_lookup(dev, prefix, modalias, filter, test);
123 if (r > 0)
124 break;
125 }
126
127 return r;
128 }
129
130 static int builtin_hwdb(struct udev_device *dev, int argc, char *argv[], bool test) {
131 static const struct option options[] = {
132 { "filter", required_argument, NULL, 'f' },
133 { "device", required_argument, NULL, 'd' },
134 { "subsystem", required_argument, NULL, 's' },
135 { "lookup-prefix", required_argument, NULL, 'p' },
136 {}
137 };
138 const char *filter = NULL;
139 const char *device = NULL;
140 const char *subsystem = NULL;
141 const char *prefix = NULL;
142 _cleanup_udev_device_unref_ struct udev_device *srcdev = NULL;
143
144 if (!hwdb)
145 return EXIT_FAILURE;
146
147 for (;;) {
148 int option;
149
150 option = getopt_long(argc, argv, "f:d:s:p:", options, NULL);
151 if (option == -1)
152 break;
153
154 switch (option) {
155 case 'f':
156 filter = optarg;
157 break;
158
159 case 'd':
160 device = optarg;
161 break;
162
163 case 's':
164 subsystem = optarg;
165 break;
166
167 case 'p':
168 prefix = optarg;
169 break;
170 }
171 }
172
173 /* query a specific key given as argument */
174 if (argv[optind]) {
175 if (udev_builtin_hwdb_lookup(dev, prefix, argv[optind], filter, test) > 0)
176 return EXIT_SUCCESS;
177 return EXIT_FAILURE;
178 }
179
180 /* read data from another device than the device we will store the data */
181 if (device) {
182 srcdev = udev_device_new_from_device_id(udev_device_get_udev(dev), device);
183 if (!srcdev)
184 return EXIT_FAILURE;
185 }
186
187 if (udev_builtin_hwdb_search(dev, srcdev, subsystem, prefix, filter, test) > 0)
188 return EXIT_SUCCESS;
189 return EXIT_FAILURE;
190 }
191
192 /* called at udev startup and reload */
193 static int builtin_hwdb_init(struct udev *udev) {
194 int r;
195
196 if (hwdb)
197 return 0;
198
199 r = sd_hwdb_new(&hwdb);
200 if (r < 0)
201 return r;
202
203 return 0;
204 }
205
206 /* called on udev shutdown and reload request */
207 static void builtin_hwdb_exit(struct udev *udev) {
208 hwdb = sd_hwdb_unref(hwdb);
209 }
210
211 /* called every couple of seconds during event activity; 'true' if config has changed */
212 static bool builtin_hwdb_validate(struct udev *udev) {
213 return hwdb_validate(hwdb);
214 }
215
216 const struct udev_builtin udev_builtin_hwdb = {
217 .name = "hwdb",
218 .cmd = builtin_hwdb,
219 .init = builtin_hwdb_init,
220 .exit = builtin_hwdb_exit,
221 .validate = builtin_hwdb_validate,
222 .help = "Hardware database",
223 };