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

systemd / systemd / 19250270288

10 Nov 2025 09:31PM UTC coverage: 72.402% (-0.009%) from 72.411%
19250270288

push

github

bluca
test: ignore EC from the second `systemctl status -a` as well

There is a TOCTOU in the `systemctl status` where a unit might change
its state during the initial ListUnitsByPatterns call and the subsequent
individual GetAll calls, which then makes the systemctl call fail even
if the unit that was originally pulled in was active/running:

[ 1922.040463] TEST-26-SYSTEMCTL.sh[117]: + systemctl status -a --state active,running,plugged
[ 1922.051423] systemd[1]: Got message type=method_call sender=n/a destination=org.freedesktop.systemd1 path=/org/freedesktop/systemd1 interface=org.freedesktop.systemd1.Manager member=ListUnitsByPatterns  cookie=1 reply_cookie=0 signature=asas error-name=n/a error-message=n/a
[ 1922.052501] systemd[1]: Sent message type=method_return sender=org.freedesktop.systemd1 destination=n/a path=n/a interface=n/a member=n/a cookie=1 reply_cookie=1 signature=a(ssssssouso) error-name=n/a error-message=n/a
[ 1922.052650] systemd[1]: Got message type=method_call sender=n/a destination=org.freedesktop.systemd1 path=/org/freedesktop/systemd1/unit/_2d_2emount interface=org.freedesktop.DBus.Properties member=GetAll  cookie=2 reply_cookie=0 signature=s error-name=n/a error-message=n/a
...
[ 1922.222061] systemd-hostnamed[424]: Idle for 30s, exiting.
...
[ 1922.224961] systemd[1]: systemd-hostnamed.service: Got notification message from PID 424: STOPPING=1, STATUS=Shutting down...
[ 1922.224983] systemd[1]: systemd-hostnamed.service: Changed running ->stop-sigterm
...
[ 1922.228984] systemd[1]: Got message type=method_call sender=n/a destination=org.freedesktop.systemd1 path=/org/freedesktop/systemd1/unit/systemd_2dhostnamed_2eservice interface=org.freedesktop.DBus.Properties member=GetAll  cookie=41 reply_cookie=0 signature=s error-name=n/a error-message=n/a
[ 1922.234402] systemd[1]: Sent message type=method_return sender=org.freedesktop.systemd1 destination=n/a path=n/a interface=n/a member=n/a cookie=43 reply_cookie=41 signature=a{sv} erro... (continued)

307121 of 424186 relevant lines covered (72.4%)

1117521.39 hits per line

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

63.41
/src/core/scope.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <unistd.h>
4

5
#include "sd-bus.h"
6

7
#include "cgroup-setup.h"
8
#include "dbus-scope.h"
9
#include "dbus-unit.h"
10
#include "exit-status.h"
11
#include "log.h"
12
#include "manager.h"
13
#include "parse-util.h"
14
#include "pidref.h"
15
#include "random-util.h"
16
#include "scope.h"
17
#include "serialize.h"
18
#include "set.h"
19
#include "special.h"
20
#include "string-table.h"
21
#include "string-util.h"
22
#include "strv.h"
23
#include "unit.h"
24
#include "user-util.h"
25

26
static const UnitActiveState state_translation_table[_SCOPE_STATE_MAX] = {
27
        [SCOPE_DEAD]         = UNIT_INACTIVE,
28
        [SCOPE_START_CHOWN]  = UNIT_ACTIVATING,
29
        [SCOPE_RUNNING]      = UNIT_ACTIVE,
30
        [SCOPE_ABANDONED]    = UNIT_ACTIVE,
31
        [SCOPE_STOP_SIGTERM] = UNIT_DEACTIVATING,
32
        [SCOPE_STOP_SIGKILL] = UNIT_DEACTIVATING,
33
        [SCOPE_FAILED]       = UNIT_FAILED,
34
};
35

36
static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
37

