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

systemd / systemd / 16062852561

03 Jul 2025 10:04PM UTC coverage: 72.193% (+0.1%) from 72.096%
16062852561

push

github

bluca
pcrlock: process components outside of location window properly

So far, when we tried to match a component to eent log entries we
skipped those components if they were outside of our location window.
That however is too aggressive, since it means any components that are
already in the logs, but outside of the location window will be
considered unrecognized in the logs, and thus removed from the PCR
policy.

Change things around: always try to match up all components, regardless
if inside the location window or outside, but then make it non-fatal we
can't find a component outside of the location window.

Fixes: #36079

7 of 9 new or added lines in 1 file covered. (77.78%)

4116 existing lines in 75 files now uncovered.

301219 of 417241 relevant lines covered (72.19%)

730820.5 hits per line

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

94.19
/src/test/test-process-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <linux/oom.h>
5
#include <pthread.h>
6
#include <stdlib.h>
7
#include <sys/eventfd.h>
8
#include <sys/mount.h>
9
#include <sys/personality.h>
10
#include <sys/prctl.h>
11
#include <sys/stat.h>
12
#include <sys/wait.h>
13
#include <unistd.h>
14
#include "strv.h"
15
#if HAVE_VALGRIND_VALGRIND_H
16
#include <valgrind/valgrind.h>
17
#endif
18

19
#include "sd-daemon.h"
20

21
#include "alloc-util.h"
22
#include "architecture.h"
23
#include "argv-util.h"
24
#include "errno-list.h"
25
#include "errno-util.h"
26
#include "fd-util.h"
27
#include "ioprio-util.h"
28
#include "log.h"
29
#include "namespace-util.h"
30
#include "parse-util.h"
31
#include "pidfd-util.h"
32
#include "pidref.h"
33
#include "process-util.h"
34
#include "procfs-util.h"
35
#include "rlimit-util.h"
36
#include "signal-util.h"
37
#include "stdio-util.h"
38
#include "string-util.h"
39
#include "terminal-util.h"
40
#include "tests.h"
41
#include "time-util.h"
42
#include "user-util.h"
43
#include "virt.h"
44

45
static void test_pid_get_comm_one(pid_t pid) {
2✔
46
        struct stat st;
2✔
UNCOV
47
        _cleanup_free_ char *a = NULL, *c = NULL, *d = NULL, *f = NULL, *i = NULL;
×
48
        _cleanup_free_ char *env = NULL;
×
49
        char path[STRLEN("/proc//comm") + DECIMAL_STR_MAX(pid_t)];
2✔
50
        pid_t e;
2✔
51
        uid_t u;
2✔
52
        gid_t g;
2✔
53
        dev_t h;
2✔
54
        int r;
2✔
55

56
        log_info("/* %s */", __func__);
2✔
57

58
        xsprintf(path, "/proc/"PID_FMT"/comm", pid);
2✔
59

60
        if (stat(path, &st) == 0) {
2✔
61
                ASSERT_OK(pid_get_comm(pid, &a));
2✔
62
                log_info("PID"PID_FMT" comm: '%s'", pid, a);
2✔
63
        } else
UNCOV
64
                log_warning("%s not exist.", path);
×
65

66
        ASSERT_OK(pid_get_cmdline(pid, 0, PROCESS_CMDLINE_COMM_FALLBACK, &c));
2✔
67
        log_info("PID"PID_FMT" cmdline: '%s'", pid, c);
2✔
68

69
        ASSERT_OK(pid_get_cmdline(pid, 8, 0, &d));
2✔
70
        log_info("PID"PID_FMT" cmdline truncated to 8: '%s'", pid, d);
2✔
71

72
        free(d);
2✔
73
        ASSERT_OK(pid_get_cmdline(pid, 1, 0, &d));
2✔
74
        log_info("PID"PID_FMT" cmdline truncated to 1: '%s'", pid, d);
2✔
75

76
        r = pid_get_ppid(pid, &e);
2✔
77
        if (pid == 1)
2✔
78
                ASSERT_ERROR(r, EADDRNOTAVAIL);
1✔
79
        else
80
                ASSERT_OK(r);
1✔
81
        if (r >= 0) {
1✔
82
                log_info("PID"PID_FMT" PPID: "PID_FMT, pid, e);
1✔
83
                ASSERT_GT(e, 0);
1✔
84
        }
85

86
        ASSERT_TRUE(pid_is_kernel_thread(pid) == 0 || pid != 1);
2✔
87

88
        ASSERT_OK_OR(get_process_exe(pid, &f), -EACCES);
2✔
89
        log_info("PID"PID_FMT" exe: '%s'", pid, strna(f));
2✔
90

91
        ASSERT_OK_ZERO(pid_get_uid(pid, &u));
2✔
92
        log_info("PID"PID_FMT" UID: "UID_FMT, pid, u);
2✔
93

94
        ASSERT_OK_ZERO(get_process_gid(pid, &g));
2✔
95
        log_info("PID"PID_FMT" GID: "GID_FMT, pid, g);
2✔
96

97
        ASSERT_OK_OR(get_process_environ(pid, &env), -EACCES);
2✔
98
        log_info("PID"PID_FMT" strlen(environ): %zi", pid, env ? (ssize_t)strlen(env) : (ssize_t)-errno);
2✔
99

100
        if (!detect_container() && pid == 1)
2✔
UNCOV
101
                ASSERT_ERROR(get_ctty_devnr(pid, &h), ENXIO);
×
102

103
        (void) getenv_for_pid(pid, "PATH", &i);
2✔
104
        log_info("PID"PID_FMT" $PATH: '%s'", pid, strna(i));
2✔
105
}
2✔
106

107
TEST(pid_get_comm) {
1✔
108
        if (saved_argc > 1) {
1✔
UNCOV
109
                pid_t pid = 0;
×
110

UNCOV
111
                (void) parse_pid(saved_argv[1], &pid);
×
112
                test_pid_get_comm_one(pid);
×
113
        } else {
114
                if (sd_booted() > 0)
1✔
115
                        test_pid_get_comm_one(1);
1✔
116
                test_pid_get_comm_one(getpid());
1✔
117
        }
118
}
1✔
119

120
static void test_pid_get_cmdline_one(pid_t pid) {
47✔
UNCOV
121
        _cleanup_free_ char *c = NULL, *d = NULL, *e = NULL, *f = NULL, *g = NULL, *h = NULL, *joined = NULL;
×
122
        _cleanup_strv_free_ char **strv_a = NULL, **strv_b = NULL;
47✔
123
        int r;
47✔
124

125
        r = pid_get_cmdline(pid, SIZE_MAX, 0, &c);
47✔
126
        log_info("PID "PID_FMT": %s", pid, r >= 0 ? c : errno_to_name(r));
47✔
127

128
        r = pid_get_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &d);
47✔
129
        log_info("      %s", r >= 0 ? d : errno_to_name(r));
47✔
130

131
        r = pid_get_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &e);
47✔
132
        log_info("      %s", r >= 0 ? e : errno_to_name(r));
