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

systemd / systemd / 20684862027

03 Jan 2026 10:26PM UTC coverage: 72.702% (+0.03%) from 72.677%
20684862027

push

github

web-flow
core/dynamic-user: two trivial modernizations (#40264)

2 of 4 new or added lines in 1 file covered. (50.0%)

215 existing lines in 37 files now uncovered.

310139 of 426587 relevant lines covered (72.7%)

1143601.25 hits per line

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

80.77
/src/shared/notify-recv.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include "sd-event.h"
4

5
#include "alloc-util.h"
6
#include "async.h"
7
#include "constants.h"
8
#include "errno-util.h"
9
#include "fd-util.h"
10
#include "fdset.h"
11
#include "log.h"
12
#include "notify-recv.h"
13
#include "pidref.h"
14
#include "process-util.h"
15
#include "socket-util.h"
16
#include "strv.h"
17

18
int notify_socket_prepare_full(
252✔
19
                sd_event *event,
20
                int64_t priority,
21
                sd_event_io_handler_t handler,
22
                void *userdata,
23
                bool accept_fds,
24
                char **ret_path,
25
                sd_event_source **ret_event_source) {
26

27
        int r;
252✔
28

29
        assert(event);
252✔
30

31
        /* This creates an autobind AF_UNIX socket and adds an IO event source for the socket, which helps
32
         * prepare the notification socket used to communicate with worker processes. */
33

34
        _cleanup_close_ int fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
504✔
35
        if (fd < 0)
252✔
36
                return log_debug_errno(errno, "Failed to create notification socket: %m");
×
37

38
        _cleanup_free_ char *path = NULL;
252✔
39
        r = socket_autobind(fd, &path);
252✔
40
        if (r < 0)
252✔
41
                return log_debug_errno(r, "Failed to bind notification socket: %m");
×
42

43
        r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
252✔
44
        if (r < 0)
252✔
45
                return log_debug_errno(r, "Failed to enable SO_PASSCRED on notification socket: %m");
×
46

47
        /* SO_PASSPIDFD is supported since kernel v6.5. */
48
        r = setsockopt_int(fd, SOL_SOCKET, SO_PASSPIDFD, true);
252✔
49
        if (r < 0)
252✔
50
                log_debug_errno(r, "Failed to enable SO_PASSPIDFD on notification socket, ignoring: %m");
×
51

52
        if (!accept_fds) {
252✔
53
                /* since kernel v6.16 */
54
                r = setsockopt_int(fd, SOL_SOCKET, SO_PASSRIGHTS, false);
145✔
55
                if (r < 0)
145✔
56
                        log_debug_errno(r, "Failed to disable SO_PASSRIGHTS on notification socket, ignoring: %m");
13✔
57
        }
58

59
        _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
252✔
60
        r = sd_event_add_io(event, &s, fd, EPOLLIN, handler, userdata);
252✔
61
        if (r < 0)
252✔
62
                return log_debug_errno(r, "Failed to create notification event source: %m");
×
63

64
        r = sd_event_source_set_priority(s, priority);
252✔
65
        if (r < 0)
252✔
66
                return log_debug_errno(r, "Failed to set priority to notification event source: %m");
×
67

68
        r = sd_event_source_set_io_fd_own(s, true);
252✔
69
        if (r < 0)
252✔
70
                return log_debug_errno(r, "Failed to make notification event source own file descriptor: %m");
×
71
        TAKE_FD(fd);
252✔
72

73
        (void) sd_event_source_set_description(s, "notify-socket");
252✔
74

75
        if (ret_event_source)
252✔
76
                *ret_event_source = TAKE_PTR(s);
2✔
77
        else {
78
                r = sd_event_source_set_floating(s, true);
250✔
79
                if (r < 0)
250✔
80
                        return log_debug_errno(r, "Failed to make notification event source floating: %m");
×
81
        }
82

83
        if (ret_path)
252✔
84
                *ret_path = TAKE_PTR(path);
252✔
85

86
        return 0;
87
}
88

89
int notify_recv_with_fds(
51,769✔
90
                int fd,
91
                char **ret_text,
92
                struct ucred *ret_ucred,
93
                PidRef *ret_pidref,
94
                FDSet **ret_fds) {
95

96
        char buf[NOTIFY_BUFFER_MAX];
51,769✔
97
        struct iovec iovec = {
51,769✔
98
                .iov_base = buf,
99
                .iov_len = sizeof(buf),
100
        };
101
        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) +
51,769✔
102
                         CMSG_SPACE(sizeof(int)) + /* SCM_PIDFD */
103
                         CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)) control;
104
        struct msghdr msghdr = {
51,769✔
105
                .msg_iov = &iovec,
106
                .msg_iovlen = 1,
107
                .msg_control = &control,
108
                .msg_controllen = sizeof(control),
109
        };
110
        ssize_t n;
51,769✔
111
        int r;
51,769✔
112

113
        assert(fd >= 0);
51,769✔
114

115
        /* Receives a $NOTIFY_SOCKET message (aka sd_notify()). Does various validations.
116
         *
117
         * Returns -EAGAIN on recoverable errors (e.g. in case an invalid message is received, following
118
         * the logic that an invalid message shall be ignored, and treated like no message at all). */
119

120
        n = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
51,769✔
121
        if (ERRNO_IS_NEG_TRANSIENT(n))
51,769✔
122
                return -EAGAIN;
51,769✔
123
        if (n == -ECHRNG) {
51,769✔
124
                log_warning_errno(n, "Got message with truncated control data (%s fds sent?), ignoring.",
×
125
                                  ret_fds ? "too many" : "unexpected");
126
                return -EAGAIN;
×
127
        }
