Treat tabs as having eight spaces instead of four
[akaros.git] / tests / epoll_server.c
1 /* Copyright (c) 2014 The Regents of the University of California
2  * Copyright (c) 2015 Google, Inc.
3  * Barret Rhoden <brho@cs.berkeley.edu>
4  * See LICENSE for details.
5  *
6  * Echo server using epoll, runs on port 23.  Main purpose is epoll testing.
7  *
8  * If you want to build the BSD sockets version, you need to comment out the
9  * #define for PLAN9NET. */
10
11 /* Comment this out for BSD sockets */
12 //#define PLAN9NET
13
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <parlib/parlib.h>
17 #include <unistd.h>
18 #include <parlib/event.h>
19 #include <benchutil/measure.h>
20 #include <parlib/uthread.h>
21 #include <parlib/timing.h>
22
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26
27 #include <sys/epoll.h>
28
29 #ifdef PLAN9NET
30
31 #include <iplib/iplib.h>
32
33 #else
34
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39
40 #endif
41
42 int main()
43 {
44         int ret;
45         int afd, dfd, lcfd, listen_fd;
46         char adir[40], ldir[40];
47         int n;
48         char buf[256];
49         /* We'll use this to see if we actually did epoll_waits instead of
50          * blocking calls.  It's not 100%, but with a human on the other end, it
51          * should be fine. */
52         bool has_epolled = FALSE;
53
54 #ifdef PLAN9NET
55         printf("Using Plan 9's networking stack\n");
56         /* This clones a conversation (opens /net/tcp/clone), then reads the
57          * cloned fd (which is the ctl) to givure out the conv number (the
58          * line), then writes "announce [addr]" into ctl.  This "announce"
59          * command often has a "bind" in it too.  plan9 bind just sets the local
60          * addr/port.  TCP announce also does this.  Returns the ctlfd. */
61         afd = announce9("tcp!*!23", adir, 0);
62
63         if (afd < 0) {
64                 perror("Announce failure");
65                 return -1;
66         }
67         printf("Announced on line %s\n", adir);
68 #else
69         printf("Using the BSD socket shims over Plan 9's networking stack\n");
70         int srv_socket, con_socket;
71         struct sockaddr_in dest, srv = {0};
72         srv.sin_family = AF_INET;
73         srv.sin_addr.s_addr = htonl(INADDR_ANY);
74         srv.sin_port = htons(23);
75         socklen_t socksize = sizeof(struct sockaddr_in);
76
77         /* Equiv to cloning a converstation in plan 9.  The shim returns the
78          * data FD for the conversation. */
79         srv_socket = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
80         if (srv_socket < 0) {
81                 perror("Socket failure");
82                 return -1;
83         }
84         /* bind + listen is equiv to announce() in plan 9.  Note that the "bind"
85          * command is used, unlike in the plan9 announce. */
86         /* Binds our socket to the given addr/port in srv. */
87         ret = bind(srv_socket, (struct sockaddr*)&srv,
88                    sizeof(struct sockaddr_in));
89         if (ret < 0) {
90                 perror("Bind failure");
91                 return -1;
92         }
93         /* marks the socket as a listener/server */
94         ret = listen(srv_socket, 1);
95         if (ret < 0) {
96                 perror("Listen failure");
97                 return -1;
98         }
99         printf("Listened on port %d\n", ntohs(srv.sin_port));
100 #endif
101
102         /* at this point, the server has done all the prep necessary to be able
103          * to sleep/block/wait on an incoming connection. */
104         #define EP_SET_SZ 10    /* this is actually the ID of the largest FD */
105         int epfd = epoll_create(EP_SET_SZ);
106         struct epoll_event ep_ev;
107         struct epoll_event results[EP_SET_SZ];
108
109         if (epfd < 0) {
110                 perror("epoll_create");
111                 exit(-1);
112         }
113         ep_ev.events = EPOLLIN | EPOLLET;
114
115 #ifdef PLAN9NET
116
117         snprintf(buf, sizeof(buf), "%s/listen", adir);
118         listen_fd = open(buf, O_PATH);
119         if (listen_fd < 0){
120                 perror("listen fd");
121                 return -1;
122         }
123         /* This is a little subtle.  We're putting a tap on the listen file /
124          * listen_fd.  When this fires, we get an event because of that
125          * listen_fd.  But we don't actually listen or do anything to that
126          * listen_fd.  It's solely for monitoring.  We open a path, below, and
127          * we'll reattempt to do *that* operation when someone tells us that our
128          * listen tap fires. */
129         ep_ev.data.fd = listen_fd;
130         if (epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &ep_ev)) {
131                 perror("epoll_ctl_add listen");
132                 exit(-1);
133         }
134         has_epolled = FALSE;
135         while (1) {
136                 /* Opens the conversation's listen file.  This blocks til
137                  * someone connects.  When they do, a new conversation is
138                  * created, and that open returned an FD for the new conv's ctl.
139                  * listen() reads that to find out the conv number (the line)
140                  * for this new conv.  listen() returns the ctl for this new
141                  * conv.
142                  *
143                  * Non-block is for the act of listening, and applies to lcfd.
144                  */
145                 lcfd = listen9(adir, ldir, O_NONBLOCK);
146                 if (lcfd >= 0)
147                         break;
148                 if (errno != EAGAIN) {
149                         perror("Listen failure");
150                         return -1;
151                 }
152                 if (epoll_wait(epfd, results, EP_SET_SZ, -1) != 1) {
153                         perror("epoll_wait");
154                         exit(-1);
155                 }
156                 has_epolled = TRUE;
157                 assert(results[0].data.fd == listen_fd);
158                 assert(results[0].events == EPOLLIN);
159         }
160         printf("Listened and got line %s\n", ldir);
161         assert(has_epolled);
162
163         /* No longer need listen_fd.  You should CTL_DEL before closing. */
164         if (epoll_ctl(epfd, EPOLL_CTL_DEL, listen_fd, &ep_ev)) {
165                 perror("epoll_ctl_del");
166                 exit(-1);
167         }
168         close(listen_fd);
169
170         /* Writes "accept [NUM]" into the ctlfd, then opens the conv's data file
171          * and returns that fd.  Writing "accept" is a noop for most of our
172          * protocols.  */
173         dfd = accept9(lcfd, ldir);
174         if (dfd < 0) {
175                 perror("Accept failure");
176                 return -1;
177         }
178
179 #else
180
181         ep_ev.data.fd = srv_socket;
182         if (epoll_ctl(epfd, EPOLL_CTL_ADD, srv_socket, &ep_ev)) {
183                 perror("epoll_ctl_add srv_socket");
184                 exit(-1);
185         }
186         has_epolled = FALSE;
187         while (1) {
188                 /* returns an FD for a new socket. */
189                 dfd = accept(srv_socket, (struct sockaddr*)&dest, &socksize);
190                 if (dfd >= 0)
191                         break;
192                 if (errno != EAGAIN) {
193                         perror("Accept failure");
194                         return -1;
195                 }
196                 if (epoll_wait(epfd, results, EP_SET_SZ, -1) != 1) {
197                         perror("epoll_wait");
198                         exit(-1);
199                 }
200                 has_epolled = TRUE;
201                 assert(results[0].data.fd == srv_socket);
202                 assert(results[0].events == EPOLLIN);
203         }
204         printf("Accepted and got dfd %d\n", dfd);
205         assert(has_epolled);
206         if (epoll_ctl(epfd, EPOLL_CTL_DEL, srv_socket, &ep_ev)) {
207                 perror("epoll_ctl_del");
208                 while (1);
209                 exit(-1);
210         }
211
212 #endif
213
214         /* In lieu of accept4, we set the new socket's nonblock status manually.
215          * Both OSs do this.  */
216         ret = fcntl(dfd, F_SETFL, O_NONBLOCK);
217         if (ret < 0) {
218                 perror("setfl dfd");
219                 exit(-1);
220         }
221         ep_ev.data.fd = dfd;
222         if (epoll_ctl(epfd, EPOLL_CTL_ADD, dfd, &ep_ev)) {
223                 perror("epoll_ctl_add dvd");
224                 exit(-1);
225         }
226         /* echo until EOF */
227         has_epolled = FALSE;
228         printf("Server read: ");
229         while (1) {
230                 while ((n = read(dfd, buf, sizeof(buf))) > 0) {
231                         for (int i = 0; i < n; i++)
232                                 printf("%c", buf[i]);
233                         fflush(stdout);
234                         /* Should epoll on this direction too. */
235                         if (write(dfd, buf, n) < 0) {
236                                 perror("writing");
237                                 exit(-1);
238                         }
239                 }
240                 if (n == 0)
241                         break;
242                 if (epoll_wait(epfd, results, EP_SET_SZ, -1) != 1) {
243                         perror("epoll_wait 2");
244                         exit(-1);
245                 }
246                 has_epolled = TRUE;
247                 assert(results[0].data.fd == dfd);
248                 /* you might get a HUP, but keep on reading! */
249         }
250         assert(has_epolled);
251         if (epoll_ctl(epfd, EPOLL_CTL_DEL, dfd, &ep_ev)) {
252                 perror("epoll_ctl_del dfd");
253                 exit(-1);
254         }
255
256 #ifdef PLAN9NET
257         close(dfd);             /* data fd for the new conv, from listen */
258         close(lcfd);            /* ctl fd for the new conv, from listen */
259         close(afd);             /* ctl fd for the listening conv */
260 #else
261         close(dfd);             /* new connection socket, from accept */
262         close(srv_socket);
263 #endif
264 }