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

systemd / systemd / 20766109442

06 Jan 2026 09:50PM UTC coverage: 72.714% (+0.3%) from 72.444%
20766109442

push

github

YHNdnzj
man: do not manually update man/rules/meson.build

Follow-up for 25393c7c9.

310283 of 426715 relevant lines covered (72.71%)

1142928.51 hits per line

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

65.37
/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-util.h"
23
#include "unit-name.h"
24
#include "verbs.h"
25

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

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

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

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

44
                char *normalized_path;
×
45

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

50
                path_simplify(normalized_path);
×
51

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

55
        return 0;
56
}
57

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

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

66
                char *fn;
×
67

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

72
                free_and_replace(*u, fn);
×
73

74
                was_path = true;
×
75
        }
76

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

80
        return 0;
81
}
82

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

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

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

99
        /* If the operation was fully executed by the SysV compat, let's finish early */
100
        if (strv_isempty(names)) {
71✔
101
                if (arg_no_reload || install_client_side() != INSTALL_CLIENT_SIDE_NO)
×
102
                        return 0;
×
103

104
                r = daemon_reload(ACTION_RELOAD, /* graceful= */ false);
×
105
                return r > 0 ? 0 : r;
×
106
        }
107

108
        if (streq(verb, "disable"))
71✔
109
                r = normalize_names(names);
15✔
110
        else if (streq(verb, "link"))
56✔
111
                r = normalize_link_paths(names);
×
112
        else
113
                r = 0;
114
        if (r < 0)
15✔
115
                return r;
116

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

125
                if (STR_IN_SET(verb, "mask", "unmask")) {
54✔
126
                        _cleanup_(lookup_paths_done) LookupPaths lp = {};
23✔
127

128
                        r = lookup_paths_init_or_warn(&lp, arg_runtime_scope, 0, arg_root);
23✔
129
                        if (r < 0)
23✔
130
                                return r;
131

132
                        STRV_FOREACH(name, names) {
49✔
133
                                r = unit_exists(&lp, *name);
26✔
134
                                if (r < 0)
26✔
135
                                        return r;
136
                                if (r == 0)
26✔
137
                                        log_notice("Unit %s does not exist, proceeding anyway.", *name);
×
138
                        }
139
                }
140

141
                r = acquire_bus(BUS_MANAGER, &bus);
54✔
142
                if (r < 0)
54✔
143
                        return r;
144

145
                polkit_agent_open_maybe();
54✔
146

147
                if (streq(verb, "enable")) {
54✔
148
                        method = "EnableUnitFiles";
149
                        expect_carries_install_info = true;
150
                } else if (streq(verb, "disable")) {
42✔
151
                        method = "DisableUnitFilesWithFlagsAndInstallInfo";
152
                        expect_carries_install_info = true;
153
                        send_force = false;
154

155
                        warn_trigger_operation = "Disabling";
156
                        warn_trigger_ignore_masked = true;
157
                } else if (streq(verb, "reenable")) {
30✔
158
                        method = "ReenableUnitFiles";
159
                        expect_carries_install_info = true;
160
                } else if (streq(verb, "link"))
29✔
161
                        method = "LinkUnitFiles";
162
                else if (streq(verb, "preset")) {
29✔
163

164
                        if (arg_preset_mode != UNIT_FILE_PRESET_FULL) {
3✔
165
                                method = "PresetUnitFilesWithMode";
166
                                send_preset_mode = true;
167
                        } else
168
                                method = "PresetUnitFiles";
1✔
169

170
                        expect_carries_install_info = true;
171
                        ignore_carries_install_info = true;
172
                } else if (streq(verb, "mask")) {
26✔
173
                        method = "MaskUnitFiles";
174

175
                        warn_trigger_operation = "Masking";
176
                        warn_trigger_ignore_masked = false;
177
                } else if (streq(verb, "unmask")) {
15✔
178
                        method = "UnmaskUnitFiles";
179
                        send_force = false;
180
                } else if (streq(verb, "revert")) {
3✔
181
                        method = "RevertUnitFiles";
182
                        send_runtime = send_force = false;
183
                } else
184
                        assert_not_reached();
×
185

186
                r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
54✔
187
                if (r < 0)
54✔
188
                        return bus_log_create_error(r);
×
189

190
                r = sd_bus_message_append_strv(m, names);
54✔
191
                if (r < 0)
54✔
192
                        return bus_log_create_error(r);
×
193

194
                if (send_preset_mode) {
54✔
195
                        r = sd_bus_message_append(m, "s", unit_file_preset_mode_to_string(arg_preset_mode));
2✔
196
                        if (r < 0)
2✔
197
                                return bus_log_create_error(r);
×
198
                }
199

