Remove libuvt

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>
This commit is contained in:
David Herrmann 2013-10-29 10:01:13 +01:00
parent 761434ecac
commit 9f2f11d2a5
12 changed files with 0 additions and 2452 deletions

1
.gitignore vendored
View File

@ -19,7 +19,6 @@ configure
*.tar.xz
libtool
libeloop.pc
libuvt.pc
libuterm.pc
m4/
stamp-*

View 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
@ -236,39 +230,6 @@ libeloop_la_CPPFLAGS += $(DBUS_CFLAGS)
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

1
README
View File

@ -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 ==

View 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:

View File

@ -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}

View File

@ -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:
*;
};

274
src/uvt.h
View File

@ -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 */

View File

@ -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);
}

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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 */

View File

@ -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);
}