47✔
133

134
        r = pid_get_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE | PROCESS_CMDLINE_COMM_FALLBACK, &f);
47✔
135
        log_info("      %s", r >= 0 ? f : errno_to_name(r));
47✔
136

137
        r = pid_get_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &g);
47✔
138
        log_info("      %s", r >= 0 ? g : errno_to_name(r));
47✔
139

140
        r = pid_get_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX | PROCESS_CMDLINE_COMM_FALLBACK, &h);
47✔
141
        log_info("      %s", r >= 0 ? h : errno_to_name(r));
47✔
142

143
        r = pid_get_cmdline_strv(pid, 0, &strv_a);
47✔
144
        if (r >= 0)
47✔
145
                ASSERT_NOT_NULL((joined = strv_join(strv_a, "\", \"")));
43✔
146
        log_info("      \"%s\"", r >= 0 ? joined : errno_to_name(r));
47✔
147

148
        joined = mfree(joined);
47✔
149

150
        r = pid_get_cmdline_strv(pid, PROCESS_CMDLINE_COMM_FALLBACK, &strv_b);
47✔
151
        if (r >= 0)
47✔
152
                ASSERT_NOT_NULL((joined = strv_join(strv_b, "\", \"")));
47✔
153
        log_info("      \"%s\"", r >= 0 ? joined : errno_to_name(r));
47✔
154
}
47✔
155

156
TEST(pid_get_cmdline) {
1✔
157
        _cleanup_closedir_ DIR *d = NULL;
2✔
158
        int r;
1✔
159

160
        ASSERT_OK(proc_dir_open(&d));
1✔
161

162
        for (;;) {
47✔
163
                pid_t pid;
48✔
164
                ASSERT_OK(r = proc_dir_read(d, &pid));
48✔
165

166
                if (r == 0) /* EOF */
48✔
167
                        break;
168

169
                test_pid_get_cmdline_one(pid);
47✔
170
        }
171
}
1✔
172

173
static void test_pid_get_comm_escape_one(const char *input, const char *output) {
10✔
174
        _cleanup_free_ char *n = NULL;
20✔
175

176
        log_debug("input: <%s> — output: <%s>", input, output);
10✔
177

178
        ASSERT_OK_ERRNO(prctl(PR_SET_NAME, input));
10✔
179
        ASSERT_OK(pid_get_comm(0, &n));
10✔
180

181
        log_debug("got: <%s>", n);
10✔
182

183
        ASSERT_STREQ(n, output);
10✔
184
}
10✔
185

186
TEST(pid_get_comm_escape) {
1✔
187
        _cleanup_free_ char *saved = NULL;
2✔
188

189
        ASSERT_OK(pid_get_comm(0, &saved));
1✔
190

191
        test_pid_get_comm_escape_one("", "");
1✔
192
        test_pid_get_comm_escape_one("foo", "foo");
1✔
193
        test_pid_get_comm_escape_one("012345678901234", "012345678901234");
1✔
194
        test_pid_get_comm_escape_one("0123456789012345", "012345678901234");
1✔
195
        test_pid_get_comm_escape_one("äöüß", "\\303\\244\\303\\266\\303\\274\\303\\237");
1✔
196
        test_pid_get_comm_escape_one("xäöüß", "x\\303\\244\\303\\266\\303\\274\\303\\237");
1✔
197
        test_pid_get_comm_escape_one("xxäöüß", "xx\\303\\244\\303\\266\\303\\274\\303\\237");
1✔
198
        test_pid_get_comm_escape_one("xxxäöüß", "xxx\\303\\244\\303\\266\\303\\274\\303\\237");
1✔
199
        test_pid_get_comm_escape_one("xxxxäöüß", "xxxx\\303\\244\\303\\266\\303\\274\\303\\237");
1✔
200
        test_pid_get_comm_escape_one("xxxxxäöüß", "xxxxx\\303\\244\\303\\266\\303\\274\\303\\237");
1✔
201

202
        ASSERT_OK_ERRNO(prctl(PR_SET_NAME, saved));
1✔
203
}
1✔
204

205
TEST(pid_is_unwaited) {
1✔
206
        pid_t pid;
1✔
207

208
        pid = fork();
1✔
209
        ASSERT_OK_ERRNO(pid);
2✔
210
        if (pid == 0) {
2✔
211
                _exit(EXIT_SUCCESS);
1✔
212
        } else {
213
                int status;
1✔
214

215
                ASSERT_OK_EQ_ERRNO(waitpid(pid, &status, 0), pid);
1✔
216
                ASSERT_OK_ZERO(pid_is_unwaited(pid));
1✔
217
        }
218
        ASSERT_OK_POSITIVE(pid_is_unwaited(getpid_cached()));
1✔
219
        ASSERT_FAIL(pid_is_unwaited(-1));
1✔
220
}
1✔
221

222
TEST(pid_is_alive) {
1✔
223
        pid_t pid;
1✔
224

225
        pid = fork();
1✔
226
        ASSERT_OK_ERRNO(pid);
2✔
227
        if (pid == 0) {
2✔
228
                _exit(EXIT_SUCCESS);
1✔
229
        } else {
230
                int status;
1✔
231

232
                ASSERT_OK_EQ_ERRNO(waitpid(pid, &status, 0), pid);
1✔
233
                ASSERT_OK_ZERO(pid_is_alive(pid));
1✔
234
        }
235
        ASSERT_OK_POSITIVE(pid_is_alive(getpid_cached()));
1✔
236
        ASSERT_FAIL(pid_is_alive(-1));
1✔
237
}
1✔
238

239
TEST(personality) {
1✔
240
        ASSERT_NOT_NULL(personality_to_string(PER_LINUX));
1✔
241
        ASSERT_NULL(personality_to_string(PERSONALITY_INVALID));
1✔
242

243
        ASSERT_STREQ(personality_to_string(PER_LINUX), architecture_to_string(native_architecture()));
1✔
244

245
        ASSERT_EQ(personality_from_string(personality_to_string(PER_LINUX)), (unsigned long) PER_LINUX);
1✔
246
        ASSERT_EQ(personality_from_string(architecture_to_string(native_architecture())), (unsigned long) PER_LINUX);
1✔
247

248
#ifdef __x86_64__
249
        ASSERT_STREQ(personality_to_string(PER_LINUX), "x86-64");
1✔
250
        ASSERT_STREQ(personality_to_string(PER_LINUX32), "x86");
1✔
251

252
        ASSERT_EQ(personality_from_string("x86-64"), (unsigned long) PER_LINUX);
1✔
253
        ASSERT_EQ(personality_from_string("x86"), (unsigned long) PER_LINUX32);
1✔
254
        ASSERT_EQ(personality_from_string("ia64"), PERSONALITY_INVALID);
1✔
255
        ASSERT_EQ(personality_from_string(NULL), PERSONALITY_INVALID);
1✔
256

257
        ASSERT_EQ(personality_from_string(personality_to_string(PER_LINUX32)), (unsigned long) PER_LINUX32);
1✔
258
#endif
259
}
1✔
260

