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

systemd / systemd / 14872145375

06 May 2025 09:07PM UTC coverage: 72.232% (+0.02%) from 72.214%
14872145375

push

github

DaanDeMeyer
string-table: annotate _to_string and _from_string with _const_ and _pure_, respectively

Follow-up for c94f6ab1b

297286 of 411572 relevant lines covered (72.23%)

695615.99 hits per line

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

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

3
#include <errno.h>
4
#include <unistd.h>
5

6
#include "alloc-util.h"
7
#include "cgroup-setup.h"
8
#include "dbus-scope.h"
9
#include "dbus-unit.h"
10
#include "exit-status.h"
11
#include "load-dropin.h"
12
#include "log.h"
13
#include "manager.h"
14
#include "parse-util.h"
15
#include "process-util.h"
16
#include "random-util.h"
17
#include "scope.h"
18
#include "serialize.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 "unit-name.h"
25
#include "user-util.h"
26

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

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

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

42
        assert(u->load_state == UNIT_STUB);
339✔
43

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

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

54
        s->controller = mfree(s->controller);
339✔
55
        s->controller_track = sd_bus_track_unref(s->controller_track);
339✔
56

57
        s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
339✔
58

59
        s->user = mfree(s->user);
339✔
60
        s->group = mfree(s->group);
339✔
61
}
339✔
62

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

66
        assert(s);
328✔
67

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

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

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

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

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

87
        assert(s);
341✔
88

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

92
        old_state = s->state;
341✔
93
        s->state = state;
341✔
94

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

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

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

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

108
static int scope_add_default_dependencies(Scope *s) {
329✔
109
        int r;
329✔
110

111
        assert(s);
329✔
112

113
        if (!UNIT(s)->default_dependencies)
329✔
114
                return 0;
115

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

125
        return 0;
126
}
127

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

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

137
        return 0;
138
}
139

140
static int scope_load_init_scope(Unit *u) {
329✔
141
        assert(u);
329✔
142

143
        if (!unit_has_name(u, SPECIAL_INIT_SCOPE))
329✔
144
                return 0;
145

146
        u->transient = true;
272✔
147
        u->perpetual = true;
272✔
148

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

152
        u->default_dependencies = false;
272✔
153

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

160
        return 1;
161
}
162

163
static int scope_add_extras(Scope *s) {
329✔
164
        int r;
329✔
165

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

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

174
        if (s->oom_policy < 0)
329✔
175
                s->oom_policy = s->cgroup_context.delegate ? OOM_CONTINUE : UNIT(s)->manager->defaults.oom_policy;
275✔
176

177
        s->cgroup_context.memory_oom_group = s->oom_policy == OOM_KILL;
329✔
178

179
        return scope_add_default_dependencies(s);
329✔
180
}
181

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

186
        assert(u->load_state == UNIT_STUB);
355✔
187

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

192
        r = scope_load_init_scope(u);
329✔
193
        if (r < 0)
329✔
194
                return r;
195

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

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

203
        r = scope_add_extras(s);
329✔
204
        if (r < 0)
329✔
205
                return r;
206

207
        return scope_verify(s);
329✔
208
}
209

