logind-dbus.c revision cae5846b2cbb5091267f59f4c7f941ce767a1f8f
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2011 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 <errno.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include "logind.h"
#include "dbus-common.h"
#include "strv.h"
#include "polkit.h"
#include "special.h"
#define BUS_MANAGER_INTERFACE \
" <interface name=\"org.freedesktop.login1.Manager\">\n" \
" <method name=\"GetSession\">\n" \
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"session\" type=\"o\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"GetSessionByPID\">\n" \
" <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"session\" type=\"o\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"GetUser\">\n" \
" <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"user\" type=\"o\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"GetSeat\">\n" \
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"seat\" type=\"o\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"ListSessions\">\n" \
" <arg name=\"sessions\" type=\"a(susso)\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"ListUsers\">\n" \
" <arg name=\"users\" type=\"a(uso)\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"ListSeats\">\n" \
" <arg name=\"seats\" type=\"a(so)\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"CreateSession\">\n" \
" <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"sevice\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"type\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"vtnr\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"tty\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"display\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"remote\" type=\"b\" direction=\"in\"/>\n" \
" <arg name=\"remote_user\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"remote_host\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"controllers\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"reset_controllers\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"kill_processes\" type=\"b\" direction=\"in\"/>\n" \
" <arg name=\"id\" type=\"s\" direction=\"out\"/>\n" \
" <arg name=\"path\" type=\"o\" direction=\"out\"/>\n" \
" <arg name=\"runtime_path\" type=\"o\" direction=\"out\"/>\n" \
" <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n" \
" <arg name=\"seat\" type=\"s\" direction=\"out\"/>\n" \
" <arg name=\"vtnr\" type=\"u\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"ActivateSession\">\n" \
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"ActivateSessionOnSeat\">\n" \
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"LockSession\">\n" \
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"UnlockSession\">\n" \
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"KillSession\">\n" \
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"KillUser\">\n" \
" <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"TerminateSession\">\n" \
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"TerminateUser\">\n" \
" <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"TerminateSeat\">\n" \
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetUserLinger\">\n" \
" <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"b\" type=\"b\" direction=\"in\"/>\n" \
" <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"AttachDevice\">\n" \
" <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"sysfs\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"FlushDevices\">\n" \
" <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"PowerOff\">\n" \
" <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"Reboot\">\n" \
" <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
" <signal name=\"SessionNew\">\n" \
" <arg name=\"id\" type=\"s\"/>\n" \
" <arg name=\"path\" type=\"o\"/>\n" \
" </signal>\n" \
" <signal name=\"SessionRemoved\">\n" \
" <arg name=\"id\" type=\"s\"/>\n" \
" <arg name=\"path\" type=\"o\"/>\n" \
" </signal>\n" \
" <signal name=\"UserNew\">\n" \
" <arg name=\"uid\" type=\"u\"/>\n" \
" <arg name=\"path\" type=\"o\"/>\n" \
" </signal>\n" \
" <signal name=\"UserRemoved\">\n" \
" <arg name=\"uid\" type=\"u\"/>\n" \
" <arg name=\"path\" type=\"o\"/>\n" \
" </signal>\n" \
" <signal name=\"SeatNew\">\n" \
" <arg name=\"id\" type=\"s\"/>\n" \
" <arg name=\"path\" type=\"o\"/>\n" \
" </signal>\n" \
" <signal name=\"SeatRemoved\">\n" \
" <arg name=\"id\" type=\"s\"/>\n" \
" <arg name=\"path\" type=\"o\"/>\n" \
" </signal>\n" \
" <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"NAutoVTs\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"KillOnlyUsers\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"KillExcludeUsers\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"KillUserProcesses\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
" </interface>\n"
#define INTROSPECTION_BEGIN \
"<node>\n" \
#define INTROSPECTION_END \
"</node>\n"
#define INTERFACES_LIST \
"org.freedesktop.login1.Manager\0"
dbus_bool_t b;
assert(i);
assert(m);
b = manager_get_idle_hint(m, NULL) > 0;
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
return -ENOMEM;
return 0;
}
static int bus_manager_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
uint64_t u;
assert(i);
assert(m);
manager_get_idle_hint(m, &t);
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
return -ENOMEM;
return 0;
}
SessionType t;
Seat *s;
int r;
int fifo_fd = -1;
bool b;
assert(m);
return -EINVAL;
if (!dbus_message_iter_next(&iter) ||
return -EINVAL;
if (leader <= 0 ||
!dbus_message_iter_next(&iter) ||
return -EINVAL;
if (!dbus_message_iter_next(&iter) ||
return -EINVAL;
t = session_type_from_string(type);
if (t < 0 ||
!dbus_message_iter_next(&iter) ||
return -EINVAL;
s = NULL;
else {
if (!s)
return -ENOENT;
}
if (!dbus_message_iter_next(&iter) ||
return -EINVAL;
if (!dbus_message_iter_next(&iter) ||
return -EINVAL;
int v;
if (!s)
s = m->vtconsole;
else if (s != m->vtconsole)
return -EINVAL;
v = vtnr_from_tty(tty);
if (v <= 0)
return v < 0 ? v : -EINVAL;
if (vtnr <= 0)
return -EINVAL;
return -EINVAL;
if (s) {
if (seat_can_multi_session(s)) {
return -EINVAL;
} else {
if (vtnr > 0)
return -EINVAL;
}
}
if (!dbus_message_iter_next(&iter) ||
return -EINVAL;
if (!dbus_message_iter_next(&iter) ||
return -EINVAL;
if (!dbus_message_iter_next(&iter) ||
return -EINVAL;
if (!dbus_message_iter_next(&iter) ||
return -EINVAL;
if (!dbus_message_iter_next(&iter) ||
return -EINVAL;
if (r < 0)
return -EINVAL;
!dbus_message_iter_next(&iter) ||
r = -EINVAL;
goto fail;
}
if (r < 0)
goto fail;
!dbus_message_iter_next(&iter) ||
r = -EINVAL;
goto fail;
}
if (r < 0)
goto fail;
if (audit_id > 0) {
if (!id) {
r = -ENOMEM;
goto fail;
}
if (session) {
if (fifo_fd < 0) {
r = fifo_fd;
goto fail;
}
/* Session already exists, client is probably
* something like "su" which changes uid but
* is still the same audit session */
if (!reply) {
r = -ENOMEM;
goto fail;
}
p = session_bus_path(session);
if (!p) {
r = -ENOMEM;
goto fail;
}
free(p);
if (!b) {
r = -ENOMEM;
goto fail;
}
return 0;
}
} else {
do {
if (!id) {
r = -ENOMEM;
goto fail;
}
}
if (r < 0)
goto fail;
r = -ENOMEM;
goto fail;
}
}
r = -ENOMEM;
goto fail;
}
}
if (!isempty(remote_user)) {
if (!session->remote_user) {
r = -ENOMEM;
goto fail;
}
}
if (!isempty(remote_host)) {
if (!session->remote_host) {
r = -ENOMEM;
goto fail;
}
}
r = -ENOMEM;
goto fail;
}
}
if (fifo_fd < 0) {
r = fifo_fd;
goto fail;
}
if (s) {
r = seat_attach_session(s, session);
if (r < 0)
goto fail;
}
r = session_start(session);
if (r < 0)
goto fail;
if (!reply) {
r = -ENOMEM;
goto fail;
}
p = session_bus_path(session);
if (!p) {
r = -ENOMEM;
goto fail;
}
free(p);
if (!b) {
r = -ENOMEM;
goto fail;
}
return 0;
fail:
if (session)
if (user)
if (fifo_fd >= 0)
if (reply)
return r;
}
struct udev_enumerate *e;
int r;
assert(m);
e = udev_enumerate_new(m->udev);
if (!e) {
r = -ENOMEM;
goto finish;
}
if (d) {
if (udev_enumerate_add_match_parent(e, d) < 0) {
r = -EIO;
goto finish;
}
}
if (udev_enumerate_scan_devices(e) < 0) {
r = -EIO;
goto finish;
}
char *t;
const char *p;
p = udev_list_entry_get_name(item);
t = strappend(p, "/uevent");
if (!t) {
r = -ENOMEM;
goto finish;
}
write_one_line_file(t, "change");
free(t);
}
r = 0;
if (e)
return r;
}
struct udev_device *d;
const char *id_for_seat;
int r;
assert(m);
if (!d)
return -ENODEV;
if (!udev_device_has_tag(d, "seat")) {
r = -ENODEV;
goto finish;
}
if (!id_for_seat) {
r = -ENODEV;
goto finish;
}
r = -ENOMEM;
goto finish;
}
if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0) {
r = -ENOMEM;
goto finish;
}
if (r < 0)
goto finish;
r = trigger_device(m, d);
if (d)
return r;
}
static int flush_devices(Manager *m) {
DIR *d;
assert(m);
if (!d) {
log_warning("Failed to open /etc/udev/rules.d: %m");
} else {
if (!dirent_is_file(de))
continue;
continue;
continue;
}
closedir(d);
}
return trigger_device(m, NULL);
}
static const BusProperty bus_login_manager_properties[] = {
{ "KillExcludeUsers", bus_property_append_strv, "as", offsetof(Manager, kill_exclude_users), true },
{ NULL, }
};
void *userdata) {
int r;
assert(m);
const char *name;
char *p;
bool b;
if (!dbus_message_get_args(
&error,
if (!session)
if (!reply)
goto oom;
p = session_bus_path(session);
if (!p)
goto oom;
free(p);
if (!b)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSessionByPID")) {
char *p;
bool b;
if (!dbus_message_get_args(
&error,
if (r <= 0)
if (!reply)
goto oom;
p = session_bus_path(session);
if (!p)
goto oom;
free(p);
if (!b)
goto oom;
char *p;
bool b;
if (!dbus_message_get_args(
&error,
if (!user)
if (!reply)
goto oom;
p = user_bus_path(user);
if (!p)
goto oom;
free(p);
if (!b)
goto oom;
const char *name;
char *p;
bool b;
if (!dbus_message_get_args(
&error,
if (!seat)
if (!reply)
goto oom;
p = seat_bus_path(seat);
if (!p)
goto oom;
free(p);
if (!b)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSessions")) {
char *p;
Iterator i;
const char *empty = "";
if (!reply)
goto oom;
goto oom;
goto oom;
p = session_bus_path(session);
if (!p)
goto oom;
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, session->seat ? (const char**) &session->seat->id : &empty) ||
free(p);
goto oom;
}
free(p);
goto oom;
}
goto oom;
char *p;
Iterator i;
if (!reply)
goto oom;
goto oom;
goto oom;
p = user_bus_path(user);
if (!p)
goto oom;
free(p);
goto oom;
}
free(p);
goto oom;
}
goto oom;
char *p;
Iterator i;
if (!reply)
goto oom;
goto oom;
goto oom;
p = seat_bus_path(seat);
if (!p)
goto oom;
free(p);
goto oom;
}
free(p);
goto oom;
}
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) {
/* Don't delay the work on OOM here, since it might be
* triggered by a low RLIMIT_NOFILE here (since we
* send a dupped fd to the client), and we'd rather
* see this fail quickly then be retried later */
if (r < 0)
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSession")) {
const char *name;
if (!dbus_message_get_args(
&error,
if (!session)
r = session_activate(session);
if (r < 0)
if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSessionOnSeat")) {
const char *session_name, *seat_name;
/* Same as ActivateSession() but refuses to work if
* the seat doesn't match */
if (!dbus_message_get_args(
&error,
if (!session)
if (!seat)
r = session_activate(session);
if (r < 0)
if (!reply)
goto oom;
const char *name;
if (!dbus_message_get_args(
&error,
if (!session)
goto oom;
if (!reply)
goto oom;
const char *swho;
const char *name;
if (!dbus_message_get_args(
&error,
else {
if (who < 0)
}
if (!session)
if (r < 0)
if (!reply)
goto oom;
if (!dbus_message_get_args(
&error,
if (!user)
if (r < 0)
if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSession")) {
const char *name;
if (!dbus_message_get_args(
&error,
if (!session)
r = session_stop(session);
if (r < 0)
if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateUser")) {
if (!dbus_message_get_args(
&error,
if (!user)
if (r < 0)
if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSeat")) {
const char *name;
if (!dbus_message_get_args(
&error,
if (!seat)
r = seat_stop_sessions(seat);
if (r < 0)
if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "SetUserLinger")) {
char *path;
if (!dbus_message_get_args(
&error,
DBUS_TYPE_BOOLEAN, &b,
errno = 0;
if (!pw)
r = verify_polkit(connection, message, "org.freedesktop.login1.set-user-linger", interactive, &error);
if (r < 0)
if (r < 0)
if (!path)
goto oom;
if (b) {
User *u;
if (r < 0)
if (manager_add_user_by_uid(m, uid, &u) >= 0)
user_start(u);
} else {
User *u;
if (u)
}
if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "AttachDevice")) {
if (!dbus_message_get_args(
&error,
r = verify_polkit(connection, message, "org.freedesktop.login1.attach-device", interactive, &error);
if (r < 0)
if (r < 0)
if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "FlushDevices")) {
if (!dbus_message_get_args(
&error,
r = verify_polkit(connection, message, "org.freedesktop.login1.flush-devices", interactive, &error);
if (r < 0)
r = flush_devices(m);
if (r < 0)
if (!reply)
goto oom;
bool multiple_sessions;
const char *name;
const char *mode = "replace";
const char *action;
if (!dbus_message_get_args(
&error,
if (!multiple_sessions) {
Session *s;
/* Hmm, there's only one session, but let's
* make sure it actually belongs to the user
* who is asking. If not, better be safe than
* sorry. */
s = hashmap_first(m->sessions);
if (s) {
unsigned long ul;
if (ul == (unsigned long) -1)
}
}
if (multiple_sessions)
action = "org.freedesktop.login1.power-off-multiple-sessions";
else
action = "org.freedesktop.login1.power-off";
} else {
if (multiple_sessions)
action = "org.freedesktop.login1.reboot-multiple-sessions";
else
action = "org.freedesktop.login1.reboot";
}
if (r < 0)
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartUnit");
if (!forward)
}
if (!freply)
if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
char *introspection = NULL;
FILE *f;
Iterator i;
char *p;
goto oom;
/* We roll our own introspection code here, instead of
* relying on bus_default_message_handler() because we
* need to generate our introspection string
* dynamically. */
goto oom;
fputs(INTROSPECTION_BEGIN, f);
if (p) {
fprintf(f, "<node name=\"seat/%s\"/>", p);
free(p);
}
}
if (p) {
fprintf(f, "<node name=\"session/%s\"/>", p);
free(p);
}
}
fputs(INTROSPECTION_END, f);
if (ferror(f)) {
fclose(f);
goto oom;
}
fclose(f);
if (!introspection)
goto oom;
goto oom;
}
} else {
const BusBoundProperties bps[] = {
{ "org.freedesktop.login1.Manager", bus_login_manager_properties, m },
{ NULL, }
};
}
if (reply) {
goto oom;
}
return DBUS_HANDLER_RESULT_HANDLED;
oom:
if (reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
const DBusObjectPathVTable bus_manager_vtable = {
};
void *userdata) {
assert(m);
const char *cgroup;
else
}
}
DBusMessage *m;
int r = -ENOMEM;
m = bus_properties_changed_new("/org/freedesktop/login1", "org.freedesktop.login1.Manager", properties);
if (!m)
goto finish;
goto finish;
r = 0;
if (m)
return r;
}