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

systemd / systemd / 14766779411

30 Apr 2025 04:55PM UTC coverage: 72.225% (-0.06%) from 72.282%
14766779411

push

github

web-flow
wait-online: handle varlink connection errors while waiting for DNS (#37283)

Currently, if systemd-networkd-wait-online is started with --dns, and
systemd-resolved is not running, it will exit with an error right away.
Similarly, if systemd-resolved is restarted while waiting for DNS
configuration, systemd-networkd-wait-online will not attempt to
re-connect, and will potentially never see subsequent DNS
configurations.

Improve this by adding socket units for the systemd-resolved varlink
servers, and re-establish the connection in systemd-networkd-wait-online
when we receive `SD_VARLINK_ERROR_DISCONNECTED`.

8 of 16 new or added lines in 2 files covered. (50.0%)

5825 existing lines in 217 files now uncovered.

297168 of 411450 relevant lines covered (72.22%)

695892.62 hits per line

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

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

3
#include "bus-error.h"
4
#include "bus-locator.h"
5
#include "path-util.h"
6
#include "systemctl.h"
7
#include "systemctl-daemon-reload.h"
8
#include "systemctl-enable.h"
9
#include "systemctl-start-unit.h"
10
#include "systemctl-sysv-compat.h"
11
#include "systemctl-util.h"
12
#include "verbs.h"
13

14
static int normalize_link_paths(char **paths) {
×
15
        int r;
×
16

17
        STRV_FOREACH(u, paths) {
×
18
                if (path_is_absolute(*u))
×
19
                        continue;
×
20

21
                if (!isempty(arg_root))
×
22
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
23
                                               "Non-absolute paths are not allowed when --root= is used: %s",
24
                                               *u);
25

26
                if (!is_path(*u))
×
27
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
28
                                               "Link argument must contain at least one directory separator.\n"
29
                                               "If you intended to link a file in the current directory, try './%s' instead.",
30
                                               *u);
31

32
                char *normalized_path;
×
33

34
                r = path_make_absolute_cwd(*u, &normalized_path);
×
35
                if (r < 0)
×
36
                        return log_error_errno(r, "Failed to normalize path '%s': %m", *u);
×
37

38
                path_simplify(normalized_path);
×
39

40
                free_and_replace(*u, normalized_path);
×
41
        }
42

43
        return 0;
44
}
45

46
static int normalize_names(char **names) {
12✔
47
        bool was_path = false;
12✔
48
        int r;
12✔
49

50
        STRV_FOREACH(u, names) {
24✔
51
                if (!is_path(*u))
12✔
52
                        continue;
12✔
53

54
                char *fn;
×
55

56
                r = path_extract_filename(*u, &fn);
×
57
                if (r < 0)
×
58
                        return log_error_errno(r, "Failed to extract file name from '%s': %m", *u);
×
59

60
                free_and_replace(*u, fn);
×
61

62
                was_path = true;
×
63
        }
64

65
        if (was_path)
12✔
66
                log_warning("Warning: Can't execute disable on the unit file path. Proceeding with the unit name.");
×
67

68
        return 0;
69
}
70

