Coverage Report

Created: 2025-06-24 06:40

/src/systemd/src/udev/udev-builtin-hwdb.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include <fnmatch.h>
4
#include <getopt.h>
5
#include <stdio.h>
6
7
#include "sd-hwdb.h"
8
9
#include "alloc-util.h"
10
#include "device-util.h"
11
#include "hwdb-util.h"
12
#include "parse-util.h"
13
#include "string-util.h"
14
#include "udev-builtin.h"
15
16
static sd_hwdb *hwdb = NULL;
17
18
int udev_builtin_hwdb_lookup(
19
                UdevEvent *event,
20
                const char *prefix,
21
                const char *modalias,
22
0
                const char *filter) {
23
24
0
        _cleanup_free_ char *lookup = NULL;
25
0
        const char *key, *value;
26
0
        int n = 0, r;
27
28
0
        if (!hwdb)
29
0
                return -ENOENT;
30
31
0
        if (prefix) {
32
0
                lookup = strjoin(prefix, modalias);
33
0
                if (!lookup)
34
0
                        return -ENOMEM;
35
0
                modalias = lookup;
36
0
        }
37
38
0
        SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value) {
39
0
                if (filter && fnmatch(filter, key, FNM_NOESCAPE) != 0)
40
0
                        continue;
41
42
0
                r = udev_builtin_add_property(event, key, value);
43
0
                if (r < 0)
44
0
                        return r;
45
0
                n++;
46
0
        }
47
0
        return n;
48
0
}
49
50
0
static const char* modalias_usb(sd_device *dev, char *s, size_t size) {
51
0
        const char *v, *p, *n = NULL;
52
0
        uint16_t vn, pn;
53
54
0
        if (sd_device_get_sysattr_value(dev, "idVendor", &v) < 0)
55
0
                return NULL;
56
0
        if (sd_device_get_sysattr_value(dev, "idProduct", &p) < 0)
57
0
                return NULL;
58
0
        if (safe_atoux16(v, &vn) < 0)
59
0
                return NULL;
60
0
        if (safe_atoux16(p, &pn) < 0)
61
0
                return NULL;
62
0
        (void) sd_device_get_sysattr_value(dev, "product", &n);
63
64
0
        (void) snprintf(s, size, "usb:v%04Xp%04X:%s", vn, pn, strempty(n));
65
0
        return s;
66
0
}
67
68
static int udev_builtin_hwdb_search(
69
                UdevEvent *event,
70
                sd_device *srcdev,
71
                const char *subsystem,
72
                const char *prefix,
73
0
                const char *filter) {
74
75
0
        sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
76
0
        char s[LINE_MAX];
77
0
        bool last = false;
78
0
        int r = 0;
79
80
0
        assert(event);
81
82
0
        if (!srcdev)
83
0
                srcdev = dev;
84
85
0
        for (sd_device *d = srcdev; d; ) {
86
0
                const char *modalias = NULL;
87
88
                /* look only at devices of a specific subsystem */
89
0
                if (subsystem) {
90
0
                        r = device_in_subsystem(d, subsystem);
91
0
                        if (r < 0)
92
0
                                return r;
93
0
                        if (r == 0)
94
0
                                goto next;
95
0
                }
96
97
0
                (void) sd_device_get_property_value(d, "MODALIAS", &modalias);
98
99
0
                r = device_is_subsystem_devtype(d, "usb", "usb_device");
100
0
                if (r < 0)
101
0
                        return r;
102
0
                if (r > 0) {
103
                        /* if the usb_device does not have a modalias, compose one */
104
0
                        if (!modalias)
105
0
                                modalias = modalias_usb(d, s, sizeof(s));
106
107
                        /* avoid looking at any parent device, they are usually just a USB hub */
108
0
                        last = true;
109
0
                }
110
111
0
                if (!modalias)
112
0
                        goto next;
113
114
0
                log_device_debug(dev, "hwdb modalias key: \"%s\"", modalias);
115
116
0
                r = udev_builtin_hwdb_lookup(event, prefix, modalias, filter);
117
0
                if (r > 0)
118
0
                        break;
119
120
0
                if (last)
121
0
                        break;
122
0
next:
123
0
                if (sd_device_get_parent(d, &d) < 0)
124
0
                        break;
125
0
        }
126
127
0
        return r;
128
0
}
129
130
0
static int builtin_hwdb(UdevEvent *event, int argc, char *argv[]) {
131
0
        static const struct option options[] = {
132
0
                { "filter", required_argument, NULL, 'f' },
133
0
                { "device", required_argument, NULL, 'd' },
134
0
                { "subsystem", required_argument, NULL, 's' },
135
0
                { "lookup-prefix", required_argument, NULL, 'p' },
136
0
                {}
137
0
        };
138
0
        const char *filter = NULL, *device = NULL, *subsystem = NULL, *prefix = NULL;
139
0
        _cleanup_(sd_device_unrefp) sd_device *srcdev = NULL;
140
0
        sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
141
0
        int r;
142
143
0
        if (!hwdb)
144
0
                return -EINVAL;
145
146
0
        for (;;) {
147
0
                int option;
148
149
0
                option = getopt_long(argc, argv, "f:d:s:p:", options, NULL);
150
0
                if (option == -1)
151
0
                        break;
152
153
0
                switch (option) {
154
0
                case 'f':
155
0
                        filter = optarg;
156
0
                        break;
157
158
0
                case 'd':
159
0
                        device = optarg;
160
0
                        break;
161
162
0
                case 's':
163
0
                        subsystem = optarg;
164
0
                        break;
165
166
0
                case 'p':
167
0
                        prefix = optarg;
168
0
                        break;
169
0
                }
170
0
        }
171
172
        /* query a specific key given as argument */
173
0
        if (argv[optind]) {
174
0
                r = udev_builtin_hwdb_lookup(event, prefix, argv[optind], filter);
175
0
                if (r < 0)
176
0
                        return log_device_debug_errno(dev, r, "Failed to look up hwdb: %m");
177
0
                if (r == 0)
178
0
                        return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ENODATA), "No entry found from hwdb.");
