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

systemd / systemd / 15263807472

26 May 2025 08:53PM UTC coverage: 72.046% (-0.002%) from 72.048%
15263807472

push

github

yuwata
src/core/manager.c: log preset activity on first boot

This gives us a little more information about what units were enabled
or disabled on that first boot and will be useful for OS developers
tracking down the source of unit state.

An example with this enabled looks like:

```
NET: Registered PF_VSOCK protocol family
systemd[1]: Applying preset policy.
systemd[1]: Unit /etc/systemd/system/dnsmasq.service is masked, ignoring.
systemd[1]: Unit /etc/systemd/system/systemd-repart.service is masked, ignoring.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket'.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir.mount' → '/etc/systemd/system/var-mnt-workdir.mount'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir\x2dtmp.mount' → '/etc/systemd/system/var-mnt-workdir\x2dtmp.mount'.
systemd[1]: Created symlink '/etc/systemd/system/afterburn-sshkeys.target.requires/afterburn-sshkeys@core.service' → '/usr/lib/systemd/system/afterburn-sshkeys@.service'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket' → '/usr/lib/systemd/system/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket' → '/usr/lib/systemd/system/systemd-resolved-monitor.socket'.
systemd[1]: Populated /etc with preset unit settings.
```

Considering it only happens on first boot and not on every boot I think
the extra information is worth the extra verbosity in the logs just for
that boot.

5 of 6 new or added lines in 1 file covered. (83.33%)

5463 existing lines in 165 files now uncovered.

299151 of 415222 relevant lines covered (72.05%)

702386.45 hits per line

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

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

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

7
#include "argv-util.h"
8
#include "capability-util.h"
9
#include "errno-util.h"
10
#include "log.h"
11
#include "missing_sched.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) {
85,956✔
22
        /* Protect against CVE-2021-4034 style attacks */
23
        assert_se(argc > 0);
85,956✔
24
        assert_se(argv);
85,956✔
25
        assert_se(argv[0]);
85,956✔
26

27
        saved_argc = argc;
85,956✔
28
        saved_argv = argv;
85,956✔
29
}
85,956✔
30

31
bool invoked_as(char *argv[], const char *token) {
98,715✔
32
        if (!argv || isempty(argv[0]))
98,715✔
33
                return false;
34

35
        if (isempty(token))
98,715✔
36
                return false;
37

38
        return strstr(last_path_component(argv[0]), token);
98,715✔
39
}
40

41
bool invoked_by_systemd(void) {
90,707✔
42
        int r;
90,707✔
43

44
        /* If the process is directly executed by PID1 (e.g. ExecStart= or generator), systemd-importd,
45
         * or systemd-homed, then $SYSTEMD_EXEC_PID= is set, and read the command line. */
46
        const char *e = getenv("SYSTEMD_EXEC_PID");
90,707✔
47
        if (!e)
90,707✔
48
                return false;
90,707✔
49

50
        if (streq(e, "*"))
72,556✔
51
                /* For testing. */
52
                return true;
53

54
        pid_t p;
72,556✔
55
        r = parse_pid(e, &p);
72,556✔
56
        if (r < 0) {
72,556✔
57
                /* We know that systemd sets the variable correctly. Something else must have set it. */
UNCOV
58
                log_debug_errno(r, "Failed to parse \"SYSTEMD_EXEC_PID=%s\", ignoring: %m", e);
×
UNCOV
59
                return false;
×
60
        }
61

62
        return getpid_cached() == p;
72,556✔
63
}
64

65
bool argv_looks_like_help(int argc, char **argv) {
156✔
66
        char **l;
156✔
67

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

78
        if (argc <= 1)
156✔
79
                return true;
80

81
        if (streq_ptr(argv[1], "help"))
155✔
82
                return true;
83

84
        l = strv_skip(argv, 1);
154✔
85

86
        return strv_contains(l, "--help") ||
457✔
87
                strv_contains(l, "-h");
152✔
88
}
89

90
static int update_argv(const char name[], size_t l) {
165,991✔
91
        static int can_do = -1;
165,991✔
92
        int r;
165,991✔
93

94
        assert(name);
165,991✔
95
        assert(l < SIZE_MAX);
165,991✔
96

97
        if (can_do == 0)
165,991✔
98
                return 0;
99
        can_do = false; /* We'll set it to true only if the whole process works */
165,573✔
100

101
        /* Calling prctl() with PR_SET_MM_ARG_{START,END} requires CAP_SYS_RESOURCE so let's use this as quick bypass
102
         * check, to avoid calling mmap() should PR_SET_MM_ARG_{START,END} fail with EPERM later on anyway. */
103
        r = have_effective_cap(CAP_SYS_RESOURCE);
165,573✔
104
        if (r < 0)
165,573✔
UNCOV
105
                return log_debug_errno(r, "Failed to check if we have enough privileges: %m");
×
106
        if (r == 0)
165,573✔
107
                return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
1,103✔
108
                                       "Skipping PR_SET_MM, as we don't have privileges.");
