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

systemd / systemd / 14815796853

02 May 2025 11:41AM UTC coverage: 72.24% (-0.003%) from 72.243%
14815796853

push

github

web-flow
Various changes to prepare for running IWYU on the repository (#37319)

These are various commits that were required to get things compiling
after running IWYU. I think all of them make sense on their own, hence
this split PR to merge them ahead of time.

81 of 96 new or added lines in 48 files covered. (84.38%)

209 existing lines in 39 files now uncovered.

297219 of 411432 relevant lines covered (72.24%)

693693.2 hits per line

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

73.93
/src/machine/machinectl.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <arpa/inet.h>
4
#include <errno.h>
5
#include <fcntl.h>
6
#include <getopt.h>
7
#include <math.h>
8
#include <net/if.h>
9
#include <netinet/in.h>
10
#include <sys/mount.h>
11
#include <sys/socket.h>
12
#include <unistd.h>
13

14
#include "sd-bus.h"
15

16
#include "alloc-util.h"
17
#include "ask-password-agent.h"
18
#include "build.h"
19
#include "build-path.h"
20
#include "bus-common-errors.h"
21
#include "bus-error.h"
22
#include "bus-locator.h"
23
#include "bus-map-properties.h"
24
#include "bus-message-util.h"
25
#include "bus-print-properties.h"
26
#include "bus-unit-procs.h"
27
#include "bus-unit-util.h"
28
#include "bus-wait-for-jobs.h"
29
#include "cgroup-show.h"
30
#include "cgroup-util.h"
31
#include "constants.h"
32
#include "copy.h"
33
#include "edit-util.h"
34
#include "env-util.h"
35
#include "fd-util.h"
36
#include "format-ifname.h"
37
#include "format-table.h"
38
#include "hostname-util.h"
39
#include "import-util.h"
40
#include "in-addr-util.h"
41
#include "locale-util.h"
42
#include "log.h"
43
#include "logs-show.h"
44
#include "machine-dbus.h"
45
#include "macro.h"
46
#include "main-func.h"
47
#include "mkdir.h"
48
#include "nulstr-util.h"
49
#include "osc-context.h"
50
#include "pager.h"
51
#include "parse-argument.h"
52
#include "parse-util.h"
53
#include "path-util.h"
54
#include "polkit-agent.h"
55
#include "pretty-print.h"
56
#include "process-util.h"
57
#include "ptyfwd.h"
58
#include "rlimit-util.h"
59
#include "signal-util.h"
60
#include "sort-util.h"
61
#include "stdio-util.h"
62
#include "string-table.h"
63
#include "strv.h"
64
#include "terminal-util.h"
65
#include "unit-name.h"
66
#include "verbs.h"
67
#include "web-util.h"
68

69
typedef enum MachineRunner {
70
        RUNNER_NSPAWN,
71
        RUNNER_VMSPAWN,
72
        _RUNNER_MAX,
73
        _RUNNER_INVALID = -EINVAL,
74
} MachineRunner;
75

76
static const char* const machine_runner_table[_RUNNER_MAX] = {
77
        [RUNNER_NSPAWN] = "nspawn",
78
        [RUNNER_VMSPAWN] = "vmspawn",
79
};
80

81
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(machine_runner, MachineRunner);
×
82

83
static const char* const machine_runner_unit_prefix_table[_RUNNER_MAX] = {
84
        [RUNNER_NSPAWN] = "systemd-nspawn",
85
        [RUNNER_VMSPAWN] = "systemd-vmspawn",
86
};
87

88
static char **arg_property = NULL;
89
static bool arg_all = false;
90
static BusPrintPropertyFlags arg_print_flags = 0;
91
static bool arg_full = false;
92
static PagerFlags arg_pager_flags = 0;
93
static bool arg_legend = true;
94
static const char *arg_kill_whom = NULL;
95
static int arg_signal = SIGTERM;
96
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
97
static const char *arg_host = NULL;
98
static bool arg_read_only = false;
99
static bool arg_mkdir = false;
100
static bool arg_quiet = false;
101
static bool arg_ask_password = true;
102
static unsigned arg_lines = 10;
103
static OutputMode arg_output = OUTPUT_SHORT;
104
static bool arg_now = false;
105
static bool arg_force = false;
106
static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
107
static MachineRunner arg_runner = RUNNER_NSPAWN;
108
static const char* arg_format = NULL;
109
static const char *arg_uid = NULL;
110
static char **arg_setenv = NULL;
111
static unsigned arg_max_addresses = 1;
112

113
STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
182✔
114
STATIC_DESTRUCTOR_REGISTER(arg_setenv, strv_freep);
182✔
115

116
static OutputFlags get_output_flags(void) {
47✔
117
        return
47✔
118
                FLAGS_SET(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) * OUTPUT_SHOW_ALL |
94✔
119
                (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
47✔
120
                colors_enabled() * OUTPUT_COLOR |
47✔
121
                !arg_quiet * OUTPUT_WARN_CUTOFF;
47✔
122
}
123

124
static int call_get_os_release(sd_bus *bus, const char *method, const char *name, const char *query, ...) {
45✔
125
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
126
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
45✔
127
        va_list ap;
45✔
128
        int r;
45✔
129

130
        assert(bus);
45✔
131
        assert(method);
45✔
132
        assert(name);
45✔
133
        assert(query);
45✔
134

135
        r = bus_call_method(bus, bus_machine_mgr, method, &error, &reply, "s", name);
45✔
136
        if (r < 0)
45✔
137
                return log_debug_errno(r, "Failed to call '%s()': %s", method, bus_error_message(&error, r));
4✔
138

139
        r = sd_bus_message_enter_container(reply, 'a', "{ss}");
41✔
140
        if (r < 0)
41✔
141
                return bus_log_parse_error(r);
×
142

143
        const char **res;
144
        size_t n_fields = 0;
145

146
        NULSTR_FOREACH(i, query)
85✔
147
                n_fields++;
44✔
148

149
        res = newa0(const char*, n_fields);
41✔
150

151
        const char *k, *v;
41✔
152
        while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
426✔
153
                size_t c = 0;
154
                NULSTR_FOREACH(i, query) {
765✔
155
                        if (streq(i, k)) {
415✔
156
                                res[c] = v;
35✔
157
                                break;
35✔
158
                        }
159
                        c++;
380✔
160
                }
161
        }
162
        if (r < 0)
41✔
163
                return bus_log_parse_error(r);
×
164

165
        r = sd_bus_message_exit_container(reply);
41✔
166
        if (r < 0)
41✔
167
                return bus_log_parse_error(r);
×
168

169
        r = 0;
41✔
170

171
        va_start(ap, query);
41✔
172
        FOREACH_ARRAY(i, res, n_fields) {
85✔
173
                r = strdup_to(va_arg(ap, char**), *i);
44✔
174
                if (r < 0)
44✔
175
                        break;
176
        }
177
        va_end(ap);
41✔
178

179
        return r;
41✔
180
}
181

182
static int call_get_addresses(
27✔
183
                sd_bus *bus,
184
                const char *name,
185
                int ifi,
186
                const char *prefix,
187
                const char *prefix2,
188
                char **ret) {
189

190
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
191
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
27✔
192
        _cleanup_free_ char *addresses = NULL;
27✔
193
        unsigned n = 0;
27✔
194
        int r;
27✔
195

196
        assert(bus);
27✔
197
        assert(name);
27✔
198
        assert(prefix);
27✔
199
        assert(prefix2);
27✔
200

201
        r = bus_call_method(bus, bus_machine_mgr, "GetMachineAddresses", NULL, &reply, "s", name);
27✔
202
        if (r < 0)
27✔
203
                return log_debug_errno(r, "Could not get addresses: %s", bus_error_message(&error, r));
×
204

205
        addresses = strdup(prefix);
27✔
206
        if (!addresses)
27✔
207
                return log_oom();
×
208

209
        r = sd_bus_message_enter_container(reply, 'a', "(iay)");
27✔
210
        if (r < 0)
27✔
211
                return bus_log_parse_error(r);
×
212

213
        prefix = "";
214
        while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
27✔
215
                int family;
×
216
                const void *a;
×
217
                size_t sz;
×
218
                char buf_ifi[1 + DECIMAL_STR_MAX(int)] = "";
×
219

220
                r = sd_bus_message_read(reply, "i", &family);
×
221
                if (r < 0)
×
222
                        return bus_log_parse_error(r);
×
223

224
                r = sd_bus_message_read_array(reply, 'y', &a, &sz);
×
225
                if (r < 0)
×
226
                        return bus_log_parse_error(r);
×
227

228
                if (family == AF_INET6 && ifi > 0)
×
229
                        xsprintf(buf_ifi, "%%%i", ifi);
×
230

231
                if (!strextend(&addresses, prefix, IN_ADDR_TO_STRING(family, a), buf_ifi))
×
232
                        return log_oom();
×
233

234
                r = sd_bus_message_exit_container(reply);
×
235
                if (r < 0)
×
236
                        return bus_log_parse_error(r);
×
237

238
                prefix = prefix2;
×
239

240
                n++;
×
241
        }
242
        if (r < 0)
27✔
243
                return bus_log_parse_error(r);
×
244

245
        r = sd_bus_message_exit_container(reply);
27✔
246
        if (r < 0)
27✔
247
                return bus_log_parse_error(r);
×
248

249
        *ret = TAKE_PTR(addresses);
27✔
250
        return (int) n;
27✔
251
}
252

253
static int show_table(Table *table, const char *word) {
6✔
254
        int r;
6✔
255

256
        assert(table);
6✔
257
        assert(word);
6✔
258

259
        if (!table_isempty(table) || OUTPUT_MODE_IS_JSON(arg_output)) {
12✔
260
                r = table_set_sort(table, (size_t) 0);
6✔
261
                if (r < 0)
6✔
262
                        return table_log_sort_error(r);
×
263

264
                table_set_header(table, arg_legend);
6✔
265

266
                if (OUTPUT_MODE_IS_JSON(arg_output))
6✔
267
                        r = table_print_json(table, NULL, output_mode_to_json_format_flags(arg_output) | SD_JSON_FORMAT_COLOR_AUTO);
×
268
                else
269
                        r = table_print(table, NULL);
6✔
270
                if (r < 0)
6✔
271
                        return table_log_print_error(r);
×
272
        }
273

274
        if (arg_legend) {
6✔
275
                if (table_isempty(table))
8✔
276
                        printf("No %s.\n", word);
×
277
                else
278
                        printf("\n%zu %s listed.\n", table_get_rows(table) - 1, word);
4✔
279
        }
280

281
        return 0;
282
}
283

284
static int list_machines(int argc, char *argv[], void *userdata) {
3✔
285
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
286
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
3✔
287
        _cleanup_(table_unrefp) Table *table = NULL;
3✔
288
        sd_bus *bus = ASSERT_PTR(userdata);
3✔
289
        int r;
3✔
290

291
        pager_open(arg_pager_flags);
3✔
292

293
        r = bus_call_method(bus, bus_machine_mgr, "ListMachines", &error, &reply, NULL);
3✔
294
        if (r < 0)
3✔
295
                return log_error_errno(r, "Could not get machines: %s", bus_error_message(&error, r));
×
296

297
        table = table_new("machine", "class", "service", "os", "version",
3✔
298
                          arg_max_addresses > 0 ? "addresses" : NULL);
299
        if (!table)
3✔
300
                return log_oom();
×
301

302
        table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
3✔
303
        if (!arg_full && arg_max_addresses > 0 && arg_max_addresses < UINT_MAX)
3✔
304
                table_set_cell_height_max(table, arg_max_addresses);
3✔
305

306
        if (arg_full)
3✔
307
                table_set_width(table, 0);
×
308

309
        r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
3✔
310
        if (r < 0)
3✔
311
                return bus_log_parse_error(r);
×
312

313
        for (;;) {
9✔
314
                _cleanup_free_ char *os = NULL, *version_id = NULL, *addresses = NULL;
6✔
315
                const char *name, *class, *service;
9✔
316

317
                r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, NULL);
9✔
318
                if (r < 0)
9✔
319
                        return bus_log_parse_error(r);
×
320
                if (r == 0)
9✔
321
                        break;
322

323
                if (name[0] == '.' && !arg_all)
6✔
324
                        continue;
3✔
325

326
                (void) call_get_os_release(
3✔
327
                                bus,
328
                                "GetMachineOSRelease",
329
                                name,
330
                                "ID\0"
331
                                "VERSION_ID\0",
332
                                &os,
333
                                &version_id);
334

335
                r = table_add_many(table,
15✔
336
                                   TABLE_STRING, empty_to_null(name),
337
                                   TABLE_STRING, empty_to_null(class),
338
                                   TABLE_STRING, empty_to_null(service),
339
                                   TABLE_STRING, empty_to_null(os),
340
                                   TABLE_STRING, empty_to_null(version_id));
341
                if (r < 0)
3✔
342
                        return table_log_add_error(r);
×
343

344
                if (arg_max_addresses > 0) {
3✔
345
                        (void) call_get_addresses(bus, name, 0, "", "\n", &addresses);
3✔
346

347
                        r = table_add_many(table,
6✔
348
                                           TABLE_STRING, empty_to_null(addresses));
349
                        if (r < 0)
3✔
350
                                return table_log_add_error(r);
×
351
                }
352
        }
