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

systemd / systemd / 13688105616

05 Mar 2025 11:59PM UTC coverage: 71.788% (-0.07%) from 71.855%
13688105616

push

github

yuwata
mkosi: update debian commit reference

* dfdab6b205 Install new files
* e00bee5b4a Install new files

294781 of 410628 relevant lines covered (71.79%)

715950.09 hits per line

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

54.84
/src/shared/async.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

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

9
#include "async.h"
10
#include "errno-util.h"
11
#include "fd-util.h"
12
#include "log.h"
13
#include "macro.h"
14
#include "process-util.h"
15
#include "signal-util.h"
16

17
int asynchronous_sync(PidRef *ret_pid) {
4✔
18
        int r;
4✔
19

20
        /* This forks off an invocation of fork() as a child process, in order to initiate synchronization to
21
         * disk. Note that we implement this as helper process rather than thread as we don't want the sync() to hang our
22
         * original process ever, and a thread would do that as the process can't exit with threads hanging in blocking
23
         * syscalls. */
24

25
        r = pidref_safe_fork("(sd-sync)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|(ret_pid ? 0 : FORK_DETACH), ret_pid);
4✔
26
        if (r < 0)
69✔
27
                return r;
28
        if (r == 0) {
69✔
29
                /* Child process */
30
                sync();
65✔
31
                _exit(EXIT_SUCCESS);
65✔
32
        }
33

34
        return 0;
35
}
36

37
int asynchronous_fsync(int fd, PidRef *ret_pid) {
×
38
        int r;
×
39

40
        assert(fd >= 0);
×
41
        /* Same as asynchronous_sync() above, but calls fsync() on a specific fd */
42

43
        r = pidref_safe_fork_full(
×
44
                        "(sd-fsync)",
45
                        /* stdio_fds= */ NULL,
46
                        /* except_fds= */ &fd,
47
                        /* n_except_fds= */ 1,
48
                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|(ret_pid ? 0 : FORK_DETACH),
49
                        ret_pid);
50
        if (r < 0)
×
51
                return r;
52
        if (r == 0) {
×
53
                /* Child process */
54
                (void) fsync(fd);
×
55
                _exit(EXIT_SUCCESS);
×
56
        }
57

58
        return 0;
59
}
60

61
/* We encode the fd to close in the userdata pointer as an unsigned value. The highest bit indicates whether
62
 * we need to fork again */
63
#define NEED_DOUBLE_FORK (1U << (sizeof(unsigned) * 8 - 1))
64

65
static int close_func(void *p) {
×
66
        unsigned v = PTR_TO_UINT(p);
×
67

68
        (void) prctl(PR_SET_NAME, (unsigned long*) "(sd-close)");
×
69

70
        /* Note: 💣 This function is invoked in a child process created via glibc's clone() wrapper. In such
71
         *       children memory allocation is not allowed, since glibc does not release malloc mutexes in
72
         *       clone() 💣 */
73

74
        if (v & NEED_DOUBLE_FORK) {
×
75
                pid_t pid;
×
76

77
                v &= ~NEED_DOUBLE_FORK;
×
78

79
                /* This inner child will be reparented to the subreaper/PID 1. Here we turn on SIGCHLD, so
80
                 * that the reaper knows when it's time to reap. */
81
                pid = clone_with_nested_stack(close_func, SIGCHLD|CLONE_FILES, UINT_TO_PTR(v));
×
82
                if (pid >= 0)
×
83
                        return 0;
84
        }
85

86
        close((int) v); /* no assert() here, we are in the child and the result would be eaten up anyway */
×
87
        return 0;
×
88
}
89

90
int asynchronous_close(int fd) {
9,292✔
91
        unsigned v;
9,292✔
92
        pid_t pid;
9,292✔
93
        int r;
9,292✔
94

95
        /* This is supposed to behave similar to safe_close(), but actually invoke close() asynchronously, so
96
         * that it will never block. Ideally the kernel would have an API for this, but it doesn't, so we
97
         * work around it, and hide this as a far away as we can.
98
         *
99
         * It is important to us that we don't use threads (via glibc pthread) in PID 1, hence we'll do a
100
         * minimal subprocess instead which shares our fd table via CLONE_FILES. */
101

102
        if (fd < 0)
9,292✔
103
                return -EBADF; /* already invalid */
9,292✔
104

105
        PROTECT_ERRNO;
×
106

107
        v = (unsigned) fd;
4,502✔
108

109
        /* We want to fork off a process that is automatically reaped. For that we'd usually double-fork. But
110
         * we can optimize this a bit: if we are PID 1 or a subreaper anyway (the systemd service manager
111
         * process qualifies as this), we can avoid the double forking, since the double forked process would
112
         * be reparented back to us anyway. */
113
        r = is_reaper_process();
4,502✔
114
        if (r < 0)
4,502✔
115
                log_debug_errno(r, "Cannot determine if we are a reaper process, assuming we are not: %m");
×
116
        if (r <= 0)
4,502✔
117
                v |= NEED_DOUBLE_FORK;
152✔
118

119
        pid = clone_with_nested_stack(close_func, CLONE_FILES | ((v & NEED_DOUBLE_FORK) ? 0 : SIGCHLD), UINT_TO_PTR(v));
4,502✔
120
        if (pid < 0)
4,502✔
121
                safe_close(fd); /* local fallback */
×
122
        else if (v & NEED_DOUBLE_FORK) {
4,502✔
123

124
                /* Reap the intermediate child. Key here is that we specify __WCLONE, since we didn't ask for
125
                 * any signal to be sent to us on process exit, and otherwise waitid() would refuse waiting
126
                 * then.
127
                 *
128
                 * We usually prefer calling waitid(), but before kernel 4.7 it didn't support __WCLONE while
129
                 * waitpid() did. Hence let's use waitpid() here, it's good enough for our purposes here. */
130
                for (;;)
152✔
131
                        if (waitpid(pid, NULL, __WCLONE) >= 0 || errno != EINTR)
152✔
132
                                break;
133
        }
134

135
        return -EBADF; /* return an invalidated fd */
4,502✔
136
}
137

138
void asynchronous_close_many(const int fds[], size_t n_fds) {
×
139
        assert(fds || n_fds == 0);
×
140

141
        FOREACH_ARRAY(i, fds, n_fds)
×
142
                asynchronous_close(*i);
×
143
}
×
144

145
int asynchronous_rm_rf(const char *p, RemoveFlags flags) {
188✔
146
        int r;
188✔
147

148
        assert(p);
188✔
149

150
        /* Forks off a child that destroys the specified path. This will be best effort only, i.e. the child
151
         * will attempt to do its thing, but we won't wait for it or check its success. */
152

153
        r = safe_fork("(sd-rmrf)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DETACH, NULL);
188✔
154
        if (r != 0)
1,415✔
155
                return r;
188✔
156

157
        /* Child */
158

159
        /* Let's block SIGTERM here, to grant the operation more time on e.g. final killing spree
160
         * during shutdown. If this gets stalled pid1 would eventually send SIGKILL to us. */
161
        BLOCK_SIGNALS(SIGTERM);
1,227✔
162

163
        r = rm_rf(p, flags);
1,227✔
164
        if (r < 0) {
1,227✔
165
                log_debug_errno(r, "Failed to rm -rf '%s', ignoring: %m", p);
×
166
                _exit(EXIT_FAILURE); /* This is a detached process, hence no one really cares, but who knows
×
167
                                      * maybe it's good for debugging/tracing to return an exit code
168
                                      * indicative of our failure here. */
169
        }
170

171
        _exit(EXIT_SUCCESS);
1,227✔
172
}
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