diff --git a/man/org.freedesktop.login1.xml b/man/org.freedesktop.login1.xml index 6781e1dcaae..034fd1078fb 100644 --- a/man/org.freedesktop.login1.xml +++ b/man/org.freedesktop.login1.xml @@ -1083,6 +1083,7 @@ node /org/freedesktop/login1/session/1 { ReleaseControl(); SetType(in s type); SetDisplay(in s display); + SetTTY(in h tty_fd); TakeDevice(in u major, in u minor, out h fd, @@ -1182,6 +1183,8 @@ node /org/freedesktop/login1/session/1 { + + @@ -1283,6 +1286,11 @@ node /org/freedesktop/login1/session/1 { controller. If TakeControl() has not been called, this method will fail. The only argument display is the new display name. + SetTTY() allows the device name of the session to be changed. This is + useful if the tty device is only known after authentication. It can only be called by session's + current controller. If TakeControl() has not been called, this method will fail. + The only argument tty_fd is a file handle to the new tty device. + TakeDevice() allows a session controller to get a file descriptor for a specific device. Pass in the major and minor numbers of the character device and systemd-logind will return a file descriptor for the device. Only a limited set of diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index e3bebc9188b..00a0e8fd589 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -23,6 +23,7 @@ #include "path-util.h" #include "signal-util.h" #include "strv.h" +#include "terminal-util.h" #include "user-util.h" static int property_get_user( @@ -421,6 +422,41 @@ static int method_set_display(sd_bus_message *message, void *userdata, sd_bus_er return sd_bus_reply_method_return(message, NULL); } +static int method_set_tty(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = ASSERT_PTR(userdata); + int fd, r, flags; + _cleanup_free_ char *q = NULL; + + assert(message); + + r = sd_bus_message_read(message, "h", &fd); + if (r < 0) + return r; + + if (!session_is_controller(s, sd_bus_message_get_sender(message))) + return sd_bus_error_set(error, BUS_ERROR_NOT_IN_CONTROL, "You must be in control of this session to set tty"); + + assert(fd >= 0); + + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) + return -errno; + if ((flags & O_ACCMODE) != O_RDWR) + return -EACCES; + if (FLAGS_SET(flags, O_PATH)) + return -ENOTTY; + + r = getttyname_malloc(fd, &q); + if (r < 0) + return r; + + r = session_set_tty(s, q); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + static int method_take_device(sd_bus_message *message, void *userdata, sd_bus_error *error) { Session *s = ASSERT_PTR(userdata); uint32_t major, minor; @@ -909,6 +945,11 @@ static const sd_bus_vtable session_vtable[] = { SD_BUS_NO_RESULT, method_set_display, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetTTY", + SD_BUS_ARGS("h", tty_fd), + SD_BUS_NO_RESULT, + method_set_tty, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD_WITH_ARGS("TakeDevice", SD_BUS_ARGS("u", major, "u", minor), SD_BUS_RESULT("h", fd, "b", inactive), diff --git a/src/login/logind-session.c b/src/login/logind-session.c index e7c917cdeef..92d588b2764 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -1132,6 +1132,23 @@ int session_set_display(Session *s, const char *display) { return 1; } +int session_set_tty(Session *s, const char *tty) { + int r; + + assert(s); + assert(tty); + + r = free_and_strdup(&s->tty, tty); + if (r <= 0) /* 0 means the strings were equal */ + return r; + + session_save(s); + + session_send_changed(s, "TTY", NULL); + + return 1; +} + static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) { Session *s = ASSERT_PTR(userdata); @@ -1349,6 +1366,9 @@ error: static void session_restore_vt(Session *s) { int r; + if (s->vtfd < 0) + return; + r = vt_restore(s->vtfd); if (r == -EIO) { int vt, old_fd; diff --git a/src/login/logind-session.h b/src/login/logind-session.h index 4c286079866..bf45301705a 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -140,6 +140,7 @@ int session_get_locked_hint(Session *s); void session_set_locked_hint(Session *s, bool b); void session_set_type(Session *s, SessionType t); int session_set_display(Session *s, const char *display); +int session_set_tty(Session *s, const char *tty); int session_create_fifo(Session *s); int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error); int session_stop(Session *s, bool force); diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf index bf905e3b0f9..8ba094bcff1 100644 --- a/src/login/org.freedesktop.login1.conf +++ b/src/login/org.freedesktop.login1.conf @@ -350,6 +350,10 @@ send_interface="org.freedesktop.login1.Session" send_member="SetDisplay"/> + + diff --git a/src/login/test-session-properties.c b/src/login/test-session-properties.c index 44d54811c3e..0bfde422138 100644 --- a/src/login/test-session-properties.c +++ b/src/login/test-session-properties.c @@ -6,10 +6,16 @@ * ./test-session-properties /org/freedesktop/login1/session/_32 */ +#include +#include +#include + #include "alloc-util.h" #include "bus-common-errors.h" #include "bus-locator.h" +#include "path-util.h" #include "string-util.h" +#include "terminal-util.h" #include "tests.h" static BusLocator session; @@ -94,6 +100,32 @@ TEST(set_display) { assert_se(isempty(display)); } +/* Tests org.freedesktop.logind.Session SetTTY */ +TEST(set_tty) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus* bus = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *tty = NULL; + const char *path = "/dev/tty2"; /* testsuite uses tty2 */ + int fd; + + fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY); + assert_se(fd >= 0); + + assert_se(sd_bus_open_system(&bus) >= 0); + + /* tty can only be set by the session controller (which we're not ATM) */ + assert_se(bus_call_method(bus, &session, "SetTTY", &error, NULL, "h", fd) < 0); + assert_se(sd_bus_error_has_name(&error, BUS_ERROR_NOT_IN_CONTROL)); + + assert_se(bus_call_method(bus, &session, "TakeControl", NULL, NULL, "b", true) >= 0); + + /* tty can be set */ + assert_se(bus_call_method(bus, &session, "SetTTY", NULL, NULL, "h", fd) >= 0); + tty = mfree(tty); + assert_se(bus_get_property_string(bus, &session, "TTY", NULL, &tty) >= 0); + assert_se(streq(tty, "tty2")); +} + static int intro(void) { if (saved_argc <= 1) return EXIT_FAILURE;