38
static void scope_init(Unit *u) {
332✔
39
        Scope *s = ASSERT_PTR(SCOPE(u));
332✔
40

41
        assert(u->load_state == UNIT_STUB);
332✔
42

43
        s->runtime_max_usec = USEC_INFINITY;
332✔
44
        s->timeout_stop_usec = u->manager->defaults.timeout_stop_usec;
332✔
45
        u->ignore_on_isolate = true;
332✔
46
        s->user = s->group = NULL;
332✔
47
        s->oom_policy = _OOM_POLICY_INVALID;
332✔
48
}
332✔
49

50
static void scope_done(Unit *u) {
332✔
51
        Scope *s = ASSERT_PTR(SCOPE(u));
332✔
52

53
        s->controller = mfree(s->controller);
332✔
54
        s->controller_track = sd_bus_track_unref(s->controller_track);
332✔
55

56
        s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
332✔
57

58
        s->user = mfree(s->user);
332✔
59
        s->group = mfree(s->group);
332✔
60
}
332✔
61

62
static usec_t scope_running_timeout(Scope *s) {
320✔
63
        usec_t delta = 0;
320✔
64

65
        assert(s);
320✔
66

67
        if (s->runtime_rand_extra_usec != 0) {
320✔
68
                delta = random_u64_range(s->runtime_rand_extra_usec);
×
69
                log_unit_debug(UNIT(s), "Adding delta of %s sec to timeout", FORMAT_TIMESPAN(delta, USEC_PER_SEC));
×
70
        }
71

72
        return usec_add(usec_add(UNIT(s)->active_enter_timestamp.monotonic,
320✔
73
                                 s->runtime_max_usec),
74
                        delta);
75
}
76

77
static int scope_arm_timer(Scope *s, bool relative, usec_t usec) {
320✔
78
        assert(s);
320✔
79

80
        return unit_arm_timer(UNIT(s), &s->timer_event_source, relative, usec, scope_dispatch_timer);
320✔
81
}
82

83
static void scope_set_state(Scope *s, ScopeState state) {
333✔
84
        ScopeState old_state;
333✔
85

86
        assert(s);
333✔
87

88
        if (s->state != state)
333✔
89
                bus_unit_send_pending_change_signal(UNIT(s), false);
333✔
90

91
        old_state = s->state;
333✔
92
        s->state = state;
333✔
93

94
        if (!IN_SET(state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL, SCOPE_START_CHOWN, SCOPE_RUNNING))
333✔
95
                s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
13✔
96

97
        if (!IN_SET(old_state, SCOPE_DEAD, SCOPE_FAILED) && IN_SET(state, SCOPE_DEAD, SCOPE_FAILED))
333✔
98
                unit_unwatch_all_pids(UNIT(s));
8✔
99

100
        if (state != old_state)
333✔
101
                log_unit_debug(UNIT(s), "Changed %s -> %s",
333✔
102
                               scope_state_to_string(old_state), scope_state_to_string(state));
103

104
        unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
333✔
105
}
333✔
106

107
static int scope_add_default_dependencies(Scope *s) {
322✔
108
        int r;
322✔
109

110
        assert(s);
322✔
111

112
        if (!UNIT(s)->default_dependencies)
322✔
113
                return 0;
114

115
        /* Make sure scopes are unloaded on shutdown */
116
        r = unit_add_two_dependencies_by_name(
92✔
117
                        UNIT(s),
46✔
118
                        UNIT_BEFORE, UNIT_CONFLICTS,
119
                        SPECIAL_SHUTDOWN_TARGET, true,
120
                        UNIT_DEPENDENCY_DEFAULT);
121
        if (r < 0)
46✔
122
                return r;
×
123

124
        return 0;
125
}
126

127
static int scope_verify(Scope *s) {
322✔
128
        assert(s);
322✔
129
        assert(UNIT(s)->load_state == UNIT_LOADED);
322✔
130

131
        if (set_isempty(UNIT(s)->pids) &&
322✔
132
            !MANAGER_IS_RELOADING(UNIT(s)->manager) &&
538✔
133
            !unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE))
231✔
134
                return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOENT), "Scope has no PIDs. Refusing.");
×
135