353

354
        r = sd_bus_message_exit_container(reply);
3✔
355
        if (r < 0)
3✔
356
                return bus_log_parse_error(r);
×
357

358
        return show_table(table, "machines");
3✔
359
}
360

361
static int list_images(int argc, char *argv[], void *userdata) {
3✔
362

363
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
364
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
3✔
365
        _cleanup_(table_unrefp) Table *table = NULL;
3✔
366
        sd_bus *bus = ASSERT_PTR(userdata);
3✔
367
        int r;
3✔
368

369
        pager_open(arg_pager_flags);
3✔
370

371
        r = bus_call_method(bus, bus_machine_mgr, "ListImages", &error, &reply, NULL);
3✔
372
        if (r < 0)
3✔
373
                return log_error_errno(r, "Could not get images: %s", bus_error_message(&error, r));
×
374

375
        table = table_new("name", "type", "ro", "usage", "created", "modified");
3✔
376
        if (!table)
3✔
377
                return log_oom();
×
378

379
        if (arg_full)
3✔
380
                table_set_width(table, 0);
×
381

382
        (void) table_set_align_percent(table, TABLE_HEADER_CELL(3), 100);
3✔
383

384
        r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
3✔
385
        if (r < 0)
3✔
386
                return bus_log_parse_error(r);
×
387

388
        for (;;) {
29✔
389
                uint64_t crtime, mtime, size;
29✔
390
                const char *name, *type;
29✔
391
                int ro_int;
29✔
392

393
                r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &ro_int, &crtime, &mtime, &size, NULL);
29✔
394
                if (r < 0)
29✔
395
                        return bus_log_parse_error(r);
×
396
                if (r == 0)
29✔
397
                        break;
398

399
                if (name[0] == '.' && !arg_all)
26✔
400
                        continue;
2✔
401

402
                r = table_add_many(table,
24✔
403
                                   TABLE_STRING, name,
404
                                   TABLE_STRING, type,
405
                                   TABLE_BOOLEAN, ro_int,
406
                                   TABLE_SET_COLOR, ro_int ? ansi_highlight_red() : NULL,
407
                                   TABLE_SIZE, size,
408
                                   TABLE_TIMESTAMP, crtime,
409
                                   TABLE_TIMESTAMP, mtime);
410
                if (r < 0)
24✔
411
                        return table_log_add_error(r);
×
412
        }
413

414
        r = sd_bus_message_exit_container(reply);
3✔
415
        if (r < 0)
3✔
416
                return bus_log_parse_error(r);
×
417

418
        return show_table(table, "images");
3✔
419
}
420

421
static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
24✔
422
        _cleanup_free_ char *cgroup = NULL;
24✔
423
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
424
        int r;
24✔
425
        unsigned c;
24✔
426

427
        assert(bus);
24✔
428
        assert(unit);
24✔
429

430
        r = show_cgroup_get_unit_path_and_warn(bus, unit, &cgroup);
24✔
431
        if (r < 0)
24✔
432
                return r;
433

434
        if (isempty(cgroup))
48✔
435
                return 0;
436

437
        c = columns();
24✔
438
        if (c > 18)
24✔
439
                c -= 18;
24✔
440
        else
441
                c = 0;
442

443
        r = unit_show_processes(bus, unit, cgroup, "\t\t  ", c, get_output_flags(), &error);
24✔
444
        if (r == -EBADR) {
24✔
445

446
                if (arg_transport == BUS_TRANSPORT_REMOTE)
×
447
                        return 0;
448

449
                /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
450

451
                if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
×
452
                        return 0;
453

454
                show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t  ", c, &leader, leader > 0, get_output_flags());
×
455
        } else if (r < 0)
24✔
456
                return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r));
×
457

458
        return 0;
459
}
460

461
static int print_os_release(sd_bus *bus, const char *method, const char *name, const char *prefix) {
42✔
462
        _cleanup_free_ char *pretty = NULL;
42✔
463
        int r;
42✔
464

465
        assert(bus);
42✔
466
        assert(name);
42✔
467
        assert(prefix);
42✔
468

469
        r = call_get_os_release(bus, method, name, "PRETTY_NAME\0", &pretty, NULL);
42✔
470
        if (r < 0)
42✔
471
                return r;
472

473
        if (pretty)
38✔
474
                printf("%s%s\n", prefix, pretty);
32✔
475

476
        return 0;
477
}
478

479
static int print_uid_shift(sd_bus *bus, const char *name) {
24✔
480
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
481
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
24✔
482
        uint32_t shift;
24✔
483
        int r;
24✔
484

485
        assert(bus);
24✔
486
        assert(name);
24✔
487

488
        r = bus_call_method(bus, bus_machine_mgr, "GetMachineUIDShift", &error, &reply, "s", name);
24✔
489
        if (r < 0)
24✔
490
                return log_debug_errno(r, "Failed to query UID/GID shift: %s", bus_error_message(&error, r));
×
491

492
        r = sd_bus_message_read(reply, "u", &shift);
24✔
493
        if (r < 0)
24✔
494
                return r;
495

496
        if (shift == 0) /* Don't show trivial mappings */
24✔
497
                return 0;
498

499
        printf("  UID Shift: %" PRIu32 "\n", shift);
24✔
500
        return 0;
501
}
502

503
typedef struct MachineStatusInfo {
504
        const char *name;
505
        sd_id128_t id;
506
        const char *class;
507
        const char *service;
508
        const char *unit;
509
        const char *root_directory;
510
        pid_t leader;
511
        struct dual_timestamp timestamp;
512
        int *netif;
513
        size_t n_netif;
514
} MachineStatusInfo;
515

516
static void machine_status_info_clear(MachineStatusInfo *info) {
24✔
517
        if (info) {
24✔
518
                free(info->netif);
24✔
519
                zero(*info);
24✔
520
        }
521
}
24✔
522

523
static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
24✔
524
        _cleanup_free_ char *addresses = NULL, *s1 = NULL, *s2 = NULL;
×
525
        int ifi = -1;
24✔
526

527
        assert(bus);
24✔
528
        assert(i);
24✔
529

530
        fputs(strna(i->name), stdout);
24✔
531

532
        if (!sd_id128_is_null(i->id))
24✔
533
                printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
24✔
534
        else
535
                putchar('\n');
×
536

537
        s1 = strdup(strempty(FORMAT_TIMESTAMP_RELATIVE(i->timestamp.realtime)));
24✔
538
        s2 = strdup(strempty(FORMAT_TIMESTAMP(i->timestamp.realtime)));
24✔
539

540
        if (!isempty(s1))
24✔
541
                printf("\t   Since: %s; %s\n", strna(s2), s1);
24✔
542
        else if (!isempty(s2))
×
543
                printf("\t   Since: %s\n", s2);
×
544

545
        if (i->leader > 0) {
24✔
546
                _cleanup_free_ char *t = NULL;
24✔
547

548
                printf("\t  Leader: %u", (unsigned) i->leader);
24✔
549

550
                (void) pid_get_comm(i->leader, &t);
24✔
551
                if (t)
24✔
552
                        printf(" (%s)", t);
24✔
553

554
                putchar('\n');
24✔
555
        }
556

557
        if (i->service) {
24✔
558
                printf("\t Service: %s", i->service);
24✔
559

560
                if (i->class)
24✔
561
                        printf("; class %s", i->class);
24✔
562

563
                putchar('\n');
24✔
564
        } else if (i->class)
×
565
                printf("\t   Class: %s\n", i->class);
×
566

567
        if (i->root_directory)
24✔
568
                printf("\t    Root: %s\n", i->root_directory);
24✔
569

570
        if (i->n_netif > 0) {
24✔
571
                fputs("\t   Iface:", stdout);
24✔
572

573
                for (size_t c = 0; c < i->n_netif; c++) {
48✔
574
                        char name[IF_NAMESIZE];
24✔
575

576
                        if (format_ifname(i->netif[c], name) >= 0) {
24✔
577
                                fputc(' ', stdout);
24✔
578
                                fputs(name, stdout);
24✔
579

580
                                if (ifi < 0)
24✔
581
                                        ifi = i->netif[c];
24✔
582
                                else
583
                                        ifi = 0;
584
                        } else
585
                                printf(" %i", i->netif[c]);
×
586
                }
587

588
                fputc('\n', stdout);
24✔
589
        }
590

591
        if (call_get_addresses(bus, i->name, ifi,
24✔
592
                               "\t Address: ", "\n\t          ",
593
                               &addresses) > 0) {
594
                fputs(addresses, stdout);
×
595
                fputc('\n', stdout);
×
596
        }
597

598
        print_os_release(bus, "GetMachineOSRelease", i->name, "\t      OS: ");
24✔
599

600
        print_uid_shift(bus, i->name);
24✔
601

602
        if (i->unit) {
24✔
603
                printf("\t    Unit: %s\n", i->unit);
24✔
604
                show_unit_cgroup(bus, i->unit, i->leader);
24✔
605

606
                if (arg_transport == BUS_TRANSPORT_LOCAL)
24✔
607

608
                        show_journal_by_unit(
46✔
609
                                        stdout,
610
                                        i->unit,
611
                                        /* namespace = */ NULL,
612
                                        arg_output,
613
                                        /* n_columns = */ 0,
614
                                        i->timestamp.monotonic,
615
                                        arg_lines,
616
                                        get_output_flags() | OUTPUT_BEGIN_NEWLINE,
23✔
617
                                        SD_JOURNAL_LOCAL_ONLY,
618
                                        /* system_unit = */ true,
619
                                        /* ellipsized = */ NULL);
620
        }
621
}
24✔
622

623
static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
24✔
624
        MachineStatusInfo *i = userdata;
24✔
625
        size_t l;
24✔
626
        const void *v;
24✔
627
        int r;
24✔
628

629
        assert_cc(sizeof(int32_t) == sizeof(int));
24✔
630
        r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
24✔
631
        if (r < 0)
24✔
632
                return r;
24✔
633
        if (r == 0)
24✔
634
                return -EBADMSG;
635

636
        i->n_netif = l / sizeof(int32_t);
24✔
637
        i->netif = memdup(v, l);
24✔
638
        if (!i->netif)
24✔
639
                return -ENOMEM;
×
640

641
        return 0;
642
}
643

644
static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
24✔
645

646
        static const struct bus_properties_map map[]  = {
24✔
647
                { "Name",               "s",  NULL,          offsetof(MachineStatusInfo, name)                },
648
                { "Class",              "s",  NULL,          offsetof(MachineStatusInfo, class)               },
649
                { "Service",            "s",  NULL,          offsetof(MachineStatusInfo, service)             },
650
                { "Unit",               "s",  NULL,          offsetof(MachineStatusInfo, unit)                },
651
                { "RootDirectory",      "s",  NULL,          offsetof(MachineStatusInfo, root_directory)      },
652
                { "Leader",             "u",  NULL,          offsetof(MachineStatusInfo, leader)              },
653
                { "Timestamp",          "t",  NULL,          offsetof(MachineStatusInfo, timestamp.realtime)  },
654
                { "TimestampMonotonic", "t",  NULL,          offsetof(MachineStatusInfo, timestamp.monotonic) },
655
                { "Id",                 "ay", bus_map_id128, offsetof(MachineStatusInfo, id)                  },
656
                { "NetworkInterfaces",  "ai", map_netif,     0                                                },
657
                {}
658
        };
