kmscon: remove cdev sessions
authorDavid Herrmann <dh.herrmann@gmail.com>
Wed, 23 Oct 2013 13:22:28 +0000 (15:22 +0200)
committerDavid Herrmann <dh.herrmann@gmail.com>
Wed, 23 Oct 2013 13:22:28 +0000 (15:22 +0200)
cdev sessions are outdated. Use libuvt instead. Remove all references to
cdev-sessions and clean up the build chain.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Makefile.am
README
configure.ac
docs/man/kmscon.xml
src/kmscon_cdev.c [deleted file]
src/kmscon_cdev.h [deleted file]
src/kmscon_conf.c
src/kmscon_conf.h
src/kmscon_seat.c

index 70ff37f..c35b040 100644 (file)
@@ -648,7 +648,6 @@ kmscon_SOURCES = \
        src/kmscon_module.c \
        src/kmscon_terminal.h \
        src/kmscon_dummy.h \
-       src/kmscon_cdev.h \
        src/kmscon_seat.h \
        src/kmscon_seat.c \
        src/kmscon_conf.h \
@@ -679,12 +678,6 @@ kmscon_SOURCES += src/kmscon_terminal.c
 kmscon_LDADD += libtsm.la
 endif
 
-if BUILD_ENABLE_SESSION_CDEV
-kmscon_SOURCES += src/kmscon_cdev.c
-kmscon_CPPFLAGS += $(FUSE_CFLAGS)
-kmscon_LDADD += $(FUSE_LIBS)
-endif
-
 #
 # Tests
 #
diff --git a/README b/README
index b2acc03..7aad582 100644 (file)
--- a/README
+++ b/README
@@ -87,7 +87,6 @@ Released tarballs can be found at:
     --with-sessions: Built in sessions. Available sessions are:
        - dummy: Dummy fallback session
        - terminal: Terminal-emulator sessions