136
        return 0;
137
}
138

139
static int scope_load_init_scope(Unit *u) {
322✔
140
        assert(u);
322✔
141

142
        if (!unit_has_name(u, SPECIAL_INIT_SCOPE))
322✔
143
                return 0;
144

145
        u->transient = true;
276✔
146
        u->perpetual = true;
276✔
147

148
        /* init.scope is a bit special, as it has to stick around forever. Because of its special semantics we
149
         * synthesize it here, instead of relying on the unit file on disk. */
150

151
        u->default_dependencies = false;
276✔
152

153
        /* Prettify things, if we can. */
154
        if (!u->description)
276✔
155
                u->description = strdup("System and Service Manager");
276✔
156
        if (!u->documentation)
276✔
157
                (void) strv_extend(&u->documentation, "man:systemd(1)");
276✔
158

159
        return 1;
160
}
161

162
static int scope_add_extras(Scope *s) {
322✔
163
        int r;
322✔
164

165
        r = unit_patch_contexts(UNIT(s));
322✔
166
        if (r < 0)
322✔
167
                return r;
168

169
        r = unit_set_default_slice(UNIT(s));
322✔
170
        if (r < 0)
322✔
171
                return r;
172

173
        if (s->oom_policy < 0)
322✔
174
                s->oom_policy = s->cgroup_context.delegate ? OOM_CONTINUE : UNIT(s)->manager->defaults.oom_policy;
279✔
175

176
        s->cgroup_context.memory_oom_group = s->oom_policy == OOM_KILL;
322✔
177

178
        return scope_add_default_dependencies(s);
322✔
179
}
180

181
static int scope_load(Unit *u) {
347✔
182
        Scope *s = ASSERT_PTR(SCOPE(u));
347✔
183
        int r;
347✔
184

185
        assert(u->load_state == UNIT_STUB);
347✔
186

187
        if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
347✔
188
                /* Refuse to load non-transient scope units, but allow them while reloading. */
189
                return -ENOENT;
190

191
        r = scope_load_init_scope(u);
322✔
192
        if (r < 0)
322✔
193
                return r;
194

195
        r = unit_load_fragment_and_dropin(u, false);
322✔
196
        if (r < 0)
322✔
197
                return r;
198

199
        if (u->load_state != UNIT_LOADED)
322✔
200
                return 0;
201

202
        r = scope_add_extras(s);
322✔
203
        if (r < 0)
322✔
204
                return r;
205

206
        return scope_verify(s);
322✔
207
}
208

209
static usec_t scope_coldplug_timeout(Scope *s) {
306✔
210
        assert(s);
306✔
211

212
        switch (s->deserialized_state) {
306✔
213

214
        case SCOPE_RUNNING:
306✔
215
                return scope_running_timeout(s);
306✔
216

217
        case SCOPE_STOP_SIGKILL:
218
        case SCOPE_STOP_SIGTERM:
219
                return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_stop_usec);
×
220

221
        default:
222
                return USEC_INFINITY;
223
        }
224
}
225

226
static int scope_coldplug(Unit *u) {
307✔
227
        Scope *s = ASSERT_PTR(SCOPE(u));
307✔
228
        int r;
307✔
229

230
        assert(s->state == SCOPE_DEAD);
307✔
231

232
        if (s->deserialized_state == s->state)
307✔
233
                return 0;
234

235
        r = scope_arm_timer(s, /* relative= */ false, scope_coldplug_timeout(s));
306✔
236
        if (r < 0)
306✔
237
                return r;
238

239
        bus_scope_track_controller(s);
306✔
240

241
        scope_set_state(s, s->deserialized_state);
306✔
242
        return 0;
306✔
243
}
244

