loginctl.c revision 4bba9156da3e1df2cee24d10d7cd88c776ef4179
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <pwd.h>
#include "log.h"
#include "util.h"
#include "macro.h"
#include "pager.h"
#include "dbus-common.h"
#include "build.h"
#include "strv.h"
#include "cgroup-show.h"
#include "sysfs-show.h"
static char **arg_property = NULL;
static bool arg_all = false;
static bool arg_no_pager = false;
static const char *arg_kill_who = NULL;
static int arg_signal = SIGTERM;
static enum transport {
static bool on_tty(void) {
static int t = -1;
/* Note that this is invoked relatively early, before we start
* the pager. That means the value we return reflects whether
* we originally were started on a tty, not if we currently
* are. But this is intended, since we want colour and so on
* when run in our own pager. */
if (_unlikely_(t < 0))
t = isatty(STDOUT_FILENO) > 0;
return t;
}
static void pager_open_if_enabled(void) {
/* Cache result before we open the pager */
on_tty();
if (!arg_no_pager)
pager_open();
}
int r;
unsigned k = 0;
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"ListSessions");
if (!m) {
log_error("Could not allocate message.");
return -ENOMEM;
}
if (!reply) {
r = -EIO;
goto finish;
}
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
if (on_tty())
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
k++;
}
if (on_tty())
printf("\n%u sessions listed.\n", k);
r = 0;
if (m)
if (reply)
return r;
}
int r;
unsigned k = 0;
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"ListUsers");
if (!m) {
log_error("Could not allocate message.");
return -ENOMEM;
}
if (!reply) {
r = -EIO;
goto finish;
}
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
if (on_tty())
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
k++;
}
if (on_tty())
printf("\n%u users listed.\n", k);
r = 0;
if (m)
if (reply)
return r;
}
int r;
unsigned k = 0;
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"ListSeats");
if (!m) {
log_error("Could not allocate message.");
return -ENOMEM;
}
if (!reply) {
r = -EIO;
goto finish;
}
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
if (on_tty())
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
k++;
}
if (on_tty())
printf("\n%u seats listed.\n", k);
r = 0;
if (m)
if (reply)
return r;
}
typedef struct SessionStatusInfo {
const char *id;
const char *name;
const char *control_group;
int vtnr;
const char *seat;
const char *tty;
const char *display;
bool remote;
const char *remote_host;
const char *remote_user;
const char *service;
const char *type;
bool active;
typedef struct UserStatusInfo {
const char *name;
const char *control_group;
const char *state;
char **sessions;
const char *display;
typedef struct SeatStatusInfo {
const char *id;
const char *active_session;
char **sessions;
static void print_session_status_info(SessionStatusInfo *i) {
assert(i);
if (i->name)
else
if (s1)
else if (s2)
if (i->leader > 0) {
char *t = NULL;
get_process_comm(i->leader, &t);
if (t) {
printf(" (%s)", t);
free(t);
}
printf("\n");
}
if (i->seat) {
if (i->vtnr > 0)
printf("\n");
}
if (i->tty)
else if (i->display)
if (i->remote_host && i->remote_user)
else if (i->remote_host)
else if (i->remote_user)
else if (i->remote)
printf("\t Remote: Yes\n");
if (i->service) {
if (i->type)
printf("\n");
} else if (i->type)
if (i->control_group) {
unsigned c;
if (arg_transport != TRANSPORT_SSH) {
c = columns();
if (c > 18)
c -= 18;
else
c = 0;
}
}
}
static void print_user_status_info(UserStatusInfo *i) {
assert(i);
if (i->name)
else
if (s1)
else if (s2)
if (!strv_isempty(i->sessions)) {
char **l;
printf("\tSessions:");
STRV_FOREACH(l, i->sessions) {
printf(" *%s", *l);
else
printf(" %s", *l);
}
printf("\n");
}
if (i->control_group) {
unsigned c;
if (arg_transport != TRANSPORT_SSH) {
c = columns();
if (c > 18)
c -= 18;
else
c = 0;
}
}
}
static void print_seat_status_info(SeatStatusInfo *i) {
assert(i);
if (!strv_isempty(i->sessions)) {
char **l;
printf("\tSessions:");
STRV_FOREACH(l, i->sessions) {
if (streq_ptr(*l, i->active_session))
printf(" *%s", *l);
else
printf(" %s", *l);
}
printf("\n");
}
if (arg_transport != TRANSPORT_SSH) {
unsigned c;
c = columns();
if (c > 21)
c -= 21;
else
c = 0;
printf("\t Devices:\n");
}
}
assert(i);
switch (dbus_message_iter_get_arg_type(iter)) {
case DBUS_TYPE_STRING: {
const char *s;
if (!isempty(s)) {
i->id = s;
i->name = s;
i->control_group = s;
i->tty = s;
i->display = s;
i->remote_host = s;
i->remote_user = s;
i->service = s;
i->type = s;
}
break;
}
case DBUS_TYPE_UINT32: {
uint32_t u;
i->vtnr = (int) u;
break;
}
case DBUS_TYPE_BOOLEAN: {
dbus_bool_t b;
i->remote = b;
i->active = b;
break;
}
case DBUS_TYPE_UINT64: {
uint64_t u;
break;
}
case DBUS_TYPE_STRUCT: {
uint32_t u;
dbus_message_iter_get_basic(&sub, &u);
const char *s;
dbus_message_iter_get_basic(&sub, &s);
if (!isempty(s))
i->seat = s;
}
break;
}
}
return 0;
}
assert(i);
switch (dbus_message_iter_get_arg_type(iter)) {
case DBUS_TYPE_STRING: {
const char *s;
if (!isempty(s)) {
i->name = s;
i->control_group = s;
i->state = s;
}
break;
}
case DBUS_TYPE_UINT32: {
uint32_t u;
break;
}
case DBUS_TYPE_UINT64: {
uint64_t u;
break;
}
case DBUS_TYPE_STRUCT: {
const char *s;
dbus_message_iter_get_basic(&sub, &s);
if (!isempty(s))
i->display = s;
}
break;
}
case DBUS_TYPE_ARRAY: {
const char *id;
const char *path;
char **l;
if (!l)
return -ENOMEM;
i->sessions = l;
}
}
return 0;
}
}
}
return 0;
}
assert(i);
switch (dbus_message_iter_get_arg_type(iter)) {
case DBUS_TYPE_STRING: {
const char *s;
if (!isempty(s)) {
i->id = s;
}
break;
}
case DBUS_TYPE_STRUCT: {
const char *s;
dbus_message_iter_get_basic(&sub, &s);
if (!isempty(s))
i->active_session = s;
}
break;
}
case DBUS_TYPE_ARRAY: {
const char *id;
const char *path;
char **l;
if (!l)
return -ENOMEM;
i->sessions = l;
}
}
return 0;
}
}
}
return 0;
}
return 0;
switch (dbus_message_iter_get_arg_type(iter)) {
case DBUS_TYPE_STRUCT: {
const char *s;
dbus_message_iter_get_basic(&sub, &s);
return 0;
}
break;
}
case DBUS_TYPE_ARRAY:
bool found = false;
const char *id;
const char *path;
if (found)
else {
found = true;
}
}
}
else if (found)
printf("\n");
return 0;
}
break;
}
return 0;
if (arg_all)
return 0;
}
static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
const char *interface = "";
int r;
"org.freedesktop.login1",
path,
"org.freedesktop.DBus.Properties",
"GetAll");
if (!m) {
log_error("Could not allocate message.");
r = -ENOMEM;
goto finish;
}
if (!dbus_message_append_args(m,
log_error("Could not append arguments to message.");
r = -ENOMEM;
goto finish;
}
if (!reply) {
r = -EIO;
goto finish;
}
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
if (*new_line)
printf("\n");
*new_line = true;
const char *name;
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
if (show_properties)
else
if (r < 0) {
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
}
if (!show_properties) {
else
}
r = 0;
if (m)
if (reply)
return r;
}
int r, ret = 0;
unsigned i;
bool show_properties, new_line = false;
if (show_properties)
if (show_properties && n <= 1) {
/* If not argument is specified inspect the manager
* itself */
goto finish;
}
for (i = 1; i < n; i++) {
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"GetSession");
if (!m) {
log_error("Could not allocate message.");
goto finish;
}
if (!dbus_message_append_args(m,
DBUS_TYPE_STRING, &args[i],
log_error("Could not append arguments to message.");
goto finish;
}
uint32_t u;
if (ret < 0) {
goto finish;
}
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"GetUser");
if (!m) {
log_error("Could not allocate message.");
goto finish;
}
if (!dbus_message_append_args(m,
DBUS_TYPE_UINT32, &u,
log_error("Could not append arguments to message.");
goto finish;
}
} else {
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"GetSeat");
if (!m) {
log_error("Could not allocate message.");
goto finish;
}
if (!dbus_message_append_args(m,
DBUS_TYPE_STRING, &args[i],
log_error("Could not append arguments to message.");
goto finish;
}
}
if (!reply) {
goto finish;
}
goto finish;
}
if (r != 0)
ret = r;
}
if (m)
if (reply)
return ret;
}
DBusMessage *m = NULL;
int ret = 0;
unsigned i;
for (i = 1; i < n; i++) {
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"ActivateSession");
if (!m) {
log_error("Could not allocate message.");
goto finish;
}
if (!dbus_message_append_args(m,
DBUS_TYPE_STRING, &args[i],
log_error("Could not append arguments to message.");
goto finish;
}
if (!reply) {
goto finish;
}
}
if (m)
return ret;
}
DBusMessage *m = NULL;
int ret = 0;
unsigned i;
if (!arg_kill_who)
arg_kill_who = "all";
for (i = 1; i < n; i++) {
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"KillSession");
if (!m) {
log_error("Could not allocate message.");
goto finish;
}
if (!dbus_message_append_args(m,
DBUS_TYPE_STRING, &args[i],
log_error("Could not append arguments to message.");
goto finish;
}
if (!reply) {
goto finish;
}
}
if (m)
return ret;
}
DBusMessage *m = NULL;
int ret = 0;
unsigned i;
dbus_bool_t b, interactive = true;
for (i = 1; i < n; i++) {
uint32_t u;
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"SetUserLinger");
if (!m) {
log_error("Could not allocate message.");
goto finish;
}
if (ret < 0) {
goto finish;
}
if (!dbus_message_append_args(m,
DBUS_TYPE_UINT32, &u,
DBUS_TYPE_BOOLEAN, &b,
log_error("Could not append arguments to message.");
goto finish;
}
if (!reply) {
goto finish;
}
}
ret = 0;
if (m)
return ret;
}
DBusMessage *m = NULL;
int ret = 0;
unsigned i;
for (i = 1; i < n; i++) {
uint32_t u;
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"TerminateUser");
if (!m) {
log_error("Could not allocate message.");
goto finish;
}
if (ret < 0) {
goto finish;
}
if (!dbus_message_append_args(m,
DBUS_TYPE_UINT32, &u,
log_error("Could not append arguments to message.");
goto finish;
}
if (!reply) {
goto finish;
}
}
ret = 0;
if (m)
return ret;
}
DBusMessage *m = NULL;
int ret = 0;
unsigned i;
if (!arg_kill_who)
arg_kill_who = "all";
for (i = 1; i < n; i++) {
uint32_t u;
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"KillUser");
if (!m) {
log_error("Could not allocate message.");
goto finish;
}
if (ret < 0) {
goto finish;
}
if (!dbus_message_append_args(m,
DBUS_TYPE_UINT32, &u,
log_error("Could not append arguments to message.");
goto finish;
}
if (!reply) {
goto finish;
}
}
ret = 0;
if (m)
return ret;
}
DBusMessage *m = NULL;
int ret = 0;
unsigned i;
dbus_bool_t interactive = true;
for (i = 2; i < n; i++) {
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"AttachDevice");
if (!m) {
log_error("Could not allocate message.");
goto finish;
}
if (!dbus_message_append_args(m,
DBUS_TYPE_STRING, &args[i],
log_error("Could not append arguments to message.");
goto finish;
}
if (!reply) {
goto finish;
}
}
if (m)
return ret;
}
int ret = 0;
dbus_bool_t interactive = true;
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"FlushDevices");
if (!m) {
log_error("Could not allocate message.");
goto finish;
}
if (!dbus_message_append_args(m,
log_error("Could not append arguments to message.");
goto finish;
}
if (!reply) {
goto finish;
}
if (m)
if (reply)
return ret;
}
DBusMessage *m = NULL;
int ret = 0;
unsigned i;
for (i = 1; i < n; i++) {
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"TerminateSeat");
if (!m) {
log_error("Could not allocate message.");
goto finish;
}
if (!dbus_message_append_args(m,
DBUS_TYPE_STRING, &args[i],
log_error("Could not append arguments to message.");
goto finish;
}
if (!reply) {
goto finish;
}
}
if (m)
return ret;
}
static int help(void) {
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Send control commands to or query the login manager.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" -p --property=NAME Show only properties by this name\n"
" -a --all Show all properties, including empty ones\n"
" --kill-who=WHO Who to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n"
" -H --host=[USER@]HOST\n"
" Show information for remote host\n"
" -P --privileged Acquire privileges before execution\n"
" --no-pager Do not pipe output into a pager\n\n"
"Commands:\n"
" list-sessions List sessions\n"
" session-status [ID...] Show session status\n"
" show-session [ID...] Show properties of one or more sessions\n"
" activate [ID] Activate a session\n"
" lock-session [ID...] Screen lock one or more sessions\n"
" unlock-session [ID...] Screen unlock one or more sessions\n"
" terminate-session [ID...] Terminate one or more sessions\n"
" kill-session [ID...] Send signal to processes of a session\n"
" list-users List users\n"
" user-status [USER...] Show user status\n"
" show-user [USER...] Show properties of one or more users\n"
" enable-linger [USER...] Enable linger state of one or more users\n"
" disable-linger [USER...] Disable linger state of one or more users\n"
" terminate-user [USER...] Terminate all sessions of one or more users\n"
" kill-user [USER...] Send signal to processes of a user\n"
" list-seats List seats\n"
" seat-status [NAME...] Show seat status\n"
" 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",
return 0;
}
enum {
ARG_VERSION = 0x100,
};
};
int c;
switch (c) {
case 'h':
help();
return 0;
case ARG_VERSION:
return 0;
case 'p': {
char **l;
if (!l)
return -ENOMEM;
arg_property = l;
/* 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 ARG_NO_PAGER:
arg_no_pager = true;
break;
case ARG_KILL_WHO:
break;
case 's':
if (arg_signal < 0) {
return -EINVAL;
}
break;
case 'P':
break;
case 'H':
break;
case '?':
return -EINVAL;
default:
log_error("Unknown option code %c", c);
return -EINVAL;
}
}
return 1;
}
static const struct {
const char* verb;
const enum {
MORE,
LESS,
} argc_cmp;
const int argc;
} verbs[] = {
};
int left;
unsigned i;
if (left <= 0)
/* Special rule: no arguments means "list-sessions" */
i = 0;
else {
help();
return 0;
}
for (i = 0; i < ELEMENTSOF(verbs); i++)
break;
if (i >= ELEMENTSOF(verbs)) {
return -EINVAL;
}
}
case EQUAL:
log_error("Invalid number of arguments.");
return -EINVAL;
}
break;
case MORE:
log_error("Too few arguments.");
return -EINVAL;
}
break;
case LESS:
log_error("Too many arguments.");
return -EINVAL;
}
break;
default:
assert_not_reached("Unknown comparison operator.");
}
if (!bus) {
return -EIO;
}
}
int r, retval = EXIT_FAILURE;
log_open();
if (r < 0)
goto finish;
else if (r == 0) {
goto finish;
}
if (arg_transport == TRANSPORT_NORMAL)
else if (arg_transport == TRANSPORT_POLKIT)
else if (arg_transport == TRANSPORT_SSH)
else
assert_not_reached("Uh, invalid transport...");
retval = r < 0 ? EXIT_FAILURE : r;
if (bus) {
}
pager_close();
return retval;
}