]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machinectl.c
io.systemd.Unit.List fix context/runtime split (#38172)
[thirdparty/systemd.git] / src / machine / machinectl.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
1ee306e1 2
1ee306e1 3#include <getopt.h>
64943cac 4#include <locale.h>
f48e75cb 5#include <net/if.h>
3f6fd1ba
LP
6#include <sys/socket.h>
7#include <unistd.h>
eef46c37 8
a1da8583 9#include "sd-bus.h"
64943cac
DDM
10#include "sd-event.h"
11#include "sd-journal.h"
3f6fd1ba 12
b5efdb8a 13#include "alloc-util.h"
a8c3ed5f 14#include "ask-password-agent.h"
d6b4d1c7 15#include "build.h"
8ce438bb 16#include "build-path.h"
68ce459f 17#include "bus-common-errors.h"
a1da8583 18#include "bus-error.h"
9b71e4ab 19#include "bus-locator.h"
807542be 20#include "bus-map-properties.h"
ab33edb0 21#include "bus-message-util.h"
9176326b 22#include "bus-print-properties.h"
25b1d72d 23#include "bus-unit-procs.h"
a0e27019 24#include "bus-unit-util.h"
1e35e81b 25#include "bus-util.h"
e45c81b8 26#include "bus-wait-for-jobs.h"
1ee306e1 27#include "cgroup-show.h"
9d127096 28#include "cgroup-util.h"
1ed35a0d 29#include "edit-util.h"
3f6fd1ba 30#include "env-util.h"
868258cf 31#include "format-ifname.h"
930a08da 32#include "format-table.h"
ca1daebd 33#include "format-util.h"
3f6fd1ba 34#include "hostname-util.h"
3d7415f4 35#include "import-util.h"
309a747f 36#include "in-addr-util.h"
76d62b63 37#include "label-util.h"
3f6fd1ba
LP
38#include "log.h"
39#include "logs-show.h"
ae03e1a9 40#include "machine-dbus.h"
f66da783 41#include "main-func.h"
d8b4d14d 42#include "nulstr-util.h"
ff03ac87 43#include "osc-context.h"
1e35e81b 44#include "output-mode.h"
3f6fd1ba 45#include "pager.h"
86beb213 46#include "parse-argument.h"
6bedfcbb 47#include "parse-util.h"
3f6fd1ba 48#include "path-util.h"
ca1daebd 49#include "pidref.h"
1b919ca4 50#include "polkit-agent.h"
294bf0c3 51#include "pretty-print.h"
0b452006 52#include "process-util.h"
3f6fd1ba 53#include "ptyfwd.h"
64943cac 54#include "runtime-scope.h"
9e29521e 55#include "stdio-util.h"
5c828e66 56#include "string-table.h"
64943cac 57#include "string-util.h"
3f6fd1ba
LP
58#include "strv.h"
59#include "terminal-util.h"
64943cac 60#include "time-util.h"
3f6fd1ba 61#include "unit-name.h"
3f6fd1ba 62#include "verbs.h"
07b0b339 63
f82dcc3f
SL
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] = {
093b0255 72 [RUNNER_NSPAWN] = "nspawn",
f82dcc3f
SL
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] = {
093b0255 79 [RUNNER_NSPAWN] = "systemd-nspawn",
f82dcc3f
SL
80 [RUNNER_VMSPAWN] = "systemd-vmspawn",
81};
82
1ee306e1
LP
83static char **arg_property = NULL;
84static bool arg_all = false;
255b1fc8 85static BusPrintPropertyFlags arg_print_flags = 0;
1ee306e1 86static bool arg_full = false;
0221d68a 87static PagerFlags arg_pager_flags = 0;
e56056e9 88static bool arg_legend = true;
4ccde410 89static const char *arg_kill_whom = NULL;
1ee306e1 90static int arg_signal = SIGTERM;
d21ed1ea 91static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
f66da783 92static const char *arg_host = NULL;
785890ac
LP
93static bool arg_read_only = false;
94static bool arg_mkdir = false;
d8f52ed2 95static bool arg_quiet = false;
acf97e21 96static bool arg_ask_password = true;
8b0cc9a3
LP
97static unsigned arg_lines = 10;
98static OutputMode arg_output = OUTPUT_SHORT;
c2434a61 99static bool arg_now = false;
3d7415f4 100static bool arg_force = false;
6e18cc9f 101static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
f82dcc3f 102static MachineRunner arg_runner = RUNNER_NSPAWN;
587fec42 103static const char* arg_format = NULL;
c454426c
LP
104static const char *arg_uid = NULL;
105static char **arg_setenv = NULL;
99b8149a 106static unsigned arg_max_addresses = 1;
07b0b339 107
f66da783
YW
108STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
109STATIC_DESTRUCTOR_REGISTER(arg_setenv, strv_freep);
110
8b0cc9a3
LP
111static OutputFlags get_output_flags(void) {
112 return
255b1fc8 113 FLAGS_SET(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) * OUTPUT_SHOW_ALL |
459b9f9f 114 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
40c9fe4c 115 colors_enabled() * OUTPUT_COLOR |
8b0cc9a3
LP
116 !arg_quiet * OUTPUT_WARN_CUTOFF;
117}
118
9153b02b 119static int call_get_os_release(sd_bus *bus, const char *method, const char *name, const char *query, ...) {
930a08da 120 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
e3fc2b02 121 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
07b0b339 122 va_list ap;
e3fc2b02
SK
123 int r;
124
125 assert(bus);
7312b039 126 assert(method);
e3fc2b02 127 assert(name);
07b0b339
SK
128 assert(query);
129
14456f76 130 r = bus_call_method(bus, bus_machine_mgr, method, &error, &reply, "s", name);
e3fc2b02 131 if (r < 0)
930a08da 132 return log_debug_errno(r, "Failed to call '%s()': %s", method, bus_error_message(&error, r));
e3fc2b02
SK
133
134 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
135 if (r < 0)
136 return bus_log_parse_error(r);
137
7312b039
MY
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;
e3fc2b02 147 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
7312b039
MY
148 size_t c = 0;
149 NULSTR_FOREACH(i, query) {
150 if (streq(i, k)) {
151 res[c] = v;
07b0b339
SK
152 break;
153 }
7312b039 154 c++;
07b0b339 155 }
e3fc2b02
SK
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
237bbec1 164 r = 0;
7312b039 165
07b0b339 166 va_start(ap, query);
7312b039
MY
167 FOREACH_ARRAY(i, res, n_fields) {
168 r = strdup_to(va_arg(ap, char**), *i);
169 if (r < 0)
170 break;
07b0b339
SK
171 }
172 va_end(ap);
e3fc2b02 173
237bbec1 174 return r;
e3fc2b02
SK
175}
176
fc6eb08e
LP
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) {
56159e0d 184
4afd3348
LP
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;
930a08da 187 _cleanup_free_ char *addresses = NULL;
930a08da 188 unsigned n = 0;
1ee306e1
LP
189 int r;
190
56159e0d 191 assert(bus);
930a08da
LP
192 assert(name);
193 assert(prefix);
194 assert(prefix2);
1ee306e1 195
14456f76 196 r = bus_call_method(bus, bus_machine_mgr, "GetMachineAddresses", NULL, &reply, "s", name);
930a08da
LP
197 if (r < 0)
198 return log_debug_errno(r, "Could not get addresses: %s", bus_error_message(&error, r));
1ee306e1 199
930a08da
LP
200 addresses = strdup(prefix);
201 if (!addresses)
202 return log_oom();
930a08da
LP
203
204 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
a1da8583 205 if (r < 0)
5b30bef8 206 return bus_log_parse_error(r);
0b63e278 207
7312b039 208 prefix = "";
930a08da
LP
209 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
210 int family;
211 const void *a;
212 size_t sz;
071e522e 213 char buf_ifi[1 + DECIMAL_STR_MAX(int)] = "";
e3fc2b02 214
930a08da
LP
215 r = sd_bus_message_read(reply, "i", &family);
216 if (r < 0)
217 return bus_log_parse_error(r);
4b241916 218
930a08da
LP
219 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
220 if (r < 0)
221 return bus_log_parse_error(r);
0b63e278 222
fc6eb08e
LP
223 if (family == AF_INET6 && ifi > 0)
224 xsprintf(buf_ifi, "%%%i", ifi);
0b63e278 225
071e522e 226 if (!strextend(&addresses, prefix, IN_ADDR_TO_STRING(family, a), buf_ifi))
fc6eb08e 227 return log_oom();
0b63e278 228
930a08da
LP
229 r = sd_bus_message_exit_container(reply);
230 if (r < 0)
231 return bus_log_parse_error(r);
0b63e278 232
930a08da 233 prefix = prefix2;
e3fc2b02 234
930a08da 235 n++;
e3fc2b02 236 }
930a08da
LP
237 if (r < 0)
238 return bus_log_parse_error(r);
a1da8583
TG
239
240 r = sd_bus_message_exit_container(reply);
930a08da
LP
241 if (r < 0)
242 return bus_log_parse_error(r);
243
930a08da
LP
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
2413a0fa 254 if (!table_isempty(table) || OUTPUT_MODE_IS_JSON(arg_output)) {
ef1e0b9a 255 r = table_set_sort(table, (size_t) 0);
930a08da 256 if (r < 0)
df83eb54 257 return table_log_sort_error(r);
930a08da
LP
258
259 table_set_header(table, arg_legend);
260
ad9d139e 261 if (OUTPUT_MODE_IS_JSON(arg_output))
309a747f 262 r = table_print_json(table, NULL, output_mode_to_json_format_flags(arg_output) | SD_JSON_FORMAT_COLOR_AUTO);
ad9d139e
LP
263 else
264 r = table_print(table, NULL);
930a08da 265 if (r < 0)
4b6607d9 266 return table_log_print_error(r);
07b0b339 267 }
0b63e278 268
f9b1947f 269 if (arg_legend) {
2413a0fa 270 if (table_isempty(table))
930a08da 271 printf("No %s.\n", word);
2413a0fa
MY
272 else
273 printf("\n%zu %s listed.\n", table_get_rows(table) - 1, word);
f9b1947f 274 }
1ee306e1 275
930a08da 276 return 0;
1ee306e1
LP
277}
278
930a08da 279static int list_machines(int argc, char *argv[], void *userdata) {
930a08da
LP
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;
99534007 283 sd_bus *bus = ASSERT_PTR(userdata);
930a08da
LP
284 int r;
285
384c2c32 286 pager_open(arg_pager_flags);
930a08da 287
14456f76 288 r = bus_call_method(bus, bus_machine_mgr, "ListMachines", &error, &reply, NULL);
930a08da
LP
289 if (r < 0)
290 return log_error_errno(r, "Could not get machines: %s", bus_error_message(&error, r));
291
99b8149a
ZJS
292 table = table_new("machine", "class", "service", "os", "version",
293 arg_max_addresses > 0 ? "addresses" : NULL);
930a08da
LP
294 if (!table)
295 return log_oom();
296
c8b62cf6 297 table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
99b8149a 298 if (!arg_full && arg_max_addresses > 0 && arg_max_addresses < UINT_MAX)
fc6eb08e
LP
299 table_set_cell_height_max(table, arg_max_addresses);
300
a65e34cc
YW
301 if (arg_full)
302 table_set_width(table, 0);
303
930a08da
LP
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;
31d99bd1 310 const char *name, *class, *service;
930a08da 311
31d99bd1 312 r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, NULL);
930a08da
LP
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
930a08da 330 r = table_add_many(table,
fc6eb08e
LP
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),
99b8149a 335 TABLE_STRING, empty_to_null(version_id));
930a08da 336 if (r < 0)
679c7c7a 337 return table_log_add_error(r);
99b8149a
ZJS
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 }
930a08da 347 }
cd61c3bf 348
930a08da
LP
349 r = sd_bus_message_exit_container(reply);
350 if (r < 0)
351 return bus_log_parse_error(r);
cd61c3bf 352
930a08da 353 return show_table(table, "machines");
cd61c3bf
LP
354}
355
56159e0d 356static int list_images(int argc, char *argv[], void *userdata) {
cd61c3bf 357
4afd3348 358 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
4afd3348 359 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
930a08da 360 _cleanup_(table_unrefp) Table *table = NULL;
99534007 361 sd_bus *bus = ASSERT_PTR(userdata);
930a08da 362 int r;
cd61c3bf 363
384c2c32 364 pager_open(arg_pager_flags);
cd61c3bf 365
14456f76 366 r = bus_call_method(bus, bus_machine_mgr, "ListImages", &error, &reply, NULL);
930a08da
LP
367 if (r < 0)
368 return log_error_errno(r, "Could not get images: %s", bus_error_message(&error, r));
369
9969b542 370 table = table_new("name", "type", "ro", "usage", "created", "modified");
930a08da
LP
371 if (!table)
372 return log_oom();
373
a65e34cc
YW
374 if (arg_full)
375 table_set_width(table, 0);
376
930a08da 377 (void) table_set_align_percent(table, TABLE_HEADER_CELL(3), 100);
cd61c3bf 378
b6b18498 379 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
cd61c3bf
LP
380 if (r < 0)
381 return bus_log_parse_error(r);
382
930a08da 383 for (;;) {
930a08da 384 uint64_t crtime, mtime, size;
31d99bd1 385 const char *name, *type;
930a08da
LP
386 int ro_int;
387
31d99bd1 388 r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &ro_int, &crtime, &mtime, &size, NULL);
930a08da
LP
389 if (r < 0)
390 return bus_log_parse_error(r);
391 if (r == 0)
392 break;
cd61c3bf
LP
393
394 if (name[0] == '.' && !arg_all)
395 continue;
396
930a08da
LP
397 r = table_add_many(table,
398 TABLE_STRING, name,
679c7c7a
YW
399 TABLE_STRING, type,
400 TABLE_BOOLEAN, ro_int,
401 TABLE_SET_COLOR, ro_int ? ansi_highlight_red() : NULL,
930a08da
LP
402 TABLE_SIZE, size,
403 TABLE_TIMESTAMP, crtime,
404 TABLE_TIMESTAMP, mtime);
405 if (r < 0)
679c7c7a 406 return table_log_add_error(r);
cd61c3bf 407 }
cd61c3bf
LP
408
409 r = sd_bus_message_exit_container(reply);
410 if (r < 0)
411 return bus_log_parse_error(r);
412
930a08da 413 return show_table(table, "images");
cd61c3bf
LP
414}
415
d5feeb37
LP
416static int show_unit_cgroup(
417 sd_bus *bus,
418 const char *unit,
419 const char *subgroup,
420 pid_t leader) {
421
bc06be75 422 _cleanup_free_ char *cgroup = NULL;
4afd3348 423 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
d5feeb37 424 OutputFlags extra_flags = 0;
8b0cc9a3 425 int r;
aa1936ea
LP
426
427 assert(bus);
428 assert(unit);
429
bc06be75 430 r = show_cgroup_get_unit_path_and_warn(bus, unit, &cgroup);
a0e27019 431 if (r < 0)
bc06be75 432 return r;
aa1936ea 433
a0e27019 434 if (isempty(cgroup))
9d127096
LP
435 return 0;
436
d5feeb37
LP
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
3ae6e646 445 unsigned c = MAX(LESS_BY(columns(), 18U), 10U);
d5feeb37 446 r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags() | extra_flags, &error);
a0e27019
LP
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
c3f90077 454 if (cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
a0e27019
LP
455 return 0;
456
0ff308c8 457 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags());
a0e27019
LP
458 } else if (r < 0)
459 return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r));
460
aa1936ea
LP
461 return 0;
462}
463
9153b02b 464static int print_os_release(sd_bus *bus, const char *method, const char *name, const char *prefix) {
e3fc2b02 465 _cleanup_free_ char *pretty = NULL;
717603e3
LP
466 int r;
467
468 assert(bus);
469 assert(name);
470 assert(prefix);
471
9153b02b 472 r = call_get_os_release(bus, method, name, "PRETTY_NAME\0", &pretty, NULL);
717603e3
LP
473 if (r < 0)
474 return r;
475
717603e3
LP
476 if (pretty)
477 printf("%s%s\n", prefix, pretty);
478
479 return 0;
480}
481
3401419b
LP
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
14456f76 491 r = bus_call_method(bus, bus_machine_mgr, "GetMachineUIDShift", &error, &reply, "s", name);
3401419b
LP
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
a13fda9e 502 printf("\tID Shift: %" PRIu32 "\n", shift);
3401419b
LP
503 return 0;
504}
505
1ee306e1 506typedef struct MachineStatusInfo {
f37f8a61 507 const char *name;
1ee306e1 508 sd_id128_t id;
f37f8a61
YW
509 const char *class;
510 const char *service;
511 const char *unit;
d5feeb37 512 const char *subgroup;
f37f8a61 513 const char *root_directory;
1ee306e1 514 pid_t leader;
ca1daebd
LP
515 uint64_t leader_pidfdid;
516 pid_t supervisor;
517 uint64_t supervisor_pidfdid;
8b0cc9a3 518 struct dual_timestamp timestamp;
f48e75cb 519 int *netif;
68e16e9c 520 size_t n_netif;
276d2001 521 uid_t uid;
1ee306e1
LP
522} MachineStatusInfo;
523
ca1daebd
LP
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);
e7e55dbd 553 }
ca1daebd
LP
554
555 putchar('\n');
e7e55dbd
DH
556}
557
a1da8583 558static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
f843f85d 559 _cleanup_free_ char *addresses = NULL, *s1 = NULL, *s2 = NULL;
f48e75cb
LP
560 int ifi = -1;
561
56159e0d 562 assert(bus);
1ee306e1
LP
563 assert(i);
564
565 fputs(strna(i->name), stdout);
566
3bbaff3e 567 if (!sd_id128_is_null(i->id))
1ee306e1
LP
568 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
569 else
570 putchar('\n');
571
f843f85d
YW
572 s1 = strdup(strempty(FORMAT_TIMESTAMP_RELATIVE(i->timestamp.realtime)));
573 s2 = strdup(strempty(FORMAT_TIMESTAMP(i->timestamp.realtime)));
1ee306e1 574
f843f85d
YW
575 if (!isempty(s1))
576 printf("\t Since: %s; %s\n", strna(s2), s1);
577 else if (!isempty(s2))
1ee306e1
LP
578 printf("\t Since: %s\n", s2);
579
ca1daebd
LP
580 print_process_info("\t Leader", i->leader, i->leader_pidfdid);
581 print_process_info("\t Superv.", i->supervisor, i->supervisor_pidfdid);
1ee306e1
LP
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
276d2001
LP
593 if (i->uid != 0)
594 printf("\t UID: " UID_FMT "\n", i->uid);
595
1ee306e1
LP
596 if (i->root_directory)
597 printf("\t Root: %s\n", i->root_directory);
598
f48e75cb 599 if (i->n_netif > 0) {
f48e75cb
LP
600 fputs("\t Iface:", stdout);
601
8571210a 602 for (size_t c = 0; c < i->n_netif; c++) {
01afd0f7 603 char name[IF_NAMESIZE];
f48e75cb 604
01afd0f7 605 if (format_ifname(i->netif[c], name) >= 0) {
f48e75cb
LP
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
4527a83b 620 if (call_get_addresses(bus, i->name, ifi,
fc6eb08e 621 "\t Address: ", "\n\t ",
4527a83b
ZJS
622 &addresses) > 0) {
623 fputs(addresses, stdout);
7026a775 624 fputc('\n', stdout);
4527a83b 625 }
878cd7e9 626
9153b02b 627 print_os_release(bus, "GetMachineOSRelease", i->name, "\t OS: ");
717603e3 628
3401419b
LP
629 print_uid_shift(bus, i->name);
630
89f7c846
LP
631 if (i->unit) {
632 printf("\t Unit: %s\n", i->unit);
d5feeb37
LP
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);
8b0cc9a3 638
ece174c5 639 if (arg_transport == BUS_TRANSPORT_LOCAL)
8b0cc9a3
LP
640
641 show_journal_by_unit(
642 stdout,
643 i->unit,
8ac0810f 644 /* namespace = */ NULL,
8b0cc9a3 645 arg_output,
8ac0810f 646 /* n_columns = */ 0,
8b0cc9a3
LP
647 i->timestamp.monotonic,
648 arg_lines,
8b0cc9a3
LP
649 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
650 SD_JOURNAL_LOCAL_ONLY,
8ac0810f
YW
651 /* system_unit = */ true,
652 /* ellipsized = */ NULL);
1ee306e1
LP
653 }
654}
655
f48e75cb
LP
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;
e7e9b6bb
ZJS
666 if (r == 0)
667 return -EBADMSG;
f48e75cb
LP
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
fefdc04b 677static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
a6c61602 678
9f6eb1cd 679 static const struct bus_properties_map map[] = {
8b0cc9a3
LP
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) },
d5feeb37 684 { "Subgroup", "s", NULL, offsetof(MachineStatusInfo, subgroup) },
8b0cc9a3
LP
685 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
686 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
ca1daebd
LP
687 { "LeaderPIDFDId", "t", NULL, offsetof(MachineStatusInfo, leader_pidfdid) },
688 { "Supervisor", "u", NULL, offsetof(MachineStatusInfo, supervisor) },
689 { "SupervisorPIDFDId", "t", NULL, offsetof(MachineStatusInfo, supervisor_pidfdid) },
8b0cc9a3
LP
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 },
276d2001 694 { "UID", "u", NULL, offsetof(MachineStatusInfo, uid) },
9f6eb1cd
KS
695 {}
696 };
a6c61602 697
f9e0eefc 698 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f37f8a61 699 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
ca1daebd 700 _cleanup_(machine_status_info_done) MachineStatusInfo info = {};
a1da8583
TG
701 int r;
702
56159e0d
LP
703 assert(verb);
704 assert(bus);
1ee306e1
LP
705 assert(path);
706 assert(new_line);
707
9f6eb1cd
KS
708 r = bus_map_all_properties(bus,
709 "org.freedesktop.machine1",
710 path,
711 map,
a7e4861c 712 0,
f9e0eefc 713 &error,
f37f8a61 714 &m,
9f6eb1cd 715 &info);
f647962d 716 if (r < 0)
f9e0eefc 717 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
1ee306e1 718
1ee306e1
LP
719 if (*new_line)
720 printf("\n");
1ee306e1
LP
721 *new_line = true;
722
9f6eb1cd 723 print_machine_status_info(bus, &info);
1ee306e1 724
9f6eb1cd
KS
725 return r;
726}
1ee306e1 727
fefdc04b 728static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
9f6eb1cd 729 int r;
1ee306e1 730
56159e0d
LP
731 assert(bus);
732 assert(path);
733 assert(new_line);
734
9f6eb1cd
KS
735 if (*new_line)
736 printf("\n");
1ee306e1 737
9f6eb1cd 738 *new_line = true;
a1da8583 739
255b1fc8 740 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, NULL, arg_property, arg_print_flags, NULL);
a1da8583 741 if (r < 0)
da927ba9 742 log_error_errno(r, "Could not get properties: %m");
1ee306e1 743
a7893c6b 744 return r;
1ee306e1
LP
745}
746
fefdc04b 747static int show_machine(int argc, char *argv[], void *userdata) {
4afd3348 748 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
9f6eb1cd 749 bool properties, new_line = false;
99534007 750 sd_bus *bus = ASSERT_PTR(userdata);
8571210a 751 int r = 0;
1ee306e1 752
56159e0d 753 properties = !strstr(argv[0], "status");
1ee306e1 754
384c2c32 755 pager_open(arg_pager_flags);
1ee306e1 756
56159e0d 757 if (properties && argc <= 1) {
a1da8583 758
9f6eb1cd 759 /* If no argument is specified, inspect the manager
1ee306e1 760 * itself */
fefdc04b 761 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
8c841f21 762 if (r < 0)
9f6eb1cd 763 return r;
1ee306e1
LP
764 }
765
8571210a 766 for (int i = 1; i < argc; i++) {
efdaa92e 767 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1ee306e1
LP
768 const char *path = NULL;
769
14456f76 770 r = bus_call_method(bus, bus_machine_mgr, "GetMachine", &error, &reply, "s", argv[i]);
4ae25393 771 if (r < 0)
2a03b9ed 772 return log_error_errno(r, "Could not get path to machine: %s", bus_error_message(&error, r));
a1da8583
TG
773
774 r = sd_bus_message_read(reply, "o", &path);
5b30bef8
LP
775 if (r < 0)
776 return bus_log_parse_error(r);
1ee306e1 777
9f6eb1cd 778 if (properties)
fefdc04b 779 r = show_machine_properties(bus, path, &new_line);
9f6eb1cd 780 else
fefdc04b
LP
781 r = show_machine_info(argv[0], bus, path, &new_line);
782 }
783
784 return r;
785}
786
6ca7d231
LP
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
14456f76 792 r = bus_call_method(bus, bus_machine_mgr, "GetImageHostname", NULL, &reply, "s", name);
6ca7d231
LP
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;
8157cc0e 808 sd_id128_t id;
6ca7d231
LP
809 int r;
810
14456f76 811 r = bus_call_method(bus, bus_machine_mgr, "GetImageMachineID", NULL, &reply, "s", name);
6ca7d231
LP
812 if (r < 0)
813 return r;
814
8157cc0e 815 r = bus_message_read_id128(reply, &id);
6ca7d231
LP
816 if (r < 0)
817 return r;
818
6ca7d231
LP
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;
6ca7d231
LP
827 int r;
828
14456f76 829 r = bus_call_method(bus, bus_machine_mgr, "GetImageMachineInfo", NULL, &reply, "s", name);
6ca7d231
LP
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
fefdc04b 857typedef struct ImageStatusInfo {
f37f8a61
YW
858 const char *name;
859 const char *path;
860 const char *type;
861 bool read_only;
fefdc04b
LP
862 usec_t crtime;
863 usec_t mtime;
c19de711 864 uint64_t usage;
b6b18498 865 uint64_t limit;
c19de711 866 uint64_t usage_exclusive;
b6b18498 867 uint64_t limit_exclusive;
fefdc04b
LP
868} ImageStatusInfo;
869
870static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
fefdc04b
LP
871 assert(bus);
872 assert(i);
873
874 if (i->name) {
875 fputs(i->name, stdout);
876 putchar('\n');
877 }
878
9a14fb62 879 if (i->type)
fefdc04b
LP
880 printf("\t Type: %s\n", i->type);
881
882 if (i->path)
883 printf("\t Path: %s\n", i->path);
884
6ca7d231
LP
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
9153b02b
LP
889 print_os_release(bus, "GetImageOSRelease", i->name, "\t OS: ");
890
fefdc04b
LP
891 printf("\t RO: %s%s%s\n",
892 i->read_only ? ansi_highlight_red() : "",
893 i->read_only ? "read-only" : "writable",
1fc464f6 894 i->read_only ? ansi_normal() : "");
fefdc04b 895
0da36375 896 if (timestamp_is_set(i->crtime))
f843f85d
YW
897 printf("\t Created: %s; %s\n",
898 FORMAT_TIMESTAMP(i->crtime), FORMAT_TIMESTAMP_RELATIVE(i->crtime));
899
0da36375 900 if (timestamp_is_set(i->mtime))
f843f85d
YW
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 }
fefdc04b
LP
919}
920
160e3793 921static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
fefdc04b
LP
922
923 static const struct bus_properties_map map[] = {
b6b18498
LP
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) },
c19de711 930 { "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) },
b6b18498 931 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
c19de711 932 { "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) },
b6b18498 933 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
fefdc04b
LP
934 {}
935 };
936
f9e0eefc 937 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f37f8a61
YW
938 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
939 ImageStatusInfo info = {};
fefdc04b
LP
940 int r;
941
fefdc04b
LP
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,
a7e4861c 950 BUS_MAP_BOOLEAN_AS_BOOL,
f9e0eefc 951 &error,
f37f8a61 952 &m,
fefdc04b
LP
953 &info);
954 if (r < 0)
f9e0eefc 955 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
fefdc04b
LP
956
957 if (*new_line)
958 printf("\n");
959 *new_line = true;
960
961 print_image_status_info(bus, &info);
962
fefdc04b
LP
963 return r;
964}
965
160e3793 966typedef struct PoolStatusInfo {
f37f8a61 967 const char *path;
160e3793
LP
968 uint64_t usage;
969 uint64_t limit;
970} PoolStatusInfo;
971
972static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) {
160e3793
LP
973 if (i->path)
974 printf("\t Path: %s\n", i->path);
975
f843f85d
YW
976 if (i->usage != UINT64_MAX)
977 printf("\t Usage: %s\n", FORMAT_BYTES(i->usage));
160e3793 978
f843f85d
YW
979 if (i->limit != UINT64_MAX)
980 printf("\t Limit: %s\n", FORMAT_BYTES(i->limit));
160e3793
LP
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
f37f8a61 992 PoolStatusInfo info = {
f5fbe71d
YW
993 .usage = UINT64_MAX,
994 .limit = UINT64_MAX,
160e3793 995 };
f9e0eefc
LP
996
997 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f37f8a61 998 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
160e3793
LP
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,
a7e4861c 1007 0,
f9e0eefc 1008 &error,
f37f8a61 1009 &m,
160e3793
LP
1010 &info);
1011 if (r < 0)
f9e0eefc 1012 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
160e3793
LP
1013
1014 print_pool_status_info(bus, &info);
1015
160e3793
LP
1016 return 0;
1017}
1018
fefdc04b
LP
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
255b1fc8 1031 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, NULL, arg_property, arg_print_flags, NULL);
fefdc04b
LP
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) {
4afd3348 1039 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
fefdc04b 1040 bool properties, new_line = false;
99534007 1041 sd_bus *bus = ASSERT_PTR(userdata);
8571210a 1042 int r = 0;
fefdc04b 1043
fefdc04b
LP
1044 properties = !strstr(argv[0], "status");
1045
384c2c32 1046 pager_open(arg_pager_flags);
fefdc04b 1047
160e3793 1048 if (argc <= 1) {
fefdc04b
LP
1049
1050 /* If no argument is specified, inspect the manager
1051 * itself */
160e3793
LP
1052
1053 if (properties)
1054 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
1055 else
1056 r = show_pool_info(bus);
fefdc04b
LP
1057 if (r < 0)
1058 return r;
1059 }
1060
8571210a 1061 for (int i = 1; i < argc; i++) {
4b6ce580 1062 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
fefdc04b
LP
1063 const char *path = NULL;
1064
14456f76 1065 r = bus_call_method(bus, bus_machine_mgr, "GetImage", &error, &reply, "s", argv[i]);
4ae25393 1066 if (r < 0)
2a03b9ed 1067 return log_error_errno(r, "Could not get path to image: %s", bus_error_message(&error, r));
fefdc04b
LP
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
160e3793 1076 r = show_image_info(bus, path, &new_line);
1ee306e1
LP
1077 }
1078
9f6eb1cd 1079 return r;
1ee306e1
LP
1080}
1081
56159e0d 1082static int kill_machine(int argc, char *argv[], void *userdata) {
4afd3348 1083 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
99534007 1084 sd_bus *bus = ASSERT_PTR(userdata);
8571210a 1085 int r;
1ee306e1 1086
f3cf6167 1087 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1088
4ccde410
ZJS
1089 if (!arg_kill_whom)
1090 arg_kill_whom = "all";
1ee306e1 1091
8571210a 1092 for (int i = 1; i < argc; i++) {
14456f76 1093 r = bus_call_method(
56159e0d 1094 bus,
14456f76 1095 bus_machine_mgr,
56159e0d
LP
1096 "KillMachine",
1097 &error,
1098 NULL,
4ccde410 1099 "ssi", argv[i], arg_kill_whom, arg_signal);
4ae25393 1100 if (r < 0)
2a03b9ed 1101 return log_error_errno(r, "Could not kill machine: %s", bus_error_message(&error, r));
1ee306e1
LP
1102 }
1103
1104 return 0;
1105}
1106
56159e0d 1107static int reboot_machine(int argc, char *argv[], void *userdata) {
f82dcc3f
SL
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
4ccde410 1114 arg_kill_whom = "leader";
1dba654b 1115 arg_signal = SIGINT; /* sysvinit + systemd */
1ee306e1 1116
56159e0d 1117 return kill_machine(argc, argv, userdata);
1dba654b 1118}
1ee306e1 1119
56159e0d 1120static int poweroff_machine(int argc, char *argv[], void *userdata) {
4ccde410 1121 arg_kill_whom = "leader";
1dba654b 1122 arg_signal = SIGRTMIN+4; /* only systemd */
1ee306e1 1123
56159e0d 1124 return kill_machine(argc, argv, userdata);
1ee306e1
LP
1125}
1126
56159e0d 1127static int terminate_machine(int argc, char *argv[], void *userdata) {
4afd3348 1128 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
99534007 1129 sd_bus *bus = ASSERT_PTR(userdata);
8571210a 1130 int r;
923d8fd3 1131
f3cf6167 1132 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1133
8571210a 1134 for (int i = 1; i < argc; i++) {
14456f76 1135 r = bus_call_method(bus, bus_machine_mgr, "TerminateMachine", &error, NULL, "s", argv[i]);
4ae25393 1136 if (r < 0)
2a03b9ed 1137 return log_error_errno(r, "Could not terminate machine: %s", bus_error_message(&error, r));
923d8fd3
LP
1138 }
1139
1140 return 0;
1141}
1142
ae03e1a9
AW
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
0370612e 1150static int copy_files(int argc, char *argv[], void *userdata) {
4afd3348 1151 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3d87174d 1152 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1fe6fa16
RM
1153 _cleanup_free_ char *abs_host_path = NULL;
1154 char *dest, *host_path, *container_path;
99534007 1155 sd_bus *bus = ASSERT_PTR(userdata);
0370612e 1156 bool copy_from;
785890ac
LP
1157 int r;
1158
f3cf6167 1159 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
2723b3b5 1160
0370612e 1161 copy_from = streq(argv[0], "copy-from");
1fe6fa16
RM
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)) {
0f474365
LP
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
1fe6fa16
RM
1171 host_path = abs_host_path;
1172 }
785890ac 1173
14456f76 1174 r = bus_message_new_method_call(
785890ac 1175 bus,
3d87174d 1176 &m,
14456f76 1177 bus_machine_mgr,
ae03e1a9 1178 select_copy_method(copy_from, arg_force));
3d87174d
LP
1179 if (r < 0)
1180 return bus_log_create_error(r);
1181
1182 r = sd_bus_message_append(
1183 m,
0370612e
LP
1184 "sss",
1185 argv[1],
1fe6fa16
RM
1186 copy_from ? container_path : host_path,
1187 copy_from ? host_path : container_path);
3d87174d
LP
1188 if (r < 0)
1189 return bus_log_create_error(r);
1190
ae03e1a9 1191 if (arg_force) {
04a3af3c 1192 r = sd_bus_message_append(m, "t", (uint64_t) MACHINE_COPY_REPLACE);
ae03e1a9
AW
1193 if (r < 0)
1194 return bus_log_create_error(r);
1195 }
1196
3d87174d
LP
1197 /* This is a slow operation, hence turn off any method call timeouts */
1198 r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
0f474365
LP
1199 if (r < 0)
1200 return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r));
f2cbe59e
LP
1201
1202 return 0;
1203}
1204
56159e0d 1205static int bind_mount(int argc, char *argv[], void *userdata) {
4afd3348 1206 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
99534007 1207 sd_bus *bus = ASSERT_PTR(userdata);
785890ac
LP
1208 int r;
1209
f3cf6167 1210 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
2723b3b5 1211
14456f76 1212 r = bus_call_method(
90adaa25 1213 bus,
14456f76 1214 bus_machine_mgr,
90adaa25
LP
1215 "BindMountMachine",
1216 &error,
1217 NULL,
1218 "sssbb",
1219 argv[1],
1220 argv[2],
1221 argv[3],
1222 arg_read_only,
1223 arg_mkdir);
4ae25393 1224 if (r < 0)
2a03b9ed 1225 return log_error_errno(r, "Failed to bind mount: %s", bus_error_message(&error, r));
785890ac 1226
90adaa25 1227 return 0;
785890ac
LP
1228}
1229
19070062 1230static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
64b504bd 1231 PTYForward *forward = ASSERT_PTR(userdata);
0ec5543c
LP
1232 int r;
1233
0ec5543c 1234 assert(m);
0ec5543c 1235
64b504bd
YW
1236 /* Tell the forwarder to exit on the next vhangup(), so that we still flush out what might be queued
1237 * and exit then. */
0ec5543c 1238
64b504bd
YW
1239 r = pty_forward_set_ignore_vhangup(forward, false);
1240 if (r < 0) {
1241 /* On error, quit immediately. */
da054c37 1242 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
64b504bd 1243 (void) sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
0ec5543c
LP
1244 }
1245
0ec5543c
LP
1246 return 0;
1247}
1248
64b504bd 1249static int process_forward(sd_event *event, sd_bus_slot *machine_removed_slot, int master, PTYForwardFlags flags, const char *name) {
690f02f4 1250 int r;
c454426c
LP
1251
1252 assert(event);
64b504bd 1253 assert(machine_removed_slot);
c454426c
LP
1254 assert(master >= 0);
1255 assert(name);
1256
fbdec792
WS
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 }
c454426c 1263
ff03ac87
LP
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
d7a6bb98
LP
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");
c454426c 1274
64b504bd
YW
1275 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1276 r = pty_forward_new(event, master, flags, &forward);
c454426c
LP
1277 if (r < 0)
1278 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1279
64b504bd
YW
1280 /* No userdata should not set previously. */
1281 assert_se(!sd_bus_slot_set_userdata(machine_removed_slot, forward));
1282
c454426c
LP
1283 r = sd_event_loop(event);
1284 if (r < 0)
1285 return log_error_errno(r, "Failed to run event loop: %m");
1286
64b504bd 1287 bool machine_died =
ae3dde80 1288 (flags & PTY_FORWARD_IGNORE_VHANGUP) &&
64b504bd 1289 pty_forward_get_ignore_vhangup(forward) == 0;
c454426c 1290
fbdec792
WS
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 }
c454426c 1299
690f02f4 1300 return 0;
c454426c
LP
1301}
1302
bc3bb330
ZJS
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
56159e0d 1338static int login_machine(int argc, char *argv[], void *userdata) {
4afd3348
LP
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;
4afd3348
LP
1341 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
1342 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
c454426c 1343 int master = -1, r;
99534007 1344 sd_bus *bus = ASSERT_PTR(userdata);
31d99bd1 1345 const char *match, *machine;
04d39279 1346
d7a0f1f4
FS
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.");
c454426c 1350
d7a0f1f4
FS
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.");
04d39279 1354
f3cf6167 1355 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1356
023fb90b 1357 r = sd_event_default(&event);
f647962d
MS
1358 if (r < 0)
1359 return log_error_errno(r, "Failed to get event loop: %m");
023fb90b
LP
1360
1361 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
1362 if (r < 0)
1363 return log_error_errno(r, "Failed to attach bus to event loop: %m");
023fb90b 1364
91913f58
LP
1365 machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1];
1366
63c372cb 1367 match = strjoina("type='signal',"
c454426c
LP
1368 "sender='org.freedesktop.machine1',"
1369 "path='/org/freedesktop/machine1',",
1370 "interface='org.freedesktop.machine1.Manager',"
1371 "member='MachineRemoved',"
91913f58 1372 "arg0='", machine, "'");
0ec5543c 1373
64b504bd 1374 r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, NULL);
0ec5543c 1375 if (r < 0)
75152a4d 1376 return log_error_errno(r, "Failed to request machine removal match: %m");
0ec5543c 1377
14456f76 1378 r = bus_call_method(bus, bus_machine_mgr, "OpenMachineLogin", &error, &reply, "s", machine);
4ae25393 1379 if (r < 0)
2a03b9ed 1380 return log_error_errno(r, "Failed to get login PTY: %s", bus_error_message(&error, r));
04d39279 1381
31d99bd1 1382 r = sd_bus_message_read(reply, "hs", &master, NULL);
40205d70 1383 if (r < 0)
ee451d76 1384 return bus_log_parse_error(r);
04d39279 1385
64b504bd 1386 return process_forward(event, slot, master, PTY_FORWARD_IGNORE_VHANGUP, machine);
c454426c 1387}
04d39279 1388
c454426c 1389static int shell_machine(int argc, char *argv[], void *userdata) {
4afd3348
LP
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;
4afd3348
LP
1392 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
1393 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
c454426c 1394 int master = -1, r;
99534007 1395 sd_bus *bus = ASSERT_PTR(userdata);
31d99bd1 1396 const char *match, *machine, *path;
bc3bb330 1397 _cleanup_free_ char *uid = NULL;
04d39279 1398
d7a0f1f4
FS
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.");
c454426c 1402
afdca6c6
LP
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();
89fec318
LP
1414 }
1415
f3cf6167 1416 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
023fb90b 1417
c454426c 1418 r = sd_event_default(&event);
f647962d 1419 if (r < 0)
c454426c 1420 return log_error_errno(r, "Failed to get event loop: %m");
023fb90b 1421
c454426c 1422 r = sd_bus_attach_event(bus, event, 0);
f647962d 1423 if (r < 0)
c454426c 1424 return log_error_errno(r, "Failed to attach bus to event loop: %m");
04d39279 1425
bc3bb330
ZJS
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");
91913f58 1429
c454426c
LP
1430 match = strjoina("type='signal',"
1431 "sender='org.freedesktop.machine1',"
1432 "path='/org/freedesktop/machine1',",
1433 "interface='org.freedesktop.machine1.Manager',"
1434 "member='MachineRemoved',"
91913f58 1435 "arg0='", machine, "'");
c7b7d449 1436
64b504bd 1437 r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, NULL);
c454426c 1438 if (r < 0)
75152a4d 1439 return log_error_errno(r, "Failed to request machine removal match: %m");
023fb90b 1440
14456f76 1441 r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "OpenMachineShell");
c454426c
LP
1442 if (r < 0)
1443 return bus_log_create_error(r);
04d39279 1444
91913f58
LP
1445 path = argc < 3 || isempty(argv[2]) ? NULL : argv[2];
1446
ef3100e9 1447 r = sd_bus_message_append(m, "sss", machine, uid, path);
c454426c
LP
1448 if (r < 0)
1449 return bus_log_create_error(r);
04d39279 1450
91913f58 1451 r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2);
c454426c
LP
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);
4027f96a 1460 if (r < 0)
2a03b9ed 1461 return log_error_errno(r, "Failed to get shell PTY: %s", bus_error_message(&error, r));
c454426c 1462
31d99bd1 1463 r = sd_bus_message_read(reply, "hs", &master, NULL);
c454426c
LP
1464 if (r < 0)
1465 return bus_log_parse_error(r);
1466
64b504bd 1467 return process_forward(event, slot, master, /* flags = */ 0, machine);
04d39279
LP
1468}
1469
1ed35a0d
MY
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) {
bc6c7a58 1511 _cleanup_(edit_file_context_done) EditFileContext context = {};
1ed35a0d
MY
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
f82dcc3f
SL
1521 if (arg_runner == RUNNER_VMSPAWN)
1522 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Edit is only supported for --runner=nspawn");
1523
a452c807 1524 r = mac_init();
1ed35a0d
MY
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
f82dcc3f
SL
1587 if (arg_runner == RUNNER_VMSPAWN)
1588 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Cat is only supported for --runner=nspawn");
1589
1ed35a0d
MY
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
063c8382 1602 q = cat_files(*name, /* dropins = */ NULL, /* flags = */ CAT_FORMAT_HAS_SECTIONS);
1ed35a0d
MY
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
063c8382 1623 q = cat_files(path, /* dropins = */ NULL, /* flags = */ CAT_FORMAT_HAS_SECTIONS);
1ed35a0d
MY
1624 if (q < 0)
1625 return r < 0 ? r : q;
1626 }
1627
1628 return r;
1629}
1630
08682124 1631static int remove_image(int argc, char *argv[], void *userdata) {
99534007 1632 sd_bus *bus = ASSERT_PTR(userdata);
8571210a 1633 int r;
08682124 1634
f3cf6167 1635 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1636
8571210a 1637 for (int i = 1; i < argc; i++) {
3d87174d
LP
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
14456f76 1641 r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "RemoveImage");
3d87174d
LP
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));
08682124
LP
1653 }
1654
1655 return 0;
1656}
1657
ebd93cb6 1658static int rename_image(int argc, char *argv[], void *userdata) {
4afd3348 1659 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
99534007 1660 sd_bus *bus = ASSERT_PTR(userdata);
ebd93cb6
LP
1661 int r;
1662
f3cf6167 1663 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1664
14456f76 1665 r = bus_call_method(
ebd93cb6 1666 bus,
14456f76 1667 bus_machine_mgr,
ebd93cb6
LP
1668 "RenameImage",
1669 &error,
1670 NULL,
1671 "ss", argv[1], argv[2]);
4ae25393 1672 if (r < 0)
2a03b9ed 1673 return log_error_errno(r, "Could not rename image: %s", bus_error_message(&error, r));
ebd93cb6
LP
1674
1675 return 0;
1676}
1677
1678static int clone_image(int argc, char *argv[], void *userdata) {
4afd3348 1679 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3d87174d 1680 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
99534007 1681 sd_bus *bus = ASSERT_PTR(userdata);
ebd93cb6
LP
1682 int r;
1683
f3cf6167 1684 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1685
14456f76 1686 r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "CloneImage");
3d87174d
LP
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));
ebd93cb6
LP
1698
1699 return 0;
1700}
1701
1702static int read_only_image(int argc, char *argv[], void *userdata) {
4afd3348 1703 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
99534007 1704 sd_bus *bus = ASSERT_PTR(userdata);
ebd93cb6
LP
1705 int b = true, r;
1706
1707 if (argc > 2) {
1708 b = parse_boolean(argv[2]);
d7a0f1f4
FS
1709 if (b < 0)
1710 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1711 "Failed to parse boolean argument: %s",
1712 argv[2]);
ebd93cb6
LP
1713 }
1714
f3cf6167 1715 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1716
14456f76 1717 r = bus_call_method(bus, bus_machine_mgr, "MarkImageReadOnly", &error, NULL, "sb", argv[1], b);
4ae25393 1718 if (r < 0)
2a03b9ed 1719 return log_error_errno(r, "Could not mark image read-only: %s", bus_error_message(&error, r));
ebd93cb6
LP
1720
1721 return 0;
1722}
1723
68ce459f
LP
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
14456f76 1731 r = bus_call_method(bus, bus_machine_mgr, "GetImage", &error, NULL, "s", name);
68ce459f
LP
1732 if (r < 0) {
1733 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_IMAGE))
1734 return 0;
1735
2a03b9ed 1736 return log_error_errno(r, "Failed to check whether image %s exists: %s", name, bus_error_message(&error, r));
68ce459f
LP
1737 }
1738
1739 return 1;
1740}
1741
7410616c 1742static int make_service_name(const char *name, char **ret) {
7410616c
LP
1743 int r;
1744
1745 assert(name);
1746 assert(ret);
b52d5758 1747 assert(arg_runner >= 0 && arg_runner < _RUNNER_MAX);
7410616c 1748
52ef5dd7 1749 if (!hostname_is_valid(name, 0))
baaa35ad
ZJS
1750 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1751 "Invalid machine name %s.", name);
7410616c 1752
f82dcc3f 1753 r = unit_name_build(machine_runner_unit_prefix_table[arg_runner], name, ".service", ret);
7410616c
LP
1754 if (r < 0)
1755 return log_error_errno(r, "Failed to build unit name: %m");
1756
1757 return 0;
1758}
1759
ebd011d9 1760static int start_machine(int argc, char *argv[], void *userdata) {
4afd3348 1761 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
ebd011d9 1762 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
99534007 1763 sd_bus *bus = ASSERT_PTR(userdata);
8571210a 1764 int r;
ebd011d9 1765
f3cf6167 1766 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
e41e9ba8 1767 ask_password_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1768
ebd011d9
LP
1769 r = bus_wait_for_jobs_new(bus, &w);
1770 if (r < 0)
b2544bb6 1771 return log_error_errno(r, "Could not watch jobs: %m");
ebd011d9 1772
8571210a 1773 for (int i = 1; i < argc; i++) {
4afd3348 1774 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
7410616c 1775 _cleanup_free_ char *unit = NULL;
ebd011d9
LP
1776 const char *object;
1777
7410616c
LP
1778 r = make_service_name(argv[i], &unit);
1779 if (r < 0)
1780 return r;
ebd011d9 1781
68ce459f
LP
1782 r = image_exists(bus, argv[i]);
1783 if (r < 0)
1784 return r;
d7a0f1f4
FS
1785 if (r == 0)
1786 return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
1787 "Machine image '%s' does not exist.",
1788 argv[i]);
68ce459f 1789
57f28dee 1790 r = bus_call_method(
ebd011d9 1791 bus,
57f28dee 1792 bus_systemd_mgr,
2723b3b5
LP
1793 "StartUnit",
1794 &error,
1795 &reply,
1796 "ss", unit, "fail");
4ae25393 1797 if (r < 0)
2a03b9ed 1798 return log_error_errno(r, "Failed to start unit: %s", bus_error_message(&error, r));
ebd011d9
LP
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
10ba4835 1809 r = bus_wait_for_jobs(w, arg_quiet, NULL);
ebd011d9
LP
1810 if (r < 0)
1811 return r;
1812
1813 return 0;
1814}
1815
d8f52ed2 1816static int enable_machine(int argc, char *argv[], void *userdata) {
4afd3348
LP
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;
e6afd73b 1819 const char *method;
99534007 1820 sd_bus *bus = ASSERT_PTR(userdata);
8571210a 1821 int r;
48a50acc 1822 bool enable;
d8f52ed2 1823
f3cf6167 1824 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
acf97e21 1825
48a50acc
DT
1826 enable = streq(argv[0], "enable");
1827 method = enable ? "EnableUnitFiles" : "DisableUnitFiles";
d8f52ed2 1828
57f28dee 1829 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
d8f52ed2
LP
1830 if (r < 0)
1831 return bus_log_create_error(r);
1832
d8f52ed2
LP
1833 r = sd_bus_message_open_container(m, 'a', "s");
1834 if (r < 0)
1835 return bus_log_create_error(r);
1836
48a50acc 1837 if (enable) {
d2493703
MY
1838 r = sd_bus_message_append(m, "s", "machines.target");
1839 if (r < 0)
1840 return bus_log_create_error(r);
1841 }
1842
8571210a 1843 for (int i = 1; i < argc; i++) {
90615ad7 1844 _cleanup_free_ char *unit = NULL;
d8f52ed2 1845
7410616c
LP
1846 r = make_service_name(argv[i], &unit);
1847 if (r < 0)
1848 return r;
d8f52ed2 1849
68ce459f
LP
1850 r = image_exists(bus, argv[i]);
1851 if (r < 0)
1852 return r;
d7a0f1f4
FS
1853 if (r == 0)
1854 return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
1855 "Machine image '%s' does not exist.",
1856 argv[i]);
68ce459f 1857
d8f52ed2
LP
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
48a50acc 1867 if (enable)
d8f52ed2
LP
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);
4ae25393 1875 if (r < 0)
2a03b9ed 1876 return log_error_errno(r, "Failed to enable or disable unit: %s", bus_error_message(&error, r));
d8f52ed2 1877
48a50acc 1878 if (enable) {
31d99bd1 1879 r = sd_bus_message_read(reply, "b", NULL);
d8f52ed2
LP
1880 if (r < 0)
1881 return bus_log_parse_error(r);
1882 }
1883
5e891cbb 1884 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
d8f52ed2 1885 if (r < 0)
ffddb3c9 1886 return r;
d8f52ed2 1887
a9399358 1888 r = bus_service_manager_reload(bus);
ffddb3c9 1889 if (r < 0)
a9399358 1890 return r;
d8f52ed2 1891
c2434a61
MY
1892 if (arg_now) {
1893 _cleanup_strv_free_ char **new_args = NULL;
1894
48a50acc 1895 new_args = strv_new(enable ? "start" : "poweroff");
ffddb3c9
DT
1896 if (!new_args)
1897 return log_oom();
c2434a61
MY
1898
1899 r = strv_extend_strv(&new_args, argv + 1, /* filter_duplicates = */ false);
ffddb3c9
DT
1900 if (r < 0)
1901 return log_oom();
c2434a61 1902
48a50acc 1903 if (enable)
ffddb3c9 1904 return start_machine(strv_length(new_args), new_args, userdata);
acc0269c 1905
8df3e0ee 1906 return poweroff_machine(strv_length(new_args), new_args, userdata);
c2434a61 1907 }
acc0269c 1908
ffddb3c9 1909 return 0;
d8f52ed2
LP
1910}
1911
d6ce17c7 1912static int set_limit(int argc, char *argv[], void *userdata) {
4afd3348 1913 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
d6ce17c7
LP
1914 sd_bus *bus = userdata;
1915 uint64_t limit;
1916 int r;
1917
f3cf6167 1918 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
730fa7ce 1919
7705a405 1920 if (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
f5fbe71d 1921 limit = UINT64_MAX;
d6ce17c7 1922 else {
59f448cf 1923 r = parse_size(argv[argc-1], 1024, &limit);
d6ce17c7 1924 if (r < 0)
730fa7ce 1925 return log_error_errno(r, "Failed to parse size: %s", argv[argc-1]);
d6ce17c7
LP
1926 }
1927
1928 if (argc > 2)
1929 /* With two arguments changes the quota limit of the
1930 * specified image */
14456f76 1931 r = bus_call_method(bus, bus_machine_mgr, "SetImageLimit", &error, NULL, "st", argv[1], limit);
d6ce17c7
LP
1932 else
1933 /* With one argument changes the pool quota limit */
14456f76 1934 r = bus_call_method(bus, bus_machine_mgr, "SetPoolLimit", &error, NULL, "t", limit);
d6ce17c7 1935
730fa7ce
LP
1936 if (r < 0)
1937 return log_error_errno(r, "Could not set limit: %s", bus_error_message(&error, r));
d6ce17c7
LP
1938
1939 return 0;
1940}
1941
d94c2b06 1942static int clean_images(int argc, char *argv[], void *userdata) {
03c2b288 1943 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
d94c2b06
LP
1944 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1945 uint64_t usage, total = 0;
d94c2b06
LP
1946 sd_bus *bus = userdata;
1947 const char *name;
1948 unsigned c = 0;
1949 int r;
1950
f3cf6167 1951 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
730fa7ce 1952
14456f76 1953 r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "CleanPool");
03c2b288
LP
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);
d94c2b06
LP
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) {
3a6797f1
YW
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",
2b59bf51 1976 name, FORMAT_BYTES(usage));
3a6797f1
YW
1977 if (total != UINT64_MAX)
1978 total += usage;
1979 }
d94c2b06
LP
1980 c++;
1981 }
1982
1983 r = sd_bus_message_exit_container(reply);
1984 if (r < 0)
1985 return bus_log_parse_error(r);
1986
3a6797f1
YW
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.",
2b59bf51 1991 c, FORMAT_BYTES(total));
d94c2b06
LP
1992
1993 return 0;
1994}
1995
8ce438bb
LP
1996static int chainload_importctl(int argc, char *argv[]) {
1997 int r;
1998
8b29949a
LP
1999 if (!arg_quiet)
2000 log_notice("The 'machinectl %1$s' command has been replaced by 'importctl -m %1$s'. Redirecting invocation.", argv[optind]);
8ce438bb
LP
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
56159e0d 2043static int help(int argc, char *argv[], void *userdata) {
37ec0fdd
LP
2044 _cleanup_free_ char *link = NULL;
2045 int r;
2046
384c2c32 2047 pager_open(arg_pager_flags);
56159e0d 2048
37ec0fdd
LP
2049 r = terminal_urlify_man("machinectl", "1", &link);
2050 if (r < 0)
2051 return log_oom();
2052
7c9437fd
LP
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"
f2cbe59e 2057 " list List running VMs and containers\n"
fefdc04b 2058 " status NAME... Show VM/container details\n"
91913f58 2059 " show [NAME...] Show properties of one or more VMs/containers\n"
ebd011d9 2060 " start NAME... Start container as a service\n"
91913f58
LP
2061 " login [NAME] Get a login prompt in a container or on the\n"
2062 " local host\n"
ef3100e9
LP
2063 " shell [[USER@]NAME [COMMAND...]]\n"
2064 " Invoke a shell (or other command) in a container\n"
2065 " or on the local host\n"
d8f52ed2
LP
2066 " enable NAME... Enable automatic container start at boot\n"
2067 " disable NAME... Disable automatic container start at boot\n"
f2cbe59e
LP
2068 " poweroff NAME... Power off one or more containers\n"
2069 " reboot NAME... Reboot one or more containers\n"
f2cbe59e 2070 " terminate NAME... Terminate one or more VMs/containers\n"
ebd93cb6 2071 " kill NAME... Send signal to processes of a VM/container\n"
f2cbe59e 2072 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
ebd93cb6 2073 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
1ed35a0d 2074 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n"
7c9437fd 2075 "\n%3$sImage Commands:%4$s\n"
56b921c3 2076 " list-images Show available container and VM images\n"
91913f58
LP
2077 " image-status [NAME...] Show image details\n"
2078 " show-image [NAME...] Show properties of image\n"
1ed35a0d
MY
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"
ebd93cb6
LP
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"
d6ce17c7 2084 " remove NAME... Remove an image\n"
5bda1f47 2085 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n"
1ed35a0d 2086 " clean Remove hidden (or all) images\n"
7c9437fd 2087 "\n%3$sOptions:%4$s\n"
e1fac8a6
ZJS
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"
c5c61950
ZJS
2096 " --value When showing properties, only print the value\n"
2097 " -P NAME Equivalent to --value --property=NAME\n"
e1fac8a6
ZJS
2098 " -q --quiet Suppress output\n"
2099 " -a --all Show all properties, including empty ones\n"
e1fac8a6 2100 " -l --full Do not ellipsize output\n"
4ccde410 2101 " --kill-whom=WHOM Whom to send signal to\n"
e1fac8a6
ZJS
2102 " -s --signal=SIGNAL Which signal to send\n"
2103 " --uid=USER Specify user ID to invoke shell as\n"
89bf86e0 2104 " -E --setenv=VAR[=VALUE] Add an environment variable for shell\n"
8ce438bb 2105 " --read-only Create read-only bind mount or clone\n"
e1fac8a6
ZJS
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"
893bcd3d 2111 " short-monotonic, short-unix, short-delta,\n"
e1fac8a6 2112 " json, json-pretty, json-sse, json-seq, cat,\n"
893bcd3d 2113 " verbose, export, with-unit)\n"
8ce438bb 2114 " --force Replace target file when copying, if necessary\n"
c2434a61
MY
2115 " --now Start or power off container after enabling or\n"
2116 " disabling it\n"
f82dcc3f 2117 " --runner=RUNNER Select between nspawn and vmspawn as the runner\n"
3c1053ff 2118 " -V Short for --runner=vmspawn\n"
7c9437fd 2119 "\nSee the %2$s for details.\n",
bc556335 2120 program_invocation_short_name,
7c9437fd
LP
2121 link,
2122 ansi_underline(),
bc556335
DDM
2123 ansi_normal(),
2124 ansi_highlight(),
7c9437fd 2125 ansi_normal());
56159e0d
LP
2126
2127 return 0;
1ee306e1
LP
2128}
2129
2130static int parse_argv(int argc, char *argv[]) {
2131
2132 enum {
2133 ARG_VERSION = 0x100,
2134 ARG_NO_PAGER,
e56056e9 2135 ARG_NO_LEGEND,
85500523 2136 ARG_VALUE,
4ccde410 2137 ARG_KILL_WHOM,
785890ac
LP
2138 ARG_READ_ONLY,
2139 ARG_MKDIR,
acf97e21 2140 ARG_NO_ASK_PASSWORD,
3d7415f4 2141 ARG_VERIFY,
f82dcc3f 2142 ARG_RUNNER,
c2434a61 2143 ARG_NOW,
3d7415f4 2144 ARG_FORCE,
587fec42 2145 ARG_FORMAT,
c454426c 2146 ARG_UID,
fc6eb08e 2147 ARG_MAX_ADDRESSES,
1ee306e1
LP
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' },
85500523 2154 { "value", no_argument, NULL, ARG_VALUE },
c5c61950 2155 { "all", no_argument, NULL, 'a' },
1ee306e1
LP
2156 { "full", no_argument, NULL, 'l' },
2157 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
e56056e9 2158 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
4ccde410 2159 { "kill-whom", required_argument, NULL, ARG_KILL_WHOM },
1ee306e1
LP
2160 { "signal", required_argument, NULL, 's' },
2161 { "host", required_argument, NULL, 'H' },
a7893c6b 2162 { "machine", required_argument, NULL, 'M' },
785890ac
LP
2163 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2164 { "mkdir", no_argument, NULL, ARG_MKDIR },
d8f52ed2 2165 { "quiet", no_argument, NULL, 'q' },
8b0cc9a3
LP
2166 { "lines", required_argument, NULL, 'n' },
2167 { "output", required_argument, NULL, 'o' },
acf97e21 2168 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
3d7415f4 2169 { "verify", required_argument, NULL, ARG_VERIFY },
f82dcc3f 2170 { "runner", required_argument, NULL, ARG_RUNNER },
c2434a61 2171 { "now", no_argument, NULL, ARG_NOW },
3d7415f4 2172 { "force", no_argument, NULL, ARG_FORCE },
587fec42 2173 { "format", required_argument, NULL, ARG_FORMAT },
c454426c 2174 { "uid", required_argument, NULL, ARG_UID },
4d46e5db 2175 { "setenv", required_argument, NULL, 'E' },
fc6eb08e 2176 { "max-addresses", required_argument, NULL, ARG_MAX_ADDRESSES },
eb9da376 2177 {}
1ee306e1
LP
2178 };
2179
368d2643 2180 bool reorder = false;
768c1dec 2181 int c, r, shell = -1;
1ee306e1
LP
2182
2183 assert(argc >= 0);
2184 assert(argv);
2185
ef9c12b1
YW
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
368d2643 2190 for (;;) {
c5c61950 2191 static const char option_string[] = "-hp:P:als:H:M:qn:o:E:V";
368d2643 2192
0bf50960 2193 c = getopt_long(argc, argv, option_string + reorder, options, NULL);
768c1dec
LP
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
368d2643
LP
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
768c1dec
LP
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. */
b3a9d980 2219 optind--; /* don't process this argument, go one step back */
768c1dec
LP
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. */
368d2643 2230 reorder = true;
768c1dec
LP
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 */
368d2643
LP
2241 }
2242
2243 break;
1ee306e1
LP
2244
2245 case 'h':
56159e0d 2246 return help(0, NULL, NULL);
1ee306e1
LP
2247
2248 case ARG_VERSION:
3f6fd1ba 2249 return version();
1ee306e1 2250
a7893c6b 2251 case 'p':
c5c61950 2252 case 'P':
a7893c6b
LP
2253 r = strv_extend(&arg_property, optarg);
2254 if (r < 0)
2255 return log_oom();
1ee306e1 2256
c5c61950 2257 /* If the user asked for a particular property, show it to them, even if empty. */
255b1fc8 2258 SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY, true);
c5c61950
ZJS
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);
1ee306e1 2266 break;
1ee306e1
LP
2267
2268 case 'a':
255b1fc8 2269 SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY, true);
1ee306e1
LP
2270 arg_all = true;
2271 break;
2272
2273 case 'l':
2274 arg_full = true;
2275 break;
2276
8b0cc9a3 2277 case 'n':
baaa35ad
ZJS
2278 if (safe_atou(optarg, &arg_lines) < 0)
2279 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2280 "Failed to parse lines '%s'", optarg);
8b0cc9a3
LP
2281 break;
2282
2283 case 'o':
5c828e66
LP
2284 if (streq(optarg, "help")) {
2285 DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX);
2286 return 0;
2287 }
2288
c706a52a
ZJS
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;
ad9d139e
LP
2293
2294 if (OUTPUT_MODE_IS_JSON(arg_output))
2295 arg_legend = false;
8b0cc9a3
LP
2296 break;
2297
1ee306e1 2298 case ARG_NO_PAGER:
0221d68a 2299 arg_pager_flags |= PAGER_DISABLE;
1ee306e1
LP
2300 break;
2301
e56056e9
TA
2302 case ARG_NO_LEGEND:
2303 arg_legend = false;
2304 break;
2305
4ccde410
ZJS
2306 case ARG_KILL_WHOM:
2307 arg_kill_whom = optarg;
1ee306e1
LP
2308 break;
2309
2310 case 's':
86beb213
ZJS
2311 r = parse_signal_argument(optarg, &arg_signal);
2312 if (r <= 0)
2313 return r;
1ee306e1
LP
2314 break;
2315
acf97e21
LP
2316 case ARG_NO_ASK_PASSWORD:
2317 arg_ask_password = false;
2318 break;
2319
1ee306e1 2320 case 'H':
d21ed1ea 2321 arg_transport = BUS_TRANSPORT_REMOTE;
a7893c6b
LP
2322 arg_host = optarg;
2323 break;
2324
2325 case 'M':
de33fc62 2326 arg_transport = BUS_TRANSPORT_MACHINE;
a7893c6b 2327 arg_host = optarg;
1ee306e1
LP
2328 break;
2329
785890ac
LP
2330 case ARG_READ_ONLY:
2331 arg_read_only = true;
2332 break;
2333
2334 case ARG_MKDIR:
2335 arg_mkdir = true;
2336 break;
2337
d8f52ed2
LP
2338 case 'q':
2339 arg_quiet = true;
2340 break;
2341
3d7415f4 2342 case ARG_VERIFY:
5c828e66
LP
2343 if (streq(optarg, "help")) {
2344 DUMP_STRING_TABLE(import_verify, ImportVerify, _IMPORT_VERIFY_MAX);
2345 return 0;
2346 }
2347
c706a52a
ZJS
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;
3d7415f4
LP
2352 break;
2353
f82dcc3f
SL
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
c2434a61
MY
2366 case ARG_NOW:
2367 arg_now = true;
2368 break;
2369
3d7415f4
LP
2370 case ARG_FORCE:
2371 arg_force = true;
2372 break;
2373
587fec42 2374 case ARG_FORMAT:
bd9c55eb 2375 if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2", "zstd"))
baaa35ad
ZJS
2376 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2377 "Unknown format: %s", optarg);
587fec42
LP
2378
2379 arg_format = optarg;
2380 break;
2381
c454426c
LP
2382 case ARG_UID:
2383 arg_uid = optarg;
2384 break;
2385
4d46e5db 2386 case 'E':
89bf86e0 2387 r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg);
c454426c 2388 if (r < 0)
89bf86e0 2389 return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
c454426c
LP
2390 break;
2391
fc6eb08e 2392 case ARG_MAX_ADDRESSES:
07b0b339 2393 if (streq(optarg, "all"))
99b8149a
ZJS
2394 arg_max_addresses = UINT_MAX;
2395 else if (safe_atou(optarg, &arg_max_addresses) < 0)
baaa35ad 2396 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
fc6eb08e 2397 "Invalid number of addresses: %s", optarg);
07b0b339
SK
2398 break;
2399
1ee306e1
LP
2400 case '?':
2401 return -EINVAL;
2402
2403 default:
04499a70 2404 assert_not_reached();
1ee306e1 2405 }
368d2643 2406 }
1ee306e1 2407
768c1dec
LP
2408done:
2409 if (shell >= 0) {
2410 char *t;
768c1dec
LP
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];
8571210a 2418 for (int i = shell; i < optind; i++)
768c1dec
LP
2419 argv[i] = argv[i+1];
2420 argv[optind] = t;
2421 }
2422
1ee306e1
LP
2423 return 1;
2424}
2425
56159e0d
LP
2426static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2427
2428 static const Verb verbs[] = {
3d7415f4
LP
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 },
160e3793 2433 { "image-status", VERB_ANY, VERB_ANY, 0, show_image },
3d7415f4
LP
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 },
9ef362bf 2438 { "restart", 2, VERB_ANY, 0, reboot_machine }, /* Convenience alias */
3d7415f4 2439 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
b2bb19bb 2440 { "stop", 2, VERB_ANY, 0, poweroff_machine }, /* Convenience alias */
3d7415f4 2441 { "kill", 2, VERB_ANY, 0, kill_machine },
91913f58
LP
2442 { "login", VERB_ANY, 2, 0, login_machine },
2443 { "shell", VERB_ANY, VERB_ANY, 0, shell_machine },
3d7415f4 2444 { "bind", 3, 4, 0, bind_mount },
1ed35a0d
MY
2445 { "edit", 2, VERB_ANY, 0, edit_settings },
2446 { "cat", 2, VERB_ANY, 0, cat_settings },
3d7415f4
LP
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 },
d6ce17c7 2456 { "set-limit", 2, 3, 0, set_limit },
d94c2b06 2457 { "clean", VERB_ANY, 1, 0, clean_images },
56159e0d 2458 {}
1ee306e1
LP
2459 };
2460
56159e0d 2461 return dispatch_verb(argc, argv, verbs, bus);
1ee306e1
LP
2462}
2463
f2a3de01 2464static int run(int argc, char *argv[]) {
f66da783 2465 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
84f6181c 2466 int r;
1ee306e1
LP
2467
2468 setlocale(LC_ALL, "");
d2acb93d 2469 log_setup();
1abaf488 2470
1ee306e1 2471 r = parse_argv(argc, argv);
84f6181c 2472 if (r <= 0)
f66da783 2473 return r;
1ee306e1 2474
fad3feec
MY
2475 journal_browse_prepare();
2476
8ce438bb
LP
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
4870133b 2484 r = bus_connect_transport(arg_transport, arg_host, RUNTIME_SCOPE_SYSTEM, &bus);
f66da783 2485 if (r < 0)
d8a77d55 2486 return bus_log_connect_error(r, arg_transport, RUNTIME_SCOPE_SYSTEM);
1ee306e1 2487
730fa7ce 2488 (void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
2723b3b5 2489
f66da783 2490 return machinectl_main(argc, argv, bus);
1ee306e1 2491}
f66da783
YW
2492
2493DEFINE_MAIN_FUNCTION(run);