245
static void scope_dump(Unit *u, FILE *f, const char *prefix) {
6✔
246
        Scope *s = ASSERT_PTR(SCOPE(u));
6✔
247

248
        assert(f);
6✔
249
        assert(prefix);
6✔
250

251
        fprintf(f,
12✔
252
                "%sScope State: %s\n"
253
                "%sResult: %s\n"
254
                "%sRuntimeMaxSec: %s\n"
255
                "%sRuntimeRandomizedExtraSec: %s\n"
256
                "%sOOMPolicy: %s\n",
257
                prefix, scope_state_to_string(s->state),
258
                prefix, scope_result_to_string(s->result),
259
                prefix, FORMAT_TIMESPAN(s->runtime_max_usec, USEC_PER_SEC),
6✔
260
                prefix, FORMAT_TIMESPAN(s->runtime_rand_extra_usec, USEC_PER_SEC),
6✔
261
                prefix, oom_policy_to_string(s->oom_policy));
262

263
        cgroup_context_dump(u, f, prefix);
6✔
264
        kill_context_dump(&s->kill_context, f, prefix);
6✔
265
}
6✔
266

267
static void scope_enter_dead(Scope *s, ScopeResult f) {
8✔
268
        assert(s);
8✔
269

270
        if (s->result == SCOPE_SUCCESS)
8✔
271
                s->result = f;
8✔
272

273
        unit_log_result(UNIT(s), s->result == SCOPE_SUCCESS, scope_result_to_string(s->result));
8✔
274
        scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD);
16✔
275
}
8✔
276

277
static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
×
278
        bool skip_signal = false;
×
279
        int r;
×
280

281
        assert(s);
×
282

283
        if (s->result == SCOPE_SUCCESS)
×
284
                s->result = f;
×
285

286
        /* If we have a controller set let's ask the controller nicely to terminate the scope, instead of us going
287
         * directly into SIGTERM berserk mode */
288
        if (state == SCOPE_STOP_SIGTERM)
×
289
                skip_signal = bus_scope_send_request_stop(s) > 0;
×
290

291
        if (skip_signal)
×
292
                r = 1; /* wait */
293
        else {
294
                r = unit_kill_context(
×
295
                                UNIT(s),
×
296
                                state != SCOPE_STOP_SIGTERM ? KILL_KILL :
297
                                s->was_abandoned            ? KILL_TERMINATE_AND_LOG :
×
298
                                                              KILL_TERMINATE);
299
                if (r < 0) {
×
300
                        log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m");
×
301
                        goto fail;
×
302
                }
303
        }
304

305
        if (r > 0) {
×
306
                r = scope_arm_timer(s, /* relative= */ true, s->timeout_stop_usec);
×
307
                if (r < 0) {
×
308
                        log_unit_warning_errno(UNIT(s), r, "Failed to install timer: %m");
×
309
                        goto fail;
×
310
                }
311

312
                scope_set_state(s, state);
×
313
        } else if (state == SCOPE_STOP_SIGTERM)
×
314
                scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_SUCCESS);
×
315
        else
316
                scope_enter_dead(s, SCOPE_SUCCESS);
×
317

318
        return;
319

320
fail:
×
321
        scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
×
322
}
323

324
static int scope_enter_start_chown(Scope *s) {
×
325
        Unit *u = UNIT(ASSERT_PTR(s));
×
326
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
×
327
        int r;
×
328

329
        assert(s->user);
×
330

331
        if (!s->cgroup_runtime)
×
332
                return -EINVAL;
333

334
        r = scope_arm_timer(s, /* relative= */ true, u->manager->defaults.timeout_start_usec);
×
335
        if (r < 0)
×
336
                return r;
337

338
        r = unit_fork_helper_process(u, "(sd-chown-cgroup)", /* into_cgroup= */ true, &pidref);
×
339
        if (r < 0)
1✔
340
                goto fail;
×
341

342
        if (r == 0) {
1✔
343
                uid_t uid = UID_INVALID;
1✔
344
                gid_t gid = GID_INVALID;
1✔
345

346
                if (!isempty(s->user)) {
1✔
347
                        const char *user = s->user;
1✔
348

349
                        r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
1✔
350
                        if (r < 0) {
1✔
351
                                log_unit_error_errno(UNIT(s), r, "Failed to resolve user \"%s\": %m", user);
×
352
                                _exit(EXIT_USER);
×
353
                        }
354
                }
355

356
                if (!isempty(s->group)) {
1✔
357
                        const char *group = s->group;
×
358

359
                        r = get_group_creds(&group, &gid, 0);
×
360
                        if (r < 0) {
×
361
                                log_unit_error_errno(UNIT(s), r, "Failed to resolve group \"%s\": %m", group);
×
362
                                _exit(EXIT_GROUP);
×
363
                        }
364
                }
365

366
                r = cg_set_access(s->cgroup_runtime->cgroup_path, uid, gid);
1✔
367
                if (r < 0) {
1✔
368
                        log_unit_error_errno(UNIT(s), r, "Failed to adjust control group access: %m");
×
369
                        _exit(EXIT_CGROUP);
×
370
                }
371

372
                _exit(EXIT_SUCCESS);
1✔
373
        }
374

375
        r = unit_watch_pidref(UNIT(s), &pidref, /* exclusive= */ true);
×
376
        if (r < 0)
×
377
                goto fail;
×
378

379
        scope_set_state(s, SCOPE_START_CHOWN);
×
380

381
        return 1;
382
fail:
×
383
        s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
×
384
        return r;
×
385
}
386