659

660
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
661
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
24✔
662
        _cleanup_(machine_status_info_clear) MachineStatusInfo info = {};
24✔
663
        int r;
24✔
664

665
        assert(verb);
24✔
666
        assert(bus);
24✔
667
        assert(path);
24✔
668
        assert(new_line);
24✔
669

670
        r = bus_map_all_properties(bus,
24✔
671
                                   "org.freedesktop.machine1",
672
                                   path,
673
                                   map,
674
                                   0,
675
                                   &error,
676
                                   &m,
677
                                   &info);
678
        if (r < 0)
24✔
679
                return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
×
680

681
        if (*new_line)
24✔
682
                printf("\n");
2✔
683
        *new_line = true;
24✔
684

685
        print_machine_status_info(bus, &info);
24✔
686

687
        return r;
688
}
689

690
static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
12✔
691
        int r;
12✔
692

693
        assert(bus);
12✔
694
        assert(path);
12✔
695
        assert(new_line);
12✔
696

697
        if (*new_line)
12✔
698
                printf("\n");
2✔
699

700
        *new_line = true;
12✔
701

702
        r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, NULL, arg_property, arg_print_flags, NULL);
12✔
703
        if (r < 0)
12✔
704
                log_error_errno(r, "Could not get properties: %m");
×
705

706
        return r;
12✔
707
}
708

709
static int show_machine(int argc, char *argv[], void *userdata) {
40✔
710
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
40✔
711
        bool properties, new_line = false;
40✔
712
        sd_bus *bus = ASSERT_PTR(userdata);
40✔
713
        int r = 0;
40✔
714

715
        properties = !strstr(argv[0], "status");
40✔
716

717
        pager_open(arg_pager_flags);
40✔
718

719
        if (properties && argc <= 1) {
40✔
720

721
                /* If no argument is specified, inspect the manager
722
                 * itself */
723
                r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
5✔
724
                if (r < 0)
5✔
725
                        return r;
726
        }
727

728
        for (int i = 1; i < argc; i++) {
71✔
729
                _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
39✔
730
                const char *path = NULL;
39✔
731

732
                r = bus_call_method(bus, bus_machine_mgr, "GetMachine", &error, &reply, "s", argv[i]);
39✔
733
                if (r < 0)
39✔
734
                        return log_error_errno(r, "Could not get path to machine: %s", bus_error_message(&error, r));
8✔
735

736
                r = sd_bus_message_read(reply, "o", &path);
31✔
737
                if (r < 0)
31✔
738
                        return bus_log_parse_error(r);
×
739

740
                if (properties)
31✔
741
                        r = show_machine_properties(bus, path, &new_line);
7✔
742
                else
743
                        r = show_machine_info(argv[0], bus, path, &new_line);
24✔
744
        }
745

746
        return r;
747
}
748

749
static int print_image_hostname(sd_bus *bus, const char *name) {
18✔
750
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
18✔
751
        const char *hn;
18✔
752
        int r;
18✔
753

754
        r = bus_call_method(bus, bus_machine_mgr, "GetImageHostname", NULL, &reply, "s", name);
18✔
755
        if (r < 0)
18✔
756
                return r;
757

758
        r = sd_bus_message_read(reply, "s", &hn);
14✔
759
        if (r < 0)
14✔
760
                return r;
761

762
        if (!isempty(hn))
32✔
763
                printf("\tHostname: %s\n", hn);
×
764

765
        return 0;
766
}
767

768
static int print_image_machine_id(sd_bus *bus, const char *name) {
18✔
769
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
18✔
770
        sd_id128_t id;
18✔
771
        int r;
18✔
772

773
        r = bus_call_method(bus, bus_machine_mgr, "GetImageMachineID", NULL, &reply, "s", name);
18✔
774
        if (r < 0)
18✔
775
                return r;
776

777
        r = bus_message_read_id128(reply, &id);
14✔
778
        if (r < 0)
14✔
779
                return r;
780

781
        if (!sd_id128_is_null(id))
28✔
782
                printf("      Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(id));
×
783

784
        return 0;
785
}
786

787
static int print_image_machine_info(sd_bus *bus, const char *name) {
18✔
788
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
18✔
789
        int r;
18✔
790

791
        r = bus_call_method(bus, bus_machine_mgr, "GetImageMachineInfo", NULL, &reply, "s", name);
18✔
792
        if (r < 0)
18✔
793
                return r;
794

795
        r = sd_bus_message_enter_container(reply, 'a', "{ss}");
14✔
796
        if (r < 0)
14✔
797
                return r;
798

799
        for (;;) {
×
800
                const char *p, *q;
14✔
801

802
                r = sd_bus_message_read(reply, "{ss}", &p, &q);
14✔
803
                if (r < 0)
14✔
804
                        return r;
×
805
                if (r == 0)
14✔
806
                        break;
807

808
                if (streq(p, "DEPLOYMENT"))
×
809
                        printf("      Deployment: %s\n", q);
×
810
        }
811

812
        r = sd_bus_message_exit_container(reply);
14✔
813
        if (r < 0)
14✔
814
                return r;
×
815

816
        return 0;
817
}
818

819
typedef struct ImageStatusInfo {
820
        const char *name;
821
        const char *path;
822
        const char *type;
823
        bool read_only;
824
        usec_t crtime;
825
        usec_t mtime;
826
        uint64_t usage;
827
        uint64_t limit;
828
        uint64_t usage_exclusive;
829
        uint64_t limit_exclusive;
830
} ImageStatusInfo;
831

832
static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
18✔
833
        assert(bus);
18✔
834
        assert(i);
18✔
835

836
        if (i->name) {
18✔
837
                fputs(i->name, stdout);
18✔
838
                putchar('\n');
18✔
839
        }
840

841
        if (i->type)
18✔
842
                printf("\t    Type: %s\n", i->type);
18✔
843

844
        if (i->path)
18✔
845
                printf("\t    Path: %s\n", i->path);
18✔
846

847
        (void) print_image_hostname(bus, i->name);
18✔
848
        (void) print_image_machine_id(bus, i->name);
18✔
849
        (void) print_image_machine_info(bus, i->name);
18✔
850

851
        print_os_release(bus, "GetImageOSRelease", i->name, "\t      OS: ");
18✔
852

853
        printf("\t      RO: %s%s%s\n",
18✔
854
               i->read_only ? ansi_highlight_red() : "",
855
               i->read_only ? "read-only" : "writable",
18✔
856
               i->read_only ? ansi_normal() : "");
18✔
857

858
        if (timestamp_is_set(i->crtime))
18✔
859
                printf("\t Created: %s; %s\n",
18✔
860
                       FORMAT_TIMESTAMP(i->crtime), FORMAT_TIMESTAMP_RELATIVE(i->crtime));
18✔
861

862
        if (timestamp_is_set(i->mtime))
18✔
863
                printf("\tModified: %s; %s\n",
4✔
864
                       FORMAT_TIMESTAMP(i->mtime), FORMAT_TIMESTAMP_RELATIVE(i->mtime));
4✔
865

866
        if (i->usage != UINT64_MAX) {
18✔
867
                if (i->usage_exclusive != i->usage && i->usage_exclusive != UINT64_MAX)
4✔
868
                        printf("\t   Usage: %s (exclusive: %s)\n",
×
869
                               FORMAT_BYTES(i->usage), FORMAT_BYTES(i->usage_exclusive));
×
870
                else
871
                        printf("\t   Usage: %s\n", FORMAT_BYTES(i->usage));
4✔
872
        }
873

874
        if (i->limit != UINT64_MAX) {
18✔
875
                if (i->limit_exclusive != i->limit && i->limit_exclusive != UINT64_MAX)
4✔
876
                        printf("\t   Limit: %s (exclusive: %s)\n",
×
877
                               FORMAT_BYTES(i->limit), FORMAT_BYTES(i->limit_exclusive));
×
878
                else
879
                        printf("\t   Limit: %s\n", FORMAT_BYTES(i->limit));
4✔
880
        }
881
}
18✔
882

883
static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
18✔
884

885
        static const struct bus_properties_map map[]  = {
18✔
886
                { "Name",                  "s",  NULL, offsetof(ImageStatusInfo, name)            },
887
                { "Path",                  "s",  NULL, offsetof(ImageStatusInfo, path)            },
888
                { "Type",                  "s",  NULL, offsetof(ImageStatusInfo, type)            },
889
                { "ReadOnly",              "b",  NULL, offsetof(ImageStatusInfo, read_only)       },
890
                { "CreationTimestamp",     "t",  NULL, offsetof(ImageStatusInfo, crtime)          },
891
                { "ModificationTimestamp", "t",  NULL, offsetof(ImageStatusInfo, mtime)           },
892
                { "Usage",                 "t",  NULL, offsetof(ImageStatusInfo, usage)           },
893
                { "Limit",                 "t",  NULL, offsetof(ImageStatusInfo, limit)           },
894
                { "UsageExclusive",        "t",  NULL, offsetof(ImageStatusInfo, usage_exclusive) },
895
                { "LimitExclusive",        "t",  NULL, offsetof(ImageStatusInfo, limit_exclusive) },
896
                {}
897
        };
898

899
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
900
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
18✔
901
        ImageStatusInfo info = {};
18✔
902
        int r;
18✔
903

904
        assert(bus);
18✔
905
        assert(path);
18✔
906
        assert(new_line);
18✔
907

908
        r = bus_map_all_properties(bus,
18✔
909
                                   "org.freedesktop.machine1",
910
                                   path,
911
                                   map,
912
                                   BUS_MAP_BOOLEAN_AS_BOOL,
913
                                   &error,
914
                                   &m,
915
                                   &info);
916
        if (r < 0)
18✔
917
                return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
×
918

919
        if (*new_line)
18✔
920
                printf("\n");
6✔
921
        *new_line = true;
18✔
922

923
        print_image_status_info(bus, &info);
18✔
924

925
        return r;
926
}
927

928
typedef struct PoolStatusInfo {
929
        const char *path;
930
        uint64_t usage;
931
        uint64_t limit;
932
} PoolStatusInfo;
933

934
static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) {
1✔
935
        if (i->path)
1✔
936
                printf("\t    Path: %s\n", i->path);
1✔
937

938
        if (i->usage != UINT64_MAX)
1✔
939
                printf("\t   Usage: %s\n", FORMAT_BYTES(i->usage));
×
940

941
        if (i->limit != UINT64_MAX)
1✔
942
                printf("\t   Limit: %s\n", FORMAT_BYTES(i->limit));
×
943
}
1✔
944

945
static int show_pool_info(sd_bus *bus) {
1✔
946

947
        static const struct bus_properties_map map[]  = {
1✔
948
                { "PoolPath",  "s",  NULL, offsetof(PoolStatusInfo, path)  },
949
                { "PoolUsage", "t",  NULL, offsetof(PoolStatusInfo, usage) },
950
                { "PoolLimit", "t",  NULL, offsetof(PoolStatusInfo, limit) },
951
                {}
952
        };
953

954
        PoolStatusInfo info = {
1✔
955
                .usage = UINT64_MAX,
956
                .limit = UINT64_MAX,
957
        };
958

959
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
960
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1✔
961
        int r;
1✔
962

963
        assert(bus);
1✔
964

965
        r = bus_map_all_properties(bus,
1✔
966
                                   "org.freedesktop.machine1",
967
                                   "/org/freedesktop/machine1",
968
                                   map,
969
                                   0,
970
                                   &error,
971
                                   &m,
972
                                   &info);
973
        if (r < 0)
1✔
974
                return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
×
975

976
        print_pool_status_info(bus, &info);
1✔
977

978
        return 0;
979
}
980

981
static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
19✔
982
        int r;
19✔
983

984
        assert(bus);
19✔
985
        assert(path);
19✔
986
        assert(new_line);
19✔
987

988
        if (*new_line)
19✔
989
                printf("\n");
