machinectl.c revision f647962d64e844689f3e2acfce6102fc47e76df2
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/***
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering This file is part of systemd.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Copyright 2013 Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is free software; you can redistribute it and/or modify it
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering under the terms of the GNU Lesser General Public License as published by
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering (at your option) any later version.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is distributed in the hope that it will be useful, but
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Lesser General Public License for more details.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering You should have received a copy of the GNU Lesser General Public License
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering***/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
b5efdb8af40ea759a1ea584c1bc44ecc81dd00ceLennart Poettering#include <sys/socket.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <unistd.h>
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering#include <errno.h>
b5efdb8af40ea759a1ea584c1bc44ecc81dd00ceLennart Poettering#include <string.h>
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering#include <getopt.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <pwd.h>
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen#include <locale.h>
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen#include <fcntl.h>
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen#include <netinet/in.h>
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen#include <arpa/inet.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <net/if.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "sd-bus.h"
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering#include "log.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "util.h"
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering#include "macro.h"
3c0cf502796be355431d4a64d738e75f543aa51dLennart Poettering#include "pager.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "bus-util.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "bus-error.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "build.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "strv.h"
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering#include "unit-name.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "cgroup-show.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "cgroup-util.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "ptyfwd.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "event-util.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic char **arg_property = NULL;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersenstatic bool arg_all = false;
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poetteringstatic bool arg_full = false;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic bool arg_no_pager = false;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic bool arg_legend = true;
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersenstatic const char *arg_kill_who = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic int arg_signal = SIGTERM;
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poetteringstatic BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
6073b6f26ab9fc6bf335faa7073ec443eef093fdTom Gundersenstatic char *arg_host = NULL;
6073b6f26ab9fc6bf335faa7073ec443eef093fdTom Gundersen
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic void pager_open_if_enabled(void) {
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering /* Cache result before we open the pager */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (arg_no_pager)
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering return;
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering pager_open(false);
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering}
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic int list_machines(sd_bus *bus, char **args, unsigned n) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering const char *name, *class, *service, *object;
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering unsigned k = 0;
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering int r;
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering pager_open_if_enabled();
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering r = sd_bus_call_method(
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering bus,
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering "org.freedesktop.machine1",
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering "/org/freedesktop/machine1",
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering "org.freedesktop.machine1.Manager",
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering "ListMachines",
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering &error,
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering &reply,
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen "");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (r < 0) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering log_error("Could not get machines: %s", bus_error_message(&error, -r));
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return r;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen }
cab5b05903096e1c9cf5575ccc73f89d15c8db69Lennart Poettering
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen if (arg_legend)
cab5b05903096e1c9cf5575ccc73f89d15c8db69Lennart Poettering printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen if (r < 0)
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen return bus_log_parse_error(r);
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering printf("%-32s %-9s %-16s\n", name, class, service);
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen k++;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen }
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen if (r < 0)
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen return bus_log_parse_error(r);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = sd_bus_message_exit_container(reply);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (r < 0)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return bus_log_parse_error(r);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering if (arg_legend)
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen printf("\n%u machines listed.\n", k);
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen return 0;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen}
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersenstatic int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen _cleanup_free_ char *path = NULL;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen const char *cgroup;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen int r, output_flags;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen unsigned c;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen assert(bus);
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen assert(unit);
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen if (arg_transport == BUS_TRANSPORT_REMOTE)
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering return 0;
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering path = unit_dbus_path_from_name(unit);
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering if (!path)
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering return log_oom();
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen r = sd_bus_get_property(
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen bus,
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen "org.freedesktop.systemd1",
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen path,
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering "ControlGroup",
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering &error,
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering &reply,
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen "s");
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen if (r < 0) {
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering return r;
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering }
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering r = sd_bus_message_read(reply, "s", &cgroup);
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen if (r < 0)
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen return bus_log_parse_error(r);
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering if (isempty(cgroup))
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt return 0;
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering return 0;
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering output_flags =
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering arg_all * OUTPUT_SHOW_ALL |
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering arg_full * OUTPUT_FULL_WIDTH;
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering c = columns();
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt if (c > 18)
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt c -= 18;
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt else
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt c = 0;
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering return 0;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering}
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poetteringstatic int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering int r;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering assert(bus);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering assert(name);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering assert(prefix);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering assert(prefix2);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering r = sd_bus_call_method(bus,
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering "org.freedesktop.machine1",
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering "/org/freedesktop/machine1",
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering "org.freedesktop.machine1.Manager",
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering "GetMachineAddresses",
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering NULL,
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering &reply,
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering "s", name);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering if (r < 0)
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering return r;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering r = sd_bus_message_enter_container(reply, 'a', "(iay)");
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering if (r < 0)
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering return bus_log_parse_error(r);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering int family;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering const void *a;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering size_t sz;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering r = sd_bus_message_read(reply, "i", &family);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering if (r < 0)
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering return bus_log_parse_error(r);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering r = sd_bus_message_read_array(reply, 'y', &a, &sz);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering if (r < 0)
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering return bus_log_parse_error(r);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering fputs(prefix, stdout);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (family == AF_INET6 && ifi > 0)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering printf("%%%i", ifi);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering fputc('\n', stdout);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering r = sd_bus_message_exit_container(reply);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (r < 0)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return bus_log_parse_error(r);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (prefix != prefix2)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering prefix = prefix2;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering }
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (r < 0)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return bus_log_parse_error(r);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering r = sd_bus_message_exit_container(reply);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (r < 0)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return bus_log_parse_error(r);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return 0;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering}
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poetteringstatic int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering const char *k, *v, *pretty = NULL;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering int r;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering assert(bus);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering assert(name);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering assert(prefix);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering r = sd_bus_call_method(bus,
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering "org.freedesktop.machine1",
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering "/org/freedesktop/machine1",
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering "org.freedesktop.machine1.Manager",
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering "GetMachineOSRelease",
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering NULL,
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering &reply,
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering "s", name);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (r < 0)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return r;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering r = sd_bus_message_enter_container(reply, 'a', "{ss}");
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (r < 0)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return bus_log_parse_error(r);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (streq(k, "PRETTY_NAME"))
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering pretty = v;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering }
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (r < 0)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return bus_log_parse_error(r);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering r = sd_bus_message_exit_container(reply);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (r < 0)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return bus_log_parse_error(r);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (pretty)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering printf("%s%s\n", prefix, pretty);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return 0;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering}
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poetteringtypedef struct MachineStatusInfo {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering char *name;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering sd_id128_t id;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering char *class;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering char *service;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering char *unit;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering char *root_directory;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering pid_t leader;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering usec_t timestamp;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering int *netif;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering unsigned n_netif;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering} MachineStatusInfo;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poetteringstatic void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering char since2[FORMAT_TIMESTAMP_MAX], *s2;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering int ifi = -1;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering assert(i);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering fputs(strna(i->name), stdout);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (!sd_id128_equal(i->id, SD_ID128_NULL))
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering else
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering putchar('\n');
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
if (s1)
printf("\t Since: %s; %s\n", s2, s1);
else if (s2)
printf("\t Since: %s\n", s2);
if (i->leader > 0) {
_cleanup_free_ char *t = NULL;
printf("\t Leader: %u", (unsigned) i->leader);
get_process_comm(i->leader, &t);
if (t)
printf(" (%s)", t);
putchar('\n');
}
if (i->service) {
printf("\t Service: %s", i->service);
if (i->class)
printf("; class %s", i->class);
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);
}
}
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_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) },
{ "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
{ "NetworkInterfaces", "ai", map_netif, 0 },
{}
};
MachineStatusInfo info = {};
int r;
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);
free(info.name);
free(info.class);
free(info.service);
free(info.unit);
free(info.root_directory);
free(info.netif);
return r;
}
static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
int r;
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(sd_bus *bus, char **args, unsigned n) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r = 0;
unsigned i;
bool properties, new_line = false;
assert(bus);
assert(args);
properties = !strstr(args[0], "status");
pager_open_if_enabled();
if (properties && n <= 1) {
/* If no argument is specified, inspect the manager
* itself */
r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
if (r < 0)
return r;
}
for (i = 1; i < n; 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", args[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_properties(bus, path, &new_line);
else
r = show_info(args[0], bus, path, &new_line);
}
return r;
}
static int kill_machine(sd_bus *bus, char **args, unsigned n) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
unsigned i;
assert(args);
if (!arg_kill_who)
arg_kill_who = "all";
for (i = 1; i < n; i++) {
int r;
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"KillMachine",
&error,
NULL,
"ssi", args[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(sd_bus *bus, char **args, unsigned n) {
arg_kill_who = "leader";
arg_signal = SIGINT; /* sysvinit + systemd */
return kill_machine(bus, args, n);
}
static int poweroff_machine(sd_bus *bus, char **args, unsigned n) {
arg_kill_who = "leader";
arg_signal = SIGRTMIN+4; /* only systemd */
return kill_machine(bus, args, n);
}
static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
unsigned i;
assert(args);
for (i = 1; i < n; i++) {
int r;
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"TerminateMachine",
&error,
NULL,
"s", args[i]);
if (r < 0) {
log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
return r;
}
}
return 0;
}
static int openpt_in_namespace(pid_t pid, int flags) {
_cleanup_close_pair_ int pair[2] = { -1, -1 };
_cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
} control = {};
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
int master = -1, r;
pid_t child;
siginfo_t si;
r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
if (r < 0)
return r;
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
child = fork();
if (child < 0)
return -errno;
if (child == 0) {
pair[0] = safe_close(pair[0]);
r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
if (r < 0)
_exit(EXIT_FAILURE);
master = posix_openpt(flags);
if (master < 0)
_exit(EXIT_FAILURE);
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
mh.msg_controllen = cmsg->cmsg_len;
if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
}
pair[1] = safe_close(pair[1]);
r = wait_for_terminate(child, &si);
if (r < 0)
return r;
if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
return -EIO;
if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
return -errno;
for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
int *fds;
unsigned n_fds;
fds = (int*) CMSG_DATA(cmsg);
n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
if (n_fds != 1) {
close_many(fds, n_fds);
return -EIO;
}
master = fds[0];
}
if (master < 0)
return -EIO;
return master;
}
static int login_machine(sd_bus *bus, char **args, unsigned n) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_bus_close_unref_ sd_bus *container_bus = NULL;
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
_cleanup_event_unref_ sd_event *event = NULL;
_cleanup_close_ int master = -1;
_cleanup_free_ char *getty = NULL;
const char *path, *pty, *p;
uint32_t leader;
sigset_t mask;
int r, ret = 0;
assert(bus);
assert(args);
if (arg_transport != BUS_TRANSPORT_LOCAL) {
log_error("Login only supported on local machines.");
return -ENOTSUP;
}
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_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"GetMachine",
&error,
&reply,
"s", args[1]);
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);
r = sd_bus_get_property(
bus,
"org.freedesktop.machine1",
path,
"org.freedesktop.machine1.Machine",
"Leader",
&error,
&reply2,
"u");
if (r < 0)
return log_error_errno(r, "Failed to retrieve PID of leader: %m");
r = sd_bus_message_read(reply2, "u", &leader);
if (r < 0)
return bus_log_parse_error(r);
master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
if (master < 0)
return log_error_errno(master, "Failed to acquire pseudo tty: %m");
pty = ptsname(master);
if (!pty) {
log_error("Failed to get pty name: %m");
return -errno;
}
p = startswith(pty, "/dev/pts/");
if (!p) {
log_error("Invalid pty name %s.", pty);
return -EIO;
}
r = sd_bus_open_system_container(&container_bus, args[1]);
if (r < 0)
return log_error_errno(r, "Failed to get container bus: %m");
getty = strjoin("container-getty@", p, ".service", NULL);
if (!getty)
return log_oom();
if (unlockpt(master) < 0) {
log_error("Failed to unlock tty: %m");
return -errno;
}
r = sd_bus_call_method(container_bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartUnit",
&error, &reply3,
"ss", getty, "replace");
if (r < 0) {
log_error("Failed to start getty service: %s", bus_error_message(&error, r));
return r;
}
container_bus = sd_bus_unref(container_bus);
assert_se(sigemptyset(&mask) == 0);
sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
r = pty_forward_new(event, master, &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");
forward = pty_forward_free(forward);
fputc('\n', stdout);
log_info("Connection to container %s terminated.", args[1]);
sd_event_get_exit_code(event, &ret);
return ret;
}
static void help(void) {
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Send control commands to or query the virtual machine and container 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"
" -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"
" -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\n"
"Commands:\n"
" list List running VMs and containers\n"
" status NAME... Show VM/container status\n"
" show NAME... Show properties of one or more VMs/containers\n"
" login NAME Get a login prompt on a container\n"
" poweroff NAME... Power off one or more containers\n"
" reboot NAME... Reboot one or more containers\n"
" kill NAME... Send signal to processes of a VM/container\n"
" terminate NAME... Terminate one or more VMs/containers\n",
program_invocation_short_name);
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
ARG_NO_LEGEND,
ARG_KILL_WHO,
};
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' },
{}
};
int c, r;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
switch (c) {
case 'h':
help();
return 0;
case ARG_VERSION:
puts(PACKAGE_STRING);
puts(SYSTEMD_FEATURES);
return 0;
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 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 'H':
arg_transport = BUS_TRANSPORT_REMOTE;
arg_host = optarg;
break;
case 'M':
arg_transport = BUS_TRANSPORT_CONTAINER;
arg_host = optarg;
break;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
return 1;
}
static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
static const struct {
const char* verb;
const enum {
MORE,
LESS,
EQUAL
} argc_cmp;
const int argc;
int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
} verbs[] = {
{ "list", LESS, 1, list_machines },
{ "status", MORE, 2, show },
{ "show", MORE, 1, show },
{ "terminate", MORE, 2, terminate_machine },
{ "reboot", MORE, 2, reboot_machine },
{ "poweroff", MORE, 2, poweroff_machine },
{ "kill", MORE, 2, kill_machine },
{ "login", MORE, 2, login_machine },
};
int left;
unsigned i;
assert(argc >= 0);
assert(argv);
left = argc - optind;
if (left <= 0)
/* Special rule: no arguments means "list" */
i = 0;
else {
if (streq(argv[optind], "help")) {
help();
return 0;
}
for (i = 0; i < ELEMENTSOF(verbs); i++)
if (streq(argv[optind], verbs[i].verb))
break;
if (i >= ELEMENTSOF(verbs)) {
log_error("Unknown operation %s", argv[optind]);
return -EINVAL;
}
}
switch (verbs[i].argc_cmp) {
case EQUAL:
if (left != verbs[i].argc) {
log_error("Invalid number of arguments.");
return -EINVAL;
}
break;
case MORE:
if (left < verbs[i].argc) {
log_error("Too few arguments.");
return -EINVAL;
}
break;
case LESS:
if (left > verbs[i].argc) {
log_error("Too many arguments.");
return -EINVAL;
}
break;
default:
assert_not_reached("Unknown comparison operator.");
}
return verbs[i].dispatch(bus, argv + optind, left);
}
int main(int argc, char*argv[]) {
_cleanup_bus_close_unref_ 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_open_transport(arg_transport, arg_host, false, &bus);
if (r < 0) {
log_error_errno(r, "Failed to create bus connection: %m");
goto finish;
}
r = machinectl_main(bus, argc, argv);
finish:
pager_close();
strv_free(arg_property);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}