387
static int scope_enter_running(Scope *s) {
14✔
388
        Unit *u = UNIT(ASSERT_PTR(s));
14✔
389
        int r;
14✔
390

391
        (void) bus_scope_track_controller(s);
14✔
392

393
        r = unit_acquire_invocation_id(u);
14✔
394
        if (r < 0)
14✔
395
                return r;
396

397
        unit_export_state_files(u);
14✔
398

399
        r = unit_attach_pids_to_cgroup(u, u->pids, NULL);
14✔
400
        if (r < 0) {
14✔
401
                log_unit_warning_errno(u, r, "Failed to add PIDs to scope's control group: %m");
×
402
                goto fail;
×
403
        }
404
        if (r == 0) {
14✔
405
                r = log_unit_warning_errno(u, SYNTHETIC_ERRNO(ECHILD), "No PIDs left to attach to the scope's control group, refusing.");
×
406
                goto fail;
×
407
        }
408
        log_unit_debug(u, "%i %s added to scope's control group.", r, r == 1 ? "process" : "processes");
14✔
409

410
        s->result = SCOPE_SUCCESS;
14✔
411

412
        scope_set_state(s, SCOPE_RUNNING);
14✔
413

414
        /* Set the maximum runtime timeout. */
415
        scope_arm_timer(s, /* relative= */ false, scope_running_timeout(s));
14✔
416

417
        /* Unwatch all pids we've just added to cgroup. We rely on empty notifications there. */
418
        unit_unwatch_all_pids(u);
14✔
419

420
        return 1;
14✔
421

422
fail:
×
423
        scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
×
424
        return r;
×
425
}
426

