• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 13935887515

18 Mar 2025 07:10PM UTC coverage: 71.913% (-0.03%) from 71.946%
13935887515

push

github

web-flow
Several fixes and cleanups around sd_listen_fds() (#36788)

15 of 24 new or added lines in 5 files covered. (62.5%)

993 existing lines in 54 files now uncovered.

296157 of 411825 relevant lines covered (71.91%)

710024.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.5
/src/udev/udev-spawn.c
1
/* SPDX-License-Identifier: GPL-2.0-or-later */
2

3
#include "sd-event.h"
4

5
#include "device-private.h"
6
#include "device-util.h"
7
#include "fd-util.h"
8
#include "path-util.h"
9
#include "process-util.h"
10
#include "signal-util.h"
11
#include "string-util.h"
12
#include "strv.h"
13
#include "udev-builtin.h"
14
#include "udev-event.h"
15
#include "udev-spawn.h"
16
#include "udev-trace.h"
17
#include "udev-worker.h"
18

19
typedef struct Spawn {
20
        sd_device *device;
21
        const char *cmd;
22
        pid_t pid;
23
        usec_t timeout_warn_usec;
24
        usec_t timeout_usec;
25
        int timeout_signal;
26
        usec_t event_birth_usec;
27
        usec_t cmd_birth_usec;
28
        bool accept_failure;
29
        int fd_stdout;
30
        int fd_stderr;
31
        char *result;
32
        size_t result_size;
33
        size_t result_len;
34
        bool truncated;
35
} Spawn;
36

37
static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
33,531✔
38
        Spawn *spawn = ASSERT_PTR(userdata);
33,531✔
39
        char buf[4096], *p;
33,531✔
40
        size_t size;
33,531✔
41
        ssize_t l;
33,531✔
42
        int r;
33,531✔
43

44
        assert(fd == spawn->fd_stdout || fd == spawn->fd_stderr);
33,531✔
45
        assert(!spawn->result || spawn->result_len < spawn->result_size);
33,531✔
46

47
        if (fd == spawn->fd_stdout && spawn->result) {
33,531✔
48
                p = spawn->result + spawn->result_len;
18,463✔
49
                size = spawn->result_size - spawn->result_len;
18,463✔
50
        } else {
51
                p = buf;
52
                size = sizeof(buf);
53
        }
54

55
        l = read(fd, p, size - (p == buf));
33,531✔
56
        if (l < 0) {
33,531✔
57
                if (errno == EAGAIN)
×
58
                        goto reenable;
×
59

60
                log_device_error_errno(spawn->device, errno,
×
61
                                       "Failed to read stdout of '%s': %m", spawn->cmd);
62

63
                return 0;
33,531✔
64
        }
65

66
        if ((size_t) l == size) {
33,531✔
67
                log_device_warning(spawn->device, "Truncating stdout of '%s' up to %zu byte.",
2✔
68
                                   spawn->cmd, spawn->result_size);
69
                l--;
2✔
70
                spawn->truncated = true;
2✔
71
        }
72

73
        p[l] = '\0';
33,531✔
74
        if (fd == spawn->fd_stdout && spawn->result)
33,531✔
75
                spawn->result_len += l;
18,463✔
76

77
        /* Log output only if we watch stderr. */
78
        if (l > 0 && spawn->fd_stderr >= 0) {
33,531✔
79
                _cleanup_strv_free_ char **v = NULL;
10,641✔
80

81
                r = strv_split_newlines_full(&v, p, EXTRACT_RETAIN_ESCAPE);
10,641✔
82
                if (r < 0)
10,641✔
83
                        log_device_debug(spawn->device,
×
84
                                         "Failed to split output from '%s'(%s), ignoring: %m",
85
                                         spawn->cmd, fd == spawn->fd_stdout ? "out" : "err");
86

87
                STRV_FOREACH(q, v)
61,779✔
88
                        log_device_debug(spawn->device, "'%s'(%s) '%s'", spawn->cmd,
51,683✔
89
                                         fd == spawn->fd_stdout ? "out" : "err", *q);
90
        }
91

92
        if (l == 0 || spawn->truncated)
33,531✔
93
                return 0;
94

95
reenable:
10,649✔
96
        /* Re-enable the event source if we did not encounter EOF */
97

98
        r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
10,649✔
99
        if (r < 0)
10,649✔
100
                log_device_error_errno(spawn->device, r,
×
101
                                       "Failed to reactivate IO source of '%s'", spawn->cmd);
102
        return 0;
103
}
104

