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

systemd / systemd / 14815796853

02 May 2025 11:41AM UTC coverage: 72.24% (-0.003%) from 72.243%
14815796853

push

github

web-flow
Various changes to prepare for running IWYU on the repository (#37319)

These are various commits that were required to get things compiling
after running IWYU. I think all of them make sense on their own, hence
this split PR to merge them ahead of time.

81 of 96 new or added lines in 48 files covered. (84.38%)

209 existing lines in 39 files now uncovered.

297219 of 411432 relevant lines covered (72.24%)

693693.2 hits per line

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

38.96
/src/socket-activate/socket-activate.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <getopt.h>
4
#include <sys/epoll.h>
5
#include <sys/prctl.h>
6
#include <sys/wait.h>
7
#include <unistd.h>
8

9
#include "sd-daemon.h"
10

11
#include "alloc-util.h"
12
#include "build.h"
13
#include "daemon-util.h"
14
#include "env-util.h"
15
#include "errno-util.h"
16
#include "escape.h"
17
#include "fd-util.h"
18
#include "log.h"
19
#include "macro.h"
20
#include "main-func.h"
21
#include "pretty-print.h"
22
#include "process-util.h"
23
#include "signal-util.h"
24
#include "socket-netlink.h"
25
#include "socket-util.h"
26
#include "string-util.h"
27
#include "strv.h"
28
#include "terminal-util.h"
29

30
static char **arg_listen = NULL;
31
static bool arg_accept = false;
32
static int arg_socket_type = SOCK_STREAM;
33
static char **arg_setenv = NULL;
34
static char **arg_fdnames = NULL;
35
static bool arg_inetd = false;
36

37
static int add_epoll(int epoll_fd, int fd) {
1✔
38
        struct epoll_event ev = {
1✔
39
                .events = EPOLLIN,
40
                .data.fd = fd,
41
        };
42

43
        assert(epoll_fd >= 0);
1✔
44
        assert(fd >= 0);
1✔
45

46
        if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
1✔
47
                return log_error_errno(errno, "Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd, fd);
×
48

49
        return 0;
50
}
51

52
static int open_sockets(int *ret_epoll_fd) {
1✔
53
        _cleanup_close_ int epoll_fd = -EBADF;
1✔
54
        int n, r, count = 0;
1✔
55

56
        assert(ret_epoll_fd);
1✔
57

58
        n = sd_listen_fds(true);
1✔
59
        if (n < 0)
1✔
60
                return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
×
61
        if (n > 0) {
1✔
62
                log_info("Received %i descriptors via the environment.", n);
×
63

64
                for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
×
65
                        r = fd_cloexec(fd, arg_accept);
×
66
                        if (r < 0)
×
67
                                return r;
68

69
                        count++;
×
70
                }
71
        }
72

73
        /* Close logging and all other descriptors */
74
        if (arg_listen) {
1✔
75
                _cleanup_free_ int *except = new(int, n);
2✔
76
                if (!except)
1✔
77
                        return log_oom();
×
78

79
                for (int i = 0; i < n; i++)
1✔
80
                        except[i] = SD_LISTEN_FDS_START + i;
×
81

82
                log_close();
1✔
83
                log_set_open_when_needed(true);
1✔
84
                log_settle_target();
1✔
85

86
                r = close_all_fds(except, n);
1✔
87
                if (r < 0)
1✔
88
                        return log_error_errno(r, "Failed to close all file descriptors: %m");
×
89
        }
90

91
        /* Note: we leak some fd's on error here. It doesn't matter much, since the program will exit
92
         * immediately anyway, but would be a pain to fix. */
93

94
        STRV_FOREACH(address, arg_listen) {
2✔
95
                r = make_socket_fd(LOG_DEBUG, *address, arg_socket_type, (arg_accept * SOCK_CLOEXEC));
1✔
96
                if (r < 0)
1✔
97
                        return log_error_errno(r, "Failed to open '%s': %m", *address);
×
98

99
                assert(r == SD_LISTEN_FDS_START + count);
1✔
100
                count++;
1✔
101
        }
102

103
        if (arg_listen) {
1✔
104
                log_open();
1✔
105
                log_set_open_when_needed(false);
1✔
106
        }
107

108
        epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1✔
109
        if (epoll_fd < 0)
1✔
110
                return log_error_errno(errno, "Failed to create epoll object: %m");
×
111

112
        for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
2✔
113
                _cleanup_free_ char *name = NULL;
1✔
114

115
                getsockname_pretty(fd, &name);
1✔
116
                log_info("Listening on %s as %i.", strna(name), fd);
1✔
117

118
                r = add_epoll(epoll_fd, fd);
1✔
119
                if (r < 0)
1✔
120
                        return r;
×
121
        }
122

123
        *ret_epoll_fd = TAKE_FD(epoll_fd);
1✔
124
        return count;
1✔
125
}
126