210
static usec_t scope_coldplug_timeout(Scope *s) {
312✔
211
        assert(s);
312✔
212

213
        switch (s->deserialized_state) {
312✔
214

215
        case SCOPE_RUNNING:
312✔
216
                return scope_running_timeout(s);
312✔
217

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

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

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

231
        assert(s->state == SCOPE_DEAD);
313✔
232

233
        if (s->deserialized_state == s->state)
313✔
234
                return 0;
235

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

240
        if (!IN_SET(s->deserialized_state, SCOPE_DEAD, SCOPE_FAILED) && u->pids) {
312✔
241
                PidRef *pid;
×
242
                SET_FOREACH(pid, u->pids) {
×
243
                        r = unit_watch_pidref(u, pid, /* exclusive= */ false);
×
244
                        if (r < 0)
×
245
                                return r;
×
246
                }
247
        }
248

249
        bus_scope_track_controller(s);
312✔
250

251
        scope_set_state(s, s->deserialized_state);
312✔
252
        return 0;
312✔
253
}
254

255
static void scope_dump(Unit *u, FILE *f, const char *prefix) {
6✔
256
        Scope *s = ASSERT_PTR(SCOPE(u));
6✔
257

258
        assert(f);
6✔
259
        assert(prefix);
6✔
260

261
        fprintf(f,
12✔
262
                "%sScope State: %s\n"
263
                "%sResult: %s\n"
264
                "%sRuntimeMaxSec: %s\n"
265
                "%sRuntimeRandomizedExtraSec: %s\n"
266
                "%sOOMPolicy: %s\n",
267
                prefix, scope_state_to_string(s->state),
268
                prefix, scope_result_to_string(s->result),
269
                prefix, FORMAT_TIMESPAN(s->runtime_max_usec, USEC_PER_SEC),
6✔
270
                prefix, FORMAT_TIMESPAN(s->runtime_rand_extra_usec, USEC_PER_SEC),
6✔
271
                prefix, oom_policy_to_string(s->oom_policy));
272

273
        cgroup_context_dump(u, f, prefix);
6✔
274
        kill_context_dump(&s->kill_context, f, prefix);
6✔
275
}
6✔
276

277
static void scope_enter_dead(Scope *s, ScopeResult f) {
8✔
278
        assert(s);
8✔
279

280
        if (s->result == SCOPE_SUCCESS)
8✔
281
                s->result = f;
8✔
282

283
        unit_log_result(UNIT(s), s->result == SCOPE_SUCCESS, scope_result_to_string(s->result));
8✔
284
        scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD);
16✔
285
}
8✔
286

287
static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
×
288
        bool skip_signal = false;
×
289
        int r;
×
290

291
        assert(s);
×
292

293
        if (s->result == SCOPE_SUCCESS)
×
294
                s->result = f;
×
295

296
        /* If we have a controller set let's ask the controller nicely to terminate the scope, instead of us going
297
         * directly into SIGTERM berserk mode */
298
        if (state == SCOPE_STOP_SIGTERM)
×
299
                skip_signal = bus_scope_send_request_stop(s) > 0;
×
300

301
        if (skip_signal)
×
302
                r = 1; /* wait */
303
        else {
304
                r = unit_kill_context(
×
305
                                UNIT(s),
×
306
                                state != SCOPE_STOP_SIGTERM ? KILL_KILL :
307
                                s->was_abandoned            ? KILL_TERMINATE_AND_LOG :
×
308
                                                              KILL_TERMINATE);
309
                if (r < 0) {
×
310
                        log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m");
×
311
                        goto fail;
×
312
                }
313
        }
314

315
        if (r > 0) {
×
316
                r = scope_arm_timer(s, /* relative= */ true, s->timeout_stop_usec);
×
317
                if (r < 0) {
×
318
                        log_unit_warning_errno(UNIT(s), r, "Failed to install timer: %m");
×
319
                        goto fail;
×
320
                }
321

322
                scope_set_state(s, state);
×
323
        } else if (state == SCOPE_STOP_SIGTERM)
×
324
                scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_SUCCESS);
×
325
        else
326
                scope_enter_dead(s, SCOPE_SUCCESS);
×
327

328
        return;
329

330
fail:
×
331
        scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
×
332
}
333