105
static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
×
106
        Spawn *spawn = ASSERT_PTR(userdata);
×
107

108
        DEVICE_TRACE_POINT(spawn_timeout, spawn->device, spawn->cmd);
×
109

110
        log_device_error(spawn->device, "Spawned process '%s' ["PID_FMT"] timed out after %s, killing.",
×
111
                         spawn->cmd, spawn->pid,
112
                         FORMAT_TIMESPAN(spawn->timeout_usec, USEC_PER_SEC));
113

114
        kill_and_sigcont(spawn->pid, spawn->timeout_signal);
×
115
        return 1;
×
116
}
117

118
static int on_spawn_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
×
119
        Spawn *spawn = ASSERT_PTR(userdata);
×
120

121
        log_device_warning(spawn->device, "Spawned process '%s' ["PID_FMT"] is taking longer than %s to complete.",
×
122
                           spawn->cmd, spawn->pid,
123
                           FORMAT_TIMESPAN(spawn->timeout_warn_usec, USEC_PER_SEC));
124

125
        return 1;
×
126
}
127

128
static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
11,446✔
129
        Spawn *spawn = ASSERT_PTR(userdata);
11,446✔
130
        int ret = -EIO;
11,446✔
131

132
        switch (si->si_code) {
11,446✔
133
        case CLD_EXITED:
11,444✔
134
                if (si->si_status == 0)
11,444✔
135
                        log_device_debug(spawn->device, "Process '%s' succeeded.", spawn->cmd);
11,455✔
136
                else
UNCOV
137
                        log_device_full(spawn->device, spawn->accept_failure ? LOG_DEBUG : LOG_WARNING,
×
138
                                        "Process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
139
                ret = si->si_status;
11,444✔
140
                break;
11,444✔
141
        case CLD_KILLED:
2✔
142
        case CLD_DUMPED:
143
                log_device_error(spawn->device, "Process '%s' terminated by signal %s.", spawn->cmd, signal_to_string(si->si_status));
2✔
144
                break;
2✔
145
        default:
×
146
                log_device_error(spawn->device, "Process '%s' failed due to unknown reason.", spawn->cmd);
×
147
        }
148

149
        DEVICE_TRACE_POINT(spawn_exit, spawn->device, spawn->cmd);
11,446✔
150

151
        sd_event_exit(sd_event_source_get_event(s), ret);
11,446✔
152
        return 1;
11,446✔
153
}
154

155
static int spawn_wait(Spawn *spawn) {
11,446✔
156
        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
11,446✔
157
        _cleanup_(sd_event_source_disable_unrefp) sd_event_source *sigchld_source = NULL;
11,446✔
158
        _cleanup_(sd_event_source_disable_unrefp) sd_event_source *stdout_source = NULL;
11,446✔
159
        _cleanup_(sd_event_source_disable_unrefp) sd_event_source *stderr_source = NULL;
11,446✔
160
        int r;
11,446✔
161

162
        assert(spawn);
11,446✔
163

164
        r = sd_event_new(&e);
11,446✔
165
        if (r < 0)
11,446✔
166
                return log_device_debug_errno(spawn->device, r, "Failed to allocate sd-event object: %m");
×
167

168
        if (spawn->timeout_usec != USEC_INFINITY) {
11,446✔
169
                if (spawn->timeout_warn_usec < spawn->timeout_usec) {
11,446✔
170
                        r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
22,892✔
171
                                              usec_add(spawn->cmd_birth_usec, spawn->timeout_warn_usec), USEC_PER_SEC,
172
                                              on_spawn_timeout_warning, spawn);
173
                        if (r < 0)
11,446✔
174
                                return log_device_debug_errno(spawn->device, r, "Failed to create timeout warning event source: %m");
×
175
                }
176

177
                r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
22,892✔
178
                                      usec_add(spawn->cmd_birth_usec, spawn->timeout_usec), USEC_PER_SEC,
179
                                      on_spawn_timeout, spawn);
