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

systemd / systemd / 19020191358

02 Nov 2025 05:04PM UTC coverage: 72.222% (-0.02%) from 72.241%
19020191358

push

github

web-flow
Enhance docs for ukify and direct kernel boots (#39516)

305246 of 422650 relevant lines covered (72.22%)

1085243.28 hits per line

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

65.7
/src/systemctl/systemctl-enable.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include "sd-bus.h"
4

5
#include "alloc-util.h"
6
#include "bus-error.h"
7
#include "bus-locator.h"
8
#include "bus-unit-util.h"
9
#include "bus-util.h"
10
#include "glyph-util.h"
11
#include "install.h"
12
#include "log.h"
13
#include "path-lookup.h"
14
#include "path-util.h"
15
#include "string-util.h"
16
#include "strv-fundamental.h"
17
#include "strv.h"
18
#include "systemctl.h"
19
#include "systemctl-daemon-reload.h"
20
#include "systemctl-enable.h"
21
#include "systemctl-start-unit.h"
22
#include "systemctl-sysv-compat.h"
23
#include "systemctl-util.h"
24
#include "unit-name.h"
25
#include "verbs.h"
26

27
static int normalize_link_paths(char **paths) {
×
28
        int r;
×
29

30
        STRV_FOREACH(u, paths) {
×
31
                if (path_is_absolute(*u))
×
32
                        continue;
×
33

34
                if (!isempty(arg_root))
×
35
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
36
                                               "Non-absolute paths are not allowed when --root= is used: %s",
37
                                               *u);
38

39
                if (!is_path(*u))
×
40
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
41
                                               "Link argument must contain at least one directory separator.\n"
42
                                               "If you intended to link a file in the current directory, try './%s' instead.",
43
                                               *u);
44

45
                char *normalized_path;
×
46

47
                r = path_make_absolute_cwd(*u, &normalized_path);
×
48
                if (r < 0)
×
49
                        return log_error_errno(r, "Failed to normalize path '%s': %m", *u);
×
50

51
                path_simplify(normalized_path);
×
52

53
                free_and_replace(*u, normalized_path);
×
54
        }
55

56
        return 0;
57
}
58

59
static int normalize_names(char **names) {
15✔
60
        bool was_path = false;
15✔
61
        int r;
15✔
62

63
        STRV_FOREACH(u, names) {
30✔
64
                if (!is_path(*u))
15✔
65
                        continue;
15✔
66

67
                char *fn;
×
68

69
                r = path_extract_filename(*u, &fn);
×
70
                if (r < 0)
×
71
                        return log_error_errno(r, "Failed to extract file name from '%s': %m", *u);
×
72

73
                free_and_replace(*u, fn);
×
74

75
                was_path = true;
×
76
        }
77

78
        if (was_path)
15✔
79
                log_warning("Warning: Can't execute disable on the unit file path. Proceeding with the unit name.");
×
80

81
        return 0;
82
}
83