200
                if (send_runtime) {
54✔
201
                        if (streq(method, "DisableUnitFilesWithFlagsAndInstallInfo"))
51✔
202
                                r = sd_bus_message_append(m, "t", arg_runtime ? (uint64_t) UNIT_FILE_RUNTIME : UINT64_C(0));
23✔
203
                        else
204
                                r = sd_bus_message_append(m, "b", arg_runtime);
39✔
205
                        if (r < 0)
51✔
206
                                return bus_log_create_error(r);
×
207
                }
208

209
                if (send_force) {
54✔
210
                        r = sd_bus_message_append(m, "b", arg_force);
27✔
211
                        if (r < 0)
27✔
212
                                return bus_log_create_error(r);
×
213
                }
214

215
                r = sd_bus_call(bus, m, 0, &error, &reply);
54✔
216
                if (r < 0)
54✔
217
                        return log_error_errno(r, "Failed to %s unit: %s", verb, bus_error_message(&error, r));
2✔
218

219
                if (expect_carries_install_info) {
52✔
220
                        r = sd_bus_message_read(reply, "b", &carries_install_info);
26✔
221
                        if (r < 0)
26✔
222
                                return bus_log_parse_error(r);
×
223
                }
224

225
                r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
52✔
226
                if (r < 0)
52✔
227
                        return r;
228

229
                /* Try to reload if enabled */
230
                if (!arg_no_reload) {
52✔
231
                        r = daemon_reload(ACTION_RELOAD, /* graceful= */ false);
52✔
232
                        if (r < 0)
52✔
233
                                return r;
234
                }
235

236
                if (warn_trigger_operation && !arg_quiet && !arg_no_warn)
52✔
237
                        STRV_FOREACH(unit, names)
42✔
238
                                warn_triggering_units(bus, *unit, warn_trigger_operation, warn_trigger_ignore_masked);
21✔
239
        } else {
240
                UnitFileFlags flags;
17✔
241
                InstallChange *changes = NULL;
17✔
242
                size_t n_changes = 0;
17✔
243

244
                CLEANUP_ARRAY(changes, n_changes, install_changes_free);
17✔
245

246
                flags = unit_file_flags_from_args();
17✔
247

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

270
                install_changes_dump(r, verb, changes, n_changes, arg_quiet);
17✔
271
                if (r < 0)
17✔
272
                        return r;
×
273
        }
274

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

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

295
                /* No strv_free here, strings are owned by 'names' */
296
                _cleanup_free_ char **enabled_in_global_scope = NULL;
×
297

298
                STRV_FOREACH(name, names) {
×
299
                        UnitFileState state;
×
300

301
                        r = unit_file_get_state(RUNTIME_SCOPE_GLOBAL, arg_root, *name, &state);
×
302
                        if (r == -ENOENT)
×
303
                                continue;
×
304
                        if (r < 0)
×
305
                                return log_error_errno(r, "Failed to get unit file state for %s: %m", *name);
×
306

307
                        if (IN_SET(state, UNIT_FILE_ENABLED, UNIT_FILE_ENABLED_RUNTIME)) {
×
308
                                r = strv_push(&enabled_in_global_scope, *name);
×
309
                                if (r < 0)
×
310
                                        return log_oom();
×
311
                        }
312
                }
313

314
                if (!strv_isempty(enabled_in_global_scope)) {
×
315
                        _cleanup_free_ char *joined = NULL;
×
316

317
                        joined = strv_join(enabled_in_global_scope, ", ");
×
318
                        if (!joined)
×
319
                                return log_oom();
×
320

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

329
        if (arg_now) {
69✔
330
                _cleanup_strv_free_ char **new_args = NULL;
12✔
331
                const char *start_verb;
12✔
332
                bool accept_path, prohibit_templates, dead_ok = false;
12✔
333

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

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

368
                assert(bus);
12✔
369

370
                if (strv_extend(&new_args, start_verb) < 0)
12✔
371
                        return log_oom();
×
372

373
                STRV_FOREACH(name, names) {
28✔
374
                        _cleanup_free_ char *fn = NULL;
16✔
375
                        const char *unit_name;
16✔
376

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

380
                                r = path_extract_filename(*name, &fn);
8✔
381
                                if (r < 0)
8✔
382
                                        return log_error_errno(r, "Failed to extract filename of '%s': %m", *name);
×
383

384
                                unit_name = fn;
8✔
385
                        } else
386
                                unit_name = *name;
8✔
387

388
                        if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
16✔
389
                                char *globbed;
1✔
390

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

401
                                assert(!STR_IN_SET(start_verb, "start", "restart"));
1✔
402

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

407
                                r = strv_consume(&new_args, globbed);
1✔
408
                        } else
409
                                r = strv_extend(&new_args, unit_name);
15✔
410
                        if (r < 0)
16✔
411
                                return log_oom();
×
412
                }
413

414
                return verb_start(strv_length(new_args), new_args, userdata);
12✔
415
        }
416

417
        return 0;
418
}
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