180
                if (r < 0)
11,446✔
181
                        return log_device_debug_errno(spawn->device, r, "Failed to create timeout event source: %m");
×
182
        }
183

184
        if (spawn->fd_stdout >= 0) {
11,446✔
185
                r = sd_event_add_io(e, &stdout_source, spawn->fd_stdout, EPOLLIN, on_spawn_io, spawn);
11,446✔
186
                if (r < 0)
11,446✔
187
                        return log_device_debug_errno(spawn->device, r, "Failed to create stdio event source: %m");
×
188
                r = sd_event_source_set_enabled(stdout_source, SD_EVENT_ONESHOT);
11,446✔
189
                if (r < 0)
11,446✔
190
                        return log_device_debug_errno(spawn->device, r, "Failed to enable stdio event source: %m");
×
191
        }
192

193
        if (spawn->fd_stderr >= 0) {
11,446✔
194
                r = sd_event_add_io(e, &stderr_source, spawn->fd_stderr, EPOLLIN, on_spawn_io, spawn);
11,436✔
195
                if (r < 0)
11,436✔
196
                        return log_device_debug_errno(spawn->device, r, "Failed to create stderr event source: %m");
×
197
                r = sd_event_source_set_enabled(stderr_source, SD_EVENT_ONESHOT);
11,436✔
198
                if (r < 0)
11,436✔
199
                        return log_device_debug_errno(spawn->device, r, "Failed to enable stderr event source: %m");
×
200
        }
201

202
        r = sd_event_add_child(e, &sigchld_source, spawn->pid, WEXITED, on_spawn_sigchld, spawn);
11,446✔
203
        if (r < 0)
11,446✔
204
                return log_device_debug_errno(spawn->device, r, "Failed to create sigchild event source: %m");
×
205
        /* SIGCHLD should be processed after IO is complete */
206
        r = sd_event_source_set_priority(sigchld_source, SD_EVENT_PRIORITY_NORMAL + 1);
11,446✔
207
        if (r < 0)
11,446✔
208
                return log_device_debug_errno(spawn->device, r, "Failed to set priority to sigchild event source: %m");
×
209

210
        return sd_event_loop(e);
11,446✔
211
}
212

