Remove libuvt
authorDavid Herrmann <dh.herrmann@gmail.com>
Tue, 29 Oct 2013 09:01:13 +0000 (10:01 +0100)
committerDavid Herrmann <dh.herrmann@gmail.com>
Tue, 29 Oct 2013 09:01:13 +0000 (10:01 +0100)
uvt is not used inside of kmscon. Moved into a separate library if
some-one is interested.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
12 files changed:
.gitignore
Makefile.am
README
configure.ac
docs/pc/libuvt.pc.in [deleted file]
docs/sym/libuvt.sym [deleted file]
src/uvt.h [deleted file]
src/uvt_cdev.c [deleted file]
src/uvt_client.c [deleted file]
src/uvt_ctx.c [deleted file]
src/uvt_internal.h [deleted file]
src/uvt_tty_null.c [deleted file]

index 912ec5b..1943279 100644 (file)
@@ -19,7 +19,6 @@ configure
 *.tar.xz
 libtool
 libeloop.pc
-libuvt.pc
 libuterm.pc
 m4/
 stamp-*
index 52a14eb..11c2d30 100644 (file)
@@ -11,10 +11,6 @@ LIBELOOP_CURRENT = 1
 LIBELOOP_REVISION = 0
 LIBELOOP_AGE = 0
 
-LIBUVT_CURRENT = 1
-LIBUVT_REVISION = 0
-LIBUVT_AGE = 0
-
 LIBUTERM_CURRENT = 1
 LIBUTERM_REVISION = 0
 LIBUTERM_AGE = 0
@@ -40,10 +36,8 @@ EXTRA_DIST = \
        docs/kmscon.service \
        docs/kmsconvt@.service \
        docs/pc/libeloop.pc.in \
-       docs/pc/libuvt.pc.in \
        docs/pc/libuterm.pc.in \
        docs/sym/libeloop.sym \
-       docs/sym/libuvt.sym \
        docs/sym/libuterm.sym
 CLEANFILES =
 pkgconfigdir = $(libdir)/pkgconfig
@@ -237,39 +231,6 @@ libeloop_la_LIBADD += $(DBUS_LIBS)
 endif
 
 #
-# libuvt
-# Implementation of Virtual Terminals in user-space with the help of CUSE/FUSE
-# so we can provide character-device drivers in user-space. Aims to be 100%
-# compatible but also provides many other use-cases.
-#
-
-if BUILD_ENABLE_UVT
-lib_LTLIBRARIES += libuvt.la
-include_HEADERS += src/uvt.h
-pkgconfig_DATA += docs/pc/libuvt.pc
-endif
-
-libuvt_la_SOURCES = \
-       src/uvt.h \
-       src/uvt_internal.h \
-       src/uvt_ctx.c \
-       src/uvt_cdev.c \
-       src/uvt_client.c \
-       src/uvt_tty_null.c
-libuvt_la_CPPFLAGS = \
-       $(AM_CPPFLAGS) \
-       $(FUSE_CFLAGS) \
-       -DFUSE_USE_VERSION=29
-libuvt_la_LIBADD = \
-       $(FUSE_LIBS) \
-       libshl.la
-EXTRA_libuvt_la_DEPENDENCIES = ${top_srcdir}/docs/sym/libuvt.sym
-libuvt_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       -version-info $(LIBUVT_CURRENT):$(LIBUVT_REVISION):$(LIBUVT_AGE) \
-       -Wl,--version-script="$(top_srcdir)/docs/sym/libuvt.sym"
-
-#
 # libuterm
 # The uterm library provides helpers to create terminals in user-space. They
 # are not limited to text-based terminals but rather provide graphics contexts
diff --git a/README b/README
index 7e68498..244e817 100644 (file)
--- a/README
+++ b/README
@@ -94,7 +94,6 @@ Released tarballs can be found at:
     --enable-kmscon: Build kmscon application [default: on]
     --enable-eloop: Build eloop event loop library [default: off]
     --enable-uterm: Build uterm library [default: off]
-    --enable-uvt: Build UVT library [default: off]
 
 == Running ==
 