6✔
990

991
        *new_line = true;
19✔
992

993
        r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, NULL, arg_property, arg_print_flags, NULL);
19✔
994
        if (r < 0)
19✔
995
                log_error_errno(r, "Could not get properties: %m");
×
996

997
        return r;
19✔
998
}
999

1000
static int show_image(int argc, char *argv[], void *userdata) {
35✔
1001
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
35✔
1002
        bool properties, new_line = false;
35✔
1003
        sd_bus *bus = ASSERT_PTR(userdata);
35✔
1004
        int r = 0;
35✔
1005

1006
        properties = !strstr(argv[0], "status");
35✔
1007

1008
        pager_open(arg_pager_flags);
35✔
1009

1010
        if (argc <= 1) {
35✔
1011

1012
                /* If no argument is specified, inspect the manager
1013
                 * itself */
1014

1015
                if (properties)
2✔
1016
                        r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
1✔
1017
                else
1018
                        r = show_pool_info(bus);
1✔
1019
                if (r < 0)
2✔
1020
                        return r;
1021
        }
1022

1023
        for (int i = 1; i < argc; i++) {
71✔
1024
                _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
45✔
1025
                const char *path = NULL;
45✔
1026

1027
                r = bus_call_method(bus, bus_machine_mgr, "GetImage", &error, &reply, "s", argv[i]);
45✔
1028
                if (r < 0)
45✔
1029
                        return log_error_errno(r, "Could not get path to image: %s", bus_error_message(&error, r));
9✔
1030

1031
                r = sd_bus_message_read(reply, "o", &path);
36✔
1032
                if (r < 0)
36✔
1033
                        return bus_log_parse_error(r);
×
1034

1035
                if (properties)
36✔
1036
                        r = show_image_properties(bus, path, &new_line);
18✔
1037
                else
1038
                        r = show_image_info(bus, path, &new_line);
18✔
1039
        }
1040

1041
        return r;
1042
}
1043

1044
static int kill_machine(int argc, char *argv[], void *userdata) {
9✔
1045
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
9✔
1046
        sd_bus *bus = ASSERT_PTR(userdata);
9✔
1047
        int r;
9✔
1048

1049
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
9✔
1050

1051
        if (!arg_kill_whom)
9✔
1052
                arg_kill_whom = "all";
1✔
1053

1054
        for (int i = 1; i < argc; i++) {
28✔
1055
                r = bus_call_method(
38✔
1056
                                bus,
1057
                                bus_machine_mgr,
1058
                                "KillMachine",
1059
                                &error,
1060
                                NULL,
1061
                                "ssi", argv[i], arg_kill_whom, arg_signal);
19✔
1062
                if (r < 0)
19✔
1063
                        return log_error_errno(r, "Could not kill machine: %s", bus_error_message(&error, r));
×
1064
        }
1065

1066
        return 0;
1067
}
1068

1069
static int reboot_machine(int argc, char *argv[], void *userdata) {
2✔
1070
        if (arg_runner == RUNNER_VMSPAWN)
2✔
1071
                return log_error_errno(
×
1072
                                SYNTHETIC_ERRNO(EOPNOTSUPP),
1073
                                "%s only support supported for --runner=nspawn",
1074
                                streq(argv[0], "reboot") ? "Reboot" : "Restart");
1075

1076
        arg_kill_whom = "leader";
2✔
1077
        arg_signal = SIGINT; /* sysvinit + systemd */
2✔
1078

1079
        return kill_machine(argc, argv, userdata);
2✔
1080
}
1081

1082
static int poweroff_machine(int argc, char *argv[], void *userdata) {
4✔
1083
        arg_kill_whom = "leader";
4✔
1084
        arg_signal = SIGRTMIN+4; /* only systemd */
4✔
1085

1086
        return kill_machine(argc, argv, userdata);
4✔
1087
}
1088

1089
static int terminate_machine(int argc, char *argv[], void *userdata) {
3✔
1090
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3✔
1091
        sd_bus *bus = ASSERT_PTR(userdata);
3✔
1092
        int r;
3✔
1093

1094
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
3✔
1095

1096
        for (int i = 1; i < argc; i++) {
6✔
1097
                r = bus_call_method(bus, bus_machine_mgr, "TerminateMachine", &error, NULL, "s", argv[i]);
3✔
1098
                if (r < 0)
3✔
1099
                        return log_error_errno(r, "Could not terminate machine: %s", bus_error_message(&error, r));
×
1100
        }
1101

1102
        return 0;
1103
}
1104

1105
static const char *select_copy_method(bool copy_from, bool force) {
3✔
1106
        if (force)
3✔
1107
                return copy_from ? "CopyFromMachineWithFlags" : "CopyToMachineWithFlags";
×
1108
        else
1109
                return copy_from ? "CopyFromMachine" : "CopyToMachine";
3✔
1110
}
1111

1112
static int copy_files(int argc, char *argv[], void *userdata) {
3✔
1113
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1114
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
3✔
1115
        _cleanup_free_ char *abs_host_path = NULL;
3✔
1116
        char *dest, *host_path, *container_path;
3✔
1117
        sd_bus *bus = ASSERT_PTR(userdata);
3✔
1118
        bool copy_from;
3✔
1119
        int r;
3✔
1120

1121
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
3✔
1122

1123
        copy_from = streq(argv[0], "copy-from");
3✔
1124
        dest = argv[3] ?: argv[2];
3✔
1125
        host_path = copy_from ? dest : argv[2];
3✔
1126
        container_path = copy_from ? argv[2] : dest;
3✔
1127

1128
        if (!path_is_absolute(host_path)) {
3✔
1129
                r = path_make_absolute_cwd(host_path, &abs_host_path);
×
1130
                if (r < 0)
×
1131
                        return log_error_errno(r, "Failed to make path absolute: %m");
×
1132

1133
                host_path = abs_host_path;
×
1134
        }
1135

1136
        r = bus_message_new_method_call(
3✔
1137
                        bus,
1138
                        &m,
1139
                        bus_machine_mgr,
1140
                        select_copy_method(copy_from, arg_force));
1141
        if (r < 0)
3✔
1142
                return bus_log_create_error(r);
×
1143

1144
        r = sd_bus_message_append(
5✔
1145
                        m,
1146
                        "sss",
1147
                        argv[1],
1148
                        copy_from ? container_path : host_path,
1149
                        copy_from ? host_path : container_path);
1150
        if (r < 0)
3✔
1151
                return bus_log_create_error(r);
×
1152

1153
        if (arg_force) {
3✔
1154
                r = sd_bus_message_append(m, "t", (uint64_t) MACHINE_COPY_REPLACE);
×
1155
                if (r < 0)
×
1156
                        return bus_log_create_error(r);
×
1157
        }
1158

1159
        /* This is a slow operation, hence turn off any method call timeouts */
1160
        r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
3✔
1161
        if (r < 0)
3✔
1162
                return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r));
×
1163

1164
        return 0;
1165
}
1166

1167
static int bind_mount(int argc, char *argv[], void *userdata) {
1✔
1168
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1✔
1169
        sd_bus *bus = ASSERT_PTR(userdata);
1✔
1170
        int r;
1✔
1171

1172
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1✔
1173

1174
        r = bus_call_method(
1✔
1175
                        bus,
1176
                        bus_machine_mgr,
1177
                        "BindMountMachine",
1178
                        &error,
1179
                        NULL,
1180
                        "sssbb",
1181
                        argv[1],
1182
                        argv[2],
1183
                        argv[3],
1184
                        arg_read_only,
1185
                        arg_mkdir);
1186
        if (r < 0)
1✔
1187
                return log_error_errno(r, "Failed to bind mount: %s", bus_error_message(&error, r));
×
1188

1189
        return 0;
1190
}
1191

1192
static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
×
1193
        PTYForward *forward = ASSERT_PTR(userdata);
×
1194
        int r;
×
1195

1196
        assert(m);
×
1197

1198
        /* Tell the forwarder to exit on the next vhangup(), so that we still flush out what might be queued
1199
         * and exit then. */
1200

1201
        r = pty_forward_set_ignore_vhangup(forward, false);
×
1202
        if (r < 0) {
×
1203
                /* On error, quit immediately. */
1204
                log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
×
1205
                (void) sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
×
1206
        }
1207

1208
        return 0;
×
1209
}
1210

1211
static int process_forward(sd_event *event, sd_bus_slot *machine_removed_slot, int master, PTYForwardFlags flags, const char *name) {
2✔
1212
        int r;
2✔
1213

1214
        assert(event);
2✔
1215
        assert(machine_removed_slot);
2✔
1216
        assert(master >= 0);
2✔
1217
        assert(name);
2✔
1218

1219
        if (!arg_quiet) {
2✔
1220
                if (streq(name, ".host"))
2✔
1221
                        log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
2✔
1222
                else
1223
                        log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
×
1224
        }
1225

1226
        _cleanup_(osc_context_closep) sd_id128_t osc_context_id = SD_ID128_NULL;
2✔
1227
        if (!terminal_is_dumb()) {
2✔
1228
                r = osc_context_open_container(name, /* ret_seq= */ NULL, &osc_context_id);
×
1229
                if (r < 0)
×
1230
                        return r;
1231
        }
1232

1233
        r = sd_event_set_signal_exit(event, true);
2✔
1234
        if (r < 0)
2✔
1235
                return log_error_errno(r, "Failed to enable SIGINT/SITERM handling: %m");
×
1236

1237
        _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
2✔
1238
        r = pty_forward_new(event, master, flags, &forward);
2✔
1239
        if (r < 0)
2✔
1240
                return log_error_errno(r, "Failed to create PTY forwarder: %m");
×
1241

1242
        /* No userdata should not set previously. */
1243
        assert_se(!sd_bus_slot_set_userdata(machine_removed_slot, forward));
2✔
1244

1245
        r = sd_event_loop(event);
2✔
1246
        if (r < 0)
2✔
1247
                return log_error_errno(r, "Failed to run event loop: %m");
×
1248

1249
        bool machine_died =
4✔
1250
                (flags & PTY_FORWARD_IGNORE_VHANGUP) &&
2✔
1251
                pty_forward_get_ignore_vhangup(forward) == 0;
×
1252

1253
        if (!arg_quiet) {
2✔
1254
                if (machine_died)
2✔
1255
                        log_info("Machine %s terminated.", name);
×
1256
                else if (streq(name, ".host"))
2✔
1257
                        log_info("Connection to the local host terminated.");
2✔
1258
                else
1259
                        log_info("Connection to machine %s terminated.", name);
×
1260
        }
1261

1262
        return 0;
1263
}
1264

1265
static int parse_machine_uid(const char *spec, const char **machine, char **uid) {
2✔
1266
        /*
1267
         * Whatever is specified in the spec takes priority over global arguments.
1268
         */
1269
        char *_uid = NULL;
2✔
1270
        const char *_machine = NULL;
2✔
1271

1272
        if (spec) {
2✔
1273
                const char *at;
2✔
1274

1275
                at = strchr(spec, '@');
2✔
1276
                if (at) {
2✔
1277
                        if (at == spec)
2✔
1278
                                /* Do the same as ssh and refuse "@host". */
1279
                                return -EINVAL;
1280

1281
                        _machine = at + 1;
2✔
1282
                        _uid = strndup(spec, at - spec);
2✔
1283
                        if (!_uid)
2✔
1284
                                return -ENOMEM;
1285
                } else
1286
                        _machine = spec;
1287
        };
2✔
1288

1289
        if (arg_uid && !_uid) {
2✔
1290
                _uid = strdup(arg_uid);
×
1291
                if (!_uid)
×
1292
                        return -ENOMEM;
1293
        }
1294

1295
        *uid = _uid;
2✔
1296
        *machine = isempty(_machine) ? ".host" : _machine;
4✔
1297
        return 0;
2✔
1298
}
1299

1300
static int login_machine(int argc, char *argv[], void *userdata) {
×
1301
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
×
1302
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1303
        _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
×
1304
        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
×
1305
        int master = -1, r;
×
1306
        sd_bus *bus = ASSERT_PTR(userdata);
×
1307
        const char *match, *machine;
×
1308

1309
        if (!strv_isempty(arg_setenv) || arg_uid)
×
1310
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1311
                                       "--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1312

1313
        if (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE))