427
static int scope_start(Unit *u) {
14✔
428
        Scope *s = ASSERT_PTR(SCOPE(u));
14✔
429

430
        if (unit_has_name(u, SPECIAL_INIT_SCOPE))
14✔
431
                return -EPERM;
432

433
        if (s->state == SCOPE_FAILED)
14✔
434
                return -EPERM;
435

436
        /* We can't fulfill this right now, please try again later */
437
        if (IN_SET(s->state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
14✔
438
                return -EAGAIN;
439

440
        assert(s->state == SCOPE_DEAD);
14✔
441

442
        if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
14✔
443
                return -ENOENT;
444

445
        (void) unit_realize_cgroup(u);
14✔
446
        (void) unit_reset_accounting(u);
14✔
447

448
        /* We check only for User= option to keep behavior consistent with logic for service units,
449
         * i.e. having 'Delegate=true Group=foo' w/o specifying User= has no effect. */
450
        if (s->user && unit_cgroup_delegate(u))
14✔
451
                return scope_enter_start_chown(s);
×
452

453
        return scope_enter_running(s);
14✔
454
}
455

456
static int scope_stop(Unit *u) {
×
457
        Scope *s = ASSERT_PTR(SCOPE(u));
×
458

459
        if (IN_SET(s->state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
×
460
                return 0;
461

462
        assert(IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED));
×
463

464
        scope_enter_signal(s, SCOPE_STOP_SIGTERM, SCOPE_SUCCESS);
×
465
        return 1;
×
466
}
467

468
static void scope_reset_failed(Unit *u) {
2✔
469
        Scope *s = ASSERT_PTR(SCOPE(u));
2✔
470

471
        if (s->state == SCOPE_FAILED)
2✔
472
                scope_set_state(s, SCOPE_DEAD);
×
473

474
        s->result = SCOPE_SUCCESS;
2✔
475
}
2✔
476

477
static int scope_get_timeout(Unit *u, usec_t *timeout) {
×
478
        Scope *s = ASSERT_PTR(SCOPE(u));
×
479
        usec_t t;
×
480
        int r;
×
481

482
        if (!s->timer_event_source)
×
483
                return 0;
×
484

485
        r = sd_event_source_get_time(s->timer_event_source, &t);
×
486
        if (r < 0)
×
487
                return r;
488
        if (t == USEC_INFINITY)
×
489
                return 0;
490

491
        *timeout = t;
×
492
        return 1;
×
493
}
494

495
static int scope_serialize(Unit *u, FILE *f, FDSet *fds) {
101✔
496
        Scope *s = ASSERT_PTR(SCOPE(u));
101✔
497
        PidRef *pid;
101✔
498

499
        assert(f);
101✔
500
        assert(fds);
101✔
501

502
        (void) serialize_item(f, "state", scope_state_to_string(s->state));
101✔
503
        (void) serialize_bool(f, "was-abandoned", s->was_abandoned);
101✔
504

505
        (void) serialize_item(f, "controller", s->controller);
101✔
506

507
        SET_FOREACH(pid, u->pids)
103✔
508
                serialize_pidref(f, fds, "pids", pid);
2✔
509

510
        return 0;
101✔
511
}
512

513
static int scope_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
154✔
514
        Scope *s = ASSERT_PTR(SCOPE(u));
154✔
515
        int r;
154✔
516

517
        assert(key);
154✔
518
        assert(value);
154✔
519
        assert(fds);
154✔
520

521
        if (streq(key, "state")) {
154✔
522
                ScopeState state;
76✔
523

524
                state = scope_state_from_string(value);
76✔
525
                if (state < 0)
76✔
526
                        log_unit_debug(u, "Failed to parse state value: %s", value);
×
527
                else
528
                        s->deserialized_state = state;
76✔
529

530
        } else if (streq(key, "was-abandoned")) {
78✔
531
                int k;
76✔
532

533
                k = parse_boolean(value);
76✔
534
                if (k < 0)
76✔
535
                        log_unit_debug(u, "Failed to parse boolean value: %s", value);
×
536
                else
537
                        s->was_abandoned = k;
76✔
538
        } else if (streq(key, "controller")) {
2✔
539

540
                r = free_and_strdup(&s->controller, value);
×
541
                if (r < 0)
×
542
                        return log_oom();
×
543

544
        } else if (streq(key, "pids")) {
2✔
545
                _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2✔
546

547
                /* We don't check if we already received the pid before here because unit_watch_pidref()
548
                 * does this check internally and discards the new pidref if we already received it before. */
549
                if (deserialize_pidref(fds, value, &pidref) >= 0) {
2✔
550
                        r = unit_watch_pidref(u, &pidref, /* exclusive= */ false);
2✔
551
                        if (r < 0)
2✔
552
                                log_unit_debug(u, "Failed to watch PID, ignoring: %s", value);
×
553
                }
554
        } else
555
                log_unit_debug(u, "Unknown serialization key: %s", key);
×
556

557
        return 0;
558
}
559

560
static void scope_notify_cgroup_empty_event(Unit *u) {
8✔
561
        Scope *s = ASSERT_PTR(SCOPE(u));
8✔
562

563
        log_unit_debug(u, "cgroup is empty");
8✔
564

565
        if (IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
8✔
566
                scope_enter_dead(s, SCOPE_SUCCESS);
8✔
567
}
8✔
568

569
static void scope_notify_cgroup_oom_event(Unit *u, bool managed_oom) {
×
570
        Scope *s = ASSERT_PTR(SCOPE(u));
×
571

572
        if (managed_oom)
×
573
                log_unit_debug(u, "Process(es) of control group were killed by systemd-oomd.");
×
574
        else
575
                log_unit_debug(u, "Process of control group was killed by the OOM killer.");
×
576

577
        if (s->oom_policy == OOM_CONTINUE)
×
578
                return;
579

580
        switch (s->state) {
×
581

582
        case SCOPE_START_CHOWN:
×
583
        case SCOPE_RUNNING:
584
                scope_enter_signal(s, SCOPE_STOP_SIGTERM, SCOPE_FAILURE_OOM_KILL);
×
585
                break;
×
586

587
        case SCOPE_STOP_SIGTERM:
×
588
                scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_FAILURE_OOM_KILL);
×
589
                break;
×
590

591
        case SCOPE_STOP_SIGKILL:
×
592
                if (s->result == SCOPE_SUCCESS)
×
593
                        s->result = SCOPE_FAILURE_OOM_KILL;
×
594
                break;
595
        /* SCOPE_DEAD, SCOPE_ABANDONED, and SCOPE_FAILED end up in default */
596
        default:
×
597
                ;
×
598
        }
599
}
600

601
static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) {
2,443✔
602
        Scope *s = ASSERT_PTR(SCOPE(u));
2,443✔
603

604
        if (s->state == SCOPE_START_CHOWN) {
2,443✔
605
                if (!is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
×
606
                        scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
×
607
                else
608
                        scope_enter_running(s);
×
609
                return;
×
610
        }
611
}
612

613
static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) {
×
614
        Scope *s = ASSERT_PTR(SCOPE(userdata));
×
615

616
        assert(s->timer_event_source == source);
×
617

618
        switch (s->state) {
×
619

620
        case SCOPE_RUNNING:
621
                log_unit_warning(UNIT(s), "Scope reached runtime time limit. Stopping.");
×
622
                scope_enter_signal(s, SCOPE_STOP_SIGTERM, SCOPE_FAILURE_TIMEOUT);
×
623
                break;
×
624

625
        case SCOPE_STOP_SIGTERM:
×
626
                if (s->kill_context.send_sigkill) {
×
627
                        log_unit_warning(UNIT(s), "Stopping timed out. Killing.");
×
628
                        scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_FAILURE_TIMEOUT);
×
629
                } else {
630
                        log_unit_warning(UNIT(s), "Stopping timed out. Skipping SIGKILL.");
×
631
                        scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
×
632
                }
633

634
                break;
635

636
        case SCOPE_STOP_SIGKILL:
637
                log_unit_warning(UNIT(s), "Still around after SIGKILL. Ignoring.");
×
638
                scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
×
639
                break;
×
640

641
        case SCOPE_START_CHOWN:
642
                log_unit_warning(UNIT(s), "User lookup timed out. Entering failed state.");
×
643
                scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
×
644
                break;
×
645

646
        default:
×
647
                assert_not_reached();
×
648
        }
649

650
        return 0;
×
651
}
652