71
int verb_enable(int argc, char *argv[], void *userdata) {
59✔
72
        const char *verb = ASSERT_PTR(argv[0]);
59✔
73
        _cleanup_strv_free_ char **names = NULL;
59✔
74
        int carries_install_info = -1;
59✔
75
        bool ignore_carries_install_info = arg_quiet || arg_no_warn;
59✔
76
        sd_bus *bus = NULL;
59✔
77
        int r;
59✔
78

79
        if (streq(verb, "preset") && should_bypass("SYSTEMD_PRESET"))
59✔
80
                return 0;
81

82
        const char *operation = strjoina("to ", verb);
295✔
83
        r = mangle_names(operation, ASSERT_PTR(strv_skip(argv, 1)), &names);
59✔
84
        if (r < 0)
59✔
85
                return r;
86

87
        r = enable_sysv_units(verb, names);
59✔
88
        if (r < 0)
59✔
89
                return r;
90

91
        /* If the operation was fully executed by the SysV compat, let's finish early */
92
        if (strv_isempty(names)) {
59✔
93
                if (arg_no_reload || install_client_side())
×
94
                        return 0;
×
95

96
                r = daemon_reload(ACTION_RELOAD, /* graceful= */ false);
×
97
                return r > 0 ? 0 : r;
×
98
        }
99

100
        if (streq(verb, "disable"))
59✔
101
                r = normalize_names(names);
12✔
102
        else if (streq(verb, "link"))
47✔
103
                r = normalize_link_paths(names);
×
104
        else
105
                r = 0;
106
        if (r < 0)
12✔
107
                return r;
108

109
        if (install_client_side()) {
59✔
110
                UnitFileFlags flags;
16✔
111
                InstallChange *changes = NULL;
16✔
112
                size_t n_changes = 0;
16✔
113

114
                CLEANUP_ARRAY(changes, n_changes, install_changes_free);
16✔
115

116
                flags = unit_file_flags_from_args();
16✔
117

118
                if (streq(verb, "enable")) {
16✔
119
                        r = unit_file_enable(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
2✔
120
                        carries_install_info = r;
2✔
121
                } else if (streq(verb, "disable")) {
14✔
122
                        r = unit_file_disable(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
3✔
123
                        carries_install_info = r;
3✔
124
                } else if (streq(verb, "reenable")) {
11✔
125
                        r = unit_file_reenable(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
1✔
126
                        carries_install_info = r;
1✔
127
                } else if (streq(verb, "link"))
10✔
128
                        r = unit_file_link(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
×
129
                else if (streq(verb, "preset"))
10✔
130
                        r = unit_file_preset(arg_runtime_scope, flags, arg_root, names, arg_preset_mode, &changes, &n_changes);
3✔
131
                else if (streq(verb, "mask"))
7✔
132
                        r = unit_file_mask(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
3✔
133
                else if (streq(verb, "unmask"))
4✔
134
                        r = unit_file_unmask(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
3✔
135
                else if (streq(verb, "revert"))
1✔
136
                        r = unit_file_revert(arg_runtime_scope, arg_root, names, &changes, &n_changes);
1✔
137
                else
138
                        assert_not_reached();
×
139

140
                install_changes_dump(r, verb, changes, n_changes, arg_quiet);
16✔
141
                if (r < 0)
16✔
142
                        return r;
×
143
        } else {
144
                _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
85✔
145
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
43✔
146
                bool expect_carries_install_info = false;
43✔
147
                bool send_runtime = true, send_force = true, send_preset_mode = false;
43✔
148
                const char *method, *warn_trigger_operation = NULL;
43✔
149
                bool warn_trigger_ignore_masked = true; /* suppress "used uninitialized" warning */
43✔
150

151
                if (STR_IN_SET(verb, "mask", "unmask")) {
43✔
152
                        _cleanup_(lookup_paths_done) LookupPaths lp = {};
16✔
153

154
                        r = lookup_paths_init_or_warn(&lp, arg_runtime_scope, 0, arg_root);
16✔
155
                        if (r < 0)
16✔
156
                                return r;
157

158
                        STRV_FOREACH(name, names) {
32✔
159
                                r = unit_exists(&lp, *name);
16✔
160
                                if (r < 0)
16✔
161
                                        return r;
162
                                if (r == 0)
16✔
UNCOV
163
                                        log_notice("Unit %s does not exist, proceeding anyway.", *name);
×
164
                        }
165
                }
166

167
                r = acquire_bus(BUS_MANAGER, &bus);
43✔
168
                if (r < 0)
43✔
169
                        return r;
170

171
                polkit_agent_open_maybe();
43✔
172

173
                if (streq(verb, "enable")) {
43✔
174
                        method = "EnableUnitFiles";
175
                        expect_carries_install_info = true;
176
                } else if (streq(verb, "disable")) {
32✔
177
                        method = "DisableUnitFilesWithFlagsAndInstallInfo";
178
                        expect_carries_install_info = true;
179
                        send_force = false;
180

181
                        warn_trigger_operation = "Disabling";
182
                        warn_trigger_ignore_masked = true;
183
                } else if (streq(verb, "reenable")) {
23✔
184
                        method = "ReenableUnitFiles";
185
                        expect_carries_install_info = true;
186
                } else if (streq(verb, "link"))
22✔
187
                        method = "LinkUnitFiles";
188
                else if (streq(verb, "preset")) {
22✔
189

190
                        if (arg_preset_mode != UNIT_FILE_PRESET_FULL) {
3✔
191
                                method = "PresetUnitFilesWithMode";
192
                                send_preset_mode = true;
193
                        } else
194
                                method = "PresetUnitFiles";
1✔
195

196
                        expect_carries_install_info = true;
197
                        ignore_carries_install_info = true;
198
                } else if (streq(verb, "mask")) {
19✔
199
                        method = "MaskUnitFiles";
200

201
                        warn_trigger_operation = "Masking";
202
                        warn_trigger_ignore_masked = false;
203
                } else if (streq(verb, "unmask")) {
12✔
204
                        method = "UnmaskUnitFiles";
205
                        send_force = false;
206
                } else if (streq(verb, "revert")) {
3✔
207
                        method = "RevertUnitFiles";
208
                        send_runtime = send_force = false;
209
                } else
210
                        assert_not_reached();
×
211

212
                r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
43✔
213
                if (r < 0)
43✔
214
                        return bus_log_create_error(r);
×
215

216
                r = sd_bus_message_append_strv(m, names);
43✔
217
                if (r < 0)
43✔
218
                        return bus_log_create_error(r);
×
219

220
                if (send_preset_mode) {
43✔
221
                        r = sd_bus_message_append(m, "s", unit_file_preset_mode_to_string(arg_preset_mode));
2✔
222
                        if (r < 0)
2✔
223
                                return bus_log_create_error(r);
×
224
                }
225

226
                if (send_runtime) {
43✔
227
                        if (streq(method, "DisableUnitFilesWithFlagsAndInstallInfo"))
40✔
228
                                r = sd_bus_message_append(m, "t", arg_runtime ? (uint64_t) UNIT_FILE_RUNTIME : UINT64_C(0));
17✔
229
                        else
230
                                r = sd_bus_message_append(m, "b", arg_runtime);
31✔
231
                        if (r < 0)
40✔
232
                                return bus_log_create_error(r);
×
233
                }
234

235
                if (send_force) {
43✔
236
                        r = sd_bus_message_append(m, "b", arg_force);
22✔
237
                        if (r < 0)
22✔
238
                                return bus_log_create_error(r);
×
239
                }
240

241
                r = sd_bus_call(bus, m, 0, &error, &reply);
43✔
242
                if (r < 0)
43✔
243
                        return log_error_errno(r, "Failed to %s unit: %s", verb, bus_error_message(&error, r));
1✔
244

245
                if (expect_carries_install_info) {
42✔
246
                        r = sd_bus_message_read(reply, "b", &carries_install_info);
23✔
247
                        if (r < 0)
23✔
248
                                return bus_log_parse_error(r);
×
249
                }
250

251
                r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
42✔
252
                if (r < 0)
42✔
253
                        return r;
254

255
                /* Try to reload if enabled */
256
                if (!arg_no_reload) {
42✔
257
                        r = daemon_reload(ACTION_RELOAD, /* graceful= */ false);
42✔
258
                        if (r < 0)
42✔
259
                                return r;
260
                }
261

262
                if (warn_trigger_operation && !arg_quiet && !arg_no_warn)
42✔
263
                        STRV_FOREACH(unit, names)
30✔
264
                                warn_triggering_units(bus, *unit, warn_trigger_operation, warn_trigger_ignore_masked);
15✔
265
        }
266

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

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

287
                /* No strv_free here, strings are owned by 'names' */
288
                _cleanup_free_ char **enabled_in_global_scope = NULL;
×
289

290
                STRV_FOREACH(name, names) {
×
291
                        UnitFileState state;
×
292

293
                        r = unit_file_get_state(RUNTIME_SCOPE_GLOBAL, arg_root, *name, &state);
×
294
                        if (r == -ENOENT)
×
295
                                continue;
×
296
                        if (r < 0)
×
297
                                return log_error_errno(r, "Failed to get unit file state for %s: %m", *name);
×
298

299
                        if (IN_SET(state, UNIT_FILE_ENABLED, UNIT_FILE_ENABLED_RUNTIME)) {
×
300
                                r = strv_push(&enabled_in_global_scope, *name);
×
301
                                if (r < 0)
×
302
                                        return log_oom();
×
303
                        }
304
                }
305

306
                if (!strv_isempty(enabled_in_global_scope)) {
×
307
                        _cleanup_free_ char *joined = NULL;
×
308

309
                        joined = strv_join(enabled_in_global_scope, ", ");
×
310
                        if (!joined)
×
311
                                return log_oom();
×
312

313
                        log_notice("The following unit files have been enabled in global scope. This means\n"
×
314
                                   "they will still be started automatically after a successful disablement\n"
315
                                   "in user scope:\n"
316
                                   "%s",
317
                                   joined);
318
                }
319
        }
320

321
        if (arg_now) {
58✔
322
                _cleanup_strv_free_ char **new_args = NULL;
7✔
323
                const char *start_verb;
7✔
324
                bool accept_path, prohibit_templates;
7✔
325

326
                if (streq(verb, "enable")) {
7✔
327
                        start_verb = "start";
328
                        accept_path = true;
329
                        prohibit_templates = true;
330
                } else if (STR_IN_SET(verb, "disable", "mask")) {
4✔
331
                        start_verb = "stop";
332
                        accept_path = false;
333
                        prohibit_templates = false;
334
                } else if (streq(verb, "reenable")) {
×
335
                        /* Note that we use try-restart here. This matches the semantics of reenable better,
336
                         * and allows us to glob template units. */
337
                        start_verb = "try-restart";
338
                        accept_path = true;
339
                        prohibit_templates = false;
340
                } else
341
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
342
                                               "--now can only be used with verb enable, disable, reenable, or mask.");
343

344
                if (install_client_side())
7✔
345
                        return log_error_errno(SYNTHETIC_ERRNO(EREMOTE),
×
346
                                               "--now cannot be used when systemd is not running or in conjunction with --root=/--global, refusing.");
347

348
                assert(bus);
7✔
349

350
                if (strv_extend(&new_args, start_verb) < 0)
7✔
351
                        return log_oom();
×
352

353
                STRV_FOREACH(name, names) {
15✔
354
                        _cleanup_free_ char *fn = NULL;
8✔
355
                        const char *unit_name;
8✔
356

357
                        if (accept_path) {
8✔
358
                                /* 'enable' and 'reenable' accept path to unit files, so extract it first. */
359

360
                                r = path_extract_filename(*name, &fn);
4✔
361
                                if (r < 0)
4✔
362
                                        return log_error_errno(r, "Failed to extract filename of '%s': %m", *name);
×
363

364
                                unit_name = fn;
4✔
365
                        } else
366
                                unit_name = *name;
4✔
367

368
                        if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
8✔
369
                                char *globbed;
1✔
370

371
                                if (prohibit_templates) {
1✔
372
                                        /* Skip template units when enabling. Globbing doesn't make sense
373
                                         * since the semantics would be altered (we're operating on
374
                                         * DefaultInstance= when enabling), and starting template unit
375
                                         * is not supported anyway. */
376
                                        log_warning("Template unit is not supported by %s --now, skipping: %s",
×
377
                                                    verb, unit_name);
378
                                        continue;
×
379
                                }
380

381
                                assert(!STR_IN_SET(start_verb, "start", "restart"));
1✔
382

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

387
                                r = strv_consume(&new_args, globbed);
1✔
388
                        } else
389
                                r = strv_extend(&new_args, unit_name);
7✔
390
                        if (r < 0)
8✔
391
                                return log_oom();
×
392
                }
393

394
                return verb_start(strv_length(new_args), new_args, userdata);
7✔
395
        }
396

397
        return 0;
398
}
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