summaryrefslogtreecommitdiff
path: root/src/login/loginctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/login/loginctl.c')
-rw-r--r--src/login/loginctl.c382
1 files changed, 348 insertions, 34 deletions
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index 1b9723fdbf..3637a408dc 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -261,6 +261,69 @@ static int list_seats(DBusConnection *bus, char **args, unsigned n) {
return 0;
}
+static int list_machines(DBusConnection *bus, char **args, unsigned n) {
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ DBusMessageIter iter, sub, sub2;
+ unsigned k = 0;
+ int r;
+
+ pager_open_if_enabled();
+
+ r = bus_method_call_with_reply (
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "ListMachines",
+ &reply,
+ NULL,
+ DBUS_TYPE_INVALID);
+ if (r)
+ return r;
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (on_tty())
+ printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *name, *class, *service, *object;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &class, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &service, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ printf("%-32s %-9s %-16s\n", name, class, service);
+
+ k++;
+
+ dbus_message_iter_next(&sub);
+ }
+
+ if (on_tty())
+ printf("\n%u machines listed.\n", k);
+
+ return 0;
+}
+
typedef struct SessionStatusInfo {
const char *id;
uid_t uid;
@@ -279,6 +342,7 @@ typedef struct SessionStatusInfo {
const char *type;
const char *class;
const char *state;
+ const char *slice;
} SessionStatusInfo;
typedef struct UserStatusInfo {
@@ -289,6 +353,7 @@ typedef struct UserStatusInfo {
const char *state;
char **sessions;
const char *display;
+ const char *slice;
} UserStatusInfo;
typedef struct SeatStatusInfo {
@@ -297,6 +362,18 @@ typedef struct SeatStatusInfo {
char **sessions;
} SeatStatusInfo;
+typedef struct MachineStatusInfo {
+ const char *name;
+ sd_id128_t id;
+ const char *default_control_group;
+ const char *class;
+ const char *service;
+ const char *slice;
+ const char *root_directory;
+ pid_t leader;
+ usec_t timestamp;
+} MachineStatusInfo;
+
static void print_session_status_info(SessionStatusInfo *i) {
char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
char since2[FORMAT_TIMESTAMP_MAX], *s2;
@@ -318,15 +395,13 @@ static void print_session_status_info(SessionStatusInfo *i) {
printf("\t Since: %s\n", s2);
if (i->leader > 0) {
- char *t = NULL;
+ _cleanup_free_ char *t = NULL;
printf("\t Leader: %u", (unsigned) i->leader);
get_process_comm(i->leader, &t);
- if (t) {
+ if (t)
printf(" (%s)", t);
- free(t);
- }
printf("\n");
}
@@ -375,6 +450,9 @@ static void print_session_status_info(SessionStatusInfo *i) {
if (i->state)
printf("\t State: %s\n", i->state);
+ if (i->slice)
+ printf("\t Slice: %s\n", i->slice);
+
if (i->default_control_group) {
unsigned c;
int output_flags =
@@ -419,6 +497,9 @@ static void print_user_status_info(UserStatusInfo *i) {
if (!isempty(i->state))
printf("\t State: %s\n", i->state);
+ if (i->slice)
+ printf("\t Slice: %s\n", i->slice);
+
if (!strv_isempty(i->sessions)) {
char **l;
printf("\tSessions:");
@@ -488,6 +569,76 @@ static void print_seat_status_info(SeatStatusInfo *i) {
}
}
+static void print_machine_status_info(MachineStatusInfo *i) {
+ char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
+ char since2[FORMAT_TIMESTAMP_MAX], *s2;
+ assert(i);
+
+ fputs(strna(i->name), stdout);
+
+ if (!sd_id128_equal(i->id, SD_ID128_NULL))
+ printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
+ else
+ putchar('\n');
+
+ 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->slice)
+ printf("\t Slice: %s\n", i->slice);
+ if (i->root_directory)
+ printf("\t Root: %s\n", i->root_directory);
+
+ if (i->default_control_group) {
+ unsigned c;
+ int output_flags =
+ arg_all * OUTPUT_SHOW_ALL |
+ arg_full * OUTPUT_FULL_WIDTH;
+
+ printf("\t CGroup: %s\n", i->default_control_group);
+
+ if (arg_transport != TRANSPORT_SSH) {
+ c = columns();
+ if (c > 18)
+ c -= 18;
+ else
+ c = 0;
+
+ show_cgroup_and_extra_by_spec(i->default_control_group,
+ "\t\t ", c, false, &i->leader,
+ i->leader > 0 ? 1 : 0,
+ output_flags);
+ }
+ }
+}
+
static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
assert(name);
assert(iter);
@@ -521,6 +672,8 @@ static int status_property_session(const char *name, DBusMessageIter *iter, Sess
i->type = s;
else if (streq(name, "Class"))
i->class = s;
+ else if (streq(name, "Slice"))
+ i->slice = s;
else if (streq(name, "State"))
i->state = s;
}
@@ -606,6 +759,8 @@ static int status_property_user(const char *name, DBusMessageIter *iter, UserSta
i->name = s;
else if (streq(name, "DefaultControlGroup"))
i->default_control_group = s;
+ else if (streq(name, "Slice"))
+ i->slice = s;
else if (streq(name, "State"))
i->state = s;
}
@@ -757,6 +912,80 @@ static int status_property_seat(const char *name, DBusMessageIter *iter, SeatSta
return 0;
}
+static int status_property_machine(const char *name, DBusMessageIter *iter, MachineStatusInfo *i) {
+ assert(name);
+ assert(iter);
+ assert(i);
+
+ switch (dbus_message_iter_get_arg_type(iter)) {
+
+ case DBUS_TYPE_STRING: {
+ const char *s;
+
+ dbus_message_iter_get_basic(iter, &s);
+
+ if (!isempty(s)) {
+ if (streq(name, "Name"))
+ i->name = s;
+ else if (streq(name, "DefaultControlGroup"))
+ i->default_control_group = s;
+ else if (streq(name, "Class"))
+ i->class = s;
+ else if (streq(name, "Service"))
+ i->service = s;
+ else if (streq(name, "Slice"))
+ i->slice = s;
+ else if (streq(name, "RootDirectory"))
+ i->root_directory = s;
+ }
+ break;
+ }
+
+ case DBUS_TYPE_UINT32: {
+ uint32_t u;
+
+ dbus_message_iter_get_basic(iter, &u);
+
+ if (streq(name, "Leader"))
+ i->leader = (pid_t) u;
+
+ break;
+ }
+
+ case DBUS_TYPE_UINT64: {
+ uint64_t u;
+
+ dbus_message_iter_get_basic(iter, &u);
+
+ if (streq(name, "Timestamp"))
+ i->timestamp = (usec_t) u;
+
+ break;
+ }
+
+ case DBUS_TYPE_ARRAY: {
+ DBusMessageIter sub;
+
+ dbus_message_iter_recurse(iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE && streq(name, "Id")) {
+ void *v;
+ int n;
+
+ dbus_message_iter_get_fixed_array(&sub, &v, &n);
+ if (n == 0)
+ i->id = SD_ID128_NULL;
+ else if (n == 16)
+ memcpy(&i->id, v, n);
+ }
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
static int print_property(const char *name, DBusMessageIter *iter) {
assert(name);
assert(iter);
@@ -838,6 +1067,7 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
SessionStatusInfo session_info = {};
UserStatusInfo user_info = {};
SeatStatusInfo seat_info = {};
+ MachineStatusInfo machine_info = {};
assert(path);
assert(new_line);
@@ -901,8 +1131,10 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
r = status_property_session(name, &sub3, &session_info);
else if (strstr(verb, "user"))
r = status_property_user(name, &sub3, &user_info);
- else
+ else if (strstr(verb, "seat"))
r = status_property_seat(name, &sub3, &seat_info);
+ else
+ r = status_property_machine(name, &sub3, &machine_info);
if (r < 0) {
log_error("Failed to parse reply.");
@@ -917,8 +1149,10 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
print_session_status_info(&session_info);
else if (strstr(verb, "user"))
print_user_status_info(&user_info);
- else
+ else if (strstr(verb, "seat"))
print_seat_status_info(&seat_info);
+ else
+ print_machine_status_info(&machine_info);
}
r = 0;
@@ -981,7 +1215,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
}
u = (uint32_t) uid;
- ret = bus_method_call_with_reply (
+ ret = bus_method_call_with_reply(
bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
@@ -991,9 +1225,10 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
NULL,
DBUS_TYPE_UINT32, &u,
DBUS_TYPE_INVALID);
- } else {
- ret = bus_method_call_with_reply (
+ } else if (strstr(args[0], "seat")) {
+
+ ret = bus_method_call_with_reply(
bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
@@ -1003,8 +1238,22 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
NULL,
DBUS_TYPE_STRING, &args[i],
DBUS_TYPE_INVALID);
+
+ } else {
+
+ ret = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "GetMachine",
+ &reply,
+ NULL,
+ DBUS_TYPE_STRING, &args[i],
+ DBUS_TYPE_INVALID);
}
- if (ret)
+
+ if (ret < 0)
goto finish;
if (!dbus_message_get_args(reply, &error,
@@ -1085,6 +1334,36 @@ static int kill_session(DBusConnection *bus, char **args, unsigned n) {
return 0;
}
+static int kill_machine(DBusConnection *bus, char **args, unsigned n) {
+ unsigned i;
+
+ assert(args);
+
+ if (!arg_kill_who)
+ arg_kill_who = "all";
+
+ for (i = 1; i < n; i++) {
+ int r;
+
+ r = bus_method_call_with_reply (
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "KillMachine",
+ NULL,
+ NULL,
+ DBUS_TYPE_STRING, &args[i],
+ DBUS_TYPE_STRING, &arg_kill_who,
+ DBUS_TYPE_INT32, &arg_signal,
+ DBUS_TYPE_INVALID);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
unsigned i;
dbus_bool_t b, interactive = true;
@@ -1288,6 +1567,31 @@ static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
return 0;
}
+static int terminate_machine(DBusConnection *bus, char **args, unsigned n) {
+ unsigned i;
+
+ assert(args);
+
+ for (i = 1; i < n; i++) {
+ int r;
+
+ r = bus_method_call_with_reply (
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "TerminateMachine",
+ NULL,
+ NULL,
+ DBUS_TYPE_STRING, &args[i],
+ DBUS_TYPE_INVALID);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
static int help(void) {
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
@@ -1326,7 +1630,12 @@ static int help(void) {
" show-seat [NAME...] Show properties of one or more seats\n"
" attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
" flush-devices Flush all device associations\n"
- " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
+ " terminate-seat [NAME...] Terminate all sessions on one or more seats\n"
+ " list-machines List running VMs and containers\n"
+ " machine-status [NAME...] Show VM/container status\n"
+ " show-machine [NAME...] Show properties of one or more VMs/containers\n"
+ " terminate-machine [NAME...] Terminate one or more VMs/containers\n"
+ " kill-machine [NAME...] Send signal to processes of a VM/container\n",
program_invocation_short_name);
return 0;
@@ -1452,29 +1761,34 @@ static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
const int argc;
int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
} verbs[] = {
- { "list-sessions", LESS, 1, list_sessions },
- { "session-status", MORE, 2, show },
- { "show-session", MORE, 1, show },
- { "activate", EQUAL, 2, activate },
- { "lock-session", MORE, 2, activate },
- { "unlock-session", MORE, 2, activate },
- { "lock-sessions", EQUAL, 1, lock_sessions },
- { "unlock-sessions", EQUAL, 1, lock_sessions },
- { "terminate-session", MORE, 2, activate },
- { "kill-session", MORE, 2, kill_session },
- { "list-users", EQUAL, 1, list_users },
- { "user-status", MORE, 2, show },
- { "show-user", MORE, 1, show },
- { "enable-linger", MORE, 2, enable_linger },
- { "disable-linger", MORE, 2, enable_linger },
- { "terminate-user", MORE, 2, terminate_user },
- { "kill-user", MORE, 2, kill_user },
- { "list-seats", EQUAL, 1, list_seats },
- { "seat-status", MORE, 2, show },
- { "show-seat", MORE, 1, show },
- { "attach", MORE, 3, attach },
- { "flush-devices", EQUAL, 1, flush_devices },
- { "terminate-seat", MORE, 2, terminate_seat },
+ { "list-sessions", LESS, 1, list_sessions },
+ { "session-status", MORE, 2, show },
+ { "show-session", MORE, 1, show },
+ { "activate", EQUAL, 2, activate },
+ { "lock-session", MORE, 2, activate },
+ { "unlock-session", MORE, 2, activate },
+ { "lock-sessions", EQUAL, 1, lock_sessions },
+ { "unlock-sessions", EQUAL, 1, lock_sessions },
+ { "terminate-session", MORE, 2, activate },
+ { "kill-session", MORE, 2, kill_session },
+ { "list-users", EQUAL, 1, list_users },
+ { "user-status", MORE, 2, show },
+ { "show-user", MORE, 1, show },
+ { "enable-linger", MORE, 2, enable_linger },
+ { "disable-linger", MORE, 2, enable_linger },
+ { "terminate-user", MORE, 2, terminate_user },
+ { "kill-user", MORE, 2, kill_user },
+ { "list-seats", EQUAL, 1, list_seats },
+ { "seat-status", MORE, 2, show },
+ { "show-seat", MORE, 1, show },
+ { "attach", MORE, 3, attach },
+ { "flush-devices", EQUAL, 1, flush_devices },
+ { "terminate-seat", MORE, 2, terminate_seat },
+ { "list-machines", EQUAL, 1, list_machines },
+ { "machine-status", MORE, 2, show },
+ { "show-machine", MORE, 1, show },
+ { "terminate-machine", MORE, 2, terminate_machine },
+ { "kill-machine", MORE, 2, kill_machine },
};
int left;