213
int udev_event_spawn(
11,446✔
214
                UdevEvent *event,
215
                bool accept_failure,
216
                const char *cmd,
217
                char *result,
218
                size_t result_size,
219
                bool *ret_truncated) {
220

221
        _cleanup_close_pair_ int outpipe[2] = EBADF_PAIR, errpipe[2] = EBADF_PAIR;
11,446✔
222
        _cleanup_strv_free_ char **argv = NULL;
×
223
        char **envp = NULL;
11,446✔
224
        Spawn spawn;
11,446✔
225
        pid_t pid;
11,446✔
226
        int r;
11,446✔
227

228
        assert(event);
11,446✔
229
        assert(IN_SET(event->event_mode, EVENT_UDEV_WORKER, EVENT_UDEVADM_TEST, EVENT_TEST_RULE_RUNNER, EVENT_TEST_SPAWN));
11,446✔
230
        assert(event->dev);
11,446✔
231
        assert(cmd);
11,446✔
232
        assert(result || result_size == 0);
11,446✔
233

234
        if (event->event_mode == EVENT_UDEVADM_TEST &&
11,446✔
235
            !STARTSWITH_SET(cmd, "ata_id", "cdrom_id", "dmi_memory_id", "fido_id", "mtd_probe", "scsi_id")) {
×
236
                log_device_debug(event->dev, "Running in test mode, skipping execution of '%s'.", cmd);
×
237
                result[0] = '\0';
×
238
                if (ret_truncated)
×
239
                        *ret_truncated = false;
×
240
                return 0;
×
241
        }
242

243
        int timeout_signal = event->worker ? event->worker->config.timeout_signal : SIGKILL;
11,446✔
244
        usec_t timeout_usec = event->worker ? event->worker->config.timeout_usec : DEFAULT_WORKER_TIMEOUT_USEC;
11,446✔
245
        usec_t now_usec = now(CLOCK_MONOTONIC);
11,446✔
246
        usec_t age_usec = usec_sub_unsigned(now_usec, event->birth_usec);
11,446✔
247
        usec_t cmd_timeout_usec = usec_sub_unsigned(timeout_usec, age_usec);
11,446✔
248
        if (cmd_timeout_usec <= 0)
11,446✔
249
                return log_device_warning_errno(event->dev, SYNTHETIC_ERRNO(ETIME),
×
250
                                                "The event already takes longer (%s) than the timeout (%s), skipping execution of '%s'.",
251
                                                FORMAT_TIMESPAN(age_usec, 1), FORMAT_TIMESPAN(timeout_usec, 1), cmd);
252

253
        /* pipes from child to parent */
254
        if (result || log_get_max_level() >= LOG_INFO)
11,446✔
255
                if (pipe2(outpipe, O_NONBLOCK|O_CLOEXEC) != 0)
11,446✔
256
                        return log_device_error_errno(event->dev, errno,
×
257
                                                      "Failed to create pipe for command '%s': %m", cmd);
258

259
        if (log_get_max_level() >= LOG_INFO)
11,446✔
260
                if (pipe2(errpipe, O_NONBLOCK|O_CLOEXEC) != 0)
11,436✔
261
                        return log_device_error_errno(event->dev, errno,
×
262
                                                      "Failed to create pipe for command '%s': %m", cmd);
263

264
        r = strv_split_full(&argv, cmd, NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_RETAIN_ESCAPE);
11,446✔
265
        if (r < 0)
11,446✔
266
                return log_device_error_errno(event->dev, r, "Failed to split command: %m");
×
267

268
        if (isempty(argv[0]))
11,446✔
269
                return log_device_error_errno(event->dev, SYNTHETIC_ERRNO(EINVAL),
×
270
                                              "Invalid command '%s'", cmd);
271

272
        /* allow programs in /usr/lib/udev/ to be called without the path */
273
        if (!path_is_absolute(argv[0])) {
11,446✔
274
                char *program;
4,140✔
275

276
                program = path_join(UDEVLIBEXECDIR, argv[0]);
4,140✔
277
                if (!program)
4,140✔
278
                        return log_oom();
11,446✔
279

280
                free_and_replace(argv[0], program);
4,140✔
281
        }
282

283
        r = device_get_properties_strv(event->dev, &envp);
11,446✔
284
        if (r < 0)
11,446✔
285
                return log_device_error_errno(event->dev, r, "Failed to get device properties");
×
286

287
        log_device_debug(event->dev, "Starting '%s'", cmd);
11,457✔
288

289
        r = safe_fork_full("(spawn)",
34,407✔
290
                           (int[]) { -EBADF, outpipe[WRITE_END], errpipe[WRITE_END] },
11,446✔
291
                           NULL, 0,
292
                           FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE,
293
                           &pid);
294
        if (r < 0)
22,961✔
295
                return log_device_error_errno(event->dev, r,
×
296
                                              "Failed to fork() to execute command '%s': %m", cmd);
297
        if (r == 0) {
22,961✔
298
                DEVICE_TRACE_POINT(spawn_exec, event->dev, cmd);
11,515✔
299
                execve(argv[0], argv, envp);
11,515✔
300
                _exit(EXIT_FAILURE);
11,515✔
301
        }
302

303
        /* parent closed child's ends of pipes */
304
        outpipe[WRITE_END] = safe_close(outpipe[WRITE_END]);
11,446✔
305
        errpipe[WRITE_END] = safe_close(errpipe[WRITE_END]);
11,446✔
306

307
        spawn = (Spawn) {
22,892✔
308
                .device = event->dev,
11,446✔
309
                .cmd = cmd,
310
                .pid = pid,
311
                .accept_failure = accept_failure,
312
                .timeout_warn_usec = udev_warn_timeout(cmd_timeout_usec),
11,446✔
313
                .timeout_usec = cmd_timeout_usec,
314
                .timeout_signal = timeout_signal,
315
                .event_birth_usec = event->birth_usec,
11,446✔
316
                .cmd_birth_usec = now_usec,
317
                .fd_stdout = outpipe[READ_END],
11,446✔
318
                .fd_stderr = errpipe[READ_END],
11,446✔
319
                .result = result,
320
                .result_size = result_size,
321
        };
322
        r = spawn_wait(&spawn);
11,446✔
323
        if (r < 0)
11,446✔
324
                return log_device_error_errno(event->dev, r,
2✔
325
                                              "Failed to wait for spawned command '%s': %m", cmd);
326

327
        if (result)
11,444✔
328
                result[spawn.result_len] = '\0';
9,238✔
329

330
        if (ret_truncated)
11,444✔
331
                *ret_truncated = spawn.truncated;
9,210✔
332

333
        return r; /* 0 for success, and positive if the program failed */
334
}
335

