d31a35bb4d71f7a77e09ccb52d2a8e26e6a0bfa2
[platform/upstream/kmscon.git] / src / uvt_client.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  * Client Sessions
28  * A client session represents the internal object that corresponds to a single
29  * open-file in the kernel. That is, for each user calling open() on a cdev, we
30  * create a client-session in UVT.
31  * Note that multiple client-sessions can share the same VT object. It is up to
32  * the API user to assign clients to the correct VTs. You can even move clients
33  * from one VT to another.
34  * On the other hand, user-space can have multiple FDs open for a single
35  * client-session similar to how they can have multiple FDs for a single
36  * open-file.
37  */
38
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <linux/kd.h>
42 #include <linux/vt.h>
43 #include <signal.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <sys/epoll.h>
47 #include <termio.h>
48 #include <termios.h>
49 #include <unistd.h>
50 #include "shl_dlist.h"
51 #include "shl_llog.h"
52 #include "shl_misc.h"
53 #include "uvt.h"
54 #include "uvt_internal.h"
55
56 #define LLOG_SUBSYSTEM "uvt_client"
57
58 /*
59  * Blocking Waiters
60  * I/O has always two modes: blocking and nonblocking
61  * Nonblocking I/O is easy. We simply check whether we can actually forward the
62  * data. If we can't, we signal that back. However, blocking I/O is a lot more
63  * complex to implement. If a user submits a blocking I/O call, we have to wait
64  * until we can finish that request. In the kernel we simply put the user
65  * context asleep until we call can finish. However, in user-space via FUSE we
66  * have no user-context. Instead, we need to work around that.
67  * The most straightforward way would be to create a thread and put that thread
68  * asleep. However, this would create one thread for every blocking I/O call
69  * which seems to be way too much overhead. Also, we don't want threads in a
70  * library. Therefore, we use a different approach.
71  * For each blocking request, we create a uvt_waiter. This waiter is then linked
72  * into the waiter list and we continue with other requests. Everytime the I/O
73  * status changes, we retry the whole waiter list and try to finish the
74  * requests. If a request is done, we signal it back and destroy the waiter.
75  * This gets slightly more complex with interrupts and fuse_req objects. See
76  * below for the implementation.
77  */
78
79 enum uvt_waiter_type {
80         UVT_WAITER_INVALID              = 0x00,
81
82         UVT_WAITER_READ                 = 0x01,
83         UVT_WAITER_WRITE                = 0x02,
84
85         UVT_WAITER_ALL                  = UVT_WAITER_READ |
86                                           UVT_WAITER_WRITE,
87 };
88
89 enum uvt_waiter_flags {
90         UVT_WAITER_KILLED               = 0x01,
91         UVT_WAITER_RELEASED             = 0x02,
92 };
93
94 struct uvt_waiter {
95         struct shl_dlist list;
96         struct uvt_client *client;
97         unsigned int flags;
98         fuse_req_t req;
99
100         unsigned int type;
101
102         union {
103                 struct {
104                         size_t size;
105                         uint8_t *buf;
106                 } read;
107
108                 struct {
109                         size_t size;
110                         uint8_t *buf;
111                 } write;
112         };
113 };
114
115 static bool uvt_waiter_is_killed(struct uvt_waiter *waiter)
116 {
117         return !waiter || (waiter->flags & UVT_WAITER_KILLED);
118 }
119
120 static void uvt_waiter_set_killed(struct uvt_waiter *waiter)
121 {
122         if (waiter)
123                 waiter->flags |= UVT_WAITER_KILLED;
124 }
125
126 static bool uvt_waiter_is_released(struct uvt_waiter *waiter)
127 {
128         return !waiter || (waiter->flags & UVT_WAITER_RELEASED);
129 }
130
131 static void uvt_waiter_set_released(struct uvt_waiter *waiter)
132 {
133         if (waiter)
134                 waiter->flags |= UVT_WAITER_RELEASED;
135 }
136
137 static void uvt_waiter_interrupt(fuse_req_t req, void *data)
138 {
139         struct uvt_waiter *waiter = data;
140
141         uvt_waiter_set_killed(waiter);
142 }
143
144 static int uvt_waiter_new(struct uvt_waiter **out, struct uvt_client *client,
145                           fuse_req_t req)
146 {
147         struct uvt_waiter *waiter;
148
149         if (!client->vt)
150                 return -EPIPE;
151         if (fuse_req_interrupted(req))
152                 return -ENOENT;
153
154         waiter = malloc(sizeof(*waiter));
155         if (!waiter)
156                 return -ENOMEM;
157         memset(waiter, 0, sizeof(*waiter));
158         waiter->client = client;
159         waiter->flags = 0;
160         waiter->req = req;
161
162         fuse_req_interrupt_func(req, uvt_waiter_interrupt, waiter);
163         if (uvt_waiter_is_killed(waiter)) {
164                 fuse_req_interrupt_func(req, NULL, NULL);
165                 free(waiter);
166                 return -ENOENT;
167         }
168
169         shl_dlist_link_tail(&client->waiters, &waiter->list);
170         *out = waiter;
171         return 0;
172 }
173
174 static int uvt_waiter_new_read(struct uvt_waiter **out,
175                                struct uvt_client *client, fuse_req_t req,
176                                uint8_t *buf, size_t size)
177 {
178         struct uvt_waiter *waiter;
179         int ret;
180
181         if (!size)
182                 return -EINVAL;
183
184         ret = uvt_waiter_new(&waiter, client, req);
185         if (ret)
186                 return ret;
187         waiter->type = UVT_WAITER_READ;
188         waiter->read.size = size;
189         waiter->read.buf = buf;
190
191         *out = waiter;
192         return 0;
193 }
194
195 static int uvt_waiter_new_write(struct uvt_waiter **out,
196                                 struct uvt_client *client, fuse_req_t req,
197                                 const uint8_t *mem, size_t size)
198 {
199         struct uvt_waiter *waiter;
200         uint8_t *buf;
201         int ret;
202
203         if (!size)
204                 return -EINVAL;
205
206         buf = malloc(size);
207         if (!buf)
208                 return -ENOMEM;
209         memcpy(buf, mem, size);
210
211         ret = uvt_waiter_new(&waiter, client, req);
212         if (ret)
213                 goto err_free;
214         waiter->type = UVT_WAITER_WRITE;
215         waiter->write.size = size;
216         waiter->write.buf = buf;
217
218         *out = waiter;
219         return 0;
220
221 err_free:
222         free(buf);
223         return ret;
224 }
225
226 static void uvt_waiter_release(struct uvt_waiter *waiter, int error)
227 {
228         if (!waiter || uvt_waiter_is_released(waiter))
229                 return;
230
231         uvt_waiter_set_released(waiter);
232         fuse_req_interrupt_func(waiter->req, NULL, NULL);
233         if (error)
234                 fuse_reply_err(waiter->req, abs(error));
235 }
236
237 static void uvt_waiter_free(struct uvt_waiter *waiter, int error)
238 {
239         shl_dlist_unlink(&waiter->list);
240         uvt_waiter_release(waiter, error);
241
242         switch (waiter->type) {
243         case UVT_WAITER_READ:
244                 free(waiter->read.buf);
245                 break;
246         case UVT_WAITER_WRITE:
247                 free(waiter->write.buf);
248                 break;
249         }
250
251         free(waiter);
252 }
253
254 static void uvt_waiter_free_read(struct uvt_waiter *waiter, size_t len)
255 {
256         if (!waiter)
257                 return;
258
259         if (!uvt_waiter_is_released(waiter)) {
260                 uvt_waiter_release(waiter, 0);
261                 fuse_reply_buf(waiter->req, (void*)waiter->read.buf, len);
262         }
263         uvt_waiter_free(waiter, -EINVAL);
264 }
265
266 static void uvt_waiter_free_write(struct uvt_waiter *waiter, size_t len)
267 {
268         if (!waiter)
269                 return;
270
271         if (!uvt_waiter_is_released(waiter)) {
272                 uvt_waiter_release(waiter, 0);
273                 fuse_reply_write(waiter->req, len);
274         }
275         uvt_waiter_free(waiter, -EINVAL);
276 }
277
278 /*
279  * Client Sessions
280  * A client session is the user-space counterpart of kernel-space open-files.
281  * For each open-file we have one client-session in user-space. Users can access
282  * a single client-session via multiple file-descriptors via dup(). However, for
283  * each open() call on the device, we create a new open-file, that is, a new
284  * client-session.
285  * A single client session dispatches all the I/O calls on the file. It does
286  * blocking and nonblocking I/O, parses ioctls() and correctly performs any
287  * other state-tracking. But it does not implement any device logic. That means,
288  * the client-session doesn't provide any functionality. Instead, you have to
289  * assign a VT to the session. The client-session performs any maintenance tasks
290  * and then forwards the requests to the VT object. If no VT object is assigned,
291  * the user gets ENODEV as error.
292  * Because the client-session performs all state-tracking and parsing, the VT
293  * object can be a lot simpler and doesn't have to be aware of any FUSE objects
294  * or sessions. Instead, the VT object can concentrate on implementing a _VT_
295  * and nothing more.
296  * Furthermore, this allows to assign the same VT object to multiple different
297  * sessions at the same time. Or to assign a different VT to each session on the
298  * same device, or any other combination you want.
299  */
300
301 static void uvt_client_waiters_retry(struct uvt_client *client,
302                                      unsigned int types);
303
304 static int uvt_client_new(struct uvt_client **out, struct uvt_cdev *cdev)
305 {
306         struct uvt_client *client;
307
308         if (!cdev)
309                 return -EINVAL;
310         if (!out)
311                 return llog_EINVAL(cdev);
312
313         client = malloc(sizeof(*client));
314         if (!client)
315                 return llog_ENOMEM(cdev);
316         memset(client, 0, sizeof(*client));
317         client->ref = 1;
318         client->cdev = cdev;
319         client->llog = cdev->llog;
320         client->llog_data = cdev->llog_data;
321         shl_dlist_init(&client->waiters);
322
323         llog_debug(client, "new client %p on cdev %p", client, cdev);
324
325         shl_dlist_link_tail(&cdev->clients, &client->list);
326         *out = client;
327         return 0;
328 }
329
330 SHL_EXPORT
331 void uvt_client_ref(struct uvt_client *client)
332 {
333         if (!client || !client->ref)
334                 return;
335
336         ++client->ref;
337 }
338
339 SHL_EXPORT
340 void uvt_client_unref(struct uvt_client *client)
341 {
342         if (!client || !client->ref || --client->ref)
343                 return;
344
345         llog_debug(client, "free client %p", client);
346
347         uvt_client_kill(client);
348         free(client);
349 }
350
351 /*
352  * This must be called after each event dispatch round. It cleans up all
353  * interrupted/killed readers. The readers cannot be released right away due
354  * to heavy locking inside of FUSE. We have to delay these tasks and clean up
355  * after each dispatch round.
356  */
357 void uvt_client_cleanup(struct uvt_client *client)
358 {
359         struct shl_dlist *i, *tmp;
360         struct uvt_waiter *waiter;
361
362         if (!client)
363                 return;
364
365         shl_dlist_for_each_safe(i, tmp, &client->waiters) {
366                 waiter = shl_dlist_entry(i, struct uvt_waiter, list);
367                 if (uvt_waiter_is_killed(waiter))
368                         uvt_waiter_free(waiter, -ENOENT);
369         }
370 }
371
372 static void uvt_client_waiters_release(struct uvt_client *client, int error)
373 {
374         struct uvt_waiter *waiter;
375         int err;
376
377         if (!client)
378                 return;
379
380         while (!shl_dlist_empty(&client->waiters)) {
381                 waiter = shl_dlist_entry(client->waiters.next,
382                                          struct uvt_waiter, list);
383
384                 if (uvt_waiter_is_killed(waiter))
385                         err = -ENOENT;
386                 else
387                         err = error;
388
389                 uvt_waiter_free(waiter, err);
390         }
391 }
392
393 SHL_EXPORT
394 bool uvt_client_is_dead(struct uvt_client *client)
395 {
396         return !client || !client->cdev;
397 }
398
399 SHL_EXPORT
400 void uvt_client_kill(struct uvt_client *client)
401 {
402         if (!client || !client->cdev)
403                 return;
404
405         llog_debug(client, "kill client %p", client);
406
407         if (client->ph) {
408                 fuse_notify_poll(client->ph);
409                 fuse_pollhandle_destroy(client->ph);
410                 client->ph = NULL;
411         }
412
413         shl_dlist_unlink(&client->list);
414         client->cdev = NULL;
415         uvt_client_set_vt(client, NULL, NULL);
416         uvt_client_waiters_release(client, -EPIPE);
417 }
418
419 /*
420  * We allow recursive VT-actions so we need sophisticated locking. That is, we
421  * allow each client->vt->XY() function to itself raise VT events. These VT
422  * events cause our uvt_client_vt_event() handler to call
423  * uvt_client_waiters_retry(). But uvt_client_waiters_retry() itself can call
424  * VT functions again.
425  * This recursion isn't particularly bad, as any _proper_ implementation would
426  * have an upper limit (which is the number of active waiters). However, to
427  * avoid wasting stack space for recursion, we lock the VT when calling VT
428  * callbacks. The uvt_client_vt_event() handler checks whether the callbacks are
429  * currently locked and sets markers otherwise. These markers cause our
430  * unlock-function to notice that we got events in between and then retries all
431  * interrupted operations.
432  * The client->vt_in_unlock is used to avoid recursion in unlock() itself.
433  */
434
435 static bool uvt_client_lock_vt(struct uvt_client *client)
436 {
437         if (!client || client->vt_locked)
438                 return false;
439
440         client->vt_locked = true;
441         return true;
442 }
443
444 static void uvt_client_unlock_vt(struct uvt_client *client)
445 {
446         unsigned int retry;
447
448         if (!client || !client->vt_locked)
449                 return;
450
451         client->vt_locked = false;
452         if (client->vt_in_unlock)
453                 return;
454
455         while (client->vt_retry) {
456                 retry = client->vt_retry;
457                 client->vt_retry = 0;
458
459                 client->vt_in_unlock = true;
460                 uvt_client_waiters_retry(client, retry);
461                 client->vt_in_unlock = false;
462         }
463 }
464
465 static void uvt_client_waiters_retry(struct uvt_client *client,
466                                      unsigned int types)
467 {
468         struct shl_dlist *iter, *tmp;
469         struct uvt_waiter *waiter;
470         int ret;
471
472         if (!client || !types || uvt_client_is_dead(client) || !client->vt)
473                 return;
474
475         if (!uvt_client_lock_vt(client))
476                 return;
477
478         shl_dlist_for_each_safe(iter, tmp, &client->waiters) {
479                 if (!types)
480                         break;
481
482                 waiter = shl_dlist_entry(iter, struct uvt_waiter, list);
483                 if (!(waiter->type & types) || uvt_waiter_is_killed(waiter))
484                         continue;
485
486                 if (waiter->type == UVT_WAITER_READ) {
487                         ret = client->vt->read(client->vt_data,
488                                                waiter->read.buf,
489                                                waiter->read.size);
490                         if (ret == -EAGAIN) {
491                                 types &= ~UVT_WAITER_READ;
492                                 continue;
493                         } else if (ret < 0) {
494                                 uvt_waiter_free(waiter, ret);
495                         } else {
496                                 if (ret > waiter->read.size)
497                                         ret = waiter->read.size;
498                                 uvt_waiter_free_read(waiter, ret);
499                         }
500                 } else if (waiter->type == UVT_WAITER_WRITE) {
501                         ret = client->vt->write(client->vt_data,
502                                                 waiter->write.buf,
503                                                 waiter->write.size);
504                         if (ret == -EAGAIN) {
505                                 types &= ~UVT_WAITER_WRITE;
506                                 continue;
507                         } else if (ret < 0) {
508                                 uvt_waiter_free(waiter, ret);
509                         } else {
510                                 if (ret > waiter->write.size)
511                                         ret = waiter->write.size;
512                                 uvt_waiter_free_write(waiter, ret);
513                         }
514                 }
515         }
516
517         uvt_client_unlock_vt(client);
518 }
519
520 static void uvt_client_vt_event(void *vt, struct uvt_vt_event *ev, void *data)
521 {
522         struct uvt_client *client = data;
523
524         if (uvt_client_is_dead(client))
525                 return;
526
527         switch (ev->type) {
528         case UVT_VT_HUP:
529                 uvt_client_kill(client);
530                 break;
531         case UVT_VT_TTY:
532                 switch (ev->tty.type) {
533                 case UVT_TTY_HUP:
534                         uvt_client_kill(client);
535                         break;
536                 case UVT_TTY_READ:
537                         if (client->ph)
538                                 fuse_notify_poll(client->ph);
539                         client->vt_retry |= UVT_WAITER_READ;
540                         break;
541                 case UVT_TTY_WRITE:
542                         if (client->ph)
543                                 fuse_notify_poll(client->ph);
544                         client->vt_retry |= UVT_WAITER_WRITE;
545                         break;
546                 }
547                 break;
548         }
549
550         uvt_client_waiters_retry(client, client->vt_retry);
551 }
552
553 SHL_EXPORT
554 int uvt_client_set_vt(struct uvt_client *client, const struct uvt_vt_ops *vt,
555                       void *vt_data)
556 {
557         int ret;
558
559         if (!client)
560                 return -EINVAL;
561         if (uvt_client_is_dead(client) && vt)
562                 return -EINVAL;
563
564         if (client->vt) {
565                 client->vt->unregister_cb(client->vt_data, uvt_client_vt_event,
566                                           client);
567                 client->vt->unref(client->vt_data);
568         }
569
570         client->vt = vt;
571         client->vt_data = vt_data;
572
573         if (client->vt) {
574                 ret = client->vt->register_cb(client->vt_data,
575                                               uvt_client_vt_event, client);
576                 if (!ret) {
577                         client->vt->ref(client->vt_data);
578                         uvt_client_waiters_retry(client, UVT_WAITER_ALL);
579                         return 0;
580                 }
581         } else {
582                 ret = 0;
583         }
584
585         client->vt = NULL;
586         client->vt_data = NULL;
587         uvt_client_waiters_release(client, -ENODEV);
588         return ret;
589 }
590
591 /*
592  * Internal FUSE low-level fops implementation
593  * These functions implement the callbacks used by the CUSE/FUSE-ll
594  * implementation in uvt_cdev objects. Our infrastructure allows to provide
595  * other callbacks, too, but this is currently not needed. Moreover, I cannot
596  * see any reason to add them to the public API as nobody would want anything
597  * different than CUSE/FUSE as frontend.
598  */
599
600 int uvt_client_ll_open(struct uvt_client **out, struct uvt_cdev *cdev,
601                        fuse_req_t req, struct fuse_file_info *fi)
602 {
603         struct uvt_client *client;
604         int ret;
605
606         ret = uvt_client_new(&client, cdev);
607         if (ret) {
608                 fuse_reply_err(req, -ret);
609                 return ret;
610         }
611
612         fi->fh = (uint64_t)(uintptr_t)(void*)client;
613         fi->nonseekable = 1;
614         fi->direct_io = 1;
615         ret = fuse_reply_open(req, fi);
616         if (ret < 0) {
617                 uvt_client_kill(client);
618                 uvt_client_unref(client);
619                 return -EFAULT;
620         }
621
622         *out = client;
623         return 0;
624 }
625
626 void uvt_client_ll_release(fuse_req_t req, struct fuse_file_info *fi)
627 {
628         struct uvt_client *client = (void*)(uintptr_t)fi->fh;
629
630         if (!client) {
631                 fuse_reply_err(req, EINVAL);
632                 return;
633         }
634
635         uvt_client_kill(client);
636         uvt_client_unref(client);
637         fuse_reply_err(req, 0);
638 }
639
640 void uvt_client_ll_read(fuse_req_t req, size_t size, off_t off,
641                         struct fuse_file_info *fi)
642 {
643         struct uvt_client *client = (void*)(uintptr_t)fi->fh;
644         struct uvt_waiter *waiter;
645         uint8_t *buf;
646         int ret;
647
648         if (!client) {
649                 fuse_reply_err(req, EINVAL);
650                 return;
651         } else if (uvt_client_is_dead(client)) {
652                 fuse_reply_err(req, EPIPE);
653                 return;
654         } else if (off) {
655                 fuse_reply_err(req, EINVAL);
656                 return;
657         } else if (!size) {
658                 fuse_reply_buf(req, "", 0);
659                 return;
660         } else if (!client->vt) {
661                 fuse_reply_err(req, ENODEV);
662                 return;
663         }
664
665         buf = malloc(size);
666         if (!buf) {
667                 fuse_reply_err(req, ENOMEM);
668                 return;
669         }
670
671         ret = client->vt->read(client->vt_data, buf, size);
672         if (ret >= 0) {
673                 if (ret > size)
674                         ret = size;
675
676                 fuse_reply_buf(req, (void*)buf, ret);
677                 free(buf);
678                 return;
679         } else if (ret == -EAGAIN && !(fi->flags & O_NONBLOCK)) {
680                 ret = uvt_waiter_new_read(&waiter, client, req, buf, size);
681                 if (!ret)
682                         return;
683         }
684
685         fuse_reply_err(req, -ret);
686         free(buf);
687 }
688
689 void uvt_client_ll_write(fuse_req_t req, const char *buf, size_t size,
690                          off_t off, struct fuse_file_info *fi)
691 {
692         struct uvt_client *client = (void*)(uintptr_t)fi->fh;
693         struct uvt_waiter *waiter;
694         int ret;
695
696         if (!client) {
697                 fuse_reply_err(req, EINVAL);
698                 return;
699         } else if (uvt_client_is_dead(client)) {
700                 fuse_reply_err(req, EPIPE);
701                 return;
702         } else if (off) {
703                 fuse_reply_err(req, EINVAL);
704                 return;
705         } else if (!size) {
706                 fuse_reply_write(req, 0);
707                 return;
708         } else if (!client->vt) {
709                 fuse_reply_err(req, ENODEV);
710                 return;
711         }
712
713         ret = client->vt->write(client->vt_data, (void*)buf, size);
714         if (ret >= 0) {
715                 if (ret > size)
716                         ret = size;
717
718                 fuse_reply_write(req, ret);
719                 return;
720         } else if (ret == -EAGAIN && !(fi->flags & O_NONBLOCK)) {
721                 ret = uvt_waiter_new_write(&waiter, client, req, (void*)buf,
722                                            size);
723                 if (!ret)
724                         return;
725         }
726
727         fuse_reply_err(req, -ret);
728 }
729
730 void uvt_client_ll_poll(fuse_req_t req, struct fuse_file_info *fi,
731                         struct fuse_pollhandle *ph)
732 {
733         struct uvt_client *client = (void*)(uintptr_t)fi->fh;
734         unsigned int flags, fl;
735
736         if (!client) {
737                 fuse_reply_err(req, EINVAL);
738                 return;
739         } else if (uvt_client_is_dead(client)) {
740                 if (ph)
741                         fuse_pollhandle_destroy(ph);
742                 fuse_reply_poll(req, EPOLLHUP | EPOLLIN | EPOLLOUT |
743                                      EPOLLWRNORM | EPOLLRDNORM);
744                 return;
745         }
746
747         if (client->ph)
748                 fuse_pollhandle_destroy(client->ph);
749         client->ph = ph;
750
751         if (!client->vt) {
752                 fuse_reply_err(req, ENODEV);
753                 return;
754         }
755
756         flags = 0;
757         fl = client->vt->poll(client->vt_data);
758         if (fl & UVT_TTY_HUP)
759                 flags |= EPOLLHUP;
760         if (fl & UVT_TTY_READ)
761                 flags |= EPOLLIN | EPOLLRDNORM;
762         if (fl & UVT_TTY_WRITE)
763                 flags |= EPOLLOUT | EPOLLWRNORM;
764
765         fuse_reply_poll(req, flags);
766 }
767
768 static bool ioctl_param(fuse_req_t req, void *arg, size_t in_want,
769                         size_t in_have, size_t out_want, size_t out_have)
770 {
771         bool retry;
772         struct iovec in, out;
773         size_t in_num, out_num;
774
775         retry = false;
776         memset(&in, 0, sizeof(in));
777         in_num = 0;
778         memset(&out, 0, sizeof(out));
779         out_num = 0;
780
781         if (in_want) {
782                 if (!in_have) {
783                         retry = true;
784                 } else if (in_have < in_want) {
785                         fuse_reply_err(req, EFAULT);
786                         return true;
787                 }
788
789                 in.iov_base = arg;
790                 in.iov_len = in_want;
791                 in_num = 1;
792         }
793         if (out_want) {
794                 if (!out_have) {
795                         retry = true;
796                 } else if (out_have < out_want) {
797                         fuse_reply_err(req, EFAULT);
798                         return true;
799                 }
800
801                 out.iov_base = arg;
802                 out.iov_len = out_want;
803                 out_num = 1;
804         }
805
806         if (retry)
807                 fuse_reply_ioctl_retry(req, in_num ? &in : NULL, in_num,
808                                        out_num ? &out : NULL, out_num);
809         return retry;
810 }
811
812 void uvt_client_ll_ioctl(fuse_req_t req, int cmd, void *arg,
813                          struct fuse_file_info *fi, unsigned int flags,
814                          const void *in_buf, size_t in_bufsz, size_t out_bufsz)
815 {
816         struct uvt_client *client = (void*)(uintptr_t)fi->fh;
817         uintptr_t uarg = (uintptr_t)arg;
818         bool compat;
819         int ret;
820         struct vt_stat vtstat;
821         struct vt_mode vtmode;
822         unsigned int uval;
823
824         if (!client) {
825                 fuse_reply_err(req, EINVAL);
826                 return;
827         } else if (uvt_client_is_dead(client)) {
828                 fuse_reply_err(req, EPIPE);
829                 return;
830         } else if (!client->vt) {
831                 fuse_reply_err(req, ENODEV);
832                 return;
833         }
834
835         /* TODO: fix compat-ioctls */
836         compat = !!(flags & FUSE_IOCTL_COMPAT);
837         if (compat) {
838                 fuse_reply_err(req, EOPNOTSUPP);
839                 return;
840         }
841
842         switch (cmd) {
843
844         /* TTY ioctls */
845
846         case TCFLSH:
847                 if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
848                         return;
849                 if (!client->vt->ioctl_TCFLSH) {
850                         fuse_reply_err(req, EOPNOTSUPP);
851                 } else {
852                         ret = client->vt->ioctl_TCFLSH(client->vt_data,
853                                                        (unsigned long)uarg);
854                         if (ret)
855                                 fuse_reply_err(req, abs(ret));
856                         else
857                                 fuse_reply_ioctl(req, 0, NULL, 0);
858                 }
859                 break;
860
861         case TIOCPKT:
862         case TCXONC:
863         case TCGETS:
864         case TCSETS:
865         case TCSETSF:
866         case TCSETSW:
867         case TCGETA:
868         case TCSETA:
869         case TCSETAF:
870         case TCSETAW:
871         case TIOCGLCKTRMIOS:
872         case TIOCSLCKTRMIOS:
873         case TCGETX:
874         case TCSETX:
875         case TCSETXW:
876         case TCSETXF:
877         case TIOCGSOFTCAR:
878         case TIOCSSOFTCAR:
879                 fuse_reply_err(req, EOPNOTSUPP);
880                 break;
881
882         /* VT ioctls */
883
884         case VT_ACTIVATE:
885                 if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
886                         return;
887                 if (!client->vt->ioctl_VT_ACTIVATE) {
888                         fuse_reply_err(req, EOPNOTSUPP);
889                 } else {
890                         ret = client->vt->ioctl_VT_ACTIVATE(client->vt_data,
891                                                         (unsigned long)uarg);
892                         if (ret)
893                                 fuse_reply_err(req, abs(ret));
894                         else
895                                 fuse_reply_ioctl(req, 0, NULL, 0);
896                 }
897                 break;
898
899         case VT_WAITACTIVE:
900                 if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
901                         return;
902                 if (!client->vt->ioctl_VT_WAITACTIVE) {
903                         fuse_reply_err(req, EOPNOTSUPP);
904                 } else {
905                         ret = client->vt->ioctl_VT_WAITACTIVE(client->vt_data,
906                                                         (unsigned long)uarg);
907                         if (ret)
908                                 fuse_reply_err(req, abs(ret));
909                         else
910                                 fuse_reply_ioctl(req, 0, NULL, 0);
911                 }
912                 break;
913
914         case VT_GETSTATE:
915                 if (ioctl_param(req, arg, 0, in_bufsz,
916                                 sizeof(struct vt_stat), out_bufsz))
917                         return;
918                 if (!client->vt->ioctl_VT_GETSTATE) {
919                         fuse_reply_err(req, EOPNOTSUPP);
920                 } else {
921                         memset(&vtstat, 0, sizeof(vtstat));
922                         ret = client->vt->ioctl_VT_GETSTATE(client->vt_data,
923                                                             &vtstat);
924                         if (ret)
925                                 fuse_reply_err(req, abs(ret));
926                         else
927                                 fuse_reply_ioctl(req, 0, &vtstat,
928                                                  sizeof(vtstat));
929                 }
930                 break;
931
932         case VT_OPENQRY:
933                 if (ioctl_param(req, arg, 0, in_bufsz,
934                                 sizeof(unsigned int), out_bufsz))
935                         return;
936                 if (!client->vt->ioctl_VT_OPENQRY) {
937                         fuse_reply_err(req, EOPNOTSUPP);
938                 } else {
939                         uval = 0;
940                         ret = client->vt->ioctl_VT_OPENQRY(client->vt_data,
941                                                            &uval);
942                         if (ret)
943                                 fuse_reply_err(req, abs(ret));
944                         else
945                                 fuse_reply_ioctl(req, 0, &uval, sizeof(uval));
946                 }
947                 break;
948
949         case VT_GETMODE:
950                 if (ioctl_param(req, arg, 0, in_bufsz,
951                                 sizeof(struct vt_mode), out_bufsz))
952                         return;
953                 if (!client->vt->ioctl_VT_GETMODE) {
954                         fuse_reply_err(req, EOPNOTSUPP);
955                 } else {
956                         memset(&vtmode, 0, sizeof(vtmode));
957                         ret = client->vt->ioctl_VT_GETMODE(client->vt_data,
958                                                            &vtmode);
959                         if (ret)
960                                 fuse_reply_err(req, abs(ret));
961                         else
962                                 fuse_reply_ioctl(req, 0, &vtmode,
963                                                  sizeof(vtmode));
964                 }
965                 break;
966
967         case VT_SETMODE:
968                 if (ioctl_param(req, arg, sizeof(struct vt_mode), in_bufsz,
969                                 0, out_bufsz))
970                         return;
971                 if (!client->vt->ioctl_VT_SETMODE) {
972                         fuse_reply_err(req, EOPNOTSUPP);
973                 } else {
974                         ret = client->vt->ioctl_VT_SETMODE(client->vt_data,
975                                                 (const struct vt_mode*)in_buf);
976                         if (ret)
977                                 fuse_reply_err(req, abs(ret));
978                         else
979                                 fuse_reply_ioctl(req, 0, NULL, 0);
980                 }
981                 break;
982
983         case VT_RELDISP:
984                 if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
985                         return;
986                 if (!client->vt->ioctl_VT_RELDISP) {
987                         fuse_reply_err(req, EOPNOTSUPP);
988                 } else {
989                         ret = client->vt->ioctl_VT_RELDISP(client->vt_data,
990                                                         (unsigned long)uarg);
991                         if (ret)
992                                 fuse_reply_err(req, abs(ret));
993                         else
994                                 fuse_reply_ioctl(req, 0, NULL, 0);
995                 }
996                 break;
997
998         case KDGETMODE:
999                 if (ioctl_param(req, arg, 0, in_bufsz,
1000                                 sizeof(unsigned int), out_bufsz))
1001                         return;
1002                 if (!client->vt->ioctl_KDGETMODE) {
1003                         fuse_reply_err(req, EOPNOTSUPP);
1004                 } else {
1005                         uval = 0;
1006                         ret = client->vt->ioctl_KDGETMODE(client->vt_data,
1007                                                           &uval);
1008                         if (ret)
1009                                 fuse_reply_err(req, abs(ret));
1010                         else
1011                                 fuse_reply_ioctl(req, 0, &uval, sizeof(uval));
1012                 }
1013                 break;
1014
1015         case KDSETMODE:
1016                 if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
1017                         return;
1018                 if (!client->vt->ioctl_KDSETMODE) {
1019                         fuse_reply_err(req, EOPNOTSUPP);
1020                 } else {
1021                         ret = client->vt->ioctl_KDSETMODE(client->vt_data,
1022                                                         (unsigned int)uarg);
1023                         if (ret)
1024                                 fuse_reply_err(req, abs(ret));
1025                         else
1026                                 fuse_reply_ioctl(req, 0, NULL, 0);
1027                 }
1028                 break;
1029
1030         case KDGKBMODE:
1031                 if (ioctl_param(req, arg, 0, in_bufsz,
1032                                 sizeof(unsigned int), out_bufsz))
1033                         return;
1034                 if (!client->vt->ioctl_KDGKBMODE) {
1035                         fuse_reply_err(req, EOPNOTSUPP);
1036                 } else {
1037                         uval = 0;
1038                         ret = client->vt->ioctl_KDGKBMODE(client->vt_data,
1039                                                           &uval);
1040                         if (ret)
1041                                 fuse_reply_err(req, abs(ret));
1042                         else
1043                                 fuse_reply_ioctl(req, 0, &uval, sizeof(uval));
1044                 }
1045                 break;
1046
1047         case KDSKBMODE:
1048                 if (ioctl_param(req, arg, 0, in_bufsz, 0, out_bufsz))
1049                         return;
1050                 if (!client->vt->ioctl_KDSKBMODE) {
1051                         fuse_reply_err(req, EOPNOTSUPP);
1052                 } else {
1053                         ret = client->vt->ioctl_KDSKBMODE(client->vt_data,
1054                                                         (unsigned int)uarg);
1055                         if (ret)
1056                                 fuse_reply_err(req, abs(ret));
1057                         else
1058                                 fuse_reply_ioctl(req, 0, NULL, 0);
1059                 }
1060                 break;
1061
1062         case TIOCLINUX:
1063         case KIOCSOUND:
1064         case KDMKTONE:
1065         case KDGKBTYPE:
1066         case KDADDIO:
1067         case KDDELIO:
1068         case KDENABIO:
1069         case KDDISABIO:
1070         case KDKBDREP:
1071         case KDMAPDISP:
1072         case KDUNMAPDISP:
1073         case KDGKBMETA:
1074         case KDSKBMETA:
1075         case KDGETKEYCODE:
1076         case KDSETKEYCODE:
1077         case KDGKBENT:
1078         case KDSKBENT:
1079         case KDGKBSENT:
1080         case KDSKBSENT:
1081         case KDGKBDIACR:
1082         case KDSKBDIACR:
1083         case KDGKBDIACRUC:
1084         case KDSKBDIACRUC:
1085         case KDGETLED:
1086         case KDSETLED:
1087         case KDGKBLED:
1088         case KDSKBLED:
1089         case KDSIGACCEPT:
1090         case VT_SETACTIVATE:
1091         case VT_DISALLOCATE:
1092         case VT_RESIZE:
1093         case VT_RESIZEX:
1094         case GIO_FONT:
1095         case PIO_FONT:
1096         case GIO_CMAP:
1097         case PIO_CMAP:
1098         case GIO_FONTX:
1099         case PIO_FONTX:
1100         case PIO_FONTRESET:
1101         case KDFONTOP:
1102         case GIO_SCRNMAP:
1103         case PIO_SCRNMAP:
1104         case GIO_UNISCRNMAP:
1105         case PIO_UNISCRNMAP:
1106         case PIO_UNIMAPCLR:
1107         case GIO_UNIMAP:
1108         case PIO_UNIMAP:
1109         case VT_LOCKSWITCH:
1110         case VT_UNLOCKSWITCH:
1111         case VT_GETHIFONTMASK:
1112         case VT_WAITEVENT:
1113                 fuse_reply_err(req, EOPNOTSUPP);
1114                 break;
1115         default:
1116                 fuse_reply_err(req, EINVAL);
1117                 break;
1118         }
1119 }