×
1314
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
1315
                                       "Login only supported on local machines.");
1316

1317
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
×
1318

1319
        r = sd_event_default(&event);
×
1320
        if (r < 0)
×
1321
                return log_error_errno(r, "Failed to get event loop: %m");
×
1322

1323
        r = sd_bus_attach_event(bus, event, 0);
×
1324
        if (r < 0)
×
1325
                return log_error_errno(r, "Failed to attach bus to event loop: %m");
×
1326

1327
        machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1];
×
1328

1329
        match = strjoina("type='signal',"
×
1330
                         "sender='org.freedesktop.machine1',"
1331
                         "path='/org/freedesktop/machine1',",
1332
                         "interface='org.freedesktop.machine1.Manager',"
1333
                         "member='MachineRemoved',"
1334
                         "arg0='", machine, "'");
1335

1336
        r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, NULL);
×
1337
        if (r < 0)
×
1338
                return log_error_errno(r, "Failed to request machine removal match: %m");
×
1339

1340
        r = bus_call_method(bus, bus_machine_mgr, "OpenMachineLogin", &error, &reply, "s", machine);
×
1341
        if (r < 0)
×
1342
                return log_error_errno(r, "Failed to get login PTY: %s", bus_error_message(&error, r));
×
1343

1344
        r = sd_bus_message_read(reply, "hs", &master, NULL);
×
1345
        if (r < 0)
×
1346
                return bus_log_parse_error(r);
×
1347

1348
        return process_forward(event, slot, master, PTY_FORWARD_IGNORE_VHANGUP, machine);
×
1349
}
1350

1351
static int shell_machine(int argc, char *argv[], void *userdata) {
2✔
1352
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
4✔
1353
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1354
        _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
2✔
1355
        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
2✔
1356
        int master = -1, r;
2✔
1357
        sd_bus *bus = ASSERT_PTR(userdata);
2✔
1358
        const char *match, *machine, *path;
2✔
1359
        _cleanup_free_ char *uid = NULL;
2✔
1360

1361
        if (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE))
2✔
1362
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
1363
                                       "Shell only supported on local machines.");
1364

1365
        /* Pass $TERM & Co. to shell session, if not explicitly specified. */
1366
        FOREACH_STRING(v, "TERM=", "COLORTERM=", "NO_COLOR=") {
8✔
1367
                if (strv_find_prefix(arg_setenv, v))
6✔
1368
                        continue;
×
1369

1370
                const char *t = strv_find_prefix(environ, v);
6✔
1371
                if (!t)
6✔
1372
                        continue;
4✔
1373

1374
                if (strv_extend(&arg_setenv, t) < 0)
2✔
1375
                        return log_oom();
×
1376
        }
1377

1378
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
2✔
1379

1380
        r = sd_event_default(&event);
2✔
1381
        if (r < 0)
2✔
1382
                return log_error_errno(r, "Failed to get event loop: %m");
×
1383

1384
        r = sd_bus_attach_event(bus, event, 0);
2✔
1385
        if (r < 0)
2✔
1386
                return log_error_errno(r, "Failed to attach bus to event loop: %m");
×
1387

1388
        r = parse_machine_uid(argc >= 2 ? argv[1] : NULL, &machine, &uid);
2✔
1389
        if (r < 0)
2✔
1390
                return log_error_errno(r, "Failed to parse machine specification: %m");
×
1391

1392
        match = strjoina("type='signal',"
18✔
1393
                         "sender='org.freedesktop.machine1',"
1394
                         "path='/org/freedesktop/machine1',",
1395
                         "interface='org.freedesktop.machine1.Manager',"
1396
                         "member='MachineRemoved',"
1397
                         "arg0='", machine, "'");
1398

1399
        r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, NULL);
2✔
1400
        if (r < 0)
2✔
1401
                return log_error_errno(r, "Failed to request machine removal match: %m");
×
1402

1403
        r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "OpenMachineShell");
2✔
1404
        if (r < 0)
2✔
1405
                return bus_log_create_error(r);
×
1406

1407
        path = argc < 3 || isempty(argv[2]) ? NULL : argv[2];
2✔
1408

1409
        r = sd_bus_message_append(m, "sss", machine, uid, path);
2✔
1410
        if (r < 0)
2✔
1411
                return bus_log_create_error(r);
×
1412

1413
        r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2);
2✔
1414
        if (r < 0)
2✔
1415
                return bus_log_create_error(r);
×
1416

1417
        r = sd_bus_message_append_strv(m, arg_setenv);
2✔
1418
        if (r < 0)
2✔
1419
                return bus_log_create_error(r);
×
1420

1421
        r = sd_bus_call(bus, m, 0, &error, &reply);
2✔
1422
        if (r < 0)
2✔
1423
                return log_error_errno(r, "Failed to get shell PTY: %s", bus_error_message(&error, r));
×
1424

1425
        r = sd_bus_message_read(reply, "hs", &master, NULL);
2✔
1426
        if (r < 0)
2✔
1427
                return bus_log_parse_error(r);
×
1428

1429
        return process_forward(event, slot, master, /* flags = */ 0, machine);
2✔
1430
}
1431

1432
static int normalize_nspawn_filename(const char *name, char **ret_file) {
3✔
1433
        _cleanup_free_ char *file = NULL;
3✔
1434

1435
        assert(name);
3✔
1436
        assert(ret_file);
3✔
1437

1438
        if (!endswith(name, ".nspawn"))
3✔
1439
                file = strjoin(name, ".nspawn");
×
1440
        else
1441
                file = strdup(name);
3✔
1442
        if (!file)
3✔
1443
                return log_oom();
×
1444

1445
        if (!filename_is_valid(file))
3✔
1446
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid settings file name '%s'.", file);
×
1447

1448
        *ret_file = TAKE_PTR(file);
3✔
1449
        return 0;
3✔
1450
}
1451

1452
static int get_settings_path(const char *name, char **ret_path) {
3✔
1453
        assert(name);
3✔
1454
        assert(ret_path);
3✔
1455

1456
        FOREACH_STRING(i, "/etc/systemd/nspawn", "/run/systemd/nspawn", "/var/lib/machines") {
7✔
1457
                _cleanup_free_ char *path = NULL;
7✔
1458

1459
                path = path_join(i, name);
7✔
1460
                if (!path)
7✔
1461
                        return -ENOMEM;
1462

1463
                if (access(path, F_OK) >= 0) {
7✔
1464
                        *ret_path = TAKE_PTR(path);
3✔
1465
                        return 0;
3✔
1466
                }
1467
        }
1468

1469
        return -ENOENT;
×
1470
}
1471

1472
static int edit_settings(int argc, char *argv[], void *userdata) {
2✔
1473
        _cleanup_(edit_file_context_done) EditFileContext context = {};
2✔
1474
        int r;
2✔
1475

1476
        if (!on_tty())
2✔
1477
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit machine settings if not on a tty.");
×
1478

1479
        if (arg_transport != BUS_TRANSPORT_LOCAL)
2✔
1480
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
1481
                                       "Edit is only supported on the host machine.");
1482

1483
        if (arg_runner == RUNNER_VMSPAWN)
2✔
1484
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Edit is only supported for --runner=nspawn");
×
1485

1486
        r = mac_init();
2✔
1487
        if (r < 0)
2✔
1488
                return r;
1489

1490
        STRV_FOREACH(name, strv_skip(argv, 1)) {
4✔
1491
                _cleanup_free_ char *file = NULL, *path = NULL;
2✔
1492

1493
                if (path_is_absolute(*name)) {
2✔
1494
                        if (!path_is_safe(*name))
×
1495
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1496
                                                       "Invalid settings file path '%s'.",
1497
                                                       *name);
1498

1499
                        r = edit_files_add(&context, *name, NULL, NULL);
×
1500
                        if (r < 0)
×
1501
                                return r;
1502
                        continue;
×
1503
                }
1504

1505
                r = normalize_nspawn_filename(*name, &file);
2✔
1506
                if (r < 0)
2✔
1507
                        return r;
1508

1509
                r = get_settings_path(file, &path);
2✔
1510
                if (r == -ENOENT) {
2✔
1511
                        log_debug("No existing settings file for machine '%s' found, creating a new file.", *name);
×
1512

1513
                        path = path_join("/etc/systemd/nspawn", file);
×
1514
                        if (!path)
×
1515
                                return log_oom();
×
1516

1517
                        r = edit_files_add(&context, path, NULL, NULL);
×
1518
                        if (r < 0)
×
1519
                                return r;
1520
                        continue;
×
1521
                }
1522
                if (r < 0)
2✔
1523
                        return log_error_errno(r, "Failed to get the path of the settings file: %m");
×
1524

1525
                if (path_startswith(path, "/var/lib/machines")) {
2✔
1526
                        _cleanup_free_ char *new_path = NULL;
1✔
1527

1528
                        new_path = path_join("/etc/systemd/nspawn", file);
1✔
1529
                        if (!new_path)
1✔
1530
                                return log_oom();
×
1531

1532
                        r = edit_files_add(&context, new_path, path, NULL);
1✔
1533
                } else
1534
                        r = edit_files_add(&context, path, NULL, NULL);
1✔
1535
                if (r < 0)
2✔
1536
                        return r;
1537
        }
1538

1539
        return do_edit_files_and_install(&context);
2✔
1540
}
1541

1542
static int cat_settings(int argc, char *argv[], void *userdata) {
2✔
1543
        int r = 0;
2✔
1544

1545
        if (arg_transport != BUS_TRANSPORT_LOCAL)
2✔
1546
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
1547
                                       "Cat is only supported on the host machine.");
1548

1549
        if (arg_runner == RUNNER_VMSPAWN)
2✔
1550
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Cat is only supported for --runner=nspawn");
×
1551

1552
        pager_open(arg_pager_flags);
2✔
1553

1554
        STRV_FOREACH(name, strv_skip(argv, 1)) {
4✔
1555
                _cleanup_free_ char *file = NULL, *path = NULL;
2✔
1556
                int q;
2✔
1557

1558
                if (path_is_absolute(*name)) {
2✔
1559
                        if (!path_is_safe(*name))
1✔
1560
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1561
                                                       "Invalid settings file path '%s'.",
1562
                                                       *name);
1563

1564
                        q = cat_files(*name, /* dropins = */ NULL, /* flags = */ CAT_FORMAT_HAS_SECTIONS);
1✔
1565
                        if (q < 0)
1✔
1566
                                return r < 0 ? r : q;
×
1567
                        continue;
1✔
1568
                }
1569

1570
                q = normalize_nspawn_filename(*name, &file);
1✔
1571
                if (q < 0)
1✔
1572
                        return r < 0 ? r : q;
×
1573

1574
                q = get_settings_path(file, &path);
1✔
1575
                if (q == -ENOENT) {
1✔
1576
                        log_error_errno(q, "No settings file found for machine '%s'.", *name);
×
1577
                        r = r < 0 ? r : q;
×
1578
                        continue;
×
1579
                }
1580
                if (q < 0) {
1✔
1581
                        log_error_errno(q, "Failed to get the path of the settings file: %m");
×
1582
                        return r < 0 ? r : q;
×
1583
                }
1584

1585
                q = cat_files(path, /* dropins = */ NULL, /* flags = */ CAT_FORMAT_HAS_SECTIONS);
1✔
1586
                if (q < 0)
1✔
1587
                        return r < 0 ? r : q;
×
1588
        }
1589

1590
        return r;
1591
}
1592

1593
static int remove_image(int argc, char *argv[], void *userdata) {
8✔
1594
        sd_bus *bus = ASSERT_PTR(userdata);
8✔
1595
        int r;
8✔
1596

1597
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
8✔
1598

1599
        for (int i = 1; i < argc; i++) {
20✔
1600
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1601
                _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
12✔
1602

1603
                r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "RemoveImage");
12✔
1604
                if (r < 0)
12✔
1605
                        return bus_log_create_error(r);
×
1606

1607
                r = sd_bus_message_append(m, "s", argv[i]);
12✔
1608
                if (r < 0)
12✔
1609
                        return bus_log_create_error(r);
×
1610

1611
                /* This is a slow operation, hence turn off any method call timeouts */
1612
                r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
12✔
1613
                if (r < 0)
12✔
1614
                        return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r));