336
void udev_event_execute_run(UdevEvent *event) {
134,822✔
337
        const char *command;
134,822✔
338
        void *val;
134,822✔
339
        int r;
134,822✔
340

341
        assert(event);
134,822✔
342

343
        ORDERED_HASHMAP_FOREACH_KEY(val, command, event->run_list) {
144,881✔
344
                UdevBuiltinCommand builtin_cmd = PTR_TO_UDEV_BUILTIN_CMD(val);
10,059✔
345

346
                if (builtin_cmd >= 0) {
7,851✔
347
                        log_device_debug(event->dev, "Running built-in command \"%s\"", command);
7,871✔
348
                        r = udev_builtin_run(event, builtin_cmd, command);
7,851✔
349
                        if (r < 0)
7,851✔
350
                                log_device_debug_errno(event->dev, r, "Failed to run built-in command \"%s\", ignoring: %m", command);
8✔
351
                } else {
352
                        if (event->worker && event->worker->config.exec_delay_usec > 0) {
2,208✔
353
                                usec_t now_usec = now(CLOCK_MONOTONIC);
×
354
                                usec_t age_usec = usec_sub_unsigned(now_usec, event->birth_usec);
×
355

356
                                if (event->worker->config.exec_delay_usec >= usec_sub_unsigned(event->worker->config.timeout_usec, age_usec)) {
×
357
                                        log_device_warning(event->dev,
×
358
                                                           "Cannot delay execution of \"%s\" for %s, skipping.",
359
                                                           command, FORMAT_TIMESPAN(event->worker->config.exec_delay_usec, USEC_PER_SEC));
360
                                        continue;
×
361
                                }
362

363
                                log_device_debug(event->dev, "Delaying execution of \"%s\" for %s.",
×
364
                                                 command, FORMAT_TIMESPAN(event->worker->config.exec_delay_usec, USEC_PER_SEC));
365
                                (void) usleep_safe(event->worker->config.exec_delay_usec);
×
366
                        }
367

368
                        log_device_debug(event->dev, "Running command \"%s\"", command);
2,208✔
369

370
                        r = udev_event_spawn(event, /* accept_failure = */ false, command, NULL, 0, NULL);
2,208✔
371
                        if (r < 0)
2,208✔
372
                                log_device_warning_errno(event->dev, r, "Failed to execute '%s', ignoring: %m", command);
2✔
373
                        else if (r > 0) /* returned value is positive when program fails */
2,206✔
374
                                log_device_debug(event->dev, "Command \"%s\" returned %d (error), ignoring.", command, r);
×
375
                }
376
        }
377
}
134,822✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc