Coverage Report

Created: 2026-10-28 06:53

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