summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@gmail.com>2013-03-04 15:11:30 +0100
committerDavid Herrmann <dh.herrmann@gmail.com>2013-03-04 15:11:30 +0100
commit176a0e8be5f3d0ebfd1c501c7335eae24d71d8a5 (patch)
treee8a137a4f453a33139bd2af23407adc25415f7ba /src
parent9c2375b6d4e725f0757d7df83473177234425dc4 (diff)
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>
Diffstat (limited to 'src')
-rw-r--r--src/uvtd_main.c243
-rw-r--r--src/uvtd_seat.c594
-rw-r--r--src/uvtd_seat.h96
3 files changed, 933 insertions, 0 deletions
diff --git a/src/uvtd_main.c b/src/uvtd_main.c
new file mode 100644
index 0000000..0dc7a83
--- /dev/null
+++ b/src/uvtd_main.c
@@ -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;
+}
diff --git a/src/uvtd_seat.c b/src/uvtd_seat.c
new file mode 100644
index 0000000..2ef3180
--- /dev/null
+++ b/src/uvtd_seat.c
@@ -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);
+}
diff --git a/src/uvtd_seat.h b/src/uvtd_seat.h
new file mode 100644
index 0000000..534d773
--- /dev/null
+++ b/src/uvtd_seat.h
@@ -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 */