127
static int exec_process(char * const *argv, int start_fd, size_t n_fds) {
1✔
128
        _cleanup_strv_free_ char **envp = NULL;
×
129
        int r;
1✔
130

131
        assert(!strv_isempty(argv));
1✔
132
        assert(start_fd >= 0);
1✔
133
        assert(n_fds > 0);
1✔
134

135
        if (arg_inetd && n_fds != 1)
1✔
136
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
137
                                       "--inetd only supported for single file descriptors.");
138

139
        FOREACH_STRING(var, "TERM", "COLORTERM", "NO_COLOR", "PATH", "USER", "HOME") {
7✔
140
                const char *n;
6✔
141

142
                n = strv_find_prefix(environ, var);
6✔
143
                if (!n)
6✔
144
                        continue;
3✔
145

146
                r = strv_extend(&envp, n);
3✔
147
                if (r < 0)
3✔
148
                        return r;
×
149
        }
150

151
        if (arg_inetd) {
1✔
152
                assert(n_fds == 1);
×
153

154
                r = rearrange_stdio(start_fd, start_fd, STDERR_FILENO); /* invalidates start_fd on success + error */
×
155
                if (r < 0)
×
156
                        return log_error_errno(r, "Failed to move fd to stdin+stdout: %m");
×
157

158
        } else {
159
                if (start_fd != SD_LISTEN_FDS_START) {
1✔
160
                        assert(n_fds == 1);
×
161

162
                        if (dup2(start_fd, SD_LISTEN_FDS_START) < 0)
×
163
                                return log_error_errno(errno, "Failed to dup connection: %m");
×
164

165
                        safe_close(start_fd);
×
166
                }
167

168
                r = strv_extendf(&envp, "LISTEN_FDS=%zu", n_fds);
1✔
169
                if (r < 0)
1✔
170
                        return r;
171

172
                r = strv_extendf(&envp, "LISTEN_PID=" PID_FMT, getpid_cached());
1✔
173
                if (r < 0)
1✔
174
                        return r;
175

176
                if (arg_fdnames) {
1✔
177
                        _cleanup_free_ char *names = NULL;
×
178
                        size_t len;
×
179

180
                        len = strv_length(arg_fdnames);
×
181
                        if (len == 1)
×
182
                                for (size_t i = 1; i < n_fds; i++) {
×
183
                                        r = strv_extend(&arg_fdnames, arg_fdnames[0]);
×
184
                                        if (r < 0)
×
185
                                                return log_oom();
×
186
                                }
187
                        else if (len != n_fds)
×
188
                                log_warning("The number of fd names is different than number of fds: %zu vs %zu", len, n_fds);
×
189

190
                        names = strv_join(arg_fdnames, ":");
×
191
                        if (!names)
×
192
                                return log_oom();
×
193

194
                        char *t = strjoin("LISTEN_FDNAMES=", names);
×
195
                        if (!t)
×
196
                                return log_oom();
×
197

198
                        r = strv_consume(&envp, t);
×
199
                        if (r < 0)
×
200
                                return r;
201
                }
202
        }
203

204
        STRV_FOREACH(s, arg_setenv) {
1✔
205
                r = strv_env_replace_strdup(&envp, *s);
×
206
                if (r < 0)
×
207
                        return r;
208
        }
209

210
        _cleanup_free_ char *joined = strv_join(argv, " ");
1✔
211
        if (!joined)
1✔
212
                return log_oom();
×
213

214
        log_info("Executing: %s", joined);
1✔
215
        execvpe(argv[0], argv, envp);
1✔
216

217
        return log_error_errno(errno, "Failed to execute '%s': %m", joined);
×
218
}
219

220
static int fork_and_exec_process(char * const *argv, int fd) {
×
221
        _cleanup_free_ char *joined = NULL;
×
222
        pid_t child_pid;
×
223
        int r;
×
224

225
        assert(!strv_isempty(argv));
×
226
        assert(fd >= 0);
×
227

228
        joined = strv_join(argv, " ");
×
229
        if (!joined)
×
230
                return log_oom();
×
231

232
        r = safe_fork("(activate)",
×
233
                      FORK_RESET_SIGNALS | FORK_DEATHSIG_SIGTERM | FORK_RLIMIT_NOFILE_SAFE | FORK_LOG,
234
                      &child_pid);
235
        if (r < 0)
×
236
                return r;
237
        if (r == 0) {
×
238
                /* In the child */
239
                (void) exec_process(argv, fd, 1);
×
240
                _exit(EXIT_FAILURE);
×
241
        }
242

243
        log_info("Spawned '%s' as PID " PID_FMT ".", joined, child_pid);
×
244
        return 0;
245
}
246

