#include "dbus-common.h"
#include "strv.h"
#include "polkit.h"
+#include "special.h"
#define BUS_MANAGER_INTERFACE \
" <interface name=\"org.freedesktop.login1.Manager\">\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 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\"/>\n" \
+ " <arg name=\"signal\" type=\"s\"/>\n" \
+ " </method>\n" \
+ " <method name=\"KillUser\">\n" \
+ " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
+ " <arg name=\"signal\" type=\"s\"/>\n" \
+ " </method>\n" \
" <method name=\"TerminateSession\">\n" \
" <arg name=\"id\" type=\"s\" 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" \
session = hashmap_get(m->sessions, id);
if (session) {
+ free(id);
fifo_fd = session_create_fifo(session);
if (fifo_fd < 0) {
goto fail;
}
+ seat = session->seat ? session->seat->id : "";
+ vtnr = session->vtnr;
b = dbus_message_append_args(
reply,
DBUS_TYPE_STRING, &session->id,
DBUS_TYPE_OBJECT_PATH, &p,
DBUS_TYPE_STRING, &session->user->runtime_path,
DBUS_TYPE_UNIX_FD, &fifo_fd,
+ DBUS_TYPE_STRING, &seat,
+ DBUS_TYPE_UINT32, &vtnr,
DBUS_TYPE_INVALID);
free(p);
close_nointr_nofail(fifo_fd);
*_reply = reply;
+ strv_free(controllers);
+ strv_free(reset_controllers);
+
return 0;
}
goto fail;
}
+ seat = s ? s->id : "";
b = dbus_message_append_args(
reply,
DBUS_TYPE_STRING, &session->id,
DBUS_TYPE_OBJECT_PATH, &p,
DBUS_TYPE_STRING, &session->user->runtime_path,
DBUS_TYPE_UNIX_FD, &fifo_fd,
+ DBUS_TYPE_STRING, &seat,
+ DBUS_TYPE_UINT32, &vtnr,
DBUS_TYPE_INVALID);
free(p);
return r;
}
-static bool device_has_tag(struct udev_device *d, const char *tag) {
- struct udev_list_entry *first, *item;
-
- assert(d);
- assert(tag);
-
- /* FIXME */
- udev_device_get_is_initialized(d);
-
- first = udev_device_get_tags_list_entry(d);
- udev_list_entry_foreach(item, first)
- if (streq(udev_list_entry_get_name(item), tag))
- return true;
-
- return false;
-}
-
-static int trigger_device(Manager *m, const char *prefix) {
+static int trigger_device(Manager *m, struct udev_device *d) {
struct udev_enumerate *e;
struct udev_list_entry *first, *item;
int r;
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;
p = udev_list_entry_get_name(item);
- if (prefix && !path_startswith(p, prefix))
- continue;
-
t = strappend(p, "/uevent");
if (!t) {
r = -ENOMEM;
if (!d)
return -ENODEV;
- if (!device_has_tag(d, "seat")) {
+ if (!udev_device_has_tag(d, "seat")) {
r = -ENODEV;
goto finish;
}
if (r < 0)
goto finish;
- r = trigger_device(m, sysfs);
+ r = trigger_device(m, d);
finish:
free(rule);
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) {
r = bus_manager_create_session(m, message, &reply);
- if (r == -ENOMEM)
- goto oom;
+
+ /* 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)
return bus_send_error_reply(connection, message, &error, r);
if (!reply)
goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillSession")) {
+ const char *swho;
+ int32_t signo;
+ KillWho who;
+ const char *name;
+ Session *session;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &swho,
+ DBUS_TYPE_INT32, &signo,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if (isempty(swho))
+ who = KILL_ALL;
+ else {
+ who = kill_who_from_string(swho);
+ if (who < 0)
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+ }
+
+ if (signo <= 0 || signo >= _NSIG)
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ session = hashmap_get(m->sessions, name);
+ if (!session)
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+
+ r = session_kill(session, who, signo);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillUser")) {
+ uint32_t uid;
+ User *user;
+ int32_t signo;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_UINT32, &uid,
+ DBUS_TYPE_INT32, &signo,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if (signo <= 0 || signo >= _NSIG)
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
+ if (!user)
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+
+ r = user_kill(user, signo);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSession")) {
const char *name;
Session *session;
if (!reply)
goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "PowerOff") ||
+ dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Reboot")) {
+ dbus_bool_t interactive;
+ bool multiple_sessions;
+ DBusMessage *forward, *freply;
+ const char *name;
+ const char *mode = "replace";
+ const char *action;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_BOOLEAN, &interactive,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ multiple_sessions = hashmap_size(m->sessions) > 1;
+
+ 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;
+
+ ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
+ if (ul == (unsigned long) -1)
+ return bus_send_error_reply(connection, message, &error, -EIO);
+
+ multiple_sessions = s->user->uid != ul;
+ }
+ }
+
+ if (streq(dbus_message_get_member(message), "PowerOff")) {
+ if (multiple_sessions)
+ action = "org.freedesktop.login1.power-off-multiple-sessions";
+ else
+ action = "org.freedesktop.login1.power-off";
+
+ name = SPECIAL_POWEROFF_TARGET;
+ } else {
+ if (multiple_sessions)
+ action = "org.freedesktop.login1.reboot-multiple-sessions";
+ else
+ action = "org.freedesktop.login1.reboot";
+
+ name = SPECIAL_REBOOT_TARGET;
+ }
+
+ r = verify_polkit(connection, message, action, interactive, &error);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ forward = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StartUnit");
+ if (!forward)
+ return bus_send_error_reply(connection, message, NULL, -ENOMEM);
+
+ if (!dbus_message_append_args(forward,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &mode,
+ DBUS_TYPE_INVALID)) {
+ dbus_message_unref(forward);
+ return bus_send_error_reply(connection, message, NULL, -ENOMEM);
+ }
+
+ freply = dbus_connection_send_with_reply_and_block(connection, forward, -1, &error);
+ dbus_message_unref(forward);
+
+ if (!freply)
+ return bus_send_error_reply(connection, message, &error, -EIO);
+
+ dbus_message_unref(freply);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
} else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
char *introspection = NULL;
FILE *f;