]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machinectl.c
machinectl: simplify option string assignment
[thirdparty/systemd.git] / src / machine / machinectl.c
CommitLineData
1ee306e1
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2013 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
3f6fd1ba 20#include <arpa/inet.h>
1ee306e1 21#include <errno.h>
3f6fd1ba 22#include <fcntl.h>
1ee306e1 23#include <getopt.h>
1ee306e1 24#include <locale.h>
f48e75cb 25#include <net/if.h>
3f6fd1ba
LP
26#include <netinet/in.h>
27#include <string.h>
785890ac 28#include <sys/mount.h>
3f6fd1ba
LP
29#include <sys/socket.h>
30#include <unistd.h>
eef46c37 31
a1da8583 32#include "sd-bus.h"
3f6fd1ba 33
b5efdb8a 34#include "alloc-util.h"
a1da8583 35#include "bus-error.h"
a0e27019 36#include "bus-unit-util.h"
3f6fd1ba 37#include "bus-util.h"
1ee306e1 38#include "cgroup-show.h"
9d127096 39#include "cgroup-util.h"
f2cbe59e 40#include "copy.h"
3f6fd1ba 41#include "env-util.h"
3ffd4af2 42#include "fd-util.h"
3f6fd1ba 43#include "hostname-util.h"
3d7415f4 44#include "import-util.h"
3f6fd1ba
LP
45#include "log.h"
46#include "logs-show.h"
47#include "macro.h"
48#include "mkdir.h"
49#include "pager.h"
6bedfcbb 50#include "parse-util.h"
3f6fd1ba 51#include "path-util.h"
0b452006 52#include "process-util.h"
3f6fd1ba 53#include "ptyfwd.h"
24882e06 54#include "signal-util.h"
3f6fd1ba
LP
55#include "spawn-polkit-agent.h"
56#include "strv.h"
57#include "terminal-util.h"
58#include "unit-name.h"
59#include "util.h"
60#include "verbs.h"
49cf4170 61#include "web-util.h"
1ee306e1
LP
62
63static char **arg_property = NULL;
64static bool arg_all = false;
85500523 65static bool arg_value = false;
1ee306e1
LP
66static bool arg_full = false;
67static bool arg_no_pager = false;
e56056e9 68static bool arg_legend = true;
1ee306e1
LP
69static const char *arg_kill_who = NULL;
70static int arg_signal = SIGTERM;
d21ed1ea 71static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
1ee306e1 72static char *arg_host = NULL;
785890ac
LP
73static bool arg_read_only = false;
74static bool arg_mkdir = false;
d8f52ed2 75static bool arg_quiet = false;
acf97e21 76static bool arg_ask_password = true;
8b0cc9a3
LP
77static unsigned arg_lines = 10;
78static OutputMode arg_output = OUTPUT_SHORT;
3d7415f4 79static bool arg_force = false;
6e18cc9f 80static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
587fec42 81static const char* arg_format = NULL;
c454426c
LP
82static const char *arg_uid = NULL;
83static char **arg_setenv = NULL;
1ee306e1 84
acf97e21
LP
85static void polkit_agent_open_if_enabled(void) {
86
87 /* Open the polkit agent as a child process if necessary */
88
89 if (!arg_ask_password)
90 return;
91
92 if (arg_transport != BUS_TRANSPORT_LOCAL)
93 return;
94
95 polkit_agent_open();
96}
97
8b0cc9a3
LP
98static OutputFlags get_output_flags(void) {
99 return
100 arg_all * OUTPUT_SHOW_ALL |
101 arg_full * OUTPUT_FULL_WIDTH |
102 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
40c9fe4c 103 colors_enabled() * OUTPUT_COLOR |
8b0cc9a3
LP
104 !arg_quiet * OUTPUT_WARN_CUTOFF;
105}
106
0b63e278
LP
107typedef struct MachineInfo {
108 const char *name;
109 const char *class;
110 const char *service;
111} MachineInfo;
112
113static int compare_machine_info(const void *a, const void *b) {
114 const MachineInfo *x = a, *y = b;
115
116 return strcmp(x->name, y->name);
117}
118
56159e0d
LP
119static int list_machines(int argc, char *argv[], void *userdata) {
120
0b63e278 121 size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
4afd3348
LP
122 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
123 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
0b63e278 124 _cleanup_free_ MachineInfo *machines = NULL;
a1da8583 125 const char *name, *class, *service, *object;
0b63e278 126 size_t n_machines = 0, n_allocated = 0, j;
56159e0d 127 sd_bus *bus = userdata;
1ee306e1
LP
128 int r;
129
56159e0d
LP
130 assert(bus);
131
ea4b98e6 132 pager_open(arg_no_pager, false);
1ee306e1 133
e138e7d7
ZJS
134 r = sd_bus_call_method(bus,
135 "org.freedesktop.machine1",
136 "/org/freedesktop/machine1",
137 "org.freedesktop.machine1.Manager",
138 "ListMachines",
139 &error,
140 &reply,
141 NULL);
a1da8583
TG
142 if (r < 0) {
143 log_error("Could not get machines: %s", bus_error_message(&error, -r));
1ee306e1 144 return r;
1ee306e1
LP
145 }
146
3d7415f4 147 r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
a1da8583 148 if (r < 0)
5b30bef8 149 return bus_log_parse_error(r);
1ee306e1 150
a1da8583 151 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
0b63e278
LP
152 size_t l;
153
fee6d013
LP
154 if (name[0] == '.' && !arg_all)
155 continue;
156
0b63e278
LP
157 if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
158 return log_oom();
1ee306e1 159
0b63e278
LP
160 machines[n_machines].name = name;
161 machines[n_machines].class = class;
162 machines[n_machines].service = service;
163
164 l = strlen(name);
165 if (l > max_name)
166 max_name = l;
167
168 l = strlen(class);
169 if (l > max_class)
170 max_class = l;
171
172 l = strlen(service);
173 if (l > max_service)
174 max_service = l;
175
313cefa1 176 n_machines++;
1ee306e1 177 }
a1da8583 178 if (r < 0)
5b30bef8 179 return bus_log_parse_error(r);
a1da8583
TG
180
181 r = sd_bus_message_exit_container(reply);
182 if (r < 0)
5b30bef8 183 return bus_log_parse_error(r);
1ee306e1 184
0b63e278
LP
185 qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
186
187 if (arg_legend)
188 printf("%-*s %-*s %-*s\n",
189 (int) max_name, "MACHINE",
190 (int) max_class, "CLASS",
191 (int) max_service, "SERVICE");
192
193 for (j = 0; j < n_machines; j++)
194 printf("%-*s %-*s %-*s\n",
195 (int) max_name, machines[j].name,
196 (int) max_class, machines[j].class,
197 (int) max_service, machines[j].service);
198
e56056e9 199 if (arg_legend)
0b63e278 200 printf("\n%zu machines listed.\n", n_machines);
1ee306e1
LP
201
202 return 0;
203}
204
cd61c3bf
LP
205typedef struct ImageInfo {
206 const char *name;
207 const char *type;
208 bool read_only;
10f9c755
LP
209 usec_t crtime;
210 usec_t mtime;
b6b18498 211 uint64_t size;
cd61c3bf
LP
212} ImageInfo;
213
214static int compare_image_info(const void *a, const void *b) {
215 const ImageInfo *x = a, *y = b;
216
217 return strcmp(x->name, y->name);
218}
219
56159e0d 220static int list_images(int argc, char *argv[], void *userdata) {
cd61c3bf 221
4afd3348 222 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
c19de711 223 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("USAGE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
4afd3348 224 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
cd61c3bf
LP
225 _cleanup_free_ ImageInfo *images = NULL;
226 size_t n_images = 0, n_allocated = 0, j;
227 const char *name, *type, *object;
56159e0d 228 sd_bus *bus = userdata;
b6b18498 229 uint64_t crtime, mtime, size;
10f9c755 230 int read_only, r;
cd61c3bf 231
56159e0d
LP
232 assert(bus);
233
ea4b98e6 234 pager_open(arg_no_pager, false);
cd61c3bf 235
e138e7d7
ZJS
236 r = sd_bus_call_method(bus,
237 "org.freedesktop.machine1",
238 "/org/freedesktop/machine1",
239 "org.freedesktop.machine1.Manager",
240 "ListImages",
241 &error,
242 &reply,
243 "");
cd61c3bf
LP
244 if (r < 0) {
245 log_error("Could not get images: %s", bus_error_message(&error, -r));
246 return r;
247 }
248
b6b18498 249 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
cd61c3bf
LP
250 if (r < 0)
251 return bus_log_parse_error(r);
252
b6b18498
LP
253 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
254 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
10f9c755 255 size_t l;
cd61c3bf
LP
256
257 if (name[0] == '.' && !arg_all)
258 continue;
259
260 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
261 return log_oom();
262
263 images[n_images].name = name;
264 images[n_images].type = type;
265 images[n_images].read_only = read_only;
10f9c755
LP
266 images[n_images].crtime = crtime;
267 images[n_images].mtime = mtime;
b6b18498 268 images[n_images].size = size;
10f9c755
LP
269
270 l = strlen(name);
271 if (l > max_name)
272 max_name = l;
cd61c3bf 273
10f9c755
LP
274 l = strlen(type);
275 if (l > max_type)
276 max_type = l;
cd61c3bf 277
10f9c755 278 if (crtime != 0) {
b6b18498 279 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
10f9c755
LP
280 if (l > max_crtime)
281 max_crtime = l;
282 }
283
284 if (mtime != 0) {
b6b18498 285 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
10f9c755
LP
286 if (l > max_mtime)
287 max_mtime = l;
288 }
cd61c3bf 289
b6b18498
LP
290 if (size != (uint64_t) -1) {
291 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
292 if (l > max_size)
293 max_size = l;
294 }
295
cd61c3bf
LP
296 n_images++;
297 }
298 if (r < 0)
299 return bus_log_parse_error(r);
300
301 r = sd_bus_message_exit_container(reply);
302 if (r < 0)
303 return bus_log_parse_error(r);
304
305 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
306
307 if (arg_legend)
b6b18498 308 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
10f9c755
LP
309 (int) max_name, "NAME",
310 (int) max_type, "TYPE",
311 "RO",
c19de711 312 (int) max_size, "USAGE",
10f9c755
LP
313 (int) max_crtime, "CREATED",
314 (int) max_mtime, "MODIFIED");
cd61c3bf
LP
315
316 for (j = 0; j < n_images; j++) {
b6b18498 317 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
10f9c755 318
b6b18498 319 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
cd61c3bf
LP
320 (int) max_name, images[j].name,
321 (int) max_type, images[j].type,
1fc464f6 322 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_normal() : "",
b6b18498
LP
323 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
324 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
325 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
cd61c3bf
LP
326 }
327
cd61c3bf
LP
328 if (arg_legend)
329 printf("\n%zu images listed.\n", n_images);
330
331 return 0;
332}
333
89f7c846 334static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
4afd3348 335 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
a0e27019 336 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
aa1936ea 337 _cleanup_free_ char *path = NULL;
aa1936ea 338 const char *cgroup;
8b0cc9a3 339 int r;
aa1936ea
LP
340 unsigned c;
341
342 assert(bus);
343 assert(unit);
344
aa1936ea
LP
345 path = unit_dbus_path_from_name(unit);
346 if (!path)
347 return log_oom();
348
a7893c6b 349 r = sd_bus_get_property(
aa1936ea
LP
350 bus,
351 "org.freedesktop.systemd1",
352 path,
21b735e7 353 unit_dbus_interface_from_name(unit),
a7893c6b 354 "ControlGroup",
aa1936ea 355 &error,
a1da8583 356 &reply,
a7893c6b 357 "s");
a0e27019
LP
358 if (r < 0)
359 return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r));
aa1936ea 360
a7893c6b 361 r = sd_bus_message_read(reply, "s", &cgroup);
5b30bef8
LP
362 if (r < 0)
363 return bus_log_parse_error(r);
aa1936ea 364
a0e27019 365 if (isempty(cgroup))
9d127096
LP
366 return 0;
367
aa1936ea
LP
368 c = columns();
369 if (c > 18)
370 c -= 18;
371 else
372 c = 0;
373
a0e27019
LP
374 r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error);
375 if (r == -EBADR) {
376
377 if (arg_transport == BUS_TRANSPORT_REMOTE)
378 return 0;
379
380 /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
381
382 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
383 return 0;
384
0ff308c8 385 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags());
a0e27019
LP
386 } else if (r < 0)
387 return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r));
388
aa1936ea
LP
389 return 0;
390}
391
f48e75cb 392static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
4afd3348 393 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
878cd7e9
LP
394 int r;
395
396 assert(bus);
397 assert(name);
398 assert(prefix);
399 assert(prefix2);
400
401 r = sd_bus_call_method(bus,
402 "org.freedesktop.machine1",
403 "/org/freedesktop/machine1",
404 "org.freedesktop.machine1.Manager",
405 "GetMachineAddresses",
406 NULL,
407 &reply,
408 "s", name);
409 if (r < 0)
410 return r;
411
0dd25fb9 412 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
878cd7e9
LP
413 if (r < 0)
414 return bus_log_parse_error(r);
415
0dd25fb9
LP
416 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
417 int family;
878cd7e9
LP
418 const void *a;
419 size_t sz;
420 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
421
0dd25fb9 422 r = sd_bus_message_read(reply, "i", &family);
878cd7e9
LP
423 if (r < 0)
424 return bus_log_parse_error(r);
425
426 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
427 if (r < 0)
428 return bus_log_parse_error(r);
429
f48e75cb
LP
430 fputs(prefix, stdout);
431 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
432 if (family == AF_INET6 && ifi > 0)
433 printf("%%%i", ifi);
434 fputc('\n', stdout);
878cd7e9
LP
435
436 r = sd_bus_message_exit_container(reply);
437 if (r < 0)
438 return bus_log_parse_error(r);
439
440 if (prefix != prefix2)
441 prefix = prefix2;
442 }
443 if (r < 0)
444 return bus_log_parse_error(r);
445
446 r = sd_bus_message_exit_container(reply);
447 if (r < 0)
448 return bus_log_parse_error(r);
449
450 return 0;
451}
452
717603e3 453static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
4afd3348 454 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
717603e3
LP
455 const char *k, *v, *pretty = NULL;
456 int r;
457
458 assert(bus);
459 assert(name);
460 assert(prefix);
461
462 r = sd_bus_call_method(bus,
463 "org.freedesktop.machine1",
464 "/org/freedesktop/machine1",
465 "org.freedesktop.machine1.Manager",
466 "GetMachineOSRelease",
467 NULL,
468 &reply,
469 "s", name);
470 if (r < 0)
471 return r;
472
473 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
474 if (r < 0)
475 return bus_log_parse_error(r);
476
477 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
478 if (streq(k, "PRETTY_NAME"))
479 pretty = v;
480
481 }
482 if (r < 0)
483 return bus_log_parse_error(r);
484
485 r = sd_bus_message_exit_container(reply);
486 if (r < 0)
487 return bus_log_parse_error(r);
488
489 if (pretty)
490 printf("%s%s\n", prefix, pretty);
491
492 return 0;
493}
494
1ee306e1 495typedef struct MachineStatusInfo {
9f6eb1cd 496 char *name;
1ee306e1 497 sd_id128_t id;
9f6eb1cd
KS
498 char *class;
499 char *service;
89f7c846 500 char *unit;
9f6eb1cd 501 char *root_directory;
1ee306e1 502 pid_t leader;
8b0cc9a3 503 struct dual_timestamp timestamp;
f48e75cb
LP
504 int *netif;
505 unsigned n_netif;
1ee306e1
LP
506} MachineStatusInfo;
507
e7e55dbd
DH
508static void machine_status_info_clear(MachineStatusInfo *info) {
509 if (info) {
510 free(info->name);
511 free(info->class);
512 free(info->service);
513 free(info->unit);
514 free(info->root_directory);
515 free(info->netif);
516 zero(*info);
517 }
518}
519
a1da8583 520static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
1ee306e1
LP
521 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
522 char since2[FORMAT_TIMESTAMP_MAX], *s2;
f48e75cb
LP
523 int ifi = -1;
524
56159e0d 525 assert(bus);
1ee306e1
LP
526 assert(i);
527
528 fputs(strna(i->name), stdout);
529
530 if (!sd_id128_equal(i->id, SD_ID128_NULL))
531 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
532 else
533 putchar('\n');
534
8b0cc9a3
LP
535 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
536 s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
1ee306e1
LP
537
538 if (s1)
539 printf("\t Since: %s; %s\n", s2, s1);
540 else if (s2)
541 printf("\t Since: %s\n", s2);
542
543 if (i->leader > 0) {
544 _cleanup_free_ char *t = NULL;
545
546 printf("\t Leader: %u", (unsigned) i->leader);
547
548 get_process_comm(i->leader, &t);
549 if (t)
550 printf(" (%s)", t);
551
552 putchar('\n');
553 }
554
555 if (i->service) {
556 printf("\t Service: %s", i->service);
557
558 if (i->class)
559 printf("; class %s", i->class);
560
561 putchar('\n');
562 } else if (i->class)
563 printf("\t Class: %s\n", i->class);
564
1ee306e1
LP
565 if (i->root_directory)
566 printf("\t Root: %s\n", i->root_directory);
567
f48e75cb
LP
568 if (i->n_netif > 0) {
569 unsigned c;
570
571 fputs("\t Iface:", stdout);
572
573 for (c = 0; c < i->n_netif; c++) {
574 char name[IF_NAMESIZE+1] = "";
575
576 if (if_indextoname(i->netif[c], name)) {
577 fputc(' ', stdout);
578 fputs(name, stdout);
579
580 if (ifi < 0)
581 ifi = i->netif[c];
582 else
583 ifi = 0;
584 } else
585 printf(" %i", i->netif[c]);
586 }
587
588 fputc('\n', stdout);
589 }
590
591 print_addresses(bus, i->name, ifi,
878cd7e9
LP
592 "\t Address: ",
593 "\t ");
594
717603e3
LP
595 print_os_release(bus, i->name, "\t OS: ");
596
89f7c846
LP
597 if (i->unit) {
598 printf("\t Unit: %s\n", i->unit);
599 show_unit_cgroup(bus, i->unit, i->leader);
8b0cc9a3 600
ece174c5 601 if (arg_transport == BUS_TRANSPORT_LOCAL)
8b0cc9a3
LP
602
603 show_journal_by_unit(
604 stdout,
605 i->unit,
606 arg_output,
607 0,
608 i->timestamp.monotonic,
609 arg_lines,
610 0,
611 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
612 SD_JOURNAL_LOCAL_ONLY,
613 true,
614 NULL);
1ee306e1
LP
615 }
616}
617
f48e75cb
LP
618static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
619 MachineStatusInfo *i = userdata;
620 size_t l;
621 const void *v;
622 int r;
623
624 assert_cc(sizeof(int32_t) == sizeof(int));
625 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
626 if (r < 0)
627 return r;
e7e9b6bb
ZJS
628 if (r == 0)
629 return -EBADMSG;
f48e75cb
LP
630
631 i->n_netif = l / sizeof(int32_t);
632 i->netif = memdup(v, l);
633 if (!i->netif)
634 return -ENOMEM;
635
636 return 0;
637}
638
fefdc04b 639static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
a6c61602 640
9f6eb1cd 641 static const struct bus_properties_map map[] = {
8b0cc9a3
LP
642 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
643 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
644 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
645 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
646 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
647 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
648 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) },
649 { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) },
650 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
651 { "NetworkInterfaces", "ai", map_netif, 0 },
9f6eb1cd
KS
652 {}
653 };
a6c61602 654
e7e55dbd 655 _cleanup_(machine_status_info_clear) MachineStatusInfo info = {};
a1da8583
TG
656 int r;
657
56159e0d
LP
658 assert(verb);
659 assert(bus);
1ee306e1
LP
660 assert(path);
661 assert(new_line);
662
9f6eb1cd
KS
663 r = bus_map_all_properties(bus,
664 "org.freedesktop.machine1",
665 path,
666 map,
667 &info);
f647962d
MS
668 if (r < 0)
669 return log_error_errno(r, "Could not get properties: %m");
1ee306e1 670
1ee306e1
LP
671 if (*new_line)
672 printf("\n");
1ee306e1
LP
673 *new_line = true;
674
9f6eb1cd 675 print_machine_status_info(bus, &info);
1ee306e1 676
9f6eb1cd
KS
677 return r;
678}
1ee306e1 679
fefdc04b 680static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
9f6eb1cd 681 int r;
1ee306e1 682
56159e0d
LP
683 assert(bus);
684 assert(path);
685 assert(new_line);
686
9f6eb1cd
KS
687 if (*new_line)
688 printf("\n");
1ee306e1 689
9f6eb1cd 690 *new_line = true;
a1da8583 691
85500523 692 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_value, arg_all);
a1da8583 693 if (r < 0)
da927ba9 694 log_error_errno(r, "Could not get properties: %m");
1ee306e1 695
a7893c6b 696 return r;
1ee306e1
LP
697}
698
fefdc04b 699static int show_machine(int argc, char *argv[], void *userdata) {
56159e0d 700
4afd3348
LP
701 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
702 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
9f6eb1cd 703 bool properties, new_line = false;
56159e0d
LP
704 sd_bus *bus = userdata;
705 int r = 0, i;
1ee306e1
LP
706
707 assert(bus);
1ee306e1 708
56159e0d 709 properties = !strstr(argv[0], "status");
1ee306e1 710
ea4b98e6 711 pager_open(arg_no_pager, false);
1ee306e1 712
56159e0d 713 if (properties && argc <= 1) {
a1da8583 714
9f6eb1cd 715 /* If no argument is specified, inspect the manager
1ee306e1 716 * itself */
fefdc04b 717 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
8c841f21 718 if (r < 0)
9f6eb1cd 719 return r;
1ee306e1
LP
720 }
721
56159e0d 722 for (i = 1; i < argc; i++) {
1ee306e1
LP
723 const char *path = NULL;
724
e138e7d7
ZJS
725 r = sd_bus_call_method(bus,
726 "org.freedesktop.machine1",
727 "/org/freedesktop/machine1",
728 "org.freedesktop.machine1.Manager",
729 "GetMachine",
730 &error,
731 &reply,
732 "s", argv[i]);
a1da8583
TG
733 if (r < 0) {
734 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
735 return r;
736 }
737
738 r = sd_bus_message_read(reply, "o", &path);
5b30bef8
LP
739 if (r < 0)
740 return bus_log_parse_error(r);
1ee306e1 741
9f6eb1cd 742 if (properties)
fefdc04b 743 r = show_machine_properties(bus, path, &new_line);
9f6eb1cd 744 else
fefdc04b
LP
745 r = show_machine_info(argv[0], bus, path, &new_line);
746 }
747
748 return r;
749}
750
751typedef struct ImageStatusInfo {
752 char *name;
753 char *path;
754 char *type;
755 int read_only;
756 usec_t crtime;
757 usec_t mtime;
c19de711 758 uint64_t usage;
b6b18498 759 uint64_t limit;
c19de711 760 uint64_t usage_exclusive;
b6b18498 761 uint64_t limit_exclusive;
fefdc04b
LP
762} ImageStatusInfo;
763
e7e55dbd
DH
764static void image_status_info_clear(ImageStatusInfo *info) {
765 if (info) {
766 free(info->name);
767 free(info->path);
768 free(info->type);
769 zero(*info);
770 }
771}
772
fefdc04b
LP
773static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
774 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
775 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
b6b18498
LP
776 char bs[FORMAT_BYTES_MAX], *s3;
777 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
fefdc04b
LP
778
779 assert(bus);
780 assert(i);
781
782 if (i->name) {
783 fputs(i->name, stdout);
784 putchar('\n');
785 }
786
9a14fb62 787 if (i->type)
fefdc04b
LP
788 printf("\t Type: %s\n", i->type);
789
790 if (i->path)
791 printf("\t Path: %s\n", i->path);
792
793 printf("\t RO: %s%s%s\n",
794 i->read_only ? ansi_highlight_red() : "",
795 i->read_only ? "read-only" : "writable",
1fc464f6 796 i->read_only ? ansi_normal() : "");
fefdc04b
LP
797
798 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
799 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
b6b18498 800 if (s1 && s2)
fefdc04b
LP
801 printf("\t Created: %s; %s\n", s2, s1);
802 else if (s2)
803 printf("\t Created: %s\n", s2);
804
805 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
806 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
b6b18498 807 if (s1 && s2)
fefdc04b
LP
808 printf("\tModified: %s; %s\n", s2, s1);
809 else if (s2)
810 printf("\tModified: %s\n", s2);
b6b18498 811
c19de711
LP
812 s3 = format_bytes(bs, sizeof(bs), i->usage);
813 s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL;
b6b18498 814 if (s3 && s4)
c19de711 815 printf("\t Usage: %s (exclusive: %s)\n", s3, s4);
b6b18498 816 else if (s3)
c19de711 817 printf("\t Usage: %s\n", s3);
b6b18498
LP
818
819 s3 = format_bytes(bs, sizeof(bs), i->limit);
820 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
821 if (s3 && s4)
822 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
823 else if (s3)
824 printf("\t Limit: %s\n", s3);
fefdc04b
LP
825}
826
160e3793 827static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
fefdc04b
LP
828
829 static const struct bus_properties_map map[] = {
b6b18498
LP
830 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
831 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
832 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
833 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
834 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
835 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
c19de711 836 { "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) },
b6b18498 837 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
c19de711 838 { "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) },
b6b18498 839 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
fefdc04b
LP
840 {}
841 };
842
e7e55dbd 843 _cleanup_(image_status_info_clear) ImageStatusInfo info = {};
fefdc04b
LP
844 int r;
845
fefdc04b
LP
846 assert(bus);
847 assert(path);
848 assert(new_line);
849
850 r = bus_map_all_properties(bus,
851 "org.freedesktop.machine1",
852 path,
853 map,
854 &info);
855 if (r < 0)
856 return log_error_errno(r, "Could not get properties: %m");
857
858 if (*new_line)
859 printf("\n");
860 *new_line = true;
861
862 print_image_status_info(bus, &info);
863
fefdc04b
LP
864 return r;
865}
866
160e3793
LP
867typedef struct PoolStatusInfo {
868 char *path;
869 uint64_t usage;
870 uint64_t limit;
871} PoolStatusInfo;
872
e7e55dbd
DH
873static void pool_status_info_clear(PoolStatusInfo *info) {
874 if (info) {
875 free(info->path);
876 zero(*info);
877 info->usage = -1;
878 info->limit = -1;
879 }
880}
881
160e3793
LP
882static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) {
883 char bs[FORMAT_BYTES_MAX], *s;
884
885 if (i->path)
886 printf("\t Path: %s\n", i->path);
887
888 s = format_bytes(bs, sizeof(bs), i->usage);
889 if (s)
890 printf("\t Usage: %s\n", s);
891
892 s = format_bytes(bs, sizeof(bs), i->limit);
893 if (s)
894 printf("\t Limit: %s\n", s);
895}
896
897static int show_pool_info(sd_bus *bus) {
898
899 static const struct bus_properties_map map[] = {
900 { "PoolPath", "s", NULL, offsetof(PoolStatusInfo, path) },
901 { "PoolUsage", "t", NULL, offsetof(PoolStatusInfo, usage) },
902 { "PoolLimit", "t", NULL, offsetof(PoolStatusInfo, limit) },
903 {}
904 };
905
e7e55dbd 906 _cleanup_(pool_status_info_clear) PoolStatusInfo info = {
160e3793
LP
907 .usage = (uint64_t) -1,
908 .limit = (uint64_t) -1,
909 };
910 int r;
911
912 assert(bus);
913
914 r = bus_map_all_properties(bus,
915 "org.freedesktop.machine1",
916 "/org/freedesktop/machine1",
917 map,
918 &info);
919 if (r < 0)
920 return log_error_errno(r, "Could not get properties: %m");
921
922 print_pool_status_info(bus, &info);
923
160e3793
LP
924 return 0;
925}
926
927
fefdc04b
LP
928static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
929 int r;
930
931 assert(bus);
932 assert(path);
933 assert(new_line);
934
935 if (*new_line)
936 printf("\n");
937
938 *new_line = true;
939
85500523 940 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_value, arg_all);
fefdc04b
LP
941 if (r < 0)
942 log_error_errno(r, "Could not get properties: %m");
943
944 return r;
945}
946
947static int show_image(int argc, char *argv[], void *userdata) {
948
4afd3348
LP
949 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
950 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
fefdc04b
LP
951 bool properties, new_line = false;
952 sd_bus *bus = userdata;
953 int r = 0, i;
954
955 assert(bus);
956
957 properties = !strstr(argv[0], "status");
958
ea4b98e6 959 pager_open(arg_no_pager, false);
fefdc04b 960
160e3793 961 if (argc <= 1) {
fefdc04b
LP
962
963 /* If no argument is specified, inspect the manager
964 * itself */
160e3793
LP
965
966 if (properties)
967 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
968 else
969 r = show_pool_info(bus);
fefdc04b
LP
970 if (r < 0)
971 return r;
972 }
973
974 for (i = 1; i < argc; i++) {
975 const char *path = NULL;
976
977 r = sd_bus_call_method(
90adaa25
LP
978 bus,
979 "org.freedesktop.machine1",
980 "/org/freedesktop/machine1",
981 "org.freedesktop.machine1.Manager",
982 "GetImage",
983 &error,
984 &reply,
985 "s", argv[i]);
fefdc04b
LP
986 if (r < 0) {
987 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
988 return r;
989 }
990
991 r = sd_bus_message_read(reply, "o", &path);
992 if (r < 0)
993 return bus_log_parse_error(r);
994
995 if (properties)
996 r = show_image_properties(bus, path, &new_line);
997 else
160e3793 998 r = show_image_info(bus, path, &new_line);
1ee306e1
LP
999 }
1000
9f6eb1cd 1001 return r;
1ee306e1
LP
1002}
1003
56159e0d 1004static int kill_machine(int argc, char *argv[], void *userdata) {
4afd3348 1005 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d 1006 sd_bus *bus = userdata;
90adaa25 1007 int r, i;
1ee306e1 1008
56159e0d 1009 assert(bus);
1ee306e1 1010
acf97e21
LP
1011 polkit_agent_open_if_enabled();
1012
1ee306e1
LP
1013 if (!arg_kill_who)
1014 arg_kill_who = "all";
1015
56159e0d 1016 for (i = 1; i < argc; i++) {
a1da8583 1017 r = sd_bus_call_method(
56159e0d
LP
1018 bus,
1019 "org.freedesktop.machine1",
1020 "/org/freedesktop/machine1",
1021 "org.freedesktop.machine1.Manager",
1022 "KillMachine",
1023 &error,
1024 NULL,
1025 "ssi", argv[i], arg_kill_who, arg_signal);
a1da8583
TG
1026 if (r < 0) {
1027 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
1ee306e1 1028 return r;
a1da8583 1029 }
1ee306e1
LP
1030 }
1031
1032 return 0;
1033}
1034
56159e0d 1035static int reboot_machine(int argc, char *argv[], void *userdata) {
1dba654b
LP
1036 arg_kill_who = "leader";
1037 arg_signal = SIGINT; /* sysvinit + systemd */
1ee306e1 1038
56159e0d 1039 return kill_machine(argc, argv, userdata);
1dba654b 1040}
1ee306e1 1041
56159e0d 1042static int poweroff_machine(int argc, char *argv[], void *userdata) {
1dba654b
LP
1043 arg_kill_who = "leader";
1044 arg_signal = SIGRTMIN+4; /* only systemd */
1ee306e1 1045
56159e0d 1046 return kill_machine(argc, argv, userdata);
1ee306e1
LP
1047}
1048
56159e0d 1049static int terminate_machine(int argc, char *argv[], void *userdata) {
4afd3348 1050 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d 1051 sd_bus *bus = userdata;
2723b3b5 1052 int r, i;
923d8fd3 1053
56159e0d 1054 assert(bus);
923d8fd3 1055
acf97e21
LP
1056 polkit_agent_open_if_enabled();
1057
56159e0d 1058 for (i = 1; i < argc; i++) {
923d8fd3
LP
1059 r = sd_bus_call_method(
1060 bus,
1061 "org.freedesktop.machine1",
1062 "/org/freedesktop/machine1",
1063 "org.freedesktop.machine1.Manager",
1dba654b 1064 "TerminateMachine",
923d8fd3 1065 &error,
1dba654b 1066 NULL,
56159e0d 1067 "s", argv[i]);
923d8fd3 1068 if (r < 0) {
1dba654b 1069 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
923d8fd3
LP
1070 return r;
1071 }
923d8fd3
LP
1072 }
1073
1074 return 0;
1075}
1076
0370612e 1077static int copy_files(int argc, char *argv[], void *userdata) {
4afd3348 1078 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1fe6fa16
RM
1079 _cleanup_free_ char *abs_host_path = NULL;
1080 char *dest, *host_path, *container_path;
0370612e
LP
1081 sd_bus *bus = userdata;
1082 bool copy_from;
785890ac
LP
1083 int r;
1084
1085 assert(bus);
0370612e 1086
2723b3b5
LP
1087 polkit_agent_open_if_enabled();
1088
0370612e 1089 copy_from = streq(argv[0], "copy-from");
1fe6fa16
RM
1090 dest = argv[3] ?: argv[2];
1091 host_path = copy_from ? dest : argv[2];
1092 container_path = copy_from ? argv[2] : dest;
1093
1094 if (!path_is_absolute(host_path)) {
0f474365
LP
1095 r = path_make_absolute_cwd(host_path, &abs_host_path);
1096 if (r < 0)
1097 return log_error_errno(r, "Failed to make path absolute: %m");
1098
1fe6fa16
RM
1099 host_path = abs_host_path;
1100 }
785890ac
LP
1101
1102 r = sd_bus_call_method(
1103 bus,
1104 "org.freedesktop.machine1",
1105 "/org/freedesktop/machine1",
1106 "org.freedesktop.machine1.Manager",
0370612e 1107 copy_from ? "CopyFromMachine" : "CopyToMachine",
785890ac 1108 &error,
0370612e
LP
1109 NULL,
1110 "sss",
1111 argv[1],
1fe6fa16
RM
1112 copy_from ? container_path : host_path,
1113 copy_from ? host_path : container_path);
0f474365
LP
1114 if (r < 0)
1115 return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r));
f2cbe59e
LP
1116
1117 return 0;
1118}
1119
56159e0d 1120static int bind_mount(int argc, char *argv[], void *userdata) {
4afd3348 1121 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
56159e0d 1122 sd_bus *bus = userdata;
785890ac
LP
1123 int r;
1124
56159e0d
LP
1125 assert(bus);
1126
2723b3b5
LP
1127 polkit_agent_open_if_enabled();
1128
90adaa25
LP
1129 r = sd_bus_call_method(
1130 bus,
1131 "org.freedesktop.machine1",
1132 "/org/freedesktop/machine1",
1133 "org.freedesktop.machine1.Manager",
1134 "BindMountMachine",
1135 &error,
1136 NULL,
1137 "sssbb",
1138 argv[1],
1139 argv[2],
1140 argv[3],
1141 arg_read_only,
1142 arg_mkdir);
785890ac 1143 if (r < 0) {
90adaa25
LP
1144 log_error("Failed to bind mount: %s", bus_error_message(&error, -r));
1145 return r;
785890ac
LP
1146 }
1147
90adaa25 1148 return 0;
785890ac
LP
1149}
1150
19070062 1151static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
0ec5543c
LP
1152 PTYForward ** forward = (PTYForward**) userdata;
1153 int r;
1154
0ec5543c
LP
1155 assert(m);
1156 assert(forward);
1157
1158 if (*forward) {
1159 /* If the forwarder is already initialized, tell it to
da054c37
LP
1160 * exit on the next vhangup(), so that we still flush
1161 * out what might be queued and exit then. */
0ec5543c 1162
da054c37 1163 r = pty_forward_set_ignore_vhangup(*forward, false);
0ec5543c
LP
1164 if (r >= 0)
1165 return 0;
1166
da054c37 1167 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
0ec5543c
LP
1168 }
1169
1170 /* On error, or when the forwarder is not initialized yet, quit immediately */
19070062 1171 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
0ec5543c
LP
1172 return 0;
1173}
1174
ae3dde80 1175static int process_forward(sd_event *event, PTYForward **forward, int master, PTYForwardFlags flags, const char *name) {
c454426c
LP
1176 char last_char = 0;
1177 bool machine_died;
1178 int ret = 0, r;
1179
1180 assert(event);
1181 assert(master >= 0);
1182 assert(name);
1183
1184 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
1185
91913f58
LP
1186 if (streq(name, ".host"))
1187 log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
1188 else
1189 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
c454426c
LP
1190
1191 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1192 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1193
ae3dde80 1194 r = pty_forward_new(event, master, flags, forward);
c454426c
LP
1195 if (r < 0)
1196 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1197
1198 r = sd_event_loop(event);
1199 if (r < 0)
1200 return log_error_errno(r, "Failed to run event loop: %m");
1201
1202 pty_forward_get_last_char(*forward, &last_char);
1203
1204 machine_died =
ae3dde80 1205 (flags & PTY_FORWARD_IGNORE_VHANGUP) &&
c454426c
LP
1206 pty_forward_get_ignore_vhangup(*forward) == 0;
1207
1208 *forward = pty_forward_free(*forward);
1209
1210 if (last_char != '\n')
1211 fputc('\n', stdout);
1212
1213 if (machine_died)
1214 log_info("Machine %s terminated.", name);
91913f58
LP
1215 else if (streq(name, ".host"))
1216 log_info("Connection to the local host terminated.");
c454426c
LP
1217 else
1218 log_info("Connection to machine %s terminated.", name);
1219
1220 sd_event_get_exit_code(event, &ret);
1221 return ret;
1222}
1223
56159e0d 1224static int login_machine(int argc, char *argv[], void *userdata) {
4afd3348
LP
1225 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1226 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
023fb90b 1227 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
4afd3348
LP
1228 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
1229 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
c454426c 1230 int master = -1, r;
56159e0d 1231 sd_bus *bus = userdata;
91913f58 1232 const char *pty, *match, *machine;
04d39279
LP
1233
1234 assert(bus);
04d39279 1235
c454426c
LP
1236 if (!strv_isempty(arg_setenv) || arg_uid) {
1237 log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1238 return -EINVAL;
1239 }
1240
bf441e3d 1241 if (arg_transport != BUS_TRANSPORT_LOCAL &&
de33fc62 1242 arg_transport != BUS_TRANSPORT_MACHINE) {
923d8fd3 1243 log_error("Login only supported on local machines.");
15411c0c 1244 return -EOPNOTSUPP;
04d39279
LP
1245 }
1246
acf97e21
LP
1247 polkit_agent_open_if_enabled();
1248
023fb90b 1249 r = sd_event_default(&event);
f647962d
MS
1250 if (r < 0)
1251 return log_error_errno(r, "Failed to get event loop: %m");
023fb90b
LP
1252
1253 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
1254 if (r < 0)
1255 return log_error_errno(r, "Failed to attach bus to event loop: %m");
023fb90b 1256
91913f58
LP
1257 machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1];
1258
63c372cb 1259 match = strjoina("type='signal',"
c454426c
LP
1260 "sender='org.freedesktop.machine1',"
1261 "path='/org/freedesktop/machine1',",
1262 "interface='org.freedesktop.machine1.Manager',"
1263 "member='MachineRemoved',"
91913f58 1264 "arg0='", machine, "'");
0ec5543c
LP
1265
1266 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1267 if (r < 0)
1268 return log_error_errno(r, "Failed to add machine removal match: %m");
1269
2723b3b5
LP
1270 r = sd_bus_call_method(
1271 bus,
1272 "org.freedesktop.machine1",
1273 "/org/freedesktop/machine1",
1274 "org.freedesktop.machine1.Manager",
1275 "OpenMachineLogin",
1276 &error,
1277 &reply,
91913f58 1278 "s", machine);
40205d70 1279 if (r < 0) {
c454426c 1280 log_error("Failed to get login PTY: %s", bus_error_message(&error, -r));
785890ac 1281 return r;
40205d70 1282 }
04d39279 1283
40205d70
LP
1284 r = sd_bus_message_read(reply, "hs", &master, &pty);
1285 if (r < 0)
ee451d76 1286 return bus_log_parse_error(r);
04d39279 1287
ae3dde80 1288 return process_forward(event, &forward, master, PTY_FORWARD_IGNORE_VHANGUP, machine);
c454426c 1289}
04d39279 1290
c454426c 1291static int shell_machine(int argc, char *argv[], void *userdata) {
4afd3348
LP
1292 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
1293 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
c454426c 1294 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
4afd3348
LP
1295 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
1296 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
c454426c
LP
1297 int master = -1, r;
1298 sd_bus *bus = userdata;
ef3100e9 1299 const char *pty, *match, *machine, *path, *uid = NULL;
04d39279 1300
c454426c
LP
1301 assert(bus);
1302
1303 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1304 arg_transport != BUS_TRANSPORT_MACHINE) {
1305 log_error("Shell only supported on local machines.");
1306 return -EOPNOTSUPP;
1307 }
1308
89fec318
LP
1309 /* Pass $TERM to shell session, if not explicitly specified. */
1310 if (!strv_find_prefix(arg_setenv, "TERM=")) {
1311 const char *t;
1312
1313 t = strv_find_prefix(environ, "TERM=");
1314 if (t) {
1315 if (strv_extend(&arg_setenv, t) < 0)
1316 return log_oom();
1317 }
1318 }
1319
c454426c 1320 polkit_agent_open_if_enabled();
023fb90b 1321
c454426c 1322 r = sd_event_default(&event);
f647962d 1323 if (r < 0)
c454426c 1324 return log_error_errno(r, "Failed to get event loop: %m");
023fb90b 1325
c454426c 1326 r = sd_bus_attach_event(bus, event, 0);
f647962d 1327 if (r < 0)
c454426c 1328 return log_error_errno(r, "Failed to attach bus to event loop: %m");
04d39279 1329
ef3100e9
LP
1330 machine = argc < 2 || isempty(argv[1]) ? NULL : argv[1];
1331
1332 if (arg_uid)
1333 uid = arg_uid;
1334 else if (machine) {
1335 const char *at;
1336
1337 at = strchr(machine, '@');
1338 if (at) {
1339 uid = strndupa(machine, at - machine);
1340 machine = at + 1;
1341 }
1342 }
1343
1344 if (isempty(machine))
1345 machine = ".host";
91913f58 1346
c454426c
LP
1347 match = strjoina("type='signal',"
1348 "sender='org.freedesktop.machine1',"
1349 "path='/org/freedesktop/machine1',",
1350 "interface='org.freedesktop.machine1.Manager',"
1351 "member='MachineRemoved',"
91913f58 1352 "arg0='", machine, "'");
c7b7d449 1353
c454426c
LP
1354 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1355 if (r < 0)
1356 return log_error_errno(r, "Failed to add machine removal match: %m");
023fb90b 1357
c454426c
LP
1358 r = sd_bus_message_new_method_call(
1359 bus,
1360 &m,
1361 "org.freedesktop.machine1",
1362 "/org/freedesktop/machine1",
1363 "org.freedesktop.machine1.Manager",
1364 "OpenMachineShell");
1365 if (r < 0)
1366 return bus_log_create_error(r);
04d39279 1367
91913f58
LP
1368 path = argc < 3 || isempty(argv[2]) ? NULL : argv[2];
1369
ef3100e9 1370 r = sd_bus_message_append(m, "sss", machine, uid, path);
c454426c
LP
1371 if (r < 0)
1372 return bus_log_create_error(r);
04d39279 1373
91913f58 1374 r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2);
c454426c
LP
1375 if (r < 0)
1376 return bus_log_create_error(r);
1377
1378 r = sd_bus_message_append_strv(m, arg_setenv);
1379 if (r < 0)
1380 return bus_log_create_error(r);
1381
1382 r = sd_bus_call(bus, m, 0, &error, &reply);
1383 if (r < 0) {
1384 log_error("Failed to get shell PTY: %s", bus_error_message(&error, -r));
1385 return r;
1386 }
1387
1388 r = sd_bus_message_read(reply, "hs", &master, &pty);
1389 if (r < 0)
1390 return bus_log_parse_error(r);
1391
40e1f4ea 1392 return process_forward(event, &forward, master, 0, machine);
04d39279
LP
1393}
1394
08682124 1395static int remove_image(int argc, char *argv[], void *userdata) {
4afd3348 1396 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
08682124 1397 sd_bus *bus = userdata;
ebd93cb6 1398 int r, i;
08682124
LP
1399
1400 assert(bus);
1401
acf97e21
LP
1402 polkit_agent_open_if_enabled();
1403
08682124 1404 for (i = 1; i < argc; i++) {
08682124
LP
1405 r = sd_bus_call_method(
1406 bus,
1407 "org.freedesktop.machine1",
1408 "/org/freedesktop/machine1",
1409 "org.freedesktop.machine1.Manager",
1410 "RemoveImage",
1411 &error,
1412 NULL,
1413 "s", argv[i]);
1414 if (r < 0) {
1415 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1416 return r;
1417 }
1418 }
1419
1420 return 0;
1421}
1422
ebd93cb6 1423static int rename_image(int argc, char *argv[], void *userdata) {
4afd3348 1424 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
ebd93cb6
LP
1425 sd_bus *bus = userdata;
1426 int r;
1427
acf97e21
LP
1428 polkit_agent_open_if_enabled();
1429
ebd93cb6
LP
1430 r = sd_bus_call_method(
1431 bus,
1432 "org.freedesktop.machine1",
1433 "/org/freedesktop/machine1",
1434 "org.freedesktop.machine1.Manager",
1435 "RenameImage",
1436 &error,
1437 NULL,
1438 "ss", argv[1], argv[2]);
1439 if (r < 0) {
1440 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1441 return r;
1442 }
1443
1444 return 0;
1445}
1446
1447static int clone_image(int argc, char *argv[], void *userdata) {
4afd3348 1448 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
ebd93cb6
LP
1449 sd_bus *bus = userdata;
1450 int r;
1451
acf97e21
LP
1452 polkit_agent_open_if_enabled();
1453
ebd93cb6
LP
1454 r = sd_bus_call_method(
1455 bus,
1456 "org.freedesktop.machine1",
1457 "/org/freedesktop/machine1",
1458 "org.freedesktop.machine1.Manager",
1459 "CloneImage",
1460 &error,
1461 NULL,
1462 "ssb", argv[1], argv[2], arg_read_only);
1463 if (r < 0) {
1464 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1465 return r;
1466 }
1467
1468 return 0;
1469}
1470
1471static int read_only_image(int argc, char *argv[], void *userdata) {
4afd3348 1472 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
ebd93cb6
LP
1473 sd_bus *bus = userdata;
1474 int b = true, r;
1475
1476 if (argc > 2) {
1477 b = parse_boolean(argv[2]);
1478 if (b < 0) {
1479 log_error("Failed to parse boolean argument: %s", argv[2]);
1480 return -EINVAL;
1481 }
1482 }
1483
acf97e21
LP
1484 polkit_agent_open_if_enabled();
1485
ebd93cb6
LP
1486 r = sd_bus_call_method(
1487 bus,
1488 "org.freedesktop.machine1",
1489 "/org/freedesktop/machine1",
1490 "org.freedesktop.machine1.Manager",
1491 "MarkImageReadOnly",
1492 &error,
1493 NULL,
1494 "sb", argv[1], b);
1495 if (r < 0) {
1496 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1497 return r;
1498 }
1499
1500 return 0;
1501}
1502
7410616c
LP
1503static int make_service_name(const char *name, char **ret) {
1504 _cleanup_free_ char *e = NULL;
1505 int r;
1506
1507 assert(name);
1508 assert(ret);
1509
1510 if (!machine_name_is_valid(name)) {
1511 log_error("Invalid machine name %s.", name);
1512 return -EINVAL;
1513 }
1514
1515 e = unit_name_escape(name);
1516 if (!e)
1517 return log_oom();
1518
1519 r = unit_name_build("systemd-nspawn", e, ".service", ret);
1520 if (r < 0)
1521 return log_error_errno(r, "Failed to build unit name: %m");
1522
1523 return 0;
1524}
1525
ebd011d9 1526static int start_machine(int argc, char *argv[], void *userdata) {
4afd3348 1527 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
ebd011d9
LP
1528 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1529 sd_bus *bus = userdata;
1530 int r, i;
1531
1532 assert(bus);
1533
acf97e21
LP
1534 polkit_agent_open_if_enabled();
1535
ebd011d9
LP
1536 r = bus_wait_for_jobs_new(bus, &w);
1537 if (r < 0)
1538 return log_oom();
1539
1540 for (i = 1; i < argc; i++) {
4afd3348 1541 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
7410616c 1542 _cleanup_free_ char *unit = NULL;
ebd011d9
LP
1543 const char *object;
1544
7410616c
LP
1545 r = make_service_name(argv[i], &unit);
1546 if (r < 0)
1547 return r;
ebd011d9 1548
2723b3b5 1549 r = sd_bus_call_method(
ebd011d9 1550 bus,
ebd011d9
LP
1551 "org.freedesktop.systemd1",
1552 "/org/freedesktop/systemd1",
1553 "org.freedesktop.systemd1.Manager",
2723b3b5
LP
1554 "StartUnit",
1555 &error,
1556 &reply,
1557 "ss", unit, "fail");
ebd011d9
LP
1558 if (r < 0) {
1559 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1560 return r;
1561 }
1562
1563 r = sd_bus_message_read(reply, "o", &object);
1564 if (r < 0)
1565 return bus_log_parse_error(r);
1566
1567 r = bus_wait_for_jobs_add(w, object);
1568 if (r < 0)
1569 return log_oom();
1570 }
1571
10ba4835 1572 r = bus_wait_for_jobs(w, arg_quiet, NULL);
ebd011d9
LP
1573 if (r < 0)
1574 return r;
1575
1576 return 0;
1577}
1578
d8f52ed2 1579static int enable_machine(int argc, char *argv[], void *userdata) {
4afd3348
LP
1580 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
1581 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
d8f52ed2
LP
1582 int carries_install_info = 0;
1583 const char *method = NULL;
1584 sd_bus *bus = userdata;
1585 int r, i;
1586
1587 assert(bus);
1588
acf97e21
LP
1589 polkit_agent_open_if_enabled();
1590
d8f52ed2
LP
1591 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1592
1593 r = sd_bus_message_new_method_call(
1594 bus,
1595 &m,
1596 "org.freedesktop.systemd1",
1597 "/org/freedesktop/systemd1",
1598 "org.freedesktop.systemd1.Manager",
1599 method);
1600 if (r < 0)
1601 return bus_log_create_error(r);
1602
d8f52ed2
LP
1603 r = sd_bus_message_open_container(m, 'a', "s");
1604 if (r < 0)
1605 return bus_log_create_error(r);
1606
1607 for (i = 1; i < argc; i++) {
90615ad7 1608 _cleanup_free_ char *unit = NULL;
d8f52ed2 1609
7410616c
LP
1610 r = make_service_name(argv[i], &unit);
1611 if (r < 0)
1612 return r;
d8f52ed2
LP
1613
1614 r = sd_bus_message_append(m, "s", unit);
1615 if (r < 0)
1616 return bus_log_create_error(r);
1617 }
1618
1619 r = sd_bus_message_close_container(m);
1620 if (r < 0)
1621 return bus_log_create_error(r);
1622
1623 if (streq(argv[0], "enable"))
1624 r = sd_bus_message_append(m, "bb", false, false);
1625 else
1626 r = sd_bus_message_append(m, "b", false);
1627 if (r < 0)
1628 return bus_log_create_error(r);
1629
1630 r = sd_bus_call(bus, m, 0, &error, &reply);
1631 if (r < 0) {
1632 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1633 return r;
1634 }
1635
1636 if (streq(argv[0], "enable")) {
1637 r = sd_bus_message_read(reply, "b", carries_install_info);
1638 if (r < 0)
1639 return bus_log_parse_error(r);
1640 }
1641
57ab2eab 1642 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
d8f52ed2
LP
1643 if (r < 0)
1644 return r;
1645
2723b3b5 1646 r = sd_bus_call_method(
d8f52ed2 1647 bus,
d8f52ed2
LP
1648 "org.freedesktop.systemd1",
1649 "/org/freedesktop/systemd1",
1650 "org.freedesktop.systemd1.Manager",
2723b3b5
LP
1651 "Reload",
1652 &error,
1653 NULL,
1654 NULL);
d8f52ed2
LP
1655 if (r < 0) {
1656 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1657 return r;
1658 }
1659
1660 return 0;
1661}
1662
19070062 1663static int match_log_message(sd_bus_message *m, void *userdata, sd_bus_error *error) {
6adf7b5e 1664 const char **our_path = userdata, *line;
3d7415f4
LP
1665 unsigned priority;
1666 int r;
1667
3d7415f4 1668 assert(m);
6adf7b5e 1669 assert(our_path);
3d7415f4
LP
1670
1671 r = sd_bus_message_read(m, "us", &priority, &line);
1672 if (r < 0) {
1673 bus_log_parse_error(r);
1674 return 0;
1675 }
1676
6adf7b5e 1677 if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
3d7415f4
LP
1678 return 0;
1679
1680 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
1681 return 0;
1682
1683 log_full(priority, "%s", line);
1684 return 0;
1685}
1686
19070062 1687static int match_transfer_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
6adf7b5e 1688 const char **our_path = userdata, *path, *result;
3d7415f4
LP
1689 uint32_t id;
1690 int r;
1691
3d7415f4 1692 assert(m);
6adf7b5e 1693 assert(our_path);
3d7415f4
LP
1694
1695 r = sd_bus_message_read(m, "uos", &id, &path, &result);
1696 if (r < 0) {
1697 bus_log_parse_error(r);
1698 return 0;
1699 }
1700
6adf7b5e 1701 if (!streq_ptr(*our_path, path))
3d7415f4
LP
1702 return 0;
1703
19070062 1704 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), !streq_ptr(result, "done"));
6adf7b5e
LP
1705 return 0;
1706}
1707
1708static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
1709 assert(s);
1710 assert(si);
1711
1712 if (!arg_quiet)
cc98b302 1713 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
6adf7b5e
LP
1714
1715 sd_event_exit(sd_event_source_get_event(s), EINTR);
3d7415f4
LP
1716 return 0;
1717}
1718
587fec42 1719static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
4afd3348
LP
1720 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1721 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1722 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1723 _cleanup_(sd_event_unrefp) sd_event* event = NULL;
6adf7b5e 1724 const char *path = NULL;
3d7415f4
LP
1725 uint32_t id;
1726 int r;
1727
1728 assert(bus);
1729 assert(m);
1730
1731 polkit_agent_open_if_enabled();
1732
6adf7b5e
LP
1733 r = sd_event_default(&event);
1734 if (r < 0)
1735 return log_error_errno(r, "Failed to get event loop: %m");
1736
1737 r = sd_bus_attach_event(bus, event, 0);
1738 if (r < 0)
1739 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1740
3d7415f4
LP
1741 r = sd_bus_add_match(
1742 bus,
1743 &slot_job_removed,
1744 "type='signal',"
1745 "sender='org.freedesktop.import1',"
1746 "interface='org.freedesktop.import1.Manager',"
1747 "member='TransferRemoved',"
1748 "path='/org/freedesktop/import1'",
6adf7b5e 1749 match_transfer_removed, &path);
3d7415f4
LP
1750 if (r < 0)
1751 return log_error_errno(r, "Failed to install match: %m");
1752
1753 r = sd_bus_add_match(
1754 bus,
1755 &slot_log_message,
1756 "type='signal',"
1757 "sender='org.freedesktop.import1',"
1758 "interface='org.freedesktop.import1.Transfer',"
1759 "member='LogMessage'",
6adf7b5e 1760 match_log_message, &path);
3d7415f4
LP
1761 if (r < 0)
1762 return log_error_errno(r, "Failed to install match: %m");
1763
1764 r = sd_bus_call(bus, m, 0, &error, &reply);
1765 if (r < 0) {
953d28cc 1766 log_error("Failed to transfer image: %s", bus_error_message(&error, -r));
3d7415f4
LP
1767 return r;
1768 }
1769
6adf7b5e 1770 r = sd_bus_message_read(reply, "uo", &id, &path);
3d7415f4
LP
1771 if (r < 0)
1772 return bus_log_parse_error(r);
1773
72c0a2c2 1774 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
3d7415f4 1775
6adf7b5e
LP
1776 if (!arg_quiet)
1777 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
3d7415f4 1778
6adf7b5e
LP
1779 sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
1780 sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
1781
1782 r = sd_event_loop(event);
1783 if (r < 0)
1784 return log_error_errno(r, "Failed to run event loop: %m");
3d7415f4 1785
6adf7b5e 1786 return -r;
3d7415f4
LP
1787}
1788
b6e676ce 1789static int import_tar(int argc, char *argv[], void *userdata) {
4afd3348 1790 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
b6e676ce
LP
1791 _cleanup_free_ char *ll = NULL;
1792 _cleanup_close_ int fd = -1;
1793 const char *local = NULL, *path = NULL;
1794 sd_bus *bus = userdata;
1795 int r;
1796
1797 assert(bus);
1798
1799 if (argc >= 2)
1800 path = argv[1];
1801 if (isempty(path) || streq(path, "-"))
1802 path = NULL;
1803
1804 if (argc >= 3)
1805 local = argv[2];
1806 else if (path)
1807 local = basename(path);
1808 if (isempty(local) || streq(local, "-"))
1809 local = NULL;
1810
1811 if (!local) {
1812 log_error("Need either path or local name.");
1813 return -EINVAL;
1814 }
1815
1816 r = tar_strip_suffixes(local, &ll);
1817 if (r < 0)
1818 return log_oom();
1819
1820 local = ll;
1821
1822 if (!machine_name_is_valid(local)) {
1823 log_error("Local name %s is not a suitable machine name.", local);
1824 return -EINVAL;
1825 }
1826
1827 if (path) {
1828 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
1829 if (fd < 0)
1830 return log_error_errno(errno, "Failed to open %s: %m", path);
1831 }
1832
1833 r = sd_bus_message_new_method_call(
1834 bus,
1835 &m,
1836 "org.freedesktop.import1",
1837 "/org/freedesktop/import1",
1838 "org.freedesktop.import1.Manager",
1839 "ImportTar");
1840 if (r < 0)
1841 return bus_log_create_error(r);
1842
1843 r = sd_bus_message_append(
1844 m,
1845 "hsbb",
1846 fd >= 0 ? fd : STDIN_FILENO,
1847 local,
1848 arg_force,
1849 arg_read_only);
1850 if (r < 0)
1851 return bus_log_create_error(r);
1852
587fec42 1853 return transfer_image_common(bus, m);
b6e676ce
LP
1854}
1855
1856static int import_raw(int argc, char *argv[], void *userdata) {
4afd3348 1857 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
b6e676ce
LP
1858 _cleanup_free_ char *ll = NULL;
1859 _cleanup_close_ int fd = -1;
1860 const char *local = NULL, *path = NULL;
1861 sd_bus *bus = userdata;
1862 int r;
1863
1864 assert(bus);
1865
1866 if (argc >= 2)
1867 path = argv[1];
1868 if (isempty(path) || streq(path, "-"))
1869 path = NULL;
1870
1871 if (argc >= 3)
1872 local = argv[2];
1873 else if (path)
1874 local = basename(path);
1875 if (isempty(local) || streq(local, "-"))
1876 local = NULL;
1877
1878 if (!local) {
1879 log_error("Need either path or local name.");
1880 return -EINVAL;
1881 }
1882
1883 r = raw_strip_suffixes(local, &ll);
1884 if (r < 0)
1885 return log_oom();
1886
1887 local = ll;
1888
1889 if (!machine_name_is_valid(local)) {
1890 log_error("Local name %s is not a suitable machine name.", local);
1891 return -EINVAL;
1892 }
1893
1894 if (path) {
1895 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
1896 if (fd < 0)
1897 return log_error_errno(errno, "Failed to open %s: %m", path);
1898 }
1899
1900 r = sd_bus_message_new_method_call(
1901 bus,
1902 &m,
1903 "org.freedesktop.import1",
1904 "/org/freedesktop/import1",
1905 "org.freedesktop.import1.Manager",
1906 "ImportRaw");
1907 if (r < 0)
1908 return bus_log_create_error(r);
1909
1910 r = sd_bus_message_append(
1911 m,
1912 "hsbb",
1913 fd >= 0 ? fd : STDIN_FILENO,
1914 local,
1915 arg_force,
1916 arg_read_only);
1917 if (r < 0)
1918 return bus_log_create_error(r);
1919
587fec42
LP
1920 return transfer_image_common(bus, m);
1921}
1922
1923static void determine_compression_from_filename(const char *p) {
1924 if (arg_format)
1925 return;
1926
1927 if (!p)
1928 return;
1929
1930 if (endswith(p, ".xz"))
1931 arg_format = "xz";
1932 else if (endswith(p, ".gz"))
1933 arg_format = "gzip";
1934 else if (endswith(p, ".bz2"))
1935 arg_format = "bzip2";
1936}
1937
1938static int export_tar(int argc, char *argv[], void *userdata) {
4afd3348 1939 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
587fec42
LP
1940 _cleanup_close_ int fd = -1;
1941 const char *local = NULL, *path = NULL;
1942 sd_bus *bus = userdata;
1943 int r;
1944
1945 assert(bus);
1946
1947 local = argv[1];
1948 if (!machine_name_is_valid(local)) {
1949 log_error("Machine name %s is not valid.", local);
1950 return -EINVAL;
1951 }
1952
1953 if (argc >= 3)
1954 path = argv[2];
1955 if (isempty(path) || streq(path, "-"))
1956 path = NULL;
1957
1958 if (path) {
1959 determine_compression_from_filename(path);
1960
1961 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
1962 if (fd < 0)
1963 return log_error_errno(errno, "Failed to open %s: %m", path);
1964 }
1965
1966 r = sd_bus_message_new_method_call(
1967 bus,
1968 &m,
1969 "org.freedesktop.import1",
1970 "/org/freedesktop/import1",
1971 "org.freedesktop.import1.Manager",
1972 "ExportTar");
1973 if (r < 0)
1974 return bus_log_create_error(r);
1975
1976 r = sd_bus_message_append(
1977 m,
1978 "shs",
1979 local,
1980 fd >= 0 ? fd : STDOUT_FILENO,
1981 arg_format);
1982 if (r < 0)
1983 return bus_log_create_error(r);
1984
1985 return transfer_image_common(bus, m);
1986}
1987
1988static int export_raw(int argc, char *argv[], void *userdata) {
4afd3348 1989 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
587fec42
LP
1990 _cleanup_close_ int fd = -1;
1991 const char *local = NULL, *path = NULL;
1992 sd_bus *bus = userdata;
1993 int r;
1994
1995 assert(bus);
1996
1997 local = argv[1];
1998 if (!machine_name_is_valid(local)) {
1999 log_error("Machine name %s is not valid.", local);
2000 return -EINVAL;
2001 }
2002
2003 if (argc >= 3)
2004 path = argv[2];
2005 if (isempty(path) || streq(path, "-"))
2006 path = NULL;
2007
2008 if (path) {
2009 determine_compression_from_filename(path);
2010
2011 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
2012 if (fd < 0)
2013 return log_error_errno(errno, "Failed to open %s: %m", path);
2014 }
2015
2016 r = sd_bus_message_new_method_call(
2017 bus,
2018 &m,
2019 "org.freedesktop.import1",
2020 "/org/freedesktop/import1",
2021 "org.freedesktop.import1.Manager",
2022 "ExportRaw");
2023 if (r < 0)
2024 return bus_log_create_error(r);
2025
2026 r = sd_bus_message_append(
2027 m,
2028 "shs",
2029 local,
2030 fd >= 0 ? fd : STDOUT_FILENO,
2031 arg_format);
2032 if (r < 0)
2033 return bus_log_create_error(r);
2034
2035 return transfer_image_common(bus, m);
b6e676ce
LP
2036}
2037
3d7415f4 2038static int pull_tar(int argc, char *argv[], void *userdata) {
4afd3348 2039 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
3d7415f4
LP
2040 _cleanup_free_ char *l = NULL, *ll = NULL;
2041 const char *local, *remote;
2042 sd_bus *bus = userdata;
2043 int r;
2044
2045 assert(bus);
2046
2047 remote = argv[1];
2048 if (!http_url_is_valid(remote)) {
2049 log_error("URL '%s' is not valid.", remote);
2050 return -EINVAL;
2051 }
2052
2053 if (argc >= 3)
2054 local = argv[2];
2055 else {
2056 r = import_url_last_component(remote, &l);
2057 if (r < 0)
2058 return log_error_errno(r, "Failed to get final component of URL: %m");
2059
2060 local = l;
2061 }
2062
2063 if (isempty(local) || streq(local, "-"))
2064 local = NULL;
2065
2066 if (local) {
2067 r = tar_strip_suffixes(local, &ll);
2068 if (r < 0)
b6e676ce 2069 return log_oom();
3d7415f4
LP
2070
2071 local = ll;
2072
2073 if (!machine_name_is_valid(local)) {
2074 log_error("Local name %s is not a suitable machine name.", local);
2075 return -EINVAL;
2076 }
2077 }
2078
2079 r = sd_bus_message_new_method_call(
2080 bus,
2081 &m,
2082 "org.freedesktop.import1",
2083 "/org/freedesktop/import1",
2084 "org.freedesktop.import1.Manager",
2085 "PullTar");
2086 if (r < 0)
2087 return bus_log_create_error(r);
2088
2089 r = sd_bus_message_append(
2090 m,
2091 "sssb",
2092 remote,
2093 local,
6e18cc9f 2094 import_verify_to_string(arg_verify),
3d7415f4
LP
2095 arg_force);
2096 if (r < 0)
2097 return bus_log_create_error(r);
2098
587fec42 2099 return transfer_image_common(bus, m);
3d7415f4
LP
2100}
2101
2102static int pull_raw(int argc, char *argv[], void *userdata) {
4afd3348 2103 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
3d7415f4
LP
2104 _cleanup_free_ char *l = NULL, *ll = NULL;
2105 const char *local, *remote;
2106 sd_bus *bus = userdata;
2107 int r;
2108
2109 assert(bus);
2110
2111 remote = argv[1];
2112 if (!http_url_is_valid(remote)) {
2113 log_error("URL '%s' is not valid.", remote);
2114 return -EINVAL;
2115 }
2116
2117 if (argc >= 3)
2118 local = argv[2];
2119 else {
2120 r = import_url_last_component(remote, &l);
2121 if (r < 0)
2122 return log_error_errno(r, "Failed to get final component of URL: %m");
2123
2124 local = l;
2125 }
2126
2127 if (isempty(local) || streq(local, "-"))
2128 local = NULL;
2129
2130 if (local) {
2131 r = raw_strip_suffixes(local, &ll);
2132 if (r < 0)
b6e676ce 2133 return log_oom();
3d7415f4
LP
2134
2135 local = ll;
2136
2137 if (!machine_name_is_valid(local)) {
2138 log_error("Local name %s is not a suitable machine name.", local);
2139 return -EINVAL;
2140 }
2141 }
2142
2143 r = sd_bus_message_new_method_call(
2144 bus,
2145 &m,
2146 "org.freedesktop.import1",
2147 "/org/freedesktop/import1",
2148 "org.freedesktop.import1.Manager",
2149 "PullRaw");
2150 if (r < 0)
2151 return bus_log_create_error(r);
2152
2153 r = sd_bus_message_append(
2154 m,
2155 "sssb",
2156 remote,
2157 local,
6e18cc9f 2158 import_verify_to_string(arg_verify),
3d7415f4
LP
2159 arg_force);
2160 if (r < 0)
2161 return bus_log_create_error(r);
2162
587fec42 2163 return transfer_image_common(bus, m);
3d7415f4
LP
2164}
2165
3d7415f4
LP
2166typedef struct TransferInfo {
2167 uint32_t id;
2168 const char *type;
2169 const char *remote;
2170 const char *local;
7079cfef 2171 double progress;
3d7415f4
LP
2172} TransferInfo;
2173
2174static int compare_transfer_info(const void *a, const void *b) {
2175 const TransferInfo *x = a, *y = b;
2176
2177 return strcmp(x->local, y->local);
2178}
2179
2180static int list_transfers(int argc, char *argv[], void *userdata) {
2181 size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
4afd3348
LP
2182 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2183 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3d7415f4
LP
2184 _cleanup_free_ TransferInfo *transfers = NULL;
2185 size_t n_transfers = 0, n_allocated = 0, j;
2186 const char *type, *remote, *local, *object;
2187 sd_bus *bus = userdata;
2188 uint32_t id, max_id = 0;
7079cfef 2189 double progress;
3d7415f4
LP
2190 int r;
2191
ea4b98e6 2192 pager_open(arg_no_pager, false);
3d7415f4 2193
e138e7d7
ZJS
2194 r = sd_bus_call_method(bus,
2195 "org.freedesktop.import1",
2196 "/org/freedesktop/import1",
2197 "org.freedesktop.import1.Manager",
2198 "ListTransfers",
2199 &error,
2200 &reply,
2201 NULL);
3d7415f4
LP
2202 if (r < 0) {
2203 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2204 return r;
2205 }
2206
7079cfef 2207 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
3d7415f4
LP
2208 if (r < 0)
2209 return bus_log_parse_error(r);
2210
7079cfef 2211 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
3d7415f4
LP
2212 size_t l;
2213
2214 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2215 return log_oom();
2216
2217 transfers[n_transfers].id = id;
2218 transfers[n_transfers].type = type;
2219 transfers[n_transfers].remote = remote;
2220 transfers[n_transfers].local = local;
7079cfef 2221 transfers[n_transfers].progress = progress;
3d7415f4
LP
2222
2223 l = strlen(type);
2224 if (l > max_type)
2225 max_type = l;
2226
2227 l = strlen(remote);
2228 if (l > max_remote)
2229 max_remote = l;
2230
2231 l = strlen(local);
2232 if (l > max_local)
2233 max_local = l;
2234
2235 if (id > max_id)
2236 max_id = id;
2237
313cefa1 2238 n_transfers++;
3d7415f4
LP
2239 }
2240 if (r < 0)
2241 return bus_log_parse_error(r);
2242
2243 r = sd_bus_message_exit_container(reply);
2244 if (r < 0)
2245 return bus_log_parse_error(r);
2246
2247 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2248
2249 if (arg_legend)
7079cfef 2250 printf("%-*s %-*s %-*s %-*s %-*s\n",
3d7415f4 2251 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
7079cfef 2252 (int) 7, "PERCENT",
3d7415f4
LP
2253 (int) max_type, "TYPE",
2254 (int) max_local, "LOCAL",
2255 (int) max_remote, "REMOTE");
2256
2257 for (j = 0; j < n_transfers; j++)
7079cfef 2258 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
3d7415f4 2259 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
7079cfef 2260 (int) 6, (unsigned) (transfers[j].progress * 100),
3d7415f4
LP
2261 (int) max_type, transfers[j].type,
2262 (int) max_local, transfers[j].local,
2263 (int) max_remote, transfers[j].remote);
2264
2265 if (arg_legend)
2266 printf("\n%zu transfers listed.\n", n_transfers);
2267
2268 return 0;
2269}
2270
2271static int cancel_transfer(int argc, char *argv[], void *userdata) {
4afd3348 2272 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3d7415f4
LP
2273 sd_bus *bus = userdata;
2274 int r, i;
2275
2276 assert(bus);
2277
2278 polkit_agent_open_if_enabled();
2279
2280 for (i = 1; i < argc; i++) {
2281 uint32_t id;
2282
2283 r = safe_atou32(argv[i], &id);
2284 if (r < 0)
2285 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
2286
2287 r = sd_bus_call_method(
2288 bus,
2289 "org.freedesktop.import1",
2290 "/org/freedesktop/import1",
2291 "org.freedesktop.import1.Manager",
2292 "CancelTransfer",
2293 &error,
2294 NULL,
2295 "u", id);
2296 if (r < 0) {
2297 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
2298 return r;
2299 }
2300 }
2301
2302 return 0;
2303}
2304
d6ce17c7 2305static int set_limit(int argc, char *argv[], void *userdata) {
4afd3348 2306 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
d6ce17c7
LP
2307 sd_bus *bus = userdata;
2308 uint64_t limit;
2309 int r;
2310
7705a405 2311 if (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
d6ce17c7
LP
2312 limit = (uint64_t) -1;
2313 else {
59f448cf 2314 r = parse_size(argv[argc-1], 1024, &limit);
d6ce17c7
LP
2315 if (r < 0)
2316 return log_error("Failed to parse size: %s", argv[argc-1]);
d6ce17c7
LP
2317 }
2318
2319 if (argc > 2)
2320 /* With two arguments changes the quota limit of the
2321 * specified image */
2322 r = sd_bus_call_method(
2323 bus,
2324 "org.freedesktop.machine1",
2325 "/org/freedesktop/machine1",
2326 "org.freedesktop.machine1.Manager",
2327 "SetImageLimit",
2328 &error,
2329 NULL,
2330 "st", argv[1], limit);
2331 else
2332 /* With one argument changes the pool quota limit */
2333 r = sd_bus_call_method(
2334 bus,
2335 "org.freedesktop.machine1",
2336 "/org/freedesktop/machine1",
2337 "org.freedesktop.machine1.Manager",
2338 "SetPoolLimit",
2339 &error,
2340 NULL,
2341 "t", limit);
2342
2343 if (r < 0) {
2344 log_error("Could not set limit: %s", bus_error_message(&error, -r));
2345 return r;
2346 }
2347
2348 return 0;
2349}
2350
d94c2b06
LP
2351static int clean_images(int argc, char *argv[], void *userdata) {
2352 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2353 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2354 uint64_t usage, total = 0;
2355 char fb[FORMAT_BYTES_MAX];
2356 sd_bus *bus = userdata;
2357 const char *name;
2358 unsigned c = 0;
2359 int r;
2360
2361 r = sd_bus_call_method(
2362 bus,
2363 "org.freedesktop.machine1",
2364 "/org/freedesktop/machine1",
2365 "org.freedesktop.machine1.Manager",
2366 "CleanPool",
2367 &error,
2368 &reply,
2369 "s", arg_all ? "all" : "hidden");
2370 if (r < 0)
2371 return log_error_errno(r, "Could not clean pool: %s", bus_error_message(&error, r));
2372
2373 r = sd_bus_message_enter_container(reply, 'a', "(st)");
2374 if (r < 0)
2375 return bus_log_parse_error(r);
2376
2377 while ((r = sd_bus_message_read(reply, "(st)", &name, &usage)) > 0) {
2378 log_info("Removed image '%s'. Freed exclusive disk space: %s",
2379 name, format_bytes(fb, sizeof(fb), usage));
2380
2381 total += usage;
2382 c++;
2383 }
2384
2385 r = sd_bus_message_exit_container(reply);
2386 if (r < 0)
2387 return bus_log_parse_error(r);
2388
2389 log_info("Removed %u images in total. Total freed exclusive disk space %s.",
2390 c, format_bytes(fb, sizeof(fb), total));
2391
2392 return 0;
2393}
2394
56159e0d
LP
2395static int help(int argc, char *argv[], void *userdata) {
2396
1ee306e1 2397 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
f2cbe59e
LP
2398 "Send control commands to or query the virtual machine and container\n"
2399 "registration manager.\n\n"
2400 " -h --help Show this help\n"
2401 " --version Show package version\n"
2402 " --no-pager Do not pipe output into a pager\n"
2403 " --no-legend Do not show the headers and footers\n"
acf97e21 2404 " --no-ask-password Do not ask for system passwords\n"
f2cbe59e
LP
2405 " -H --host=[USER@]HOST Operate on remote host\n"
2406 " -M --machine=CONTAINER Operate on local container\n"
2407 " -p --property=NAME Show only properties by this name\n"
d8f52ed2 2408 " -q --quiet Suppress output\n"
f2cbe59e 2409 " -a --all Show all properties, including empty ones\n"
85500523 2410 " --value When showing properties, only print the value\n"
f2cbe59e
LP
2411 " -l --full Do not ellipsize output\n"
2412 " --kill-who=WHO Who to send signal to\n"
2413 " -s --signal=SIGNAL Which signal to send\n"
c454426c 2414 " --uid=USER Specify user ID to invoke shell as\n"
4d46e5db 2415 " -E --setenv=VAR=VALUE Add an environment variable for shell\n"
f2cbe59e 2416 " --read-only Create read-only bind mount\n"
8b0cc9a3
LP
2417 " --mkdir Create directory before bind mounting, if missing\n"
2418 " -n --lines=INTEGER Number of journal entries to show\n"
2419 " -o --output=STRING Change journal output mode (short,\n"
2420 " short-monotonic, verbose, export, json,\n"
3d7415f4 2421 " json-pretty, json-sse, cat)\n"
7f444afa
LP
2422 " --verify=MODE Verification mode for downloaded images (no,\n"
2423 " checksum, signature)\n"
b43d75c3 2424 " --force Download image even if already exists\n\n"
cd61c3bf 2425 "Machine Commands:\n"
f2cbe59e 2426 " list List running VMs and containers\n"
fefdc04b 2427 " status NAME... Show VM/container details\n"
91913f58 2428 " show [NAME...] Show properties of one or more VMs/containers\n"
ebd011d9 2429 " start NAME... Start container as a service\n"
91913f58
LP
2430 " login [NAME] Get a login prompt in a container or on the\n"
2431 " local host\n"
ef3100e9
LP
2432 " shell [[USER@]NAME [COMMAND...]]\n"
2433 " Invoke a shell (or other command) in a container\n"
2434 " or on the local host\n"
d8f52ed2
LP
2435 " enable NAME... Enable automatic container start at boot\n"
2436 " disable NAME... Disable automatic container start at boot\n"
f2cbe59e
LP
2437 " poweroff NAME... Power off one or more containers\n"
2438 " reboot NAME... Reboot one or more containers\n"
f2cbe59e 2439 " terminate NAME... Terminate one or more VMs/containers\n"
ebd93cb6 2440 " kill NAME... Send signal to processes of a VM/container\n"
f2cbe59e 2441 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
ebd93cb6
LP
2442 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2443 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
fefdc04b 2444 "Image Commands:\n"
56b921c3 2445 " list-images Show available container and VM images\n"
91913f58
LP
2446 " image-status [NAME...] Show image details\n"
2447 " show-image [NAME...] Show properties of image\n"
ebd93cb6
LP
2448 " clone NAME NAME Clone an image\n"
2449 " rename NAME NAME Rename an image\n"
2450 " read-only NAME [BOOL] Mark or unmark image read-only\n"
d6ce17c7 2451 " remove NAME... Remove an image\n"
5bda1f47
LP
2452 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n"
2453 " clean Remove hidden (or all) images\n\n"
b5b38b41
LP
2454 "Image Transfer Commands:\n"
2455 " pull-tar URL [NAME] Download a TAR container image\n"
2456 " pull-raw URL [NAME] Download a RAW container or VM image\n"
587fec42
LP
2457 " import-tar FILE [NAME] Import a local TAR container image\n"
2458 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
6e9efa59
LP
2459 " export-tar NAME [FILE] Export a TAR container image locally\n"
2460 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
b5b38b41 2461 " list-transfers Show list of downloads in progress\n"
3d7415f4 2462 " cancel-transfer Cancel a download\n"
f7621db0 2463 , program_invocation_short_name);
56159e0d
LP
2464
2465 return 0;
1ee306e1
LP
2466}
2467
2468static int parse_argv(int argc, char *argv[]) {
2469
2470 enum {
2471 ARG_VERSION = 0x100,
2472 ARG_NO_PAGER,
e56056e9 2473 ARG_NO_LEGEND,
85500523 2474 ARG_VALUE,
1ee306e1 2475 ARG_KILL_WHO,
785890ac
LP
2476 ARG_READ_ONLY,
2477 ARG_MKDIR,
acf97e21 2478 ARG_NO_ASK_PASSWORD,
3d7415f4
LP
2479 ARG_VERIFY,
2480 ARG_FORCE,
587fec42 2481 ARG_FORMAT,
c454426c 2482 ARG_UID,
1ee306e1
LP
2483 };
2484
2485 static const struct option options[] = {
2486 { "help", no_argument, NULL, 'h' },
2487 { "version", no_argument, NULL, ARG_VERSION },
2488 { "property", required_argument, NULL, 'p' },
2489 { "all", no_argument, NULL, 'a' },
85500523 2490 { "value", no_argument, NULL, ARG_VALUE },
1ee306e1
LP
2491 { "full", no_argument, NULL, 'l' },
2492 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
e56056e9 2493 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1ee306e1
LP
2494 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2495 { "signal", required_argument, NULL, 's' },
2496 { "host", required_argument, NULL, 'H' },
a7893c6b 2497 { "machine", required_argument, NULL, 'M' },
785890ac
LP
2498 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2499 { "mkdir", no_argument, NULL, ARG_MKDIR },
d8f52ed2 2500 { "quiet", no_argument, NULL, 'q' },
8b0cc9a3
LP
2501 { "lines", required_argument, NULL, 'n' },
2502 { "output", required_argument, NULL, 'o' },
acf97e21 2503 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
3d7415f4
LP
2504 { "verify", required_argument, NULL, ARG_VERIFY },
2505 { "force", no_argument, NULL, ARG_FORCE },
587fec42 2506 { "format", required_argument, NULL, ARG_FORMAT },
c454426c 2507 { "uid", required_argument, NULL, ARG_UID },
4d46e5db 2508 { "setenv", required_argument, NULL, 'E' },
eb9da376 2509 {}
1ee306e1
LP
2510 };
2511
368d2643 2512 bool reorder = false;
a7893c6b 2513 int c, r;
1ee306e1
LP
2514
2515 assert(argc >= 0);
2516 assert(argv);
2517
368d2643 2518 for (;;) {
0bf50960 2519 const char * const option_string = "+hp:als:H:M:qn:o:";
368d2643 2520
0bf50960 2521 c = getopt_long(argc, argv, option_string + reorder, options, NULL);
368d2643
LP
2522 if (c < 0) {
2523 /* We generally are fine with the fact that getopt_long() reorders the command line, and looks
2524 * for switches after the main verb. However, for "shell" we really don't want that, since we
2525 * want that switches passed after that are passed to the program to execute, and not processed
2526 * by us. To make this possible, we'll first invoke getopt_long() with reordering disabled
2527 * (i.e. with the "+" prefix in the option string), and as soon as we hit the end (i.e. the
2528 * verb) we check if that's "shell". If it is, we exit the loop, since we don't want any
2529 * further options processed. However, if it is anything else, we process the same argument
2530 * again, but this time allow reordering. */
2531
2532 if (!reorder && optind < argc && !streq(argv[optind], "shell")) {
2533 reorder = true;
2534 optind--;
2535 continue;
2536 }
2537
2538 break;
2539 }
1ee306e1
LP
2540
2541 switch (c) {
2542
2543 case 'h':
56159e0d 2544 return help(0, NULL, NULL);
1ee306e1
LP
2545
2546 case ARG_VERSION:
3f6fd1ba 2547 return version();
1ee306e1 2548
a7893c6b
LP
2549 case 'p':
2550 r = strv_extend(&arg_property, optarg);
2551 if (r < 0)
2552 return log_oom();
1ee306e1
LP
2553
2554 /* If the user asked for a particular
2555 * property, show it to him, even if it is
2556 * empty. */
2557 arg_all = true;
2558 break;
1ee306e1
LP
2559
2560 case 'a':
2561 arg_all = true;
2562 break;
2563
85500523
ZJS
2564 case ARG_VALUE:
2565 arg_value = true;
2566 break;
2567
1ee306e1
LP
2568 case 'l':
2569 arg_full = true;
2570 break;
2571
8b0cc9a3
LP
2572 case 'n':
2573 if (safe_atou(optarg, &arg_lines) < 0) {
2574 log_error("Failed to parse lines '%s'", optarg);
2575 return -EINVAL;
2576 }
2577 break;
2578
2579 case 'o':
2580 arg_output = output_mode_from_string(optarg);
2581 if (arg_output < 0) {
2582 log_error("Unknown output '%s'.", optarg);
2583 return -EINVAL;
2584 }
2585 break;
2586
1ee306e1
LP
2587 case ARG_NO_PAGER:
2588 arg_no_pager = true;
2589 break;
2590
e56056e9
TA
2591 case ARG_NO_LEGEND:
2592 arg_legend = false;
2593 break;
2594
1ee306e1
LP
2595 case ARG_KILL_WHO:
2596 arg_kill_who = optarg;
2597 break;
2598
2599 case 's':
2600 arg_signal = signal_from_string_try_harder(optarg);
2601 if (arg_signal < 0) {
2602 log_error("Failed to parse signal string %s.", optarg);
2603 return -EINVAL;
2604 }
2605 break;
2606
acf97e21
LP
2607 case ARG_NO_ASK_PASSWORD:
2608 arg_ask_password = false;
2609 break;
2610
1ee306e1 2611 case 'H':
d21ed1ea 2612 arg_transport = BUS_TRANSPORT_REMOTE;
a7893c6b
LP
2613 arg_host = optarg;
2614 break;
2615
2616 case 'M':
de33fc62 2617 arg_transport = BUS_TRANSPORT_MACHINE;
a7893c6b 2618 arg_host = optarg;
1ee306e1
LP
2619 break;
2620
785890ac
LP
2621 case ARG_READ_ONLY:
2622 arg_read_only = true;
2623 break;
2624
2625 case ARG_MKDIR:
2626 arg_mkdir = true;
2627 break;
2628
d8f52ed2
LP
2629 case 'q':
2630 arg_quiet = true;
2631 break;
2632
3d7415f4 2633 case ARG_VERIFY:
6e18cc9f
LP
2634 arg_verify = import_verify_from_string(optarg);
2635 if (arg_verify < 0) {
2636 log_error("Failed to parse --verify= setting: %s", optarg);
2637 return -EINVAL;
2638 }
3d7415f4
LP
2639 break;
2640
2641 case ARG_FORCE:
2642 arg_force = true;
2643 break;
2644
587fec42
LP
2645 case ARG_FORMAT:
2646 if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) {
2647 log_error("Unknown format: %s", optarg);
2648 return -EINVAL;
2649 }
2650
2651 arg_format = optarg;
2652 break;
2653
c454426c
LP
2654 case ARG_UID:
2655 arg_uid = optarg;
2656 break;
2657
4d46e5db 2658 case 'E':
c454426c
LP
2659 if (!env_assignment_is_valid(optarg)) {
2660 log_error("Environment assignment invalid: %s", optarg);
2661 return -EINVAL;
2662 }
2663
2664 r = strv_extend(&arg_setenv, optarg);
2665 if (r < 0)
2666 return log_oom();
2667 break;
2668
1ee306e1
LP
2669 case '?':
2670 return -EINVAL;
2671
2672 default:
eb9da376 2673 assert_not_reached("Unhandled option");
1ee306e1 2674 }
368d2643 2675 }
1ee306e1
LP
2676
2677 return 1;
2678}
2679
56159e0d
LP
2680static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2681
2682 static const Verb verbs[] = {
3d7415f4
LP
2683 { "help", VERB_ANY, VERB_ANY, 0, help },
2684 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2685 { "list-images", VERB_ANY, 1, 0, list_images },
2686 { "status", 2, VERB_ANY, 0, show_machine },
160e3793 2687 { "image-status", VERB_ANY, VERB_ANY, 0, show_image },
3d7415f4
LP
2688 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2689 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2690 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2691 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2692 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2693 { "kill", 2, VERB_ANY, 0, kill_machine },
91913f58
LP
2694 { "login", VERB_ANY, 2, 0, login_machine },
2695 { "shell", VERB_ANY, VERB_ANY, 0, shell_machine },
3d7415f4
LP
2696 { "bind", 3, 4, 0, bind_mount },
2697 { "copy-to", 3, 4, 0, copy_files },
2698 { "copy-from", 3, 4, 0, copy_files },
2699 { "remove", 2, VERB_ANY, 0, remove_image },
2700 { "rename", 3, 3, 0, rename_image },
2701 { "clone", 3, 3, 0, clone_image },
2702 { "read-only", 2, 3, 0, read_only_image },
2703 { "start", 2, VERB_ANY, 0, start_machine },
2704 { "enable", 2, VERB_ANY, 0, enable_machine },
2705 { "disable", 2, VERB_ANY, 0, enable_machine },
b6e676ce
LP
2706 { "import-tar", 2, 3, 0, import_tar },
2707 { "import-raw", 2, 3, 0, import_raw },
587fec42
LP
2708 { "export-tar", 2, 3, 0, export_tar },
2709 { "export-raw", 2, 3, 0, export_raw },
3d7415f4
LP
2710 { "pull-tar", 2, 3, 0, pull_tar },
2711 { "pull-raw", 2, 3, 0, pull_raw },
3d7415f4
LP
2712 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2713 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
d6ce17c7 2714 { "set-limit", 2, 3, 0, set_limit },
d94c2b06 2715 { "clean", VERB_ANY, 1, 0, clean_images },
56159e0d 2716 {}
1ee306e1
LP
2717 };
2718
56159e0d 2719 return dispatch_verb(argc, argv, verbs, bus);
1ee306e1
LP
2720}
2721
2722int main(int argc, char*argv[]) {
4afd3348 2723 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
84f6181c 2724 int r;
1ee306e1
LP
2725
2726 setlocale(LC_ALL, "");
2727 log_parse_environment();
2728 log_open();
2729
2730 r = parse_argv(argc, argv);
84f6181c 2731 if (r <= 0)
1ee306e1 2732 goto finish;
1ee306e1 2733
266f3e26 2734 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
a1da8583 2735 if (r < 0) {
da927ba9 2736 log_error_errno(r, "Failed to create bus connection: %m");
a1da8583
TG
2737 goto finish;
2738 }
1ee306e1 2739
2723b3b5
LP
2740 sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
2741
56159e0d 2742 r = machinectl_main(argc, argv, bus);
1ee306e1
LP
2743
2744finish:
1ee306e1 2745 pager_close();
acf97e21 2746 polkit_agent_close();
1ee306e1 2747
84f6181c 2748 strv_free(arg_property);
c454426c 2749 strv_free(arg_setenv);
84f6181c
LP
2750
2751 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1ee306e1 2752}