index 6c975c5..01ae4fc 100644 (file)
@@ -100,11 +100,6 @@ PKG_CHECK_MODULES([PANGO], [pango pangoft2],
 AC_SUBST(PANGO_CFLAGS)
 AC_SUBST(PANGO_LIBS)
 
-PKG_CHECK_MODULES([FUSE], [fuse >= 2.9.0],
-                  [have_fuse=yes], [have_fuse=no])
-AC_SUBST(FUSE_CFLAGS)
-AC_SUBST(FUSE_LIBS)
-
 PKG_CHECK_MODULES([CAIRO], [cairo],
                   [have_cairo=yes], [have_cairo=no])
 AC_SUBST(CAIRO_CFLAGS)
@@ -149,18 +144,6 @@ elif test "x$enable_eloop" = "x" ; then
 fi
 AC_MSG_RESULT([$enable_eloop])
 
-# UVT
-AC_MSG_CHECKING([whether user wants UVT])
-AC_ARG_ENABLE([uvt],
-              [AS_HELP_STRING([--enable-uvt],
-                              [build uvt library])])
-if test "x$enable_all" = "xyes" ; then
-        enable_uvt="yes"
-elif test "x$enable_uvt" = "x" ; then
-        enable_uvt="no (default)"
-fi
-AC_MSG_RESULT([$enable_uvt])
-
 # uterm
 AC_MSG_CHECKING([whether user wants uterm])
 AC_ARG_ENABLE([uterm],
@@ -448,25 +431,6 @@ else
         eloop_missing="enable-eloop"
 fi
 
-# UVT
-uvt_avail=no
-uvt_missing=""
-if test ! "x$enable_uvt" = "xno" ; then
-        uvt_avail=yes
-        if test "x$have_fuse" = "xno" ; then
-                uvt_avail=no
-                uvt_missing="fuse"
-        fi
-
-        if test "x$uvt_avail" = "xno" ; then
-                if test "x$enable_uvt" = "xyes" ; then
-                        AC_ERROR([missing for UVT: $uvt_missing])
-                fi
-        fi
-else
-        uvt_missing="enable-uvt"
-fi
-
 # video fbdev
 video_fbdev_avail=no
 video_fbdev_missing=""
@@ -855,14 +819,6 @@ if test "x$video_fbdev_avail" = "xyes" ; then
         fi
 fi
 
-# UVT
-uvt_enabled=no
-if test "x$uvt_avail" = "xyes" ; then
-        if test "x${enable_uvt% *}" = "xyes" ; then
-                uvt_enabled=yes
-        fi
-fi
-
 # eloop
 eloop_enabled=no
 if test "x$eloop_avail" = "xyes" ; then
@@ -925,10 +881,6 @@ AM_CONDITIONAL([BUILD_ENABLE_ELOOP_DBUS],
 AM_CONDITIONAL([BUILD_ENABLE_ELOOP],
                [test "x$eloop_enabled" = "xyes"])
 
-# UVT
-AM_CONDITIONAL([BUILD_ENABLE_UVT],
-               [test "x$uvt_enabled" = "xyes"])
-
 # video fbdev
 if test "x$video_fbdev_enabled" = "xyes" ; then
         AC_DEFINE([BUILD_ENABLE_VIDEO_FBDEV], [1],
@@ -1117,7 +1069,6 @@ fi
 
 AC_CONFIG_FILES([Makefile
                  docs/pc/libeloop.pc
-                 docs/pc/libuvt.pc
                  docs/pc/libuterm.pc])
 AC_OUTPUT
 
@@ -1137,7 +1088,6 @@ AC_MSG_NOTICE([Build configuration:
   Applications and Libraries:
                kmscon: $kmscon_enabled ($kmscon_avail: $kmscon_missing)
                 uterm: $uterm_enabled ($uterm_avail: $uterm_missing)
-                  uvt: $uvt_enabled ($uvt_avail: $uvt_missing)
                 eloop: $eloop_enabled ($eloop_avail: $eloop_missing)
 
   Miscellaneous Options:
diff --git a/docs/pc/libuvt.pc.in b/docs/pc/libuvt.pc.in
deleted file mode 100644 (file)
index e72d50c..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: uvt
-Description: User-space Virtual Terminal Implementation
-URL: @PACKAGE_URL@
-Version: @PACKAGE_VERSION@
-Libs: -L${libdir} -luvt
-Cflags: -I${includedir}
diff --git a/docs/sym/libuvt.sym b/docs/sym/libuvt.sym
deleted file mode 100644 (file)
index 2ecee21..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/***
- * UVT - Userspace Virtual Terminals
- *
- * 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.
- ***/
-
-LIBUVT_1 {
-global:
-       uvt_ctx_new;
-       uvt_ctx_ref;
-       uvt_ctx_unref;
-       uvt_ctx_get_fd;
-       uvt_ctx_dispatch;
-       uvt_ctx_get_major;
-       uvt_ctx_new_minor;
-       uvt_ctx_free_minor;
-
-       uvt_cdev_new;
-       uvt_cdev_ref;
-       uvt_cdev_unref;
-       uvt_cdev_register_cb;
-       uvt_cdev_unregister_cb;
-
-       uvt_client_ref;
-       uvt_client_unref;
-       uvt_client_set_vt;
-       uvt_client_kill;
-       uvt_client_is_dead;
-local:
-       *;
-};
diff --git a/src/uvt.h b/src/uvt.h
deleted file mode 100644 (file)
index e767005..0000000
--- a/src/uvt.h
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * UVT - Userspace Virtual Terminals
- *
- * Copyright (c) 2011-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.
- */
-
-/*
- * Userspace Virtual Terminals
- * Virtual terminals were historically implemented in the kernel via a
- * character-device. This layer provides a user-space implementation via
- * CUSE/FUSE that can be used to provide the same API from user-space.
- */
-
-#ifndef UVT_H
-#define UVT_H
-
-#include <inttypes.h>
-#include <linux/kd.h>
-#include <linux/vt.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <termio.h>
-#include <termios.h>
-
-/* UVT types */
-
-struct uvt_client;
-struct uvt_cdev;
-struct uvt_ctx;
-
-/* TTYs */
-
-enum uvt_tty_event_type {
-       UVT_TTY_HUP             = 0x01,
-       UVT_TTY_READ            = 0x02,
-       UVT_TTY_WRITE           = 0x04,
-};
-
-struct uvt_tty_event {
-       unsigned int type;
-};
-
-typedef void (*uvt_tty_cb) (void *tty, struct uvt_tty_event *ev, void *data);
-
-struct uvt_tty_ops {
-       void (*ref) (void *data);
-       void (*unref) (void *data);
-       int (*register_cb) (void *data, uvt_tty_cb cb, void *cb_data);
-       void (*unregister_cb) (void *data, uvt_tty_cb cb, void *cb_data);
-
-       int (*read) (void *data, uint8_t *mem, size_t len);
-       int (*write) (void *data, const uint8_t *mem, size_t len);
-       unsigned int (*poll) (void *data);
-};
-
-/* virtual terminals */
-
-enum uvt_vt_event_type {
-       UVT_VT_HUP              = 0x01,
-       UVT_VT_TTY              = 0x02,
-};
-
-struct uvt_vt_event {
-       unsigned int type;
-
-       union {
-               struct uvt_tty_event tty;
-       };
-};
-
-typedef void (*uvt_vt_cb) (void *vt, struct uvt_vt_event *ev, void *data);
-
-struct uvt_vt_ops {
-       void (*ref) (void *data);
-       void (*unref) (void *data);
-       int (*register_cb) (void *data, uvt_vt_cb cb, void *cb_data);
-       void (*unregister_cb) (void *data, uvt_vt_cb cb, void *cb_data);
-
-       int (*read) (void *data, uint8_t *mem, size_t len);
-       int (*write) (void *data, const uint8_t *mem, size_t len);
-       unsigned int (*poll) (void *data);
-
-       /* TTY ioctls */
-       int (*ioctl_TCFLSH) (void *data, unsigned long arg);
-
-       /* VT ioctls */
-       int (*ioctl_VT_ACTIVATE) (void *data, unsigned long arg);
-       int (*ioctl_VT_WAITACTIVE) (void *data, unsigned long arg);
-       int (*ioctl_VT_GETSTATE) (void *data, struct vt_stat *arg);
-       int (*ioctl_VT_OPENQRY) (void *data, unsigned int *arg);
-       int (*ioctl_VT_GETMODE) (void *data, struct vt_mode *arg);
-       int (*ioctl_VT_SETMODE) (void *data, const struct vt_mode *arg,
-                                pid_t pid);
-       int (*ioctl_VT_RELDISP) (void *data, unsigned long arg);
-       int (*ioctl_KDGETMODE) (void *data, unsigned int *arg);
-       int (*ioctl_KDSETMODE) (void *data, unsigned int arg);
-       int (*ioctl_KDGKBMODE) (void *data, unsigned int *arg);
-       int (*ioctl_KDSKBMODE) (void *data, unsigned int arg);
-
-/*
-   Complete list of all ioctls that the kernel supports. The internal handler
-   returns -EOPNOTSUPP for all of them as they haven't been implemented, yet.
-   We need to check if they are actually required or whether it's not worth the
-   effort.
-   Please implement them only if you know a client that requires them. Also
-   consider implementing them as a no-op if the client doesn't depend on the
-   call to actually do something. We want to keep the actual callbacks at a
-   minimum.
-
-   TTY ioctls
-
-       int (*ioctl_TIOCPKT) (void *data, ...);
-       int (*ioctl_TCXONC) (void *data, ...);
-       int (*ioctl_TCGETS) (void *data, struct termios *arg);
-       int (*ioctl_TCSETS) (void *data, const struct termios *arg);
-       int (*ioctl_TCSETSF) (void *data, const struct termios *arg);
-       int (*ioctl_TCSETSW) (void *data, const struct termios *arg);
-       int (*ioctl_TCGETA) (void *data, ...);
-       int (*ioctl_TCSETA) (void *data, ...);
-       int (*ioctl_TCSETAF) (void *data, ...);
-       int (*ioctl_TCSETAW) (void *data, ...);
-       int (*ioctl_TIOCGLCKTRMIOS) (void *data, ...);
-       int (*ioctl_TIOCSLCKTRMIOS) (void *data, ...);
-       int (*ioctl_TCGETX) (void *data, ...);
-       int (*ioctl_TCSETX) (void *data, ...);
-       int (*ioctl_TCSETXW) (void *data, ...);
-       int (*ioctl_TCSETXF) (void *data, ...);
-       int (*ioctl_TIOCGSOFTCAR) (void *data, ...);
-       int (*ioctl_TIOCSSOFTCAR) (void *data, ...);
-
-   VT ioctls
-
-       int (*ioctl_TIOCLINUX) (void *data, ...);
-       int (*ioctl_KIOCSOUND) (void *data, ...);
-       int (*ioctl_KDMKTONE) (void *data, ...);
-       int (*ioctl_KDGKBTYPE) (void *data, char *arg);
-       int (*ioctl_KDADDIO) (void *data, unsigned long arg);
-       int (*ioctl_KDDELIO) (void *data, unsigned long arg);
-       int (*ioctl_KDENABIO) (void *data);
-       int (*ioctl_KDDISABIO) (void *data);
-       int (*ioctl_KDKBDREP) (void *data, struct kbd_repeat *arg);
-       int (*ioctl_KDMAPDISP) (void *data);
-       int (*ioctl_KDUNMAPDISP) (void *data);
-       int (*ioctl_KDGKBMETA) (void *data, long *arg);
-       int (*ioctl_KDSKBMETA) (void *data, long arg);
-       int (*ioctl_KDGETKEYCODE) (void *data, ...);
-       int (*ioctl_KDSETKEYCODE) (void *data, ...);
-       int (*ioctl_KDGKBENT) (void *data, ...);
-       int (*ioctl_KDSKBENT) (void *data, ...);
-       int (*ioctl_KDGKBSENT) (void *data, ...);
-       int (*ioctl_KDSKBSENT) (void *data, ...);
-       int (*ioctl_KDGKBDIACR) (void *data, ...);
-       int (*ioctl_KDSKBDIACR) (void *data, ...);
-       int (*ioctl_KDGKBDIACRUC) (void *data, ...);
-       int (*ioctl_KDSKBDIACRUC) (void *data, ...);
-       int (*ioctl_KDGETLED) (void *data, char *arg);
-       int (*ioctl_KDSETLED) (void *data, long arg);
-       int (*ioctl_KDGKBLED) (void *data, char *arg);
-       int (*ioctl_KDSKBLED) (void *data, long arg);
-       int (*ioctl_KDSIGACCEPT) (void *data, ...);
-       int (*ioctl_VT_SETACTIVATE) (void *data, ...);
-       int (*ioctl_VT_DISALLOCATE) (void *data, ...);
-       int (*ioctl_VT_RESIZE) (void *data, ...);
-       int (*ioctl_VT_RESIZEX) (void *data, ...);
-       int (*ioctl_GIO_FONT) (void *data, ...);
-       int (*ioctl_PIO_FONT) (void *data, ...);
-       int (*ioctl_GIO_CMAP) (void *data, ...);
-       int (*ioctl_PIO_CMAP) (void *data, ...);
-       int (*ioctl_GIO_FONTX) (void *data, ...);
-       int (*ioctl_PIO_FONTX) (void *data, ...);
-       int (*ioctl_PIO_FONTRESET) (void *data, ...);
-       int (*ioctl_KDFONTOP) (void *data, ...);
-       int (*ioctl_GIO_SCRNMAP) (void *data, ...);
-       int (*ioctl_PIO_SCRNMAP) (void *data, ...);
-       int (*ioctl_GIO_UNISCRNMAP) (void *data, ...);
-       int (*ioctl_PIO_UNISCRNMAP) (void *data, ...);
-       int (*ioctl_PIO_UNIMAPCLR) (void *data, ...);
-       int (*ioctl_GIO_UNIMAP) (void *data, ...);
-       int (*ioctl_PIO_UNIMAP) (void *data, ...);
-       int (*ioctl_VT_LOCKSWITCH) (void *data);
-       int (*ioctl_VT_UNLOCKSWITCH) (void *data);
-       int (*ioctl_VT_GETHIFONTMASK) (void *data, ...);
-       int (*ioctl_VT_WAITEVENT) (void *data, ...);
-*/
-};
-
-/* client sessions */
-
-void uvt_client_ref(struct uvt_client *client);
-void uvt_client_unref(struct uvt_client *client);
-
-int uvt_client_set_vt(struct uvt_client *client, const struct uvt_vt_ops *vt,
-                     void *vt_data);
-void uvt_client_kill(struct uvt_client *client);
-bool uvt_client_is_dead(struct uvt_client *client);
-
-/* character devices */
-
-enum uvt_cdev_event_type {
-       UVT_CDEV_HUP,
-       UVT_CDEV_OPEN,
-};
-
-struct uvt_cdev_event {
-       unsigned int type;
-
-       union {
-               struct uvt_client *client;
-       };
-};
-
-typedef void (*uvt_cdev_cb) (struct uvt_cdev *cdev,
-                            struct uvt_cdev_event *ev,
-                            void *data);
-
-int uvt_cdev_new(struct uvt_cdev **out, struct uvt_ctx *ctx,
-                const char *name, unsigned int major, unsigned int minor);
-void uvt_cdev_ref(struct uvt_cdev *cdev);
-void uvt_cdev_unref(struct uvt_cdev *cdev);
-
-int uvt_cdev_register_cb(struct uvt_cdev *cdev, uvt_cdev_cb cb, void *data);
-void uvt_cdev_unregister_cb(struct uvt_cdev *cdev, uvt_cdev_cb cb, void *data);
-
-/* contexts */
-
-typedef void (*uvt_log_t) (void *data,
-                          const char *file,
-                          int line,
-                          const char *func,
-                          const char *subs,
-                          unsigned int sev,
-                          const char *format,
-                          va_list args);
-
-int uvt_ctx_new(struct uvt_ctx **out, uvt_log_t log, void *log_data);
-void uvt_ctx_ref(struct uvt_ctx *ctx);
-void uvt_ctx_unref(struct uvt_ctx *ctx);
-
-int uvt_ctx_get_fd(struct uvt_ctx *ctx);
-void uvt_ctx_dispatch(struct uvt_ctx *ctx);
-
-unsigned int uvt_ctx_get_major(struct uvt_ctx *ctx);
-int uvt_ctx_new_minor(struct uvt_ctx *ctx, unsigned int *out);
-void uvt_ctx_free_minor(struct uvt_ctx *ctx, unsigned int minor);
-
-/* pty tty implementation */
-
-struct uvt_tty_null;
-extern const struct uvt_tty_ops uvt_tty_null_ops;
-
-int uvt_tty_null_new(struct uvt_tty_null **out, struct uvt_ctx *ctx);
-void uvt_tty_null_ref(struct uvt_tty_null *tty);
-void uvt_tty_null_unref(struct uvt_tty_null *tty);
-
-#endif /* UVT_H */
diff --git a/src/uvt_cdev.c b/src/uvt_cdev.c
deleted file mode 100644 (file)
index 291857b..0000000
+++ /dev/null
@@ -1,485 +0,0 @@
-/*
- * UVT - Userspace Virtual Terminals
- *
- * Copyright (c) 2011-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.
- */
-
-/*
- * Character Devices
- * This implements a VT character device entry point via the CUSE API. It does
- * not implement the VT API on top of the character-device (cdev) but only
- * provides the entry point. It is up to the user to bind open-files to VT and
- * client objects.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/major.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include "shl_dlist.h"
-#include "shl_hook.h"
-#include "shl_llog.h"
-#include "uvt.h"
-#include "uvt_internal.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 LLOG_SUBSYSTEM "uvt_cdev"
-
-/*
- * 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.
- * We simply dispatch each call to uvt_client as this implements all the
- * client-session related operations.
- */
-
-static void ll_open(fuse_req_t req, struct fuse_file_info *fi)
-{
-       struct uvt_cdev *cdev = fuse_req_userdata(req);
-       struct uvt_client *client;
-       struct uvt_cdev_event ev;
-       int ret;
-
-       ret = uvt_client_ll_open(&client, cdev, req, fi);
-       if (ret)
-               return;
-
-       memset(&ev, 0, sizeof(ev));
-       ev.type = UVT_CDEV_OPEN;
-       ev.client = client;
-       shl_hook_call(cdev->hook, cdev, &ev);
-}
-
-static void ll_destroy(void *data) {
-       struct uvt_cdev *cdev = data;
-       struct uvt_client *client;
-
-       /* on unexpected shutdown this kills all open clients */
-       while (!shl_dlist_empty(&cdev->clients)) {
-               client = shl_dlist_entry(cdev->clients.next,
-                                        struct uvt_client, list);
-               uvt_client_kill(client);
-               uvt_client_unref(client);
-       }
-}
-
-static const struct cuse_lowlevel_ops ll_ops = {
-       .init = NULL,
-       .destroy = ll_destroy,
-       .open = ll_open,
-       .release = uvt_client_ll_release,
-       .read = uvt_client_ll_read,
-       .write = uvt_client_ll_write,
-       .poll = uvt_client_ll_poll,
-       .ioctl = uvt_client_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 so we mark it as O_NONBLOCK. 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 uvt_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 */
-               llog_error(cdev, "fuse channel shut down on cdev %p", cdev);
-               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) {
-                       llog_error(cdev, "fuse channel unmounted on cdev %p",
-                                  cdev);
-                       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;
-               llog_error(cdev, "fuse channel read error on cdev %p (%d): %m",
-                          cdev, 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 uvt_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 interrupts */
-               if (!fuse_session_exited(se) && errno != ENOENT) {
-                       cdev->error = -errno;
-                       llog_error(cdev, "cannot write to fuse-channel on cdev %p (%d): %m",
-                                  cdev, 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 character file.
- * 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. This also stops all
- * client sessions, obviously.
- */
-
-static void uvt_cdev_hup(struct uvt_cdev *cdev, int error)
-{
-       struct uvt_cdev_event ev;
-
-       ev_eloop_rm_fd(cdev->efd);
-       cdev->efd = NULL;
-       cdev->error = error;
-
-       memset(&ev, 0, sizeof(ev));
-       ev.type = UVT_CDEV_HUP;
-
-       shl_hook_call(cdev->hook, cdev, &ev);
-}
-
-static void channel_event(struct ev_fd *fd, int mask, void *data)
-{
-       struct uvt_cdev *cdev = data;
-       int ret;
-       struct fuse_buf buf;
-       struct fuse_chan *ch;
-       struct shl_dlist *iter;
-       struct uvt_client *client;
-
-       if (!(mask & EV_READABLE)) {
-               if (mask & (EV_HUP | EV_ERR)) {
-                       llog_error(cdev, "HUP/ERR on fuse channel on cdev %p",
-                                  cdev);
-                       uvt_cdev_hup(cdev, -EPIPE);
-               }
-
-               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) {
-               llog_error(cdev, "fuse channel read error on cdev %p: %d",
-                          cdev, ret);
-               uvt_cdev_hup(cdev, ret);
-               return;
-       }
-
-       fuse_session_process_buf(cdev->session, &buf, ch);
-       if (fuse_session_exited(cdev->session)) {
-               llog_error(cdev, "fuse session exited on cdev %p", cdev);
-               uvt_cdev_hup(cdev, cdev->error ? : -EFAULT);
-               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(iter, &cdev->clients) {
-               client = shl_dlist_entry(iter, struct uvt_client, list);
-               uvt_client_cleanup(client);
-       }
-}
-
-static int uvt_cdev_init(struct uvt_cdev *cdev, const char *name,
-                        unsigned int major, unsigned int minor)
-{
-       const char *dev_info_argv[1];
-       struct cuse_info ci;
-       size_t bufsize;
-       char *nparam;
-       int ret;
-
-       /* 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! */
-
-       if (!major)
-               major = TTY_MAJOR;
-
-       if (!major || major > 255) {
-               llog_error(cdev, "invalid major %u on cdev %p",
-                          major, cdev);
-               return -EINVAL;
-       }
-       if (!minor) {
-               llog_error(cdev, "invalid minor %u on cdev %p",
-                          minor, cdev);
-               return -EINVAL;
-       }
-       if (!name || !*name) {
-               llog_error(cdev, "empty name on cdev %p",
-                          cdev);
-               return -EINVAL;
-       }
-
-       llog_info(cdev, "creating device /dev/%s %u:%u on cdev %p",
-                 name, major, minor, cdev);
-
-       ret = asprintf(&nparam, "DEVNAME=%s", name);
-       if (ret <= 0)
-               return llog_ENOMEM(cdev);
-
-       dev_info_argv[0] = nparam;
-       memset(&ci, 0, sizeof(ci));
-       ci.dev_major = major;
-       ci.dev_minor = 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(nparam);
-
-       if (!cdev->session) {
-               llog_error(cdev, "cannot create fuse-ll session on cdev %p",
-                          cdev);
-               return -ENOMEM;
-       }
-
-       cdev->fd = open(cdev->ctx->cuse_file, O_RDWR | O_CLOEXEC | O_NONBLOCK);
-       if (cdev->fd < 0) {
-               llog_error(cdev, "cannot open cuse-file %s on cdev %p (%d): %m",
-                          cdev->ctx->cuse_file, cdev, errno);
-               ret = -EFAULT;
-               goto err_session;
-       }
-
-       bufsize = getpagesize() + 0x1000;
-       if (bufsize < 0x21000)
-               bufsize = 0x21000;
-
-       cdev->bufsize = bufsize;
-       cdev->buf = malloc(bufsize);
-       if (!cdev->buf) {
-               ret = llog_ENOMEM(cdev);
-               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) {
-               llog_error(cdev, "cannot allocate fuse-channel on cdev %p",
-                          cdev);
-               ret = -ENOMEM;
-               goto err_buf;
-       }
-
-       ret = ev_eloop_new_fd(cdev->ctx->eloop, &cdev->efd, cdev->fd,
-                             EV_READABLE, channel_event, cdev);
-       if (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;
-}
-
-static void uvt_cdev_destroy(struct uvt_cdev *cdev)
-{
-       if (cdev->error)
-               llog_warning(cdev, "cdev %p failed with error %d",
-                            cdev, cdev->error);
-
-       fuse_session_destroy(cdev->session);
-       ev_eloop_rm_fd(cdev->efd);
-       free(cdev->buf);
-       close(cdev->fd);
-}
-
-SHL_EXPORT
-int uvt_cdev_new(struct uvt_cdev **out, struct uvt_ctx *ctx,
-                const char *name, unsigned int major, unsigned int minor)
-{
-       struct uvt_cdev *cdev;
-       int ret;
-
-       if (!ctx)
-               return -EINVAL;
-       if (!out)
-               return llog_EINVAL(ctx);
-
-       cdev = malloc(sizeof(*cdev));
-       if (!cdev)
-               return llog_ENOMEM(ctx);
-       memset(cdev, 0, sizeof(*cdev));
-       cdev->ref = 1;
-       cdev->ctx = ctx;
-       cdev->llog = ctx->llog;
-       cdev->llog_data = ctx->llog_data;
-       shl_dlist_init(&cdev->clients);
-
-       llog_debug(cdev, "new cdev %p on ctx %p", cdev, cdev->ctx);
-
-       ret = shl_hook_new(&cdev->hook);
-       if (ret)
-               goto err_free;
-
-       ret = uvt_cdev_init(cdev, name, major, minor);
-       if (ret)
-               goto err_hook;
-
-       uvt_ctx_ref(cdev->ctx);
-       *out = cdev;
-       return 0;
-
-err_hook:
-       shl_hook_free(cdev->hook);
-err_free:
-       free(cdev);
-       return ret;
-}
-
-SHL_EXPORT
-void uvt_cdev_ref(struct uvt_cdev *cdev)
-{
-       if (!cdev || !cdev->ref)
-               return;
-
-       ++cdev->ref;
-}
-
-SHL_EXPORT
-void uvt_cdev_unref(struct uvt_cdev *cdev)
-{
-       if (!cdev || !cdev->ref || --cdev->ref)
-               return;
-
-       llog_debug(cdev, "free cdev %p", cdev);
-
-       uvt_cdev_destroy(cdev);
-       shl_hook_free(cdev->hook);
-       uvt_ctx_unref(cdev->ctx);
-       free(cdev);
-}
-
-SHL_EXPORT
-int uvt_cdev_register_cb(struct uvt_cdev *cdev, uvt_cdev_cb cb, void *data)
-{
-       if (!cdev)
-               return -EINVAL;
-
-       return shl_hook_add_cast(cdev->hook, cb, data, false);
-}
-
-SHL_EXPORT
-void uvt_cdev_unregister_cb(struct uvt_cdev *cdev, uvt_cdev_cb cb, void *data)
-{
-       if (!cdev)
-               return;
-
-       shl_hook_rm_cast(cdev->hook, cb, data);
-}
diff --git a/src/uvt_client.c b/src/uvt_client.c
deleted file mode 100644 (file)
index 574001e..0000000
+++ /dev/null
@@ -1,1120 +0,0 @@
-/*
- * UVT - Userspace Virtual Terminals
- *
- * Copyright (c) 2011-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.
- */
-
-/*
- * Client Sessions
- * A client session represents the internal object that corresponds to a single
- * open-file in the kernel. That is, for each user calling open() on a cdev, we
- * create a client-session in UVT.
- * Note that multiple client-sessions can share the same VT object. It is up to
- * the API user to assign clients to the correct VTs. You can even move clients
- * from one VT to another.
- * On the other hand, user-space can have multiple FDs open for a single
- * client-session similar to how they can have multiple FDs for a single
- * open-file.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/kd.h>
-#include <linux/vt.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <termio.h>
-#include <termios.h>
-#include <unistd.h>
-#include "shl_dlist.h"
-#include "shl_llog.h"
-#include "shl_misc.h"
-#include "uvt.h"
-#include "uvt_internal.h"
-
-#define LLOG_SUBSYSTEM "uvt_client"
-
-/*
- * Blocking Waiters
- * I/O has always two modes: blocking and nonblocking
- * Nonblocking I/O is easy. We simply check whether we can actually forward the
- * data. If we can't, we signal that back. However, blocking I/O is a lot more
- * complex to implement. If a user submits a blocking I/O call, we have to wait
- * until we can finish that request. In the kernel we simply put the user
- * context asleep until we call can finish. However, in user-space via FUSE we
- * have no user-context. Instead, we need to work around that.
- * The most straightforward way would be to create a thread and put that thread
- * asleep. However, this would create one thread for every blocking I/O call
- * which seems to be way too much overhead. Also, we don't want threads in a
- * library. Therefore, we use a different approach.
- * For each blocking request, we create a uvt_waiter. This waiter is then linked
- * into the waiter list and we continue with other requests. Everytime the I/O
- * status changes, we retry the whole waiter list and try to finish the
- * requests. If a request is done, we signal it back and destroy the waiter.
- * This gets slightly more complex with interrupts and fuse_req objects. See
- * below for the implementation.
- */
-
-enum uvt_waiter_type {
-       UVT_WAITER_INVALID              = 0x00,
-
-       UVT_WAITER_READ                 = 0x01,
-       UVT_WAITER_WRITE                = 0x02,
-
-       UVT_WAITER_ALL                  = UVT_WAITER_READ |
-                                         UVT_WAITER_WRITE,
-};
-
-enum uvt_waiter_flags {
-       UVT_WAITER_KILLED               = 0x01,
-       UVT_WAITER_RELEASED             = 0x02,
-};
-
-struct uvt_waiter {
-       struct shl_dlist list;
-       struct uvt_client *client;
-       unsigned int flags;
-       fuse_req_t req;
-
-       unsigned int type;
-
-       union {
-               struct {
-                       size_t size;
-                       uint8_t *buf;
-               } read;
-
-               struct {
-                       size_t size;
-                       uint8_t *buf;
-               } write;
-       };
-};
-
-static bool uvt_waiter_is_killed(struct uvt_waiter *waiter)
-{
-       return !waiter || (waiter->flags & UVT_WAITER_KILLED);
-}
-
-static void uvt_waiter_set_killed(struct uvt_waiter *waiter)
-{
-       if (waiter)
-               waiter->flags |= UVT_WAITER_KILLED;
-}
-
-static bool uvt_waiter_is_released(struct uvt_waiter *waiter)
-{
-       return !waiter || (waiter->flags & UVT_WAITER_RELEASED);
-}
-
-static void uvt_waiter_set_released(struct uvt_waiter *waiter)
-{
-       if (waiter)
-               waiter->flags |= UVT_WAITER_RELEASED;
-}
-
-static void uvt_waiter_interrupt(fuse_req_t req, void *data)
-{
-       struct uvt_waiter *waiter = data;
-
-       uvt_waiter_set_killed(waiter);
-}
-
-static int uvt_waiter_new(struct uvt_waiter **out, struct uvt_client *client,
-                         fuse_req_t req)
-{
-       struct uvt_waiter *waiter;
-
-       if (!client->vt)
-               return -EPIPE;
-       if (fuse_req_interrupted(req))
-               return -ENOENT;
-
-       waiter = malloc(sizeof(*waiter));
-       if (!waiter)
-               return -ENOMEM;
-       memset(waiter, 0, sizeof(*waiter));
-       waiter->client = client;
-       waiter->flags = 0;
-       waiter->req = req;
-
-       fuse_req_interrupt_func(req, uvt_waiter_interrupt, waiter);
-       if (uvt_waiter_is_killed(waiter)) {
-               fuse_req_interrupt_func(req, NULL, NULL);
-               free(waiter);
-               return -ENOENT;
-       }
-
-       shl_dlist_link_tail(&client->waiters, &waiter->list);
-       *out = waiter;
-       return 0;
-}
-
-static int uvt_waiter_new_read(struct uvt_waiter **out,
-                              struct uvt_client *client, fuse_req_t req,
-                              uint8_t *buf, size_t size)
-{
-       struct uvt_waiter *waiter;
-       int ret;
-
-       if (!size)
-               return -EINVAL;
-
-       ret = uvt_waiter_new(&waiter, client, req);
-       if (ret)
-               return ret;
-       waiter->type = UVT_WAITER_READ;
-       waiter->read.size = size;
-       waiter->read.buf = buf;
-
-       *out = waiter;
-       return 0;
-}
-
-static int uvt_waiter_new_write(struct uvt_waiter **out,
-                               struct uvt_client *client, fuse_req_t req,
-                               const uint8_t *mem, size_t size)
-{
-       struct uvt_waiter *waiter;
-       uint8_t *buf;
-       int ret;
-
-       if (!size)
-               return -EINVAL;
-
-       buf = malloc(size);
-       if (!buf)
-               return -ENOMEM;
-       memcpy(buf, mem, size);
-
-       ret = uvt_waiter_new(&waiter, client, req);
-       if (ret)
-               goto err_free;
-       waiter->type = UVT_WAITER_WRITE;
-       waiter->write.size = size;
-       waiter->write.buf = buf;
-
-       *out = waiter;
-       return 0;
-
-err_free:
-       free(buf);
-       return ret;
-}
-
-static void uvt_waiter_release(struct uvt_waiter *waiter, int error)
-{
-       if (!waiter || uvt_waiter_is_released(waiter))
-               return;
-
-       uvt_waiter_set_released(waiter);
-       fuse_req_interrupt_func(waiter->req, NULL, NULL);
-       if (error)
-               fuse_reply_err(waiter->req, abs(error));
-}
-
-static void uvt_waiter_free(struct uvt_waiter *waiter, int error)
-{
-       shl_dlist_unlink(&waiter->list);
-       uvt_waiter_release(waiter, error);
-
-       switch (waiter->type) {
-       case UVT_WAITER_READ:
-               free(waiter->read.buf);
-               break;
-       case UVT_WAITER_WRITE:
-               free(waiter->write.buf);
-               break;
-       }
-
-       free(waiter);
-}
-
-static void uvt_waiter_free_read(struct uvt_waiter *waiter, size_t len)
-{
-       if (!waiter)
-               return;
-
-       if (!uvt_waiter_is_released(waiter)) {
-               uvt_waiter_release(waiter, 0);
-               fuse_reply_buf(waiter->req, (void*)waiter->read.buf, len);
-       }
-       uvt_waiter_free(waiter, -EINVAL);
-}
-
-static void uvt_waiter_free_write(struct uvt_waiter *waiter, size_t len)
-{
-       if (!waiter)
-               return;
-
-       if (!uvt_waiter_is_released(waiter)) {
-               uvt_waiter_release(waiter, 0);
-               fuse_reply_write(waiter->req, len);
-       }
-       uvt_waiter_free(waiter, -EINVAL);
-}
-
-/*
- * Client Sessions
- * A client session is the user-space counterpart of kernel-space open-files.
- * For each open-file we have one client-session in user-space. Users can access
- * a single client-session via multiple file-descriptors via dup(). However, for
- * each open() call on the device, we create a new open-file, that is, a new
- * client-session.
- * A single client session dispatches all the I/O calls on the file. It does
- * blocking and nonblocking I/O, parses ioctls() and correctly performs any
- * other state-tracking. But it does not implement any device logic. That means,
- * the client-session doesn't provide any functionality. Instead, you have to
- * assign a VT to the session. The client-session performs any maintenance tasks
- * and then forwards the requests to the VT object. If no VT object is assigned,
- * the user gets ENODEV as error.
- * Because the client-session performs all state-tracking and parsing, the VT
- * object can be a lot simpler and doesn't have to be aware of any FUSE objects
- * or sessions. Instead, the VT object can concentrate on implementing a _VT_
- * and nothing more.
- * Furthermore, this allows to assign the same VT object to multiple different
- * sessions at the same time. Or to assign a different VT to each session on the
- * same device, or any other combination you want.
- */
-
-static void uvt_client_waiters_retry(struct uvt_client *client,
-                                    unsigned int types);
-
-static int uvt_client_new(struct uvt_client **out, struct uvt_cdev *cdev)
-{
-       struct uvt_client *client;
-
-       if (!cdev)
-               return -EINVAL;
-       if (!out)
-               return llog_EINVAL(cdev);
-
-       client = malloc(sizeof(*client));
-       if (!client)
-               return llog_ENOMEM(cdev);
-       memset(client, 0, sizeof(*client));
-       client->ref = 1;
-       client->cdev = cdev;
-       client->llog = cdev->llog;
-       client->llog_data = cdev->llog_data;
-       shl_dlist_init(&client->waiters);
-
-       llog_debug(client, "new client %p on cdev %p", client, cdev);
-
-       shl_dlist_link_tail(&cdev->clients, &client->list);
-       *out = client;
-       return 0;
-}
-
-SHL_EXPORT
-void uvt_client_ref(struct uvt_client *client)
-{
-       if (!client || !client->ref)
-               return;
-
-       ++client->ref;
-}
-
-SHL_EXPORT
-void uvt_client_unref(struct uvt_client *client)
-{
-       if (!client || !client->ref || --client->ref)
-               return;
-
-       llog_debug(client, "free client %p", client);
-
-       uvt_client_kill(client);
-       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.
- */
-void uvt_client_cleanup(struct uvt_client *client)
-{
-       struct shl_dlist *i, *tmp;
-       struct uvt_waiter *waiter;
-
-       if (!client)
-               return;
-
-       shl_dlist_for_each_safe(i, tmp, &client->waiters) {
-               waiter = shl_dlist_entry(i, struct uvt_waiter, list);
-               if (uvt_waiter_is_killed(waiter))
-                       uvt_waiter_free(waiter, -ENOENT);
-       }
-}
-
-static void uvt_client_waiters_release(struct uvt_client *client, int error)
-{
-       struct uvt_waiter *waiter;
-       int err;
-
-       if (!client)
-               return;
-
-       while (!shl_dlist_empty(&client->waiters)) {
-               waiter = shl_dlist_entry(client->waiters.next,
-                                        struct uvt_waiter, list);
-
-               if (uvt_waiter_is_killed(waiter))
-                       err = -ENOENT;
-               else
-                       err = error;
-
-               uvt_waiter_free(waiter, err);
-       }
-}
-
-SHL_EXPORT
-bool uvt_client_is_dead(struct uvt_client *client)
-{
-       return !client || !client->cdev;
-}
-
-SHL_EXPORT
-void uvt_client_kill(struct uvt_client *client)
-{
-       if (!client || !client->cdev)
-               return;
-
-       llog_debug(client, "kill client %p", client);
-
-       if (client->ph) {
-               fuse_notify_poll(client->ph);
-               fuse_pollhandle_destroy(client->ph);
-               client->ph = NULL;
-       }
-
-       shl_dlist_unlink(&client->list);
-       client->cdev = NULL;
-       uvt_client_set_vt(client, NULL, NULL);
-       uvt_client_waiters_release(client, -EPIPE);
-}
-
-/*
- * We allow recursive VT-actions so we need sophisticated locking. That is, we
- * allow each client->vt->XY() function to itself raise VT events. These VT
- * events cause our uvt_client_vt_event() handler to call
- * uvt_client_waiters_retry(). But uvt_client_waiters_retry() itself can call
- * VT functions again.
- * This recursion isn't particularly bad, as any _proper_ implementation would
- * have an upper limit (which is the number of active waiters). However, to
- * avoid wasting stack space for recursion, we lock the VT when calling VT
- * callbacks. The uvt_client_vt_event() handler checks whether the callbacks are
- * currently locked and sets markers otherwise. These markers cause our
- * unlock-function to notice that we got events in between and then retries all
- * interrupted operations.
- * The client->vt_in_unlock is used to avoid recursion in unlock() itself.
- */
-
-static bool uvt_client_lock_vt(struct uvt_client *client)
-{
-       if (!client || client->vt_locked)
-               return false;
-
-       client->vt_locked = true;
-       return true;
-}
-
-static void uvt_client_unlock_vt(struct uvt_client *client)
-{
-       unsigned int retry;
-
-       if (!client || !client->vt_locked)
-               return;
-
-       client->vt_locked = false;
-       if (client->vt_in_unlock)
-               return;
-
-       while (client->vt_retry) {
-               retry = client->vt_retry;
-               client->vt_retry = 0;
-
-               client->vt_in_unlock = true;
-               uvt_client_waiters_retry(client, retry);
-               client->vt_in_unlock = false;
-       }
-}
-
-static void uvt_client_waiters_retry(struct uvt_client *client,
-                                    unsigned int types)
-{
-       struct shl_dlist *iter, *tmp;
-       struct uvt_waiter *waiter;
-       int ret;
-
-       if (!client || !types || uvt_client_is_dead(client) || !client->vt)
-               return;
-
-       if (!uvt_client_lock_vt(client))
-               return;
-
-       shl_dlist_for_each_safe(iter, tmp, &client->waiters) {
-               if (!types)
-                       break;
-
-               waiter = shl_dlist_entry(iter, struct uvt_waiter, list);
-               if (!(waiter->type & types) || uvt_waiter_is_killed(waiter))
-                       continue;
-
-               if (waiter->type == UVT_WAITER_READ) {
-                       ret = client->vt->read(client->vt_data,
-                                              waiter->read.buf,
-                                              waiter->read.size);
-                       if (ret == -EAGAIN) {
-                               types &= ~UVT_WAITER_READ;
-                               continue;
-                       } else if (ret < 0) {
-                               uvt_waiter_free(waiter, ret);
-                       } else {
-                               if (ret > waiter->read.size)
-                                       ret = waiter->read.size;
-                               uvt_waiter_free_read(waiter, ret);
-                       }
-               } else if (waiter->type == UVT_WAITER_WRITE) {
-                       ret = client->vt->write(client->vt_data,
-                                               waiter->write.buf,
-                                               waiter->write.size);
-                       if (ret == -EAGAIN) {
-                               types &= ~UVT_WAITER_WRITE;
-                               continue;
-                       } else if (ret < 0) {
-                               uvt_waiter_free(waiter, ret);
-                       } else {
-                               if (ret > waiter->write.size)
-                                       ret = waiter->write.size;
-                               uvt_waiter_free_write(waiter, ret);
-                       }
-               }
-       }
-
-       uvt_client_unlock_vt(client);
-}
-
-static void uvt_client_vt_event(void *vt, struct uvt_vt_event *ev, void *data)
-{
-       struct uvt_client *client = data;
-
-       if (uvt_client_is_dead(client))
-               return;
-
-       switch (ev->type) {
-       case UVT_VT_HUP:
-               uvt_client_kill(client);
-               break;
-       case UVT_VT_TTY:
-               switch (ev->tty.type) {
-               case UVT_TTY_HUP:
-                       uvt_client_kill(client);
-                       break;
-               case UVT_TTY_READ:
-                       if (client->ph)
-                               fuse_notify_poll(client->ph);
-                       client->vt_retry |= UVT_WAITER_READ;
-                       break;
-               case UVT_TTY_WRITE:
-                       if (client->ph)
-                               fuse_notify_poll(client->ph);
-                       client->vt_retry |= UVT_WAITER_WRITE;
-                       break;
-               }
-               break;
-       }
-
-       uvt_client_waiters_retry(client, client->vt_retry);
-}
-
-SHL_EXPORT
-int uvt_client_set_vt(struct uvt_client *client, const struct uvt_vt_ops *vt,
-                     void *vt_data)
-{
-       int ret;
-
-       if (!client)
-               return -EINVAL;
-       if (uvt_client_is_dead(client) && vt)
-               return -EINVAL;
-
-       if (client->vt) {
-               client->vt->unregister_cb(client->vt_data, uvt_client_vt_event,
-                                         client);
-               client->vt->unref(client->vt_data);
-       }
-
-       client->vt = vt;
-       client->vt_data = vt_data;
-
-       if (client->vt) {
-               ret = client->vt->register_cb(client->vt_data,
-                                             uvt_client_vt_event, client);
-               if (!ret) {
-                       client->vt->ref(client->vt_data);
-                       uvt_client_waiters_retry(client, UVT_WAITER_ALL);
-                       return 0;
-               }
-       } else {
-               ret = 0;
-       }
-
-       client->vt = NULL;
-       client->vt_data = NULL;
-       uvt_client_waiters_release(client, -ENODEV);
-       return ret;
-}
-
-/*
- * Internal FUSE low-level fops implementation
- * These functions implement the callbacks used by the CUSE/FUSE-ll
- * implementation in uvt_cdev objects. Our infrastructure allows to provide
- * other callbacks, too, but this is currently not needed. Moreover, I cannot
- * see any reason to add them to the public API as nobody would want anything
- * different than CUSE/FUSE as frontend.
- */
-
-int uvt_client_ll_open(struct uvt_client **out, struct uvt_cdev *cdev,
-                      fuse_req_t req, struct fuse_file_info *fi)
-{
-       struct uvt_client *client;
-       int ret;
-
-       ret = uvt_client_new(&client, cdev);
-       if (ret) {
-               fuse_reply_err(req, -ret);
-               return ret;
-       }
-
-       fi->fh = (uint64_t)(uintptr_t)(void*)client;
-       fi->nonseekable = 1;
-       fi->direct_io = 1;
-       ret = fuse_reply_open(req, fi);
-       if (ret < 0) {
-               uvt_client_kill(client);
-               uvt_client_unref(client);
-               return -EFAULT;
-       }
-
-       *out = client;
-       return 0;
-}
-
-void uvt_client_ll_release(fuse_req_t req, struct fuse_file_info *fi)
-{
-       struct uvt_client *client = (void*)(uintptr_t)fi->fh;
-
-       if (!client) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       }
-
-       uvt_client_kill(client);
-       uvt_client_unref(client);
-       fuse_reply_err(req, 0);
-}
-
-void uvt_client_ll_read(fuse_req_t req, size_t size, off_t off,
-                       struct fuse_file_info *fi)
-{
-       struct uvt_client *client = (void*)(uintptr_t)fi->fh;
-       struct uvt_waiter *waiter;
-       uint8_t *buf;
-       int ret;
-
-       if (!client) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       } else if (uvt_client_is_dead(client)) {
-               fuse_reply_err(req, EPIPE);
-               return;
-       } else if (off) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       } else if (!size) {
-               fuse_reply_buf(req, "", 0);
-               return;
-       } else if (!client->vt) {
-               fuse_reply_err(req, ENODEV);
-               return;
-       }
-
-       buf = malloc(size);
-       if (!buf) {
-               fuse_reply_err(req, ENOMEM);
-               return;
-       }
-
-       ret = client->vt->read(client->vt_data, buf, size);
-       if (ret >= 0) {
-               if (ret > size)
-                       ret = size;
-
-               fuse_reply_buf(req, (void*)buf, ret);
-               free(buf);
-               return;
-       } else if (ret == -EAGAIN && !(fi->flags & O_NONBLOCK)) {
-               ret = uvt_waiter_new_read(&waiter, client, req, buf, size);
-               if (!ret)
-                       return;
-       }
-
-       fuse_reply_err(req, -ret);
-       free(buf);
-}
-
-void uvt_client_ll_write(fuse_req_t req, const char *buf, size_t size,
-                        off_t off, struct fuse_file_info *fi)
-{
-       struct uvt_client *client = (void*)(uintptr_t)fi->fh;
-       struct uvt_waiter *waiter;
-       int ret;
-
-       if (!client) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       } else if (uvt_client_is_dead(client)) {
-               fuse_reply_err(req, EPIPE);
-               return;
-       } else if (off) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       } else if (!size) {
-               fuse_reply_write(req, 0);
-               return;
-       } else if (!client->vt) {
-               fuse_reply_err(req, ENODEV);
-               return;
-       }
-
-       ret = client->vt->write(client->vt_data, (void*)buf, size);
-       if (ret >= 0) {
-               if (ret > size)
-                       ret = size;
-
-               fuse_reply_write(req, ret);
-               return;
-       } else if (ret == -EAGAIN && !(fi->flags & O_NONBLOCK)) {
-               ret = uvt_waiter_new_write(&waiter, client, req, (void*)buf,
-                                          size);
-               if (!ret)
-                       return;
-       }
-
-       fuse_reply_err(req, -ret);
-}
-
-void uvt_client_ll_poll(fuse_req_t req, struct fuse_file_info *fi,
-                       struct fuse_pollhandle *ph)
-{
-       struct uvt_client *client = (void*)(uintptr_t)fi->fh;
-       unsigned int flags, fl;
-
-       if (!client) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       } else if (uvt_client_is_dead(client)) {
-               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;
-
-       if (!client->vt) {
-               fuse_reply_err(req, ENODEV);
-               return;
-       }
-
-       flags = 0;
-       fl = client->vt->poll(client->vt_data);
-       if (fl & UVT_TTY_HUP)
-               flags |= EPOLLHUP;
-       if (fl & UVT_TTY_READ)
-               flags |= EPOLLIN | EPOLLRDNORM;
-       if (fl & UVT_TTY_WRITE)
-               flags |= EPOLLOUT | EPOLLWRNORM;
-
-       fuse_reply_poll(req, flags);
-}
-
-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;
-}
-
-void uvt_client_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 uvt_client *client = (void*)(uintptr_t)fi->fh;
-       uintptr_t uarg = (uintptr_t)arg;
-       bool compat;
-       int ret;
-       struct vt_stat vtstat;
-       struct vt_mode vtmode;
-       unsigned int uval;
-
-       if (!client) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       } else if (uvt_client_is_dead(client)) {
-               fuse_reply_err(req, EPIPE);
-               return;
-       } else if (!client->vt) {
-               fuse_reply_err(req, ENODEV);
-               return;
-       }
-
-       /* TODO: fix compat-ioctls */
-       compat = !!(flags & FUSE_IOCTL_COMPAT);
-       if (compat) {
-               fuse_reply_err(req, EOPNOTSUPP);
-               return;
-       }
-
-       switch (cmd) {
-
-       /* TTY ioctls */
-
-       case TCFLSH:
-               if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
-                       return;
-               if (!client->vt->ioctl_TCFLSH) {
-                       fuse_reply_err(req, EOPNOTSUPP);
-               } else {
-                       ret = client->vt->ioctl_TCFLSH(client->vt_data,
-                                                      (unsigned long)uarg);
-                       if (ret)
-                               fuse_reply_err(req, abs(ret));
-                       else
-                               fuse_reply_ioctl(req, 0, NULL, 0);
-               }
-               break;
-
-       case TIOCPKT:
-       case TCXONC:
-       case TCGETS:
-       case TCSETS:
-       case TCSETSF:
-       case TCSETSW:
-       case TCGETA:
-       case TCSETA:
-       case TCSETAF:
-       case TCSETAW:
-       case TIOCGLCKTRMIOS:
-       case TIOCSLCKTRMIOS:
-       case TCGETX:
-       case TCSETX:
-       case TCSETXW:
-       case TCSETXF:
-       case TIOCGSOFTCAR:
-       case TIOCSSOFTCAR:
-               fuse_reply_err(req, EOPNOTSUPP);
-               break;
-
-       /* VT ioctls */
-
-       case VT_ACTIVATE:
-               if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
-                       return;
-               if (!client->vt->ioctl_VT_ACTIVATE) {
-                       fuse_reply_err(req, EOPNOTSUPP);
-               } else {
-                       ret = client->vt->ioctl_VT_ACTIVATE(client->vt_data,
-                                                       (unsigned long)uarg);
-                       if (ret)
-                               fuse_reply_err(req, abs(ret));
-                       else
-                               fuse_reply_ioctl(req, 0, NULL, 0);
-               }
-               break;
-
-       case VT_WAITACTIVE:
-               if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
-                       return;
-               if (!client->vt->ioctl_VT_WAITACTIVE) {
-                       fuse_reply_err(req, EOPNOTSUPP);
-               } else {
-                       ret = client->vt->ioctl_VT_WAITACTIVE(client->vt_data,
-                                                       (unsigned long)uarg);
-                       if (ret)
-                               fuse_reply_err(req, abs(ret));
-                       else
-                               fuse_reply_ioctl(req, 0, NULL, 0);
-               }
-               break;
-
-       case VT_GETSTATE:
-               if (ioctl_param(req, arg, 0, in_bufsz,
-                               sizeof(struct vt_stat), out_bufsz))
-                       return;
-               if (!client->vt->ioctl_VT_GETSTATE) {
-                       fuse_reply_err(req, EOPNOTSUPP);
-               } else {
-                       memset(&vtstat, 0, sizeof(vtstat));
-                       ret = client->vt->ioctl_VT_GETSTATE(client->vt_data,
-                                                           &vtstat);
-                       if (ret)
-                               fuse_reply_err(req, abs(ret));
-                       else
-                               fuse_reply_ioctl(req, 0, &vtstat,
-                                                sizeof(vtstat));
-               }
-               break;
-
-       case VT_OPENQRY:
-               if (ioctl_param(req, arg, 0, in_bufsz,
-                               sizeof(unsigned int), out_bufsz))
-                       return;
-               if (!client->vt->ioctl_VT_OPENQRY) {
-                       fuse_reply_err(req, EOPNOTSUPP);
-               } else {
-                       uval = 0;
-                       ret = client->vt->ioctl_VT_OPENQRY(client->vt_data,
-                                                          &uval);
-                       if (ret)
-                               fuse_reply_err(req, abs(ret));
-                       else
-                               fuse_reply_ioctl(req, 0, &uval, sizeof(uval));
-               }
-               break;
-
-       case VT_GETMODE:
-               if (ioctl_param(req, arg, 0, in_bufsz,
-                               sizeof(struct vt_mode), out_bufsz))
-                       return;
-               if (!client->vt->ioctl_VT_GETMODE) {
-                       fuse_reply_err(req, EOPNOTSUPP);
-               } else {
-                       memset(&vtmode, 0, sizeof(vtmode));
-                       ret = client->vt->ioctl_VT_GETMODE(client->vt_data,
-                                                          &vtmode);
-                       if (ret)
-                               fuse_reply_err(req, abs(ret));
-                       else
-                               fuse_reply_ioctl(req, 0, &vtmode,
-                                                sizeof(vtmode));
-               }
-               break;
-
-       case VT_SETMODE:
-               if (ioctl_param(req, arg, sizeof(struct vt_mode), in_bufsz,
-                               0, out_bufsz))
-                       return;
-               if (!client->vt->ioctl_VT_SETMODE) {
-                       fuse_reply_err(req, EOPNOTSUPP);
-               } else {
-                       ret = client->vt->ioctl_VT_SETMODE(client->vt_data,
-                                               (const struct vt_mode*)in_buf,
-                                               fuse_req_ctx(req)->pid);
-                       if (ret)
-                               fuse_reply_err(req, abs(ret));
-                       else
-                               fuse_reply_ioctl(req, 0, NULL, 0);
-               }
-               break;
-
-       case VT_RELDISP:
-               if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
-                       return;
-               if (!client->vt->ioctl_VT_RELDISP) {
-                       fuse_reply_err(req, EOPNOTSUPP);
-               } else {
-                       ret = client->vt->ioctl_VT_RELDISP(client->vt_data,
-                                                       (unsigned long)uarg);
-                       if (ret)
-                               fuse_reply_err(req, abs(ret));
-                       else
-                               fuse_reply_ioctl(req, 0, NULL, 0);
-               }
-               break;
-
-       case KDGETMODE:
-               if (ioctl_param(req, arg, 0, in_bufsz,
-                               sizeof(unsigned int), out_bufsz))
-                       return;
-               if (!client->vt->ioctl_KDGETMODE) {
-                       fuse_reply_err(req, EOPNOTSUPP);
-               } else {
-                       uval = 0;
-                       ret = client->vt->ioctl_KDGETMODE(client->vt_data,
-                                                         &uval);
-                       if (ret)
-                               fuse_reply_err(req, abs(ret));
-                       else
-                               fuse_reply_ioctl(req, 0, &uval, sizeof(uval));
-               }
-               break;
-
-       case KDSETMODE:
-               if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
-                       return;
-               if (!client->vt->ioctl_KDSETMODE) {
-                       fuse_reply_err(req, EOPNOTSUPP);
-               } else {
-                       ret = client->vt->ioctl_KDSETMODE(client->vt_data,
-                                                       (unsigned int)uarg);
-                       if (ret)
-                               fuse_reply_err(req, abs(ret));
-                       else
-                               fuse_reply_ioctl(req, 0, NULL, 0);
-               }
-               break;
-
-       case KDGKBMODE:
-               if (ioctl_param(req, arg, 0, in_bufsz,
-                               sizeof(unsigned int), out_bufsz))
-                       return;
-               if (!client->vt->ioctl_KDGKBMODE) {
-                       fuse_reply_err(req, EOPNOTSUPP);
-               } else {
-                       uval = 0;
-                       ret = client->vt->ioctl_KDGKBMODE(client->vt_data,
-                                                         &uval);
-                       if (ret)
-                               fuse_reply_err(req, abs(ret));
-                       else
-                               fuse_reply_ioctl(req, 0, &uval, sizeof(uval));
-               }
-               break;
-
-       case KDSKBMODE:
-               if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
-                       return;
-               if (!client->vt->ioctl_KDSKBMODE) {
-                       fuse_reply_err(req, EOPNOTSUPP);
-               } else {
-                       ret = client->vt->ioctl_KDSKBMODE(client->vt_data,
-                                                       (unsigned int)uarg);
-                       if (ret)
-                               fuse_reply_err(req, abs(ret));
-                       else
-                               fuse_reply_ioctl(req, 0, NULL, 0);
-               }
-               break;
-
-       case TIOCLINUX:
-       case KIOCSOUND:
-       case KDMKTONE:
-       case KDGKBTYPE:
-       case KDADDIO:
-       case KDDELIO:
-       case KDENABIO:
-       case KDDISABIO:
-       case KDKBDREP:
-       case KDMAPDISP:
-       case KDUNMAPDISP:
-       case KDGKBMETA:
-       case KDSKBMETA:
-       case KDGETKEYCODE:
-       case KDSETKEYCODE:
-       case KDGKBENT:
-       case KDSKBENT:
-       case KDGKBSENT:
-       case KDSKBSENT:
-       case KDGKBDIACR:
-       case KDSKBDIACR:
-       case KDGKBDIACRUC:
-       case KDSKBDIACRUC:
-       case KDGETLED:
-       case KDSETLED:
-       case KDGKBLED:
-       case KDSKBLED:
-       case KDSIGACCEPT:
-       case VT_SETACTIVATE:
-       case VT_DISALLOCATE:
-       case VT_RESIZE:
-       case VT_RESIZEX:
-       case GIO_FONT:
-       case PIO_FONT:
-       case GIO_CMAP:
-       case PIO_CMAP:
-       case GIO_FONTX:
-       case PIO_FONTX:
-       case PIO_FONTRESET:
-       case KDFONTOP:
-       case GIO_SCRNMAP:
-       case PIO_SCRNMAP:
-       case GIO_UNISCRNMAP:
-       case PIO_UNISCRNMAP:
-       case PIO_UNIMAPCLR:
-       case GIO_UNIMAP:
-       case PIO_UNIMAP:
-       case VT_LOCKSWITCH:
-       case VT_UNLOCKSWITCH:
-       case VT_GETHIFONTMASK:
-       case VT_WAITEVENT:
-               fuse_reply_err(req, EOPNOTSUPP);
-               break;
-       default:
-               fuse_reply_err(req, EINVAL);
-               break;
-       }
-}
diff --git a/src/uvt_ctx.c b/src/uvt_ctx.c
deleted file mode 100644 (file)
index ad1e534..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * UVT - Userspace Virtual Terminals
- *
- * Copyright (c) 2011-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.
- */
-
-/*
- * UVT Contexts
- * A UVT context is used to provide basic infrastructure for all other UVT
- * objects. It allows easy integration of multiple UVT objects into a single
- * application.
- */
-
-#include <eloop.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/major.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include "shl_array.h"
-#include "shl_flagset.h"
-#include "shl_llog.h"
-#include "shl_misc.h"
-#include "uvt.h"
-#include "uvt_internal.h"
-
-#define LLOG_SUBSYSTEM "uvt_ctx"
-
-SHL_EXPORT
-int uvt_ctx_new(struct uvt_ctx **out, uvt_log_t log, void *log_data)
-{
-       struct uvt_ctx *ctx;
-       int ret;
-
-       if (!out)
-               return llog_dEINVAL(log, log_data);
-
-       ctx = malloc(sizeof(*ctx));
-       if (!ctx)
-               return llog_dENOMEM(log, log_data);
-       memset(ctx, 0, sizeof(*ctx));
-       ctx->ref = 1;
-       ctx->llog = log;
-       ctx->llog_data = log_data;
-
-       /* Default major/minor uses the TTY_MAJOR number with an offset of 2^15
-        * to avoid ID-clashes with any in-kernel TTY driver. As kernel drivers
-        * use static IDs only, a lower number would be fine, too, but lets be
-        * safe and just use high numbers. */
-       ctx->major = TTY_MAJOR;
-       ctx->minor_offset = 16384;
-
-       llog_debug(ctx, "new ctx %p", ctx);
-
-       ret = ev_eloop_new(&ctx->eloop, ctx->llog, ctx->llog_data);
-       if (ret)
-               goto err_free;
-
-       ctx->cuse_file = strdup("/dev/cuse");
-       if (!ctx->cuse_file) {
-               ret = llog_ENOMEM(ctx);
-               goto err_eloop;
-       }
-
-       ret = shl_flagset_new(&ctx->minors);
-       if (ret)
-               goto err_file;
-
-       *out = ctx;
-       return 0;
-
-err_file:
-       free(ctx->cuse_file);
-err_eloop:
-       ev_eloop_unref(ctx->eloop);
-err_free:
-       free(ctx);
-       return ret;
-}
-
-SHL_EXPORT
-void uvt_ctx_ref(struct uvt_ctx *ctx)
-{
-       if (!ctx || !ctx->ref)
-               return;
-
-       ++ctx->ref;
-}
-
-SHL_EXPORT
-void uvt_ctx_unref(struct uvt_ctx *ctx)
-{
-       if (!ctx || !ctx->ref || --ctx->ref)
-               return;
-
-       llog_debug(ctx, "free ctx %p", ctx);
-
-       shl_flagset_free(ctx->minors);
-       free(ctx->cuse_file);
-       ev_eloop_unref(ctx->eloop);
-       free(ctx);
-}
-
-SHL_EXPORT
-int uvt_ctx_get_fd(struct uvt_ctx *ctx)
-{
-       if (!ctx)
-               return -1;
-
-       return ev_eloop_get_fd(ctx->eloop);
-}
-
-SHL_EXPORT
-void uvt_ctx_dispatch(struct uvt_ctx *ctx)
-{
-       if (!ctx)
-               return;
-
-       ev_eloop_dispatch(ctx->eloop, 0);
-}
-
-SHL_EXPORT
-unsigned int uvt_ctx_get_major(struct uvt_ctx *ctx)
-{
-       return ctx->major;
-}
-
-SHL_EXPORT
-int uvt_ctx_new_minor(struct uvt_ctx *ctx, unsigned int *out)
-{
-       int ret;
-
-       ret = shl_flagset_alloc(ctx->minors, out);
-       if (ret)
-               return ret;
-
-       *out += ctx->minor_offset;
-       return 0;
-}
-
-SHL_EXPORT
-void uvt_ctx_free_minor(struct uvt_ctx *ctx, unsigned int minor)
-{
-       if (!ctx || minor < ctx->minor_offset)
-               return;
-
-       shl_flagset_unset(ctx->minors, minor - ctx->minor_offset);
-}
diff --git a/src/uvt_internal.h b/src/uvt_internal.h
deleted file mode 100644 (file)
index 479cf2b..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * UVT - Userspace Virtual Terminals
- *
- * Copyright (c) 2011-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.
- */
-
-/*
- * Userspace Virtual Terminals Internals
- * Internal header of the UVT implementation.
- */
-
-#ifndef UVT_INTERNAL_H
-#define UVT_INTERNAL_H
-
-#include <eloop.h>
-#include <stdlib.h>
-#include <uvt.h>
-#include "shl_array.h"
-#include "shl_dlist.h"
-#include "shl_hook.h"
-#include "shl_llog.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>
-
-/* contexts */
-
-struct uvt_ctx {
-       unsigned long ref;
-       llog_submit_t llog;
-       void *llog_data;
-       struct ev_eloop *eloop;
-
-       char *cuse_file;
-       unsigned int major;
-       unsigned int minor_offset;
-       struct shl_array *minors;
-};
-
-/* character devices */
-
-struct uvt_cdev {
-       unsigned long ref;
-       struct uvt_ctx *ctx;
-       llog_submit_t llog;
-       void *llog_data;
-
-       int error;
-       struct shl_hook *hook;
-
-       struct fuse_session *session;
-       int fd;
-       struct fuse_chan *channel;
-       struct ev_fd *efd;
-
-       size_t bufsize;
-       char *buf;
-
-       struct shl_dlist clients;
-};
-
-/* client sessions */
-
-struct uvt_client {
-       unsigned long ref;
-       struct shl_dlist list;
-       struct uvt_cdev *cdev;
-       llog_submit_t llog;
-       void *llog_data;
-
-       struct fuse_pollhandle *ph;
-       struct shl_dlist waiters;
-
-       const struct uvt_vt_ops *vt;
-       void *vt_data;
-       bool vt_locked;
-       bool vt_in_unlock;
-       unsigned int vt_retry;
-};
-
-void uvt_client_cleanup(struct uvt_client *client);
-
-int uvt_client_ll_open(struct uvt_client **out, struct uvt_cdev *cdev,
-                      fuse_req_t req, struct fuse_file_info *fi);
-void uvt_client_ll_release(fuse_req_t req, struct fuse_file_info *fi);
-void uvt_client_ll_read(fuse_req_t req, size_t size, off_t off,
-                       struct fuse_file_info *fi);
-void uvt_client_ll_write(fuse_req_t req, const char *buf, size_t size,
-                        off_t off, struct fuse_file_info *fi);
-void uvt_client_ll_poll(fuse_req_t req, struct fuse_file_info *fi,
-                       struct fuse_pollhandle *ph);
-void uvt_client_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);
-
-#endif /* UVT_INTERNAL_H */
diff --git a/src/uvt_tty_null.c b/src/uvt_tty_null.c
deleted file mode 100644 (file)
index 0c04942..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * UVT - Userspace Virtual Terminals
- *
- * Copyright (c) 2011-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.
- */
-
-/*
- * Null TTY
- * This tty simply discards all incoming messages and never produces any
- * outgoing messages. Ioctls return static data or fail with some generic error
- * code if they would modify internal state that we cannot emulate easily.
- */
-
-#include <eloop.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include "shl_llog.h"
-#include "uvt.h"
-#include "uvt_internal.h"
-
-#define LLOG_SUBSYSTEM "uvt_tty_null"
-
-struct uvt_tty_null {
-       unsigned long ref;
-       struct uvt_ctx *ctx;
-       llog_submit_t llog;
-       void *llog_data;
-};
-
-static void tty_null_ref(void *data)
-{
-       uvt_tty_null_ref(data);
-}
-
-static void tty_null_unref(void *data)
-{
-       uvt_tty_null_unref(data);
-}
-
-static int tty_null_register_cb(void *data, uvt_tty_cb cb, void *cb_data)
-{
-       return 0;
-}
-
-static void tty_null_unregister_cb(void *data, uvt_tty_cb cb, void *cb_data)
-{
-}
-
-static int tty_null_read(void *data, uint8_t *buf, size_t size)
-{
-       return -EAGAIN;
-}
-
-static int tty_null_write(void *data, const uint8_t *buf, size_t size)
-{
-       return size;
-}
-
-static unsigned int tty_null_poll(void *data)
-{
-       return UVT_TTY_WRITE;
-}
-
-const struct uvt_tty_ops uvt_tty_null_ops = {
-       .ref = tty_null_ref,
-       .unref = tty_null_unref,
-       .register_cb = tty_null_register_cb,
-       .unregister_cb = tty_null_unregister_cb,
-
-       .read = tty_null_read,
-       .write = tty_null_write,
-       .poll = tty_null_poll,
-};
-
-int uvt_tty_null_new(struct uvt_tty_null **out, struct uvt_ctx *ctx)
-{
-       struct uvt_tty_null *tty;
-
-       if (!ctx)
-               return -EINVAL;
-       if (!out)
-               return llog_EINVAL(ctx);
-
-       tty = malloc(sizeof(*tty));
-       if (!tty)
-               return llog_ENOMEM(ctx);
-       memset(tty, 0, sizeof(*tty));
-       tty->ref = 1;
-       tty->ctx = ctx;
-       tty->llog = tty->ctx->llog;
-       tty->llog_data = tty->ctx->llog_data;
-
-       uvt_ctx_ref(tty->ctx);
-       *out = tty;
-       return 0;
-}
-
-void uvt_tty_null_ref(struct uvt_tty_null *tty)
-{
-       if (!tty || !tty->ref)
-               return;
-
-       ++tty->ref;
-}
-
-void uvt_tty_null_unref(struct uvt_tty_null *tty)
-{
-       if (!tty || !tty->ref || --tty->ref)
-               return;
-
-       uvt_ctx_unref(tty->ctx);
-       free(tty);
-}