334
static int scope_enter_start_chown(Scope *s) {
×
335
        Unit *u = UNIT(ASSERT_PTR(s));
×
336
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
×
337
        int r;
×
338

339
        assert(s->user);
×
340

341
        if (!s->cgroup_runtime)
×
342
                return -EINVAL;
343

344
        r = scope_arm_timer(s, /* relative= */ true, u->manager->defaults.timeout_start_usec);
×
345
        if (r < 0)
×
346
                return r;
347

348
        r = unit_fork_helper_process(u, "(sd-chown-cgroup)", /* into_cgroup= */ true, &pidref);
×
349
        if (r < 0)
1✔
350
                goto fail;
×
351

352
        if (r == 0) {
1✔
353
                uid_t uid = UID_INVALID;
1✔
354
                gid_t gid = GID_INVALID;
1✔
355

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

359
                        r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
1✔
360
                        if (r < 0) {
1✔
361
                                log_unit_error_errno(UNIT(s), r, "Failed to resolve user \"%s\": %m", user);
×
362
                                _exit(EXIT_USER);
×
363
                        }
364
                }
365

366
                if (!isempty(s->group)) {
1✔
367
                        const char *group = s->group;
×
368

369
                        r = get_group_creds(&group, &gid, 0);
×
370
                        if (r < 0) {
×
371
                                log_unit_error_errno(UNIT(s), r, "Failed to resolve group \"%s\": %m", group);
×
372
                                _exit(EXIT_GROUP);
×
373
                        }
374
                }
375

376
                r = cg_set_access(s->cgroup_runtime->cgroup_path, uid, gid);
1✔
377
                if (r < 0) {
1✔
378
                        log_unit_error_errno(UNIT(s), r, "Failed to adjust control group access: %m");
×
379
                        _exit(EXIT_CGROUP);
×
380
                }
381

382
                _exit(EXIT_SUCCESS);
1✔
383
        }
384

385
        r = unit_watch_pidref(UNIT(s), &pidref, /* exclusive= */ true);
×
386
        if (r < 0)
×
387
                goto fail;
×
388

389
        scope_set_state(s, SCOPE_START_CHOWN);
×
390

391
        return 1;
392
fail:
×
393
        s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
×
394
        return r;
×
395
}
396

397
static int scope_enter_running(Scope *s) {
16✔
398
        Unit *u = UNIT(ASSERT_PTR(s));
16✔
399
        int r;
16✔
400

401
        (void) bus_scope_track_controller(s);
16✔
402

403
        r = unit_acquire_invocation_id(u);
16✔
404
        if (r < 0)
16✔
405
                return r;
406

407
        unit_export_state_files(u);
16✔
408

409
        r = unit_attach_pids_to_cgroup(u, u->pids, NULL);
16✔
410
        if (r < 0) {
16✔
411
                log_unit_warning_errno(u, r, "Failed to add PIDs to scope's control group: %m");
×
412
                goto fail;
×
413
        }
414
        if (r == 0) {
16✔
415
                r = log_unit_warning_errno(u, SYNTHETIC_ERRNO(ECHILD), "No PIDs left to attach to the scope's control group, refusing.");
×
416
                goto fail;
×
417
        }
418
        log_unit_debug(u, "%i %s added to scope's control group.", r, r == 1 ? "process" : "processes");
16✔
419

420
        s->result = SCOPE_SUCCESS;
16✔
421

422
        scope_set_state(s, SCOPE_RUNNING);
16✔
423

424
        /* Set the maximum runtime timeout. */
425
        scope_arm_timer(s, /* relative= */ false, scope_running_timeout(s));
16✔
426

427
        /* Unwatch all pids we've just added to cgroup. We rely on empty notifications there. */
428
        unit_unwatch_all_pids(u);
16✔
429

430
        return 1;
16✔
431

432
fail:
×
433
        scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
×
434
        return r;
×
435
}
436

