summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@gmail.com>2013-10-29 10:01:13 +0100
committerDavid Herrmann <dh.herrmann@gmail.com>2013-10-29 10:01:13 +0100
commit9f2f11d2a53a0ca91b763d04ca7003d35f83cbf3 (patch)
tree2c2445b0325e6d4cb521d87a4c969102249d6f2d /src
parent761434ecac7d9a4f1a95e3f3b4eee6e539947d32 (diff)
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>
Diffstat (limited to 'src')
-rw-r--r--src/uvt.h274
-rw-r--r--src/uvt_cdev.c485
-rw-r--r--src/uvt_client.c1120
-rw-r--r--src/uvt_ctx.c168
-rw-r--r--src/uvt_internal.h118
-rw-r--r--src/uvt_tty_null.c135
6 files changed, 0 insertions, 2300 deletions
diff --git a/src/uvt.h b/src/uvt.h
deleted file mode 100644
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
index 291857b..0000000
--- a/src/uvt_cdev.c
+++ /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
index 574001e..0000000
--- a/src/uvt_client.c
+++ /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
index ad1e534..0000000
--- a/src/uvt_ctx.c
+++ /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
index 479cf2b..0000000
--- a/src/uvt_internal.h
+++ /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
index 0c04942..0000000
--- a/src/uvt_tty_null.c
+++ /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);
-}