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

systemd / systemd / 26546993077

27 May 2026 08:34PM UTC coverage: 72.995% (+0.3%) from 72.667%
26546993077

push

github

bluca
test-pressure: set timeout to make not wait forever

If this runs on a slow or busy machine, then we may not get enough
pressure to trigger the event sources. In such case, the test does not
finish. It is problematic when the test is _not_ run with 'meson test',
e.g. debian/ubuntu CIs.

Let's introduce a timeout for each event loop, and skip test cases
gracefully.

8 of 12 new or added lines in 1 file covered. (66.67%)

19671 existing lines in 226 files now uncovered.

337119 of 461841 relevant lines covered (72.99%)

1326365.62 hits per line

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

85.84
/src/basic/argv-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <sched.h>
4
#include <stdlib.h>
5
#include <sys/mman.h>
6
#include <sys/prctl.h>
7

8
#include "argv-util.h"
9
#include "capability-util.h"
10
#include "errno-util.h"
11
#include "log.h"
12
#include "parse-util.h"
13
#include "path-util.h"
14
#include "process-util.h"
15
#include "string-util.h"
16
#include "strv.h"
17

18
int saved_argc = 0;
19
char **saved_argv = NULL;
20

21
void save_argc_argv(int argc, char **argv) {
80,511✔
22
        /* Protect against CVE-2021-4034 style attacks */
23
        assert_se(argc > 0);
80,511✔
24
        assert_se(argv);
80,511✔
25
        assert_se(argv[0]);
80,511✔
26

27
        saved_argc = argc;
80,511✔
28
        saved_argv = argv;
80,511✔
29
}
80,511✔
30

31
bool invoked_as(char *argv[], const char *token) {
79,773✔
32
        const char *progname = secure_getenv("SYSTEMD_INVOKED_AS");
79,773✔
33

34
        if (!progname && argv)
79,773✔
35
                progname = argv[0];
79,755✔
36

37
        if (isempty(progname) || isempty(token))
159,544✔
38
                return false;
39

40
        return strstr(last_path_component(progname), token);
79,771✔
41
}
42

43
bool invoked_by_systemd(void) {
81,371✔
44
        static int cached = -1;
81,371✔
45
        int r;
81,371✔
46

47
        if (cached >= 0)
81,371✔
48
                return cached;
842✔
49

50
        /* If the process is directly executed by PID1 (e.g. ExecStart= or generator), systemd-importd,
51
         * or systemd-homed, then $SYSTEMD_EXEC_PID= is set, and read the command line. */
52
        const char *e = getenv("SYSTEMD_EXEC_PID");
80,529✔
53
        if (!e)
80,529✔
54
                return (cached = false);
19,238✔
55

56
        if (streq(e, "*"))
61,291✔
57
                /* For testing. */
UNCOV
58
                return (cached = true);
×
59

60
        pid_t p;
61,291✔
61
        r = parse_pid(e, &p);
61,291✔
62
        if (r < 0) {
61,291✔
63
                /* We know that systemd sets the variable correctly. Something else must have set it. */
UNCOV
64
                log_debug_errno(r, "Failed to parse \"SYSTEMD_EXEC_PID=%s\", ignoring: %m", e);
×
UNCOV
65
                return (cached = false);
×
66
        }
67

68
        cached = getpid_cached() == p;
61,291✔
69
        return cached;
61,291✔
70
}
71

72
bool argv_looks_like_help(int argc, char **argv) {
30✔
73
        char **l;
30✔
74

75
        /* Scans the command line for indications the user asks for help. This is supposed to be called by
76
         * tools that do not implement getopt()-style command line parsing because they are not primarily
77
         * user-facing. Detects four ways of asking for help:
78
         *
79
         * 1. Passing zero arguments
80
         * 2. Passing "help" as first argument
81
         * 3. Passing --help as any argument
82
         * 4. Passing -h as any argument
83
         */
84

85
        if (argc <= 1)
30✔
86
                return true;
87

88
        if (streq_ptr(argv[1], "help"))
29✔
89
                return true;
90

91
        l = strv_skip(argv, 1);
28✔
92

93
        return strv_contains(l, "--help") ||
79✔
94
                strv_contains(l, "-h");
26✔
95
}
96

97
static int update_argv(const char name[], size_t l) {
191,867✔
98
        static int can_do = -1;
191,867✔
99
        int r;
191,867✔
100

101
        assert(name);
191,867✔
102
        assert(l < SIZE_MAX);
191,867✔
103

104
        if (can_do == 0)
191,867✔
105
                return 0;
106
        can_do = false; /* We'll set it to true only if the whole process works */
191,352✔
107

108
        /* Calling prctl() with PR_SET_MM_ARG_{START,END} requires CAP_SYS_RESOURCE so let's use this as quick bypass
109
         * check, to avoid calling mmap() should PR_SET_MM_ARG_{START,END} fail with EPERM later on anyway. */
110
        r = have_effective_cap(CAP_SYS_RESOURCE);
191,352✔
111
        if (r < 0)
191,352✔
UNCOV
112
                return log_debug_errno(r, "Failed to check if we have enough privileges: %m");
×
113
        if (r == 0)
191,352✔
114
                return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
1,619✔
115
                                       "Skipping PR_SET_MM, as we don't have privileges.");
116

117
        static size_t mm_size = 0;
189,733✔
118
        static char *mm = NULL;
189,733✔
119

