uvtd: add new Virtual Terminal daemon
This introduces uvtd which replaces kmscon sessions as an external helper program. It's still a dummy program but it will get extended soon. After that, kmscon sessions will get removed and limited to a single seat. This will simplify kmscon itself heavily and move rarely used features out of kmscon into helpers. Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
This commit is contained in:
parent
9c2375b6d4
commit
176a0e8be5
1
.gitignore
vendored
1
.gitignore
vendored
@ -40,6 +40,7 @@ docs/reference/kmscon.????*
|
||||
docs/reference/*.stamp
|
||||
docs/reference/version.xml
|
||||
docs/reference/*/
|
||||
uvtd
|
||||
wlterm
|
||||
docs/man/*.1
|
||||
docs/man/*.3
|
||||
|
25
Makefile.am
25
Makefile.am
@ -1,6 +1,6 @@
|
||||
#
|
||||
# Kmscon - Global Makefile
|
||||
# Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
|
||||
# Copyright (c) 2012-2013 David Herrmann <dh.herrmann@googlemail.com>
|
||||
#
|
||||
|
||||
#
|
||||
@ -30,7 +30,7 @@ LIBUTERM_AGE = 0
|
||||
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
|
||||
AM_MAKEFLAGS = --no-print-directory
|
||||
AUTOMAKE_OPTIONS = color-tests
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = --enable-wlterm
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = --enable-wlterm --enable-uvtd
|
||||
|
||||
SUBDIRS = .
|
||||
|
||||
@ -687,6 +687,27 @@ wlterm_LDADD = \
|
||||
libshl.la \
|
||||
-lpthread
|
||||
|
||||
#
|
||||
# uvtd
|
||||
#
|
||||
|
||||
if BUILD_ENABLE_UVTD
|
||||
bin_PROGRAMS += uvtd
|
||||
endif
|
||||
|
||||
uvtd_SOURCES = \
|
||||
src/uvtd_main.c \
|
||||
src/uvtd_seat.h \
|
||||
src/uvtd_seat.c
|
||||
uvtd_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
$(XKBCOMMON_CFLAGS)
|
||||
uvtd_LDADD = \
|
||||
$(XKBCOMMON_LIBS) \
|
||||
libeloop.la \
|
||||
libshl.la \
|
||||
libuterm.la
|
||||
|
||||
#
|
||||
# Tests
|
||||
#
|
||||
|
49
configure.ac
49
configure.ac
@ -188,6 +188,16 @@ if test "x$enable_wlterm" = "x" ; then
|
||||
fi
|
||||
AC_MSG_RESULT([$enable_wlterm])
|
||||
|
||||
# uvtd
|
||||
AC_MSG_CHECKING([whether user wants uvtd])
|
||||
AC_ARG_ENABLE([uvtd],
|
||||
[AS_HELP_STRING([--enable-uvtd],
|
||||
[build uvtd])])
|
||||
if test "x$enable_uvtd" = "x" ; then
|
||||
enable_uvtd="no (default)"
|
||||
fi
|
||||
AC_MSG_RESULT([$enable_uvtd])
|
||||
|
||||
# debug
|
||||
AC_MSG_CHECKING([whether to build with debugging on])
|
||||
AC_ARG_ENABLE([debug],
|
||||
@ -803,6 +813,30 @@ else
|
||||
wlterm_missing="enable-wlterm"
|
||||
fi
|
||||
|
||||
# uvtd
|
||||
uvtd_avail=no
|
||||
uvtd_missing=""
|
||||
if test ! "x$enable_uvtd" = "xno" ; then
|
||||
uvtd_avail=yes
|
||||
if test "x$uvt_avail" = "xno" ; then
|
||||
uvtd_avail=no
|
||||
uvtd_missing="$uvt_missing,$uvtd_missing"
|
||||
fi
|
||||
|
||||
if test "x$eloop_avail" = "xno" ; then
|
||||
uvtd_avail=no
|
||||
uvtd_missing="$eloop_missing,$uvtd_missing"
|
||||
fi
|
||||
|
||||
if test "x$uvtd_avail" = "xno" ; then
|
||||
if test "x$enable_uvtd" = "xyes" ; then
|
||||
AC_ERROR([missing for uvtd: $uvtd_missing])
|
||||
fi
|
||||
fi
|
||||
else
|
||||
uvtd_missing="enable-uvtd"
|
||||
fi
|
||||
|
||||
#
|
||||
# Enable all required modules
|
||||
# We now know which modules can be built by checking the *_avail variables set
|
||||
@ -811,6 +845,16 @@ fi
|
||||
# needs them. This is done top-down of course.
|
||||
#
|
||||
|
||||
# uvtd
|
||||
uvtd_enabled=no
|
||||
if test "x$uvtd_avail" = "xyes" ; then
|
||||
if test "x${enable_uvtd% *}" = "xyes" ; then
|
||||
uvtd_enabled=yes
|
||||
enable_eloop=yes
|
||||
enable_uvt=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
# wlterm
|
||||
wlterm_enabled=no
|
||||
if test "x$wlterm_avail" = "xyes" ; then
|
||||
@ -1194,6 +1238,10 @@ AM_CONDITIONAL([BUILD_ENABLE_KMSCON],
|
||||
AM_CONDITIONAL([BUILD_ENABLE_WLTERM],
|
||||
[test "x$wlterm_enabled" = "xyes"])
|
||||
|
||||
# uvtd
|
||||
AM_CONDITIONAL([BUILD_ENABLE_UVTD],
|
||||
[test "x$uvtd_enabled" = "xyes"])
|
||||
|
||||
#
|
||||
# Miscellaneous Checks
|
||||
# All checks below are independent of module checking or depend on the results
|
||||
@ -1278,6 +1326,7 @@ AC_MSG_NOTICE([Build configuration:
|
||||
Applications and Libraries:
|
||||
kmscon: $kmscon_enabled ($kmscon_avail: $kmscon_missing)
|
||||
wlterm: $wlterm_enabled ($wlterm_avail: $wlterm_missing)
|
||||
uvtd: $uvtd_enabled ($uvtd_avail: $uvtd_missing)
|
||||
uterm: $uterm_enabled ($uterm_avail: $uterm_missing)
|
||||
tsm: $tsm_enabled ($tsm_avail: $tsm_missing)
|
||||
uvt: $uvt_enabled ($uvt_avail: $uvt_missing)
|
||||
|
243
src/uvtd_main.c
Normal file
243
src/uvtd_main.c
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* uvtd - User-space VT daemon
|
||||
*
|
||||
* Copyright (c) 2012-2013 David Herrmann <dh.herrmann@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include "eloop.h"
|
||||
#include "shl_dlist.h"
|
||||
#include "shl_log.h"
|
||||
#include "uterm_input.h"
|
||||
#include "uterm_monitor.h"
|
||||
#include "uvtd_seat.h"
|
||||
|
||||
struct app_seat {
|
||||
struct shl_dlist list;
|
||||
struct uvtd_app *app;
|
||||
struct uterm_monitor_seat *useat;
|
||||
struct uvtd_seat *seat;
|
||||
};
|
||||
|
||||
struct uvtd_app {
|
||||
struct ev_eloop *eloop;
|
||||
struct uterm_monitor *mon;
|
||||
struct shl_dlist seats;
|
||||
};
|
||||
|
||||
static void app_seat_event(struct uvtd_seat *seat, unsigned int ev, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static int app_seat_new(struct uvtd_app *app, const char *sname,
|
||||
struct uterm_monitor_seat *useat)
|
||||
{
|
||||
struct app_seat *seat;
|
||||
int ret;
|
||||
|
||||
seat = malloc(sizeof(*seat));
|
||||
if (!seat)
|
||||
return -ENOMEM;
|
||||
|
||||
log_debug("new seat %p on %s", seat, sname);
|
||||
|
||||
memset(seat, 0, sizeof(*seat));
|
||||
seat->app = app;
|
||||
seat->useat = useat;
|
||||
|
||||
ret = uvtd_seat_new(&seat->seat, sname, app->eloop, app_seat_event,
|
||||
seat);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
uterm_monitor_set_seat_data(seat->useat, seat);
|
||||
shl_dlist_link(&app->seats, &seat->list);
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
free(seat);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void app_seat_free(struct app_seat *seat)
|
||||
{
|
||||
log_debug("free seat %p", seat);
|
||||
|
||||
shl_dlist_unlink(&seat->list);
|
||||
uterm_monitor_set_seat_data(seat->useat, NULL);
|
||||
uvtd_seat_free(seat->seat);
|
||||
free(seat);
|
||||
}
|
||||
|
||||
static void app_monitor_event(struct uterm_monitor *mon,
|
||||
struct uterm_monitor_event *ev,
|
||||
void *data)
|
||||
{
|
||||
struct uvtd_app *app = data;
|
||||
struct app_seat *seat;
|
||||
int ret;
|
||||
|
||||
switch (ev->type) {
|
||||
case UTERM_MONITOR_NEW_SEAT:
|
||||
ret = app_seat_new(app, ev->seat_name, ev->seat);
|
||||
if (ret)
|
||||
return;
|
||||
break;
|
||||
case UTERM_MONITOR_FREE_SEAT:
|
||||
if (ev->seat_data)
|
||||
app_seat_free(ev->seat_data);
|
||||
break;
|
||||
case UTERM_MONITOR_NEW_DEV:
|
||||
seat = ev->seat_data;
|
||||
if (!seat)
|
||||
return;
|
||||
|
||||
switch (ev->dev_type) {
|
||||
case UTERM_MONITOR_INPUT:
|
||||
log_debug("new input device %s on seat %p",
|
||||
ev->dev_node, seat);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case UTERM_MONITOR_FREE_DEV:
|
||||
seat = ev->seat_data;
|
||||
if (!seat)
|
||||
return;
|
||||
|
||||
switch (ev->dev_type) {
|
||||
case UTERM_MONITOR_INPUT:
|
||||
log_debug("free input device %s on seat %p",
|
||||
ev->dev_node, seat);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void app_sig_generic(struct ev_eloop *eloop,
|
||||
struct signalfd_siginfo *info,
|
||||
void *data)
|
||||
{
|
||||
struct uvtd_app *app = data;
|
||||
|
||||
log_info("terminating due to caught signal %d", info->ssi_signo);
|
||||
ev_eloop_exit(app->eloop);
|
||||
}
|
||||
|
||||
static void app_sig_ignore(struct ev_eloop *eloop,
|
||||
struct signalfd_siginfo *info,
|
||||
void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void destroy_app(struct uvtd_app *app)
|
||||
{
|
||||
uterm_monitor_unref(app->mon);
|
||||
ev_eloop_unregister_signal_cb(app->eloop, SIGPIPE, app_sig_ignore,
|
||||
app);
|
||||
ev_eloop_unregister_signal_cb(app->eloop, SIGINT, app_sig_generic,
|
||||
app);
|
||||
ev_eloop_unregister_signal_cb(app->eloop, SIGTERM, app_sig_generic,
|
||||
app);
|
||||
ev_eloop_unref(app->eloop);
|
||||
}
|
||||
|
||||
static int setup_app(struct uvtd_app *app)
|
||||
{
|
||||
int ret;
|
||||
|
||||
shl_dlist_init(&app->seats);
|
||||
|
||||
ret = ev_eloop_new(&app->eloop, log_llog, NULL);
|
||||
if (ret) {
|
||||
log_error("cannot create eloop object: %d", ret);
|
||||
goto err_app;
|
||||
}
|
||||
|
||||
ret = ev_eloop_register_signal_cb(app->eloop, SIGTERM,
|
||||
app_sig_generic, app);
|
||||
if (ret) {
|
||||
log_error("cannot register SIGTERM signal handler: %d", ret);
|
||||
goto err_app;
|
||||
}
|
||||
|
||||
ret = ev_eloop_register_signal_cb(app->eloop, SIGINT,
|
||||
app_sig_generic, app);
|
||||
if (ret) {
|
||||
log_error("cannot register SIGINT signal handler: %d", ret);
|
||||
goto err_app;
|
||||
}
|
||||
|
||||
ret = ev_eloop_register_signal_cb(app->eloop, SIGPIPE,
|
||||
app_sig_ignore, app);
|
||||
if (ret) {
|
||||
log_error("cannot register SIGPIPE signal handler: %d", ret);
|
||||
goto err_app;
|
||||
}
|
||||
|
||||
ret = uterm_monitor_new(&app->mon, app->eloop, app_monitor_event, app);
|
||||
if (ret) {
|
||||
log_error("cannot create device monitor: %d", ret);
|
||||
goto err_app;
|
||||
}
|
||||
|
||||
log_debug("scanning for devices...");
|
||||
uterm_monitor_scan(app->mon);
|
||||
|
||||
return 0;
|
||||
|
||||
err_app:
|
||||
destroy_app(app);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
struct uvtd_app app;
|
||||
|
||||
log_set_config(&LOG_CONFIG_INFO(1, 1));
|
||||
log_print_init("uvtd");
|
||||
|
||||
memset(&app, 0, sizeof(app));
|
||||
|
||||
ret = setup_app(&app);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
ev_eloop_run(app.eloop, -1);
|
||||
|
||||
ret = 0;
|
||||
destroy_app(&app);
|
||||
err_out:
|
||||
if (ret)
|
||||
log_err("cannot initialize uvtd, errno %d: %s",
|
||||
ret, strerror(-ret));
|
||||
log_info("exiting");
|
||||
return -ret;
|
||||
}
|
594
src/uvtd_seat.c
Normal file
594
src/uvtd_seat.c
Normal file
@ -0,0 +1,594 @@
|
||||
/*
|
||||
* uvtd - User-space VT daemon
|
||||
*
|
||||
* Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Seats
|
||||
* Each set of input+output devices form a single seat. Each seat is independent
|
||||
* of each other and there can be exactly one user per seat interacting with the
|
||||
* system.
|
||||
* Per seat, we have multiple sessions. But only one session can be active at a
|
||||
* time per seat. We allow external sessions, so session activation/deactivation
|
||||
* may be asynchronous.
|
||||
*
|
||||
* A seat object manages all the sessions for a single seat. As long as a seat
|
||||
* is asleep, no session is active. If you wake it up, the seat manager
|
||||
* automatically schedules a session. You can then request other sessions to be
|
||||
* scheduled and the seat manager will try to deactivate the current session and
|
||||
* reactivate the new session.
|
||||
*
|
||||
* Note that session deactivation may be asynchronous (unless forced). So some
|
||||
* calls might return -EINPROGRESS if the session-deactivation is pending. This
|
||||
* shouldn't bother the user as the session will notify back soon that the
|
||||
* deactivation was successfull. However, if it doesn't the user can chose to
|
||||
* perform any other action and we will retry the operation. As a last resort,
|
||||
* you can always kill the session by unregistering it or forcing a
|
||||
* deactivation.
|
||||
* "async_schedule" tracks the task that requested the deactivation of a
|
||||
* session. So when the session notifies us that it got deactivated, we know
|
||||
* what the user wanted and can perform the requested task now.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "eloop.h"
|
||||
#include "shl_dlist.h"
|
||||
#include "shl_log.h"
|
||||
#include "uvtd_seat.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "seat"
|
||||
|
||||
struct uvtd_session {
|
||||
struct shl_dlist list;
|
||||
unsigned long ref;
|
||||
struct uvtd_seat *seat;
|
||||
|
||||
bool enabled;
|
||||
bool deactivating;
|
||||
|
||||
uvtd_session_cb_t cb;
|
||||
void *data;
|
||||
};
|
||||
|
||||
/* task that requested the pending session-deactivation */
|
||||
enum uvtd_async_schedule {
|
||||
SCHEDULE_NONE, /* default, causes a reschedule */
|
||||
SCHEDULE_SWITCH, /* causes a reschedule */
|
||||
SCHEDULE_SLEEP, /* puts the seat asleep */
|
||||
SCHEDULE_UNREGISTER, /* unregisters the session */
|
||||
};
|
||||
|
||||
struct uvtd_seat {
|
||||
struct ev_eloop *eloop;
|
||||
char *name;
|
||||
|
||||
size_t session_count;
|
||||
struct shl_dlist sessions;
|
||||
|
||||
bool awake;
|
||||
struct uvtd_session *current_sess;
|
||||
struct uvtd_session *scheduled_sess;
|
||||
struct uvtd_session *dummy_sess;
|
||||
|
||||
unsigned int async_schedule;
|
||||
|
||||
uvtd_seat_cb_t cb;
|
||||
void *data;
|
||||
};
|
||||
|
||||
static int session_call(struct uvtd_session *sess, unsigned int event)
|
||||
{
|
||||
if (!sess->cb)
|
||||
return 0;
|
||||
|
||||
return sess->cb(sess, event, sess->data);
|
||||
}
|
||||
|
||||
static int session_call_activate(struct uvtd_session *sess)
|
||||
{
|
||||
log_debug("activate session %p", sess);
|
||||
return session_call(sess, UVTD_SESSION_ACTIVATE);
|
||||
}
|
||||
|
||||
static int session_call_deactivate(struct uvtd_session *sess)
|
||||
{
|
||||
log_debug("deactivate session %p", sess);
|
||||
return session_call(sess, UVTD_SESSION_DEACTIVATE);
|
||||
}
|
||||
|
||||
/* drop the current session as if it was successfully deactivated */
|
||||
static void seat_yield(struct uvtd_seat *seat)
|
||||
{
|
||||
if (!seat->current_sess)
|
||||
return;
|
||||
|
||||
seat->current_sess->deactivating = false;
|
||||
seat->current_sess = NULL;
|
||||
seat->async_schedule = SCHEDULE_NONE;
|
||||
}
|
||||
|
||||
static int seat_go_asleep(struct uvtd_seat *seat, bool force)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!seat->awake)
|
||||
return 0;
|
||||
|
||||
if (seat->current_sess) {
|
||||
ret = -EBUSY;
|
||||
if (!force)
|
||||
return ret;
|
||||
|
||||
seat_yield(seat);
|
||||
}
|
||||
|
||||
seat->awake = false;
|
||||
|
||||
if (seat->cb)
|
||||
seat->cb(seat, UVTD_SEAT_SLEEP, seat->data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void seat_go_awake(struct uvtd_seat *seat)
|
||||
{
|
||||
if (seat->awake)
|
||||
return;
|
||||
|
||||
seat->awake = true;
|
||||
}
|
||||
|
||||
static int seat_run(struct uvtd_seat *seat)
|
||||
{
|
||||
int ret;
|
||||
struct uvtd_session *session;
|
||||
|
||||
if (!seat->awake)
|
||||
return -EBUSY;
|
||||
if (seat->current_sess)
|
||||
return 0;
|
||||
|
||||
if (!seat->scheduled_sess) {
|
||||
log_debug("no session scheduled to run (num: %zu)",
|
||||
seat->session_count);
|
||||
return -ENOENT;
|
||||
}
|
||||
session = seat->scheduled_sess;
|
||||
|
||||
/* TODO: unregister session and try next on failure */
|
||||
ret = session_call_activate(session);
|
||||
if (ret) {
|
||||
log_warning("cannot activate session %p: %d", session, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
seat->current_sess = session;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seat_pause(struct uvtd_seat *seat, bool force, unsigned int async)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!seat->current_sess)
|
||||
return 0;
|
||||
|
||||
/* TODO: pass \force to the session */
|
||||
seat->current_sess->deactivating = true;
|
||||
ret = session_call_deactivate(seat->current_sess);
|
||||
if (ret) {
|
||||
if (!force && ret == -EINPROGRESS) {
|
||||
seat->async_schedule = async;
|
||||
log_debug("pending deactivation for session %p",
|
||||
seat->current_sess);
|
||||
} else {
|
||||
log_warning("cannot deactivate session %p (%d): %d",
|
||||
seat->current_sess, force, ret);
|
||||
}
|
||||
|
||||
if (!force)
|
||||
return ret;
|
||||
}
|
||||
|
||||
seat_yield(seat);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void seat_reschedule(struct uvtd_seat *seat)
|
||||
{
|
||||
struct shl_dlist *iter, *start;
|
||||
struct uvtd_session *sess;
|
||||
|
||||
if (seat->scheduled_sess && seat->scheduled_sess->enabled)
|
||||
return;
|
||||
|
||||
if (seat->current_sess && seat->current_sess->enabled) {
|
||||
seat->scheduled_sess = seat->current_sess;
|
||||
return;
|
||||
}
|
||||
|
||||
if (seat->current_sess)
|
||||
start = &seat->current_sess->list;
|
||||
else
|
||||
start = &seat->sessions;
|
||||
|
||||
shl_dlist_for_each_but_one(iter, start, &seat->sessions) {
|
||||
sess = shl_dlist_entry(iter, struct uvtd_session, list);
|
||||
|
||||
if (sess != seat->dummy_sess && sess->enabled) {
|
||||
seat->scheduled_sess = sess;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (seat->dummy_sess && seat->dummy_sess->enabled)
|
||||
seat->scheduled_sess = seat->dummy_sess;
|
||||
else
|
||||
seat->scheduled_sess = NULL;
|
||||
}
|
||||
|
||||
static bool seat_has_schedule(struct uvtd_seat *seat)
|
||||
{
|
||||
return seat->scheduled_sess &&
|
||||
seat->scheduled_sess != seat->current_sess;
|
||||
}
|
||||
|
||||
static int seat_switch(struct uvtd_seat *seat)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = seat_pause(seat, false, SCHEDULE_SWITCH);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return seat_run(seat);
|
||||
}
|
||||
|
||||
static void seat_schedule(struct uvtd_seat *seat, struct uvtd_session *sess)
|
||||
{
|
||||
seat->scheduled_sess = sess;
|
||||
seat_reschedule(seat);
|
||||
if (seat_has_schedule(seat))
|
||||
seat_switch(seat);
|
||||
}
|
||||
|
||||
static void seat_next(struct uvtd_seat *seat, bool reverse)
|
||||
{
|
||||
struct shl_dlist *cur, *iter;
|
||||
struct uvtd_session *s, *next;
|
||||
|
||||
if (seat->current_sess)
|
||||
cur = &seat->current_sess->list;
|
||||
else if (seat->session_count)
|
||||
cur = &seat->sessions;
|
||||
else
|
||||
return;
|
||||
|
||||
next = NULL;
|
||||
if (!seat->current_sess && seat->dummy_sess &&
|
||||
seat->dummy_sess->enabled)
|
||||
next = seat->dummy_sess;
|
||||
|
||||
if (reverse) {
|
||||
shl_dlist_for_each_reverse_but_one(iter, cur,
|
||||
&seat->sessions) {
|
||||
s = shl_dlist_entry(iter, struct uvtd_session, list);
|
||||
|
||||
if (s->enabled && seat->dummy_sess != s) {
|
||||
next = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
shl_dlist_for_each_but_one(iter, cur, &seat->sessions) {
|
||||
s = shl_dlist_entry(iter, struct uvtd_session, list);
|
||||
|
||||
if (s->enabled && seat->dummy_sess != s) {
|
||||
next = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!next)
|
||||
return;
|
||||
|
||||
seat_schedule(seat, next);
|
||||
}
|
||||
|
||||
int uvtd_seat_new(struct uvtd_seat **out, const char *seatname,
|
||||
struct ev_eloop *eloop, uvtd_seat_cb_t cb, void *data)
|
||||
{
|
||||
struct uvtd_seat *seat;
|
||||
int ret;
|
||||
|
||||
if (!out || !eloop || !seatname)
|
||||
return -EINVAL;
|
||||
|
||||
seat = malloc(sizeof(*seat));
|
||||
if (!seat)
|
||||
return -ENOMEM;
|
||||
memset(seat, 0, sizeof(*seat));
|
||||
seat->eloop = eloop;
|
||||
seat->cb = cb;
|
||||
seat->data = data;
|
||||
shl_dlist_init(&seat->sessions);
|
||||
|
||||
seat->name = strdup(seatname);
|
||||
if (!seat->name) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ev_eloop_ref(seat->eloop);
|
||||
*out = seat;
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
free(seat);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void uvtd_seat_free(struct uvtd_seat *seat)
|
||||
{
|
||||
struct uvtd_session *s;
|
||||
int ret;
|
||||
|
||||
if (!seat)
|
||||
return;
|
||||
|
||||
ret = seat_pause(seat, true, SCHEDULE_NONE);
|
||||
if (ret)
|
||||
log_warning("destroying seat %s while session %p is active",
|
||||
seat->name, seat->current_sess);
|
||||
|
||||
ret = seat_go_asleep(seat, true);
|
||||
if (ret)
|
||||
log_warning("destroying seat %s while still awake: %d",
|
||||
seat->name, ret);
|
||||
|
||||
while (!shl_dlist_empty(&seat->sessions)) {
|
||||
s = shl_dlist_entry(seat->sessions.next, struct uvtd_session,
|
||||
list);
|
||||
uvtd_session_unregister(s);
|
||||
}
|
||||
|
||||
free(seat->name);
|
||||
ev_eloop_unref(seat->eloop);
|
||||
free(seat);
|
||||
}
|
||||
|
||||
const char *uvtd_seat_get_name(struct uvtd_seat *seat)
|
||||
{
|
||||
return seat ? seat->name : NULL;
|
||||
}
|
||||
|
||||
struct ev_eloop *uvtd_seat_get_eloop(struct uvtd_seat *seat)
|
||||
{
|
||||
return seat ? seat->eloop : NULL;
|
||||
}
|
||||
|
||||
int uvtd_seat_sleep(struct uvtd_seat *seat, bool force)
|
||||
{
|
||||
int ret, err = 0;
|
||||
|
||||
if (!seat)
|
||||
return -EINVAL;
|
||||
|
||||
ret = seat_pause(seat, force, SCHEDULE_SLEEP);
|
||||
if (ret) {
|
||||
if (force)
|
||||
err = ret;
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = seat_go_asleep(seat, force);
|
||||
if (ret) {
|
||||
if (force)
|
||||
err = ret;
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void uvtd_seat_wake_up(struct uvtd_seat *seat)
|
||||
{
|
||||
if (!seat)
|
||||
return;
|
||||
|
||||
seat_go_awake(seat);
|
||||
seat_run(seat);
|
||||
}
|
||||
|
||||
int uvtd_seat_register_session(struct uvtd_seat *seat,
|
||||
struct uvtd_session **out,
|
||||
unsigned int id, uvtd_session_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
struct uvtd_session *sess;
|
||||
|
||||
if (!seat || !out)
|
||||
return -EINVAL;
|
||||
|
||||
sess = malloc(sizeof(*sess));
|
||||
if (!sess)
|
||||
return -ENOMEM;
|
||||
|
||||
log_debug("register session %p", sess);
|
||||
|
||||
memset(sess, 0, sizeof(*sess));
|
||||
sess->ref = 1;
|
||||
sess->seat = seat;
|
||||
sess->cb = cb;
|
||||
sess->data = data;
|
||||
|
||||
/* TODO: add support for \ids */
|
||||
/* register new sessions next to the current one */
|
||||
if (seat->current_sess)
|
||||
shl_dlist_link(&seat->current_sess->list, &sess->list);
|
||||
else
|
||||
shl_dlist_link_tail(&seat->sessions, &sess->list);
|
||||
|
||||
++seat->session_count;
|
||||
*out = sess;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uvtd_session_ref(struct uvtd_session *sess)
|
||||
{
|
||||
if (!sess || !sess->ref)
|
||||
return;
|
||||
|
||||
++sess->ref;
|
||||
}
|
||||
|
||||
void uvtd_session_unref(struct uvtd_session *sess)
|
||||
{
|
||||
if (!sess || !sess->ref || --sess->ref)
|
||||
return;
|
||||
|
||||
uvtd_session_unregister(sess);
|
||||
free(sess);
|
||||
}
|
||||
|
||||
void uvtd_session_unregister(struct uvtd_session *sess)
|
||||
{
|
||||
struct uvtd_seat *seat;
|
||||
int ret;
|
||||
bool forced = false;
|
||||
|
||||
if (!sess || !sess->seat)
|
||||
return;
|
||||
|
||||
log_debug("unregister session %p", sess);
|
||||
|
||||
seat = sess->seat;
|
||||
sess->enabled = false;
|
||||
if (seat->dummy_sess == sess)
|
||||
seat->dummy_sess = NULL;
|
||||
seat_reschedule(seat);
|
||||
|
||||
if (seat->current_sess == sess) {
|
||||
ret = seat_pause(seat, true, SCHEDULE_NONE);
|
||||
if (ret) {
|
||||
forced = true;
|
||||
log_warning("unregistering active session %p; skipping automatic session-switch",
|
||||
sess);
|
||||
}
|
||||
}
|
||||
|
||||
shl_dlist_unlink(&sess->list);
|
||||
--seat->session_count;
|
||||
sess->seat = NULL;
|
||||
|
||||
session_call(sess, UVTD_SESSION_UNREGISTER);
|
||||
uvtd_session_unref(sess);
|
||||
|
||||
/* If this session was active and we couldn't deactivate it, then it
|
||||
* might still have resources allocated that couldn't get freed. In this
|
||||
* case we should not automatically switch to the next session as it is
|
||||
* very likely that it will not be able to start.
|
||||
* Instead, we stay inactive and wait for user/external input to switch
|
||||
* to another session. This delay will then hopefully be long enough so
|
||||
* all resources got freed. */
|
||||
if (!forced)
|
||||
seat_run(seat);
|
||||
}
|
||||
|
||||
bool uvtd_session_is_registered(struct uvtd_session *sess)
|
||||
{
|
||||
return sess && sess->seat;
|
||||
}
|
||||
|
||||
bool uvtd_session_is_active(struct uvtd_session *sess)
|
||||
{
|
||||
return sess && sess->seat && sess->seat->current_sess == sess;
|
||||
}
|
||||
|
||||
void uvtd_session_schedule(struct uvtd_session *sess)
|
||||
{
|
||||
if (!sess || !sess->seat)
|
||||
return;
|
||||
|
||||
seat_schedule(sess->seat, sess);
|
||||
}
|
||||
|
||||
void uvtd_session_enable(struct uvtd_session *sess)
|
||||
{
|
||||
if (!sess || sess->enabled)
|
||||
return;
|
||||
|
||||
log_debug("enable session %p", sess);
|
||||
sess->enabled = true;
|
||||
|
||||
if (sess->seat &&
|
||||
(!sess->seat->current_sess ||
|
||||
sess->seat->current_sess == sess->seat->dummy_sess))
|
||||
seat_schedule(sess->seat, sess);
|
||||
}
|
||||
|
||||
void uvtd_session_disable(struct uvtd_session *sess)
|
||||
{
|
||||
if (!sess || !sess->enabled)
|
||||
return;
|
||||
|
||||
log_debug("disable session %p", sess);
|
||||
sess->enabled = false;
|
||||
}
|
||||
|
||||
bool uvtd_session_is_enabled(struct uvtd_session *sess)
|
||||
{
|
||||
return sess && sess->enabled;
|
||||
}
|
||||
|
||||
void uvtd_session_notify_deactivated(struct uvtd_session *sess)
|
||||
{
|
||||
struct uvtd_seat *seat;
|
||||
unsigned int sched;
|
||||
|
||||
if (!sess || !sess->seat)
|
||||
return;
|
||||
|
||||
seat = sess->seat;
|
||||
if (seat->current_sess != sess)
|
||||
return;
|
||||
|
||||
sched = seat->async_schedule;
|
||||
log_debug("session %p notified core about deactivation (schedule: %u)",
|
||||
sess, sched);
|
||||
seat_yield(seat);
|
||||
seat_reschedule(seat);
|
||||
|
||||
if (sched == SCHEDULE_SLEEP)
|
||||
seat_go_asleep(seat, false);
|
||||
else if (sched == SCHEDULE_UNREGISTER)
|
||||
uvtd_session_unregister(sess);
|
||||
else
|
||||
seat_run(seat);
|
||||
}
|
96
src/uvtd_seat.h
Normal file
96
src/uvtd_seat.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* uvtd - User-space VT daemon
|
||||
*
|
||||
* Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Seats
|
||||
* Each set of input+output devices form a single seat. Each seat is independent
|
||||
* of each other and there can be exactly one user per seat interacting with the
|
||||
* system.
|
||||
* Per seat, we have multiple sessions. But only one session can be active at a
|
||||
* time per seat. We allow external sessions, so session activation/deactivation
|
||||
* may be asynchronous.
|
||||
*/
|
||||
|
||||
#ifndef UVTD_SEAT_H
|
||||
#define UVTD_SEAT_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "eloop.h"
|
||||
|
||||
/* sessions */
|
||||
|
||||
struct uvtd_session;
|
||||
|
||||
enum uvtd_session_event_type {
|
||||
UVTD_SESSION_ACTIVATE,
|
||||
UVTD_SESSION_DEACTIVATE,
|
||||
UVTD_SESSION_UNREGISTER,
|
||||
};
|
||||
|
||||
typedef int (*uvtd_session_cb_t) (struct uvtd_session *session,
|
||||
unsigned int event,
|
||||
void *data);
|
||||
|
||||
void uvtd_session_ref(struct uvtd_session *sess);
|
||||
void uvtd_session_unref(struct uvtd_session *sess);
|
||||
void uvtd_session_unregister(struct uvtd_session *sess);
|
||||
bool uvtd_session_is_registered(struct uvtd_session *sess);
|
||||
|
||||
bool uvtd_session_is_active(struct uvtd_session *sess);
|
||||
void uvtd_session_schedule(struct uvtd_session *sess);
|
||||
|
||||
void uvtd_session_enable(struct uvtd_session *sess);
|
||||
void uvtd_session_disable(struct uvtd_session *sess);
|
||||
bool uvtd_session_is_enabled(struct uvtd_session *sess);
|
||||
|
||||
void uvtd_session_notify_deactivated(struct uvtd_session *sess);
|
||||
|
||||
/* seats */
|
||||
|
||||
struct uvtd_seat;
|
||||
|
||||
enum uvtd_seat_event {
|
||||
UVTD_SEAT_SLEEP,
|
||||
};
|
||||
|
||||
typedef void (*uvtd_seat_cb_t) (struct uvtd_seat *seat, unsigned int event,
|
||||
void *data);
|
||||
|
||||
int uvtd_seat_new(struct uvtd_seat **out, const char *seatname,
|
||||
struct ev_eloop *eloop, uvtd_seat_cb_t cb, void *data);
|
||||
void uvtd_seat_free(struct uvtd_seat *seat);
|
||||
|
||||
const char *uvtd_seat_get_name(struct uvtd_seat *seat);
|
||||
struct ev_eloop *uvtd_seat_get_eloop(struct uvtd_seat *seat);
|
||||
int uvtd_seat_sleep(struct uvtd_seat *seat, bool force);
|
||||
void uvtd_seat_wake_up(struct uvtd_seat *seat);
|
||||
void uvtd_seat_schedule(struct uvtd_seat *seat, unsigned int id);
|
||||
|
||||
int uvtd_seat_register_session(struct uvtd_seat *seat,
|
||||
struct uvtd_session **out,
|
||||
unsigned int id, uvtd_session_cb_t cb,
|
||||
void *data);
|
||||
|
||||
#endif /* UVTD_SEAT_H */
|
Loading…
x
Reference in New Issue
Block a user