×
1615
        }
1616

1617
        return 0;
1618
}
1619

1620
static int rename_image(int argc, char *argv[], void *userdata) {
3✔
1621
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3✔
1622
        sd_bus *bus = ASSERT_PTR(userdata);
3✔
1623
        int r;
3✔
1624

1625
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
3✔
1626

1627
        r = bus_call_method(
3✔
1628
                        bus,
1629
                        bus_machine_mgr,
1630
                        "RenameImage",
1631
                        &error,
1632
                        NULL,
1633
                        "ss", argv[1], argv[2]);
1634
        if (r < 0)
3✔
1635
                return log_error_errno(r, "Could not rename image: %s", bus_error_message(&error, r));
×
1636

1637
        return 0;
1638
}
1639

1640
static int clone_image(int argc, char *argv[], void *userdata) {
13✔
1641
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1642
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
13✔
1643
        sd_bus *bus = ASSERT_PTR(userdata);
13✔
1644
        int r;
13✔
1645

1646
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
13✔
1647

1648
        r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "CloneImage");
13✔
1649
        if (r < 0)
13✔
1650
                return bus_log_create_error(r);
×
1651

1652
        r = sd_bus_message_append(m, "ssb", argv[1], argv[2], arg_read_only);
13✔
1653
        if (r < 0)
13✔
1654
                return bus_log_create_error(r);
×
1655

1656
        /* This is a slow operation, hence turn off any method call timeouts */
1657
        r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
13✔
1658
        if (r < 0)
13✔
1659
                return log_error_errno(r, "Could not clone image: %s", bus_error_message(&error, r));
×
1660

1661
        return 0;
1662
}
1663

1664
static int read_only_image(int argc, char *argv[], void *userdata) {
5✔
1665
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
5✔
1666
        sd_bus *bus = ASSERT_PTR(userdata);
5✔
1667
        int b = true, r;
5✔
1668

1669
        if (argc > 2) {
5✔
1670
                b = parse_boolean(argv[2]);
5✔
1671
                if (b < 0)
5✔
1672
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3✔
1673
                                               "Failed to parse boolean argument: %s",
1674
                                               argv[2]);
1675
        }
1676

1677
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
2✔
1678

1679
        r = bus_call_method(bus, bus_machine_mgr, "MarkImageReadOnly", &error, NULL, "sb", argv[1], b);
2✔
1680
        if (r < 0)
2✔
1681
                return log_error_errno(r, "Could not mark image read-only: %s", bus_error_message(&error, r));
×
1682

1683
        return 0;
1684
}
1685

1686
static int image_exists(sd_bus *bus, const char *name) {
27✔
1687
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
27✔
1688
        int r;
27✔
1689

1690
        assert(bus);
27✔
1691
        assert(name);
27✔
1692

1693
        r = bus_call_method(bus, bus_machine_mgr, "GetImage", &error, NULL, "s", name);
27✔
1694
        if (r < 0) {
27✔
1695
                if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_IMAGE))
×
1696
                        return 0;
1697

1698
                return log_error_errno(r, "Failed to check whether image %s exists: %s", name, bus_error_message(&error, r));
×
1699
        }
1700

1701
        return 1;
1702
}
1703

1704
static int make_service_name(const char *name, char **ret) {
29✔
1705
        int r;
29✔
1706

1707
        assert(name);
29✔
1708
        assert(ret);
29✔
1709
        assert(arg_runner >= 0 && arg_runner < (MachineRunner) ELEMENTSOF(machine_runner_unit_prefix_table));
29✔
1710

1711
        if (!hostname_is_valid(name, 0))
29✔
1712
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2✔
1713
                                       "Invalid machine name %s.", name);
1714

1715
        r = unit_name_build(machine_runner_unit_prefix_table[arg_runner], name, ".service", ret);
27✔
1716
        if (r < 0)
27✔
1717
                return log_error_errno(r, "Failed to build unit name: %m");
×
1718

1719
        return 0;
1720
}
1721

1722
static int start_machine(int argc, char *argv[], void *userdata) {
15✔
1723
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
15✔
1724
        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
15✔
1725
        sd_bus *bus = ASSERT_PTR(userdata);
15✔
1726
        int r;
15✔
1727

1728
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
15✔
1729
        ask_password_agent_open_if_enabled(arg_transport, arg_ask_password);
15✔
1730

1731
        r = bus_wait_for_jobs_new(bus, &w);
15✔
1732
        if (r < 0)
15✔
1733
                return log_error_errno(r, "Could not watch jobs: %m");
×
1734

1735
        for (int i = 1; i < argc; i++) {
32✔
1736
                _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
17✔
1737
                _cleanup_free_ char *unit = NULL;
17✔
1738
                const char *object;
17✔
1739

1740
                r = make_service_name(argv[i], &unit);
17✔
1741
                if (r < 0)
17✔
1742
                        return r;
1743

1744
                r = image_exists(bus, argv[i]);
17✔
1745
                if (r < 0)
17✔
1746
                        return r;
1747
                if (r == 0)
17✔
1748
                        return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
×
1749
                                               "Machine image '%s' does not exist.",
1750
                                               argv[i]);
1751

1752
                r = bus_call_method(
17✔
1753
                                bus,
1754
                                bus_systemd_mgr,
1755
                                "StartUnit",
1756
                                &error,
1757
                                &reply,
1758
                                "ss", unit, "fail");
1759
                if (r < 0)
17✔
1760
                        return log_error_errno(r, "Failed to start unit: %s", bus_error_message(&error, r));
×
1761

1762
                r = sd_bus_message_read(reply, "o", &object);
17✔
1763
                if (r < 0)
17✔
1764
                        return bus_log_parse_error(r);
×
1765

1766
                r = bus_wait_for_jobs_add(w, object);
17✔
1767
                if (r < 0)
17✔
1768
                        return log_oom();
×
1769
        }
1770

1771
        r = bus_wait_for_jobs(w, arg_quiet, NULL);
15✔
1772
        if (r < 0)
15✔
1773
                return r;
×
1774

1775
        return 0;
1776
}
1777

1778
static int enable_machine(int argc, char *argv[], void *userdata) {
6✔
1779
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
12✔
1780
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
6✔
1781
        const char *method;
6✔
1782
        sd_bus *bus = ASSERT_PTR(userdata);
6✔
1783
        int r;
6✔
1784
        bool enable;
6✔
1785

1786
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
6✔
1787

1788
        enable = streq(argv[0], "enable");
6✔
1789
        method = enable ? "EnableUnitFiles" : "DisableUnitFiles";
6✔
1790

1791
        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
6✔
1792
        if (r < 0)
6✔
1793
                return bus_log_create_error(r);
×
1794

1795
        r = sd_bus_message_open_container(m, 'a', "s");
6✔
1796
        if (r < 0)
6✔
1797
                return bus_log_create_error(r);
×
1798

1799
        if (enable) {
6✔
1800
                r = sd_bus_message_append(m, "s", "machines.target");
3✔
1801
                if (r < 0)
3✔
1802
                        return bus_log_create_error(r);
×
1803
        }
1804

1805
        for (int i = 1; i < argc; i++) {
16✔
1806
                _cleanup_free_ char *unit = NULL;
12✔
1807

1808
                r = make_service_name(argv[i], &unit);
12✔
1809
                if (r < 0)
12✔
1810
                        return r;
1811

1812
                r = image_exists(bus, argv[i]);
10✔
1813
                if (r < 0)
10✔
1814
                        return r;
1815
                if (r == 0)
10✔
1816
                        return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
×
1817
                                               "Machine image '%s' does not exist.",
1818
                                               argv[i]);
1819

1820
                r = sd_bus_message_append(m, "s", unit);
10✔
1821
                if (r < 0)
10✔
1822
                        return bus_log_create_error(r);
×
1823
        }
1824

1825
        r = sd_bus_message_close_container(m);
4✔
1826
        if (r < 0)
4✔
1827
                return bus_log_create_error(r);
×
1828

1829
        if (enable)
4✔
1830
                r = sd_bus_message_append(m, "bb", false, false);
2✔
1831
        else
1832
                r = sd_bus_message_append(m, "b", false);
2✔
1833
        if (r < 0)
4✔
1834
                return bus_log_create_error(r);
×
1835

1836
        r = sd_bus_call(bus, m, 0, &error, &reply);
4✔
1837
        if (r < 0)
4✔
1838
                return log_error_errno(r, "Failed to enable or disable unit: %s", bus_error_message(&error, r));
×
1839

1840
        if (enable) {
4✔
1841
                r = sd_bus_message_read(reply, "b", NULL);
2✔
1842
                if (r < 0)
2✔
1843
                        return bus_log_parse_error(r);
×
1844
        }
1845

1846
        r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
4✔
1847
        if (r < 0)
4✔
1848
                return r;
1849

1850
        r = bus_service_manager_reload(bus);
4✔
1851
        if (r < 0)
4✔
1852
                return r;
1853

1854
        if (arg_now) {
4✔
1855
                _cleanup_strv_free_ char **new_args = NULL;
×
1856

1857
                new_args = strv_new(enable ? "start" : "poweroff");
×
1858
                if (!new_args)
×
1859
                        return log_oom();
×
1860

1861
                r = strv_extend_strv(&new_args, argv + 1, /* filter_duplicates = */ false);
×
1862
                if (r < 0)
×
1863
                        return log_oom();
×
1864

1865
                if (enable)
×
1866
                        return start_machine(strv_length(new_args), new_args, userdata);
×
1867

1868
                return poweroff_machine(strv_length(new_args), new_args, userdata);
×
1869
        }
1870

1871
        return 0;
1872
}
1873

1874
static int set_limit(int argc, char *argv[], void *userdata) {
×
1875
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1876
        sd_bus *bus = userdata;
×
1877
        uint64_t limit;
×
1878
        int r;
×
1879

1880
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
×
1881

1882
        if (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
×
1883
                limit = UINT64_MAX;
×
1884
        else {
1885
                r = parse_size(argv[argc-1], 1024, &limit);
×
1886
                if (r < 0)
×
1887
                        return log_error_errno(r, "Failed to parse size: %s", argv[argc-1]);
×
1888
        }
1889

1890
        if (argc > 2)
×
1891
                /* With two arguments changes the quota limit of the
1892
                 * specified image */
1893
                r = bus_call_method(bus, bus_machine_mgr, "SetImageLimit", &error, NULL, "st", argv[1], limit);
×
1894
        else
1895
                /* With one argument changes the pool quota limit */
1896
                r = bus_call_method(bus, bus_machine_mgr, "SetPoolLimit", &error, NULL, "t", limit);
×
1897

1898
        if (r < 0)
×
1899
                return log_error_errno(r, "Could not set limit: %s", bus_error_message(&error, r));
×
1900

1901
        return 0;
1902
}
1903

1904
static int clean_images(int argc, char *argv[], void *userdata) {
2✔
1905
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
4✔
1906
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2✔
1907
        uint64_t usage, total = 0;
2✔
1908
        sd_bus *bus = userdata;
2✔
1909
        const char *name;
2✔
1910
        unsigned c = 0;
2✔
1911
        int r;
2✔
1912

1913
        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
2✔
1914

1915
        r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "CleanPool");
2✔
1916
        if (r < 0)
2✔
1917
                return bus_log_create_error(r);
×
1918

1919
        r = sd_bus_message_append(m, "s", arg_all ? "all" : "hidden");
3✔
1920
        if (r < 0)
2✔
1921
                return bus_log_create_error(r);
×
1922

1923
        /* This is a slow operation, hence permit a longer time for completion. */
1924
        r = sd_bus_call(bus, m, USEC_INFINITY, &error, &reply);
2✔
1925
        if (r < 0)
2✔
1926
                return log_error_errno(r, "Could not clean pool: %s", bus_error_message(&error, r));
×
1927

1928
        r = sd_bus_message_enter_container(reply, 'a', "(st)");
2✔
1929
        if (r < 0)
2✔
1930
                return bus_log_parse_error(r);
×
1931

1932
        while ((r = sd_bus_message_read(reply, "(st)", &name, &usage)) > 0) {
17✔
1933
                if (usage == UINT64_MAX) {
15✔
1934
                        log_info("Removed image '%s'", name);
13✔
1935
                        total = UINT64_MAX;
1936
                } else {
1937
                        log_info("Removed image '%s'. Freed exclusive disk space: %s",
2✔
1938
                                 name, FORMAT_BYTES(usage));
1939
                        if (total != UINT64_MAX)
2✔
UNCOV
1940
                                total += usage;
×
1941
                }
1942
                c++;
15✔
1943
        }
1944

1945
        r = sd_bus_message_exit_container(reply);
2✔
1946
        if (r < 0)
2✔
1947
                return bus_log_parse_error(r);
×
1948

1949
        if (total == UINT64_MAX)
2✔
1950
                log_info("Removed %u images in total.", c);
2✔
1951
        else
1952
                log_info("Removed %u images in total. Total freed exclusive disk space: %s.",
×
1953
                         c, FORMAT_BYTES(total));
1954

1955
        return 0;
1956
}
1957