120
        if (mm_size < l+1) {
189,733✔
121
                size_t nn_size;
28,915✔
122
                char *nn;
28,915✔
123

124
                nn_size = PAGE_ALIGN(l+1);
28,915✔
125
                if (nn_size >= SIZE_MAX)
28,915✔
UNCOV
126
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "The requested argument is too long.");
×
127

128
                nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
28,915✔
129
                if (nn == MAP_FAILED)
28,915✔
UNCOV
130
                        return log_debug_errno(errno, "mmap() failed: %m");
×
131

132
                strncpy(nn, name, nn_size);
28,915✔
133

134
                /* Now, let's tell the kernel about this new memory */
135
                if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
28,915✔
136
                        if (ERRNO_IS_PRIVILEGE(errno))
22✔
137
                                return log_debug_errno(errno, "PR_SET_MM_ARG_START failed: %m");
22✔
138

139
                        /* HACK: prctl() API is kind of dumb on this point.  The existing end address may already be
140
                         * below the desired start address, in which case the kernel may have kicked this back due
141
                         * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in
142
                         * action).  The proper solution would be to have a prctl() API that could set both start+end
143
                         * simultaneously, or at least let us query the existing address to anticipate this condition
144
                         * and respond accordingly.  For now, we can only guess at the cause of this failure and try
145
                         * a workaround--which will briefly expand the arg space to something potentially huge before
146
                         * resizing it to what we want. */
147
                        log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m");
×
148

149
                        if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) {
×
150
                                r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
×
UNCOV
151
                                (void) munmap(nn, nn_size);
×
UNCOV
152
                                return r;
×
153
                        }
154

UNCOV
155
                        if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0)
×
UNCOV
156
                                return log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m");
×
157
                } else {
158
                        /* And update the end pointer to the new end, too. If this fails, we don't really know what
159
                         * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure,
160
                         * and continue. */
161
                        if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0)
28,893✔
UNCOV
162
                                log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
×
163
                }
164

165
                if (mm)
28,893✔
UNCOV
166
                        (void) munmap(mm, mm_size);
×
167

168
                mm = nn;
28,893✔
169
                mm_size = nn_size;
28,893✔
170
        } else {
171
                strncpy(mm, name, mm_size);
160,818✔
172

173
                /* Update the end pointer, continuing regardless of any failure. */
174
                if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0) < 0)
160,818✔
175
                        log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
2✔
176
        }
177

178
        can_do = true;
189,711✔
179
        return 0;
189,711✔
180
}
181

182
int rename_process_full(const char *comm, const char *invocation) {
191,869✔
183
        bool truncated = false;
191,869✔
184

185
        /* This is a like a poor man's setproctitle(). It changes the comm field by the name specified by
186
         * 'comm', and changes argv[0] and the glibc's internally used names of the process
187
         * (program_invocation_name and program_invocation_short_name) by the name specified by 'invocation'.
188
         * For the first one a limit of 16 chars applies; to the second one in many cases one of 10 (i.e.
189
         * length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded; to the third one
190
         * 7 (i.e. the length of "systemd". If you pass a longer string it will likely be truncated.
191
         *
192
         * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */
193

194
        if (isempty(comm))
191,869✔
195
                return -EINVAL; /* let's not confuse users unnecessarily with an empty name */
196

197
        if (!is_main_thread())
191,867✔
198
                return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we
199
                                * cache things without locking, and we make assumptions that PR_SET_NAME sets the
200
                                * process name that isn't correct on any other threads */
201

202
        size_t l = strlen(comm);
191,867✔
203

204
        /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we
205
         * can use PR_SET_NAME, which sets the thread name for the calling thread. */
206
        if (prctl(PR_SET_NAME, comm) < 0)
191,867✔
UNCOV
207
                log_debug_errno(errno, "PR_SET_NAME failed: %m");
×
208
        if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */
191,867✔
209
                truncated = true;
149,073✔
210

211
        /* If nothing specified, fall back to comm. */
212
        if (isempty(invocation))
191,867✔
213
                invocation = comm;
214

215
        l = strlen(invocation);
191,867✔
216

217
        /* Second step, change glibc's ID of the process name. */
218
        if (program_invocation_name) {
191,867✔
219
                size_t k;
191,867✔
220

221
                k = strlen(program_invocation_name);
191,867✔
222
                strncpy(program_invocation_name, invocation, k);
191,867✔
223
                if (l > k)
191,867✔
224
                        truncated = true;
104,886✔
225

226
                /* Also update the short name. */
227
                char *p = strrchr(program_invocation_name, '/');
191,867✔
228
                program_invocation_short_name = p ? p + 1 : program_invocation_name;
191,867✔
229
        }
230

231
        /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but
232
         * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at
233
         * the end. This is the best option for changing /proc/self/cmdline. */
234
        (void) update_argv(invocation, l);
191,867✔
235

236
        /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if
237
         * it still looks here */
238
        if (saved_argc > 0) {
191,867✔
239
                if (saved_argv[0]) {
191,722✔
240
                        size_t k;
191,722✔
241

242
                        k = strlen(saved_argv[0]);
191,722✔
243
                        strncpy(saved_argv[0], invocation, k);
191,722✔
244
                        if (l > k)
191,722✔
245
                                truncated = true;
104,886✔
246
                }
247

248
                for (int i = 1; i < saved_argc; i++) {
462,311✔
249
                        if (!saved_argv[i])
270,589✔
250
                                break;
251

252
                        memzero(saved_argv[i], strlen(saved_argv[i]));
270,589✔
253
                }
254
        }
255

256
        return !truncated;
191,867✔
257
}
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