128
        if (n == -EXFULL) {
51,769✔
129
                log_warning_errno(n, "Got message with truncated payload data, ignoring.");
×
130
                return -EAGAIN;
×
131
        }
132
        if (n < 0)
51,769✔
133
                return log_error_errno(n, "Failed to receive notification message: %m");
×
134

135
        const struct ucred *ucred = NULL;
51,769✔
136
        _cleanup_close_ int pidfd = -EBADF;
51,769✔
137
        int *fd_array = NULL;
51,769✔
138
        size_t n_fds = 0;
51,769✔
139

140
        struct cmsghdr *cmsg;
51,769✔
141
        CMSG_FOREACH(cmsg, &msghdr) {
314,244✔
142
                if (cmsg->cmsg_level != SOL_SOCKET)
105,353✔
143
                        continue;
×
144

145
                switch (cmsg->cmsg_type) {
105,353✔
146

147
                case SCM_RIGHTS:
1,815✔
148
                        assert(!fd_array && n_fds == 0);
1,815✔
149

150
                        fd_array = CMSG_TYPED_DATA(cmsg, int);
1,815✔
151
                        n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
1,815✔
152
                        break;
1,815✔
153

154
                case SCM_PIDFD:
51,769✔
155
                        assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
51,769✔
156
                        assert(pidfd < 0);
51,769✔
157

158
                        pidfd = *CMSG_TYPED_DATA(cmsg, int);
51,769✔
159
                        break;
51,769✔
160

161
                case SCM_CREDENTIALS:
51,769✔
162
                        assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
51,769✔
163
                        assert(!ucred);
51,769✔
164

165
                        ucred = CMSG_TYPED_DATA(cmsg, struct ucred);
51,769✔
166
                        break;
167
                }
168
        }
169

170
        _cleanup_(fdset_free_asyncp) FDSet *fds = NULL;
51,769✔
171
        if (n_fds > 0) {
51,769✔
172
                assert(fd_array);
1,815✔
173

174
                if (!ret_fds) {
1,815✔
175
                        log_debug("Received fds via notification while none is expected, closing all.");
×
176
                        asynchronous_close_many(fd_array, n_fds);
×
177
                } else {
178
                        r = fdset_new_array(&fds, fd_array, n_fds);
1,815✔
179
                        if (r < 0) {
1,815✔
180
                                asynchronous_close_many(fd_array, n_fds);
×
181
                                log_warning_errno(r, "Failed to collect fds from notification, ignoring message: %m");
×
182
                                return -EAGAIN;
×
183
                        }
184
                }
185
        }
186

187
        if ((ret_ucred || ret_pidref) && (!ucred || !pid_is_valid(ucred->pid)))
51,769✔
188
                return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN),
×
189
                                         "Got notification datagram lacking valid credential information, ignoring.");
190

191
        /* As extra safety check, let's make sure the string we get doesn't contain embedded NUL bytes.
192
         * We permit one trailing NUL byte in the message, but don't expect it. */
193
        if (n > 1 && memchr(buf, 0, n - 1))
51,769✔
194
                return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Got notification message with embedded NUL, ignoring.");
×
195

196
        if (ret_text) {
51,769✔
197
                char *s = memdup_suffix0(buf, n);
51,769✔
198
                if (!s) {
51,769✔
199
                        log_oom_warning();
×
200
                        return -EAGAIN;
201
                }
202

203
                *ret_text = s;
51,769✔
204
        }
205

206
        if (ret_ucred)
51,769✔
207
                *ret_ucred = *ucred;
3,727✔
208

209
        if (ret_pidref) {
51,769✔
210
                if (pidfd >= 0)
51,769✔
211
                        *ret_pidref = (PidRef) {
51,769✔
212
                                .pid = ucred->pid,
51,769✔
213
                                .fd = TAKE_FD(pidfd),
51,769✔
214
                        };
215
                else
UNCOV
216
                        *ret_pidref = PIDREF_MAKE_FROM_PID(ucred->pid);
×
217
        }
218

219
        if (ret_fds)
51,769✔
220
                *ret_fds = TAKE_PTR(fds);
3,824✔
221

222
        return 0;
223
}
224

225
int notify_recv_with_fds_strv(
51,433✔
226
                int fd,
227
                char ***ret_list,
228
                struct ucred *ret_ucred,
229
                PidRef *ret_pidref,
230
                FDSet **ret_fds) {
231

232
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
51,433✔
233
        _cleanup_(fdset_free_asyncp) FDSet *fds = NULL;
×
234
        struct ucred ucred = UCRED_INVALID;
51,433✔
235
        _cleanup_free_ char *text = NULL;
51,433✔
236
        int r;
51,433✔
237

238
        r = notify_recv_with_fds(
146,748✔
239
                        fd,
240
                        ret_list ? &text : NULL,
241
                        ret_ucred ? &ucred : NULL,
242
                        ret_pidref ? &pidref : NULL,
243
                        ret_fds ? &fds : NULL);
244
        if (r < 0)
51,433✔
245
                return r;
246

247
        if (ret_list) {
51,433✔
248
                char **l = strv_split_newlines(text);
51,433✔
249
                if (!l) {
51,433✔
250
                        log_oom_warning();
×
251
                        return -EAGAIN;
252
                }
253

254
                *ret_list = l;
51,433✔
255
        }
256

257
        if (ret_ucred)
51,433✔
258
                *ret_ucred = ucred;
3,727✔
259
        if (ret_pidref)
51,433✔
260
                *ret_pidref = TAKE_PIDREF(pidref);
51,433✔
261
        if (ret_fds)
51,433✔
262
                *ret_fds = TAKE_PTR(fds);
3,824✔
263

264
        return 0;
265
}
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