84
int verb_enable(int argc, char *argv[], void *userdata) {
71✔
85
        const char *verb = ASSERT_PTR(argv[0]);
71✔
86
        _cleanup_strv_free_ char **names = NULL;
71✔
87
        int carries_install_info = -1;
71✔
88
        bool ignore_carries_install_info = arg_quiet || arg_no_warn;
71✔
89
        sd_bus *bus = NULL;
71✔
90
        int r;
71✔
91

92
        if (streq(verb, "preset") && should_bypass("SYSTEMD_PRESET"))
71✔
93
                return 0;
94

95
        const char *operation = strjoina("to ", verb);
355✔
96
        r = mangle_names(operation, ASSERT_PTR(strv_skip(argv, 1)), &names);
71✔
97
        if (r < 0)
71✔
98
                return r;
99

100
        r = enable_sysv_units(verb, names);
71✔
101
        if (r < 0)
71✔
102
                return r;
103

104
        /* If the operation was fully executed by the SysV compat, let's finish early */
105
        if (strv_isempty(names)) {
71✔
106
                if (arg_no_reload || install_client_side() != INSTALL_CLIENT_SIDE_NO)
×
107
                        return 0;
×
108

109
                r = daemon_reload(ACTION_RELOAD, /* graceful= */ false);
×
110
                return r > 0 ? 0 : r;
×
111
        }
112

113
        if (streq(verb, "disable"))
71✔
114
                r = normalize_names(names);
15✔
115
        else if (streq(verb, "link"))
56✔
116
                r = normalize_link_paths(names);
×
117
        else
118
                r = 0;
119
        if (r < 0)
15✔
120
                return r;
121

122
        if (install_client_side() == INSTALL_CLIENT_SIDE_NO) {
71✔
123
                _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
106✔
124
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
54✔
125
                bool expect_carries_install_info = false;
54✔
126
                bool send_runtime = true, send_force = true, send_preset_mode = false;
54✔
127
                const char *method, *warn_trigger_operation = NULL;
54✔
128
                bool warn_trigger_ignore_masked = true; /* suppress "used uninitialized" warning */
54✔
129

130
                if (STR_IN_SET(verb, "mask", "unmask")) {
54✔
131
                        _cleanup_(lookup_paths_done) LookupPaths lp = {};
23✔
132

133
                        r = lookup_paths_init_or_warn(&lp, arg_runtime_scope, 0, arg_root);
23✔
134
                        if (r < 0)
23✔
135
                                return r;
136

137
                        STRV_FOREACH(name, names) {
49✔
138
                                r = unit_exists(&lp, *name);
26✔
139
                                if (r < 0)
26✔
140
                                        return r;
141
                                if (r == 0)
26✔
142
                                        log_notice("Unit %s does not exist, proceeding anyway.", *name);
×
143
                        }
144
                }
145

146
                r = acquire_bus(BUS_MANAGER, &bus);
54✔
147
                if (r < 0)
54✔
148
                        return r;
149

150
                polkit_agent_open_maybe();
54✔
151

152
                if (streq(verb, "enable")) {
54✔
153
                        method = "EnableUnitFiles";
154
                        expect_carries_install_info = true;
155
                } else if (streq(verb, "disable")) {
42✔
156
                        method = "DisableUnitFilesWithFlagsAndInstallInfo";
157
                        expect_carries_install_info = true;
158
                        send_force = false;
159

160
                        warn_trigger_operation = "Disabling";
161
                        warn_trigger_ignore_masked = true;
162
                } else if (streq(verb, "reenable")) {
30✔
163
                        method = "ReenableUnitFiles";
164
                        expect_carries_install_info = true;
165
                } else if (streq(verb, "link"))
29✔
166
                        method = "LinkUnitFiles";
167
                else if (streq(verb, "preset")) {
29✔
168

169
                        if (arg_preset_mode != UNIT_FILE_PRESET_FULL) {
3✔
170
                                method = "PresetUnitFilesWithMode";
171
                                send_preset_mode = true;
172
                        } else
173
                                method = "PresetUnitFiles";
1✔
174

175
                        expect_carries_install_info = true;
176
                        ignore_carries_install_info = true;
177
                } else if (streq(verb, "mask")) {
26✔
178
                        method = "MaskUnitFiles";
179

180
                        warn_trigger_operation = "Masking";
181
                        warn_trigger_ignore_masked = false;
182
                } else if (streq(verb, "unmask")) {
15✔
183
                        method = "UnmaskUnitFiles";
184
                        send_force = false;
185
                } else if (streq(verb, "revert")) {
3✔
186
                        method = "RevertUnitFiles";
187
                        send_runtime = send_force = false;
188
                } else
189
                        assert_not_reached();
×
190

191
                r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
54✔
192
                if (r < 0)
54✔
193
                        return bus_log_create_error(r);
×
194

195
                r = sd_bus_message_append_strv(m, names);
54✔
196
                if (r < 0)
54✔
197
                        return bus_log_create_error(r);
×
198

199
                if (send_preset_mode) {
54✔
200
                        r = sd_bus_message_append(m, "s", unit_file_preset_mode_to_string(arg_preset_mode));
2✔
201
                        if (r < 0)
2✔
202
                                return bus_log_create_error(r);
×
203
                }
204

205
                if (send_runtime) {
54✔
206
                        if (streq(method, "DisableUnitFilesWithFlagsAndInstallInfo"))
51✔
207
                                r = sd_bus_message_append(m, "t", arg_runtime ? (uint64_t) UNIT_FILE_RUNTIME : UINT64_C(0));
23✔
208
                        else
209
                                r = sd_bus_message_append(m, "b", arg_runtime);
39✔
210
                        if (r < 0)
51✔
211
                                return bus_log_create_error(r);
×
212
                }
213

214
                if (send_force) {
54✔
215
                        r = sd_bus_message_append(m, "b", arg_force);
27✔
216
                        if (r < 0)
27✔
217
                                return bus_log_create_error(r);
×
218
                }
219

220
                r = sd_bus_call(bus, m, 0, &error, &reply);
54✔
221
                if (r < 0)
54✔
222
                        return log_error_errno(r, "Failed to %s unit: %s", verb, bus_error_message(&error, r));
2✔
223

224
                if (expect_carries_install_info) {
52✔
225
                        r = sd_bus_message_read(reply, "b", &carries_install_info);
26✔
226
                        if (r < 0)
26✔
227
                                return bus_log_parse_error(r);
×
228
                }
229

230
                r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
52✔
231
                if (r < 0)
52✔
232
                        return r;
233

234
                /* Try to reload if enabled */
235
                if (!arg_no_reload) {
52✔
236
                        r = daemon_reload(ACTION_RELOAD, /* graceful= */ false);
52✔
237
                        if (r < 0)
52✔
238
                                return r;
239
                }
240

241
                if (warn_trigger_operation && !arg_quiet && !arg_no_warn)
52✔
242
                        STRV_FOREACH(unit, names)
42✔
243
                                warn_triggering_units(bus, *unit, warn_trigger_operation, warn_trigger_ignore_masked);
21✔
244
        } else {
245
                UnitFileFlags flags;
17✔
246
                InstallChange *changes = NULL;
17✔
247
                size_t n_changes = 0;
17✔
248

249
                CLEANUP_ARRAY(changes, n_changes, install_changes_free);
17✔
250

251
                flags = unit_file_flags_from_args();
17✔
252

253
                if (streq(verb, "enable")) {
17✔
254
                        r = unit_file_enable(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
3✔
255
                        carries_install_info = r;
3✔
256
                } else if (streq(verb, "disable")) {
14✔
257
                        r = unit_file_disable(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
3✔
258
                        carries_install_info = r;
3✔
259
                } else if (streq(verb, "reenable")) {
11✔
260
                        r = unit_file_reenable(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
1✔
261
                        carries_install_info = r;
1✔
262
                } else if (streq(verb, "link"))
10✔
263
                        r = unit_file_link(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
×
264
                else if (streq(verb, "preset"))
10✔
265
                        r = unit_file_preset(arg_runtime_scope, flags, arg_root, names, arg_preset_mode, &changes, &n_changes);
3✔
266
                else if (streq(verb, "mask"))
7✔
267
                        r = unit_file_mask(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
3✔
268
                else if (streq(verb, "unmask"))
4✔
269
                        r = unit_file_unmask(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
3✔
270
                else if (streq(verb, "revert"))
1✔
271
                        r = unit_file_revert(arg_runtime_scope, arg_root, names, &changes, &n_changes);
1✔
272
                else
273
                        assert_not_reached();
×
274

275
                install_changes_dump(r, verb, changes, n_changes, arg_quiet);
17✔
276
                if (r < 0)
17✔
277
                        return r;
×
278
        }
279

280
        if (carries_install_info == 0 && !ignore_carries_install_info)
69✔
281
                log_notice("The unit files have no installation config (WantedBy=, RequiredBy=, UpheldBy=,\n"
×
282
                           "Also=, or Alias= settings in the [Install] section, and DefaultInstance= for\n"
283
                           "template units). This means they are not meant to be enabled or disabled using systemctl.\n"
284
                           " \n" /* trick: the space is needed so that the line does not get stripped from output */
285
                           "Possible reasons for having these kinds of units are:\n"
286
                           "%1$s A unit may be statically enabled by being symlinked from another unit's\n"
287
                           "  .wants/, .requires/, or .upholds/ directory.\n"
288
                           "%1$s A unit's purpose may be to act as a helper for some other unit which has\n"
289
                           "  a requirement dependency on it.\n"
290
                           "%1$s A unit may be started when needed via activation (socket, path, timer,\n"
291
                           "  D-Bus, udev, scripted systemctl call, ...).\n"
292
                           "%1$s In case of template units, the unit is meant to be enabled with some\n"
293
                           "  instance name specified.",
294
                           glyph(GLYPH_BULLET));
295

296
        if (streq(verb, "disable") && arg_runtime_scope == RUNTIME_SCOPE_USER && !arg_quiet && !arg_no_warn) {
69✔
297
                /* If some of the units are disabled in user scope but still enabled in global scope,
298
                 * we emit a warning for that. */
299

300
                /* No strv_free here, strings are owned by 'names' */
301
                _cleanup_free_ char **enabled_in_global_scope = NULL;
×
302

303
                STRV_FOREACH(name, names) {
×
304
                        UnitFileState state;
×
305

306
                        r = unit_file_get_state(RUNTIME_SCOPE_GLOBAL, arg_root, *name, &state);
×
307
                        if (r == -ENOENT)
×
308
                                continue;
×
309
                        if (r < 0)
×
310
                                return log_error_errno(r, "Failed to get unit file state for %s: %m", *name);
×
311

312
                        if (IN_SET(state, UNIT_FILE_ENABLED, UNIT_FILE_ENABLED_RUNTIME)) {
×
313
                                r = strv_push(&enabled_in_global_scope, *name);
×
314
                                if (r < 0)
×
315
                                        return log_oom();
×
316
                        }
317
                }
318

319
                if (!strv_isempty(enabled_in_global_scope)) {
×
320
                        _cleanup_free_ char *joined = NULL;
×
321

322
                        joined = strv_join(enabled_in_global_scope, ", ");
×
323
                        if (!joined)
×
324
                                return log_oom();
×
325

326
                        log_notice("The following unit files have been enabled in global scope. This means\n"
×
327
                                   "they will still be started automatically after a successful disablement\n"
328
                                   "in user scope:\n"
329
                                   "%s",
330
                                   joined);
331
                }
332
        }
333

334
        if (arg_now) {
69✔
335
                _cleanup_strv_free_ char **new_args = NULL;
12✔
336
                const char *start_verb;
12✔
337
                bool accept_path, prohibit_templates, dead_ok = false;
12✔
338

339
                if (streq(verb, "enable")) {
12✔
340
                        start_verb = "start";
341
                        accept_path = true;
342
                        prohibit_templates = true;
343
                } else if (STR_IN_SET(verb, "disable", "mask")) {
8✔
344
                        start_verb = "stop";
345
                        accept_path = false;
346
                        prohibit_templates = false;
347
                        dead_ok = true;  /* If the service is not running anyway, no need to stop it. */
348
                } else if (streq(verb, "reenable")) {
×
349
                        /* Note that we use try-restart here. This matches the semantics of reenable better,
350
                         * and allows us to glob template units. */
351
                        start_verb = "try-restart";
352
                        accept_path = true;
353
                        prohibit_templates = false;
354
                } else
355
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
356
                                               "--now can only be used with verb enable, disable, reenable, or mask.");
357

358
                switch (install_client_side()) {
12✔
359
                case INSTALL_CLIENT_SIDE_NO:
360
                        break;
361
                case INSTALL_CLIENT_SIDE_OVERRIDE:
×
362
                case INSTALL_CLIENT_SIDE_OFFLINE:
363
                case INSTALL_CLIENT_SIDE_NOT_BOOTED:
364
                        if (!dead_ok)
×
365
                                log_warning("Cannot %s unit with --now when systemd is not running, ignoring.", start_verb);
×
366
                        return 0;
×
367
                case INSTALL_CLIENT_SIDE_ARG_ROOT:
368
                        return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "--now cannot be used with --root=.");
×
369
                case INSTALL_CLIENT_SIDE_GLOBAL_SCOPE:
370
                        return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "--now cannot be used with --global.");
×
371
                }
372

373
                assert(bus);
12✔
374

375
                if (strv_extend(&new_args, start_verb) < 0)
12✔
376
                        return log_oom();
×
377

378
                STRV_FOREACH(name, names) {
28✔
379
                        _cleanup_free_ char *fn = NULL;
16✔
380
                        const char *unit_name;
16✔
381

382
                        if (accept_path) {
16✔
383
                                /* 'enable' and 'reenable' accept path to unit files, so extract it first. */
384

385
                                r = path_extract_filename(*name, &fn);
8✔
386
                                if (r < 0)
8✔
387
                                        return log_error_errno(r, "Failed to extract filename of '%s': %m", *name);
×
388

389
                                unit_name = fn;
8✔
390
                        } else
391
                                unit_name = *name;
8✔
392

393
                        if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
16✔
394
                                char *globbed;
1✔
395

396
                                if (prohibit_templates) {
1✔
397
                                        /* Skip template units when enabling. Globbing doesn't make sense
398
                                         * since the semantics would be altered (we're operating on
399
                                         * DefaultInstance= when enabling), and starting template unit
400
                                         * is not supported anyway. */
401
                                        log_warning("Template unit is not supported by %s --now, skipping: %s",
×
402
                                                    verb, unit_name);
403
                                        continue;
×
404
                                }
405

406
                                assert(!STR_IN_SET(start_verb, "start", "restart"));
1✔
407

408
                                r = unit_name_replace_instance_full(unit_name, "*", /* accept_glob = */ true, &globbed);
1✔
409
                                if (r < 0)
1✔
410
                                        return log_error_errno(r, "Failed to glob unit name '%s': %m", unit_name);
×
411

412
                                r = strv_consume(&new_args, globbed);
1✔
413
                        } else
414
                                r = strv_extend(&new_args, unit_name);
15✔
415
                        if (r < 0)
16✔
416
                                return log_oom();
×
417
                }
418

419
                return verb_start(strv_length(new_args), new_args, userdata);
12✔
420
        }
421

422
        return 0;
423
}
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