1958
static int chainload_importctl(int argc, char *argv[]) {
17✔
1959
        int r;
17✔
1960

1961
        if (!arg_quiet)
17✔
1962
                log_notice("The 'machinectl %1$s' command has been replaced by 'importctl -m %1$s'. Redirecting invocation.", argv[optind]);
17✔
1963

1964
        _cleanup_strv_free_ char **c =
×
1965
                strv_new("importctl", "--class=machine");
17✔
1966
        if (!c)
17✔
1967
                return log_oom();
×
1968

1969
        if (FLAGS_SET(arg_pager_flags, PAGER_DISABLE))
17✔
1970
                if (strv_extend(&c, "--no-pager") < 0)
×
1971
                        return log_oom();
×
1972
        if (!arg_legend)
17✔
1973
                if (strv_extend(&c, "--no-legend") < 0)
×
1974
                        return log_oom();
×
1975
        if (arg_read_only)
17✔
1976
                if (strv_extend(&c, "--read-only") < 0)
×
1977
                        return log_oom();
×
1978
        if (arg_force)
17✔
1979
                if (strv_extend(&c, "--force") < 0)
×
1980
                        return log_oom();
×
1981
        if (arg_quiet)
17✔
1982
                if (strv_extend(&c, "--quiet") < 0)
×
1983
                        return log_oom();
×
1984
        if (!arg_ask_password)
17✔
1985
                if (strv_extend(&c, "--no-ask-password") < 0)
×
1986
                        return log_oom();
×
1987
        if (strv_extend_many(&c, "--verify", import_verify_to_string(arg_verify)) < 0)
17✔
1988
                return log_oom();
×
1989
        if (arg_format)
17✔
1990
                if (strv_extend_many(&c, "--format", arg_format) < 0)
×
1991
                        return log_oom();
×
1992

1993
        if (strv_extend_strv(&c, argv + optind, /* filter_duplicates= */ false) < 0)
17✔
1994
                return log_oom();
×
1995

1996
        if (DEBUG_LOGGING) {
17✔
1997
                _cleanup_free_ char *joined = strv_join(c, " ");
×
1998
                log_debug("Chainloading: %s", joined);
×
1999
        }
2000

2001
        r = invoke_callout_binary(BINDIR "/importctl", c);
17✔
2002
        return log_error_errno(r, "Failed to invoke 'importctl': %m");
×
2003
}
2004

2005
static int help(int argc, char *argv[], void *userdata) {
1✔
2006
        _cleanup_free_ char *link = NULL;
1✔
2007
        int r;
1✔
2008

2009
        pager_open(arg_pager_flags);
1✔
2010

2011
        r = terminal_urlify_man("machinectl", "1", &link);
1✔
2012
        if (r < 0)
1✔
2013
                return log_oom();
×
2014

2015
        printf("%1$s [OPTIONS...] COMMAND ...\n\n"
2✔
2016
               "%5$sSend control commands to or query the virtual machine and container%6$s\n"
2017
               "%5$sregistration manager.%6$s\n"
2018
               "\n%3$sMachine Commands:%4$s\n"
2019
               "  list                        List running VMs and containers\n"
2020
               "  status NAME...              Show VM/container details\n"
2021
               "  show [NAME...]              Show properties of one or more VMs/containers\n"
2022
               "  start NAME...               Start container as a service\n"
2023
               "  login [NAME]                Get a login prompt in a container or on the\n"
2024
               "                              local host\n"
2025
               "  shell [[USER@]NAME [COMMAND...]]\n"
2026
               "                              Invoke a shell (or other command) in a container\n"
2027
               "                              or on the local host\n"
2028
               "  enable NAME...              Enable automatic container start at boot\n"
2029
               "  disable NAME...             Disable automatic container start at boot\n"
2030
               "  poweroff NAME...            Power off one or more containers\n"
2031
               "  reboot NAME...              Reboot one or more containers\n"
2032
               "  terminate NAME...           Terminate one or more VMs/containers\n"
2033
               "  kill NAME...                Send signal to processes of a VM/container\n"
2034
               "  copy-to NAME PATH [PATH]    Copy files from the host to a container\n"
2035
               "  copy-from NAME PATH [PATH]  Copy files from a container to the host\n"
2036
               "  bind NAME PATH [PATH]       Bind mount a path from the host into a container\n"
2037
               "\n%3$sImage Commands:%4$s\n"
2038
               "  list-images                 Show available container and VM images\n"
2039
               "  image-status [NAME...]      Show image details\n"
2040
               "  show-image [NAME...]        Show properties of image\n"
2041
               "  edit NAME|FILE...           Edit settings of one or more VMs/containers\n"
2042
               "  cat NAME|FILE...            Show settings of one or more VMs/containers\n"
2043
               "  clone NAME NAME             Clone an image\n"
2044
               "  rename NAME NAME            Rename an image\n"
2045
               "  read-only NAME [BOOL]       Mark or unmark image read-only\n"
2046
               "  remove NAME...              Remove an image\n"
2047
               "  set-limit [NAME] BYTES      Set image or pool size limit (disk quota)\n"
2048
               "  clean                       Remove hidden (or all) images\n"
2049
               "\n%3$sOptions:%4$s\n"
2050
               "  -h --help                   Show this help\n"
2051
               "     --version                Show package version\n"
2052
               "     --no-pager               Do not pipe output into a pager\n"
2053
               "     --no-legend              Do not show the headers and footers\n"
2054
               "     --no-ask-password        Do not ask for system passwords\n"
2055
               "  -H --host=[USER@]HOST       Operate on remote host\n"
2056
               "  -M --machine=CONTAINER      Operate on local container\n"
2057
               "  -p --property=NAME          Show only properties by this name\n"
2058
               "     --value                  When showing properties, only print the value\n"
2059
               "  -P NAME                     Equivalent to --value --property=NAME\n"
2060
               "  -q --quiet                  Suppress output\n"
2061
               "  -a --all                    Show all properties, including empty ones\n"
2062
               "  -l --full                   Do not ellipsize output\n"
2063
               "     --kill-whom=WHOM         Whom to send signal to\n"
2064
               "  -s --signal=SIGNAL          Which signal to send\n"
2065
               "     --uid=USER               Specify user ID to invoke shell as\n"
2066
               "  -E --setenv=VAR[=VALUE]     Add an environment variable for shell\n"
2067
               "     --read-only              Create read-only bind mount or clone\n"
2068
               "     --mkdir                  Create directory before bind mounting, if missing\n"
2069
               "  -n --lines=INTEGER          Number of journal entries to show\n"
2070
               "     --max-addresses=INTEGER  Number of internet addresses to show at most\n"
2071
               "  -o --output=STRING          Change journal output mode (short, short-precise,\n"
2072
               "                               short-iso, short-iso-precise, short-full,\n"
2073
               "                               short-monotonic, short-unix, short-delta,\n"
2074
               "                               json, json-pretty, json-sse, json-seq, cat,\n"
2075
               "                               verbose, export, with-unit)\n"
2076
               "     --force                  Replace target file when copying, if necessary\n"
2077
               "     --now                    Start or power off container after enabling or\n"
2078
               "                              disabling it\n"
2079
               "     --runner=RUNNER          Select between nspawn and vmspawn as the runner\n"
2080
               "  -V                          Short for --runner=vmspawn\n"
2081
               "\nSee the %2$s for details.\n",
2082
               program_invocation_short_name,
2083
               link,
2084
               ansi_underline(),
2085
               ansi_normal(),
2086
               ansi_highlight(),
2087
               ansi_normal());
2088

2089
        return 0;
2090
}
2091

