2 * UVT - Userspace Virtual Terminals
4 * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files
8 * (the "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 * This implements a VT character device entry point via the CUSE API. It does
29 * not implement the VT API on top of the character-device (cdev) but only
30 * provides the entry point. It is up to the user to bind open-files to VT and
36 #include <linux/major.h>
41 #include "shl_dlist.h"
45 #include "uvt_internal.h"
47 #include <fuse/fuse.h>
48 #include <fuse/fuse_common.h>
49 #include <fuse/fuse_lowlevel.h>
50 #include <fuse/fuse_opt.h>
51 #include <fuse/cuse_lowlevel.h>
53 #define LLOG_SUBSYSTEM "uvt_cdev"
57 * This implements all the file-system operations on the character-device. It
58 * is important that we handle interrupts correctly (ENOENT) and never loose
59 * any data. This is all single threaded as it is not performance critical at
61 * We simply dispatch each call to uvt_client as this implements all the
62 * client-session related operations.
65 static void ll_open(fuse_req_t req, struct fuse_file_info *fi)
67 struct uvt_cdev *cdev = fuse_req_userdata(req);
68 struct uvt_client *client;
69 struct uvt_cdev_event ev;
72 ret = uvt_client_ll_open(&client, cdev, req, fi);
76 memset(&ev, 0, sizeof(ev));
77 ev.type = UVT_CDEV_OPEN;
79 shl_hook_call(cdev->hook, cdev, &ev);
82 static void ll_destroy(void *data) {
83 struct uvt_cdev *cdev = data;
84 struct uvt_client *client;
86 /* on unexpected shutdown this kills all open clients */
87 while (!shl_dlist_empty(&cdev->clients)) {
88 client = shl_dlist_entry(cdev->clients.next,
89 struct uvt_client, list);
90 uvt_client_kill(client);
91 uvt_client_unref(client);
95 static const struct cuse_lowlevel_ops ll_ops = {
97 .destroy = ll_destroy,
99 .release = uvt_client_ll_release,
100 .read = uvt_client_ll_read,
101 .write = uvt_client_ll_write,
102 .poll = uvt_client_ll_poll,
103 .ioctl = uvt_client_ll_ioctl,
110 * The connection to the FUSE kernel module is done via a file-descriptor.
111 * Writing to it is synchronous, so the commands that we write are
112 * _immediately_ executed and return the result to us. Furthermore, write()
113 * is always non-blocking and always succeeds so no reason to watch for
114 * EAGAIN. Reading from the FD, on the other hand, may block if there is no
115 * data available so we mark it as O_NONBLOCK. The kernel maintains
116 * an event-queue that we read from. So there may be pending events that we
117 * haven't read but which affect the calls that we write to the kernel. This
118 * is important when handling interrupts.
119 * chan_receive() and chan_send() handle I/O to the kernel module and are
120 * hooked up into a fuse-channel.
123 static int chan_receive(struct fuse_chan **chp, char *buf, size_t size)
125 struct fuse_chan *ch = *chp;
126 struct uvt_cdev *cdev = fuse_chan_data(ch);
127 struct fuse_session *se = fuse_chan_session(ch);
128 int fd = fuse_chan_fd(ch);
138 if (fuse_session_exited(se))
141 res = read(fd, buf, size);
143 /* EOF on cuse file */
144 llog_error(cdev, "fuse channel shut down on cdev %p", cdev);
145 fuse_session_exit(se);
147 } else if (res < 0) {
148 /* ENOENT is returned if the operation was interrupted, it's
153 /* ENODEV is returned if the FS got unmounted. This shouldn't
154 * occur for CUSE devices. Anyway, exit if this happens. */
155 if (errno == ENODEV) {
156 llog_error(cdev, "fuse channel unmounted on cdev %p",
158 fuse_session_exit(se);
162 /* EINTR and EAGAIN are simply forwarded to the caller. */
163 if (errno == EINTR || errno == EAGAIN)
166 cdev->error = -errno;
167 llog_error(cdev, "fuse channel read error on cdev %p (%d): %m",
169 fuse_session_exit(se);
176 static int chan_send(struct fuse_chan *ch, const struct iovec iov[],
179 struct uvt_cdev *cdev = fuse_chan_data(ch);
180 struct fuse_session *se = fuse_chan_session(ch);
181 int fd = fuse_chan_fd(ch);
189 ret = writev(fd, iov, count);
191 /* ENOENT is returned on interrupts */
192 if (!fuse_session_exited(se) && errno != ENOENT) {
193 cdev->error = -errno;
194 llog_error(cdev, "cannot write to fuse-channel on cdev %p (%d): %m",
196 fuse_session_exit(se);
204 static const struct fuse_chan_ops chan_ops = {
205 .receive = chan_receive,
212 * This creates the high-level character-device driver and registers a
213 * fake-session that is used to control each character file.
214 * channel_event() is a callback when I/O is possible on the FUSE FD and
215 * performs all outstanding tasks.
216 * On error, the fake-session is unregistered and deleted. This also stops all
217 * client sessions, obviously.
220 static void uvt_cdev_hup(struct uvt_cdev *cdev, int error)
222 struct uvt_cdev_event ev;
224 ev_eloop_rm_fd(cdev->efd);
228 memset(&ev, 0, sizeof(ev));
229 ev.type = UVT_CDEV_HUP;
231 shl_hook_call(cdev->hook, cdev, &ev);
234 static void channel_event(struct ev_fd *fd, int mask, void *data)
236 struct uvt_cdev *cdev = data;
239 struct fuse_chan *ch;
240 struct shl_dlist *iter;
241 struct uvt_client *client;
243 if (!(mask & EV_READABLE)) {
244 if (mask & (EV_HUP | EV_ERR)) {
245 llog_error(cdev, "HUP/ERR on fuse channel on cdev %p",
247 uvt_cdev_hup(cdev, -EPIPE);
253 memset(&buf, 0, sizeof(buf));
255 buf.size = cdev->bufsize;
257 ret = fuse_session_receive_buf(cdev->session, &buf, &ch);
258 if (ret == -EINTR || ret == -EAGAIN) {
260 } else if (ret < 0) {
261 llog_error(cdev, "fuse channel read error on cdev %p: %d",
263 uvt_cdev_hup(cdev, ret);
267 fuse_session_process_buf(cdev->session, &buf, ch);
268 if (fuse_session_exited(cdev->session)) {
269 llog_error(cdev, "fuse session exited on cdev %p", cdev);
270 uvt_cdev_hup(cdev, cdev->error ? : -EFAULT);
274 /* Readers can get interrupted asynchronously. Due to heavy locking
275 * inside of FUSE, we cannot release them right away. So cleanup all
276 * killed readers after we processed all buffers. */
277 shl_dlist_for_each(iter, &cdev->clients) {
278 client = shl_dlist_entry(iter, struct uvt_client, list);
279 uvt_client_cleanup(client);
283 static int uvt_cdev_init(struct uvt_cdev *cdev, const char *name,
284 unsigned int major, unsigned int minor)
286 const char *dev_info_argv[1];
292 /* TODO: libfuse makes sure that fd 0, 1 and 2 are available as
293 * standard streams, otherwise they fail. This is awkward and we
294 * should check whether this is really needed and _why_?
295 * If it is needed, fix upstream to stop that crazy! */
300 if (!major || major > 255) {
301 llog_error(cdev, "invalid major %u on cdev %p",
306 llog_error(cdev, "invalid minor %u on cdev %p",
310 if (!name || !*name) {
311 llog_error(cdev, "empty name on cdev %p",
316 llog_info(cdev, "creating device /dev/%s %u:%u on cdev %p",
317 name, major, minor, cdev);
319 ret = asprintf(&nparam, "DEVNAME=%s", name);
321 return llog_ENOMEM(cdev);
323 dev_info_argv[0] = nparam;
324 memset(&ci, 0, sizeof(ci));
325 ci.dev_major = major;
326 ci.dev_minor = minor;
327 ci.dev_info_argc = 1;
328 ci.dev_info_argv = dev_info_argv;
329 ci.flags = CUSE_UNRESTRICTED_IOCTL;
331 cdev->session = cuse_lowlevel_new(NULL, &ci, &ll_ops, cdev);
334 if (!cdev->session) {
335 llog_error(cdev, "cannot create fuse-ll session on cdev %p",
340 cdev->fd = open(cdev->ctx->cuse_file, O_RDWR | O_CLOEXEC | O_NONBLOCK);
342 llog_error(cdev, "cannot open cuse-file %s on cdev %p (%d): %m",
343 cdev->ctx->cuse_file, cdev, errno);
348 bufsize = getpagesize() + 0x1000;
349 if (bufsize < 0x21000)
352 cdev->bufsize = bufsize;
353 cdev->buf = malloc(bufsize);
355 ret = llog_ENOMEM(cdev);
359 /* Argh! libfuse does not use "const" for the "chan_ops" pointer so we
360 * actually have to cast it. Their implementation does not write into it
361 * so we can safely use a constant storage for it.
362 * TODO: Fix libfuse upstream! */
363 cdev->channel = fuse_chan_new((void*)&chan_ops, cdev->fd, bufsize,
365 if (!cdev->channel) {
366 llog_error(cdev, "cannot allocate fuse-channel on cdev %p",
372 ret = ev_eloop_new_fd(cdev->ctx->eloop, &cdev->efd, cdev->fd,
373 EV_READABLE, channel_event, cdev);
377 fuse_session_add_chan(cdev->session, cdev->channel);
381 fuse_chan_destroy(cdev->channel);
387 fuse_session_destroy(cdev->session);
391 static void uvt_cdev_destroy(struct uvt_cdev *cdev)
394 llog_warning(cdev, "cdev %p failed with error %d",
397 fuse_session_destroy(cdev->session);
398 ev_eloop_rm_fd(cdev->efd);
404 int uvt_cdev_new(struct uvt_cdev **out, struct uvt_ctx *ctx,
405 const char *name, unsigned int major, unsigned int minor)
407 struct uvt_cdev *cdev;
413 return llog_EINVAL(ctx);
415 cdev = malloc(sizeof(*cdev));
417 return llog_ENOMEM(ctx);
418 memset(cdev, 0, sizeof(*cdev));
421 cdev->llog = ctx->llog;
422 cdev->llog_data = ctx->llog_data;
423 shl_dlist_init(&cdev->clients);
425 llog_debug(cdev, "new cdev %p on ctx %p", cdev, cdev->ctx);
427 ret = shl_hook_new(&cdev->hook);
431 ret = uvt_cdev_init(cdev, name, major, minor);
435 uvt_ctx_ref(cdev->ctx);
440 shl_hook_free(cdev->hook);
447 void uvt_cdev_ref(struct uvt_cdev *cdev)
449 if (!cdev || !cdev->ref)
456 void uvt_cdev_unref(struct uvt_cdev *cdev)
458 if (!cdev || !cdev->ref || --cdev->ref)
461 llog_debug(cdev, "free cdev %p", cdev);
463 uvt_cdev_destroy(cdev);
464 shl_hook_free(cdev->hook);
465 uvt_ctx_unref(cdev->ctx);
470 int uvt_cdev_register_cb(struct uvt_cdev *cdev, uvt_cdev_cb cb, void *data)
475 return shl_hook_add_cast(cdev->hook, cb, data, false);
479 void uvt_cdev_unregister_cb(struct uvt_cdev *cdev, uvt_cdev_cb cb, void *data)
484 shl_hook_rm_cast(cdev->hook, cb, data);