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