2092
static int parse_argv(int argc, char *argv[]) {
199✔
2093

2094
        enum {
199✔
2095
                ARG_VERSION = 0x100,
2096
                ARG_NO_PAGER,
2097
                ARG_NO_LEGEND,
2098
                ARG_VALUE,
2099
                ARG_KILL_WHOM,
2100
                ARG_READ_ONLY,
2101
                ARG_MKDIR,
2102
                ARG_NO_ASK_PASSWORD,
2103
                ARG_VERIFY,
2104
                ARG_RUNNER,
2105
                ARG_NOW,
2106
                ARG_FORCE,
2107
                ARG_FORMAT,
2108
                ARG_UID,
2109
                ARG_MAX_ADDRESSES,
2110
        };
2111

2112
        static const struct option options[] = {
199✔
2113
                { "help",            no_argument,       NULL, 'h'                 },
2114
                { "version",         no_argument,       NULL, ARG_VERSION         },
2115
                { "property",        required_argument, NULL, 'p'                 },
2116
                { "value",           no_argument,       NULL, ARG_VALUE           },
2117
                { "all",             no_argument,       NULL, 'a'                 },
2118
                { "full",            no_argument,       NULL, 'l'                 },
2119
                { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
2120
                { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
2121
                { "kill-whom",       required_argument, NULL, ARG_KILL_WHOM       },
2122
                { "signal",          required_argument, NULL, 's'                 },
2123
                { "host",            required_argument, NULL, 'H'                 },
2124
                { "machine",         required_argument, NULL, 'M'                 },
2125
                { "read-only",       no_argument,       NULL, ARG_READ_ONLY       },
2126
                { "mkdir",           no_argument,       NULL, ARG_MKDIR           },
2127
                { "quiet",           no_argument,       NULL, 'q'                 },
2128
                { "lines",           required_argument, NULL, 'n'                 },
2129
                { "output",          required_argument, NULL, 'o'                 },
2130
                { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
2131
                { "verify",          required_argument, NULL, ARG_VERIFY          },
2132
                { "runner",          required_argument, NULL, ARG_RUNNER          },
2133
                { "now",             no_argument,       NULL, ARG_NOW             },
2134
                { "force",           no_argument,       NULL, ARG_FORCE           },
2135
                { "format",          required_argument, NULL, ARG_FORMAT          },
2136
                { "uid",             required_argument, NULL, ARG_UID             },
2137
                { "setenv",          required_argument, NULL, 'E'                 },
2138
                { "max-addresses",   required_argument, NULL, ARG_MAX_ADDRESSES   },
2139
                {}
2140
        };
2141

2142
        bool reorder = false;
199✔
2143
        int c, r, shell = -1;
199✔
2144

2145
        assert(argc >= 0);
199✔
2146
        assert(argv);
199✔
2147

2148
        /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
2149
         * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
2150
        optind = 0;
199✔
2151

2152
        for (;;) {
462✔
2153
                static const char option_string[] = "-hp:P:als:H:M:qn:o:E:V";
462✔
2154

2155
                c = getopt_long(argc, argv, option_string + reorder, options, NULL);
924✔
2156
                if (c < 0)
462✔
2157
                        break;
2158

2159
                switch (c) {
287✔
2160

2161
                case 1: /* getopt_long() returns 1 if "-" was the first character of the option string, and a
197✔
2162
                         * non-option argument was discovered. */
2163

2164
                        assert(!reorder);
197✔
2165

2166
                        /* We generally are fine with the fact that getopt_long() reorders the command line, and looks
2167
                         * for switches after the main verb. However, for "shell" we really don't want that, since we
2168
                         * want that switches specified after the machine name are passed to the program to execute,
2169
                         * and not processed by us. To make this possible, we'll first invoke getopt_long() with
2170
                         * reordering disabled (i.e. with the "-" prefix in the option string), looking for the first
2171
                         * non-option parameter. If it's the verb "shell" we remember its position and continue
2172
                         * processing options. In this case, as soon as we hit the next non-option argument we found
2173
                         * the machine name, and stop further processing. If the first non-option argument is any other
2174
                         * verb than "shell" we switch to normal reordering mode and continue processing arguments
2175
                         * normally. */
2176

2177
                        if (shell >= 0) {
197✔
2178
                                /* If we already found the "shell" verb on the command line, and now found the next
2179
                                 * non-option argument, then this is the machine name and we should stop processing
2180
                                 * further arguments.  */
2181
                                optind--; /* don't process this argument, go one step back */
2✔
2182
                                goto done;
2✔
2183
                        }
2184
                        if (streq(optarg, "shell"))
195✔
2185
                                /* Remember the position of the "shell" verb, and continue processing normally. */
2186
                                shell = optind - 1;
2✔
2187
                        else {
2188
                                int saved_optind;
193✔
2189

2190
                                /* OK, this is some other verb. In this case, turn on reordering again, and continue
2191
                                 * processing normally. */
2192
                                reorder = true;
193✔
2193

2194
                                /* We changed the option string. getopt_long() only looks at it again if we invoke it
2195
                                 * at least once with a reset option index. Hence, let's reset the option index here,
2196
                                 * then invoke getopt_long() again (ignoring what it has to say, after all we most
2197
                                 * likely already processed it), and the bump the option index so that we read the
2198
                                 * intended argument again. */
2199
                                saved_optind = optind;
193✔
2200
                                optind = 0;
193✔
2201
                                (void) getopt_long(argc, argv, option_string + reorder, options, NULL);
193✔
2202
                                optind = saved_optind - 1; /* go one step back, process this argument again */
193✔
2203
                        }
2204

2205
                        break;
2206

2207
                case 'h':
1✔
2208
                        return help(0, NULL, NULL);
1✔
2209

2210
                case ARG_VERSION:
1✔
2211
                        return version();
1✔
2212

2213
                case 'p':
12✔
2214
                case 'P':
2215
                        r = strv_extend(&arg_property, optarg);
12✔
2216
                        if (r < 0)
12✔
2217
                                return log_oom();
×
2218

2219
                        /* If the user asked for a particular property, show it to them, even if empty. */
2220
                        SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY, true);
12✔
2221

2222
                        if (c == 'p')
12✔
2223
                                break;
2224
                        _fallthrough_;
12✔
2225

2226
                case ARG_VALUE:
2227
                        SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_ONLY_VALUE, true);
12✔
2228
                        break;
12✔
2229

2230
                case 'a':
6✔
2231
                        SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY, true);
6✔
2232
                        arg_all = true;
6✔
2233
                        break;
6✔
2234

2235
                case 'l':
1✔
2236
                        arg_full = true;
1✔
2237
                        break;
1✔
2238

2239
                case 'n':
5✔
2240
                        if (safe_atou(optarg, &arg_lines) < 0)
5✔
2241
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3✔
2242
                                                       "Failed to parse lines '%s'", optarg);
2243
                        break;
2244

2245
                case 'o':
21✔
2246
                        if (streq(optarg, "help")) {
21✔
2247
                                DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX);
34✔
2248
                                return 0;
2✔
2249
                        }
2250

2251
                        r = output_mode_from_string(optarg);
19✔
2252
                        if (r < 0)
19✔
2253
                                return log_error_errno(r, "Unknown output '%s'.", optarg);
3✔
2254
                        arg_output = r;
16✔
2255

2256
                        if (OUTPUT_MODE_IS_JSON(arg_output))
478✔
2257
                                arg_legend = false;
4✔
2258
                        break;
2259

2260
                case ARG_NO_PAGER:
1✔
2261
                        arg_pager_flags |= PAGER_DISABLE;
1✔
2262
                        break;
1✔
2263

2264
                case ARG_NO_LEGEND:
2✔
2265
                        arg_legend = false;
2✔
2266
                        break;
2✔
2267

2268
                case ARG_KILL_WHOM:
2✔
2269
                        arg_kill_whom = optarg;
2✔
2270
                        break;
2✔
2271

2272
                case 's':
3✔
2273
                        r = parse_signal_argument(optarg, &arg_signal);
3✔
2274
                        if (r <= 0)
3✔
2275
                                return r;
2276
                        break;
2277

2278
                case ARG_NO_ASK_PASSWORD:
1✔
2279
                        arg_ask_password = false;
1✔
2280
                        break;
1✔
2281

2282
                case 'H':
×
2283
                        arg_transport = BUS_TRANSPORT_REMOTE;
×
2284
                        arg_host = optarg;
×
2285
                        break;
×
2286

2287
                case 'M':
6✔
2288
                        arg_transport = BUS_TRANSPORT_MACHINE;
6✔
2289
                        arg_host = optarg;
6✔
2290
                        break;
6✔
2291

2292
                case ARG_READ_ONLY:
×
2293
                        arg_read_only = true;
×
2294
                        break;
×
2295

2296
                case ARG_MKDIR:
1✔
2297
                        arg_mkdir = true;
1✔
2298
                        break;
1✔
2299

2300
                case 'q':
1✔
2301
                        arg_quiet = true;
1✔
2302
                        break;
1✔
2303

2304
                case ARG_VERIFY:
3✔
2305
                        if (streq(optarg, "help")) {
3✔
2306
                                DUMP_STRING_TABLE(import_verify, ImportVerify, _IMPORT_VERIFY_MAX);
×
2307
                                return 0;
×
2308
                        }
2309

2310
                        r = import_verify_from_string(optarg);
3✔
2311
                        if (r < 0)
3✔
2312
                                return log_error_errno(r, "Failed to parse --verify= setting: %s", optarg);
3✔
2313
                        arg_verify = r;
×
2314
                        break;
×
2315

2316
                case 'V':
×
2317
                        arg_runner = RUNNER_VMSPAWN;
×
2318
                        break;
×
2319

2320
                case ARG_RUNNER:
×
2321
                        r = machine_runner_from_string(optarg);
×
2322
                        if (r < 0)
×
2323
                                return log_error_errno(r, "Failed to parse --runner= setting: %s", optarg);
×
2324

2325
                        arg_runner = r;
×
2326
                        break;
×
2327

2328
                case ARG_NOW:
×
2329
                        arg_now = true;
×
2330
                        break;
×
2331

2332
                case ARG_FORCE:
×
2333
                        arg_force = true;
×
2334
                        break;
×
2335

2336
                case ARG_FORMAT:
3✔
2337
                        if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2", "zstd"))
3✔
2338
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3✔
2339
                                                       "Unknown format: %s", optarg);
2340

2341
                        arg_format = optarg;
×
2342
                        break;
×
2343

2344
                case ARG_UID:
×
2345
                        arg_uid = optarg;
×
2346
                        break;
×
2347

2348
                case 'E':
4✔
2349
                        r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg);
4✔
2350
                        if (r < 0)
4✔
2351
                                return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
3✔
2352
                        break;
2353

2354
                case ARG_MAX_ADDRESSES:
4✔
2355
                        if (streq(optarg, "all"))
4✔
2356
                                arg_max_addresses = UINT_MAX;
×
2357
                        else if (safe_atou(optarg, &arg_max_addresses) < 0)
4✔
2358
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3✔
2359
                                                       "Invalid number of addresses: %s", optarg);
2360
                        break;
2361

2362
                case '?':
2363
                        return -EINVAL;
2364

2365
                default:
×
2366
                        assert_not_reached();
×
2367
                }
2368
        }
2369

2370
done:
175✔
2371
        if (shell >= 0) {
177✔
2372
                char *t;
2✔
2373

2374
                /* We found the "shell" verb while processing the argument list. Since we turned off reordering of the
2375
                 * argument list initially let's readjust it now, and move the "shell" verb to the back. */
2376

2377
                optind -= 1; /* place the option index where the "shell" verb will be placed */
2✔
2378

2379
                t = argv[shell];
2✔
2380
                for (int i = shell; i < optind; i++)
3✔
2381
                        argv[i] = argv[i+1];
1✔
2382
                argv[optind] = t;
2✔
2383
        }
2384

2385
        return 1;
2386
}
2387

2388
static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
157✔
2389

2390
        static const Verb verbs[] = {
157✔
2391
                { "help",            VERB_ANY, VERB_ANY, 0,            help              },
2392
                { "list",            VERB_ANY, 1,        VERB_DEFAULT, list_machines     },
2393
                { "list-images",     VERB_ANY, 1,        0,            list_images       },
2394
                { "status",          2,        VERB_ANY, 0,            show_machine      },
2395
                { "image-status",    VERB_ANY, VERB_ANY, 0,            show_image        },
2396
                { "show",            VERB_ANY, VERB_ANY, 0,            show_machine      },
2397
                { "show-image",      VERB_ANY, VERB_ANY, 0,            show_image        },
2398
                { "terminate",       2,        VERB_ANY, 0,            terminate_machine },
2399
                { "reboot",          2,        VERB_ANY, 0,            reboot_machine    },
2400
                { "restart",         2,        VERB_ANY, 0,            reboot_machine    }, /* Convenience alias */
2401
                { "poweroff",        2,        VERB_ANY, 0,            poweroff_machine  },
2402
                { "stop",            2,        VERB_ANY, 0,            poweroff_machine  }, /* Convenience alias */
2403
                { "kill",            2,        VERB_ANY, 0,            kill_machine      },
2404
                { "login",           VERB_ANY, 2,        0,            login_machine     },
2405
                { "shell",           VERB_ANY, VERB_ANY, 0,            shell_machine     },
2406
                { "bind",            3,        4,        0,            bind_mount        },
2407
                { "edit",            2,        VERB_ANY, 0,            edit_settings     },
2408
                { "cat",             2,        VERB_ANY, 0,            cat_settings      },
2409
                { "copy-to",         3,        4,        0,            copy_files        },
2410
                { "copy-from",       3,        4,        0,            copy_files        },
2411
                { "remove",          2,        VERB_ANY, 0,            remove_image      },
2412
                { "rename",          3,        3,        0,            rename_image      },
2413
                { "clone",           3,        3,        0,            clone_image       },
2414
                { "read-only",       2,        3,        0,            read_only_image   },
2415
                { "start",           2,        VERB_ANY, 0,            start_machine     },
2416
                { "enable",          2,        VERB_ANY, 0,            enable_machine    },
2417
                { "disable",         2,        VERB_ANY, 0,            enable_machine    },
2418
                { "set-limit",       2,        3,        0,            set_limit         },
2419
                { "clean",           VERB_ANY, 1,        0,            clean_images      },
2420
                {}
2421
        };
2422

2423
        return dispatch_verb(argc, argv, verbs, bus);
157✔
2424
}
2425

2426
static int run(int argc, char *argv[]) {
199✔
2427
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
182✔
2428
        int r;
199✔
2429

2430
        setlocale(LC_ALL, "");
199✔
2431
        log_setup();
199✔
2432

2433
        r = parse_argv(argc, argv);
199✔
2434
        if (r <= 0)
199✔
2435
                return r;
2436

2437
        journal_browse_prepare();
177✔
2438

2439
        if (STRPTR_IN_SET(argv[optind],
177✔
2440
                          "import-tar", "import-raw", "import-fs",
2441
                          "export-tar", "export-raw",
2442
                          "pull-tar", "pull-raw",
2443
                          "list-transfers", "cancel-transfer"))
2444
                return chainload_importctl(argc, argv);
17✔
2445

2446
        r = bus_connect_transport(arg_transport, arg_host, RUNTIME_SCOPE_SYSTEM, &bus);
160✔
2447
        if (r < 0)
160✔
2448
                return bus_log_connect_error(r, arg_transport, RUNTIME_SCOPE_SYSTEM);
182✔
2449

2450
        (void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
157✔
2451

2452
        return machinectl_main(argc, argv, bus);
157✔
2453
}
2454

2455
DEFINE_MAIN_FUNCTION(run);
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