You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
225 lines
4.4 KiB
225 lines
4.4 KiB
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <systemd/sd-bus.h>
|
|
#include <systemd/sd-login.h>
|
|
#include <unistd.h>
|
|
#include <sys/sysmacros.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#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;
|
|
}
|