-       - cdev: Fake VTs via CUSE (DEPRECATED! Don't use it!)
 
   The following options select which applications are built. If
   dependency-checks fail, they are disabled by default unless explicitly enabled
index dd3d4d3..41afd53 100644 (file)
@@ -388,17 +388,14 @@ AC_ARG_WITH([sessions],
             [with_sessions="default"])
 enable_session_dummy="no"
 enable_session_terminal="no"
-enable_session_cdev="no"
 if test "x$enable_all" = "xyes" ; then
         enable_session_dummy="yes"
         enable_session_terminal="yes"
-        enable_session_cdev="yes"
-        with_sessions="dummy,terminal,cdev (all)"
+        with_sessions="dummy,terminal (all)"
 elif test "x$with_sessions" = "xdefault" ; then
         enable_session_dummy="yes (default)"
         enable_session_terminal="yes (default)"
-        enable_session_cdev="yes (default)"
-        with_sessions="dummy,terminal,cdev (default)"
+        with_sessions="dummy,terminal (default)"
 elif test ! "x$with_sessions" = "x" ; then
         SAVEIFS="$IFS"
         IFS=","
@@ -407,8 +404,6 @@ elif test ! "x$with_sessions" = "x" ; then
                         enable_session_dummy="yes"
                 elif test "x$i" = "xterminal" ; then
                         enable_session_terminal="yes"
-                elif test "x$i" = "xcdev" ; then
-                        enable_session_cdev="yes"
                 else
                         IFS="$SAVEIFS"
                         AC_ERROR([Unknown session type $i])
@@ -756,30 +751,6 @@ else
         session_terminal_missing="enable-session-terminal"
 fi
 
-# session cdev
-session_cdev_avail=no
-session_cdev_missing=""
-if test ! "x$enable_session_cdev" = "xno" ; then
-        session_cdev_avail=yes
-        if test "x$have_fuse" = "xno" ; then
-                session_cdev_avail=no
-                session_cdev_missing="libfuse,$session_cdev_missing"
-        fi
-
-        if test "x$tsm_avail" = "xno" ; then
-                session_cdev_avail=no
-                session_cdev_missing="$tsm_missing,$session_cdev_missing"
-        fi
-
-        if test "x$session_cdev_avail" = "xno" ; then
-                if test "x$enable_session_cdev" = "xyes" ; then
-                        AC_ERROR([missing for session-cdev: $session_cdev_missing])
-                fi
-        fi
-else
-        session_cdev_missing="enable-session-cdev"
-fi
-
 # kmscon
 kmscon_avail=no
 kmscon_missing=""
@@ -827,14 +798,6 @@ if test "x$kmscon_avail" = "xyes" ; then
         fi
 fi
 
-# session cdev
-session_cdev_enabled=no
-if test "x$session_cdev_avail" = "xyes" ; then
-        if test "x${enable_session_cdev% *}" = "xyes" ; then
-                session_cdev_enabled=yes
-        fi
-fi
-
 # session terminal
 session_terminal_enabled=no
 if test "x$session_terminal_avail" = "xyes" ; then
@@ -1172,15 +1135,6 @@ fi
 AM_CONDITIONAL([BUILD_ENABLE_SESSION_TERMINAL],
                [test "x$session_terminal_enabled" = "xyes"])
 
-# session cdev
-if test "x$session_cdev_enabled" = "xyes" ; then
-        AC_DEFINE([BUILD_ENABLE_SESSION_CDEV], [1],
-                  [Build cdev session])
-fi
-
-AM_CONDITIONAL([BUILD_ENABLE_SESSION_CDEV],
-               [test "x$session_cdev_enabled" = "xyes"])
-
 # kmscon
 AM_CONDITIONAL([BUILD_ENABLE_KMSCON],
                [test "x$kmscon_enabled" = "xyes"])
@@ -1299,6 +1253,5 @@ AC_MSG_NOTICE([Build configuration:
   Session Types:
                 dummy: $session_dummy_enabled ($session_dummy_avail: $session_dummy_missing)
              terminal: $session_terminal_enabled ($session_terminal_avail: $session_terminal_missing)
-                 cdev: $session_cdev_enabled ($session_cdev_avail: $session_cdev_missing)
 
         Run "${MAKE-make}" to start compilation process])
index 3d8a639..8f058ff 100644 (file)
                 (default: on)</para>
         </listitem>
       </varlistentry>
-
-      <varlistentry>
-        <term><option>--cdev-session</option></term>
-        <listitem>
-          <para>Start cdev-VT-emulation session after setup is done. This is
-                limited to
-                <option>--listen</option> mode. (default: off)</para>
-        </listitem>
-      </varlistentry>
     </variablelist>
 
     <para>Terminal Options:</para>
     <para>Terminal sessions provide a terminal emulator. They are the main
           session type and provide all the terminal-emulation
           functionality.</para>
-
-    <para>CDev sessions provide a way to hook-up external applications as kmscon
-          sessions. They provide fake virtual terminals on seats that don't have
-          them so you can switch between multiple graphics applications/servers
-          without needing VTs. See <emphasis>Virtual Terminals</emphasis>
-          below.</para>
   </refsect1>
 
   <refsect1>
           This is done via VT APIs on seat0. But note that if the kernel is
           compiled without CONFIG_VT (which controls whether VTs are available),
           then even seat0 does not have VTs.</para>
-
-    <para>On seats without VTs we need a replacement API. However, a new
-          API would require modifying every application that uses the VT API to
-          also support the new replacement if you want to run it on those seats.
-          Therefore, kmscon provides a backwards-compatible API. It uses the
-          linux CUSE API to create VTs for all seats that have no VTs. You need
-          to pass <option>--cdev-session</option> to kmscon to enable this.
-          kmscon will then create one cdev (<emphasis>c</emphasis>haracter
-          <emphasis>dev</emphasis>ice) for each seat and calls it
-          <filename>/dev/ttyF{seatname}</filename> ('F' for 'fake'). This
-          character device provides the same API as a VT so your X-Server (or
-          even kmscon in default-mode) can use it instead of a real VT.</para>
-
-    <para>In contrast to real VTs, each application that opens this fake VT will
-          get a different VT assigned. So you can use multiple X-Servers all
-          opening <filename>/dev/ttyF{seatname}</filename> and they will each
-          show up as different kmscon session. But note that not all TTY ioctls
-          are available on fake VTs. kmscon only emulates the API that is
-          currently used by most VT users. If you want to run an application
-          that needs more functionality, please open a bug-report.</para>
-
-    <para>Switching VTs is done by signaling the active process to go asleep and
-          signaling the new VT to wake up. If kmscon emulates VTs, it needs to
-          be able to send these signals to the processes running on fake VTs. In
-          most situations this means kmscon must have root privileges.</para>
   </refsect1>
 
   <refsect1>
diff --git a/src/kmscon_cdev.c b/src/kmscon_cdev.c
deleted file mode 100644 (file)
index d26629a..0000000
+++ /dev/null
@@ -1,1431 +0,0 @@
-/*
- * kmscon - Character-Device Session
- *
- * Copyright (c) 2012 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.
- */
-
-/*
- * Character-Device Session
- */
-
-#define FUSE_USE_VERSION 29
-
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/kd.h>
-#include <linux/major.h>
-#include <linux/vt.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <termio.h>
-#include <termios.h>
-#include <unistd.h>
-#include "kmscon_cdev.h"
-#include "kmscon_seat.h"
-#include "shl_array.h"
-#include "shl_dlist.h"
-#include "shl_log.h"
-#include "shl_ring.h"
-#include "tsm_screen.h"
-#include "tsm_vte.h"
-#include "uterm_input.h"
-
-#include <fuse/fuse.h>
-#include <fuse/fuse_common.h>
-#include <fuse/fuse_lowlevel.h>
-#include <fuse/fuse_opt.h>
-#include <fuse/cuse_lowlevel.h>
-
-#define LOG_SUBSYSTEM "cdev"
-
-struct kmscon_cdev {
-       struct kmscon_seat *seat;
-       struct ev_eloop *eloop;
-       struct uterm_input *input;
-       struct kmscon_session *s;
-       struct ev_fd *efd;
-       unsigned int minor;
-
-       struct fuse_session *session;
-       int fd;
-       struct fuse_chan *channel;
-
-       size_t bufsize;
-       char *buf;
-
-       struct shl_dlist clients;
-       int error;
-};
-
-struct cdev_client {
-       struct shl_dlist list;
-       struct kmscon_cdev *cdev;
-       bool dead;
-
-       struct tsm_screen *screen;
-       struct tsm_vte *vte;
-
-       bool active;
-       struct kmscon_session *s;
-
-       struct fuse_pollhandle *ph;
-       struct shl_ring *ring;
-       struct shl_dlist readers;
-
-       long kdmode;
-       long kbmode;
-
-       struct vt_mode vtmode;
-       struct fuse_ctx user;
-       bool pending_switch;
-
-       struct shl_dlist waiters;
-};
-
-struct cdev_reader {
-       struct shl_dlist list;
-       bool killed;
-       fuse_req_t req;
-       size_t len;
-};
-
-struct cdev_waiter {
-       struct shl_dlist list;
-       bool killed;
-       fuse_req_t req;
-};
-
-static pthread_mutex_t cdev_lock = PTHREAD_MUTEX_INITIALIZER;
-static struct shl_array *cdev_ids = NULL;
-
-static int cdev_allocate_id(void)
-{
-       static const bool init = true;
-       int ret, len, i;
-
-       pthread_mutex_lock(&cdev_lock);
-
-       if (!cdev_ids) {
-               ret = shl_array_new(&cdev_ids, sizeof(bool), 4);
-               if (ret)
-                       goto err_unlock;
-       }
-
-       len = shl_array_get_length(cdev_ids);
-       for (i = 0; i < len; ++i)
-               if (!*SHL_ARRAY_AT(cdev_ids, bool, i))
-                       break;
-
-       if (i >= len) {
-               ret = shl_array_push(cdev_ids, &init);
-               if (ret)
-                       goto err_unlock;
-               ret = len;
-       } else {
-               *SHL_ARRAY_AT(cdev_ids, bool, i) = true;
-               ret = i;
-       }
-
-err_unlock:
-       pthread_mutex_unlock(&cdev_lock);
-       return (ret < 0) ? ret : (ret + 16384);
-}
-
-/*
- * Cdev Clients
- * As opposed to kernel VTs, we only provide one single char-dev per seat and
- * each client that opens it is managed separately. Hence, it is not possible to
- * use such a VT shared by two clients except if you pass the FD between the
- * clients. This has several advantages and avoids many bugs in the kernel VT
- * implementation.
- * For every user opening a /dev/ttyF<seat> device, a separate cdev_client is
- * created. A cdev_client emulates a single kernel VT but is managed as a
- * dedicated kmscon_session on its seat. We start a kmscon_terminal as backend
- * so you actually can run "agetty" on this fake-VT. When set into graphical
- * mode, the terminal is suspended and you can run an XServer on it. We emulate
- * the VT-switching signal-API, too. We release all DRM devices if a fake-VT is
- * active and reacquire them afterwards. This allows the clients to actually
- * implement graphical terminals. However, if you fail to release the DRM
- * devices, we actually try to kill the fake-VT so we can get access again.
- */
-
-static void reader_interrupt(fuse_req_t req, void *data)
-{
-       struct cdev_reader *reader = data;
-
-       if (!reader)
-               return;
-
-       reader->killed = true;
-}
-
-static int reader_new(struct cdev_reader **out, struct cdev_client *client,
-                     fuse_req_t req)
-{
-       struct cdev_reader *reader;
-
-       if (fuse_req_interrupted(req))
-               return -ENOENT;
-
-       reader = malloc(sizeof(*reader));
-       if (!reader)
-               return -ENOMEM;
-       memset(reader, 0, sizeof(*reader));
-       reader->req = req;
-       fuse_req_interrupt_func(req, reader_interrupt, reader);
-       if (reader->killed) {
-               fuse_req_interrupt_func(req, NULL, NULL);
-               free(reader);
-               return -ENOENT;
-       }
-
-       shl_dlist_link_tail(&client->readers, &reader->list);
-       *out = reader;
-       return 0;
-}
-
-static void reader_free(struct cdev_reader *reader, int error)
-{
-       shl_dlist_unlink(&reader->list);
-       if (reader->req) {
-               fuse_req_interrupt_func(reader->req, NULL, NULL);
-               fuse_reply_err(reader->req, -error);
-       }
-       free(reader);
-}
-
-static int reader_release(struct cdev_reader *reader, const char *buf,
-                         size_t len)
-{
-       int ret;
-
-       fuse_req_interrupt_func(reader->req, NULL, NULL);
-       ret = fuse_reply_buf(reader->req, buf, len);
-       reader->req = NULL;
-       reader_free(reader, 0);
-       return ret;
-}
-
-static void waiter_interrupt(fuse_req_t req, void *data)
-{
-       struct cdev_waiter *waiter = data;
-
-       if (!waiter)
-               return;
-
-       waiter->killed = true;
-}
-
-static int waiter_new(struct cdev_waiter **out, struct cdev_client *client,
-                     fuse_req_t req)
-{
-       struct cdev_waiter *waiter;
-
-       if (fuse_req_interrupted(req))
-               return -ENOENT;
-
-       waiter = malloc(sizeof(*waiter));
-       if (!waiter)
-               return -ENOMEM;
-       memset(waiter, 0, sizeof(*waiter));
-       waiter->req = req;
-       fuse_req_interrupt_func(req, waiter_interrupt, waiter);
-       if (waiter->killed) {
-               fuse_req_interrupt_func(req, NULL, NULL);
-               free(waiter);
-               return -ENOENT;
-       }
-
-       shl_dlist_link_tail(&client->waiters, &waiter->list);
-       *out = waiter;
-       return 0;
-}
-
-static void waiter_free(struct cdev_waiter *waiter, int error)
-{
-       shl_dlist_unlink(&waiter->list);
-       if (waiter->req) {
-               fuse_req_interrupt_func(waiter->req, NULL, NULL);
-               fuse_reply_err(waiter->req, -error);
-       }
-       free(waiter);
-}
-
-static int waiter_release(struct cdev_waiter *waiter)
-{
-       int ret;
-
-       fuse_req_interrupt_func(waiter->req, NULL, NULL);
-       ret = fuse_reply_ioctl(waiter->req, 0, NULL, 0);
-       waiter->req = NULL;
-       waiter_free(waiter, 0);
-       return ret;
-}
-
-static void client_vte_event(struct tsm_vte *vte, const char *u8, size_t len,
-                            void *data)
-{
-       struct cdev_client *client = data;
-       struct cdev_reader *reader;
-       int ret;
-       bool was_empty;
-       const char *buf;
-       size_t size;
-
-       /* TODO: we should have a maximum buffer size here */
-       was_empty = shl_ring_is_empty(client->ring);
-       ret = shl_ring_write(client->ring, u8, len);
-       if (ret)
-               log_warning("cannot resize buffer for cdev client: %d", ret);
-
-       if (shl_ring_is_empty(client->ring))
-               return;
-
-       if (was_empty && client->ph) {
-               fuse_notify_poll(client->ph);
-               fuse_pollhandle_destroy(client->ph);
-               client->ph = NULL;
-       }
-
-       while (!shl_dlist_empty(&client->readers)) {
-               reader = shl_dlist_entry(client->readers.next,
-                                        struct cdev_reader, list);
-               if (reader->killed)
-                       continue;
-
-               /* TODO: fix filling the whole buffer instead of returning
-                * partial buffers when the ring data is split */
-               buf = shl_ring_peek(client->ring, &size, 0);
-               if (!size)
-                       break;
-               if (size > reader->len)
-                       size = reader->len;
-
-               ret = reader_release(reader, buf, size);
-               if (ret < 0)
-                       continue;
-               shl_ring_drop(client->ring, size);
-       }
-}
-
-static void client_input_event(struct uterm_input *input,
-                              struct uterm_input_event *ev,
-                              void *data)
-{
-       struct cdev_client *client = data;
-
-       if (!client->active || ev->handled)
-               return;
-
-       /* we drop all input in K_OFF mode */
-       if (client->kbmode == K_OFF)
-               return;
-
-       /* TODO: see kmscon_terminal on how this is special. We need to fix this
-        * when xkbcommon provides the first multi-sym events. */
-       if (ev->num_syms > 1)
-               return;
-
-       if (tsm_vte_handle_keyboard(client->vte, ev->keysyms[0], ev->ascii,
-                                   ev->mods, ev->codepoints[0])) {
-               tsm_screen_sb_reset(client->screen);
-               ev->handled = true;
-       }
-}
-
-static int client_activate(struct cdev_client *client)
-{
-       int ret;
-       struct cdev_waiter *waiter;
-
-       /* TODO: Check whether we have CAP_KILL capability during startup */
-       if (client->vtmode.mode == VT_PROCESS && client->vtmode.acqsig) {
-               ret = kill(client->user.pid, client->vtmode.acqsig);
-               if (ret)
-                       log_warning("cannot send activation signal to process %d of cdev client %p (%d): %m",
-                                   client->user.pid, client, errno);
-       }
-
-       while (!shl_dlist_empty(&client->waiters)) {
-               waiter = shl_dlist_entry(client->waiters.next,
-                                        struct cdev_waiter, list);
-               if (waiter->killed)
-                       waiter_free(waiter, 0);
-               else
-                       waiter_release(waiter);
-       }
-
-       client->active = true;
-       return 0;
-}
-
-static int client_deactivate(struct cdev_client *client)
-{
-       int ret;
-
-       if (client->vtmode.mode == VT_PROCESS && client->vtmode.relsig) {
-               ret = kill(client->user.pid, client->vtmode.relsig);
-               if (ret)
-                       log_warning("cannot send deactivation signal to process %d of cdev client %p (%d): %m",
-                                   client->user.pid, client, errno);
-               client->pending_switch = true;
-               return -EINPROGRESS;
-       }
-
-       client->active = false;
-       return 0;
-}
-
-static void client_kill(struct cdev_client *client)
-{
-       struct cdev_reader *reader;
-       struct cdev_waiter *waiter;
-
-       if (client->dead) {
-               log_error("killing already dead client");
-               return;
-       }
-
-       log_debug("kill fake TTY client %p", client);
-
-       client->dead = true;
-
-       if (client->ph) {
-               fuse_notify_poll(client->ph);
-               fuse_pollhandle_destroy(client->ph);
-       }
-
-       while (!shl_dlist_empty(&client->readers)) {
-               reader = shl_dlist_entry(client->readers.next,
-                                        struct cdev_reader, list);
-               reader_free(reader, -EPIPE);
-       }
-
-       while (!shl_dlist_empty(&client->waiters)) {
-               waiter = shl_dlist_entry(client->waiters.next,
-                                        struct cdev_waiter, list);
-               waiter_free(waiter, -EPIPE);
-       }
-
-       uterm_input_unregister_cb(client->cdev->input, client_input_event,
-                                 client);
-       tsm_vte_unref(client->vte);
-       tsm_screen_unref(client->screen);
-       shl_ring_free(client->ring);
-}
-
-static int client_session_event(struct kmscon_session *s,
-                               struct kmscon_session_event *ev,
-                               void *data)
-{
-       struct cdev_client *client = data;
-
-       switch (ev->type) {
-       case KMSCON_SESSION_ACTIVATE:
-               return client_activate(client);
-       case KMSCON_SESSION_DEACTIVATE:
-               return client_deactivate(client);
-       case KMSCON_SESSION_UNREGISTER:
-               client_kill(client);
-               break;
-       }
-
-       return 0;
-}
-
-static int client_new(struct cdev_client **out, struct kmscon_cdev *cdev)
-{
-       struct cdev_client *client;
-       int ret;
-
-       client = malloc(sizeof(*client));
-       if (!client)
-               return -ENOMEM;
-       memset(client, 0, sizeof(*client));
-       client->cdev = cdev;
-       client->kdmode = KD_TEXT;
-       client->kbmode = K_UNICODE;
-       client->vtmode.mode = VT_AUTO;
-       shl_dlist_init(&client->readers);
-       shl_dlist_init(&client->waiters);
-
-       log_debug("new fake TTY client %p", client);
-
-       ret = shl_ring_new(&client->ring);
-       if (ret) {
-               log_error("cannot create ring buffer for new cdev client: %d",
-                         ret);
-               goto err_free;
-       }
-
-       /* TODO: Share the terminal-handling with the terminal-session. We
-        * currently just create the screen/vte objects here to get meaningful
-        * parsers. However, we should also correctly handled the terminal as is
-        * and draw it to the screen if in text-mode.
-        * This is nearly identical to the terminal-session so we should share
-        * the implementation between both instead of doing everything ourself
-        * here. */
-
-       ret = tsm_screen_new(&client->screen, log_llog, NULL);
-       if (ret) {
-               log_error("cannot create TSM screen for new cdev client: %d",
-                         ret);
-               goto err_ring;
-       }
-
-       ret = tsm_vte_new(&client->vte, client->screen, client_vte_event,
-                         client, log_llog, NULL);
-       if (ret) {
-               log_error("cannot create TSM VTE for new cdev client: %d",
-                         ret);
-               goto err_screen;
-       }
-
-       ret = uterm_input_register_cb(cdev->input, client_input_event, client);
-       if (ret) {
-               log_error("cannot register input callback for cdev client: %d",
-                         ret);
-               goto err_vte;
-       }
-
-       ret = kmscon_seat_register_session(cdev->seat, &client->s,
-                                          client_session_event, client);
-       if (ret) {
-               log_error("cannot register session for cdev client: %d", ret);
-               goto err_input;
-       }
-
-       shl_dlist_link(&cdev->clients, &client->list);
-       *out = client;
-       return 0;
-
-err_input:
-       uterm_input_unregister_cb(cdev->input, client_input_event, client);
-err_vte:
-       tsm_vte_unref(client->vte);
-err_screen:
-       tsm_screen_unref(client->screen);
-err_ring:
-       shl_ring_free(client->ring);
-err_free:
-       free(client);
-       return ret;
-}
-
-static void client_destroy(struct cdev_client *client)
-{
-       log_debug("destroy client %p", client);
-
-       if (!client->dead)
-               kmscon_session_unregister(client->s);
-       shl_dlist_unlink(&client->list);
-       free(client);
-}
-
-/* This must be called after each event dispatch round. It cleans up all
- * interrupted/killed readers. The readers cannot be released right away due to
- * heavy locking inside of FUSE. We have to delay these tasks and clean up after
- * each dispatch round. */
-static void client_cleanup(struct cdev_client *client)
-{
-       struct shl_dlist *i, *tmp;
-       struct cdev_reader *reader;
-       struct cdev_waiter *waiter;
-
-       shl_dlist_for_each_safe(i, tmp, &client->readers) {
-               reader = shl_dlist_entry(i, struct cdev_reader, list);
-               if (reader->killed)
-                       reader_free(reader, -ENOENT);
-       }
-
-       shl_dlist_for_each_safe(i, tmp, &client->waiters) {
-               waiter = shl_dlist_entry(i, struct cdev_waiter, list);
-               if (waiter->killed)
-                       waiter_free(waiter, -ENOENT);
-       }
-}
-
-/*
- * FUSE low-level ops
- * This implements all the file-system operations on the character-device. It is
- * important that we handle interrupts correctly (ENOENT) and never loose any
- * data. This is all single threaded as it is not performance critical at all.
- */
-
-static void ll_open(fuse_req_t req, struct fuse_file_info *fi)
-{
-       struct kmscon_cdev *cdev = fuse_req_userdata(req);
-       struct cdev_client *client;
-       int ret;
-
-       ret = client_new(&client, cdev);
-       if (ret)
-               goto err_out;
-
-       fi->fh = (long)client;
-       fi->nonseekable = 1;
-       fi->direct_io = 1;
-       ret = fuse_reply_open(req, fi);
-       if (ret < 0)
-               client_destroy(client);
-       else
-               kmscon_session_enable(client->s);
-
-       return;
-
-err_out:
-       fuse_reply_err(req, -ret);
-}
-
-static void ll_release(fuse_req_t req, struct fuse_file_info *fi)
-{
-       struct cdev_client *client = (void*)fi->fh;
-
-       if (!client) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       }
-
-       client_destroy(client);
-       fuse_reply_err(req, 0);
-}
-
-static void ll_read(fuse_req_t req, size_t size, off_t off,
-                   struct fuse_file_info *fi)
-{
-       struct cdev_client *client = (void*)fi->fh;
-       struct cdev_reader *reader;
-       const char *buf;
-       size_t len;
-       int ret;
-
-       if (!client) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       }
-
-       if (client->dead) {
-               fuse_reply_err(req, EPIPE);
-               return;
-       }
-
-       if (off != 0) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       }
-
-       if (!size) {
-               fuse_reply_buf(req, "", 0);
-               return;
-       }
-
-       /* TODO: use a proper intermediate buffer as this might return only
-        * partial data */
-       buf = shl_ring_peek(client->ring, &len, 0);
-       if (!len) {
-               if (fi->flags & O_NONBLOCK) {
-                       fuse_reply_err(req, EAGAIN);
-                       return;
-               }
-
-               ret = reader_new(&reader, client, req);
-               if (ret) {
-                       fuse_reply_err(req, -ret);
-                       return;
-               }
-
-               reader->len = size;
-               return;
-       }
-
-       if (len > size)
-               len = size;
-       ret = fuse_reply_buf(req, buf, len);
-       if (ret < 0)
-               return;
-       shl_ring_drop(client->ring, len);
-}
-
-static void ll_write(fuse_req_t req, const char *buf, size_t size, off_t off,
-                    struct fuse_file_info *fi)
-{
-       struct cdev_client *client = (void*)fi->fh;
-       int ret;
-
-       if (!client) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       }
-
-       if (client->dead) {
-               fuse_reply_err(req, EPIPE);
-               return;
-       }
-
-       ret = fuse_reply_write(req, size);
-       if (ret < 0)
-               return;
-       tsm_vte_input(client->vte, buf, size);
-}
-
-static void ll_poll(fuse_req_t req, struct fuse_file_info *fi,
-                   struct fuse_pollhandle *ph)
-{
-       struct cdev_client *client = (void*)fi->fh;
-       unsigned int flags;
-
-       if (!client) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       }
-
-       if (client->dead) {
-               if (ph)
-                       fuse_pollhandle_destroy(ph);
-               fuse_reply_poll(req, EPOLLHUP | EPOLLIN | EPOLLOUT |
-                                    EPOLLWRNORM | EPOLLRDNORM);
-               return;
-       }
-
-       if (client->ph)
-               fuse_pollhandle_destroy(client->ph);
-       client->ph = ph;
-
-       flags = EPOLLOUT | EPOLLWRNORM;
-       if (!shl_ring_is_empty(client->ring))
-               flags |= EPOLLIN | EPOLLRDNORM;
-
-       fuse_reply_poll(req, flags);
-}
-
-static void ioctl_TCFLSH(struct cdev_client *client, fuse_req_t req, int val)
-{
-       switch (val) {
-       case TCIFLUSH:
-               shl_ring_flush(client->ring);
-               break;
-       case TCIOFLUSH:
-               shl_ring_flush(client->ring);
-               /* fallthrough */
-       case TCOFLUSH:
-               /* nothing to do; we have no output queue */
-               break;
-       default:
-               fuse_reply_err(req, EINVAL);
-               return;
-       }
-
-       fuse_reply_ioctl(req, 0, NULL, 0);
-}
-
-static void ioctl_VT_ACTIVATE(struct cdev_client *client, fuse_req_t req,
-                             int val)
-{
-       unsigned short target, id;
-
-       id = client->cdev->minor;
-       target = val;
-
-       if (id == target) {
-               kmscon_session_schedule(client->s);
-       } else {
-               kmscon_seat_schedule(client->cdev->seat, target);
-       }
-
-       fuse_reply_ioctl(req, 0, NULL, 0);
-}
-
-static void ioctl_VT_WAITACTIVE(struct cdev_client *client, fuse_req_t req,
-                               int val)
-{
-       int ret;
-       struct cdev_waiter *waiter;
-
-       if (client->active) {
-               fuse_reply_ioctl(req, 0, NULL, 0);
-               return;
-       }
-
-       ret = waiter_new(&waiter, client, req);
-       if (ret) {
-               fuse_reply_err(req, -ret);
-               return;
-       }
-}
-
-static void ioctl_VT_GETSTATE(struct cdev_client *client, fuse_req_t req)
-{
-       struct vt_stat buf;
-       unsigned short id;
-
-       id = client->cdev->minor;
-       if (id == 0 || id == 1)
-               id = 2;
-
-       memset(&buf, 0, sizeof(buf));
-       buf.v_active = client->active ? id : 1;
-       buf.v_signal = 0;
-       buf.v_state = ~0;
-
-       fuse_reply_ioctl(req, 0, &buf, sizeof(buf));
-}
-
-static void ioctl_VT_GETMODE(struct cdev_client *client, fuse_req_t req)
-{
-       fuse_reply_ioctl(req, 0, &client->vtmode, sizeof(client->vtmode));
-}
-
-static void ioctl_VT_SETMODE(struct cdev_client *client, fuse_req_t req,
-                            struct vt_mode *mode)
-{
-       bool proc;
-
-       proc = mode->mode == VT_PROCESS;
-
-       /* TODO: implement "waitv" logic */
-       if (mode->waitv) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       }
-
-       if (mode->frsig)
-               log_debug("cdev client uses non-zero 'frsig' in VT_SETMODE: %d",
-                         mode->frsig);
-
-       if (mode->mode != VT_AUTO && mode->mode != VT_PROCESS) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       }
-
-       if (proc && (mode->relsig > SIGRTMAX || mode->acqsig > SIGRTMAX ||
-                    mode->relsig < 0 || mode->acqsig < 0)) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       }
-
-       memcpy(&client->vtmode, mode, sizeof(*mode));
-       memcpy(&client->user, fuse_req_ctx(req), sizeof(client->user));
-       fuse_reply_ioctl(req, 0, NULL, 0);
-}
-
-static void ioctl_VT_RELDISP(struct cdev_client *client, fuse_req_t req,
-                            int val)
-{
-       if (client->pending_switch) {
-               client->pending_switch = false;
-               if (val > 0) {
-                       client->active = false;
-                       kmscon_session_notify_deactivated(client->s);
-               }
-       }
-
-       fuse_reply_ioctl(req, 0, NULL, 0);
-}
-
-static void ioctl_KDGETMODE(struct cdev_client *client, fuse_req_t req)
-{
-       fuse_reply_ioctl(req, 0, &client->kdmode, sizeof(long));
-}
-
-static void ioctl_KDSETMODE(struct cdev_client *client, fuse_req_t req,
-                           long val)
-{
-       int ret;
-
-       switch (val) {
-       case KD_TEXT:
-               ret = kmscon_session_set_foreground(client->s);
-               if (ret) {
-                       fuse_reply_err(req, -ret);
-                       return;
-               }
-               client->kdmode = KD_TEXT;
-               break;
-       case KD_GRAPHICS:
-               ret = kmscon_session_set_background(client->s);
-               if (ret) {
-                       fuse_reply_err(req, -ret);
-                       return;
-               }
-               client->kdmode = KD_GRAPHICS;
-               break;
-       default:
-               fuse_reply_err(req, EINVAL);
-               return;
-       }
-
-       fuse_reply_ioctl(req, 0, NULL, 0);
-}
-
-static void ioctl_KDGKBMODE(struct cdev_client *client, fuse_req_t req)
-{
-       fuse_reply_ioctl(req, 0, &client->kbmode, sizeof(long));
-}
-
-static void ioctl_KDSKBMODE(struct cdev_client *client, fuse_req_t req,
-                           long val)
-{
-       switch (val) {
-       case K_RAW:
-       case K_UNICODE:
-       case K_OFF:
-               /* TODO: we handle K_RAW/K_UNICODE the same way as it is unclear
-                * what K_RAW should do? */
-               client->kbmode = val;
-               fuse_reply_ioctl(req, 0, NULL, 0);
-               break;
-       case K_XLATE:
-       case K_MEDIUMRAW:
-               /* TODO: what do these do? */
-               fuse_reply_err(req, EOPNOTSUPP);
-               break;
-       default:
-               fuse_reply_err(req, EINVAL);
-               break;
-       }
-}
-
-static bool ioctl_param(fuse_req_t req, void *arg, size_t in_want,
-                       size_t in_have, size_t out_want, size_t out_have)
-{
-       bool retry;
-       struct iovec in, out;
-       size_t in_num, out_num;
-
-       retry = false;
-       memset(&in, 0, sizeof(in));
-       in_num = 0;
-       memset(&out, 0, sizeof(out));
-       out_num = 0;
-
-       if (in_want) {
-               if (!in_have) {
-                       retry = true;
-               } else if (in_have < in_want) {
-                       fuse_reply_err(req, EFAULT);
-                       return true;
-               }
-
-               in.iov_base = arg;
-               in.iov_len = in_want;
-               in_num = 1;
-       }
-       if (out_want) {
-               if (!out_have) {
-                       retry = true;
-               } else if (out_have < out_want) {
-                       fuse_reply_err(req, EFAULT);
-                       return true;
-               }
-
-               out.iov_base = arg;
-               out.iov_len = out_want;
-               out_num = 1;
-       }
-
-       if (retry)
-               fuse_reply_ioctl_retry(req, in_num ? &in : NULL, in_num,
-                                      out_num ? &out : NULL, out_num);
-       return retry;
-}
-
-static void ll_ioctl(fuse_req_t req, int cmd, void *arg,
-                    struct fuse_file_info *fi, unsigned int flags,
-                    const void *in_buf, size_t in_bufsz, size_t out_bufsz)
-{
-       struct cdev_client *client = (void*)fi->fh;
-       bool compat;
-
-       if (!client) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       }
-
-       if (client->dead) {
-               fuse_reply_err(req, EPIPE);
-               return;
-       }
-
-       /* TODO: fix compat-ioctls */
-       compat = !!(flags & FUSE_IOCTL_COMPAT);
-       if (compat) {
-               fuse_reply_err(req, EOPNOTSUPP);
-               return;
-       }
-
-       switch (cmd) {
-       case TCFLSH:
-               if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
-                       return;
-               ioctl_TCFLSH(client, req, (long)arg);
-               break;
-       case VT_ACTIVATE:
-               if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
-                       return;
-               ioctl_VT_ACTIVATE(client, req, (long)arg);
-               break;
-       case VT_WAITACTIVE:
-               if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
-                       return;
-               ioctl_VT_WAITACTIVE(client, req, (long)arg);
-               break;
-       case VT_GETSTATE:
-               if (ioctl_param(req, arg, 0, in_bufsz,
-                               sizeof(struct vt_stat), out_bufsz))
-                       return;
-               ioctl_VT_GETSTATE(client, req);
-               break;
-       case VT_OPENQRY:
-               if (ioctl_param(req, arg, 0, in_bufsz,
-                               sizeof(int), out_bufsz))
-                       return;
-               fuse_reply_err(req, EOPNOTSUPP);
-               break;
-       case VT_GETMODE:
-               if (ioctl_param(req, arg, 0, in_bufsz,
-                               sizeof(struct vt_mode), out_bufsz))
-                       return;
-               ioctl_VT_GETMODE(client, req);
-               break;
-       case VT_SETMODE:
-               if (ioctl_param(req, arg, sizeof(struct vt_mode), in_bufsz,
-                               0, out_bufsz))
-                       return;
-               ioctl_VT_SETMODE(client, req, (struct vt_mode*)in_buf);
-               break;
-       case VT_RELDISP:
-               if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
-                       return;
-               ioctl_VT_RELDISP(client, req, (long)arg);
-               break;
-       case KDGETMODE:
-               if (ioctl_param(req, arg, 0, in_bufsz,
-                               sizeof(long), out_bufsz))
-                       return;
-               ioctl_KDGETMODE(client, req);
-               break;
-       case KDSETMODE:
-               if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
-                       return;
-               ioctl_KDSETMODE(client, req, (long)arg);
-               break;
-       case KDGKBMODE:
-               if (ioctl_param(req, arg, 0, in_bufsz,
-                               sizeof(long), out_bufsz))
-                       return;
-               ioctl_KDGKBMODE(client, req);
-               break;
-       case KDSKBMODE:
-               if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
-                       return;
-               ioctl_KDSKBMODE(client, req, (long)arg);
-               break;
-       case TCGETS:
-               if (ioctl_param(req, arg, 0, in_bufsz,
-                               sizeof(struct termios), out_bufsz))
-                       return;
-               fuse_reply_err(req, EOPNOTSUPP);
-               break;
-       case TCSETS:
-               if (ioctl_param(req, arg, sizeof(struct termios), in_bufsz,
-                               0, out_bufsz))
-                       return;
-               fuse_reply_err(req, EOPNOTSUPP);
-               break;
-       case TCSETSW:
-               if (ioctl_param(req, arg, sizeof(struct termios), in_bufsz,
-                               0, out_bufsz))
-                       return;
-               fuse_reply_err(req, EOPNOTSUPP);
-               break;
-       case TCSETSF:
-               if (ioctl_param(req, arg, sizeof(struct termios), in_bufsz,
-                               0, out_bufsz))
-                       return;
-               fuse_reply_err(req, EOPNOTSUPP);
-               break;
-       default:
-               fuse_reply_err(req, EINVAL);
-               break;
-       }
-}
-
-static void ll_destroy(void *data) {
-       struct kmscon_cdev *cdev = data;
-       struct cdev_client *client;
-
-       /* on unexpected shutdown this releases all currently open clients */
-       while (!shl_dlist_empty(&cdev->clients)) {
-               client = shl_dlist_entry(cdev->clients.next,
-                                        struct cdev_client, list);
-               client_destroy(client);
-       }
-}
-
-static const struct cuse_lowlevel_ops ll_ops = {
-       .init = NULL,
-       .destroy = ll_destroy,
-       .open = ll_open,
-       .release = ll_release,
-       .read = ll_read,
-       .write = ll_write,
-       .poll = ll_poll,
-       .ioctl = ll_ioctl,
-       .flush = NULL,
-       .fsync = NULL,
-};
-
-/*
- * FUSE channel ops
- * The connection to the FUSE kernel module is done via a file-descriptor.
- * Writing to it is synchronous, so the commands that we write are _immediately_
- * executed and return the result to us. Furthermore, write() is always
- * non-blocking and always succeeds so no reason to watch for EAGAIN.
- * Reading from the FD, on the other hand, may block if there is no data
- * available. However, we only read if the FD was signaled readable so we can
- * use a blocking FD to avoid any side-effects. The kernel maintains an
- * event-queue that we read from. So there may be pending events that we haven't
- * read but which affect the calls that we write to the kernel. This is
- * important when handling interrupts.
- * chan_receive() and chan_send() handle I/O to the kernel module and are hooked
- * up into a fuse-channel.
- */
-
-static int chan_receive(struct fuse_chan **chp, char *buf, size_t size)
-{
-       struct fuse_chan *ch = *chp;
-       struct kmscon_cdev *cdev = fuse_chan_data(ch);
-       struct fuse_session *se = fuse_chan_session(ch);
-       int fd = fuse_chan_fd(ch);
-       ssize_t res;
-
-       if (!se || !cdev)
-               return -EINVAL;
-
-       if (!size)
-               return 0;
-
-restart:
-       if (fuse_session_exited(se))
-               return 0;
-
-       res = read(fd, buf, size);
-       if (!res) {
-               /* EOF on cuse file */
-               log_error("fuse channel shut down");
-               fuse_session_exit(se);
-               return 0;
-       } else if (res < 0) {
-               /* ENOENT is returned if the operation was interrupted, it's
-                * safe to restart */
-               if (errno == ENOENT)
-                       goto restart;
-
-               /* ENODEV is returned if the FS got unmounted. This shouldn't
-                * occur for CUSE devices. Anyway, exit if this happens. */
-               if (errno == ENODEV) {
-                       fuse_session_exit(se);
-                       return 0;
-               }
-
-               /* EINTR and EAGAIN are simply forwarded to the caller. */
-               if (errno == EINTR || errno == EAGAIN)
-                       return -errno;
-
-               cdev->error = -errno;
-               log_error("fuse channel read error (%d): %m", errno);
-               fuse_session_exit(se);
-               return cdev->error;
-       }
-
-       return res;
-}
-
-static int chan_send(struct fuse_chan *ch, const struct iovec iov[],
-                    size_t count)
-{
-       struct kmscon_cdev *cdev = fuse_chan_data(ch);
-       struct fuse_session *se = fuse_chan_session(ch);
-       int fd = fuse_chan_fd(ch);
-       int ret;
-
-       if (!cdev || !se)
-               return -EINVAL;
-       if (!iov || !count)
-               return 0;
-
-       ret = writev(fd, iov, count);
-       if (ret < 0) {
-               /* ENOENT is returned on interruptions */
-               if (!fuse_session_exited(se) && errno != ENOENT) {
-                       cdev->error = -errno;
-                       log_error("cannot write to fuse-channel (%d): %m",
-                                 errno);
-                       fuse_session_exit(se);
-               }
-               return cdev->error;
-       }
-
-       return 0;
-}
-
-static const struct fuse_chan_ops chan_ops = {
-       .receive = chan_receive,
-       .send = chan_send,
-       .destroy = NULL,
-};
-
-/*
- * Character Device
- * This creates the high-level character-device driver and registers a
- * fake-session that is used to control each fake-VT session.
- * channel_event() is a callback when I/O is possible on the FUSE FD and
- * performs all outstanding tasks.
- * On error, the fake-session is unregistered and deleted which also destroys
- * _all_ client fake-sessions.
- */
-
-static void channel_event(struct ev_fd *fd, int mask, void *data)
-{
-       struct kmscon_cdev *cdev = data;
-       int ret;
-       struct fuse_buf buf;
-       struct fuse_chan *ch;
-       struct shl_dlist *i;
-       struct cdev_client *client;
-
-       if (mask & (EV_HUP | EV_ERR)) {
-               log_error("HUP/ERR on fuse channel");
-               cdev->error = -EPIPE;
-               kmscon_session_unregister(cdev->s);
-               return;
-       }
-
-       if (!(mask & EV_READABLE))
-               return;
-
-       memset(&buf, 0, sizeof(buf));
-       buf.mem = cdev->buf;
-       buf.size = cdev->bufsize;
-       ch = cdev->channel;
-       ret = fuse_session_receive_buf(cdev->session, &buf, &ch);
-       if (ret == -EINTR || ret == -EAGAIN) {
-               return;
-       } else if (ret < 0) {
-               log_error("fuse channel read error: %d", ret);
-               cdev->error = ret;
-               kmscon_session_unregister(cdev->s);
-               return;
-       }
-
-       fuse_session_process_buf(cdev->session, &buf, ch);
-       if (fuse_session_exited(cdev->session)) {
-               log_error("fuse session exited");
-               if (!cdev->error)
-                       cdev->error = -EFAULT;
-               kmscon_session_unregister(cdev->s);
-               return;
-       }
-
-       /* Readers can get interrupted asynchronously. Due to heavy locking
-        * inside of FUSE, we cannot release them right away. So cleanup all
-        * killed readers after we processed all buffers. */
-       shl_dlist_for_each(i, &cdev->clients) {
-               client = shl_dlist_entry(i, struct cdev_client, list);
-               client_cleanup(client);
-       }
-}
-
-static int kmscon_cdev_init(struct kmscon_cdev *cdev)
-{
-       static const char prefix[] = "DEVNAME=";
-       static const char fname[] = "/dev/cuse";
-       int ret, id;
-       size_t bufsize;
-       struct cuse_info ci;
-       const char *dev_info_argv[1];
-       char *name;
-
-       /* TODO: libfuse makes sure that fd 0, 1 and 2 are available as standard
-        * streams, otherwise they fail. This is awkward and we should check
-        * whether this is really needed and _why_?
-        * If it is needed, fix upstream to stop that crazy! */
-
-       shl_dlist_init(&cdev->clients);
-
-       ret = asprintf(&name, "%sttyF%s", prefix,
-                      kmscon_seat_get_name(cdev->seat));
-       if (ret <= 0) {
-               log_error("cannot allocate memory for fuse-devname");
-               return -ENOMEM;
-       }
-
-       log_info("initializing fake VT TTY device /dev/%s",
-                &name[sizeof(prefix) - 1]);
-
-       id = cdev_allocate_id();
-       if (id < 0) {
-               log_error("cannot allocate new cdev TTY id: %d", id);
-               free(name);
-               return id;
-       }
-       cdev->minor = id;
-
-       dev_info_argv[0] = name;
-       memset(&ci, 0, sizeof(ci));
-       ci.dev_major = TTY_MAJOR;
-       ci.dev_minor = cdev->minor;
-       ci.dev_info_argc = 1;
-       ci.dev_info_argv = dev_info_argv;
-       ci.flags = CUSE_UNRESTRICTED_IOCTL;
-
-       cdev->session = cuse_lowlevel_new(NULL, &ci, &ll_ops, cdev);
-       free(name);
-
-       if (!cdev->session) {
-               log_error("cannot create fuse-ll session");
-               return -ENOMEM;
-       }
-
-       cdev->fd = open(fname, O_RDWR | O_CLOEXEC);
-       if (cdev->fd < 0) {
-               log_error("cannot open %s (%d): %m", fname, errno);
-               ret = -EFAULT;
-               goto err_session;
-       }
-
-       bufsize = getpagesize() + 0x1000;
-       if (bufsize < 0x21000)
-               bufsize = 0x21000;
-
-       cdev->bufsize = bufsize;
-       cdev->buf = malloc(bufsize);
-       if (!cdev->buf) {
-               log_error("cannot allocate memory for buffer of size %zu",
-                         bufsize);
-               ret = -ENOMEM;
-               goto err_fd;
-       }
-
-       /* Argh! libfuse does not use "const" for the "chan_ops" pointer so we
-        * actually have to cast it. Their implementation does not write into it
-        * so we can safely use a constant storage for it.
-        * TODO: Fix libfuse upstream! */
-       cdev->channel = fuse_chan_new((void*)&chan_ops, cdev->fd, bufsize,
-                                     cdev);
-       if (!cdev->channel) {
-               log_error("cannot allocate fuse-channel");
-               ret = -ENOMEM;
-               goto err_buf;
-       }
-
-       ret = ev_eloop_new_fd(cdev->eloop, &cdev->efd, cdev->fd, EV_READABLE,
-                             channel_event, cdev);
-       if (ret) {
-               log_error("cannot create fd-object in eloop: %d", ret);
-               goto err_chan;
-       }
-
-       fuse_session_add_chan(cdev->session, cdev->channel);
-       return 0;
-
-err_chan:
-       fuse_chan_destroy(cdev->channel);
-err_buf:
-       free(cdev->buf);
-err_fd:
-       close(cdev->fd);
-err_session:
-       fuse_session_destroy(cdev->session);
-       return ret;
-}
-
-void kmscon_cdev_destroy(struct kmscon_cdev *cdev)
-{
-       if (!cdev)
-               return;
-
-       if (cdev->error)
-               log_warning("cdev module failed with error %d (maybe another kmscon process is already running?)",
-                           cdev->error);
-
-       fuse_session_destroy(cdev->session);
-       ev_eloop_rm_fd(cdev->efd);
-       free(cdev->buf);
-       close(cdev->fd);
-}
-
-static int session_event(struct kmscon_session *session,
-                        struct kmscon_session_event *ev, void *data)
-{
-       struct kmscon_cdev *cdev = data;
-
-       switch (ev->type) {
-       case KMSCON_SESSION_UNREGISTER:
-               log_debug("destroy cdev session");
-               kmscon_cdev_destroy(cdev);
-               free(cdev);
-               break;
-       }
-
-       return 0;
-}
-
-int kmscon_cdev_register(struct kmscon_session **out,
-                        struct kmscon_seat *seat)
-{
-       struct kmscon_cdev *cdev;
-       int ret;
-
-       if (!out || !seat)
-               return -EINVAL;
-
-       cdev = malloc(sizeof(*cdev));
-       if (!cdev)
-               return -ENOMEM;
-       memset(cdev, 0, sizeof(*cdev));
-       cdev->seat = seat;
-       cdev->eloop = kmscon_seat_get_eloop(seat);
-       cdev->input = kmscon_seat_get_input(seat);
-
-       ret = kmscon_cdev_init(cdev);
-       if (ret)
-               goto err_free;
-
-       ret = kmscon_seat_register_session(seat, &cdev->s, session_event, cdev);
-       if (ret) {
-               log_error("cannot register session for cdev: %d", ret);
-               goto err_cdev;
-       }
-
-       *out = cdev->s;
-       return 0;
-
-err_cdev:
-       kmscon_cdev_destroy(cdev);
-err_free:
-       free(cdev);
-       return ret;
-}
diff --git a/src/kmscon_cdev.h b/src/kmscon_cdev.h
deleted file mode 100644 (file)
index 4586481..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * kmscon - Character-Device Session
- *
- * Copyright (c) 2012 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.
- */
-
-/*
- * Character-Device Session
- */
-
-#ifndef KMSCON_CDEV_H
-#define KMSCON_CDEV_H
-
-#include <errno.h>
-#include <stdlib.h>
-#include "kmscon_seat.h"
-
-#ifdef BUILD_ENABLE_SESSION_CDEV
-
-int kmscon_cdev_register(struct kmscon_session **out,
-                        struct kmscon_seat *seat);
-
-#else /* !BUILD_ENABLE_SESSION_CDEV */
-
-static inline int kmscon_cdev_register(struct kmscon_session **out,
-                                      struct kmscon_seat *seat)
-{
-       return -EOPNOTSUPP;
-}
-
-#endif /* BUILD_ENABLE_SESSION_CDEV */
-
-#endif /* KMSCON_CDEV_H */
index 27df14a..2173b44 100644 (file)
@@ -79,7 +79,6 @@ static void print_help()
                "\t    --session-max <max>         [50]  Maximum number of sessions\n"
                "\t    --session-control           [off] Allow keyboard session-control\n"
                "\t    --terminal-session          [on]  Enable terminal session\n"
