]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-builtin-hwdb.c
Add SPDX license identifiers to source files under the LGPL
[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 "string-util.h"
31 #include "udev-util.h"
32 #include "udev.h"
33
34 static sd_hwdb *hwdb;
35
36 int udev_builtin_hwdb_lookup(struct udev_device *dev,
37 const char *prefix, const char *modalias,
38 const char *filter, bool test) {
39 _cleanup_free_ char *lookup = NULL;
40 const char *key, *value;
41 int n = 0;
42
43 if (!hwdb)
44 return -ENOENT;
45
46 if (prefix) {
47 lookup = strjoin(prefix, modalias);
48 if (!lookup)
49 return -ENOMEM;
50 modalias = lookup;
51 }
52
53 SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value) {
54 if (filter && fnmatch(filter, key, FNM_NOESCAPE) != 0)
55 continue;
56
57 if (udev_builtin_add_property(dev, test, key, value) < 0)
58 return -ENOMEM;
59 n++;
60 }
61 return n;
62 }
63
64 static const char *modalias_usb(struct udev_device *dev, char *s, size_t size) {
65 const char *v, *p;
66 int vn, pn;
67
68 v = udev_device_get_sysattr_value(dev, "idVendor");
69 if (!v)
70 return NULL;
71 p = udev_device_get_sysattr_value(dev, "idProduct");
72 if (!p)
73 return NULL;
74 vn = strtol(v, NULL, 16);
75 if (vn <= 0)
76 return NULL;
77 pn = strtol(p, NULL, 16);
78 if (pn <= 0)
79 return NULL;
80 snprintf(s, size, "usb:v%04Xp%04X*", vn, pn);
81 return s;
82 }
83
84 static int udev_builtin_hwdb_search(struct udev_device *dev, struct udev_device *srcdev,
85 const char *subsystem, const char *prefix,
86 const char *filter, bool test) {
87 struct udev_device *d;
88 char s[16];
89 bool last = false;
90 int r = 0;
91
92 assert(dev);
93
94 if (!srcdev)
95 srcdev = dev;
96
97 for (d = srcdev; d && !last; d = udev_device_get_parent(d)) {
98 const char *dsubsys;
99 const char *modalias = NULL;
100
101 dsubsys = udev_device_get_subsystem(d);
102 if (!dsubsys)
103 continue;
104
105 /* look only at devices of a specific subsystem */
106 if (subsystem && !streq(dsubsys, subsystem))
107 continue;
108
109 modalias = udev_device_get_property_value(d, "MODALIAS");
110
111 if (streq(dsubsys, "usb") && streq_ptr(udev_device_get_devtype(d), "usb_device")) {
112 /* if the usb_device does not have a modalias, compose one */
113 if (!modalias)
114 modalias = modalias_usb(d, s, sizeof(s));
115
116 /* avoid looking at any parent device, they are usually just a USB hub */
117 last = true;
118 }
119
120 if (!modalias)
121 continue;
122
123 r = udev_builtin_hwdb_lookup(dev, prefix, modalias, filter, test);
124 if (r > 0)
125 break;
126 }
127
128 return r;
129 }
130
131 static int builtin_hwdb(struct udev_device *dev, int argc, char *argv[], bool test) {
132 static const struct option options[] = {
133 { "filter", required_argument, NULL, 'f' },
134 { "device", required_argument, NULL, 'd' },
135 { "subsystem", required_argument, NULL, 's' },
136 { "lookup-prefix", required_argument, NULL, 'p' },
137 {}
138 };
139 const char *filter = NULL;
140 const char *device = NULL;
141 const char *subsystem = NULL;
142 const char *prefix = NULL;
143 _cleanup_udev_device_unref_ struct udev_device *srcdev = NULL;
144
145 if (!hwdb)
146 return EXIT_FAILURE;
147
148 for (;;) {
149 int option;
150
151 option = getopt_long(argc, argv, "f:d:s:p:", options, NULL);
152 if (option == -1)
153 break;
154
155 switch (option) {
156 case 'f':
157 filter = optarg;
158 break;
159
160 case 'd':
161 device = optarg;
162 break;
163
164 case 's':
165 subsystem = optarg;
166 break;
167
168 case 'p':
169 prefix = optarg;
170 break;
171 }
172 }
173
174 /* query a specific key given as argument */
175 if (argv[optind]) {
176 if (udev_builtin_hwdb_lookup(dev, prefix, argv[optind], filter, test) > 0)
177 return EXIT_SUCCESS;
178 return EXIT_FAILURE;
179 }
180
181 /* read data from another device than the device we will store the data */
182 if (device) {
183 srcdev = udev_device_new_from_device_id(udev_device_get_udev(dev), device);
184 if (!srcdev)
185 return EXIT_FAILURE;
186 }
187
188 if (udev_builtin_hwdb_search(dev, srcdev, subsystem, prefix, filter, test) > 0)
189 return EXIT_SUCCESS;
190 return EXIT_FAILURE;
191 }
192
193 /* called at udev startup and reload */
194 static int builtin_hwdb_init(struct udev *udev) {
195 int r;
196
197 if (hwdb)
198 return 0;
199
200 r = sd_hwdb_new(&hwdb);
201 if (r < 0)
202 return r;
203
204 return 0;
205 }
206
207 /* called on udev shutdown and reload request */
208 static void builtin_hwdb_exit(struct udev *udev) {
209 hwdb = sd_hwdb_unref(hwdb);
210 }
211
212 /* called every couple of seconds during event activity; 'true' if config has changed */
213 static bool builtin_hwdb_validate(struct udev *udev) {
214 return hwdb_validate(hwdb);
215 }
216
217 const struct udev_builtin udev_builtin_hwdb = {
218 .name = "hwdb",
219 .cmd = builtin_hwdb,
220 .init = builtin_hwdb_init,
221 .exit = builtin_hwdb_exit,
222 .validate = builtin_hwdb_validate,
223 .help = "Hardware database",
224 };