437
static int scope_start(Unit *u) {
16✔
438
        Scope *s = ASSERT_PTR(SCOPE(u));
16✔
439

440
        if (unit_has_name(u, SPECIAL_INIT_SCOPE))
16✔
441
                return -EPERM;
442

443
        if (s->state == SCOPE_FAILED)
16✔
444
                return -EPERM;
445

446
        /* We can't fulfill this right now, please try again later */
447
        if (IN_SET(s->state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
16✔
448
                return -EAGAIN;
449

450
        assert(s->state == SCOPE_DEAD);
16✔
451

452
        if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
16✔
453
                return -ENOENT;
454

455
        (void) unit_realize_cgroup(u);
16✔
456
        (void) unit_reset_accounting(u);
16✔
457

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

463
        return scope_enter_running(s);
16✔
464
}
465

466
static int scope_stop(Unit *u) {
×
467
        Scope *s = ASSERT_PTR(SCOPE(u));
×
468

469
        if (IN_SET(s->state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
×
470
                return 0;
471

472
        assert(IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED));
×
473

474
        scope_enter_signal(s, SCOPE_STOP_SIGTERM, SCOPE_SUCCESS);
×
475
        return 1;
×
476
}
477

478
static void scope_reset_failed(Unit *u) {
2✔
479
        Scope *s = ASSERT_PTR(SCOPE(u));
2✔
480

481
        if (s->state == SCOPE_FAILED)
2✔
482
                scope_set_state(s, SCOPE_DEAD);
×
483

484
        s->result = SCOPE_SUCCESS;
2✔
485
}
2✔
486

487
static int scope_get_timeout(Unit *u, usec_t *timeout) {
×
488
        Scope *s = ASSERT_PTR(SCOPE(u));
×
489
        usec_t t;
×
490
        int r;
×
491

492
        if (!s->timer_event_source)
×
493
                return 0;
×
494

495
        r = sd_event_source_get_time(s->timer_event_source, &t);
×
496
        if (r < 0)
×
497
                return r;
498
        if (t == USEC_INFINITY)
×
499
                return 0;
500

501
        *timeout = t;
×
502
        return 1;
×
503
}
504

505
static int scope_serialize(Unit *u, FILE *f, FDSet *fds) {
122✔
506
        Scope *s = ASSERT_PTR(SCOPE(u));
122✔
507
        PidRef *pid;
122✔
508

509
        assert(f);
122✔
510
        assert(fds);
122✔
511

512
        (void) serialize_item(f, "state", scope_state_to_string(s->state));
122✔
513
        (void) serialize_bool(f, "was-abandoned", s->was_abandoned);
122✔
514

515
        if (s->controller)
122✔
516
                (void) serialize_item(f, "controller", s->controller);
×
517

518
        SET_FOREACH(pid, u->pids)
123✔
519
                serialize_pidref(f, fds, "pids", pid);
1✔
520

521
        return 0;
122✔
522
}
523

524
static int scope_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
198✔
525
        Scope *s = ASSERT_PTR(SCOPE(u));
198✔
526
        int r;
198✔
527

528
        assert(key);
198✔
529
        assert(value);
198✔
530
        assert(fds);
198✔
531

532
        if (streq(key, "state")) {
198✔
533
                ScopeState state;
98✔
534

535
                state = scope_state_from_string(value);
98✔
536
                if (state < 0)
98✔
537
                        log_unit_debug(u, "Failed to parse state value: %s", value);
×
538
                else
539
                        s->deserialized_state = state;
98✔
540

541
        } else if (streq(key, "was-abandoned")) {
100✔
542
                int k;
98✔
543

544
                k = parse_boolean(value);
98✔
545
                if (k < 0)
98✔
546
                        log_unit_debug(u, "Failed to parse boolean value: %s", value);
×
547
                else
548
                        s->was_abandoned = k;
98✔
549
        } else if (streq(key, "controller")) {
2✔
550

551
                r = free_and_strdup(&s->controller, value);
×
552
                if (r < 0)
×
553
                        return log_oom();
×
554

555
        } else if (streq(key, "pids")) {
2✔
556
                _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2✔
557

558
                /* We don't check if we already received the pid before here because unit_watch_pidref()
559
                 * does this check internally and discards the new pidref if we already received it before. */
560
                if (deserialize_pidref(fds, value, &pidref) >= 0) {
2✔
561
                        r = unit_watch_pidref(u, &pidref, /* exclusive= */ false);
2✔
562
                        if (r < 0)
2✔
563
                                log_unit_debug(u, "Failed to watch PID, ignoring: %s", value);
×
564
                }
565
        } else
566
                log_unit_debug(u, "Unknown serialization key: %s", key);
×
567

568
        return 0;
569
}
570

571
static void scope_notify_cgroup_empty_event(Unit *u) {
8✔
572
        Scope *s = ASSERT_PTR(SCOPE(u));
8✔
573

574
        log_unit_debug(u, "cgroup is empty");
8✔
575

576
        if (IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
8✔
577
                scope_enter_dead(s, SCOPE_SUCCESS);
8✔
578
}
8✔
579

580
static void scope_notify_cgroup_oom_event(Unit *u, bool managed_oom) {
×
581
        Scope *s = ASSERT_PTR(SCOPE(u));
×
582

583
        if (managed_oom)
×
584
                log_unit_debug(u, "Process(es) of control group were killed by systemd-oomd.");
×
585
        else
586
                log_unit_debug(u, "Process of control group was killed by the OOM killer.");
×
587

588
        if (s->oom_policy == OOM_CONTINUE)
×
589
                return;
590

591
        switch (s->state) {
×
592

593
        case SCOPE_START_CHOWN:
×
594
        case SCOPE_RUNNING:
595
                scope_enter_signal(s, SCOPE_STOP_SIGTERM, SCOPE_FAILURE_OOM_KILL);
×
596
                break;
×
597

598
        case SCOPE_STOP_SIGTERM:
×
599
                scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_FAILURE_OOM_KILL);
×
600
                break;
×
601

602
        case SCOPE_STOP_SIGKILL:
×
603
                if (s->result == SCOPE_SUCCESS)
×
604
                        s->result = SCOPE_FAILURE_OOM_KILL;
×
605
                break;
606
        /* SCOPE_DEAD, SCOPE_ABANDONED, and SCOPE_FAILED end up in default */
607
        default:
×
608
                ;
×
609
        }
610
}
611

612
static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) {
2,637✔
613
        Scope *s = ASSERT_PTR(SCOPE(u));
2,637✔
614

615
        if (s->state == SCOPE_START_CHOWN) {
2,637✔
616
                if (!is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
×
617
                        scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
×
618
                else
619
                        scope_enter_running(s);
×
620
                return;
×
621
        }
622
}
623

624
static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) {
×
625
        Scope *s = ASSERT_PTR(SCOPE(userdata));
×
626

627
        assert(s->timer_event_source == source);
×
628

629
        switch (s->state) {
×
630

631
        case SCOPE_RUNNING:
632
                log_unit_warning(UNIT(s), "Scope reached runtime time limit. Stopping.");
×
633
                scope_enter_signal(s, SCOPE_STOP_SIGTERM, SCOPE_FAILURE_TIMEOUT);
×
634
                break;
×
635

636
        case SCOPE_STOP_SIGTERM:
×
637
                if (s->kill_context.send_sigkill) {
×
638
                        log_unit_warning(UNIT(s), "Stopping timed out. Killing.");
×
639
                        scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_FAILURE_TIMEOUT);
×
640
                } else {
641
                        log_unit_warning(UNIT(s), "Stopping timed out. Skipping SIGKILL.");
×
642
                        scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
×
643
                }
644

645
                break;
646

647
        case SCOPE_STOP_SIGKILL:
648
                log_unit_warning(UNIT(s), "Still around after SIGKILL. Ignoring.");
×
649
                scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
×
650
                break;
×
651

652
        case SCOPE_START_CHOWN:
653
                log_unit_warning(UNIT(s), "User lookup timed out. Entering failed state.");
×
654
                scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
×
655
                break;
×
656

657
        default:
×
658
                assert_not_reached();
×
659
        }
660

661
        return 0;
×
662
}
663

