#include #include #include #include #include #include #include #include #include #include #include "session.h" #include "otd.h" int take_device(struct otd *restrict otd, const char *restrict path, bool *restrict paused_out) { int ret; int fd = -1; sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; struct otd_session *s = &otd->session; struct stat st; if (stat(path, &st) < 0) { fprintf(stderr, "Failed to stat '%s'\n", path); return -1; } ret = sd_bus_call_method(s->bus, "org.freedesktop.login1", s->path, "org.freedesktop.login1.Session", "TakeDevice", &error, &msg, "uu", major(st.st_rdev), minor(st.st_rdev)); if (ret < 0) { fprintf(stderr, "%s\n", error.message); goto error; } int paused = 0; ret = sd_bus_message_read(msg, "hb", &fd, &paused); if (ret < 0) { fprintf(stderr, "%s\n", strerror(-ret)); goto error; } // The original fd seem to be closed when the message is freed // so we just clone it. fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (paused_out) *paused_out = paused; error: sd_bus_error_free(&error); sd_bus_message_unref(msg); return fd; } void release_device(struct otd *otd, int fd) { int ret; sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; struct otd_session *s = &otd->session; struct stat st; if (fstat(fd, &st) < 0) { fprintf(stderr, "Could not stat fd %d\n", fd); return; } ret = sd_bus_call_method(s->bus, "org.freedesktop.login1", s->path, "org.freedesktop.login1.Session", "ReleaseDevice", &error, &msg, "uu", major(st.st_rdev), minor(st.st_rdev)); if (ret < 0) { /* Log something */; } sd_bus_error_free(&error); sd_bus_message_unref(msg); } static bool session_activate(struct otd *otd) { int ret; sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; struct otd_session *s = &otd->session; ret = sd_bus_call_method(s->bus, "org.freedesktop.login1", s->path, "org.freedesktop.login1.Session", "Activate", &error, &msg, ""); if (ret < 0) { fprintf(stderr, "%s\n", error.message); } sd_bus_error_free(&error); sd_bus_message_unref(msg); return ret >= 0; } static bool take_control(struct otd *otd) { int ret; sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; struct otd_session *s = &otd->session; ret = sd_bus_call_method(s->bus, "org.freedesktop.login1", s->path, "org.freedesktop.login1.Session", "TakeControl", &error, &msg, "b", false); if (ret < 0) { /* Log something */; } sd_bus_error_free(&error); sd_bus_message_unref(msg); return ret >= 0; } static void release_control(struct otd *otd) { int ret; sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; struct otd_session *s = &otd->session; ret = sd_bus_call_method(s->bus, "org.freedesktop.login1", s->path, "org.freedesktop.login1.Session", "ReleaseControl", &error, &msg, ""); if (ret < 0) { /* Log something */; } sd_bus_error_free(&error); sd_bus_message_unref(msg); } void otd_close_session(struct otd *otd) { release_device(otd, otd->fd); release_control(otd); sd_bus_unref(otd->session.bus); free(otd->session.id); free(otd->session.path); free(otd->session.seat); } bool otd_new_session(struct otd *otd) { int ret; struct otd_session *s = &otd->session; ret = sd_pid_get_session(getpid(), &s->id); if (ret < 0) { fprintf(stderr, "Could not get session\n"); goto error; } ret = sd_session_get_seat(s->id, &s->seat); if (ret < 0) { fprintf(stderr, "Could not get seat\n"); goto error; } // This could be done using asprintf, but I don't want to define _GNU_SOURCE const char *fmt = "/org/freedesktop/login1/session/%s"; int len = snprintf(NULL, 0, fmt, s->id); s->path = malloc(len + 1); if (!s->path) goto error; sprintf(s->path, fmt, s->id); ret = sd_bus_open_system(&s->bus); if (ret < 0) { fprintf(stderr, "Could not open bus\n"); goto error; } if (!session_activate(otd)) { fprintf(stderr, "Could not activate session\n"); goto error_bus; } if (!take_control(otd)) { fprintf(stderr, "Could not take control of session\n"); goto error_bus; } return true; error_bus: sd_bus_unref(s->bus); error: free(s->path); free(s->id); free(s->seat); return false; }