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

systemd / systemd / 20447389715

21 Dec 2025 07:31PM UTC coverage: 72.37% (-0.1%) from 72.5%
20447389715

push

github

DaanDeMeyer
mkosi: Use initrd as exitrd

Let's speed up image builds by avoiding building
an exitrd and instead reusing the initrd image for
the same purpose.

308584 of 426400 relevant lines covered (72.37%)

1134231.7 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-notify.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) {
3,037✔
43
        assert(verb);
3,037✔
44

45
        FOREACH_ELEMENT(i, unit_actions)
6,938✔
46
                if (streq(i->verb, verb))
6,938✔
47
                        return i->method;
3,037✔
48

49
        return "StartUnit";
50
}
51

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

55
        FOREACH_ELEMENT(i, unit_actions)
6,938✔
56
                if (streq(i->verb, verb))
6,938✔
57
                        return i->job_type;
3,037✔
58

59
        return "start";
60
}
61

62
static int start_unit_one(
3,112✔
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;
3,112✔
73
        const char *path;
3,112✔
74
        bool done = false;
3,112✔
75
        int r;
3,112✔
76

77
        assert(method);
3,112✔
78
        assert(name);
3,112✔
79
        assert(mode);
3,112✔
80
        assert(error);
3,112✔
81

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

86
        if (arg_dry_run)
3,112✔
87
                return 0;
88

89
        if (arg_show_transaction) {
3,108✔
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);
3,098✔
142
                if (r < 0)
3,098✔
143
                        goto fail;
69✔
144

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

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

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

160
        if (wu) {
3,039✔
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:
69✔
169
        /* There's always a fallback possible for legacy actions. */
170
        if (arg_action != ACTION_SYSTEMCTL)
69✔
171
                return r;
172

173
        if (sd_bus_error_has_name(error, BUS_ERROR_UNIT_MASKED) &&
69✔
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));
69✔
182

183
        if (!sd_bus_error_has_names(error, BUS_ERROR_NO_SUCH_UNIT,
69✔
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_RESCUE]                 = { SPECIAL_RESCUE_TARGET,                 "rescue",                 "isolate"              },
236
        [ACTION_EMERGENCY]              = { SPECIAL_EMERGENCY_TARGET,              "emergency",              "isolate"              },
237
        [ACTION_DEFAULT]                = { SPECIAL_DEFAULT_TARGET,                "default",                "isolate"              },
238
        [ACTION_EXIT]                   = { SPECIAL_EXIT_TARGET,                   "exit",                   "replace-irreversibly" },
239
        [ACTION_SUSPEND]                = { SPECIAL_SUSPEND_TARGET,                "suspend",                "replace-irreversibly" },
240
        [ACTION_HIBERNATE]              = { SPECIAL_HIBERNATE_TARGET,              "hibernate",              "replace-irreversibly" },
241
        [ACTION_HYBRID_SLEEP]           = { SPECIAL_HYBRID_SLEEP_TARGET,           "hybrid-sleep",           "replace-irreversibly" },
242
        [ACTION_SUSPEND_THEN_HIBERNATE] = { SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET, "suspend-then-hibernate", "replace-irreversibly" },
243
        [ACTION_SLEEP]                  = { NULL, /* handled only by logind */     "sleep",                  NULL                   },
244
};
245

246
enum action verb_to_action(const char *verb) {
3,080✔
247
        assert(verb);
3,080✔
248

249
        for (enum action i = 0; i < _ACTION_MAX; i++)
64,045✔
250
                if (streq_ptr(action_table[i].verb, verb))
61,007✔
251
                        return i;
252

253
        return _ACTION_INVALID;
254
}
255

256
static const char** make_extra_args(const char *extra_args[static 4]) {
2,939✔
257
        size_t n = 0;
2,939✔
258

259
        assert(extra_args);
2,939✔
260

261
        if (arg_runtime_scope != RUNTIME_SCOPE_SYSTEM)
2,939✔
262
                extra_args[n++] = "--user";
17✔
263

264
        switch (arg_transport) {
2,939✔
265

266
        case BUS_TRANSPORT_REMOTE:
×
267
                extra_args[n++] = "-H";
×
268
                extra_args[n++] = arg_host;
×
269
                break;
×
270

271
        case BUS_TRANSPORT_MACHINE:
6✔
272
                extra_args[n++] = "-M";
6✔
273
                extra_args[n++] = arg_host;
6✔
274
                break;
6✔
275

276
        case BUS_TRANSPORT_CAPSULE:
1✔
277
                extra_args[n++] = "-C";
1✔
278
                extra_args[n++] = arg_host;
1✔
279
                break;
1✔
280

281
        case BUS_TRANSPORT_LOCAL:
282
                break;
283

284
        default:
×
285
                assert_not_reached();
×
286
        }
287

288
        extra_args[n] = NULL;
2,939✔
289
        return extra_args;
2,939✔
290
}
291

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

301
        if (arg_wait && !STR_IN_SET(argv[0], "start", "restart"))
3,042✔
302
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
303
                                       "--wait may only be used with the 'start' or 'restart' commands.");