653
int scope_abandon(Scope *s) {
7✔
654
        assert(s);
7✔
655

656
        if (unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE))
7✔
657
                return -EPERM;
658

659
        if (!IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED))
7✔
660
                return -ESTALE;
661

662
        s->was_abandoned = true;
5✔
663

664
        s->controller = mfree(s->controller);
5✔
665
        s->controller_track = sd_bus_track_unref(s->controller_track);
5✔
666

667
        scope_set_state(s, SCOPE_ABANDONED);
5✔
668

669
        return 0;
5✔
670
}
671

672
static UnitActiveState scope_active_state(Unit *u) {
13,478✔
673
        Scope *s = ASSERT_PTR(SCOPE(u));
13,478✔
674

675
        return state_translation_table[s->state];
13,478✔
676
}
677

678
static const char *scope_sub_state_to_string(Unit *u) {
113✔
679
        Scope *s = ASSERT_PTR(SCOPE(u));
113✔
680

681
        return scope_state_to_string(s->state);
113✔
682
}
683

684
static void scope_enumerate_perpetual(Manager *m) {
276✔
685
        Unit *u;
276✔
686
        int r;
276✔
687

688
        assert(m);
276✔
689

690
        /* Let's unconditionally add the "init.scope" special unit
691
         * that encapsulates PID 1. Note that PID 1 already is in the
692
         * cgroup for this, we hence just need to allocate the object
693
         * for it and that's it. */
694

695
        u = manager_get_unit(m, SPECIAL_INIT_SCOPE);
276✔
696
        if (!u) {
276✔
697
                r = unit_new_for_name(m, sizeof(Scope), SPECIAL_INIT_SCOPE, &u);
276✔
698
                if (r < 0)
276✔
699
                        return (void) log_error_errno(r, "Failed to allocate the special %s unit: %m",
×
700
                                                      SPECIAL_INIT_SCOPE);
701
        }
702

703
        u->transient = true;
276✔
704
        u->perpetual = true;
276✔
705
        SCOPE(u)->deserialized_state = SCOPE_RUNNING;
276✔
706

707
        unit_add_to_load_queue(u);
276✔
708
        unit_add_to_dbus_queue(u);
276✔
709
        /* Enqueue an explicit cgroup realization here. Unlike other cgroups this one already exists and is
710
         * populated (by us, after all!) already, even when we are not in a reload cycle. Hence we cannot
711
         * apply the settings at creation time anymore, but let's at least apply them asynchronously. */
712
        unit_add_to_cgroup_realize_queue(u);
276✔
713
}
714