261
TEST(pid_get_cmdline_harder) {
1✔
262
        char path[] = "/tmp/test-cmdlineXXXXXX";
1✔
263
        _cleanup_close_ int fd = -EBADF;
1✔
264
        _cleanup_free_ char *line = NULL;
1✔
265
        _cleanup_strv_free_ char **args = NULL;
1✔
266
        pid_t pid;
1✔
267
        int r;
1✔
268

269
        if (geteuid() != 0) {
1✔
UNCOV
270
                log_info("Skipping %s: not root", __func__);
×
271
                return;
×
272
        }
273

274
        if (!have_namespaces()) {
1✔
UNCOV
275
                log_notice("Testing without namespaces, skipping %s", __func__);
×
276
                return;
×
277
        }
278

279
#if HAVE_VALGRIND_VALGRIND_H
280
        /* valgrind patches open(/proc//cmdline)
281
         * so, test_pid_get_cmdline_harder fails always
282
         * See https://github.com/systemd/systemd/pull/3555#issuecomment-226564908 */
283
        if (RUNNING_ON_VALGRIND) {
284
                log_info("Skipping %s: running on valgrind", __func__);
285
                return;
286
        }
287
#endif
288

289
        pid = fork();
1✔
290
        if (pid > 0) {
2✔
291
                siginfo_t si;
1✔
292

293
                (void) wait_for_terminate(pid, &si);
1✔
294

295
                ASSERT_EQ(si.si_code, CLD_EXITED);
1✔
296
                ASSERT_OK_ZERO(si.si_status);
1✔
297

298
                return;
1✔
299
        }
300

301
        ASSERT_OK_ZERO(pid);
1✔
302

303
        r = detach_mount_namespace();
1✔
304
        if (r < 0) {
1✔
UNCOV
305
                log_warning_errno(r, "detach mount namespace failed: %m");
×
306
                if (!ERRNO_IS_PRIVILEGE(r))
×
307
                        ASSERT_OK(r);
×
308
                return;
309
        }
310

311
        fd = mkostemp(path, O_CLOEXEC);
1✔
312
        ASSERT_OK_ERRNO(fd);
1✔
313

314
        /* Note that we don't unmount the following bind-mount at the end of the test because the kernel
315
         * will clear up its /proc/PID/ hierarchy automatically as soon as the test stops. */
316
        if (mount(path, "/proc/self/cmdline", "bind", MS_BIND, NULL) < 0) {
1✔
317
                /* This happens under selinux… Abort the test in this case. */
UNCOV
318
                log_warning_errno(errno, "mount(..., \"/proc/self/cmdline\", \"bind\", ...) failed: %m");
×
319
                ASSERT_TRUE(IN_SET(errno, EPERM, EACCES));
×
320
                return;
321
        }
322

323
        /* Set RLIMIT_STACK to infinity to test we don't try to allocate unnecessarily large values to read
324
         * the cmdline. */
325
        if (setrlimit(RLIMIT_STACK, &RLIMIT_MAKE_CONST(RLIM_INFINITY)) < 0)
1✔
UNCOV
326
                log_warning("Testing without RLIMIT_STACK=infinity");
×
327

328
        ASSERT_OK_ERRNO(unlink(path));
1✔
329

330
        ASSERT_OK_ERRNO(prctl(PR_SET_NAME, "testa"));
1✔
331

332
        ASSERT_ERROR(pid_get_cmdline(0, SIZE_MAX, 0, &line), ENOENT);
1✔
333

334
        ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
335
        log_debug("'%s'", line);
1✔
336
        ASSERT_STREQ(line, "[testa]");
1✔
337
        line = mfree(line);
1✔
338

339
        ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK | PROCESS_CMDLINE_QUOTE, &line));
1✔
340
        log_debug("'%s'", line);
1✔
341
        ASSERT_STREQ(line, "\"[testa]\""); /* quoting is enabled here */
1✔
342
        line = mfree(line);
1✔
343