247
static int do_accept(char * const *argv, int fd) {
×
248
        _cleanup_free_ char *local = NULL, *peer = NULL;
×
249
        _cleanup_close_ int fd_accepted = -EBADF;
×
250

251
        fd_accepted = accept4(fd, NULL, NULL, 0);
×
252
        if (fd_accepted < 0) {
×
253
                if (ERRNO_IS_ACCEPT_AGAIN(errno))
×
254
                        return 0;
255

256
                return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
×
257
        }
258

259
        (void) getsockname_pretty(fd_accepted, &local);
×
260
        (void) getpeername_pretty(fd_accepted, true, &peer);
×
261
        log_info("Connection from %s to %s", strna(peer), strna(local));
×
262

263
        return fork_and_exec_process(argv, fd_accepted);
×
264
}
265

266
/* SIGCHLD handler. */
267
static void sigchld_hdl(int sig) {
×
268
        int r;
×
269

270
        PROTECT_ERRNO;
×
271

272
        for (;;) {
×
273
                siginfo_t si = {};
×
274

275
                r = waitid(P_ALL, 0, &si, WEXITED | WNOHANG);
×
276
                if (r < 0) {
×
277
                        if (errno != ECHILD)
×
278
                                log_error_errno(errno, "Failed to reap children: %m");
×
279
                        return;
×
280
                }
281
                if (si.si_pid == 0)
×
282
                        return;
283

284
                log_info("Child %d died with code %d", si.si_pid, si.si_status);
×
285
        }
286
}
287

288
static int install_chld_handler(void) {
×
289
        static const struct sigaction act = {
×
290
                .sa_flags = SA_NOCLDSTOP | SA_RESTART,
291
                .sa_handler = sigchld_hdl,
292
        };
293

294
        if (sigaction(SIGCHLD, &act, NULL) < 0)
×
295
                return log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
×
296

297
        return 0;
298
}
299

300
static int help(void) {
×
301
        _cleanup_free_ char *link = NULL;
×
302
        int r;
×
303

304
        r = terminal_urlify_man("systemd-socket-activate", "1", &link);
×
305
        if (r < 0)
×
306
                return log_oom();
×
307

308
        printf("%s [OPTIONS...]\n"
×
309
               "\n%sListen on sockets and launch child on connection.%s\n"
310
               "\nOptions:\n"
311
               "  -h --help                  Show this help and exit\n"
312
               "     --version               Print version string and exit\n"
313
               "  -l --listen=ADDR           Listen for raw connections at ADDR\n"
314
               "  -d --datagram              Listen on datagram instead of stream socket\n"
315
               "     --seqpacket             Listen on SOCK_SEQPACKET instead of stream socket\n"
316
               "  -a --accept                Spawn separate child for each connection\n"
317
               "  -E --setenv=NAME[=VALUE]   Pass an environment variable to children\n"
318
               "     --fdname=NAME[:NAME...] Specify names for file descriptors\n"
319
               "     --inetd                 Enable inetd file descriptor passing protocol\n"
320
               "\nNote: file descriptors from sd_listen_fds() will be passed through.\n"
321
               "\nSee the %s for details.\n",
322
               program_invocation_short_name,
323
               ansi_highlight(),
324
               ansi_normal(),
325
               link);
326

327
        return 0;
328
}
329

330
static int parse_argv(int argc, char *argv[]) {
1✔
331
        enum {
1✔
332
                ARG_VERSION = 0x100,
333
                ARG_FDNAME,
334
                ARG_SEQPACKET,
335
                ARG_INETD,
336
        };
337

338
        static const struct option options[] = {
1✔
339
                { "help",        no_argument,       NULL, 'h'           },
340
                { "version",     no_argument,       NULL, ARG_VERSION   },
341
                { "datagram",    no_argument,       NULL, 'd'           },
342
                { "seqpacket",   no_argument,       NULL, ARG_SEQPACKET },
343
                { "listen",      required_argument, NULL, 'l'           },
344
                { "accept",      no_argument,       NULL, 'a'           },
345
                { "setenv",      required_argument, NULL, 'E'           },
346
                { "environment", required_argument, NULL, 'E'           }, /* legacy alias */
347
                { "fdname",      required_argument, NULL, ARG_FDNAME    },
348
                { "inetd",       no_argument,       NULL, ARG_INETD     },
349
                {}
350
        };
351

352
        int c, r;
1✔
353

354
        assert(argc >= 0);
1✔
355
        assert(argv);
1✔
356

357
        /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
358
         * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
359
        optind = 0;
1✔
360
        while ((c = getopt_long(argc, argv, "+hl:aE:d", options, NULL)) >= 0)
2✔
361
                switch (c) {
1✔
362
                case 'h':
×
363
                        return help();
×
364

365
                case ARG_VERSION:
×
366
                        return version();
×
367

368
                case 'l':
1✔
369
                        r = strv_extend(&arg_listen, optarg);
1✔
370
                        if (r < 0)
1✔
371
                                return log_oom();
×
372

373
                        break;
374

375
                case 'd':
×
376
                        if (arg_socket_type == SOCK_SEQPACKET)
×
377
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
378
                                                       "--datagram may not be combined with --seqpacket.");
379

380
                        arg_socket_type = SOCK_DGRAM;
×
381
                        break;
×
382

383
                case ARG_SEQPACKET:
×
384
                        if (arg_socket_type == SOCK_DGRAM)
×
385
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
386
                                                       "--seqpacket may not be combined with --datagram.");