664
int scope_abandon(Scope *s) {
7✔
665
        assert(s);
7✔
666

667
        if (unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE))
7✔
668
                return -EPERM;
669

670
        if (!IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED))
7✔
671
                return -ESTALE;
672

673
        s->was_abandoned = true;
5✔
674

675
        s->controller = mfree(s->controller);
5✔
676
        s->controller_track = sd_bus_track_unref(s->controller_track);
5✔
677

678
        scope_set_state(s, SCOPE_ABANDONED);
5✔
679

680
        return 0;
5✔
681
}
682

683
static UnitActiveState scope_active_state(Unit *u) {
14,419✔
684
        Scope *s = ASSERT_PTR(SCOPE(u));
14,419✔
685

686
        return state_translation_table[s->state];
14,419✔
687
}
688

689
static const char *scope_sub_state_to_string(Unit *u) {
151✔
690
        Scope *s = ASSERT_PTR(SCOPE(u));
151✔
691

692
        return scope_state_to_string(s->state);
151✔
693
}
694

695
static void scope_enumerate_perpetual(Manager *m) {
272✔
696
        Unit *u;
272✔
697
        int r;
272✔
698

699
        assert(m);
272✔
700

701
        /* Let's unconditionally add the "init.scope" special unit
702
         * that encapsulates PID 1. Note that PID 1 already is in the
703
         * cgroup for this, we hence just need to allocate the object
704
         * for it and that's it. */
705

706
        u = manager_get_unit(m, SPECIAL_INIT_SCOPE);
272✔
707
        if (!u) {
272✔
708
                r = unit_new_for_name(m, sizeof(Scope), SPECIAL_INIT_SCOPE, &u);
272✔
709
                if (r < 0)  {
272✔
710
                        log_error_errno(r, "Failed to allocate the special " SPECIAL_INIT_SCOPE " unit: %m");
×
711
                        return;
×
712
                }
713
        }
714

715
        u->transient = true;
272✔
716
        u->perpetual = true;
272✔
717
        SCOPE(u)->deserialized_state = SCOPE_RUNNING;
272✔
718

719
        unit_add_to_load_queue(u);
272✔
720
        unit_add_to_dbus_queue(u);
272✔
721
        /* Enqueue an explicit cgroup realization here. Unlike other cgroups this one already exists and is
722
         * populated (by us, after all!) already, even when we are not in a reload cycle. Hence we cannot
723
         * apply the settings at creation time anymore, but let's at least apply them asynchronously. */
724
        unit_add_to_cgroup_realize_queue(u);
272✔
725
}
726