344
        ASSERT_OK(pid_get_cmdline(0, 0, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
345
        log_debug("'%s'", line);
1✔
346
        ASSERT_STREQ(line, "");
1✔
347
        line = mfree(line);
1✔
348

349
        ASSERT_OK(pid_get_cmdline(0, 1, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
350
        ASSERT_STREQ(line, "…");
1✔
351
        line = mfree(line);
1✔
352

353
        ASSERT_OK(pid_get_cmdline(0, 2, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
354
        ASSERT_STREQ(line, "[…");
1✔
355
        line = mfree(line);
1✔
356

357
        ASSERT_OK(pid_get_cmdline(0, 3, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
358
        ASSERT_STREQ(line, "[t…");
1✔
359
        line = mfree(line);
1✔
360

361
        ASSERT_OK(pid_get_cmdline(0, 4, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
362
        ASSERT_STREQ(line, "[te…");
1✔
363
        line = mfree(line);
1✔
364

365
        ASSERT_OK(pid_get_cmdline(0, 5, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
366
        ASSERT_STREQ(line, "[tes…");
1✔
367
        line = mfree(line);
1✔
368

369
        ASSERT_OK(pid_get_cmdline(0, 6, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
370
        ASSERT_STREQ(line, "[test…");
1✔
371
        line = mfree(line);
1✔
372

373
        ASSERT_OK(pid_get_cmdline(0, 7, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
374
        ASSERT_STREQ(line, "[testa]");
1✔
375
        line = mfree(line);
1✔
376

377
        ASSERT_OK(pid_get_cmdline(0, 8, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
378
        ASSERT_STREQ(line, "[testa]");
1✔
379
        line = mfree(line);
1✔
380

381
        ASSERT_OK(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args));
1✔
382
        ASSERT_TRUE(strv_equal(args, STRV_MAKE("[testa]")));
1✔
383
        args = strv_free(args);
1✔
384

385
        /* Test with multiple arguments that don't require quoting */
386

387
        ASSERT_OK_EQ_ERRNO(write(fd, "foo\0bar", 8), 8);
1✔
388

389
        ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, 0, &line));
1✔
390
        log_debug("'%s'", line);
1✔
391
        ASSERT_STREQ(line, "foo bar");
1✔
392
        line = mfree(line);
1✔
393

394
        ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
395
        ASSERT_STREQ(line, "foo bar");
1✔
396
        line = mfree(line);
1✔
397

398
        ASSERT_OK(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args));
1✔
399
        ASSERT_TRUE(strv_equal(args, STRV_MAKE("foo", "bar")));
1✔
400
        args = strv_free(args);
1✔
401

402
        ASSERT_OK_EQ_ERRNO(write(fd, "quux", 4), 4);
1✔
403
        ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, 0, &line));
1✔
404
        log_debug("'%s'", line);
1✔
405
        ASSERT_STREQ(line, "foo bar quux");
1✔
406
        line = mfree(line);
1✔
407

408
        ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
409
        log_debug("'%s'", line);
1✔
410
        ASSERT_STREQ(line, "foo bar quux");
1✔
411
        line = mfree(line);
1✔
412

413
        ASSERT_OK(pid_get_cmdline(0, 1, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
414
        log_debug("'%s'", line);
1✔
415
        ASSERT_STREQ(line, "…");
1✔
416
        line = mfree(line);
1✔
417

418
        ASSERT_OK(pid_get_cmdline(0, 2, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
419
        log_debug("'%s'", line);
1✔
420
        ASSERT_STREQ(line, "f…");
1✔
421
        line = mfree(line);
1✔
422

423
        ASSERT_OK(pid_get_cmdline(0, 3, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
424
        log_debug("'%s'", line);
1✔
425
        ASSERT_STREQ(line, "fo…");
1✔
426
        line = mfree(line);
1✔
427

428
        ASSERT_OK(pid_get_cmdline(0, 4, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
429
        log_debug("'%s'", line);
1✔
430
        ASSERT_STREQ(line, "foo…");
1✔
431
        line = mfree(line);
1✔
432

433
        ASSERT_OK(pid_get_cmdline(0, 5, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
434
        log_debug("'%s'", line);
1✔
435
        ASSERT_STREQ(line, "foo …");
1✔
436
        line = mfree(line);
1✔
437

438
        ASSERT_OK(pid_get_cmdline(0, 6, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
439
        log_debug("'%s'", line);
1✔
440
        ASSERT_STREQ(line, "foo b…");
1✔
441
        line = mfree(line);
1✔
442

443
        ASSERT_OK(pid_get_cmdline(0, 7, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
444
        log_debug("'%s'", line);
1✔
445
        ASSERT_STREQ(line, "foo ba…");
1✔
446
        line = mfree(line);
1✔
447

448
        ASSERT_OK(pid_get_cmdline(0, 8, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
449
        log_debug("'%s'", line);
1✔
450
        ASSERT_STREQ(line, "foo bar…");
1✔
451
        line = mfree(line);
1✔
452

453
        ASSERT_OK(pid_get_cmdline(0, 9, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
454
        log_debug("'%s'", line);
1✔
455
        ASSERT_STREQ(line, "foo bar …");
1✔
456
        line = mfree(line);
1✔
457

458
        ASSERT_OK(pid_get_cmdline(0, 10, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
459
        log_debug("'%s'", line);
1✔
460
        ASSERT_STREQ(line, "foo bar q…");
1✔
461
        line = mfree(line);
1✔
462

463
        ASSERT_OK(pid_get_cmdline(0, 11, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
464
        log_debug("'%s'", line);
1✔
465
        ASSERT_STREQ(line, "foo bar qu…");
1✔
466
        line = mfree(line);
1✔
467

468
        ASSERT_OK(pid_get_cmdline(0, 12, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
469
        log_debug("'%s'", line);
1✔
470
        ASSERT_STREQ(line, "foo bar quux");
1✔
471
        line = mfree(line);
1✔
472

473
        ASSERT_OK(pid_get_cmdline(0, 13, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
474
        log_debug("'%s'", line);
1✔
475
        ASSERT_STREQ(line, "foo bar quux");
1✔
476
        line = mfree(line);
1✔
477

478
        ASSERT_OK(pid_get_cmdline(0, 14, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
479
        log_debug("'%s'", line);
1✔
480
        ASSERT_STREQ(line, "foo bar quux");
1✔
481
        line = mfree(line);
1✔
482

483
        ASSERT_OK(pid_get_cmdline(0, 1000, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
484
        log_debug("'%s'", line);
1✔
485
        ASSERT_STREQ(line, "foo bar quux");
1✔
486
        line = mfree(line);
1✔
487

488
        ASSERT_OK(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args));
1✔
489
        ASSERT_TRUE(strv_equal(args, STRV_MAKE("foo", "bar", "quux")));
1✔
490
        args = strv_free(args);
1✔
491

492
        ASSERT_OK_ERRNO(ftruncate(fd, 0));
1✔
493
        ASSERT_OK_ERRNO(prctl(PR_SET_NAME, "aaaa bbbb cccc"));
1✔
494

495
        ASSERT_ERROR(pid_get_cmdline(0, SIZE_MAX, 0, &line), ENOENT);
1✔
496

497
        ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
498
        log_debug("'%s'", line);
1✔
499
        ASSERT_STREQ(line, "[aaaa bbbb cccc]");
1✔
500
        line = mfree(line);
1✔
501

502
        ASSERT_OK(pid_get_cmdline(0, 10, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
503
        log_debug("'%s'", line);
1✔
504
        ASSERT_STREQ(line, "[aaaa bbb…");
1✔
505
        line = mfree(line);
1✔
506

507
        ASSERT_OK(pid_get_cmdline(0, 11, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
508
        log_debug("'%s'", line);
1✔
509
        ASSERT_STREQ(line, "[aaaa bbbb…");
1✔
510
        line = mfree(line);
1✔
511

512
        ASSERT_OK(pid_get_cmdline(0, 12, PROCESS_CMDLINE_COMM_FALLBACK, &line));
1✔
513
        log_debug("'%s'", line);
1✔
514
        ASSERT_STREQ(line, "[aaaa bbbb …");
1✔
515
        line = mfree(line);
1✔
516

517
        ASSERT_OK(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args));
1✔
518
        ASSERT_TRUE(strv_equal(args, STRV_MAKE("[aaaa bbbb cccc]")));
1✔
519
        args = strv_free(args);
1✔
520

521
        /* Test with multiple arguments that do require quoting */
522

523
#define CMDLINE1 "foo\0'bar'\0\"bar$\"\0x y z\0!``\0"
524
#define EXPECT1  "foo \"'bar'\" \"\\\"bar\\$\\\"\" \"x y z\" \"!\\`\\`\""
525
#define EXPECT1p "foo $'\\'bar\\'' $'\"bar$\"' $'x y z' $'!``'"
526
#define EXPECT1v STRV_MAKE("foo", "'bar'", "\"bar$\"", "x y z", "!``")
527

528
        ASSERT_OK_ZERO_ERRNO(lseek(fd, SEEK_SET, 0));
1✔
529
        ASSERT_OK_EQ_ERRNO(write(fd, CMDLINE1, sizeof(CMDLINE1)), (ssize_t) sizeof(CMDLINE1));
1✔
530
        ASSERT_OK_ZERO_ERRNO(ftruncate(fd, sizeof(CMDLINE1)));
1✔
531

532
        ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &line));
1✔
533
        log_debug("got: ==%s==", line);
1✔
534
        log_debug("exp: ==%s==", EXPECT1);
1✔
535
        ASSERT_STREQ(line, EXPECT1);
1✔
536
        line = mfree(line);
1✔
537

538
        ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &line));
1✔
539
        log_debug("got: ==%s==", line);
1✔
540
        log_debug("exp: ==%s==", EXPECT1p);
1✔
541
        ASSERT_STREQ(line, EXPECT1p);
1✔
542
        line = mfree(line);
1✔
543

544
        ASSERT_OK(pid_get_cmdline_strv(0, 0, &args));
1✔
545
        ASSERT_TRUE(strv_equal(args, EXPECT1v));
1✔
546
        args = strv_free(args);
1✔
547

548
#define CMDLINE2 "foo\0\1\2\3\0\0"
549
#define EXPECT2  "foo \"\\001\\002\\003\""
550
#define EXPECT2p "foo $'\\001\\002\\003'"
551
#define EXPECT2v STRV_MAKE("foo", "\1\2\3")
552

553
        ASSERT_OK_ZERO_ERRNO(lseek(fd, SEEK_SET, 0));
1✔
554
        ASSERT_OK_EQ_ERRNO(write(fd, CMDLINE2, sizeof(CMDLINE2)), (ssize_t) sizeof(CMDLINE2));
1✔
555
        ASSERT_OK_ZERO_ERRNO(ftruncate(fd, sizeof CMDLINE2));
1✔
556

557
        ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &line));
1✔
558
        log_debug("got: ==%s==", line);
1✔
559
        log_debug("exp: ==%s==", EXPECT2);
1✔
560
        ASSERT_STREQ(line, EXPECT2);
1✔
561
        line = mfree(line);
1✔
562

563
        ASSERT_OK(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &line));
1✔
564
        log_debug("got: ==%s==", line);
1✔
565
        log_debug("exp: ==%s==", EXPECT2p);
1✔
566
        ASSERT_STREQ(line, EXPECT2p);
1✔
567
        line = mfree(line);
1✔
568

569
        ASSERT_OK(pid_get_cmdline_strv(0, 0, &args));
1✔
570
        ASSERT_TRUE(strv_equal(args, EXPECT2v));
1✔
571
        args = strv_free(args);
1✔
572

573
        safe_close(fd);
1✔
574
        _exit(EXIT_SUCCESS);
1✔
575
}
576

577
TEST(getpid_cached) {
1✔
578
        siginfo_t si;
1✔
579
        pid_t a, b, c, d, e, f, child;
1✔
580

581
        a = getpid();
1✔
582
        b = getpid_cached();
1✔
583
        c = getpid();
1✔
584

585
        ASSERT_EQ(a, b);
1✔
586
        ASSERT_EQ(a, c);
1✔
587

588
        child = fork();
1✔
589
        ASSERT_OK_ERRNO(child);
2✔
590

591
        if (child == 0) {
2✔
592
                /* In child */
593
                a = getpid();
1✔
594
                b = getpid_cached();
1✔
595
                c = getpid();
1✔
596

597
                ASSERT_EQ(a, b);
1✔
598
                ASSERT_EQ(a, c);
1✔
599
                _exit(EXIT_SUCCESS);
1✔
600
        }
601

602
        d = getpid();
1✔
603
        e = getpid_cached();
1✔
604
        f = getpid();
1✔
605

606
        ASSERT_EQ(a, d);
1✔
607
        ASSERT_EQ(a, e);
1✔
608
        ASSERT_EQ(a, f);
1✔
609

610
        ASSERT_OK(wait_for_terminate(child, &si));
1✔
611
        ASSERT_EQ(si.si_status, 0);
1✔
612
        ASSERT_EQ(si.si_code, CLD_EXITED);
1✔
613
}
1✔
614

615
TEST(getpid_measure) {
1✔
616
        usec_t t, q;
1✔
617

618
        unsigned long long iterations = slow_tests_enabled() ? 1000000 : 1000;
1✔
619

620
        log_info("/* %s (%llu iterations) */", __func__, iterations);
1✔
621

622
        t = now(CLOCK_MONOTONIC);
1✔
623
        for (unsigned long long i = 0; i < iterations; i++)
1,001✔
624
                (void) getpid();
1,000✔
625
        q = now(CLOCK_MONOTONIC) - t;
1✔
626

627
        log_info(" glibc getpid(): %lf μs each", (double) q / iterations);
1✔
628

629
        iterations *= 50; /* _cached() is about 50 times faster, so we need more iterations */
1✔
630

631
        t = now(CLOCK_MONOTONIC);
1✔
632
        for (unsigned long long i = 0; i < iterations; i++)
50,001✔
633
                (void) getpid_cached();
50,000✔
634
        q = now(CLOCK_MONOTONIC) - t;
1✔
635

636
        log_info("getpid_cached(): %lf μs each", (double) q / iterations);
1✔
637
}
1✔
638

639
TEST(safe_fork) {
1✔
640
        siginfo_t status;
1✔
641
        pid_t pid;
1✔
642
        int r;
1✔
643

644
        BLOCK_SIGNALS(SIGCHLD);
2✔
645

646
        r = safe_fork("(test-child)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_REOPEN_LOG, &pid);
1✔
647
        ASSERT_OK(r);
2✔
648

649
        if (r == 0) {
2✔
650
                /* child */
651
                usleep_safe(100 * USEC_PER_MSEC);
1✔
652

653
                _exit(88);
1✔
654
        }
655

656
        ASSERT_OK(wait_for_terminate(pid, &status));
1✔
657
        ASSERT_EQ(status.si_code, CLD_EXITED);
1✔
658
        ASSERT_EQ(status.si_status, 88);
1✔
659

660
        _cleanup_(pidref_done) PidRef child = PIDREF_NULL;
1✔
661
        r = pidref_safe_fork("(test-child)", FORK_DETACH, &child);
1✔
662
        if (r == 0) {
1✔
663
                /* Don't freeze so this doesn't linger around forever in case something goes wrong. */
UNCOV
664
                usleep_safe(100 * USEC_PER_SEC);
×
665
                _exit(EXIT_SUCCESS);
×
666
        }
667

668
        ASSERT_OK_POSITIVE(r);
1✔
669
        ASSERT_GT(child.pid, 0);
1✔
670
        ASSERT_OK(pidref_get_ppid(&child, &pid));
1✔
671
        ASSERT_OK(pidref_kill(&child, SIGKILL));
1✔
672

673
        if (is_reaper_process())
1✔
UNCOV
674
                ASSERT_EQ(pid, getpid_cached());
×
675
        else
676
                ASSERT_NE(pid, getpid_cached());
1✔
677
}
1✔
678

679
TEST(pid_to_ptr) {
1✔
680
        ASSERT_EQ(PTR_TO_PID(NULL), 0);
1✔
681
        ASSERT_NULL(PID_TO_PTR(0));
1✔
682

683
        ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(1)), 1);
1✔
684
        ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(2)), 2);
1✔
685
        ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(-1)), -1);
1✔
686
        ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(-2)), -2);
1✔
687

688
        ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(INT16_MAX)), INT16_MAX);
1✔
689
        ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(INT16_MIN)), INT16_MIN);
1✔
690

691
        ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(INT32_MAX)), INT32_MAX);
1✔
692
        ASSERT_EQ(PTR_TO_PID(PID_TO_PTR(INT32_MIN)), INT32_MIN);
1✔
693
}
1✔
694

695
static void test_ioprio_class_from_to_string_one(const char *val, int expected, int normalized) {
10✔
696
        ASSERT_EQ(ioprio_class_from_string(val), expected);
10✔
697
        if (expected >= 0) {
10✔
698
                _cleanup_free_ char *s = NULL;
7✔
699
                unsigned ret;
7✔
700
                int combined;
7✔
701

702
                ASSERT_OK_ZERO(ioprio_class_to_string_alloc(expected, &s));
7✔
703
                /* We sometimes get a class number and sometimes a name back */
704
                ASSERT_TRUE(streq(s, val) || safe_atou(val, &ret) == 0);
9✔
705

706
                /* Make sure normalization works, i.e. NONE → BE gets normalized */
707
                combined = ioprio_normalize(ioprio_prio_value(expected, 0));
7✔
708
                ASSERT_EQ(ioprio_prio_class(combined), normalized);
7✔
709
                ASSERT_TRUE(expected != IOPRIO_CLASS_NONE || ioprio_prio_data(combined) == 4);
7✔
710
        }
711
}
10✔
712

713
TEST(ioprio_class_from_to_string) {
1✔
714
        test_ioprio_class_from_to_string_one("none", IOPRIO_CLASS_NONE, IOPRIO_CLASS_BE);
1✔
715
        test_ioprio_class_from_to_string_one("realtime", IOPRIO_CLASS_RT, IOPRIO_CLASS_RT);
1✔
716
        test_ioprio_class_from_to_string_one("best-effort", IOPRIO_CLASS_BE, IOPRIO_CLASS_BE);
1✔
717
        test_ioprio_class_from_to_string_one("idle", IOPRIO_CLASS_IDLE, IOPRIO_CLASS_IDLE);
1✔
718
        test_ioprio_class_from_to_string_one("0", IOPRIO_CLASS_NONE, IOPRIO_CLASS_BE);
1✔
719
        test_ioprio_class_from_to_string_one("1", 1, 1);
1✔
720
        test_ioprio_class_from_to_string_one("7", 7, 7);
1✔
721
        test_ioprio_class_from_to_string_one("8", -EINVAL, -EINVAL);
1✔
722
        test_ioprio_class_from_to_string_one("9", -EINVAL, -EINVAL);
1✔
723
        test_ioprio_class_from_to_string_one("-1", -EINVAL, -EINVAL);
1✔
724
}
1✔
725

726
TEST(setpriority_closest) {
1✔
727
        int r;
1✔
728

729
        r = safe_fork("(test-setprio)",
1✔
730
                      FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_LOG|FORK_REOPEN_LOG, NULL);
731
        ASSERT_OK(r);
2✔
732

733
        if (r == 0) {
2✔
734
                bool full_test;
1✔
735
                int p, q;
1✔
736
                /* child */
737

738
                /* rlimit of 30 equals nice level of -10 */
739
                if (setrlimit(RLIMIT_NICE, &RLIMIT_MAKE_CONST(30)) < 0) {
1✔
740
                        /* If this fails we are probably unprivileged or in a userns of some kind, let's skip
741
                         * the full test */
UNCOV
742
                        if (!ERRNO_IS_PRIVILEGE(errno))
×
743
                                ASSERT_OK_ERRNO(-1);
×
744
                        full_test = false;
745
                } else {
746
                        /* However, if the hard limit was above 30, setrlimit would succeed unprivileged, so
747
                         * check if the UID/GID can be changed before enabling the full test. */
748
                        if (setresgid(GID_NOBODY, GID_NOBODY, GID_NOBODY) < 0) {
1✔
749
                                /* If the nobody user does not exist (user namespace) we get EINVAL. */
UNCOV
750
                                if (!ERRNO_IS_PRIVILEGE(errno) && errno != EINVAL)
×
751
                                        ASSERT_OK_ERRNO(-1);
×
752
                                full_test = false;
753
                        } else if (setresuid(UID_NOBODY, UID_NOBODY, UID_NOBODY) < 0) {
1✔
754
                                /* If the nobody user does not exist (user namespace) we get EINVAL. */
UNCOV
755
                                if (!ERRNO_IS_PRIVILEGE(errno) && errno != EINVAL)
×
756
                                        ASSERT_OK_ERRNO(-1);
×
757
                                full_test = false;
758
                        } else
759
                                full_test = true;
760
                }
761

762
                errno = 0;
1✔
763
                p = getpriority(PRIO_PROCESS, 0);
1✔
764
                ASSERT_EQ(errno, 0);
1✔
765

766
                /* It should always be possible to set our nice level to the current one */
767
                ASSERT_OK_POSITIVE(setpriority_closest(p));
1✔
768

769
                errno = 0;
1✔
770
                q = getpriority(PRIO_PROCESS, 0);
1✔
771
                ASSERT_EQ(errno, 0);
1✔
772
                ASSERT_EQ(p, q);
1✔
773

774
                /* It should also be possible to set the nice level to one higher */
775
                if (p < PRIO_MAX-1) {
1✔
776
                        ASSERT_OK_POSITIVE(setpriority_closest(++p));
1✔
777

778
                        errno = 0;
1✔
779
                        q = getpriority(PRIO_PROCESS, 0);
1✔
780
                        ASSERT_EQ(errno, 0);
1✔
781
                        ASSERT_EQ(p, q);
1✔
782
                }
783

784
                /* It should also be possible to set the nice level to two higher */
785
                if (p < PRIO_MAX-1) {
1✔
786
                        ASSERT_OK_POSITIVE(setpriority_closest(++p));
1✔
787

788
                        errno = 0;
1✔
789
                        q = getpriority(PRIO_PROCESS, 0);
1✔
790
                        ASSERT_EQ(errno, 0);
1✔
791
                        ASSERT_EQ(p, q);
1✔
792
                }
793

794
                if (full_test) {
1✔
795
                        /* These two should work, given the RLIMIT_NICE we set above */
796
                        ASSERT_OK_POSITIVE(setpriority_closest(-10));
1✔
797
                        errno = 0;
1✔
798
                        q = getpriority(PRIO_PROCESS, 0);
1✔
799
                        ASSERT_EQ(errno, 0);
1✔
800
                        ASSERT_EQ(q, -10);
1✔
801

802
                        ASSERT_OK_POSITIVE(setpriority_closest(-9));
1✔
803
                        errno = 0;
1✔
804
                        q = getpriority(PRIO_PROCESS, 0);
1✔
805
                        ASSERT_EQ(errno, 0);
1✔
806
                        ASSERT_EQ(q, -9);
1✔
807

808
                        /* This should succeed but should be clamped to the limit */
809
                        ASSERT_OK_ZERO(setpriority_closest(-11));
1✔
810
                        errno = 0;
1✔
811
                        q = getpriority(PRIO_PROCESS, 0);
1✔
812
                        ASSERT_EQ(errno, 0);
1✔
813
                        ASSERT_EQ(q, -10);
1✔
814

815
                        ASSERT_OK_POSITIVE(setpriority_closest(-8));
1✔
816
                        errno = 0;
1✔
817
                        q = getpriority(PRIO_PROCESS, 0);
1✔
818
                        ASSERT_EQ(errno, 0);
1✔
819
                        ASSERT_EQ(q, -8);
1✔
820

821
                        /* This should succeed but should be clamped to the limit */
822
                        ASSERT_OK_ZERO(setpriority_closest(-12));
1✔
823
                        errno = 0;
1✔
824
                        q = getpriority(PRIO_PROCESS, 0);
1✔
825
                        ASSERT_EQ(errno, 0);
1✔
826
                        ASSERT_EQ(q, -10);
1✔
827
                }
828

829
                _exit(EXIT_SUCCESS);
1✔
830
        }
831
}
1✔
832

833
TEST(pid_get_ppid) {
1✔
834
        uint64_t limit;
1✔
835
        int r;
1✔
836

837
        ASSERT_ERROR(pid_get_ppid(1, NULL), EADDRNOTAVAIL);
1✔
838

839
        /* the process with the PID above the global limit definitely doesn't exist. Verify that */
840
        ASSERT_OK(procfs_get_pid_max(&limit));
1✔
841
        log_debug("kernel.pid_max = %"PRIu64, limit);
1✔
842

843
        if (limit < INT_MAX) {
1✔
844
                r = pid_get_ppid(limit + 1, NULL);
1✔
845
                log_debug_errno(r, "get_process_limit(%"PRIu64") → %d/%m", limit + 1, r);
1✔
846
                assert(r == -ESRCH);
1✔
847
        }
848

849
        for (pid_t pid = 0;;) {
1✔
850
                _cleanup_free_ char *c1 = NULL, *c2 = NULL;
1✔
851
                pid_t ppid;
2✔
852

853
                r = pid_get_ppid(pid, &ppid);
2✔
854
                if (r == -EADDRNOTAVAIL) {
2✔
855
                        log_info("No further parent PID");
1✔
856
                        break;
1✔
857
                }
858

859
                ASSERT_OK(r);
1✔
860

861
                ASSERT_OK(pid_get_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &c1));
1✔
862
                ASSERT_OK(pid_get_cmdline(ppid, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &c2));
1✔
863

864
                log_info("Parent of " PID_FMT " (%s) is " PID_FMT " (%s).", pid, c1, ppid, c2);
1✔
865

866
                pid = ppid;
1✔
867
        }
868

869
        /* the same via pidref */
UNCOV
870
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
×
871
        ASSERT_OK(pidref_set_self(&pidref));
1✔
872
        for (;;) {
3✔
873
                _cleanup_free_ char *c1 = NULL, *c2 = NULL;
2✔
UNCOV
874
                _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
×
875
                r = pidref_get_ppid_as_pidref(&pidref, &parent);
2✔
876
                if (r == -EADDRNOTAVAIL) {
2✔
877
                        log_info("No further parent PID");
1✔
878
                        break;
1✔
879
                }
880

881
                ASSERT_OK(r);
1✔
882

883
                ASSERT_OK(pidref_get_cmdline(&pidref, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &c1));
1✔
884
                ASSERT_OK(pidref_get_cmdline(&parent, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &c2));
1✔
885

886
                log_info("Parent of " PID_FMT " (%s) is " PID_FMT " (%s).", pidref.pid, c1, parent.pid, c2);
1✔
887

888
                pidref_done(&pidref);
1✔
889
                pidref = TAKE_PIDREF(parent);
1✔
890
        }
891
}
1✔
892

893
TEST(set_oom_score_adjust) {
1✔
894
        int a, b, r;
1✔
895

896
        ASSERT_OK(get_oom_score_adjust(&a));
1✔
897

898
        r = set_oom_score_adjust(OOM_SCORE_ADJ_MIN);
1✔
899
        if (!ERRNO_IS_PRIVILEGE(r))
1✔
900
                ASSERT_OK(r);
1✔
901

UNCOV
902
        if (r >= 0) {
×
903
                ASSERT_OK(get_oom_score_adjust(&b));
1✔
904
                ASSERT_EQ(b, OOM_SCORE_ADJ_MIN);
1✔
905
        }
906

907
        ASSERT_OK(set_oom_score_adjust(a));
1✔
908
        ASSERT_OK(get_oom_score_adjust(&b));
1✔
909
        ASSERT_EQ(b, a);
1✔
910
}
1✔
911

912
static void* dummy_thread(void *p) {
2✔
913
        int fd = PTR_TO_FD(p);
2✔
914
        char x;
2✔
915

916
        /* let main thread know we are ready */
917
        ASSERT_OK_EQ_ERRNO(write(fd, &(const char) { 'x' }, 1), 1);
2✔
918

919
        /* wait for the main thread to tell us to shut down */
920
        ASSERT_OK_EQ_ERRNO(read(fd, &x, 1), 1);
2✔
921
        return NULL;
2✔
922
}
923

924
TEST(get_process_threads) {
1✔
925
        int r;
1✔
926

927
        /* Run this test in a child, so that we can guarantee there's exactly one thread around in the child */
928
        r = safe_fork("(nthreads)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_LOG, NULL);
1✔
929
        ASSERT_OK(r);
2✔
930

931
        if (r == 0) {
2✔
UNCOV
932
                _cleanup_close_pair_ int pfd[2] = EBADF_PAIR, ppfd[2] = EBADF_PAIR;
×
933
                pthread_t t, tt;
1✔
934
                char x;
1✔
935

936
                ASSERT_OK_ERRNO(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, pfd));
1✔
937
                ASSERT_OK_ERRNO(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, ppfd));
1✔
938

939
                ASSERT_OK_EQ(get_process_threads(0), 1);
1✔
940
                ASSERT_OK_ZERO_ERRNO(pthread_create(&t, NULL, &dummy_thread, FD_TO_PTR(pfd[0])));
1✔
941
                ASSERT_OK_EQ_ERRNO(read(pfd[1], &x, 1), 1);
1✔
942
                ASSERT_OK_EQ(get_process_threads(0), 2);
1✔
943
                ASSERT_OK_ZERO_ERRNO(pthread_create(&tt, NULL, &dummy_thread, FD_TO_PTR(ppfd[0])));
1✔
944
                ASSERT_OK_EQ_ERRNO(read(ppfd[1], &x, 1), 1);
1✔
945
                ASSERT_OK_EQ(get_process_threads(0), 3);
1✔
946

947
                ASSERT_OK_EQ_ERRNO(write(pfd[1], &(const char) { 'x' }, 1), 1);
1✔
948
                ASSERT_OK_ZERO_ERRNO(pthread_join(t, NULL));
1✔
949

950
                /* the value reported via /proc/ is decreased asynchronously, and there appears to be no nice
951
                 * way to sync on it. Hence we do the weak >= 2 check, even though == 2 is what we'd actually
952
                 * like to check here */
953
                r = get_process_threads(0);
1✔
954
                ASSERT_OK(r);
1✔
955
                ASSERT_GE(r, 2);
1✔
956

957
                ASSERT_OK_EQ_ERRNO(write(ppfd[1], &(const char) { 'x' }, 1), 1);
1✔
958
                ASSERT_OK_ZERO_ERRNO(pthread_join(tt, NULL));
1✔
959

960
                /* similar here */
961
                r = get_process_threads(0);
1✔
962
                ASSERT_OK(r);
1✔
963
                ASSERT_GE(r, 1);
1✔
964

965
                _exit(EXIT_SUCCESS);
1✔
966
        }
967
}
1✔
968

969
TEST(is_reaper_process) {
1✔
970
        int r;
1✔
971

972
        r = safe_fork("(regular)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_WAIT, NULL);
1✔
973
        ASSERT_OK(r);
2✔
974
        if (r == 0) {
2✔
975
                /* child */
976

977
                ASSERT_OK_ZERO(is_reaper_process());
1✔
978
                _exit(EXIT_SUCCESS);
1✔
979
        }
980

981
        r = safe_fork("(newpid)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_WAIT, NULL);
1✔
982
        ASSERT_OK(r);
2✔
983
        if (r == 0) {
2✔
984
                /* child */
985

986
                if (unshare(CLONE_NEWPID) < 0) {
1✔
UNCOV
987
                        if (ERRNO_IS_PRIVILEGE(errno) || ERRNO_IS_NOT_SUPPORTED(errno)) {
×
988
                                log_notice("Skipping CLONE_NEWPID reaper check, lacking privileges/support");
×
989
                                _exit(EXIT_SUCCESS);
×
990
                        }
991
                }
992

993
                r = safe_fork("(newpid1)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_WAIT, NULL);
1✔
994
                ASSERT_OK(r);
2✔
995
                if (r == 0) {
2✔
996
                        /* grandchild, which is PID1 in a pidns */
997
                        ASSERT_OK_EQ(getpid_cached(), 1);
1✔
998
                        ASSERT_OK_POSITIVE(is_reaper_process());
1✔
999
                        _exit(EXIT_SUCCESS);
1✔
1000
                }
1001

1002
                _exit(EXIT_SUCCESS);
1✔
1003
        }
1004

1005
        r = safe_fork("(subreaper)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_WAIT, NULL);
1✔
1006
        ASSERT_OK(r);
2✔
1007
        if (r == 0) {
2✔
1008
                /* child */
1009
                ASSERT_OK(make_reaper_process(true));
1✔
1010

1011
                ASSERT_OK_POSITIVE(is_reaper_process());
1✔
1012
                _exit(EXIT_SUCCESS);
1✔
1013
        }
1014
}
1✔
1015

1016
TEST(pid_get_start_time) {
1✔
1017
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
1✔
1018

1019
        ASSERT_OK(pidref_set_self(&pidref));
1✔
1020

1021
        usec_t start_time;
1✔
1022
        ASSERT_OK(pidref_get_start_time(&pidref, &start_time));
1✔
1023
        log_info("our starttime: " USEC_FMT, start_time);
1✔
1024

1025
        _cleanup_(pidref_done_sigkill_wait) PidRef child = PIDREF_NULL;
1✔
1026

1027
        ASSERT_OK_POSITIVE(pidref_safe_fork("(stub)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_FREEZE, &child));
1✔
1028

1029
        usec_t start_time2;
1✔
1030
        ASSERT_OK(pidref_get_start_time(&child, &start_time2));
1✔
1031

1032
        log_info("child starttime: " USEC_FMT, start_time2);
1✔
1033

1034
        ASSERT_GE(start_time2, start_time);
1✔
1035
}
1✔
1036

1037
TEST(pidref_from_same_root_fs) {
1✔
1038
        int r;
1✔
1039

1040
        _cleanup_(pidref_done) PidRef pid1 = PIDREF_NULL, self = PIDREF_NULL;
1✔
1041

1042
        ASSERT_OK(pidref_set_self(&self));
1✔
1043
        ASSERT_OK(pidref_set_pid(&pid1, 1));
1✔
1044

1045
        ASSERT_OK_POSITIVE(pidref_from_same_root_fs(&self, &self));
1✔
1046
        ASSERT_OK_POSITIVE(pidref_from_same_root_fs(&pid1, &pid1));
1✔
1047

1048
        r = pidref_from_same_root_fs(&pid1, &self);
1✔
1049
        if (ERRNO_IS_NEG_PRIVILEGE(r))
1✔
UNCOV
1050
                return (void) log_tests_skipped("skipping pidref_from_same_root_fs() test, lacking privileged.");
×
1051
        ASSERT_OK(r);
1✔
1052
        log_info("PID1 and us have the same rootfs: %s", yes_no(r));
2✔
1053

1054
        int q = pidref_from_same_root_fs(&self, &pid1);
1✔
1055
        ASSERT_OK(q);
1✔
1056
        ASSERT_EQ(r, q);
1✔
1057

UNCOV
1058
        _cleanup_(pidref_done_sigkill_wait) PidRef child1 = PIDREF_NULL;
×
1059
        ASSERT_OK(pidref_safe_fork("(child1)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_FREEZE, &child1));
1✔
1060
        ASSERT_OK_POSITIVE(pidref_from_same_root_fs(&self, &child1));
1✔
1061

1062
        _cleanup_close_ int efd = eventfd(0, EFD_CLOEXEC);
2✔
1063
        ASSERT_OK_ERRNO(efd);
1✔
1064

1065
        _cleanup_(pidref_done_sigkill_wait) PidRef child2 = PIDREF_NULL;
1✔
1066
        r = pidref_safe_fork("(child2)", FORK_RESET_SIGNALS|FORK_REOPEN_LOG, &child2);
1✔
1067
        ASSERT_OK(r);
1✔
1068

1069
        if (r == 0) {
1✔
UNCOV
1070
                ASSERT_OK_ERRNO(chroot("/usr"));
×
1071
                uint64_t u = 1;
×
1072

UNCOV
1073
                ASSERT_OK_EQ_ERRNO(write(efd, &u, sizeof(u)), (ssize_t) sizeof(u));
×
1074
                freeze();
×
1075
        }
1076

1077
        uint64_t u;
1✔
1078
        ASSERT_OK_EQ_ERRNO(read(efd, &u, sizeof(u)), (ssize_t) sizeof(u));
1✔
1079

1080
        ASSERT_OK_ZERO(pidref_from_same_root_fs(&self, &child2));
1✔
1081
        ASSERT_OK_ZERO(pidref_from_same_root_fs(&child2, &self));
1✔
1082
}
1083

1084
TEST(pidfd_get_inode_id_self_cached) {
1✔
1085
        int r;
1✔
1086

1087
        log_info("pid=" PID_FMT, getpid_cached());
1✔
1088

1089
        uint64_t id;
1✔
1090
        r = pidfd_get_inode_id_self_cached(&id);
1✔
1091
        if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
1✔
UNCOV
1092
                log_info("pidfdid not supported");
×
1093
        else {
1094
                assert(r >= 0);
1✔
1095
                log_info("pidfdid=%" PRIu64, id);
1✔
1096
        }
1097
}
1✔
1098

1099
static int intro(void) {
1✔
1100
        log_show_color(true);
1✔
1101
        return EXIT_SUCCESS;
1✔
1102
}
1103

1104
DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);
1✔
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