387

388
                        arg_socket_type = SOCK_SEQPACKET;
×
389
                        break;
×
390

391
                case 'a':
×
392
                        arg_accept = true;
×
393
                        break;
×
394

395
                case 'E':
×
396
                        r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg);
×
397
                        if (r < 0)
×
398
                                return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
×
399
                        break;
400

401
                case ARG_FDNAME: {
×
402
                        _cleanup_strv_free_ char **names = NULL;
×
403

404
                        names = strv_split(optarg, ":");
×
405
                        if (!names)
×
406
                                return log_oom();
×
407

408
                        STRV_FOREACH(s, names)
×
409
                                if (!fdname_is_valid(*s)) {
×
410
                                        _cleanup_free_ char *esc = NULL;
×
411

412
                                        esc = cescape(*s);
×
413
                                        log_warning("File descriptor name \"%s\" is not valid.", esc);
×
414
                                }
415

416
                        /* Empty optargs means one empty name */
417
                        r = strv_extend_strv(&arg_fdnames,
×
418
                                             strv_isempty(names) ? STRV_MAKE("") : names,
×
419
                                             false);
420
                        if (r < 0)
×
421
                                return log_error_errno(r, "strv_extend_strv: %m");
×
422
                        break;
×
423
                }
424

425
                case ARG_INETD:
×
426
                        arg_inetd = true;
×
427
                        break;
×
428

429
                case '?':
430
                        return -EINVAL;
431

432
                default:
×
433
                        assert_not_reached();
×
434
                }
435

436
        if (optind == argc)
1✔
437
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
438
                                       "%s: command to execute is missing.",
439
                                       program_invocation_short_name);
440

441
        if (arg_socket_type == SOCK_DGRAM && arg_accept)
1✔
442
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
443
                                       "Datagram sockets do not accept connections. "
444
                                       "The --datagram and --accept options may not be combined.");
445

446
        return 1 /* work to do */;
447
}
448

449
static int run(int argc, char **argv) {
1✔
450
        _cleanup_close_ int epoll_fd = -EBADF;
×
451
        _cleanup_strv_free_ char **exec_argv = NULL;
×
452
        int r, n;
1✔
453

454
        log_setup();
1✔
455

456
        r = parse_argv(argc, argv);
1✔
457
        if (r <= 0)
1✔
458
                return r;
459

460
        exec_argv = strv_copy(argv + optind);
1✔
461
        if (!exec_argv)
1✔
462
                return log_oom();
×
463

464
        assert(!strv_isempty(exec_argv));
1✔
465

466
        n = open_sockets(&epoll_fd);
1✔
467
        if (n < 0)
1✔
468
                return n;
469
        if (n == 0)
1✔
470
                return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No sockets to listen on specified or passed in.");
×
471

472
        /* Notify the caller that all sockets are open now. We only do this in --accept mode however,
473
         * since otherwise our process will be replaced and it's better to leave the readiness notify
474
         * to the actual payload. */
475
        _unused_ _cleanup_(notify_on_cleanup) const char *notify = NULL;
×
476
        if (arg_accept) {
1✔
477
                r = install_chld_handler();
×
478
                if (r < 0)
×
479
                        return r;
480

NEW
481
                notify = notify_start(NOTIFY_READY_MESSAGE, NOTIFY_STOPPING_MESSAGE);
×
482
        }
483

484
        for (;;) {
1✔
485
                struct epoll_event event;
1✔
486

487
                if (epoll_wait(epoll_fd, &event, 1, -1) < 0) {
1✔
488
                        if (errno == EINTR)
×
489
                                continue;
×
490

491
                        return log_error_errno(errno, "epoll_wait() failed: %m");
×
492
                }
493

494
                log_info("Communication attempt on fd %i.", event.data.fd);
1✔
495

496
                if (!arg_accept)
1✔
497
                        return exec_process(exec_argv, SD_LISTEN_FDS_START, (size_t) n);
1✔
498

499
                r = do_accept(exec_argv, event.data.fd);
×
500
                if (r < 0)
×
501
                        return r;
502
        }
503
}
504

505
DEFINE_MAIN_FUNCTION(run);
2✔
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