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

systemd / systemd / 16082515961

04 Jul 2025 08:23PM UTC coverage: 72.095% (-0.1%) from 72.193%
16082515961

push

github

poettering
seccomp-util: allowlist open_tree() as part of @file-system

Now that we make use of open_tree() in places we previously used
openat() with O_PATH, it makes sense to move it from @mount to
@file-system. Without the OPEN_TREE_CLONE flag open_tree() is after all
unprivileged.

Note that open_tree_attr() I left in @mount, since it's purpose is
really to set mount options when cloning, and that's clearly a mount
related thing, not so much something you could use unpriv.

Follow-up for: c5de7b14a

This addresses an issue tracked down by Antonio Feijoo: since the commit
that started to use open_tree() various apps started to crash because
they used seccomp filters and sd-device started to use open_tree()
internally.

300842 of 417287 relevant lines covered (72.09%)

715300.57 hits per line

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

81.19
/src/systemctl/systemctl-start-unit.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include "sd-bus.h"
4

5
#include "alloc-util.h"
6
#include "ansi-color.h"
7
#include "bus-common-errors.h"
8
#include "bus-error.h"
9
#include "bus-locator.h"
10
#include "bus-util.h"
11
#include "bus-wait-for-jobs.h"
12
#include "bus-wait-for-units.h"
13
#include "fork-journal.h"
14
#include "pidref.h"
15
#include "runtime-scope.h"
16
#include "special.h"
17
#include "string-util.h"
18
#include "strv.h"
19
#include "systemctl.h"
20
#include "systemctl-start-unit.h"
21
#include "systemctl-util.h"
22

23
static const struct {
24
        const char *verb;      /* systemctl verb */
25
        const char *method;    /* Name of the specific D-Bus method */
26
        const char *job_type;  /* Job type when passing to the generic EnqueueUnitJob() method */
27
} unit_actions[] = {
28
        { "start",                 "StartUnit",              "start"                 },
29
        { "stop",                  "StopUnit",               "stop"                  },
30
        { "condstop",              "StopUnit",               "stop"                  }, /* legacy alias */
31
        { "reload",                "ReloadUnit",             "reload"                },
32
        { "restart",               "RestartUnit",            "restart"               },
33
        { "try-restart",           "TryRestartUnit",         "try-restart"           },
34
        { "condrestart",           "TryRestartUnit",         "try-restart"           }, /* legacy alias */
35
        { "reload-or-restart",     "ReloadOrRestartUnit",    "reload-or-restart"     },
36
        { "try-reload-or-restart", "ReloadOrTryRestartUnit", "reload-or-try-restart" },
37
        { "reload-or-try-restart", "ReloadOrTryRestartUnit", "reload-or-try-restart" }, /* legacy alias */
38
        { "condreload",            "ReloadOrTryRestartUnit", "reload-or-try-restart" }, /* legacy alias */
39
        { "force-reload",          "ReloadOrTryRestartUnit", "reload-or-try-restart" }, /* legacy alias */
40
};
41

42
static const char* verb_to_method(const char *verb) {
2,020✔
43
        assert(verb);
2,020✔
44

45
        FOREACH_ELEMENT(i, unit_actions)
4,666✔
46
                if (streq(i->verb, verb))
4,666✔
47
                        return i->method;
2,020✔
48

49
        return "StartUnit";
50
}
51

52
static const char* verb_to_job_type(const char *verb) {
2,020✔
53
        assert(verb);
2,020✔
54

55
        FOREACH_ELEMENT(i, unit_actions)
4,666✔
56
                if (streq(i->verb, verb))
4,666✔
57
                        return i->job_type;
2,020✔
58

59
        return "start";
60
}
61

