uvtd: vt: implement VT_GETMODE/SETMODE ioctl state-tracking
[platform/upstream/kmscon.git] / src / uvt_cdev.c
1 /*
2  * UVT - Userspace Virtual Terminals
3  *
4  * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
5  *
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:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
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.
24  */
25
26 /*
27  * Character Devices
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
31  * client objects.
32  */
33
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <linux/major.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include "shl_dlist.h"
42 #include "shl_hook.h"
43 #include "shl_llog.h"
44 #include "uvt.h"
45 #include "uvt_internal.h"
46
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>
52
53 #define LLOG_SUBSYSTEM "uvt_cdev"
54
55 /*
56  * FUSE low-level ops
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
60  * all.
61  * We simply dispatch each call to uvt_client as this implements all the
62  * client-session related operations.
63  */
64
65 static void ll_open(fuse_req_t req, struct fuse_file_info *fi)
66 {
67         struct uvt_cdev *cdev = fuse_req_userdata(req);
68         struct uvt_client *client;
69         struct uvt_cdev_event ev;
70         int ret;
71
72         ret = uvt_client_ll_open(&client, cdev, req, fi);
73         if (ret)
74                 return;
75
76         memset(&ev, 0, sizeof(ev));
77         ev.type = UVT_CDEV_OPEN;
78         ev.client = client;
79         shl_hook_call(cdev->hook, cdev, &ev);
80 }
81
82 static void ll_destroy(void *data) {
83         struct uvt_cdev *cdev = data;
84         struct uvt_client *client;
85
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);
92         }
93 }
94
95 static const struct cuse_lowlevel_ops ll_ops = {
96         .init = NULL,
97         .destroy = ll_destroy,
98         .open = ll_open,
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,
104         .flush = NULL,
105         .fsync = NULL,
106 };
107
108 /*
109  * FUSE channel ops
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.
121  */
122
123 static int chan_receive(struct fuse_chan **chp, char *buf, size_t size)
124 {
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);
129         ssize_t res;
130
131         if (!se || !cdev)
132                 return -EINVAL;
133
134         if (!size)
135                 return 0;
136
137 restart:
138         if (fuse_session_exited(se))
139                 return 0;
140
141         res = read(fd, buf, size);
142         if (!res) {
143                 /* EOF on cuse file */
144                 llog_error(cdev, "fuse channel shut down on cdev %p", cdev);
145                 fuse_session_exit(se);
146                 return 0;
147         } else if (res < 0) {
148                 /* ENOENT is returned if the operation was interrupted, it's
149                  * safe to restart */
150                 if (errno == ENOENT)
151                         goto restart;
152
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",
157                                    cdev);
158                         fuse_session_exit(se);
159                         return 0;
160                 }
161
162                 /* EINTR and EAGAIN are simply forwarded to the caller. */
163                 if (errno == EINTR || errno == EAGAIN)
164                         return -errno;
165
166                 cdev->error = -errno;
167                 llog_error(cdev, "fuse channel read error on cdev %p (%d): %m",
168                            cdev, errno);
169                 fuse_session_exit(se);
170                 return cdev->error;
171         }
172
173         return res;
174 }
175
176 static int chan_send(struct fuse_chan *ch, const struct iovec iov[],
177                      size_t count)
178 {
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);
182         int ret;
183
184         if (!cdev || !se)
185                 return -EINVAL;
186         if (!iov || !count)
187                 return 0;
188
189         ret = writev(fd, iov, count);
190         if (ret < 0) {
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",
195                                    cdev, errno);
196                         fuse_session_exit(se);
197                 }
198                 return cdev->error;
199         }
200
201         return 0;
202 }
203
204 static const struct fuse_chan_ops chan_ops = {
205         .receive = chan_receive,
206         .send = chan_send,
207         .destroy = NULL,
208 };
209
210 /*
211  * Character Device
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.
218  */
219
220 static void uvt_cdev_hup(struct uvt_cdev *cdev, int error)
221 {
222         struct uvt_cdev_event ev;
223
224         ev_eloop_rm_fd(cdev->efd);
225         cdev->efd = NULL;
226         cdev->error = error;
227
228         memset(&ev, 0, sizeof(ev));
229         ev.type = UVT_CDEV_HUP;
230
231         shl_hook_call(cdev->hook, cdev, &ev);
232 }
233
234 static void channel_event(struct ev_fd *fd, int mask, void *data)
235 {
236         struct uvt_cdev *cdev = data;
237         int ret;
238         struct fuse_buf buf;
239         struct fuse_chan *ch;
240         struct shl_dlist *iter;
241         struct uvt_client *client;
242
243         if (!(mask & EV_READABLE)) {
244                 if (mask & (EV_HUP | EV_ERR)) {
245                         llog_error(cdev, "HUP/ERR on fuse channel on cdev %p",
246                                    cdev);
247                         uvt_cdev_hup(cdev, -EPIPE);
248                 }
249
250                 return;
251         }
252
253         memset(&buf, 0, sizeof(buf));
254         buf.mem = cdev->buf;
255         buf.size = cdev->bufsize;
256         ch = cdev->channel;
257         ret = fuse_session_receive_buf(cdev->session, &buf, &ch);
258         if (ret == -EINTR || ret == -EAGAIN) {
259                 return;
260         } else if (ret < 0) {
261                 llog_error(cdev, "fuse channel read error on cdev %p: %d",
262                            cdev, ret);
263                 uvt_cdev_hup(cdev, ret);
264                 return;
265         }
266
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);
271                 return;
272         }
273
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);
280         }
281 }
282
283 static int uvt_cdev_init(struct uvt_cdev *cdev, const char *name,
284                          unsigned int major, unsigned int minor)
285 {
286         const char *dev_info_argv[1];
287         struct cuse_info ci;
288         size_t bufsize;
289         char *nparam;
290         int ret;
291
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! */
296
297         if (!major)
298                 major = TTY_MAJOR;
299
300         if (!major || major > 255) {
301                 llog_error(cdev, "invalid major %u on cdev %p",
302                            major, cdev);
303                 return -EINVAL;
304         }
305         if (!minor) {
306                 llog_error(cdev, "invalid minor %u on cdev %p",
307                            minor, cdev);
308                 return -EINVAL;
309         }
310         if (!name || !*name) {
311                 llog_error(cdev, "empty name on cdev %p",
312                            cdev);
313                 return -EINVAL;
314         }
315
316         llog_info(cdev, "creating device /dev/%s %u:%u on cdev %p",
317                   name, major, minor, cdev);
318
319         ret = asprintf(&nparam, "DEVNAME=%s", name);
320         if (ret <= 0)
321                 return llog_ENOMEM(cdev);
322
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;
330
331         cdev->session = cuse_lowlevel_new(NULL, &ci, &ll_ops, cdev);
332         free(nparam);
333
334         if (!cdev->session) {
335                 llog_error(cdev, "cannot create fuse-ll session on cdev %p",
336                            cdev);
337                 return -ENOMEM;
338         }
339
340         cdev->fd = open(cdev->ctx->cuse_file, O_RDWR | O_CLOEXEC | O_NONBLOCK);
341         if (cdev->fd < 0) {
342                 llog_error(cdev, "cannot open cuse-file %s on cdev %p (%d): %m",
343                            cdev->ctx->cuse_file, cdev, errno);
344                 ret = -EFAULT;
345                 goto err_session;
346         }
347
348         bufsize = getpagesize() + 0x1000;
349         if (bufsize < 0x21000)
350                 bufsize = 0x21000;
351
352         cdev->bufsize = bufsize;
353         cdev->buf = malloc(bufsize);
354         if (!cdev->buf) {
355                 ret = llog_ENOMEM(cdev);
356                 goto err_fd;
357         }
358
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,
364                                       cdev);
365         if (!cdev->channel) {
366                 llog_error(cdev, "cannot allocate fuse-channel on cdev %p",
367                            cdev);
368                 ret = -ENOMEM;
369                 goto err_buf;
370         }
371
372         ret = ev_eloop_new_fd(cdev->ctx->eloop, &cdev->efd, cdev->fd,
373                               EV_READABLE, channel_event, cdev);
374         if (ret)
375                 goto err_chan;
376
377         fuse_session_add_chan(cdev->session, cdev->channel);
378         return 0;
379
380 err_chan:
381         fuse_chan_destroy(cdev->channel);
382 err_buf:
383         free(cdev->buf);
384 err_fd:
385         close(cdev->fd);
386 err_session:
387         fuse_session_destroy(cdev->session);
388         return ret;
389 }
390
391 static void uvt_cdev_destroy(struct uvt_cdev *cdev)
392 {
393         if (cdev->error)
394                 llog_warning(cdev, "cdev %p failed with error %d",
395                              cdev, cdev->error);
396
397         fuse_session_destroy(cdev->session);
398         ev_eloop_rm_fd(cdev->efd);
399         free(cdev->buf);
400         close(cdev->fd);
401 }
402
403 SHL_EXPORT
404 int uvt_cdev_new(struct uvt_cdev **out, struct uvt_ctx *ctx,
405                  const char *name, unsigned int major, unsigned int minor)
406 {
407         struct uvt_cdev *cdev;
408         int ret;
409
410         if (!ctx)
411                 return -EINVAL;
412         if (!out)
413                 return llog_EINVAL(ctx);
414
415         cdev = malloc(sizeof(*cdev));
416         if (!cdev)
417                 return llog_ENOMEM(ctx);
418         memset(cdev, 0, sizeof(*cdev));
419         cdev->ref = 1;
420         cdev->ctx = ctx;
421         cdev->llog = ctx->llog;
422         cdev->llog_data = ctx->llog_data;
423         shl_dlist_init(&cdev->clients);
424
425         llog_debug(cdev, "new cdev %p on ctx %p", cdev, cdev->ctx);
426
427         ret = shl_hook_new(&cdev->hook);
428         if (ret)
429                 goto err_free;
430
431         ret = uvt_cdev_init(cdev, name, major, minor);
432         if (ret)
433                 goto err_hook;
434
435         uvt_ctx_ref(cdev->ctx);
436         *out = cdev;
437         return 0;
438
439 err_hook:
440         shl_hook_free(cdev->hook);
441 err_free:
442         free(cdev);
443         return ret;
444 }
445
446 SHL_EXPORT
447 void uvt_cdev_ref(struct uvt_cdev *cdev)
448 {
449         if (!cdev || !cdev->ref)
450                 return;
451
452         ++cdev->ref;
453 }
454
455 SHL_EXPORT
456 void uvt_cdev_unref(struct uvt_cdev *cdev)
457 {
458         if (!cdev || !cdev->ref || --cdev->ref)
459                 return;
460
461         llog_debug(cdev, "free cdev %p", cdev);
462
463         uvt_cdev_destroy(cdev);
464         shl_hook_free(cdev->hook);
465         uvt_ctx_unref(cdev->ctx);
466         free(cdev);
467 }
468
469 SHL_EXPORT
470 int uvt_cdev_register_cb(struct uvt_cdev *cdev, uvt_cdev_cb cb, void *data)
471 {
472         if (!cdev)
473                 return -EINVAL;
474
475         return shl_hook_add_cast(cdev->hook, cb, data, false);
476 }
477
478 SHL_EXPORT
479 void uvt_cdev_unregister_cb(struct uvt_cdev *cdev, uvt_cdev_cb cb, void *data)
480 {
481         if (!cdev)
482                 return;
483
484         shl_hook_rm_cast(cdev->hook, cb, data);
485 }