]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/machine/machinectl.c
io.systemd.Unit.List fix context/runtime split (#38172)
[thirdparty/systemd.git] / src / machine / machinectl.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <getopt.h>
4#include <locale.h>
5#include <net/if.h>
6#include <sys/socket.h>
7#include <unistd.h>
8
9#include "sd-bus.h"
10#include "sd-event.h"
11#include "sd-journal.h"
12
13#include "alloc-util.h"
14#include "ask-password-agent.h"
15#include "build.h"
16#include "build-path.h"
17#include "bus-common-errors.h"
18#include "bus-error.h"
19#include "bus-locator.h"
20#include "bus-map-properties.h"
21#include "bus-message-util.h"
22#include "bus-print-properties.h"
23#include "bus-unit-procs.h"
24#include "bus-unit-util.h"
25#include "bus-util.h"
26#include "bus-wait-for-jobs.h"
27#include "cgroup-show.h"
28#include "cgroup-util.h"
29#include "edit-util.h"
30#include "env-util.h"
31#include "format-ifname.h"
32#include "format-table.h"
33#include "format-util.h"
34#include "hostname-util.h"
35#include "import-util.h"
36#include "in-addr-util.h"
37#include "label-util.h"
38#include "log.h"
39#include "logs-show.h"
40#include "machine-dbus.h"
41#include "main-func.h"
42#include "nulstr-util.h"
43#include "osc-context.h"
44#include "output-mode.h"
45#include "pager.h"
46#include "parse-argument.h"
47#include "parse-util.h"
48#include "path-util.h"
49#include "pidref.h"
50#include "polkit-agent.h"
51#include "pretty-print.h"
52#include "process-util.h"
53#include "ptyfwd.h"
54#include "runtime-scope.h"
55#include "stdio-util.h"
56#include "string-table.h"
57#include "string-util.h"
58#include "strv.h"
59#include "terminal-util.h"
60#include "time-util.h"
61#include "unit-name.h"
62#include "verbs.h"
63
64typedef enum MachineRunner {
65 RUNNER_NSPAWN,
66 RUNNER_VMSPAWN,
67 _RUNNER_MAX,
68 _RUNNER_INVALID = -EINVAL,
69} MachineRunner;
70
71static const char* const machine_runner_table[_RUNNER_MAX] = {
72 [RUNNER_NSPAWN] = "nspawn",
73 [RUNNER_VMSPAWN] = "vmspawn",
74};
75
76DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(machine_runner, MachineRunner);
77
78static const char* const machine_runner_unit_prefix_table[_RUNNER_MAX] = {
79 [RUNNER_NSPAWN] = "systemd-nspawn",
80 [RUNNER_VMSPAWN] = "systemd-vmspawn",
81};
82
83static char **arg_property = NULL;
84static bool arg_all = false;
85static BusPrintPropertyFlags arg_print_flags = 0;
86static bool arg_full = false;
87static PagerFlags arg_pager_flags = 0;
88static bool arg_legend = true;
89static const char *arg_kill_whom = NULL;
90static int arg_signal = SIGTERM;
91static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
92static const char *arg_host = NULL;
93static bool arg_read_only = false;
94static bool arg_mkdir = false;
95static bool arg_quiet = false;
96static bool arg_ask_password = true;
97static unsigned arg_lines = 10;
98static OutputMode arg_output = OUTPUT_SHORT;
99static bool arg_now = false;
100static bool arg_force = false;
101static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
102static MachineRunner arg_runner = RUNNER_NSPAWN;
103static const char* arg_format = NULL;
104static const char *arg_uid = NULL;
105static char **arg_setenv = NULL;
106static unsigned arg_max_addresses = 1;
107
108STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
109STATIC_DESTRUCTOR_REGISTER(arg_setenv, strv_freep);
110
111static OutputFlags get_output_flags(void) {
112 return
113 FLAGS_SET(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) * OUTPUT_SHOW_ALL |
114 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
115 colors_enabled() * OUTPUT_COLOR |
116 !arg_quiet * OUTPUT_WARN_CUTOFF;
117}
118
119static int call_get_os_release(sd_bus *bus, const char *method, const char *name, const char *query, ...) {
120 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
121 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
122 va_list ap;
123 int r;
124
125 assert(bus);
126 assert(method);
127 assert(name);
128 assert(query);
129
130 r = bus_call_method(bus, bus_machine_mgr, method, &error, &reply, "s", name);
131 if (r < 0)
132 return log_debug_errno(r, "Failed to call '%s()': %s", method, bus_error_message(&error, r));
133
134 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
135 if (r < 0)
136 return bus_log_parse_error(r);
137
138 const char **res;
139 size_t n_fields = 0;
140
141 NULSTR_FOREACH(i, query)
142 n_fields++;
143
144 res = newa0(const char*, n_fields);
145
146 const char *k, *v;
147 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
148 size_t c = 0;
149 NULSTR_FOREACH(i, query) {
150 if (streq(i, k)) {
151 res[c] = v;
152 break;
153 }
154 c++;
155 }
156 }
157 if (r < 0)
158 return bus_log_parse_error(r);
159
160 r = sd_bus_message_exit_container(reply);
161 if (r < 0)
162 return bus_log_parse_error(r);
163
164 r = 0;
165
166 va_start(ap, query);
167 FOREACH_ARRAY(i, res, n_fields) {
168 r = strdup_to(va_arg(ap, char**), *i);
169 if (r < 0)
170 break;
171 }
172 va_end(ap);
173
174 return r;
175}
176
177static int call_get_addresses(
178 sd_bus *bus,
179 const char *name,
180 int ifi,
181 const char *prefix,
182 const char *prefix2,
183 char **ret) {
184
185 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
186 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
187 _cleanup_free_ char *addresses = NULL;
188 unsigned n = 0;
189 int r;
190
191 assert(bus);
192 assert(name);
193 assert(prefix);
194 assert(prefix2);
195
196 r = bus_call_method(bus, bus_machine_mgr, "GetMachineAddresses", NULL, &reply, "s", name);
197 if (r < 0)
198 return log_debug_errno(r, "Could not get addresses: %s", bus_error_message(&error, r));
199
200 addresses = strdup(prefix);
201 if (!addresses)
202 return log_oom();
203
204 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
205 if (r < 0)
206 return bus_log_parse_error(r);
207
208 prefix = "";
209 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
210 int family;
211 const void *a;
212 size_t sz;
213 char buf_ifi[1 + DECIMAL_STR_MAX(int)] = "";
214
215 r = sd_bus_message_read(reply, "i", &family);
216 if (r < 0)
217 return bus_log_parse_error(r);
218
219 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
220 if (r < 0)
221 return bus_log_parse_error(r);
222
223 if (family == AF_INET6 && ifi > 0)
224 xsprintf(buf_ifi, "%%%i", ifi);
225
226 if (!strextend(&addresses, prefix, IN_ADDR_TO_STRING(family, a), buf_ifi))
227 return log_oom();
228
229 r = sd_bus_message_exit_container(reply);
230 if (r < 0)
231 return bus_log_parse_error(r);
232
233 prefix = prefix2;
234
235 n++;
236 }
237 if (r < 0)
238 return bus_log_parse_error(r);
239
240 r = sd_bus_message_exit_container(reply);
241 if (r < 0)
242 return bus_log_parse_error(r);
243
244 *ret = TAKE_PTR(addresses);
245 return (int) n;
246}
247
248static int show_table(Table *table, const char *word) {
249 int r;
250
251 assert(table);
252 assert(word);
253
254 if (!table_isempty(table) || OUTPUT_MODE_IS_JSON(arg_output)) {
255 r = table_set_sort(table, (size_t) 0);
256 if (r < 0)
257 return table_log_sort_error(r);
258
259 table_set_header(table, arg_legend);
260
261 if (OUTPUT_MODE_IS_JSON(arg_output))
262 r = table_print_json(table, NULL, output_mode_to_json_format_flags(arg_output) | SD_JSON_FORMAT_COLOR_AUTO);
263 else
264 r = table_print(table, NULL);
265 if (r < 0)
266 return table_log_print_error(r);
267 }
268
269 if (arg_legend) {
270 if (table_isempty(table))
271 printf("No %s.\n", word);
272 else
273 printf("\n%zu %s listed.\n", table_get_rows(table) - 1, word);
274 }
275
276 return 0;
277}
278
279static int list_machines(int argc, char *argv[], void *userdata) {
280 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
281 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
282 _cleanup_(table_unrefp) Table *table = NULL;
283 sd_bus *bus = ASSERT_PTR(userdata);
284 int r;
285
286 pager_open(arg_pager_flags);
287
288 r = bus_call_method(bus, bus_machine_mgr, "ListMachines", &error, &reply, NULL);
289 if (r < 0)
290 return log_error_errno(r, "Could not get machines: %s", bus_error_message(&error, r));
291
292 table = table_new("machine", "class", "service", "os", "version",
293 arg_max_addresses > 0 ? "addresses" : NULL);
294 if (!table)
295 return log_oom();
296
297 table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
298 if (!arg_full && arg_max_addresses > 0 && arg_max_addresses < UINT_MAX)
299 table_set_cell_height_max(table, arg_max_addresses);
300
301 if (arg_full)
302 table_set_width(table, 0);
303
304 r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
305 if (r < 0)
306 return bus_log_parse_error(r);
307
308 for (;;) {
309 _cleanup_free_ char *os = NULL, *version_id = NULL, *addresses = NULL;
310 const char *name, *class, *service;
311
312 r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, NULL);
313 if (r < 0)
314 return bus_log_parse_error(r);
315 if (r == 0)
316 break;
317
318 if (name[0] == '.' && !arg_all)
319 continue;
320
321 (void) call_get_os_release(
322 bus,
323 "GetMachineOSRelease",
324 name,
325 "ID\0"
326 "VERSION_ID\0",
327 &os,
328 &version_id);
329
330 r = table_add_many(table,
331 TABLE_STRING, empty_to_null(name),
332 TABLE_STRING, empty_to_null(class),
333 TABLE_STRING, empty_to_null(service),
334 TABLE_STRING, empty_to_null(os),
335 TABLE_STRING, empty_to_null(version_id));
336 if (r < 0)
337 return table_log_add_error(r);
338
339 if (arg_max_addresses > 0) {
340 (void) call_get_addresses(bus, name, 0, "", "\n", &addresses);
341
342 r = table_add_many(table,
343 TABLE_STRING, empty_to_null(addresses));
344 if (r < 0)
345 return table_log_add_error(r);
346 }
347 }
348
349 r = sd_bus_message_exit_container(reply);
350 if (r < 0)
351 return bus_log_parse_error(r);
352
353 return show_table(table, "machines");
354}
355
356static int list_images(int argc, char *argv[], void *userdata) {
357
358 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
359 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
360 _cleanup_(table_unrefp) Table *table = NULL;
361 sd_bus *bus = ASSERT_PTR(userdata);
362 int r;
363
364 pager_open(arg_pager_flags);
365
366 r = bus_call_method(bus, bus_machine_mgr, "ListImages", &error, &reply, NULL);
367 if (r < 0)
368 return log_error_errno(r, "Could not get images: %s", bus_error_message(&error, r));
369
370 table = table_new("name", "type", "ro", "usage", "created", "modified");
371 if (!table)
372 return log_oom();
373
374 if (arg_full)
375 table_set_width(table, 0);
376
377 (void) table_set_align_percent(table, TABLE_HEADER_CELL(3), 100);
378
379 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
380 if (r < 0)
381 return bus_log_parse_error(r);
382
383 for (;;) {
384 uint64_t crtime, mtime, size;
385 const char *name, *type;
386 int ro_int;
387
388 r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &ro_int, &crtime, &mtime, &size, NULL);
389 if (r < 0)
390 return bus_log_parse_error(r);
391 if (r == 0)
392 break;
393
394 if (name[0] == '.' && !arg_all)
395 continue;
396
397 r = table_add_many(table,
398 TABLE_STRING, name,
399 TABLE_STRING, type,
400 TABLE_BOOLEAN, ro_int,
401 TABLE_SET_COLOR, ro_int ? ansi_highlight_red() : NULL,
402 TABLE_SIZE, size,
403 TABLE_TIMESTAMP, crtime,
404 TABLE_TIMESTAMP, mtime);
405 if (r < 0)
406 return table_log_add_error(r);
407 }
408
409 r = sd_bus_message_exit_container(reply);
410 if (r < 0)
411 return bus_log_parse_error(r);
412
413 return show_table(table, "images");
414}
415
416static int show_unit_cgroup(
417 sd_bus *bus,
418 const char *unit,
419 const char *subgroup,
420 pid_t leader) {
421
422 _cleanup_free_ char *cgroup = NULL;
423 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
424 OutputFlags extra_flags = 0;
425 int r;
426
427 assert(bus);
428 assert(unit);
429
430 r = show_cgroup_get_unit_path_and_warn(bus, unit, &cgroup);
431 if (r < 0)
432 return r;
433
434 if (isempty(cgroup))
435 return 0;
436
437 if (!empty_or_root(subgroup)) {
438 if (!path_extend(&cgroup, subgroup))
439 return log_oom();
440
441 /* If we have a subcgroup, then hide all processes outside of it */
442 extra_flags |= OUTPUT_HIDE_EXTRA;
443 }
444
445 unsigned c = MAX(LESS_BY(columns(), 18U), 10U);
446 r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags() | extra_flags, &error);
447 if (r == -EBADR) {
448
449 if (arg_transport == BUS_TRANSPORT_REMOTE)
450 return 0;
451
452 /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
453
454 if (cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
455 return 0;
456
457 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags());
458 } else if (r < 0)
459 return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r));
460
461 return 0;
462}
463
464static int print_os_release(sd_bus *bus, const char *method, const char *name, const char *prefix) {
465 _cleanup_free_ char *pretty = NULL;
466 int r;
467
468 assert(bus);
469 assert(name);
470 assert(prefix);
471
472 r = call_get_os_release(bus, method, name, "PRETTY_NAME\0", &pretty, NULL);
473 if (r < 0)
474 return r;
475
476 if (pretty)
477 printf("%s%s\n", prefix, pretty);
478
479 return 0;
480}
481
482static int print_uid_shift(sd_bus *bus, const char *name) {
483 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
484 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
485 uint32_t shift;
486 int r;
487
488 assert(bus);
489 assert(name);
490
491 r = bus_call_method(bus, bus_machine_mgr, "GetMachineUIDShift", &error, &reply, "s", name);
492 if (r < 0)
493 return log_debug_errno(r, "Failed to query UID/GID shift: %s", bus_error_message(&error, r));
494
495 r = sd_bus_message_read(reply, "u", &shift);
496 if (r < 0)
497 return r;
498
499 if (shift == 0) /* Don't show trivial mappings */
500 return 0;
501
502 printf("\tID Shift: %" PRIu32 "\n", shift);
503 return 0;
504}
505
506typedef struct MachineStatusInfo {
507 const char *name;
508 sd_id128_t id;
509 const char *class;
510 const char *service;
511 const char *unit;
512 const char *subgroup;
513 const char *root_directory;
514 pid_t leader;
515 uint64_t leader_pidfdid;
516 pid_t supervisor;
517 uint64_t supervisor_pidfdid;
518 struct dual_timestamp timestamp;
519 int *netif;
520 size_t n_netif;
521 uid_t uid;
522} MachineStatusInfo;
523
524static void machine_status_info_done(MachineStatusInfo *info) {
525 if (!info)
526 return;
527
528 free(info->netif);
529 zero(*info);
530}
531
532static void print_process_info(const char *field, pid_t pid, uint64_t pidfdid) {
533 int r;
534
535 assert(field);
536
537 if (pid <= 0)
538 return;
539
540 printf("%s: " PID_FMT, field, pid);
541
542 _cleanup_(pidref_done) PidRef pr = PIDREF_NULL;
543 r = pidref_set_pid_and_pidfd_id(&pr, pid, pidfdid);
544 if (r < 0)
545 log_debug_errno(r, "Failed to acquire reference to process, ignoring: %m");
546 else {
547 _cleanup_free_ char *t = NULL;
548 r = pidref_get_comm(&pr, &t);
549 if (r < 0)
550 log_debug_errno(r, "Failed to acquire name of process, ignoring: %m");
551 else
552 printf(" (%s)", t);
553 }
554
555 putchar('\n');
556}
557
558static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
559 _cleanup_free_ char *addresses = NULL, *s1 = NULL, *s2 = NULL;
560 int ifi = -1;
561
562 assert(bus);
563 assert(i);
564
565 fputs(strna(i->name), stdout);
566
567 if (!sd_id128_is_null(i->id))
568 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
569 else
570 putchar('\n');
571
572 s1 = strdup(strempty(FORMAT_TIMESTAMP_RELATIVE(i->timestamp.realtime)));
573 s2 = strdup(strempty(FORMAT_TIMESTAMP(i->timestamp.realtime)));
574
575 if (!isempty(s1))
576 printf("\t Since: %s; %s\n", strna(s2), s1);
577 else if (!isempty(s2))
578 printf("\t Since: %s\n", s2);
579
580 print_process_info("\t Leader", i->leader, i->leader_pidfdid);
581 print_process_info("\t Superv.", i->supervisor, i->supervisor_pidfdid);
582
583 if (i->service) {
584 printf("\t Service: %s", i->service);
585
586 if (i->class)
587 printf("; class %s", i->class);
588
589 putchar('\n');
590 } else if (i->class)
591 printf("\t Class: %s\n", i->class);
592
593 if (i->uid != 0)
594 printf("\t UID: " UID_FMT "\n", i->uid);
595
596 if (i->root_directory)
597 printf("\t Root: %s\n", i->root_directory);
598
599 if (i->n_netif > 0) {
600 fputs("\t Iface:", stdout);
601
602 for (size_t c = 0; c < i->n_netif; c++) {
603 char name[IF_NAMESIZE];
604
605 if (format_ifname(i->netif[c], name) >= 0) {
606 fputc(' ', stdout);
607 fputs(name, stdout);
608
609 if (ifi < 0)
610 ifi = i->netif[c];
611 else
612 ifi = 0;
613 } else
614 printf(" %i", i->netif[c]);
615 }
616
617 fputc('\n', stdout);
618 }
619
620 if (call_get_addresses(bus, i->name, ifi,
621 "\t Address: ", "\n\t ",
622 &addresses) > 0) {
623 fputs(addresses, stdout);
624 fputc('\n', stdout);
625 }
626
627 print_os_release(bus, "GetMachineOSRelease", i->name, "\t OS: ");
628
629 print_uid_shift(bus, i->name);
630
631 if (i->unit) {
632 printf("\t Unit: %s\n", i->unit);
633
634 if (!empty_or_root(i->subgroup))
635 printf("\tSubgroup: %s\n", i->subgroup);
636
637 show_unit_cgroup(bus, i->unit, i->subgroup, i->leader);
638
639 if (arg_transport == BUS_TRANSPORT_LOCAL)
640
641 show_journal_by_unit(
642 stdout,
643 i->unit,
644 /* namespace = */ NULL,
645 arg_output,
646 /* n_columns = */ 0,
647 i->timestamp.monotonic,
648 arg_lines,
649 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
650 SD_JOURNAL_LOCAL_ONLY,
651 /* system_unit = */ true,
652 /* ellipsized = */ NULL);
653 }
654}
655
656static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
657 MachineStatusInfo *i = userdata;
658 size_t l;
659 const void *v;
660 int r;
661
662 assert_cc(sizeof(int32_t) == sizeof(int));
663 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
664 if (r < 0)
665 return r;
666 if (r == 0)
667 return -EBADMSG;
668
669 i->n_netif = l / sizeof(int32_t);
670 i->netif = memdup(v, l);
671 if (!i->netif)
672 return -ENOMEM;
673
674 return 0;
675}
676
677static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
678
679 static const struct bus_properties_map map[] = {
680 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
681 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
682 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
683 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
684 { "Subgroup", "s", NULL, offsetof(MachineStatusInfo, subgroup) },
685 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
686 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
687 { "LeaderPIDFDId", "t", NULL, offsetof(MachineStatusInfo, leader_pidfdid) },
688 { "Supervisor", "u", NULL, offsetof(MachineStatusInfo, supervisor) },
689 { "SupervisorPIDFDId", "t", NULL, offsetof(MachineStatusInfo, supervisor_pidfdid) },
690 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) },
691 { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) },
692 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
693 { "NetworkInterfaces", "ai", map_netif, 0 },
694 { "UID", "u", NULL, offsetof(MachineStatusInfo, uid) },
695 {}
696 };
697
698 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
699 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
700 _cleanup_(machine_status_info_done) MachineStatusInfo info = {};
701 int r;
702
703 assert(verb);
704 assert(bus);
705 assert(path);
706 assert(new_line);
707
708 r = bus_map_all_properties(bus,
709 "org.freedesktop.machine1",
710 path,
711 map,
712 0,
713 &error,
714 &m,
715 &info);
716 if (r < 0)
717 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
718
719 if (*new_line)
720 printf("\n");
721 *new_line = true;
722
723 print_machine_status_info(bus, &info);
724
725 return r;
726}
727
728static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
729 int r;
730
731 assert(bus);
732 assert(path);
733 assert(new_line);
734
735 if (*new_line)
736 printf("\n");
737
738 *new_line = true;
739
740 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, NULL, arg_property, arg_print_flags, NULL);
741 if (r < 0)
742 log_error_errno(r, "Could not get properties: %m");
743
744 return r;
745}
746
747static int show_machine(int argc, char *argv[], void *userdata) {
748 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
749 bool properties, new_line = false;
750 sd_bus *bus = ASSERT_PTR(userdata);
751 int r = 0;
752
753 properties = !strstr(argv[0], "status");
754
755 pager_open(arg_pager_flags);
756
757 if (properties && argc <= 1) {
758
759 /* If no argument is specified, inspect the manager
760 * itself */
761 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
762 if (r < 0)
763 return r;
764 }
765
766 for (int i = 1; i < argc; i++) {
767 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
768 const char *path = NULL;
769
770 r = bus_call_method(bus, bus_machine_mgr, "GetMachine", &error, &reply, "s", argv[i]);
771 if (r < 0)
772 return log_error_errno(r, "Could not get path to machine: %s", bus_error_message(&error, r));
773
774 r = sd_bus_message_read(reply, "o", &path);
775 if (r < 0)
776 return bus_log_parse_error(r);
777
778 if (properties)
779 r = show_machine_properties(bus, path, &new_line);
780 else
781 r = show_machine_info(argv[0], bus, path, &new_line);
782 }
783
784 return r;
785}
786
787static int print_image_hostname(sd_bus *bus, const char *name) {
788 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
789 const char *hn;
790 int r;
791
792 r = bus_call_method(bus, bus_machine_mgr, "GetImageHostname", NULL, &reply, "s", name);
793 if (r < 0)
794 return r;
795
796 r = sd_bus_message_read(reply, "s", &hn);
797 if (r < 0)
798 return r;
799
800 if (!isempty(hn))
801 printf("\tHostname: %s\n", hn);
802
803 return 0;
804}
805
806static int print_image_machine_id(sd_bus *bus, const char *name) {
807 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
808 sd_id128_t id;
809 int r;
810
811 r = bus_call_method(bus, bus_machine_mgr, "GetImageMachineID", NULL, &reply, "s", name);
812 if (r < 0)
813 return r;
814
815 r = bus_message_read_id128(reply, &id);
816 if (r < 0)
817 return r;
818
819 if (!sd_id128_is_null(id))
820 printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(id));
821
822 return 0;
823}
824
825static int print_image_machine_info(sd_bus *bus, const char *name) {
826 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
827 int r;
828
829 r = bus_call_method(bus, bus_machine_mgr, "GetImageMachineInfo", NULL, &reply, "s", name);
830 if (r < 0)
831 return r;
832
833 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
834 if (r < 0)
835 return r;
836
837 for (;;) {
838 const char *p, *q;
839
840 r = sd_bus_message_read(reply, "{ss}", &p, &q);
841 if (r < 0)
842 return r;
843 if (r == 0)
844 break;
845
846 if (streq(p, "DEPLOYMENT"))
847 printf(" Deployment: %s\n", q);
848 }
849
850 r = sd_bus_message_exit_container(reply);
851 if (r < 0)
852 return r;
853
854 return 0;
855}
856
857typedef struct ImageStatusInfo {
858 const char *name;
859 const char *path;
860 const char *type;
861 bool read_only;
862 usec_t crtime;
863 usec_t mtime;
864 uint64_t usage;
865 uint64_t limit;
866 uint64_t usage_exclusive;
867 uint64_t limit_exclusive;
868} ImageStatusInfo;
869
870static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
871 assert(bus);
872 assert(i);
873
874 if (i->name) {
875 fputs(i->name, stdout);
876 putchar('\n');
877 }
878
879 if (i->type)
880 printf("\t Type: %s\n", i->type);
881
882 if (i->path)
883 printf("\t Path: %s\n", i->path);
884
885 (void) print_image_hostname(bus, i->name);
886 (void) print_image_machine_id(bus, i->name);
887 (void) print_image_machine_info(bus, i->name);
888
889 print_os_release(bus, "GetImageOSRelease", i->name, "\t OS: ");
890
891 printf("\t RO: %s%s%s\n",
892 i->read_only ? ansi_highlight_red() : "",
893 i->read_only ? "read-only" : "writable",
894 i->read_only ? ansi_normal() : "");
895
896 if (timestamp_is_set(i->crtime))
897 printf("\t Created: %s; %s\n",
898 FORMAT_TIMESTAMP(i->crtime), FORMAT_TIMESTAMP_RELATIVE(i->crtime));
899
900 if (timestamp_is_set(i->mtime))
901 printf("\tModified: %s; %s\n",
902 FORMAT_TIMESTAMP(i->mtime), FORMAT_TIMESTAMP_RELATIVE(i->mtime));
903
904 if (i->usage != UINT64_MAX) {
905 if (i->usage_exclusive != i->usage && i->usage_exclusive != UINT64_MAX)
906 printf("\t Usage: %s (exclusive: %s)\n",
907 FORMAT_BYTES(i->usage), FORMAT_BYTES(i->usage_exclusive));
908 else
909 printf("\t Usage: %s\n", FORMAT_BYTES(i->usage));
910 }
911
912 if (i->limit != UINT64_MAX) {
913 if (i->limit_exclusive != i->limit && i->limit_exclusive != UINT64_MAX)
914 printf("\t Limit: %s (exclusive: %s)\n",
915 FORMAT_BYTES(i->limit), FORMAT_BYTES(i->limit_exclusive));
916 else
917 printf("\t Limit: %s\n", FORMAT_BYTES(i->limit));
918 }
919}
920
921static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
922
923 static const struct bus_properties_map map[] = {
924 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
925 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
926 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
927 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
928 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
929 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
930 { "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) },
931 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
932 { "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) },
933 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
934 {}
935 };
936
937 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
938 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
939 ImageStatusInfo info = {};
940 int r;
941
942 assert(bus);
943 assert(path);
944 assert(new_line);
945
946 r = bus_map_all_properties(bus,
947 "org.freedesktop.machine1",
948 path,
949 map,
950 BUS_MAP_BOOLEAN_AS_BOOL,
951 &error,
952 &m,
953 &info);
954 if (r < 0)
955 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
956
957 if (*new_line)
958 printf("\n");
959 *new_line = true;
960
961 print_image_status_info(bus, &info);
962
963 return r;
964}
965
966typedef struct PoolStatusInfo {
967 const char *path;
968 uint64_t usage;
969 uint64_t limit;
970} PoolStatusInfo;
971
972static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) {
973 if (i->path)
974 printf("\t Path: %s\n", i->path);
975
976 if (i->usage != UINT64_MAX)
977 printf("\t Usage: %s\n", FORMAT_BYTES(i->usage));
978
979 if (i->limit != UINT64_MAX)
980 printf("\t Limit: %s\n", FORMAT_BYTES(i->limit));
981}
982
983static int show_pool_info(sd_bus *bus) {
984
985 static const struct bus_properties_map map[] = {
986 { "PoolPath", "s", NULL, offsetof(PoolStatusInfo, path) },
987 { "PoolUsage", "t", NULL, offsetof(PoolStatusInfo, usage) },
988 { "PoolLimit", "t", NULL, offsetof(PoolStatusInfo, limit) },
989 {}
990 };
991
992 PoolStatusInfo info = {
993 .usage = UINT64_MAX,
994 .limit = UINT64_MAX,
995 };
996
997 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
998 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
999 int r;
1000
1001 assert(bus);
1002
1003 r = bus_map_all_properties(bus,
1004 "org.freedesktop.machine1",
1005 "/org/freedesktop/machine1",
1006 map,
1007 0,
1008 &error,
1009 &m,
1010 &info);
1011 if (r < 0)
1012 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
1013
1014 print_pool_status_info(bus, &info);
1015
1016 return 0;
1017}
1018
1019static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
1020 int r;
1021
1022 assert(bus);
1023 assert(path);
1024 assert(new_line);
1025
1026 if (*new_line)
1027 printf("\n");
1028
1029 *new_line = true;
1030
1031 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, NULL, arg_property, arg_print_flags, NULL);
1032 if (r < 0)
1033 log_error_errno(r, "Could not get properties: %m");
1034
1035 return r;
1036}
1037
1038static int show_image(int argc, char *argv[], void *userdata) {
1039 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1040 bool properties, new_line = false;
1041 sd_bus *bus = ASSERT_PTR(userdata);
1042 int r = 0;
1043
1044 properties = !strstr(argv[0], "status");
1045
1046 pager_open(arg_pager_flags);
1047
1048 if (argc <= 1) {
1049
1050 /* If no argument is specified, inspect the manager
1051 * itself */
1052
1053 if (properties)
1054 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
1055 else
1056 r = show_pool_info(bus);
1057 if (r < 0)
1058 return r;
1059 }
1060
1061 for (int i = 1; i < argc; i++) {
1062 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1063 const char *path = NULL;
1064
1065 r = bus_call_method(bus, bus_machine_mgr, "GetImage", &error, &reply, "s", argv[i]);
1066 if (r < 0)
1067 return log_error_errno(r, "Could not get path to image: %s", bus_error_message(&error, r));
1068
1069 r = sd_bus_message_read(reply, "o", &path);
1070 if (r < 0)
1071 return bus_log_parse_error(r);
1072
1073 if (properties)
1074 r = show_image_properties(bus, path, &new_line);
1075 else
1076 r = show_image_info(bus, path, &new_line);
1077 }
1078
1079 return r;
1080}
1081
1082static int kill_machine(int argc, char *argv[], void *userdata) {
1083 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1084 sd_bus *bus = ASSERT_PTR(userdata);
1085 int r;
1086
1087 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1088
1089 if (!arg_kill_whom)
1090 arg_kill_whom = "all";
1091
1092 for (int i = 1; i < argc; i++) {
1093 r = bus_call_method(
1094 bus,
1095 bus_machine_mgr,
1096 "KillMachine",
1097 &error,
1098 NULL,
1099 "ssi", argv[i], arg_kill_whom, arg_signal);
1100 if (r < 0)
1101 return log_error_errno(r, "Could not kill machine: %s", bus_error_message(&error, r));
1102 }
1103
1104 return 0;
1105}
1106
1107static int reboot_machine(int argc, char *argv[], void *userdata) {
1108 if (arg_runner == RUNNER_VMSPAWN)
1109 return log_error_errno(
1110 SYNTHETIC_ERRNO(EOPNOTSUPP),
1111 "%s only support supported for --runner=nspawn",
1112 streq(argv[0], "reboot") ? "Reboot" : "Restart");
1113
1114 arg_kill_whom = "leader";
1115 arg_signal = SIGINT; /* sysvinit + systemd */
1116
1117 return kill_machine(argc, argv, userdata);
1118}
1119
1120static int poweroff_machine(int argc, char *argv[], void *userdata) {
1121 arg_kill_whom = "leader";
1122 arg_signal = SIGRTMIN+4; /* only systemd */
1123
1124 return kill_machine(argc, argv, userdata);
1125}
1126
1127static int terminate_machine(int argc, char *argv[], void *userdata) {
1128 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1129 sd_bus *bus = ASSERT_PTR(userdata);
1130 int r;
1131
1132 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1133
1134 for (int i = 1; i < argc; i++) {
1135 r = bus_call_method(bus, bus_machine_mgr, "TerminateMachine", &error, NULL, "s", argv[i]);
1136 if (r < 0)
1137 return log_error_errno(r, "Could not terminate machine: %s", bus_error_message(&error, r));
1138 }
1139
1140 return 0;
1141}
1142
1143static const char *select_copy_method(bool copy_from, bool force) {
1144 if (force)
1145 return copy_from ? "CopyFromMachineWithFlags" : "CopyToMachineWithFlags";
1146 else
1147 return copy_from ? "CopyFromMachine" : "CopyToMachine";
1148}
1149
1150static int copy_files(int argc, char *argv[], void *userdata) {
1151 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1152 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1153 _cleanup_free_ char *abs_host_path = NULL;
1154 char *dest, *host_path, *container_path;
1155 sd_bus *bus = ASSERT_PTR(userdata);
1156 bool copy_from;
1157 int r;
1158
1159 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1160
1161 copy_from = streq(argv[0], "copy-from");
1162 dest = argv[3] ?: argv[2];
1163 host_path = copy_from ? dest : argv[2];
1164 container_path = copy_from ? argv[2] : dest;
1165
1166 if (!path_is_absolute(host_path)) {
1167 r = path_make_absolute_cwd(host_path, &abs_host_path);
1168 if (r < 0)
1169 return log_error_errno(r, "Failed to make path absolute: %m");
1170
1171 host_path = abs_host_path;
1172 }
1173
1174 r = bus_message_new_method_call(
1175 bus,
1176 &m,
1177 bus_machine_mgr,
1178 select_copy_method(copy_from, arg_force));
1179 if (r < 0)
1180 return bus_log_create_error(r);
1181
1182 r = sd_bus_message_append(
1183 m,
1184 "sss",
1185 argv[1],
1186 copy_from ? container_path : host_path,
1187 copy_from ? host_path : container_path);
1188 if (r < 0)
1189 return bus_log_create_error(r);
1190
1191 if (arg_force) {
1192 r = sd_bus_message_append(m, "t", (uint64_t) MACHINE_COPY_REPLACE);
1193 if (r < 0)
1194 return bus_log_create_error(r);
1195 }
1196
1197 /* This is a slow operation, hence turn off any method call timeouts */
1198 r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
1199 if (r < 0)
1200 return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r));
1201
1202 return 0;
1203}
1204
1205static int bind_mount(int argc, char *argv[], void *userdata) {
1206 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1207 sd_bus *bus = ASSERT_PTR(userdata);
1208 int r;
1209
1210 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1211
1212 r = bus_call_method(
1213 bus,
1214 bus_machine_mgr,
1215 "BindMountMachine",
1216 &error,
1217 NULL,
1218 "sssbb",
1219 argv[1],
1220 argv[2],
1221 argv[3],
1222 arg_read_only,
1223 arg_mkdir);
1224 if (r < 0)
1225 return log_error_errno(r, "Failed to bind mount: %s", bus_error_message(&error, r));
1226
1227 return 0;
1228}
1229
1230static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1231 PTYForward *forward = ASSERT_PTR(userdata);
1232 int r;
1233
1234 assert(m);
1235
1236 /* Tell the forwarder to exit on the next vhangup(), so that we still flush out what might be queued
1237 * and exit then. */
1238
1239 r = pty_forward_set_ignore_vhangup(forward, false);
1240 if (r < 0) {
1241 /* On error, quit immediately. */
1242 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
1243 (void) sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
1244 }
1245
1246 return 0;
1247}
1248
1249static int process_forward(sd_event *event, sd_bus_slot *machine_removed_slot, int master, PTYForwardFlags flags, const char *name) {
1250 int r;
1251
1252 assert(event);
1253 assert(machine_removed_slot);
1254 assert(master >= 0);
1255 assert(name);
1256
1257 if (!arg_quiet) {
1258 if (streq(name, ".host"))
1259 log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
1260 else
1261 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
1262 }
1263
1264 _cleanup_(osc_context_closep) sd_id128_t osc_context_id = SD_ID128_NULL;
1265 if (!terminal_is_dumb()) {
1266 r = osc_context_open_container(name, /* ret_seq= */ NULL, &osc_context_id);
1267 if (r < 0)
1268 return r;
1269 }
1270
1271 r = sd_event_set_signal_exit(event, true);
1272 if (r < 0)
1273 return log_error_errno(r, "Failed to enable SIGINT/SITERM handling: %m");
1274
1275 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1276 r = pty_forward_new(event, master, flags, &forward);
1277 if (r < 0)
1278 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1279
1280 /* No userdata should not set previously. */
1281 assert_se(!sd_bus_slot_set_userdata(machine_removed_slot, forward));
1282
1283 r = sd_event_loop(event);
1284 if (r < 0)
1285 return log_error_errno(r, "Failed to run event loop: %m");
1286
1287 bool machine_died =
1288 (flags & PTY_FORWARD_IGNORE_VHANGUP) &&
1289 pty_forward_get_ignore_vhangup(forward) == 0;
1290
1291 if (!arg_quiet) {
1292 if (machine_died)
1293 log_info("Machine %s terminated.", name);
1294 else if (streq(name, ".host"))
1295 log_info("Connection to the local host terminated.");
1296 else
1297 log_info("Connection to machine %s terminated.", name);
1298 }
1299
1300 return 0;
1301}
1302
1303static int parse_machine_uid(const char *spec, const char **machine, char **uid) {
1304 /*
1305 * Whatever is specified in the spec takes priority over global arguments.
1306 */
1307 char *_uid = NULL;
1308 const char *_machine = NULL;
1309
1310 if (spec) {
1311 const char *at;
1312
1313 at = strchr(spec, '@');
1314 if (at) {
1315 if (at == spec)
1316 /* Do the same as ssh and refuse "@host". */
1317 return -EINVAL;
1318
1319 _machine = at + 1;
1320 _uid = strndup(spec, at - spec);
1321 if (!_uid)
1322 return -ENOMEM;
1323 } else
1324 _machine = spec;
1325 };
1326
1327 if (arg_uid && !_uid) {
1328 _uid = strdup(arg_uid);
1329 if (!_uid)
1330 return -ENOMEM;
1331 }
1332
1333 *uid = _uid;
1334 *machine = isempty(_machine) ? ".host" : _machine;
1335 return 0;
1336}
1337
1338static int login_machine(int argc, char *argv[], void *userdata) {
1339 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1340 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1341 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
1342 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
1343 int master = -1, r;
1344 sd_bus *bus = ASSERT_PTR(userdata);
1345 const char *match, *machine;
1346
1347 if (!strv_isempty(arg_setenv) || arg_uid)
1348 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1349 "--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1350
1351 if (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE))
1352 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1353 "Login only supported on local machines.");
1354
1355 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1356
1357 r = sd_event_default(&event);
1358 if (r < 0)
1359 return log_error_errno(r, "Failed to get event loop: %m");
1360
1361 r = sd_bus_attach_event(bus, event, 0);
1362 if (r < 0)
1363 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1364
1365 machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1];
1366
1367 match = strjoina("type='signal',"
1368 "sender='org.freedesktop.machine1',"
1369 "path='/org/freedesktop/machine1',",
1370 "interface='org.freedesktop.machine1.Manager',"
1371 "member='MachineRemoved',"
1372 "arg0='", machine, "'");
1373
1374 r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, NULL);
1375 if (r < 0)
1376 return log_error_errno(r, "Failed to request machine removal match: %m");
1377
1378 r = bus_call_method(bus, bus_machine_mgr, "OpenMachineLogin", &error, &reply, "s", machine);
1379 if (r < 0)
1380 return log_error_errno(r, "Failed to get login PTY: %s", bus_error_message(&error, r));
1381
1382 r = sd_bus_message_read(reply, "hs", &master, NULL);
1383 if (r < 0)
1384 return bus_log_parse_error(r);
1385
1386 return process_forward(event, slot, master, PTY_FORWARD_IGNORE_VHANGUP, machine);
1387}
1388
1389static int shell_machine(int argc, char *argv[], void *userdata) {
1390 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
1391 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1392 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
1393 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
1394 int master = -1, r;
1395 sd_bus *bus = ASSERT_PTR(userdata);
1396 const char *match, *machine, *path;
1397 _cleanup_free_ char *uid = NULL;
1398
1399 if (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE))
1400 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1401 "Shell only supported on local machines.");
1402
1403 /* Pass $TERM & Co. to shell session, if not explicitly specified. */
1404 FOREACH_STRING(v, "TERM=", "COLORTERM=", "NO_COLOR=") {
1405 if (strv_find_prefix(arg_setenv, v))
1406 continue;
1407
1408 const char *t = strv_find_prefix(environ, v);
1409 if (!t)
1410 continue;
1411
1412 if (strv_extend(&arg_setenv, t) < 0)
1413 return log_oom();
1414 }
1415
1416 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1417
1418 r = sd_event_default(&event);
1419 if (r < 0)
1420 return log_error_errno(r, "Failed to get event loop: %m");
1421
1422 r = sd_bus_attach_event(bus, event, 0);
1423 if (r < 0)
1424 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1425
1426 r = parse_machine_uid(argc >= 2 ? argv[1] : NULL, &machine, &uid);
1427 if (r < 0)
1428 return log_error_errno(r, "Failed to parse machine specification: %m");
1429
1430 match = strjoina("type='signal',"
1431 "sender='org.freedesktop.machine1',"
1432 "path='/org/freedesktop/machine1',",
1433 "interface='org.freedesktop.machine1.Manager',"
1434 "member='MachineRemoved',"
1435 "arg0='", machine, "'");
1436
1437 r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, NULL);
1438 if (r < 0)
1439 return log_error_errno(r, "Failed to request machine removal match: %m");
1440
1441 r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "OpenMachineShell");
1442 if (r < 0)
1443 return bus_log_create_error(r);
1444
1445 path = argc < 3 || isempty(argv[2]) ? NULL : argv[2];
1446
1447 r = sd_bus_message_append(m, "sss", machine, uid, path);
1448 if (r < 0)
1449 return bus_log_create_error(r);
1450
1451 r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2);
1452 if (r < 0)
1453 return bus_log_create_error(r);
1454
1455 r = sd_bus_message_append_strv(m, arg_setenv);
1456 if (r < 0)
1457 return bus_log_create_error(r);
1458
1459 r = sd_bus_call(bus, m, 0, &error, &reply);
1460 if (r < 0)
1461 return log_error_errno(r, "Failed to get shell PTY: %s", bus_error_message(&error, r));
1462
1463 r = sd_bus_message_read(reply, "hs", &master, NULL);
1464 if (r < 0)
1465 return bus_log_parse_error(r);
1466
1467 return process_forward(event, slot, master, /* flags = */ 0, machine);
1468}
1469
1470static int normalize_nspawn_filename(const char *name, char **ret_file) {
1471 _cleanup_free_ char *file = NULL;
1472
1473 assert(name);
1474 assert(ret_file);
1475
1476 if (!endswith(name, ".nspawn"))
1477 file = strjoin(name, ".nspawn");
1478 else
1479 file = strdup(name);
1480 if (!file)
1481 return log_oom();
1482
1483 if (!filename_is_valid(file))
1484 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid settings file name '%s'.", file);
1485
1486 *ret_file = TAKE_PTR(file);
1487 return 0;
1488}
1489
1490static int get_settings_path(const char *name, char **ret_path) {
1491 assert(name);
1492 assert(ret_path);
1493
1494 FOREACH_STRING(i, "/etc/systemd/nspawn", "/run/systemd/nspawn", "/var/lib/machines") {
1495 _cleanup_free_ char *path = NULL;
1496
1497 path = path_join(i, name);
1498 if (!path)
1499 return -ENOMEM;
1500
1501 if (access(path, F_OK) >= 0) {
1502 *ret_path = TAKE_PTR(path);
1503 return 0;
1504 }
1505 }
1506
1507 return -ENOENT;
1508}
1509
1510static int edit_settings(int argc, char *argv[], void *userdata) {
1511 _cleanup_(edit_file_context_done) EditFileContext context = {};
1512 int r;
1513
1514 if (!on_tty())
1515 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit machine settings if not on a tty.");
1516
1517 if (arg_transport != BUS_TRANSPORT_LOCAL)
1518 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1519 "Edit is only supported on the host machine.");
1520
1521 if (arg_runner == RUNNER_VMSPAWN)
1522 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Edit is only supported for --runner=nspawn");
1523
1524 r = mac_init();
1525 if (r < 0)
1526 return r;
1527
1528 STRV_FOREACH(name, strv_skip(argv, 1)) {
1529 _cleanup_free_ char *file = NULL, *path = NULL;
1530
1531 if (path_is_absolute(*name)) {
1532 if (!path_is_safe(*name))
1533 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1534 "Invalid settings file path '%s'.",
1535 *name);
1536
1537 r = edit_files_add(&context, *name, NULL, NULL);
1538 if (r < 0)
1539 return r;
1540 continue;
1541 }
1542
1543 r = normalize_nspawn_filename(*name, &file);
1544 if (r < 0)
1545 return r;
1546
1547 r = get_settings_path(file, &path);
1548 if (r == -ENOENT) {
1549 log_debug("No existing settings file for machine '%s' found, creating a new file.", *name);
1550
1551 path = path_join("/etc/systemd/nspawn", file);
1552 if (!path)
1553 return log_oom();
1554
1555 r = edit_files_add(&context, path, NULL, NULL);
1556 if (r < 0)
1557 return r;
1558 continue;
1559 }
1560 if (r < 0)
1561 return log_error_errno(r, "Failed to get the path of the settings file: %m");
1562
1563 if (path_startswith(path, "/var/lib/machines")) {
1564 _cleanup_free_ char *new_path = NULL;
1565
1566 new_path = path_join("/etc/systemd/nspawn", file);
1567 if (!new_path)
1568 return log_oom();
1569
1570 r = edit_files_add(&context, new_path, path, NULL);
1571 } else
1572 r = edit_files_add(&context, path, NULL, NULL);
1573 if (r < 0)
1574 return r;
1575 }
1576
1577 return do_edit_files_and_install(&context);
1578}
1579
1580static int cat_settings(int argc, char *argv[], void *userdata) {
1581 int r = 0;
1582
1583 if (arg_transport != BUS_TRANSPORT_LOCAL)
1584 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1585 "Cat is only supported on the host machine.");
1586
1587 if (arg_runner == RUNNER_VMSPAWN)
1588 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Cat is only supported for --runner=nspawn");
1589
1590 pager_open(arg_pager_flags);
1591
1592 STRV_FOREACH(name, strv_skip(argv, 1)) {
1593 _cleanup_free_ char *file = NULL, *path = NULL;
1594 int q;
1595
1596 if (path_is_absolute(*name)) {
1597 if (!path_is_safe(*name))
1598 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1599 "Invalid settings file path '%s'.",
1600 *name);
1601
1602 q = cat_files(*name, /* dropins = */ NULL, /* flags = */ CAT_FORMAT_HAS_SECTIONS);
1603 if (q < 0)
1604 return r < 0 ? r : q;
1605 continue;
1606 }
1607
1608 q = normalize_nspawn_filename(*name, &file);
1609 if (q < 0)
1610 return r < 0 ? r : q;
1611
1612 q = get_settings_path(file, &path);
1613 if (q == -ENOENT) {
1614 log_error_errno(q, "No settings file found for machine '%s'.", *name);
1615 r = r < 0 ? r : q;
1616 continue;
1617 }
1618 if (q < 0) {
1619 log_error_errno(q, "Failed to get the path of the settings file: %m");
1620 return r < 0 ? r : q;
1621 }
1622
1623 q = cat_files(path, /* dropins = */ NULL, /* flags = */ CAT_FORMAT_HAS_SECTIONS);
1624 if (q < 0)
1625 return r < 0 ? r : q;
1626 }
1627
1628 return r;
1629}
1630
1631static int remove_image(int argc, char *argv[], void *userdata) {
1632 sd_bus *bus = ASSERT_PTR(userdata);
1633 int r;
1634
1635 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1636
1637 for (int i = 1; i < argc; i++) {
1638 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1639 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1640
1641 r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "RemoveImage");
1642 if (r < 0)
1643 return bus_log_create_error(r);
1644
1645 r = sd_bus_message_append(m, "s", argv[i]);
1646 if (r < 0)
1647 return bus_log_create_error(r);
1648
1649 /* This is a slow operation, hence turn off any method call timeouts */
1650 r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
1651 if (r < 0)
1652 return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r));
1653 }
1654
1655 return 0;
1656}
1657
1658static int rename_image(int argc, char *argv[], void *userdata) {
1659 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1660 sd_bus *bus = ASSERT_PTR(userdata);
1661 int r;
1662
1663 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1664
1665 r = bus_call_method(
1666 bus,
1667 bus_machine_mgr,
1668 "RenameImage",
1669 &error,
1670 NULL,
1671 "ss", argv[1], argv[2]);
1672 if (r < 0)
1673 return log_error_errno(r, "Could not rename image: %s", bus_error_message(&error, r));
1674
1675 return 0;
1676}
1677
1678static int clone_image(int argc, char *argv[], void *userdata) {
1679 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1680 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1681 sd_bus *bus = ASSERT_PTR(userdata);
1682 int r;
1683
1684 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1685
1686 r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "CloneImage");
1687 if (r < 0)
1688 return bus_log_create_error(r);
1689
1690 r = sd_bus_message_append(m, "ssb", argv[1], argv[2], arg_read_only);
1691 if (r < 0)
1692 return bus_log_create_error(r);
1693
1694 /* This is a slow operation, hence turn off any method call timeouts */
1695 r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
1696 if (r < 0)
1697 return log_error_errno(r, "Could not clone image: %s", bus_error_message(&error, r));
1698
1699 return 0;
1700}
1701
1702static int read_only_image(int argc, char *argv[], void *userdata) {
1703 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1704 sd_bus *bus = ASSERT_PTR(userdata);
1705 int b = true, r;
1706
1707 if (argc > 2) {
1708 b = parse_boolean(argv[2]);
1709 if (b < 0)
1710 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1711 "Failed to parse boolean argument: %s",
1712 argv[2]);
1713 }
1714
1715 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1716
1717 r = bus_call_method(bus, bus_machine_mgr, "MarkImageReadOnly", &error, NULL, "sb", argv[1], b);
1718 if (r < 0)
1719 return log_error_errno(r, "Could not mark image read-only: %s", bus_error_message(&error, r));
1720
1721 return 0;
1722}
1723
1724static int image_exists(sd_bus *bus, const char *name) {
1725 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1726 int r;
1727
1728 assert(bus);
1729 assert(name);
1730
1731 r = bus_call_method(bus, bus_machine_mgr, "GetImage", &error, NULL, "s", name);
1732 if (r < 0) {
1733 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_IMAGE))
1734 return 0;
1735
1736 return log_error_errno(r, "Failed to check whether image %s exists: %s", name, bus_error_message(&error, r));
1737 }
1738
1739 return 1;
1740}
1741
1742static int make_service_name(const char *name, char **ret) {
1743 int r;
1744
1745 assert(name);
1746 assert(ret);
1747 assert(arg_runner >= 0 && arg_runner < _RUNNER_MAX);
1748
1749 if (!hostname_is_valid(name, 0))
1750 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1751 "Invalid machine name %s.", name);
1752
1753 r = unit_name_build(machine_runner_unit_prefix_table[arg_runner], name, ".service", ret);
1754 if (r < 0)
1755 return log_error_errno(r, "Failed to build unit name: %m");
1756
1757 return 0;
1758}
1759
1760static int start_machine(int argc, char *argv[], void *userdata) {
1761 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1762 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1763 sd_bus *bus = ASSERT_PTR(userdata);
1764 int r;
1765
1766 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1767 ask_password_agent_open_if_enabled(arg_transport, arg_ask_password);
1768
1769 r = bus_wait_for_jobs_new(bus, &w);
1770 if (r < 0)
1771 return log_error_errno(r, "Could not watch jobs: %m");
1772
1773 for (int i = 1; i < argc; i++) {
1774 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1775 _cleanup_free_ char *unit = NULL;
1776 const char *object;
1777
1778 r = make_service_name(argv[i], &unit);
1779 if (r < 0)
1780 return r;
1781
1782 r = image_exists(bus, argv[i]);
1783 if (r < 0)
1784 return r;
1785 if (r == 0)
1786 return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
1787 "Machine image '%s' does not exist.",
1788 argv[i]);
1789
1790 r = bus_call_method(
1791 bus,
1792 bus_systemd_mgr,
1793 "StartUnit",
1794 &error,
1795 &reply,
1796 "ss", unit, "fail");
1797 if (r < 0)
1798 return log_error_errno(r, "Failed to start unit: %s", bus_error_message(&error, r));
1799
1800 r = sd_bus_message_read(reply, "o", &object);
1801 if (r < 0)
1802 return bus_log_parse_error(r);
1803
1804 r = bus_wait_for_jobs_add(w, object);
1805 if (r < 0)
1806 return log_oom();
1807 }
1808
1809 r = bus_wait_for_jobs(w, arg_quiet, NULL);
1810 if (r < 0)
1811 return r;
1812
1813 return 0;
1814}
1815
1816static int enable_machine(int argc, char *argv[], void *userdata) {
1817 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
1818 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1819 const char *method;
1820 sd_bus *bus = ASSERT_PTR(userdata);
1821 int r;
1822 bool enable;
1823
1824 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1825
1826 enable = streq(argv[0], "enable");
1827 method = enable ? "EnableUnitFiles" : "DisableUnitFiles";
1828
1829 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
1830 if (r < 0)
1831 return bus_log_create_error(r);
1832
1833 r = sd_bus_message_open_container(m, 'a', "s");
1834 if (r < 0)
1835 return bus_log_create_error(r);
1836
1837 if (enable) {
1838 r = sd_bus_message_append(m, "s", "machines.target");
1839 if (r < 0)
1840 return bus_log_create_error(r);
1841 }
1842
1843 for (int i = 1; i < argc; i++) {
1844 _cleanup_free_ char *unit = NULL;
1845
1846 r = make_service_name(argv[i], &unit);
1847 if (r < 0)
1848 return r;
1849
1850 r = image_exists(bus, argv[i]);
1851 if (r < 0)
1852 return r;
1853 if (r == 0)
1854 return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
1855 "Machine image '%s' does not exist.",
1856 argv[i]);
1857
1858 r = sd_bus_message_append(m, "s", unit);
1859 if (r < 0)
1860 return bus_log_create_error(r);
1861 }
1862
1863 r = sd_bus_message_close_container(m);
1864 if (r < 0)
1865 return bus_log_create_error(r);
1866
1867 if (enable)
1868 r = sd_bus_message_append(m, "bb", false, false);
1869 else
1870 r = sd_bus_message_append(m, "b", false);
1871 if (r < 0)
1872 return bus_log_create_error(r);
1873
1874 r = sd_bus_call(bus, m, 0, &error, &reply);
1875 if (r < 0)
1876 return log_error_errno(r, "Failed to enable or disable unit: %s", bus_error_message(&error, r));
1877
1878 if (enable) {
1879 r = sd_bus_message_read(reply, "b", NULL);
1880 if (r < 0)
1881 return bus_log_parse_error(r);
1882 }
1883
1884 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1885 if (r < 0)
1886 return r;
1887
1888 r = bus_service_manager_reload(bus);
1889 if (r < 0)
1890 return r;
1891
1892 if (arg_now) {
1893 _cleanup_strv_free_ char **new_args = NULL;
1894
1895 new_args = strv_new(enable ? "start" : "poweroff");
1896 if (!new_args)
1897 return log_oom();
1898
1899 r = strv_extend_strv(&new_args, argv + 1, /* filter_duplicates = */ false);
1900 if (r < 0)
1901 return log_oom();
1902
1903 if (enable)
1904 return start_machine(strv_length(new_args), new_args, userdata);
1905
1906 return poweroff_machine(strv_length(new_args), new_args, userdata);
1907 }
1908
1909 return 0;
1910}
1911
1912static int set_limit(int argc, char *argv[], void *userdata) {
1913 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1914 sd_bus *bus = userdata;
1915 uint64_t limit;
1916 int r;
1917
1918 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1919
1920 if (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
1921 limit = UINT64_MAX;
1922 else {
1923 r = parse_size(argv[argc-1], 1024, &limit);
1924 if (r < 0)
1925 return log_error_errno(r, "Failed to parse size: %s", argv[argc-1]);
1926 }
1927
1928 if (argc > 2)
1929 /* With two arguments changes the quota limit of the
1930 * specified image */
1931 r = bus_call_method(bus, bus_machine_mgr, "SetImageLimit", &error, NULL, "st", argv[1], limit);
1932 else
1933 /* With one argument changes the pool quota limit */
1934 r = bus_call_method(bus, bus_machine_mgr, "SetPoolLimit", &error, NULL, "t", limit);
1935
1936 if (r < 0)
1937 return log_error_errno(r, "Could not set limit: %s", bus_error_message(&error, r));
1938
1939 return 0;
1940}
1941
1942static int clean_images(int argc, char *argv[], void *userdata) {
1943 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
1944 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1945 uint64_t usage, total = 0;
1946 sd_bus *bus = userdata;
1947 const char *name;
1948 unsigned c = 0;
1949 int r;
1950
1951 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1952
1953 r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "CleanPool");
1954 if (r < 0)
1955 return bus_log_create_error(r);
1956
1957 r = sd_bus_message_append(m, "s", arg_all ? "all" : "hidden");
1958 if (r < 0)
1959 return bus_log_create_error(r);
1960
1961 /* This is a slow operation, hence permit a longer time for completion. */
1962 r = sd_bus_call(bus, m, USEC_INFINITY, &error, &reply);
1963 if (r < 0)
1964 return log_error_errno(r, "Could not clean pool: %s", bus_error_message(&error, r));
1965
1966 r = sd_bus_message_enter_container(reply, 'a', "(st)");
1967 if (r < 0)
1968 return bus_log_parse_error(r);
1969
1970 while ((r = sd_bus_message_read(reply, "(st)", &name, &usage)) > 0) {
1971 if (usage == UINT64_MAX) {
1972 log_info("Removed image '%s'", name);
1973 total = UINT64_MAX;
1974 } else {
1975 log_info("Removed image '%s'. Freed exclusive disk space: %s",
1976 name, FORMAT_BYTES(usage));
1977 if (total != UINT64_MAX)
1978 total += usage;
1979 }
1980 c++;
1981 }
1982
1983 r = sd_bus_message_exit_container(reply);
1984 if (r < 0)
1985 return bus_log_parse_error(r);
1986
1987 if (total == UINT64_MAX)
1988 log_info("Removed %u images in total.", c);
1989 else
1990 log_info("Removed %u images in total. Total freed exclusive disk space: %s.",
1991 c, FORMAT_BYTES(total));
1992
1993 return 0;
1994}
1995
1996static int chainload_importctl(int argc, char *argv[]) {
1997 int r;
1998
1999 if (!arg_quiet)
2000 log_notice("The 'machinectl %1$s' command has been replaced by 'importctl -m %1$s'. Redirecting invocation.", argv[optind]);
2001
2002 _cleanup_strv_free_ char **c =
2003 strv_new("importctl", "--class=machine");
2004 if (!c)
2005 return log_oom();
2006
2007 if (FLAGS_SET(arg_pager_flags, PAGER_DISABLE))
2008 if (strv_extend(&c, "--no-pager") < 0)
2009 return log_oom();
2010 if (!arg_legend)
2011 if (strv_extend(&c, "--no-legend") < 0)
2012 return log_oom();
2013 if (arg_read_only)
2014 if (strv_extend(&c, "--read-only") < 0)
2015 return log_oom();
2016 if (arg_force)
2017 if (strv_extend(&c, "--force") < 0)
2018 return log_oom();
2019 if (arg_quiet)
2020 if (strv_extend(&c, "--quiet") < 0)
2021 return log_oom();
2022 if (!arg_ask_password)
2023 if (strv_extend(&c, "--no-ask-password") < 0)
2024 return log_oom();
2025 if (strv_extend_many(&c, "--verify", import_verify_to_string(arg_verify)) < 0)
2026 return log_oom();
2027 if (arg_format)
2028 if (strv_extend_many(&c, "--format", arg_format) < 0)
2029 return log_oom();
2030
2031 if (strv_extend_strv(&c, argv + optind, /* filter_duplicates= */ false) < 0)
2032 return log_oom();
2033
2034 if (DEBUG_LOGGING) {
2035 _cleanup_free_ char *joined = strv_join(c, " ");
2036 log_debug("Chainloading: %s", joined);
2037 }
2038
2039 r = invoke_callout_binary(BINDIR "/importctl", c);
2040 return log_error_errno(r, "Failed to invoke 'importctl': %m");
2041}
2042
2043static int help(int argc, char *argv[], void *userdata) {
2044 _cleanup_free_ char *link = NULL;
2045 int r;
2046
2047 pager_open(arg_pager_flags);
2048
2049 r = terminal_urlify_man("machinectl", "1", &link);
2050 if (r < 0)
2051 return log_oom();
2052
2053 printf("%1$s [OPTIONS...] COMMAND ...\n\n"
2054 "%5$sSend control commands to or query the virtual machine and container%6$s\n"
2055 "%5$sregistration manager.%6$s\n"
2056 "\n%3$sMachine Commands:%4$s\n"
2057 " list List running VMs and containers\n"
2058 " status NAME... Show VM/container details\n"
2059 " show [NAME...] Show properties of one or more VMs/containers\n"
2060 " start NAME... Start container as a service\n"
2061 " login [NAME] Get a login prompt in a container or on the\n"
2062 " local host\n"
2063 " shell [[USER@]NAME [COMMAND...]]\n"
2064 " Invoke a shell (or other command) in a container\n"
2065 " or on the local host\n"
2066 " enable NAME... Enable automatic container start at boot\n"
2067 " disable NAME... Disable automatic container start at boot\n"
2068 " poweroff NAME... Power off one or more containers\n"
2069 " reboot NAME... Reboot one or more containers\n"
2070 " terminate NAME... Terminate one or more VMs/containers\n"
2071 " kill NAME... Send signal to processes of a VM/container\n"
2072 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2073 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2074 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n"
2075 "\n%3$sImage Commands:%4$s\n"
2076 " list-images Show available container and VM images\n"
2077 " image-status [NAME...] Show image details\n"
2078 " show-image [NAME...] Show properties of image\n"
2079 " edit NAME|FILE... Edit settings of one or more VMs/containers\n"
2080 " cat NAME|FILE... Show settings of one or more VMs/containers\n"
2081 " clone NAME NAME Clone an image\n"
2082 " rename NAME NAME Rename an image\n"
2083 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2084 " remove NAME... Remove an image\n"
2085 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n"
2086 " clean Remove hidden (or all) images\n"
2087 "\n%3$sOptions:%4$s\n"
2088 " -h --help Show this help\n"
2089 " --version Show package version\n"
2090 " --no-pager Do not pipe output into a pager\n"
2091 " --no-legend Do not show the headers and footers\n"
2092 " --no-ask-password Do not ask for system passwords\n"
2093 " -H --host=[USER@]HOST Operate on remote host\n"
2094 " -M --machine=CONTAINER Operate on local container\n"
2095 " -p --property=NAME Show only properties by this name\n"
2096 " --value When showing properties, only print the value\n"
2097 " -P NAME Equivalent to --value --property=NAME\n"
2098 " -q --quiet Suppress output\n"
2099 " -a --all Show all properties, including empty ones\n"
2100 " -l --full Do not ellipsize output\n"
2101 " --kill-whom=WHOM Whom to send signal to\n"
2102 " -s --signal=SIGNAL Which signal to send\n"
2103 " --uid=USER Specify user ID to invoke shell as\n"
2104 " -E --setenv=VAR[=VALUE] Add an environment variable for shell\n"
2105 " --read-only Create read-only bind mount or clone\n"
2106 " --mkdir Create directory before bind mounting, if missing\n"
2107 " -n --lines=INTEGER Number of journal entries to show\n"
2108 " --max-addresses=INTEGER Number of internet addresses to show at most\n"
2109 " -o --output=STRING Change journal output mode (short, short-precise,\n"
2110 " short-iso, short-iso-precise, short-full,\n"
2111 " short-monotonic, short-unix, short-delta,\n"
2112 " json, json-pretty, json-sse, json-seq, cat,\n"
2113 " verbose, export, with-unit)\n"
2114 " --force Replace target file when copying, if necessary\n"
2115 " --now Start or power off container after enabling or\n"
2116 " disabling it\n"
2117 " --runner=RUNNER Select between nspawn and vmspawn as the runner\n"
2118 " -V Short for --runner=vmspawn\n"
2119 "\nSee the %2$s for details.\n",
2120 program_invocation_short_name,
2121 link,
2122 ansi_underline(),
2123 ansi_normal(),
2124 ansi_highlight(),
2125 ansi_normal());
2126
2127 return 0;
2128}
2129
2130static int parse_argv(int argc, char *argv[]) {
2131
2132 enum {
2133 ARG_VERSION = 0x100,
2134 ARG_NO_PAGER,
2135 ARG_NO_LEGEND,
2136 ARG_VALUE,
2137 ARG_KILL_WHOM,
2138 ARG_READ_ONLY,
2139 ARG_MKDIR,
2140 ARG_NO_ASK_PASSWORD,
2141 ARG_VERIFY,
2142 ARG_RUNNER,
2143 ARG_NOW,
2144 ARG_FORCE,
2145 ARG_FORMAT,
2146 ARG_UID,
2147 ARG_MAX_ADDRESSES,
2148 };
2149
2150 static const struct option options[] = {
2151 { "help", no_argument, NULL, 'h' },
2152 { "version", no_argument, NULL, ARG_VERSION },
2153 { "property", required_argument, NULL, 'p' },
2154 { "value", no_argument, NULL, ARG_VALUE },
2155 { "all", no_argument, NULL, 'a' },
2156 { "full", no_argument, NULL, 'l' },
2157 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2158 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2159 { "kill-whom", required_argument, NULL, ARG_KILL_WHOM },
2160 { "signal", required_argument, NULL, 's' },
2161 { "host", required_argument, NULL, 'H' },
2162 { "machine", required_argument, NULL, 'M' },
2163 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2164 { "mkdir", no_argument, NULL, ARG_MKDIR },
2165 { "quiet", no_argument, NULL, 'q' },
2166 { "lines", required_argument, NULL, 'n' },
2167 { "output", required_argument, NULL, 'o' },
2168 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
2169 { "verify", required_argument, NULL, ARG_VERIFY },
2170 { "runner", required_argument, NULL, ARG_RUNNER },
2171 { "now", no_argument, NULL, ARG_NOW },
2172 { "force", no_argument, NULL, ARG_FORCE },
2173 { "format", required_argument, NULL, ARG_FORMAT },
2174 { "uid", required_argument, NULL, ARG_UID },
2175 { "setenv", required_argument, NULL, 'E' },
2176 { "max-addresses", required_argument, NULL, ARG_MAX_ADDRESSES },
2177 {}
2178 };
2179
2180 bool reorder = false;
2181 int c, r, shell = -1;
2182
2183 assert(argc >= 0);
2184 assert(argv);
2185
2186 /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
2187 * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
2188 optind = 0;
2189
2190 for (;;) {
2191 static const char option_string[] = "-hp:P:als:H:M:qn:o:E:V";
2192
2193 c = getopt_long(argc, argv, option_string + reorder, options, NULL);
2194 if (c < 0)
2195 break;
2196
2197 switch (c) {
2198
2199 case 1: /* getopt_long() returns 1 if "-" was the first character of the option string, and a
2200 * non-option argument was discovered. */
2201
2202 assert(!reorder);
2203
2204 /* We generally are fine with the fact that getopt_long() reorders the command line, and looks
2205 * for switches after the main verb. However, for "shell" we really don't want that, since we
2206 * want that switches specified after the machine name are passed to the program to execute,
2207 * and not processed by us. To make this possible, we'll first invoke getopt_long() with
2208 * reordering disabled (i.e. with the "-" prefix in the option string), looking for the first
2209 * non-option parameter. If it's the verb "shell" we remember its position and continue
2210 * processing options. In this case, as soon as we hit the next non-option argument we found
2211 * the machine name, and stop further processing. If the first non-option argument is any other
2212 * verb than "shell" we switch to normal reordering mode and continue processing arguments
2213 * normally. */
2214
2215 if (shell >= 0) {
2216 /* If we already found the "shell" verb on the command line, and now found the next
2217 * non-option argument, then this is the machine name and we should stop processing
2218 * further arguments. */
2219 optind--; /* don't process this argument, go one step back */
2220 goto done;
2221 }
2222 if (streq(optarg, "shell"))
2223 /* Remember the position of the "shell" verb, and continue processing normally. */
2224 shell = optind - 1;
2225 else {
2226 int saved_optind;
2227
2228 /* OK, this is some other verb. In this case, turn on reordering again, and continue
2229 * processing normally. */
2230 reorder = true;
2231
2232 /* We changed the option string. getopt_long() only looks at it again if we invoke it
2233 * at least once with a reset option index. Hence, let's reset the option index here,
2234 * then invoke getopt_long() again (ignoring what it has to say, after all we most
2235 * likely already processed it), and the bump the option index so that we read the
2236 * intended argument again. */
2237 saved_optind = optind;
2238 optind = 0;
2239 (void) getopt_long(argc, argv, option_string + reorder, options, NULL);
2240 optind = saved_optind - 1; /* go one step back, process this argument again */
2241 }
2242
2243 break;
2244
2245 case 'h':
2246 return help(0, NULL, NULL);
2247
2248 case ARG_VERSION:
2249 return version();
2250
2251 case 'p':
2252 case 'P':
2253 r = strv_extend(&arg_property, optarg);
2254 if (r < 0)
2255 return log_oom();
2256
2257 /* If the user asked for a particular property, show it to them, even if empty. */
2258 SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY, true);
2259
2260 if (c == 'p')
2261 break;
2262 _fallthrough_;
2263
2264 case ARG_VALUE:
2265 SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_ONLY_VALUE, true);
2266 break;
2267
2268 case 'a':
2269 SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY, true);
2270 arg_all = true;
2271 break;
2272
2273 case 'l':
2274 arg_full = true;
2275 break;
2276
2277 case 'n':
2278 if (safe_atou(optarg, &arg_lines) < 0)
2279 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2280 "Failed to parse lines '%s'", optarg);
2281 break;
2282
2283 case 'o':
2284 if (streq(optarg, "help")) {
2285 DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX);
2286 return 0;
2287 }
2288
2289 r = output_mode_from_string(optarg);
2290 if (r < 0)
2291 return log_error_errno(r, "Unknown output '%s'.", optarg);
2292 arg_output = r;
2293
2294 if (OUTPUT_MODE_IS_JSON(arg_output))
2295 arg_legend = false;
2296 break;
2297
2298 case ARG_NO_PAGER:
2299 arg_pager_flags |= PAGER_DISABLE;
2300 break;
2301
2302 case ARG_NO_LEGEND:
2303 arg_legend = false;
2304 break;
2305
2306 case ARG_KILL_WHOM:
2307 arg_kill_whom = optarg;
2308 break;
2309
2310 case 's':
2311 r = parse_signal_argument(optarg, &arg_signal);
2312 if (r <= 0)
2313 return r;
2314 break;
2315
2316 case ARG_NO_ASK_PASSWORD:
2317 arg_ask_password = false;
2318 break;
2319
2320 case 'H':
2321 arg_transport = BUS_TRANSPORT_REMOTE;
2322 arg_host = optarg;
2323 break;
2324
2325 case 'M':
2326 arg_transport = BUS_TRANSPORT_MACHINE;
2327 arg_host = optarg;
2328 break;
2329
2330 case ARG_READ_ONLY:
2331 arg_read_only = true;
2332 break;
2333
2334 case ARG_MKDIR:
2335 arg_mkdir = true;
2336 break;
2337
2338 case 'q':
2339 arg_quiet = true;
2340 break;
2341
2342 case ARG_VERIFY:
2343 if (streq(optarg, "help")) {
2344 DUMP_STRING_TABLE(import_verify, ImportVerify, _IMPORT_VERIFY_MAX);
2345 return 0;
2346 }
2347
2348 r = import_verify_from_string(optarg);
2349 if (r < 0)
2350 return log_error_errno(r, "Failed to parse --verify= setting: %s", optarg);
2351 arg_verify = r;
2352 break;
2353
2354 case 'V':
2355 arg_runner = RUNNER_VMSPAWN;
2356 break;
2357
2358 case ARG_RUNNER:
2359 r = machine_runner_from_string(optarg);
2360 if (r < 0)
2361 return log_error_errno(r, "Failed to parse --runner= setting: %s", optarg);
2362
2363 arg_runner = r;
2364 break;
2365
2366 case ARG_NOW:
2367 arg_now = true;
2368 break;
2369
2370 case ARG_FORCE:
2371 arg_force = true;
2372 break;
2373
2374 case ARG_FORMAT:
2375 if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2", "zstd"))
2376 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2377 "Unknown format: %s", optarg);
2378
2379 arg_format = optarg;
2380 break;
2381
2382 case ARG_UID:
2383 arg_uid = optarg;
2384 break;
2385
2386 case 'E':
2387 r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg);
2388 if (r < 0)
2389 return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
2390 break;
2391
2392 case ARG_MAX_ADDRESSES:
2393 if (streq(optarg, "all"))
2394 arg_max_addresses = UINT_MAX;
2395 else if (safe_atou(optarg, &arg_max_addresses) < 0)
2396 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2397 "Invalid number of addresses: %s", optarg);
2398 break;
2399
2400 case '?':
2401 return -EINVAL;
2402
2403 default:
2404 assert_not_reached();
2405 }
2406 }
2407
2408done:
2409 if (shell >= 0) {
2410 char *t;
2411
2412 /* We found the "shell" verb while processing the argument list. Since we turned off reordering of the
2413 * argument list initially let's readjust it now, and move the "shell" verb to the back. */
2414
2415 optind -= 1; /* place the option index where the "shell" verb will be placed */
2416
2417 t = argv[shell];
2418 for (int i = shell; i < optind; i++)
2419 argv[i] = argv[i+1];
2420 argv[optind] = t;
2421 }
2422
2423 return 1;
2424}
2425
2426static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2427
2428 static const Verb verbs[] = {
2429 { "help", VERB_ANY, VERB_ANY, 0, help },
2430 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2431 { "list-images", VERB_ANY, 1, 0, list_images },
2432 { "status", 2, VERB_ANY, 0, show_machine },
2433 { "image-status", VERB_ANY, VERB_ANY, 0, show_image },
2434 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2435 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2436 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2437 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2438 { "restart", 2, VERB_ANY, 0, reboot_machine }, /* Convenience alias */
2439 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2440 { "stop", 2, VERB_ANY, 0, poweroff_machine }, /* Convenience alias */
2441 { "kill", 2, VERB_ANY, 0, kill_machine },
2442 { "login", VERB_ANY, 2, 0, login_machine },
2443 { "shell", VERB_ANY, VERB_ANY, 0, shell_machine },
2444 { "bind", 3, 4, 0, bind_mount },
2445 { "edit", 2, VERB_ANY, 0, edit_settings },
2446 { "cat", 2, VERB_ANY, 0, cat_settings },
2447 { "copy-to", 3, 4, 0, copy_files },
2448 { "copy-from", 3, 4, 0, copy_files },
2449 { "remove", 2, VERB_ANY, 0, remove_image },
2450 { "rename", 3, 3, 0, rename_image },
2451 { "clone", 3, 3, 0, clone_image },
2452 { "read-only", 2, 3, 0, read_only_image },
2453 { "start", 2, VERB_ANY, 0, start_machine },
2454 { "enable", 2, VERB_ANY, 0, enable_machine },
2455 { "disable", 2, VERB_ANY, 0, enable_machine },
2456 { "set-limit", 2, 3, 0, set_limit },
2457 { "clean", VERB_ANY, 1, 0, clean_images },
2458 {}
2459 };
2460
2461 return dispatch_verb(argc, argv, verbs, bus);
2462}
2463
2464static int run(int argc, char *argv[]) {
2465 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2466 int r;
2467
2468 setlocale(LC_ALL, "");
2469 log_setup();
2470
2471 r = parse_argv(argc, argv);
2472 if (r <= 0)
2473 return r;
2474
2475 journal_browse_prepare();
2476
2477 if (STRPTR_IN_SET(argv[optind],
2478 "import-tar", "import-raw", "import-fs",
2479 "export-tar", "export-raw",
2480 "pull-tar", "pull-raw",
2481 "list-transfers", "cancel-transfer"))
2482 return chainload_importctl(argc, argv);
2483
2484 r = bus_connect_transport(arg_transport, arg_host, RUNTIME_SCOPE_SYSTEM, &bus);
2485 if (r < 0)
2486 return bus_log_connect_error(r, arg_transport, RUNTIME_SCOPE_SYSTEM);
2487
2488 (void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
2489
2490 return machinectl_main(argc, argv, bus);
2491}
2492
2493DEFINE_MAIN_FUNCTION(run);