-               "\t    --cdev-session              [off] Enable kernel VT emulation session\n"
                "\n"
                "Terminal Options:\n"
                "\t-l, --login                 [/bin/login -p]\n"
@@ -483,17 +482,11 @@ static int aftercheck_listen(struct conf_option *opt, int argc, char **argv,
                             int idx)
 {
        struct kmscon_conf_t *conf = KMSCON_CONF_FROM_FIELD(opt->mem, listen);
-       int ret = -EFAULT;
 
        if (conf->listen)
                return 0;
 
-       if (conf->cdev_session)
-               log_error("you can use --cdev-session only in combination with --listen");
-       else
-               ret = 0;
-
-       return ret;
+       return 0;
 }
 
 /*
@@ -569,7 +562,6 @@ int kmscon_conf_new(struct conf_ctx **out)
                CONF_OPTION_UINT(0, "session-max", &conf->session_max, 50),
                CONF_OPTION_BOOL(0, "session-control", &conf->session_control, false),
                CONF_OPTION_BOOL(0, "terminal-session", &conf->terminal_session, true),
-               CONF_OPTION_BOOL(0, "cdev-session", &conf->cdev_session, false),
 
                /* Terminal Options */
                CONF_OPTION(0, 'l', "login", &conf_login, aftercheck_login, NULL, file_login, &conf->login, false),
index 113ea1a..afca331 100644 (file)
@@ -78,8 +78,6 @@ struct kmscon_conf_t {
        bool session_control;
        /* run terminal session */
        bool terminal_session;
-       /* cdev session */
-       bool cdev_session;
 
        /* Terminal Options */
        /* custom login process */
index 5efa17b..2bde083 100644 (file)
@@ -34,7 +34,6 @@
 #include <string.h>
 #include "conf.h"
 #include "eloop.h"
-#include "kmscon_cdev.h"
 #include "kmscon_conf.h"
 #include "kmscon_dummy.h"
 #include "kmscon_seat.h"
@@ -824,14 +823,6 @@ void kmscon_seat_startup(struct kmscon_seat *seat)
                        kmscon_session_enable(s);
        }
 
-       if (seat->conf->cdev_session) {
-               ret = kmscon_cdev_register(&s, seat);
-               if (ret == -EOPNOTSUPP)
-                       log_notice("cdev sessions not compiled in");
-               else if (ret)
-                       log_error("cannot register cdev session");
-       }
-
        if (seat->conf->switchvt ||
            uterm_vt_get_type(seat->vt) == UTERM_VT_FAKE)
                uterm_vt_activate(seat->vt);