109

110
        static size_t mm_size = 0;
164,470✔
111
        static char *mm = NULL;
164,470✔
112

113
        if (mm_size < l+1) {
164,470✔
114
                size_t nn_size;
25,959✔
115
                char *nn;
25,959✔
116

117
                nn_size = PAGE_ALIGN(l+1);
25,959✔
118
                if (nn_size >= SIZE_MAX)
25,959✔
UNCOV
119
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "The requested argument is too long.");
×
120

121
                nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
25,959✔
122
                if (nn == MAP_FAILED)
25,959✔
UNCOV
123
                        return log_debug_errno(errno, "mmap() failed: %m");
×
124

125
                strncpy(nn, name, nn_size);
25,959✔
126

127
                /* Now, let's tell the kernel about this new memory */
128
                if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
25,959✔
129
                        if (ERRNO_IS_PRIVILEGE(errno))
16✔
130
                                return log_debug_errno(errno, "PR_SET_MM_ARG_START failed: %m");
16✔
131

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

UNCOV
142
                        if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) {
×
UNCOV
143
                                r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
×
UNCOV
144
                                (void) munmap(nn, nn_size);
×
UNCOV
145
                                return r;
×
146
                        }
147

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

158
                if (mm)
25,943✔
UNCOV
159
                        (void) munmap(mm, mm_size);
×
160

161
                mm = nn;
25,943✔
162
                mm_size = nn_size;
25,943✔
163
        } else {
164
                strncpy(mm, name, mm_size);
138,511✔
165

166
                /* Update the end pointer, continuing regardless of any failure. */
167
                if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0) < 0)
138,511✔
168
                        log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
2✔
169
        }
170

171
        can_do = true;
164,454✔
172
        return 0;
164,454✔
173
}
174

175
int rename_process(const char name[]) {
165,993✔
176
        bool truncated = false;
165,993✔
177

178
        /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's
179
         * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in
180
         * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded;
181
         * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be
182
         * truncated.
183
         *
184
         * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */
185

186
        if (isempty(name))
165,993✔
187
                return -EINVAL; /* let's not confuse users unnecessarily with an empty name */
188

189
        if (!is_main_thread())
165,991✔
190
                return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we
191
                                * cache things without locking, and we make assumptions that PR_SET_NAME sets the
192
                                * process name that isn't correct on any other threads */
193

194
        size_t l = strlen(name);
165,991✔
195

196
        /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we
197
         * can use PR_SET_NAME, which sets the thread name for the calling thread. */
198
        if (prctl(PR_SET_NAME, name) < 0)
165,991✔
UNCOV
199
                log_debug_errno(errno, "PR_SET_NAME failed: %m");
×
200
        if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */
165,991✔
201
                truncated = true;
124,130✔
202

203
        /* Second step, change glibc's ID of the process name. */
204
        if (program_invocation_name) {
165,991✔
205
                size_t k;
165,991✔
206

207
                k = strlen(program_invocation_name);
165,991✔
208
                strncpy(program_invocation_name, name, k);
165,991✔
209
                if (l > k)
165,991✔
210
                        truncated = true;
91,615✔
211

212
                /* Also update the short name. */
213
                char *p = strrchr(program_invocation_name, '/');
165,991✔
214
                program_invocation_short_name = p ? p + 1 : program_invocation_name;
165,991✔
215
        }
216

217
        /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but
218
         * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at
219
         * the end. This is the best option for changing /proc/self/cmdline. */
220
        (void) update_argv(name, l);
165,991✔
221

222
        /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if
223
         * it still looks here */
224
        if (saved_argc > 0) {
165,991✔
225
                if (saved_argv[0]) {
165,842✔
226
                        size_t k;
165,842✔
227

228
                        k = strlen(saved_argv[0]);
165,842✔
229
                        strncpy(saved_argv[0], name, k);
165,842✔
230
                        if (l > k)
165,842✔
231
                                truncated = true;
91,615✔
232
                }
233

234
                for (int i = 1; i < saved_argc; i++) {
418,979✔
235
                        if (!saved_argv[i])
253,137✔
236
                                break;
237

238
                        memzero(saved_argv[i], strlen(saved_argv[i]));
253,137✔
239
                }
240
        }
241

242
        return !truncated;
165,991✔
243
}
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