machinectl.c revision 10ba4835042c9815a755f8faa774651ec3d52467
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering/***
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering This file is part of systemd.
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering Copyright 2013 Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering systemd is free software; you can redistribute it and/or modify it
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering under the terms of the GNU Lesser General Public License as published by
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering (at your option) any later version.
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering systemd is distributed in the hope that it will be useful, but
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering Lesser General Public License for more details.
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering You should have received a copy of the GNU Lesser General Public License
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering***/
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include <arpa/inet.h>
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include <errno.h>
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include <fcntl.h>
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include <getopt.h>
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include <locale.h>
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include <net/if.h>
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include <netinet/in.h>
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include <string.h>
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include <sys/mount.h>
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include <sys/socket.h>
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include <unistd.h>
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "sd-bus.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "alloc-util.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "bus-error.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "bus-util.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "cgroup-show.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "cgroup-util.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "copy.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "env-util.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "fd-util.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "hostname-util.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "import-util.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "log.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "logs-show.h"
baed47c3c20512507e497058d388782400a072f6Lennart Poettering#include "macro.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "mkdir.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "pager.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "parse-util.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "path-util.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "process-util.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "ptyfwd.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "signal-util.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "spawn-polkit-agent.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "strv.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "terminal-util.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "unit-name.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "util.h"
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering#include "verbs.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "web-util.h"
9f6445e34a57c270f013c9416c123e56261553ddLennart Poettering
507f22bd0172bff5e5d98145b1419bd472a2c57fZbigniew Jędrzejewski-Szmekstatic char **arg_property = NULL;
507f22bd0172bff5e5d98145b1419bd472a2c57fZbigniew Jędrzejewski-Szmekstatic bool arg_all = false;
e627440b41bb0284e4892f7aa9d84c77972487e2Lennart Poetteringstatic bool arg_full = false;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic bool arg_no_pager = false;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic bool arg_legend = true;
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poetteringstatic const char *arg_kill_who = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic int arg_signal = SIGTERM;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic char *arg_host = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic bool arg_read_only = false;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic bool arg_mkdir = false;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic bool arg_quiet = false;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic bool arg_ask_password = true;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic unsigned arg_lines = 10;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic OutputMode arg_output = OUTPUT_SHORT;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic bool arg_force = false;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poetteringstatic ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic const char* arg_format = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic const char *arg_uid = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic char **arg_setenv = NULL;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic void pager_open_if_enabled(void) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (arg_no_pager)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering pager_open(false);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
b7c9ae91d111b3e89d1ffc00e08f9ed97a8ff5dbLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic void polkit_agent_open_if_enabled(void) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering /* Open the polkit agent as a child process if necessary */
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (!arg_ask_password)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (arg_transport != BUS_TRANSPORT_LOCAL)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering polkit_agent_open();
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic OutputFlags get_output_flags(void) {
baed47c3c20512507e497058d388782400a072f6Lennart Poettering return
baed47c3c20512507e497058d388782400a072f6Lennart Poettering arg_all * OUTPUT_SHOW_ALL |
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering arg_full * OUTPUT_FULL_WIDTH |
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
baed47c3c20512507e497058d388782400a072f6Lennart Poettering on_tty() * OUTPUT_COLOR |
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering !arg_quiet * OUTPUT_WARN_CUTOFF;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poetteringtypedef struct MachineInfo {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering const char *name;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering const char *class;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering const char *service;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering} MachineInfo;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poetteringstatic int compare_machine_info(const void *a, const void *b) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering const MachineInfo *x = a, *y = b;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return strcmp(x->name, y->name);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic int list_machines(int argc, char *argv[], void *userdata) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering _cleanup_free_ MachineInfo *machines = NULL;
b7c9ae91d111b3e89d1ffc00e08f9ed97a8ff5dbLennart Poettering const char *name, *class, *service, *object;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering size_t n_machines = 0, n_allocated = 0, j;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering sd_bus *bus = userdata;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering int r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(bus);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering pager_open_if_enabled();
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering r = sd_bus_call_method(
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering bus,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering "org.freedesktop.machine1",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering "/org/freedesktop/machine1",
baed47c3c20512507e497058d388782400a072f6Lennart Poettering "org.freedesktop.machine1.Manager",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering "ListMachines",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering &error,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering &reply,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering NULL);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering log_error("Could not get machines: %s", bus_error_message(&error, -r));
b7c9ae91d111b3e89d1ffc00e08f9ed97a8ff5dbLennart Poettering return r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
507f22bd0172bff5e5d98145b1419bd472a2c57fZbigniew Jędrzejewski-Szmek
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return bus_log_parse_error(r);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering size_t l;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
b7c9ae91d111b3e89d1ffc00e08f9ed97a8ff5dbLennart Poettering if (name[0] == '.' && !arg_all)
b7c9ae91d111b3e89d1ffc00e08f9ed97a8ff5dbLennart Poettering continue;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return log_oom();
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering machines[n_machines].name = name;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering machines[n_machines].class = class;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering machines[n_machines].service = service;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering l = strlen(name);
baed47c3c20512507e497058d388782400a072f6Lennart Poettering if (l > max_name)
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering max_name = l;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering l = strlen(class);
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering if (l > max_class)
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering max_class = l;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering l = strlen(service);
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering if (l > max_service)
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering max_service = l;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering n_machines ++;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering }
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering if (r < 0)
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering return bus_log_parse_error(r);
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering r = sd_bus_message_exit_container(reply);
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering if (r < 0)
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering return bus_log_parse_error(r);
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering if (arg_legend)
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering printf("%-*s %-*s %-*s\n",
507f22bd0172bff5e5d98145b1419bd472a2c57fZbigniew Jędrzejewski-Szmek (int) max_name, "MACHINE",
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering (int) max_class, "CLASS",
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering (int) max_service, "SERVICE");
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering for (j = 0; j < n_machines; j++)
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering printf("%-*s %-*s %-*s\n",
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering (int) max_name, machines[j].name,
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering (int) max_class, machines[j].class,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering (int) max_service, machines[j].service);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (arg_legend)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering printf("\n%zu machines listed.\n", n_machines);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering return 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poetteringtypedef struct ImageInfo {
671e021c92c835c6c701dc61463149d05b6f31afLennart Poettering const char *name;
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering const char *type;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering bool read_only;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering usec_t crtime;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering usec_t mtime;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering uint64_t size;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering} ImageInfo;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic int compare_image_info(const void *a, const void *b) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering const ImageInfo *x = a, *y = b;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return strcmp(x->name, y->name);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic int list_images(int argc, char *argv[], void *userdata) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("USAGE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering _cleanup_free_ ImageInfo *images = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering size_t n_images = 0, n_allocated = 0, j;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering const char *name, *type, *object;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering sd_bus *bus = userdata;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering uint64_t crtime, mtime, size;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering int read_only, r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(bus);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering pager_open_if_enabled();
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering r = sd_bus_call_method(
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering bus,
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering "org.freedesktop.machine1",
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering "/org/freedesktop/machine1",
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering "org.freedesktop.machine1.Manager",
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering "ListImages",
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering &error,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering &reply,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering "");
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering log_error("Could not get images: %s", bus_error_message(&error, -r));
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return bus_log_parse_error(r);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
3c1668da6202f1ead3d4d3981b89e9da1a0e98e3Lennart Poettering while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
3c1668da6202f1ead3d4d3981b89e9da1a0e98e3Lennart Poettering char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
3c1668da6202f1ead3d4d3981b89e9da1a0e98e3Lennart Poettering size_t l;
3c1668da6202f1ead3d4d3981b89e9da1a0e98e3Lennart Poettering
3c1668da6202f1ead3d4d3981b89e9da1a0e98e3Lennart Poettering if (name[0] == '.' && !arg_all)
3c1668da6202f1ead3d4d3981b89e9da1a0e98e3Lennart Poettering continue;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return log_oom();
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering images[n_images].name = name;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering images[n_images].type = type;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering images[n_images].read_only = read_only;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering images[n_images].crtime = crtime;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering images[n_images].mtime = mtime;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering images[n_images].size = size;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering l = strlen(name);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (l > max_name)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering max_name = l;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering l = strlen(type);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (l > max_type)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering max_type = l;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (crtime != 0) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (l > max_crtime)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering max_crtime = l;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (mtime != 0) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (l > max_mtime)
baed47c3c20512507e497058d388782400a072f6Lennart Poettering max_mtime = l;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (size != (uint64_t) -1) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (l > max_size)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering max_size = l;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering n_images++;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering if (r < 0)
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering return bus_log_parse_error(r);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering r = sd_bus_message_exit_container(reply);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return bus_log_parse_error(r);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (arg_legend)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
baed47c3c20512507e497058d388782400a072f6Lennart Poettering (int) max_name, "NAME",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering (int) max_type, "TYPE",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering "RO",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering (int) max_size, "USAGE",
baed47c3c20512507e497058d388782400a072f6Lennart Poettering (int) max_crtime, "CREATED",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering (int) max_mtime, "MODIFIED");
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering for (j = 0; j < n_images; j++) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering (int) max_name, images[j].name,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering (int) max_type, images[j].type,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_normal() : "",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
baed47c3c20512507e497058d388782400a072f6Lennart Poettering (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (arg_legend)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering printf("\n%zu images listed.\n", n_images);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
272410e1790cb407bcedee4e5dfaac3f625957afLennart Poettering return 0;
272410e1790cb407bcedee4e5dfaac3f625957afLennart Poettering}
272410e1790cb407bcedee4e5dfaac3f625957afLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering _cleanup_free_ char *path = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering const char *cgroup;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering int r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering unsigned c;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(bus);
baed47c3c20512507e497058d388782400a072f6Lennart Poettering assert(unit);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (arg_transport == BUS_TRANSPORT_REMOTE)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering path = unit_dbus_path_from_name(unit);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (!path)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return log_oom();
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering r = sd_bus_get_property(
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering bus,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering "org.freedesktop.systemd1",
baed47c3c20512507e497058d388782400a072f6Lennart Poettering path,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering unit_dbus_interface_from_name(unit),
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering "ControlGroup",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering &error,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering &reply,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering "s");
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering r = sd_bus_message_read(reply, "s", &cgroup);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return bus_log_parse_error(r);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
3e4b9b506d676d1cb8692306b38c05f8529e5cdbLennart Poettering if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering c = columns();
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (c > 18)
baed47c3c20512507e497058d388782400a072f6Lennart Poettering c -= 18;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering else
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering c = 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering int r;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering assert(bus);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(name);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(prefix);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(prefix2);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering r = sd_bus_call_method(bus,
baed47c3c20512507e497058d388782400a072f6Lennart Poettering "org.freedesktop.machine1",
baed47c3c20512507e497058d388782400a072f6Lennart Poettering "/org/freedesktop/machine1",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering "org.freedesktop.machine1.Manager",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering "GetMachineAddresses",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering NULL,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering &reply,
baed47c3c20512507e497058d388782400a072f6Lennart Poettering "s", name);
baed47c3c20512507e497058d388782400a072f6Lennart Poettering if (r < 0)
b7c9ae91d111b3e89d1ffc00e08f9ed97a8ff5dbLennart Poettering return r;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering r = sd_bus_message_enter_container(reply, 'a', "(iay)");
b7c9ae91d111b3e89d1ffc00e08f9ed97a8ff5dbLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return bus_log_parse_error(r);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering int family;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering const void *a;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering size_t sz;
03e334a1c7dc8c20c38902aa039440763acc9b17Lennart Poettering char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
03e334a1c7dc8c20c38902aa039440763acc9b17Lennart Poettering r = sd_bus_message_read(reply, "i", &family);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return bus_log_parse_error(r);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering r = sd_bus_message_read_array(reply, 'y', &a, &sz);
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering if (r < 0)
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering return bus_log_parse_error(r);
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering fputs(prefix, stdout);
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering if (family == AF_INET6 && ifi > 0)
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering printf("%%%i", ifi);
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering fputc('\n', stdout);
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering r = sd_bus_message_exit_container(reply);
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering if (r < 0)
baed47c3c20512507e497058d388782400a072f6Lennart Poettering return bus_log_parse_error(r);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (prefix != prefix2)
baed47c3c20512507e497058d388782400a072f6Lennart Poettering prefix = prefix2;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering return bus_log_parse_error(r);
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering r = sd_bus_message_exit_container(reply);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return bus_log_parse_error(r);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering const char *k, *v, *pretty = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering int r;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(bus);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(name);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(prefix);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering r = sd_bus_call_method(bus,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering "org.freedesktop.machine1",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering "/org/freedesktop/machine1",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering "org.freedesktop.machine1.Manager",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering "GetMachineOSRelease",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering NULL,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering &reply,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering "s", name);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering return r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering r = sd_bus_message_enter_container(reply, 'a', "{ss}");
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return bus_log_parse_error(r);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (streq(k, "PRETTY_NAME"))
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering pretty = v;
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return bus_log_parse_error(r);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering r = sd_bus_message_exit_container(reply);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return bus_log_parse_error(r);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (pretty)
4da416aa20b956571d74720bc91222881443e24bLennart Poettering printf("%s%s\n", prefix, pretty);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering return 0;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering}
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poetteringtypedef struct MachineStatusInfo {
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering char *name;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering sd_id128_t id;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering char *class;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering char *service;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering char *unit;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering char *root_directory;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering pid_t leader;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering struct dual_timestamp timestamp;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering int *netif;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering unsigned n_netif;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering} MachineStatusInfo;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poetteringstatic void machine_status_info_clear(MachineStatusInfo *info) {
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering if (info) {
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering free(info->name);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering free(info->class);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering free(info->service);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering free(info->unit);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering free(info->root_directory);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering free(info->netif);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering zero(*info);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering }
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering}
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poetteringstatic void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering char since2[FORMAT_TIMESTAMP_MAX], *s2;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering int ifi = -1;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering assert(bus);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering assert(i);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering fputs(strna(i->name), stdout);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering if (!sd_id128_equal(i->id, SD_ID128_NULL))
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering else
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering putchar('\n');
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering if (s1)
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering printf("\t Since: %s; %s\n", s2, s1);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering else if (s2)
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering printf("\t Since: %s\n", s2);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering if (i->leader > 0) {
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering _cleanup_free_ char *t = NULL;
4da416aa20b956571d74720bc91222881443e24bLennart Poettering
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering printf("\t Leader: %u", (unsigned) i->leader);
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering get_process_comm(i->leader, &t);
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering if (t)
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering printf(" (%s)", t);
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering putchar('\n');
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering }
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering if (i->service) {
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering printf("\t Service: %s", i->service);
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering if (i->class)
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering printf("; class %s", i->class);
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering putchar('\n');
} else if (i->class)
printf("\t Class: %s\n", i->class);
if (i->root_directory)
printf("\t Root: %s\n", i->root_directory);
if (i->n_netif > 0) {
unsigned c;
fputs("\t Iface:", stdout);
for (c = 0; c < i->n_netif; c++) {
char name[IF_NAMESIZE+1] = "";
if (if_indextoname(i->netif[c], name)) {
fputc(' ', stdout);
fputs(name, stdout);
if (ifi < 0)
ifi = i->netif[c];
else
ifi = 0;
} else
printf(" %i", i->netif[c]);
}
fputc('\n', stdout);
}
print_addresses(bus, i->name, ifi,
"\t Address: ",
"\t ");
print_os_release(bus, i->name, "\t OS: ");
if (i->unit) {
printf("\t Unit: %s\n", i->unit);
show_unit_cgroup(bus, i->unit, i->leader);
if (arg_transport == BUS_TRANSPORT_LOCAL)
show_journal_by_unit(
stdout,
i->unit,
arg_output,
0,
i->timestamp.monotonic,
arg_lines,
0,
get_output_flags() | OUTPUT_BEGIN_NEWLINE,
SD_JOURNAL_LOCAL_ONLY,
true,
NULL);
}
}
static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
MachineStatusInfo *i = userdata;
size_t l;
const void *v;
int r;
assert_cc(sizeof(int32_t) == sizeof(int));
r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
if (r < 0)
return r;
if (r == 0)
return -EBADMSG;
i->n_netif = l / sizeof(int32_t);
i->netif = memdup(v, l);
if (!i->netif)
return -ENOMEM;
return 0;
}
static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
static const struct bus_properties_map map[] = {
{ "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
{ "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
{ "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
{ "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
{ "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
{ "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
{ "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) },
{ "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) },
{ "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
{ "NetworkInterfaces", "ai", map_netif, 0 },
{}
};
_cleanup_(machine_status_info_clear) MachineStatusInfo info = {};
int r;
assert(verb);
assert(bus);
assert(path);
assert(new_line);
r = bus_map_all_properties(bus,
"org.freedesktop.machine1",
path,
map,
&info);
if (r < 0)
return log_error_errno(r, "Could not get properties: %m");
if (*new_line)
printf("\n");
*new_line = true;
print_machine_status_info(bus, &info);
return r;
}
static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
int r;
assert(bus);
assert(path);
assert(new_line);
if (*new_line)
printf("\n");
*new_line = true;
r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
if (r < 0)
log_error_errno(r, "Could not get properties: %m");
return r;
}
static int show_machine(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
bool properties, new_line = false;
sd_bus *bus = userdata;
int r = 0, i;
assert(bus);
properties = !strstr(argv[0], "status");
pager_open_if_enabled();
if (properties && argc <= 1) {
/* If no argument is specified, inspect the manager
* itself */
r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
if (r < 0)
return r;
}
for (i = 1; i < argc; i++) {
const char *path = NULL;
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"GetMachine",
&error,
&reply,
"s", argv[i]);
if (r < 0) {
log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
return r;
}
r = sd_bus_message_read(reply, "o", &path);
if (r < 0)
return bus_log_parse_error(r);
if (properties)
r = show_machine_properties(bus, path, &new_line);
else
r = show_machine_info(argv[0], bus, path, &new_line);
}
return r;
}
typedef struct ImageStatusInfo {
char *name;
char *path;
char *type;
int read_only;
usec_t crtime;
usec_t mtime;
uint64_t usage;
uint64_t limit;
uint64_t usage_exclusive;
uint64_t limit_exclusive;
} ImageStatusInfo;
static void image_status_info_clear(ImageStatusInfo *info) {
if (info) {
free(info->name);
free(info->path);
free(info->type);
zero(*info);
}
}
static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
char bs[FORMAT_BYTES_MAX], *s3;
char bs_exclusive[FORMAT_BYTES_MAX], *s4;
assert(bus);
assert(i);
if (i->name) {
fputs(i->name, stdout);
putchar('\n');
}
if (i->type)
printf("\t Type: %s\n", i->type);
if (i->path)
printf("\t Path: %s\n", i->path);
printf("\t RO: %s%s%s\n",
i->read_only ? ansi_highlight_red() : "",
i->read_only ? "read-only" : "writable",
i->read_only ? ansi_normal() : "");
s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
if (s1 && s2)
printf("\t Created: %s; %s\n", s2, s1);
else if (s2)
printf("\t Created: %s\n", s2);
s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
if (s1 && s2)
printf("\tModified: %s; %s\n", s2, s1);
else if (s2)
printf("\tModified: %s\n", s2);
s3 = format_bytes(bs, sizeof(bs), i->usage);
s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL;
if (s3 && s4)
printf("\t Usage: %s (exclusive: %s)\n", s3, s4);
else if (s3)
printf("\t Usage: %s\n", s3);
s3 = format_bytes(bs, sizeof(bs), i->limit);
s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
if (s3 && s4)
printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
else if (s3)
printf("\t Limit: %s\n", s3);
}
static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
static const struct bus_properties_map map[] = {
{ "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
{ "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
{ "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
{ "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
{ "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
{ "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
{ "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) },
{ "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
{ "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) },
{ "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
{}
};
_cleanup_(image_status_info_clear) ImageStatusInfo info = {};
int r;
assert(bus);
assert(path);
assert(new_line);
r = bus_map_all_properties(bus,
"org.freedesktop.machine1",
path,
map,
&info);
if (r < 0)
return log_error_errno(r, "Could not get properties: %m");
if (*new_line)
printf("\n");
*new_line = true;
print_image_status_info(bus, &info);
return r;
}
typedef struct PoolStatusInfo {
char *path;
uint64_t usage;
uint64_t limit;
} PoolStatusInfo;
static void pool_status_info_clear(PoolStatusInfo *info) {
if (info) {
free(info->path);
zero(*info);
info->usage = -1;
info->limit = -1;
}
}
static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) {
char bs[FORMAT_BYTES_MAX], *s;
if (i->path)
printf("\t Path: %s\n", i->path);
s = format_bytes(bs, sizeof(bs), i->usage);
if (s)
printf("\t Usage: %s\n", s);
s = format_bytes(bs, sizeof(bs), i->limit);
if (s)
printf("\t Limit: %s\n", s);
}
static int show_pool_info(sd_bus *bus) {
static const struct bus_properties_map map[] = {
{ "PoolPath", "s", NULL, offsetof(PoolStatusInfo, path) },
{ "PoolUsage", "t", NULL, offsetof(PoolStatusInfo, usage) },
{ "PoolLimit", "t", NULL, offsetof(PoolStatusInfo, limit) },
{}
};
_cleanup_(pool_status_info_clear) PoolStatusInfo info = {
.usage = (uint64_t) -1,
.limit = (uint64_t) -1,
};
int r;
assert(bus);
r = bus_map_all_properties(bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
map,
&info);
if (r < 0)
return log_error_errno(r, "Could not get properties: %m");
print_pool_status_info(bus, &info);
return 0;
}
static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
int r;
assert(bus);
assert(path);
assert(new_line);
if (*new_line)
printf("\n");
*new_line = true;
r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
if (r < 0)
log_error_errno(r, "Could not get properties: %m");
return r;
}
static int show_image(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
bool properties, new_line = false;
sd_bus *bus = userdata;
int r = 0, i;
assert(bus);
properties = !strstr(argv[0], "status");
pager_open_if_enabled();
if (argc <= 1) {
/* If no argument is specified, inspect the manager
* itself */
if (properties)
r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
else
r = show_pool_info(bus);
if (r < 0)
return r;
}
for (i = 1; i < argc; i++) {
const char *path = NULL;
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"GetImage",
&error,
&reply,
"s", argv[i]);
if (r < 0) {
log_error("Could not get path to image: %s", bus_error_message(&error, -r));
return r;
}
r = sd_bus_message_read(reply, "o", &path);
if (r < 0)
return bus_log_parse_error(r);
if (properties)
r = show_image_properties(bus, path, &new_line);
else
r = show_image_info(bus, path, &new_line);
}
return r;
}
static int kill_machine(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r, i;
assert(bus);
polkit_agent_open_if_enabled();
if (!arg_kill_who)
arg_kill_who = "all";
for (i = 1; i < argc; i++) {
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"KillMachine",
&error,
NULL,
"ssi", argv[i], arg_kill_who, arg_signal);
if (r < 0) {
log_error("Could not kill machine: %s", bus_error_message(&error, -r));
return r;
}
}
return 0;
}
static int reboot_machine(int argc, char *argv[], void *userdata) {
arg_kill_who = "leader";
arg_signal = SIGINT; /* sysvinit + systemd */
return kill_machine(argc, argv, userdata);
}
static int poweroff_machine(int argc, char *argv[], void *userdata) {
arg_kill_who = "leader";
arg_signal = SIGRTMIN+4; /* only systemd */
return kill_machine(argc, argv, userdata);
}
static int terminate_machine(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r, i;
assert(bus);
polkit_agent_open_if_enabled();
for (i = 1; i < argc; i++) {
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"TerminateMachine",
&error,
NULL,
"s", argv[i]);
if (r < 0) {
log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
return r;
}
}
return 0;
}
static int copy_files(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *abs_host_path = NULL;
char *dest, *host_path, *container_path;
sd_bus *bus = userdata;
bool copy_from;
int r;
assert(bus);
polkit_agent_open_if_enabled();
copy_from = streq(argv[0], "copy-from");
dest = argv[3] ?: argv[2];
host_path = copy_from ? dest : argv[2];
container_path = copy_from ? argv[2] : dest;
if (!path_is_absolute(host_path)) {
r = path_make_absolute_cwd(host_path, &abs_host_path);
if (r < 0)
return log_error_errno(r, "Failed to make path absolute: %m");
host_path = abs_host_path;
}
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
copy_from ? "CopyFromMachine" : "CopyToMachine",
&error,
NULL,
"sss",
argv[1],
copy_from ? container_path : host_path,
copy_from ? host_path : container_path);
if (r < 0)
return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r));
return 0;
}
static int bind_mount(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r;
assert(bus);
polkit_agent_open_if_enabled();
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"BindMountMachine",
&error,
NULL,
"sssbb",
argv[1],
argv[2],
argv[3],
arg_read_only,
arg_mkdir);
if (r < 0) {
log_error("Failed to bind mount: %s", bus_error_message(&error, -r));
return r;
}
return 0;
}
static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
PTYForward ** forward = (PTYForward**) userdata;
int r;
assert(m);
assert(forward);
if (*forward) {
/* If the forwarder is already initialized, tell it to
* exit on the next vhangup(), so that we still flush
* out what might be queued and exit then. */
r = pty_forward_set_ignore_vhangup(*forward, false);
if (r >= 0)
return 0;
log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
}
/* On error, or when the forwarder is not initialized yet, quit immediately */
sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
return 0;
}
static int process_forward(sd_event *event, PTYForward **forward, int master, PTYForwardFlags flags, const char *name) {
char last_char = 0;
bool machine_died;
int ret = 0, r;
assert(event);
assert(master >= 0);
assert(name);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
if (streq(name, ".host"))
log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
else
log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
r = pty_forward_new(event, master, flags, forward);
if (r < 0)
return log_error_errno(r, "Failed to create PTY forwarder: %m");
r = sd_event_loop(event);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
pty_forward_get_last_char(*forward, &last_char);
machine_died =
(flags & PTY_FORWARD_IGNORE_VHANGUP) &&
pty_forward_get_ignore_vhangup(*forward) == 0;
*forward = pty_forward_free(*forward);
if (last_char != '\n')
fputc('\n', stdout);
if (machine_died)
log_info("Machine %s terminated.", name);
else if (streq(name, ".host"))
log_info("Connection to the local host terminated.");
else
log_info("Connection to machine %s terminated.", name);
sd_event_get_exit_code(event, &ret);
return ret;
}
static int login_machine(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
_cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
int master = -1, r;
sd_bus *bus = userdata;
const char *pty, *match, *machine;
assert(bus);
if (!strv_isempty(arg_setenv) || arg_uid) {
log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
return -EINVAL;
}
if (arg_transport != BUS_TRANSPORT_LOCAL &&
arg_transport != BUS_TRANSPORT_MACHINE) {
log_error("Login only supported on local machines.");
return -EOPNOTSUPP;
}
polkit_agent_open_if_enabled();
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to get event loop: %m");
r = sd_bus_attach_event(bus, event, 0);
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1];
match = strjoina("type='signal',"
"sender='org.freedesktop.machine1',"
"path='/org/freedesktop/machine1',",
"interface='org.freedesktop.machine1.Manager',"
"member='MachineRemoved',"
"arg0='", machine, "'");
r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
if (r < 0)
return log_error_errno(r, "Failed to add machine removal match: %m");
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"OpenMachineLogin",
&error,
&reply,
"s", machine);
if (r < 0) {
log_error("Failed to get login PTY: %s", bus_error_message(&error, -r));
return r;
}
r = sd_bus_message_read(reply, "hs", &master, &pty);
if (r < 0)
return bus_log_parse_error(r);
return process_forward(event, &forward, master, PTY_FORWARD_IGNORE_VHANGUP, machine);
}
static int shell_machine(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
_cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
int master = -1, r;
sd_bus *bus = userdata;
const char *pty, *match, *machine, *path, *uid = NULL;
assert(bus);
if (arg_transport != BUS_TRANSPORT_LOCAL &&
arg_transport != BUS_TRANSPORT_MACHINE) {
log_error("Shell only supported on local machines.");
return -EOPNOTSUPP;
}
/* Pass $TERM to shell session, if not explicitly specified. */
if (!strv_find_prefix(arg_setenv, "TERM=")) {
const char *t;
t = strv_find_prefix(environ, "TERM=");
if (t) {
if (strv_extend(&arg_setenv, t) < 0)
return log_oom();
}
}
polkit_agent_open_if_enabled();
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to get event loop: %m");
r = sd_bus_attach_event(bus, event, 0);
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
machine = argc < 2 || isempty(argv[1]) ? NULL : argv[1];
if (arg_uid)
uid = arg_uid;
else if (machine) {
const char *at;
at = strchr(machine, '@');
if (at) {
uid = strndupa(machine, at - machine);
machine = at + 1;
}
}
if (isempty(machine))
machine = ".host";
match = strjoina("type='signal',"
"sender='org.freedesktop.machine1',"
"path='/org/freedesktop/machine1',",
"interface='org.freedesktop.machine1.Manager',"
"member='MachineRemoved',"
"arg0='", machine, "'");
r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
if (r < 0)
return log_error_errno(r, "Failed to add machine removal match: %m");
r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"OpenMachineShell");
if (r < 0)
return bus_log_create_error(r);
path = argc < 3 || isempty(argv[2]) ? NULL : argv[2];
r = sd_bus_message_append(m, "sss", machine, uid, path);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_strv(m, arg_setenv);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0) {
log_error("Failed to get shell PTY: %s", bus_error_message(&error, -r));
return r;
}
r = sd_bus_message_read(reply, "hs", &master, &pty);
if (r < 0)
return bus_log_parse_error(r);
return process_forward(event, &forward, master, 0, machine);
}
static int remove_image(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r, i;
assert(bus);
polkit_agent_open_if_enabled();
for (i = 1; i < argc; i++) {
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"RemoveImage",
&error,
NULL,
"s", argv[i]);
if (r < 0) {
log_error("Could not remove image: %s", bus_error_message(&error, -r));
return r;
}
}
return 0;
}
static int rename_image(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r;
polkit_agent_open_if_enabled();
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"RenameImage",
&error,
NULL,
"ss", argv[1], argv[2]);
if (r < 0) {
log_error("Could not rename image: %s", bus_error_message(&error, -r));
return r;
}
return 0;
}
static int clone_image(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r;
polkit_agent_open_if_enabled();
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"CloneImage",
&error,
NULL,
"ssb", argv[1], argv[2], arg_read_only);
if (r < 0) {
log_error("Could not clone image: %s", bus_error_message(&error, -r));
return r;
}
return 0;
}
static int read_only_image(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int b = true, r;
if (argc > 2) {
b = parse_boolean(argv[2]);
if (b < 0) {
log_error("Failed to parse boolean argument: %s", argv[2]);
return -EINVAL;
}
}
polkit_agent_open_if_enabled();
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"MarkImageReadOnly",
&error,
NULL,
"sb", argv[1], b);
if (r < 0) {
log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
return r;
}
return 0;
}
static int make_service_name(const char *name, char **ret) {
_cleanup_free_ char *e = NULL;
int r;
assert(name);
assert(ret);
if (!machine_name_is_valid(name)) {
log_error("Invalid machine name %s.", name);
return -EINVAL;
}
e = unit_name_escape(name);
if (!e)
return log_oom();
r = unit_name_build("systemd-nspawn", e, ".service", ret);
if (r < 0)
return log_error_errno(r, "Failed to build unit name: %m");
return 0;
}
static int start_machine(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
sd_bus *bus = userdata;
int r, i;
assert(bus);
polkit_agent_open_if_enabled();
r = bus_wait_for_jobs_new(bus, &w);
if (r < 0)
return log_oom();
for (i = 1; i < argc; i++) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *unit = NULL;
const char *object;
r = make_service_name(argv[i], &unit);
if (r < 0)
return r;
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartUnit",
&error,
&reply,
"ss", unit, "fail");
if (r < 0) {
log_error("Failed to start unit: %s", bus_error_message(&error, -r));
return r;
}
r = sd_bus_message_read(reply, "o", &object);
if (r < 0)
return bus_log_parse_error(r);
r = bus_wait_for_jobs_add(w, object);
if (r < 0)
return log_oom();
}
r = bus_wait_for_jobs(w, arg_quiet, NULL);
if (r < 0)
return r;
return 0;
}
static int enable_machine(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int carries_install_info = 0;
const char *method = NULL;
sd_bus *bus = userdata;
int r, i;
assert(bus);
polkit_agent_open_if_enabled();
method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
method);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'a', "s");
if (r < 0)
return bus_log_create_error(r);
for (i = 1; i < argc; i++) {
_cleanup_free_ char *unit = NULL;
r = make_service_name(argv[i], &unit);
if (r < 0)
return r;
r = sd_bus_message_append(m, "s", unit);
if (r < 0)
return bus_log_create_error(r);
}
r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
if (streq(argv[0], "enable"))
r = sd_bus_message_append(m, "bb", false, false);
else
r = sd_bus_message_append(m, "b", false);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0) {
log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
return r;
}
if (streq(argv[0], "enable")) {
r = sd_bus_message_read(reply, "b", carries_install_info);
if (r < 0)
return bus_log_parse_error(r);
}
r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
if (r < 0)
return r;
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"Reload",
&error,
NULL,
NULL);
if (r < 0) {
log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
return r;
}
return 0;
}
static int match_log_message(sd_bus_message *m, void *userdata, sd_bus_error *error) {
const char **our_path = userdata, *line;
unsigned priority;
int r;
assert(m);
assert(our_path);
r = sd_bus_message_read(m, "us", &priority, &line);
if (r < 0) {
bus_log_parse_error(r);
return 0;
}
if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
return 0;
if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
return 0;
log_full(priority, "%s", line);
return 0;
}
static int match_transfer_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
const char **our_path = userdata, *path, *result;
uint32_t id;
int r;
assert(m);
assert(our_path);
r = sd_bus_message_read(m, "uos", &id, &path, &result);
if (r < 0) {
bus_log_parse_error(r);
return 0;
}
if (!streq_ptr(*our_path, path))
return 0;
sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), !streq_ptr(result, "done"));
return 0;
}
static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
assert(s);
assert(si);
if (!arg_quiet)
log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
sd_event_exit(sd_event_source_get_event(s), EINTR);
return 0;
}
static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
_cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_event_unrefp) sd_event* event = NULL;
const char *path = NULL;
uint32_t id;
int r;
assert(bus);
assert(m);
polkit_agent_open_if_enabled();
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to get event loop: %m");
r = sd_bus_attach_event(bus, event, 0);
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
r = sd_bus_add_match(
bus,
&slot_job_removed,
"type='signal',"
"sender='org.freedesktop.import1',"
"interface='org.freedesktop.import1.Manager',"
"member='TransferRemoved',"
"path='/org/freedesktop/import1'",
match_transfer_removed, &path);
if (r < 0)
return log_error_errno(r, "Failed to install match: %m");
r = sd_bus_add_match(
bus,
&slot_log_message,
"type='signal',"
"sender='org.freedesktop.import1',"
"interface='org.freedesktop.import1.Transfer',"
"member='LogMessage'",
match_log_message, &path);
if (r < 0)
return log_error_errno(r, "Failed to install match: %m");
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0) {
log_error("Failed transfer image: %s", bus_error_message(&error, -r));
return r;
}
r = sd_bus_message_read(reply, "uo", &id, &path);
if (r < 0)
return bus_log_parse_error(r);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
if (!arg_quiet)
log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
r = sd_event_loop(event);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
return -r;
}
static int import_tar(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *ll = NULL;
_cleanup_close_ int fd = -1;
const char *local = NULL, *path = NULL;
sd_bus *bus = userdata;
int r;
assert(bus);
if (argc >= 2)
path = argv[1];
if (isempty(path) || streq(path, "-"))
path = NULL;
if (argc >= 3)
local = argv[2];
else if (path)
local = basename(path);
if (isempty(local) || streq(local, "-"))
local = NULL;
if (!local) {
log_error("Need either path or local name.");
return -EINVAL;
}
r = tar_strip_suffixes(local, &ll);
if (r < 0)
return log_oom();
local = ll;
if (!machine_name_is_valid(local)) {
log_error("Local name %s is not a suitable machine name.", local);
return -EINVAL;
}
if (path) {
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return log_error_errno(errno, "Failed to open %s: %m", path);
}
r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.import1",
"/org/freedesktop/import1",
"org.freedesktop.import1.Manager",
"ImportTar");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"hsbb",
fd >= 0 ? fd : STDIN_FILENO,
local,
arg_force,
arg_read_only);
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
static int import_raw(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *ll = NULL;
_cleanup_close_ int fd = -1;
const char *local = NULL, *path = NULL;
sd_bus *bus = userdata;
int r;
assert(bus);
if (argc >= 2)
path = argv[1];
if (isempty(path) || streq(path, "-"))
path = NULL;
if (argc >= 3)
local = argv[2];
else if (path)
local = basename(path);
if (isempty(local) || streq(local, "-"))
local = NULL;
if (!local) {
log_error("Need either path or local name.");
return -EINVAL;
}
r = raw_strip_suffixes(local, &ll);
if (r < 0)
return log_oom();
local = ll;
if (!machine_name_is_valid(local)) {
log_error("Local name %s is not a suitable machine name.", local);
return -EINVAL;
}
if (path) {
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return log_error_errno(errno, "Failed to open %s: %m", path);
}
r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.import1",
"/org/freedesktop/import1",
"org.freedesktop.import1.Manager",
"ImportRaw");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"hsbb",
fd >= 0 ? fd : STDIN_FILENO,
local,
arg_force,
arg_read_only);
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
static void determine_compression_from_filename(const char *p) {
if (arg_format)
return;
if (!p)
return;
if (endswith(p, ".xz"))
arg_format = "xz";
else if (endswith(p, ".gz"))
arg_format = "gzip";
else if (endswith(p, ".bz2"))
arg_format = "bzip2";
}
static int export_tar(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_close_ int fd = -1;
const char *local = NULL, *path = NULL;
sd_bus *bus = userdata;
int r;
assert(bus);
local = argv[1];
if (!machine_name_is_valid(local)) {
log_error("Machine name %s is not valid.", local);
return -EINVAL;
}
if (argc >= 3)
path = argv[2];
if (isempty(path) || streq(path, "-"))
path = NULL;
if (path) {
determine_compression_from_filename(path);
fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
if (fd < 0)
return log_error_errno(errno, "Failed to open %s: %m", path);
}
r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.import1",
"/org/freedesktop/import1",
"org.freedesktop.import1.Manager",
"ExportTar");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"shs",
local,
fd >= 0 ? fd : STDOUT_FILENO,
arg_format);
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
static int export_raw(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_close_ int fd = -1;
const char *local = NULL, *path = NULL;
sd_bus *bus = userdata;
int r;
assert(bus);
local = argv[1];
if (!machine_name_is_valid(local)) {
log_error("Machine name %s is not valid.", local);
return -EINVAL;
}
if (argc >= 3)
path = argv[2];
if (isempty(path) || streq(path, "-"))
path = NULL;
if (path) {
determine_compression_from_filename(path);
fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
if (fd < 0)
return log_error_errno(errno, "Failed to open %s: %m", path);
}
r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.import1",
"/org/freedesktop/import1",
"org.freedesktop.import1.Manager",
"ExportRaw");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"shs",
local,
fd >= 0 ? fd : STDOUT_FILENO,
arg_format);
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
static int pull_tar(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *l = NULL, *ll = NULL;
const char *local, *remote;
sd_bus *bus = userdata;
int r;
assert(bus);
remote = argv[1];
if (!http_url_is_valid(remote)) {
log_error("URL '%s' is not valid.", remote);
return -EINVAL;
}
if (argc >= 3)
local = argv[2];
else {
r = import_url_last_component(remote, &l);
if (r < 0)
return log_error_errno(r, "Failed to get final component of URL: %m");
local = l;
}
if (isempty(local) || streq(local, "-"))
local = NULL;
if (local) {
r = tar_strip_suffixes(local, &ll);
if (r < 0)
return log_oom();
local = ll;
if (!machine_name_is_valid(local)) {
log_error("Local name %s is not a suitable machine name.", local);
return -EINVAL;
}
}
r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.import1",
"/org/freedesktop/import1",
"org.freedesktop.import1.Manager",
"PullTar");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"sssb",
remote,
local,
import_verify_to_string(arg_verify),
arg_force);
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
static int pull_raw(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *l = NULL, *ll = NULL;
const char *local, *remote;
sd_bus *bus = userdata;
int r;
assert(bus);
remote = argv[1];
if (!http_url_is_valid(remote)) {
log_error("URL '%s' is not valid.", remote);
return -EINVAL;
}
if (argc >= 3)
local = argv[2];
else {
r = import_url_last_component(remote, &l);
if (r < 0)
return log_error_errno(r, "Failed to get final component of URL: %m");
local = l;
}
if (isempty(local) || streq(local, "-"))
local = NULL;
if (local) {
r = raw_strip_suffixes(local, &ll);
if (r < 0)
return log_oom();
local = ll;
if (!machine_name_is_valid(local)) {
log_error("Local name %s is not a suitable machine name.", local);
return -EINVAL;
}
}
r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.import1",
"/org/freedesktop/import1",
"org.freedesktop.import1.Manager",
"PullRaw");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"sssb",
remote,
local,
import_verify_to_string(arg_verify),
arg_force);
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
typedef struct TransferInfo {
uint32_t id;
const char *type;
const char *remote;
const char *local;
double progress;
} TransferInfo;
static int compare_transfer_info(const void *a, const void *b) {
const TransferInfo *x = a, *y = b;
return strcmp(x->local, y->local);
}
static int list_transfers(int argc, char *argv[], void *userdata) {
size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ TransferInfo *transfers = NULL;
size_t n_transfers = 0, n_allocated = 0, j;
const char *type, *remote, *local, *object;
sd_bus *bus = userdata;
uint32_t id, max_id = 0;
double progress;
int r;
pager_open_if_enabled();
r = sd_bus_call_method(
bus,
"org.freedesktop.import1",
"/org/freedesktop/import1",
"org.freedesktop.import1.Manager",
"ListTransfers",
&error,
&reply,
NULL);
if (r < 0) {
log_error("Could not get transfers: %s", bus_error_message(&error, -r));
return r;
}
r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
if (r < 0)
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
size_t l;
if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
return log_oom();
transfers[n_transfers].id = id;
transfers[n_transfers].type = type;
transfers[n_transfers].remote = remote;
transfers[n_transfers].local = local;
transfers[n_transfers].progress = progress;
l = strlen(type);
if (l > max_type)
max_type = l;
l = strlen(remote);
if (l > max_remote)
max_remote = l;
l = strlen(local);
if (l > max_local)
max_local = l;
if (id > max_id)
max_id = id;
n_transfers ++;
}
if (r < 0)
return bus_log_parse_error(r);
r = sd_bus_message_exit_container(reply);
if (r < 0)
return bus_log_parse_error(r);
qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
if (arg_legend)
printf("%-*s %-*s %-*s %-*s %-*s\n",
(int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
(int) 7, "PERCENT",
(int) max_type, "TYPE",
(int) max_local, "LOCAL",
(int) max_remote, "REMOTE");
for (j = 0; j < n_transfers; j++)
printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
(int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
(int) 6, (unsigned) (transfers[j].progress * 100),
(int) max_type, transfers[j].type,
(int) max_local, transfers[j].local,
(int) max_remote, transfers[j].remote);
if (arg_legend)
printf("\n%zu transfers listed.\n", n_transfers);
return 0;
}
static int cancel_transfer(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r, i;
assert(bus);
polkit_agent_open_if_enabled();
for (i = 1; i < argc; i++) {
uint32_t id;
r = safe_atou32(argv[i], &id);
if (r < 0)
return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
r = sd_bus_call_method(
bus,
"org.freedesktop.import1",
"/org/freedesktop/import1",
"org.freedesktop.import1.Manager",
"CancelTransfer",
&error,
NULL,
"u", id);
if (r < 0) {
log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
return r;
}
}
return 0;
}
static int set_limit(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
uint64_t limit;
int r;
if (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
limit = (uint64_t) -1;
else {
r = parse_size(argv[argc-1], 1024, &limit);
if (r < 0)
return log_error("Failed to parse size: %s", argv[argc-1]);
}
if (argc > 2)
/* With two arguments changes the quota limit of the
* specified image */
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"SetImageLimit",
&error,
NULL,
"st", argv[1], limit);
else
/* With one argument changes the pool quota limit */
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"SetPoolLimit",
&error,
NULL,
"t", limit);
if (r < 0) {
log_error("Could not set limit: %s", bus_error_message(&error, -r));
return r;
}
return 0;
}
static int help(int argc, char *argv[], void *userdata) {
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Send control commands to or query the virtual machine and container\n"
"registration manager.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" --no-legend Do not show the headers and footers\n"
" --no-ask-password Do not ask for system passwords\n"
" -H --host=[USER@]HOST Operate on remote host\n"
" -M --machine=CONTAINER Operate on local container\n"
" -p --property=NAME Show only properties by this name\n"
" -q --quiet Suppress output\n"
" -a --all Show all properties, including empty ones\n"
" -l --full Do not ellipsize output\n"
" --kill-who=WHO Who to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n"
" --uid=USER Specify user ID to invoke shell as\n"
" --setenv=VAR=VALUE Add an environment variable for shell\n"
" --read-only Create read-only bind mount\n"
" --mkdir Create directory before bind mounting, if missing\n"
" -n --lines=INTEGER Number of journal entries to show\n"
" -o --output=STRING Change journal output mode (short,\n"
" short-monotonic, verbose, export, json,\n"
" json-pretty, json-sse, cat)\n"
" --verify=MODE Verification mode for downloaded images (no,\n"
" checksum, signature)\n"
" --force Download image even if already exists\n\n"
"Machine Commands:\n"
" list List running VMs and containers\n"
" status NAME... Show VM/container details\n"
" show [NAME...] Show properties of one or more VMs/containers\n"
" start NAME... Start container as a service\n"
" login [NAME] Get a login prompt in a container or on the\n"
" local host\n"
" shell [[USER@]NAME [COMMAND...]]\n"
" Invoke a shell (or other command) in a container\n"
" or on the local host\n"
" enable NAME... Enable automatic container start at boot\n"
" disable NAME... Disable automatic container start at boot\n"
" poweroff NAME... Power off one or more containers\n"
" reboot NAME... Reboot one or more containers\n"
" terminate NAME... Terminate one or more VMs/containers\n"
" kill NAME... Send signal to processes of a VM/container\n"
" copy-to NAME PATH [PATH] Copy files from the host to a container\n"
" copy-from NAME PATH [PATH] Copy files from a container to the host\n"
" bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
"Image Commands:\n"
" list-images Show available container and VM images\n"
" image-status [NAME...] Show image details\n"
" show-image [NAME...] Show properties of image\n"
" clone NAME NAME Clone an image\n"
" rename NAME NAME Rename an image\n"
" read-only NAME [BOOL] Mark or unmark image read-only\n"
" remove NAME... Remove an image\n"
" set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
"Image Transfer Commands:\n"
" pull-tar URL [NAME] Download a TAR container image\n"
" pull-raw URL [NAME] Download a RAW container or VM image\n"
" import-tar FILE [NAME] Import a local TAR container image\n"
" import-raw FILE [NAME] Import a local RAW container or VM image\n"
" export-tar NAME [FILE] Export a TAR container image locally\n"
" export-raw NAME [FILE] Export a RAW container or VM image locally\n"
" list-transfers Show list of downloads in progress\n"
" cancel-transfer Cancel a download\n"
, program_invocation_short_name);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
ARG_NO_LEGEND,
ARG_KILL_WHO,
ARG_READ_ONLY,
ARG_MKDIR,
ARG_NO_ASK_PASSWORD,
ARG_VERIFY,
ARG_FORCE,
ARG_FORMAT,
ARG_UID,
ARG_SETENV,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "property", required_argument, NULL, 'p' },
{ "all", no_argument, NULL, 'a' },
{ "full", no_argument, NULL, 'l' },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "kill-who", required_argument, NULL, ARG_KILL_WHO },
{ "signal", required_argument, NULL, 's' },
{ "host", required_argument, NULL, 'H' },
{ "machine", required_argument, NULL, 'M' },
{ "read-only", no_argument, NULL, ARG_READ_ONLY },
{ "mkdir", no_argument, NULL, ARG_MKDIR },
{ "quiet", no_argument, NULL, 'q' },
{ "lines", required_argument, NULL, 'n' },
{ "output", required_argument, NULL, 'o' },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "verify", required_argument, NULL, ARG_VERIFY },
{ "force", no_argument, NULL, ARG_FORCE },
{ "format", required_argument, NULL, ARG_FORMAT },
{ "uid", required_argument, NULL, ARG_UID },
{ "setenv", required_argument, NULL, ARG_SETENV },
{}
};
int c, r;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
switch (c) {
case 'h':
return help(0, NULL, NULL);
case ARG_VERSION:
return version();
case 'p':
r = strv_extend(&arg_property, optarg);
if (r < 0)
return log_oom();
/* If the user asked for a particular
* property, show it to him, even if it is
* empty. */
arg_all = true;
break;
case 'a':
arg_all = true;
break;
case 'l':
arg_full = true;
break;
case 'n':
if (safe_atou(optarg, &arg_lines) < 0) {
log_error("Failed to parse lines '%s'", optarg);
return -EINVAL;
}
break;
case 'o':
arg_output = output_mode_from_string(optarg);
if (arg_output < 0) {
log_error("Unknown output '%s'.", optarg);
return -EINVAL;
}
break;
case ARG_NO_PAGER:
arg_no_pager = true;
break;
case ARG_NO_LEGEND:
arg_legend = false;
break;
case ARG_KILL_WHO:
arg_kill_who = optarg;
break;
case 's':
arg_signal = signal_from_string_try_harder(optarg);
if (arg_signal < 0) {
log_error("Failed to parse signal string %s.", optarg);
return -EINVAL;
}
break;
case ARG_NO_ASK_PASSWORD:
arg_ask_password = false;
break;
case 'H':
arg_transport = BUS_TRANSPORT_REMOTE;
arg_host = optarg;
break;
case 'M':
arg_transport = BUS_TRANSPORT_MACHINE;
arg_host = optarg;
break;
case ARG_READ_ONLY:
arg_read_only = true;
break;
case ARG_MKDIR:
arg_mkdir = true;
break;
case 'q':
arg_quiet = true;
break;
case ARG_VERIFY:
arg_verify = import_verify_from_string(optarg);
if (arg_verify < 0) {
log_error("Failed to parse --verify= setting: %s", optarg);
return -EINVAL;
}
break;
case ARG_FORCE:
arg_force = true;
break;
case ARG_FORMAT:
if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) {
log_error("Unknown format: %s", optarg);
return -EINVAL;
}
arg_format = optarg;
break;
case ARG_UID:
arg_uid = optarg;
break;
case ARG_SETENV:
if (!env_assignment_is_valid(optarg)) {
log_error("Environment assignment invalid: %s", optarg);
return -EINVAL;
}
r = strv_extend(&arg_setenv, optarg);
if (r < 0)
return log_oom();
break;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
return 1;
}
static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },
{ "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
{ "list-images", VERB_ANY, 1, 0, list_images },
{ "status", 2, VERB_ANY, 0, show_machine },
{ "image-status", VERB_ANY, VERB_ANY, 0, show_image },
{ "show", VERB_ANY, VERB_ANY, 0, show_machine },
{ "show-image", VERB_ANY, VERB_ANY, 0, show_image },
{ "terminate", 2, VERB_ANY, 0, terminate_machine },
{ "reboot", 2, VERB_ANY, 0, reboot_machine },
{ "poweroff", 2, VERB_ANY, 0, poweroff_machine },
{ "kill", 2, VERB_ANY, 0, kill_machine },
{ "login", VERB_ANY, 2, 0, login_machine },
{ "shell", VERB_ANY, VERB_ANY, 0, shell_machine },
{ "bind", 3, 4, 0, bind_mount },
{ "copy-to", 3, 4, 0, copy_files },
{ "copy-from", 3, 4, 0, copy_files },
{ "remove", 2, VERB_ANY, 0, remove_image },
{ "rename", 3, 3, 0, rename_image },
{ "clone", 3, 3, 0, clone_image },
{ "read-only", 2, 3, 0, read_only_image },
{ "start", 2, VERB_ANY, 0, start_machine },
{ "enable", 2, VERB_ANY, 0, enable_machine },
{ "disable", 2, VERB_ANY, 0, enable_machine },
{ "import-tar", 2, 3, 0, import_tar },
{ "import-raw", 2, 3, 0, import_raw },
{ "export-tar", 2, 3, 0, export_tar },
{ "export-raw", 2, 3, 0, export_raw },
{ "pull-tar", 2, 3, 0, pull_tar },
{ "pull-raw", 2, 3, 0, pull_raw },
{ "list-transfers", VERB_ANY, 1, 0, list_transfers },
{ "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
{ "set-limit", 2, 3, 0, set_limit },
{}
};
return dispatch_verb(argc, argv, verbs, bus);
}
int main(int argc, char*argv[]) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
r = bus_connect_transport(arg_transport, arg_host, false, &bus);
if (r < 0) {
log_error_errno(r, "Failed to create bus connection: %m");
goto finish;
}
sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
r = machinectl_main(argc, argv, bus);
finish:
pager_close();
polkit_agent_close();
strv_free(arg_property);
strv_free(arg_setenv);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}