727
static const char* const scope_result_table[_SCOPE_RESULT_MAX] = {
728
        [SCOPE_SUCCESS]           = "success",
729
        [SCOPE_FAILURE_RESOURCES] = "resources",
730
        [SCOPE_FAILURE_TIMEOUT]   = "timeout",
731
        [SCOPE_FAILURE_OOM_KILL]  = "oom-kill",
732
};
733

734
DEFINE_STRING_TABLE_LOOKUP(scope_result, ScopeResult);
196✔
735

736
const UnitVTable scope_vtable = {
737
        .object_size = sizeof(Scope),
738
        .cgroup_context_offset = offsetof(Scope, cgroup_context),
739
        .kill_context_offset = offsetof(Scope, kill_context),
740
        .cgroup_runtime_offset = offsetof(Scope, cgroup_runtime),
741

742
        .sections =
743
                "Unit\0"
744
                "Scope\0"
745
                "Install\0",
746
        .private_section = "Scope",
747

748
        .can_transient = true,
749
        .can_delegate = true,
750
        .can_fail = true,
751
        .once_only = true,
752
        .can_set_managed_oom = true,
753

754
        .init = scope_init,
755
        .load = scope_load,
756
        .done = scope_done,
757

758
        .coldplug = scope_coldplug,
759

760
        .dump = scope_dump,
761

762
        .start = scope_start,
763
        .stop = scope_stop,
764

765
        .freezer_action = unit_cgroup_freezer_action,
766

767
        .get_timeout = scope_get_timeout,
768

769
        .serialize = scope_serialize,
770
        .deserialize_item = scope_deserialize_item,
771

772
        .active_state = scope_active_state,
773
        .sub_state_to_string = scope_sub_state_to_string,
774

775
        .sigchld_event = scope_sigchld_event,
776

777
        .reset_failed = scope_reset_failed,
778

779
        .notify_cgroup_empty = scope_notify_cgroup_empty_event,
780
        .notify_cgroup_oom = scope_notify_cgroup_oom_event,
781

782
        .bus_set_property = bus_scope_set_property,
783
        .bus_commit_properties = bus_scope_commit_properties,
784

785
        .enumerate_perpetual = scope_enumerate_perpetual,
786
};
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