304

305
        /* We cannot do sender tracking on the private bus, so we need the full one for RefUnit to implement
306
         * --wait */
307
        r = acquire_bus(arg_wait ? BUS_FULL : BUS_MANAGER, &bus);
3,042✔
308
        if (r < 0)
3,042✔
309
                return r;
310

311
        ask_password_agent_open_maybe();
3,042✔
312
        polkit_agent_open_maybe();
3,042✔
313

314
        if (arg_action == ACTION_SYSTEMCTL) {
3,042✔
315
                enum action action;
3,042✔
316

317
                action = verb_to_action(argv[0]);
3,042✔
318

319
                assert(action != ACTION_SLEEP);
3,042✔
320

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

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

350
                method = "StartUnit";
351
                job_type = "start";
352
                mode = action_table[arg_action].mode;
353
                one_name = action_table[arg_action].target;
354
        }
355

356
        if (one_name) {
4✔
357
                names = strv_new(one_name);
4✔
358
                if (!names)
4✔
359
                        return log_oom();
×
360
        } else if (!arg_marked) {
3,038✔
361
                bool expanded;
3,037✔
362

363
                r = expand_unit_names(bus, strv_skip(argv, 1), suffix, &names, &expanded);
3,037✔
364
                if (r < 0)
3,037✔
365
                        return log_error_errno(r, "Failed to expand names: %m");
×
366

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

377
        if (!arg_no_block) {
3,042✔
378
                r = bus_wait_for_jobs_new(bus, &w);
2,939✔
379
                if (r < 0)
2,939✔
380
                        return log_error_errno(r, "Could not watch jobs: %m");
×
381
        }
382

383
        if (arg_wait) {
3,042✔
384
                r = bus_wait_for_units_new(bus, &wu);
13✔
385
                if (r < 0)
13✔
386
                        return log_error_errno(r, "Failed to allocate unit watch context: %m");
×
387
        }
388

389
        _cleanup_(fork_notify_terminate) PidRef journal_pid = PIDREF_NULL;
3,042✔
390
        if (arg_marked)
3,042✔
391
                ret = enqueue_marked_jobs(bus, w);
1✔
392
        else {
393
                if (arg_verbose)
3,041✔
394
                        (void) journal_fork(arg_runtime_scope, names, &journal_pid);
×
395

396
                STRV_FOREACH(name, names) {
6,153✔
397
                        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3,112✔
398

399
                        r = start_unit_one(bus, method, job_type, *name, mode, &error, w, wu);
3,112✔
400
                        if (ret == EXIT_SUCCESS && r < 0)
3,112✔
401
                                ret = translate_bus_error_to_exit_status(r, &error);
68✔
402

403
                        if (r >= 0 && streq(method, "StopUnit")) {
3,112✔
404
                                r = strv_push(&stopped_units, *name);
1,909✔
405
                                if (r < 0)
1,909✔
406
                                        return log_oom();
×
407
                        }
408
                }
409
        }
410

411
        if (!arg_no_block) {
3,042✔
412
                const char *extra_args[4];
2,939✔
413
                WaitJobsFlags flags = 0;
2,939✔
414

415
                SET_FLAG(flags, BUS_WAIT_JOBS_LOG_ERROR, !arg_quiet);
2,939✔
416
                SET_FLAG(flags, BUS_WAIT_JOBS_LOG_SUCCESS, arg_show_transaction);
2,939✔
417
                r = bus_wait_for_jobs(w, flags, make_extra_args(extra_args));
2,939✔
418
                if (r < 0)
2,939✔
419
                        return r;
18✔
420

421
                /* When stopping units, warn if they can still be triggered by
422
                 * another active unit (socket, path, timer) */
423
                if (!arg_quiet && !arg_no_warn)
2,921✔
424
                        STRV_FOREACH(unit, stopped_units)
4,819✔
425
                                warn_triggering_units(bus, *unit, "Stopping", /* ignore_masked= */ true);
1,899✔
426
        }
427

428
        if (arg_wait) {
3,024✔
429
                r = bus_wait_for_units_run(wu);
13✔
430
                if (r < 0)
13✔
431
                        return log_error_errno(r, "Failed to wait for units: %m");
×
432
                if (r == BUS_WAIT_FAILURE && ret == EXIT_SUCCESS)
13✔
433
                        ret = EXIT_FAILURE;
1✔
434
        }
435

436
        return ret;
437
}
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