179
0
                return r;
180
0
        }
181
182
        /* read data from another device than the device we will store the data */
183
0
        if (device) {
184
0
                r = sd_device_new_from_device_id(&srcdev, device);
185
0
                if (r < 0)
186
0
                        return log_device_debug_errno(dev, r, "Failed to create sd_device object '%s': %m", device);
187
0
        }
188
189
0
        r = udev_builtin_hwdb_search(event, srcdev, subsystem, prefix, filter);
190
0
        if (r < 0)
191
0
                return log_device_debug_errno(dev, r, "Failed to look up hwdb: %m");
192
0
        if (r == 0)
193
0
                return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ENODATA), "No entry found from hwdb.");
194
0
        return r;
195
0
}
196
197
/* called at udev startup and reload */
198
0
static int builtin_hwdb_init(void) {
199
0
        int r;
200
201
0
        if (hwdb)
202
0
                return 0;
203
204
0
        r = sd_hwdb_new(&hwdb);
205
0
        if (r < 0)
206
0
                return r;
207
208
0
        log_debug("Loaded hardware database.");
209
0
        return 0;
210
0
}
211
212
/* called on udev shutdown and reload request */
213
0
static void builtin_hwdb_exit(void) {
214
0
        hwdb = sd_hwdb_unref(hwdb);
215
0
        log_debug("Unloaded hardware database.");
216
0
}
217
218
/* called every couple of seconds during event activity; 'true' if config has changed */
219
0
static bool builtin_hwdb_should_reload(void) {
220
0
        if (hwdb_should_reload(hwdb)) {
221
0
                log_debug("hwdb needs reloading.");
222
0
                return true;
223
0
        }
224
225
0
        return false;
226
0
}
227
228
const UdevBuiltin udev_builtin_hwdb = {
229
        .name = "hwdb",
230
        .cmd = builtin_hwdb,
231
        .init = builtin_hwdb_init,
232
        .exit = builtin_hwdb_exit,
233
        .should_reload = builtin_hwdb_should_reload,
234
        .help = "Hardware database",
235
};