715
static const char* const scope_result_table[_SCOPE_RESULT_MAX] = {
716
        [SCOPE_SUCCESS]           = "success",
717
        [SCOPE_FAILURE_RESOURCES] = "resources",
718
        [SCOPE_FAILURE_TIMEOUT]   = "timeout",
719
        [SCOPE_FAILURE_OOM_KILL]  = "oom-kill",
720
};
721

722
DEFINE_STRING_TABLE_LOOKUP(scope_result, ScopeResult);
160✔
723

724
const UnitVTable scope_vtable = {
725
        .object_size = sizeof(Scope),
726
        .cgroup_context_offset = offsetof(Scope, cgroup_context),
727
        .kill_context_offset = offsetof(Scope, kill_context),
728
        .cgroup_runtime_offset = offsetof(Scope, cgroup_runtime),
729

730
        .sections =
731
                "Unit\0"
732
                "Scope\0"
733
                "Install\0",
734
        .private_section = "Scope",
735

736
        .can_transient = true,
737
        .can_delegate = true,
738
        .can_fail = true,
739
        .once_only = true,
740
        .can_set_managed_oom = true,
741

742
        .init = scope_init,
743
        .load = scope_load,
744
        .done = scope_done,
745

746
        .coldplug = scope_coldplug,
747

748
        .dump = scope_dump,
749

750
        .start = scope_start,
751
        .stop = scope_stop,
752

753
        .freezer_action = unit_cgroup_freezer_action,
754

755
        .get_timeout = scope_get_timeout,
756

757
        .serialize = scope_serialize,
758
        .deserialize_item = scope_deserialize_item,
759

760
        .active_state = scope_active_state,
761
        .sub_state_to_string = scope_sub_state_to_string,
762

763
        .sigchld_event = scope_sigchld_event,
764

765
        .reset_failed = scope_reset_failed,
766

767
        .notify_cgroup_empty = scope_notify_cgroup_empty_event,
768
        .notify_cgroup_oom = scope_notify_cgroup_oom_event,
769

770
        .bus_set_property = bus_scope_set_property,
771
        .bus_commit_properties = bus_scope_commit_properties,
772

773
        .enumerate_perpetual = scope_enumerate_perpetual,
774
};
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