Add a chan_ctl devop; support fcntl on chans
[akaros.git] / tools / compilers / gcc-glibc / glibc-2.19-akaros / sysdeps / akaros / fcntl-ext.c
1 /* Copyright (c) 2015 Google, Inc.
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details. */
4
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <stdarg.h>
8 #include <stdlib.h>
9 #include <ros/syscall.h>
10
11 #include "plan9_sockets.h"
12
13 /* O_NONBLOCK is handled in userspace for conversations set up with socket
14  * shims for compatibility with existing code.  9ns code should not use fcntl to
15  * attempt to set nonblock.
16  *
17  * Note that FDs that resulted from open of a clone with O_NONBLOCK will still
18  * have O_NONBLOCK set on the chan for the ctl, but only for the first chan that
19  * represents ctl.  So some 9ns code can see O_NONBLOCK from getfl. */
20
21 static int toggle_nonblock(Rock *r)
22 {
23         int ret, ctlfd;
24         char on_msg[] = "nonblock on";
25         char off_msg[] = "nonblock off";
26
27         ctlfd = open(r->ctl, O_RDWR);
28         if (!ctlfd)
29                 return -1;
30         if (r->sopts & SOCK_NONBLOCK) {
31                 ret = write(ctlfd, off_msg, sizeof(off_msg));
32                 if (ret < 0)
33                         goto out;
34                 r->sopts &= ~SOCK_NONBLOCK;
35         } else {
36                 ret = write(ctlfd, on_msg, sizeof(on_msg));
37                 if (ret < 0)
38                         goto out;
39                 r->sopts |= SOCK_NONBLOCK;
40         }
41         ret = 0;
42 out:
43         close(ctlfd);
44         return ret;
45 }
46
47 /* Sets the fd's nonblock status IAW arg's settings.  Returns 0 on success.
48  * Modifies *arg such that the new version is the one that should be passed to
49  * the kernel for setfl. */
50 static int set_nonblock_status(int fd, int *arg)
51 {
52         int ret = 0;
53         Rock *r = _sock_findrock(fd, 0);
54         if (!r) {
55                 /* We don't clear O_NONBLOCK, so non-sockets will still pass it to the
56                  * kernel and probably error out */
57                 return 0;
58         }
59         /* XOR of two flag tests, checking if we need to change anything */
60         if (!(r->sopts & SOCK_NONBLOCK) != !(*arg & O_NONBLOCK))
61                 ret = toggle_nonblock(r);
62         /* Either way, we clear O_NONBLOCK, so we don't ask the kernel to set it */
63         *arg &= ~O_NONBLOCK;
64         return ret;
65 }
66
67 /* If fd is a nonblocking socket, this returns O_NONBLOCK */
68 static int get_nonblock_status(int fd)
69 {
70         Rock *r = _sock_findrock(fd, 0);
71         if (!r)
72                 return 0;
73         return r->sopts & SOCK_NONBLOCK ? O_NONBLOCK : 0;
74 }
75
76 /* in sysdeps/akaros/fcntl.c */
77 extern int __vfcntl(int fd, int cmd, va_list vl);
78
79 int fcntl(int fd, int cmd, ...)
80 {
81         int ret, arg;
82         va_list vl;
83         va_start(vl, cmd);
84         switch (cmd) {
85                 case F_GETFL:
86                         ret = ros_syscall(SYS_fcntl, fd, cmd, 0, 0, 0, 0);
87                         if (ret != -1)
88                                 ret |= get_nonblock_status(fd);
89                         break;
90                 case F_SETFL:
91                         arg = va_arg(vl, int);
92                         if ((ret = set_nonblock_status(fd, &arg)))
93                                 return ret;
94                         ret = ros_syscall(SYS_fcntl, fd, cmd, arg, 0, 0, 0);
95                         break;
96                 default:
97                         ret = __vfcntl(fd, cmd, vl);
98         }
99         va_end(vl);
100         return ret;
101 }