62
static int start_unit_one(
2,084✔
63
                sd_bus *bus,
64
                const char *method,    /* When using classic per-job bus methods */
65
                const char *job_type,  /* When using new-style EnqueueUnitJob() */
66
                const char *name,
67
                const char *mode,
68
                sd_bus_error *error,
69
                BusWaitForJobs *w,
70
                BusWaitForUnits *wu) {
71

72
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2,084✔
73
        const char *path;
2,084✔
74
        bool done = false;
2,084✔
75
        int r;
2,084✔
76

77
        assert(method);
2,084✔
78
        assert(name);
2,084✔
79
        assert(mode);
2,084✔
80
        assert(error);
2,084✔
81

82
        log_debug("%s dbus call org.freedesktop.systemd1.Manager %s(%s, %s)",
2,150✔
83
                  arg_dry_run ? "Would execute" : "Executing",
84
                  method, name, mode);
85

86
        if (arg_dry_run)
2,084✔
87
                return 0;
88

89
        if (arg_show_transaction) {
2,080✔
90
                _cleanup_(sd_bus_error_free) sd_bus_error enqueue_error = SD_BUS_ERROR_NULL;
10✔
91

92
                /* Use the new, fancy EnqueueUnitJob() API if the user wants us to print the transaction */
93
                r = bus_call_method(
10✔
94
                                bus,
95
                                bus_systemd_mgr,
96
                                "EnqueueUnitJob",
97
                                &enqueue_error,
98
                                &reply,
99
                                "sss",
100
                                name, job_type, mode);
101
                if (r < 0) {
10✔
102
                        if (!sd_bus_error_has_name(&enqueue_error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
×
103
                                (void) sd_bus_error_move(error, &enqueue_error);
×
104
                                goto fail;
×
105
                        }
106

107
                        /* Hmm, the API is not yet available. Let's use the classic API instead (see below). */
108
                        log_notice("--show-transaction not supported by this service manager, proceeding without.");
×
109
                } else {
110
                        const char *u, *jt;
10✔
111
                        uint32_t id;
10✔
112

113
                        r = sd_bus_message_read(reply, "uosos", &id, &path, &u, NULL, &jt);
10✔
114
                        if (r < 0)
10✔
115
                                return bus_log_parse_error(r);
×
116

117
                        log_info("Enqueued anchor job %" PRIu32 " %s/%s.", id, u, jt);
10✔
118

119
                        r = sd_bus_message_enter_container(reply, 'a', "(uosos)");
10✔
120
                        if (r < 0)
10✔
121
                                return bus_log_parse_error(r);
×
122
                        for (;;) {
13✔
123
                                r = sd_bus_message_read(reply, "(uosos)", &id, NULL, &u, NULL, &jt);
13✔
124
                                if (r < 0)
13✔
125
                                        return bus_log_parse_error(r);
×
126
                                if (r == 0)
13✔
127
                                        break;
128

129
                                log_info("Enqueued auxiliary job %" PRIu32 " %s/%s.", id, u, jt);
3✔
130
                        }
131

132
                        r = sd_bus_message_exit_container(reply);
10✔
133
                        if (r < 0)
10✔
134
                                return bus_log_parse_error(r);
×
135

136
                        done = true;
10✔
137
                }
138
        }
139

140
        if (!done) {
10✔
141
                r = bus_call_method(bus, bus_systemd_mgr, method, error, &reply, "ss", name, mode);
2,070✔
142
                if (r < 0)
2,070✔
143
                        goto fail;
67✔
144

145
                r = sd_bus_message_read(reply, "o", &path);
2,003✔
146
                if (r < 0)
2,003✔
147
                        return bus_log_parse_error(r);
×
148
        }
149

150
        if (need_daemon_reload(bus, name) > 0)
2,013✔
151
                warn_unit_file_changed(name);
×
152

153
        if (w) {
2,013✔
154
                log_debug("Adding %s to the set", path);
1,921✔
155
                r = bus_wait_for_jobs_add(w, path);
1,921✔
156
                if (r < 0)
1,921✔
157
                        return log_error_errno(r, "Failed to watch job for %s: %m", name);
×
158
        }
159

160
        if (wu) {
2,013✔
161
                r = bus_wait_for_units_add_unit(wu, name, BUS_WAIT_FOR_INACTIVE|BUS_WAIT_NO_JOB, NULL, NULL);
14✔
162
                if (r < 0)
14✔
163
                        return log_error_errno(r, "Failed to watch unit %s: %m", name);
×
164
        }
165

166
        return 0;
167

168
fail:
67✔
169
        /* There's always a fallback possible for legacy actions. */
170
        if (arg_action != ACTION_SYSTEMCTL)
67✔
171
                return r;
172

173
        if (sd_bus_error_has_name(error, BUS_ERROR_UNIT_MASKED) &&
67✔
174
            STR_IN_SET(method, "TryRestartUnit", "ReloadOrTryRestartUnit")) {
×
175
                /* Ignore masked unit if try-* is requested */
176

177
                log_debug_errno(r, "Failed to %s %s, ignoring: %s", job_type, name, bus_error_message(error, r));
×
178
                return 0;
×
179
        }
180

181
        log_error_errno(r, "Failed to %s %s: %s", job_type, name, bus_error_message(error, r));
67✔
182

183
        if (!sd_bus_error_has_names(error, BUS_ERROR_NO_SUCH_UNIT,
67✔
184
                                           BUS_ERROR_UNIT_MASKED,
185
                                           BUS_ERROR_JOB_TYPE_NOT_APPLICABLE))
186
                log_error("See %s logs and 'systemctl%s status%s %s' for details.",
62✔
187
                          runtime_scope_to_string(arg_runtime_scope),
188
                          arg_runtime_scope == RUNTIME_SCOPE_SYSTEM ? "" : " --user",
189
                          name[0] == '-' ? " --" : "",
190
                          name);
191

192
        return r;
193
}
194

195
static int enqueue_marked_jobs(
1✔
196
                sd_bus *bus,
197
                BusWaitForJobs *w) {
198

199
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1✔
200
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1✔
201
        int r;
1✔
202

203
        log_debug("%s dbus call org.freedesktop.systemd1.Manager EnqueueMarkedJobs()",
1✔
204
                  arg_dry_run ? "Would execute" : "Executing");
205

206
        if (arg_dry_run)
1✔
207
                return 0;
208

209
        r = bus_call_method(bus, bus_systemd_mgr, "EnqueueMarkedJobs", &error, &reply, NULL);
1✔
210
        if (r < 0)
1✔
211
                return log_error_errno(r, "Failed to start jobs: %s", bus_error_message(&error, r));
×
212

213
        _cleanup_strv_free_ char **paths = NULL;
1✔
214
        r = sd_bus_message_read_strv(reply, &paths);
1✔
215
        if (r < 0)
1✔
216
                return bus_log_parse_error(r);
×
217

218
        if (w)
1✔
219
                STRV_FOREACH(path, paths) {
2✔
220
                        log_debug("Adding %s to the set", *path);
1✔
221
                        r = bus_wait_for_jobs_add(w, *path);
1✔
222
                        if (r < 0)
1✔
223
                                return log_error_errno(r, "Failed to watch job %s: %m", *path);
×
224
                }
225

226
        return 0;
227
}
228

229
const struct action_metadata action_table[_ACTION_MAX] = {
230
        [ACTION_HALT]                   = { SPECIAL_HALT_TARGET,                   "halt",                   "replace-irreversibly" },
231
        [ACTION_POWEROFF]               = { SPECIAL_POWEROFF_TARGET,               "poweroff",               "replace-irreversibly" },
232
        [ACTION_REBOOT]                 = { SPECIAL_REBOOT_TARGET,                 "reboot",                 "replace-irreversibly" },
233
        [ACTION_KEXEC]                  = { SPECIAL_KEXEC_TARGET,                  "kexec",                  "replace-irreversibly" },
234
        [ACTION_SOFT_REBOOT]            = { SPECIAL_SOFT_REBOOT_TARGET,            "soft-reboot",            "replace-irreversibly" },
235
        [ACTION_RUNLEVEL2]              = { SPECIAL_MULTI_USER_TARGET,             NULL,                     "isolate"              },
236
        [ACTION_RUNLEVEL3]              = { SPECIAL_MULTI_USER_TARGET,             NULL,                     "isolate"              },
237
        [ACTION_RUNLEVEL4]              = { SPECIAL_MULTI_USER_TARGET,             NULL,                     "isolate"              },
238
        [ACTION_RUNLEVEL5]              = { SPECIAL_GRAPHICAL_TARGET,              NULL,                     "isolate"              },
239
        [ACTION_RESCUE]                 = { SPECIAL_RESCUE_TARGET,                 "rescue",                 "isolate"              },
240
        [ACTION_EMERGENCY]              = { SPECIAL_EMERGENCY_TARGET,              "emergency",              "isolate"              },
241
        [ACTION_DEFAULT]                = { SPECIAL_DEFAULT_TARGET,                "default",                "isolate"              },
242
        [ACTION_EXIT]                   = { SPECIAL_EXIT_TARGET,                   "exit",                   "replace-irreversibly" },
243
        [ACTION_SUSPEND]                = { SPECIAL_SUSPEND_TARGET,                "suspend",                "replace-irreversibly" },
244
        [ACTION_HIBERNATE]              = { SPECIAL_HIBERNATE_TARGET,              "hibernate",              "replace-irreversibly" },
245
        [ACTION_HYBRID_SLEEP]           = { SPECIAL_HYBRID_SLEEP_TARGET,           "hybrid-sleep",           "replace-irreversibly" },
246
        [ACTION_SUSPEND_THEN_HIBERNATE] = { SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET, "suspend-then-hibernate", "replace-irreversibly" },
247
        [ACTION_SLEEP]                  = { NULL, /* handled only by logind */     "sleep",                  NULL                   },
248
};
249

250
enum action verb_to_action(const char *verb) {
2,063✔
251
        assert(verb);
2,063✔
252

253
        for (enum action i = 0; i < _ACTION_MAX; i++)
52,817✔
254
                if (streq_ptr(action_table[i].verb, verb))
50,796✔
255
                        return i;
256

257
        return _ACTION_INVALID;
258
}
259

260
static const char** make_extra_args(const char *extra_args[static 4]) {
1,932✔
261
        size_t n = 0;
1,932✔
262

263
        assert(extra_args);
1,932✔
264

265
        if (arg_runtime_scope != RUNTIME_SCOPE_SYSTEM)
1,932✔
266
                extra_args[n++] = "--user";
8✔
267

268
        switch (arg_transport) {
1,932✔
269

270
        case BUS_TRANSPORT_REMOTE:
×
271
                extra_args[n++] = "-H";
×
272
                extra_args[n++] = arg_host;
×
273
                break;
×
274

275
        case BUS_TRANSPORT_MACHINE:
6✔
276
                extra_args[n++] = "-M";
6✔
277
                extra_args[n++] = arg_host;
6✔
278
                break;
6✔
279

280
        case BUS_TRANSPORT_CAPSULE:
1✔
281
                extra_args[n++] = "-C";
1✔
282
                extra_args[n++] = arg_host;
1✔
283
                break;
1✔
284

285
        case BUS_TRANSPORT_LOCAL:
286
                break;
287

288
        default:
×
289
                assert_not_reached();
×
290
        }
291

292
        extra_args[n] = NULL;
1,932✔
293
        return extra_args;
1,932✔
294
}
295

296
int verb_start(int argc, char *argv[], void *userdata) {
2,025✔
297
        _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *wu = NULL;
2,025✔
298
        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
×
299
        const char *method, *job_type, *mode, *one_name, *suffix = NULL;
2,025✔
300
        _cleanup_free_ char **stopped_units = NULL; /* Do not use _cleanup_strv_free_ */
2,025✔
301
        _cleanup_strv_free_ char **names = NULL;
2,025✔
302
        int r, ret = EXIT_SUCCESS;
2,025✔
303
        sd_bus *bus;
2,025✔
304

305
        if (arg_wait && !STR_IN_SET(argv[0], "start", "restart"))
2,025✔
306
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
307
                                       "--wait may only be used with the 'start' or 'restart' commands.");
308

309
        /* We cannot do sender tracking on the private bus, so we need the full one for RefUnit to implement
310
         * --wait */
311
        r = acquire_bus(arg_wait ? BUS_FULL : BUS_MANAGER, &bus);
2,025✔
312
        if (r < 0)
2,025✔
313
                return r;
314

315
        ask_password_agent_open_maybe();
2,025✔
316
        polkit_agent_open_maybe();
2,025✔
317

318
        if (arg_action == ACTION_SYSTEMCTL) {
2,025✔
319
                enum action action;
2,025✔
320

321
                action = verb_to_action(argv[0]);
2,025✔
322

323
                assert(action != ACTION_SLEEP);
2,025✔
324

325
                if (action != _ACTION_INVALID) {
2,025✔
326
                        /* A command in style "systemctl reboot", "systemctl poweroff", … */
327
                        method = "StartUnit";
4✔
328
                        job_type = "start";
4✔
329
                        mode = action_table[action].mode;
4✔
330
                        one_name = action_table[action].target;
4✔
331
                } else {
332
                        if (streq(argv[0], "isolate")) {
2,021✔
333
                                /* A "systemctl isolate <unit1> <unit2> …" command */
334
                                method = "StartUnit";
335
                                job_type = "start";
336
                                mode = "isolate";
337
                                suffix = ".target";
338
                        } else if (!arg_marked) {
2,021✔
339
                                /* A command in style of "systemctl start <unit1> <unit2> …", "systemctl stop <unit1> <unit2> …" and so on */
340
                                method = verb_to_method(argv[0]);
2,020✔
341
                                job_type = verb_to_job_type(argv[0]);
2,020✔
342
                                mode = arg_job_mode();
2,020✔
343
                        } else
344
                                method = job_type = mode = NULL;
345

346
                        one_name = NULL;
347
                }
348
        } else {
349
                /* A SysV legacy command such as "halt", "reboot", "poweroff", … */
350
                assert(arg_action >= 0 && arg_action < _ACTION_MAX);
×
351
                assert(action_table[arg_action].target);
×
352
                assert(action_table[arg_action].mode);
×
353

354
                method = "StartUnit";
355
                job_type = "start";
356
                mode = action_table[arg_action].mode;
357
                one_name = action_table[arg_action].target;
358
        }
359

360
        if (one_name) {
4✔
361
                names = strv_new(one_name);
4✔
362
                if (!names)
4✔
363
                        return log_oom();
×
364
        } else if (!arg_marked) {
2,021✔
365
                bool expanded;
2,020✔
366

367
                r = expand_unit_names(bus, strv_skip(argv, 1), suffix, &names, &expanded);
2,020✔
368
                if (r < 0)
2,020✔
369
                        return log_error_errno(r, "Failed to expand names: %m");
×
370

371
                if (!arg_all && expanded && streq(job_type, "start") && !arg_quiet) {
2,020✔
372
                        log_warning("Warning: %ssystemctl start called with a glob pattern.%s",
×
373
                                    ansi_highlight_red(),
374
                                    ansi_normal());
375
                        log_notice("Hint: unit globs expand to loaded units, so start will usually have no effect.\n"
×
376
                                   "      Passing --all will also load units which are pulled in by other units.\n"
377
                                   "      See systemctl(1) for more details.");
378
                }
379
        }
380

381
        if (!arg_no_block) {
2,025✔
382
                r = bus_wait_for_jobs_new(bus, &w);
1,932✔
383
                if (r < 0)
1,932✔
384
                        return log_error_errno(r, "Could not watch jobs: %m");
×
385
        }
386

387
        if (arg_wait) {
2,025✔
388
                r = bus_wait_for_units_new(bus, &wu);
13✔
389
                if (r < 0)
13✔
390
                        return log_error_errno(r, "Failed to allocate unit watch context: %m");
×
391
        }
392

393
        _cleanup_(journal_terminate) PidRef journal_pid = PIDREF_NULL;
2,025✔
394
        if (arg_marked)
2,025✔
395
                ret = enqueue_marked_jobs(bus, w);
1✔
396
        else {
397
                if (arg_verbose)
2,024✔
398
                        (void) journal_fork(arg_runtime_scope, names, &journal_pid);
×
399

400
                STRV_FOREACH(name, names) {
4,108✔
401
                        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2,084✔
402

403
                        r = start_unit_one(bus, method, job_type, *name, mode, &error, w, wu);
2,084✔
404
                        if (ret == EXIT_SUCCESS && r < 0)
2,084✔
405
                                ret = translate_bus_error_to_exit_status(r, &error);
66✔
406

407
                        if (r >= 0 && streq(method, "StopUnit")) {
2,084✔
408
                                r = strv_push(&stopped_units, *name);
1,049✔
409
                                if (r < 0)
1,049✔
410
                                        return log_oom();
×
411
                        }
412
                }
413
        }
414

415
        if (!arg_no_block) {
2,025✔
416
                const char *extra_args[4];
1,932✔
417
                WaitJobsFlags flags = 0;
1,932✔
418

419
                SET_FLAG(flags, BUS_WAIT_JOBS_LOG_ERROR, !arg_quiet);
1,932✔
420
                SET_FLAG(flags, BUS_WAIT_JOBS_LOG_SUCCESS, arg_show_transaction);
1,932✔
421
                r = bus_wait_for_jobs(w, flags, make_extra_args(extra_args));
1,932✔
422
                if (r < 0)
1,932✔
423
                        return r;
17✔
424

425
                /* When stopping units, warn if they can still be triggered by
426
                 * another active unit (socket, path, timer) */
427
                if (!arg_quiet && !arg_no_warn)
1,915✔
428
                        STRV_FOREACH(unit, stopped_units)
2,958✔
429
                                warn_triggering_units(bus, *unit, "Stopping", /* ignore_masked = */ true);
1,044✔
430
        }
431

432
        if (arg_wait) {
2,008✔
433
                r = bus_wait_for_units_run(wu);
13✔
434
                if (r < 0)
13✔
435
                        return log_error_errno(r, "Failed to wait for units: %m");
×
436
                if (r == BUS_WAIT_FAILURE && ret == EXIT_SUCCESS)
13✔
437
                        ret = EXIT_FAILURE;
1✔
438
        }
439

440
        return ret;
441
}
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