Standardize License information
[openafs.git] / src / lwp / test / selserver.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /* selserver.c tests the IOMGR_Select interface. Specifically it verifies
11  * that the read/write/exception lists work correctly with file descriptors
12  * larger than 31. Generally, the calls to IOMGR_Select pass in all the
13  * read, write and exception fd_sets. When handling is complete, the file
14  * descriptor is cleared from the set. Then if fd_set is checked to make sure
15  * there are no other bits set. The fd_sets which should have had nothing set
16  * are also checked.
17  * 
18  * The client can send one of the following:
19  * -end - shoots the selserver.
20  * -delay n - The selserver thread handling this request should sleep for
21  *            n seconds before accepting data. Used in conjuction with -write
22  *            so that the write STREAM can fill. This tests the IOMGR_Select
23  *            write fd_sets.
24  * -write n - writes n bytes to the selserver. If -delay is not set, a default
25  *            of 5 seconds is used.
26  * -soob - Send an out-of-band message to selserver. Tests the exception
27  *            fd_set.
28  */
29
30 /* Typical test scanario:
31  * selclient -soob : used to test exception fd's on selserver.
32  * selclient -delay 20 -write 150000 : used to test read/write fd's.
33  * Adjust delay to 40 seconds if running loopback. Times and sizes derived
34  * on IRIX 6.2 and 6.4.
35  */
36
37 #include <unistd.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <errno.h>
42 #include <sys/select.h>
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <bstring.h>
46 #include <sys/time.h>
47 #include <netinet/in.h>
48 #include <netdb.h>
49 #include <signal.h>
50 #include <sys/ioctl.h>
51 #include <assert.h>
52 #include <sys/stat.h>
53
54 #include <afs/param.h>
55
56 #include "../lwp.h"
57 #include "seltest.h"
58
59 extern int IOMGR_Select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
60
61
62 void sendTest(int, int, int);
63 void handleRequest(char *);
64
65 /* Server Pool */
66 #define MAX_THREADS 5
67 int nThreads = 0;
68
69 typedef struct {
70 #define CH_FREE 0
71 #define CH_INUSE 1
72     int ch_state;
73     int ch_fd; 
74     struct sockaddr_in ch_addr;
75     fd_set ch_read, ch_write, ch_except;
76     PROCESS ch_pid;
77 } clientHandle_t;
78
79 void handleWrite(clientHandle_t *, selcmd_t *);
80
81 clientHandle_t clientHandles[MAX_THREADS];
82
83 clientHandle_t *getClientHandle()
84 {
85     int i;
86     for (i=0; i<MAX_THREADS; i++) {
87         if (clientHandles[i].ch_state == CH_FREE) {
88             clientHandles[i].ch_state = CH_INUSE;
89             nThreads ++;
90             return &clientHandles[i];
91         }
92     }
93     Die(1, "No free client handles!\n");
94
95     return (clientHandle_t*)NULL; /* quiet compiler. */
96 }
97
98 int nSigIO = 0;
99 void sigIO()
100 {
101     nSigIO++;
102 }
103
104
105 Usage()
106 {
107     printf("Usage: selserver [-fd n] [-syssel] port\n");
108     printf("\t-fd n\tUse file descriptor n for socket.\n");
109     exit(1);
110 }
111
112 char *program;
113
114 main(int ac, char **av)
115 {
116     int i;
117     int on = 1;
118     short port = -1; /* host order. */
119     int setFD = 0;
120     struct sockaddr_in saddr;
121     int acceptFD;
122     clientHandle_t *clientHandle;
123     int code;
124     int addr_len;
125     PROCESS pid;
126     fd_set *rfds, *wfds, *efds;
127     int sockFD;
128
129     program = av[0];
130
131     if (ac < 2)
132         Usage();
133
134 /*    lwp_debug = 1; */
135
136     signal(SIGIO, sigIO);
137
138
139     for (i=1; i<ac; i++) {
140         if (!strcmp("-fd", av[i])) {
141             if (++i>=ac) {
142                 printf("Missing number for -fd option.\n");
143                 Usage();
144             }
145             setFD = atoi(av[i]);
146             if (setFD<=2) {
147                 printf("%d: file descriptor must be at least 3.\n", setFD);
148                 Usage();
149             }
150         }
151         else {
152             if (port == -1) {
153                 port = atoi(av[i]);
154                 if (port<=0) {
155                     printf("%s: port must be at least 1\n", av[i]);
156                     Usage();
157                 }
158             }
159             else {
160                 printf("%s: Unknown argument.\n", av[i]);
161             }
162         }
163     }
164
165     if (port == -1) {
166         printf("Missing port.\n");
167         Usage();
168     }
169
170     if (!setFD) {
171         setFD = 31;
172         printf("Using default socket of %d.\n", setFD);
173     }
174
175     OpenFDs(setFD);
176
177     IOMGR_Initialize();
178
179     /* Setup server processes */
180     for (i=0; i<MAX_THREADS; i++) {
181         if (LWP_CreateProcess(handleRequest, 32768,
182                               LWP_NORMAL_PRIORITY,
183                               (char*)&clientHandles[i],
184                               "HandleRequestThread",
185                               &pid) < 0 ) {
186             printf("%s: Failed to start all LWP's\n", program);
187             exit(1);
188         }
189         clientHandles[i].ch_pid = pid;
190     }
191
192
193     sockFD = socket(AF_INET, SOCK_STREAM, 0);
194     if (sockFD<0) {
195         perror("socket");
196         exit(1);
197     }
198     Log("Using socket at file descriptor %d.\n", sockFD);
199
200     if (setsockopt(sockFD, SOL_SOCKET, SO_REUSEADDR, (char *) &on,
201                    sizeof(on)) < 0) {
202         perror("setsockopt: ");
203         exit(1);
204     }
205
206     memset((void*)&saddr, 0, sizeof(saddr));
207
208     saddr.sin_family = AF_INET;
209     saddr.sin_port = ntohs(port);
210     saddr.sin_addr.s_addr = htonl(INADDR_ANY);
211
212     if (bind(sockFD, (struct sockaddr*)&saddr, sizeof(saddr))<0) {
213         perror("bind: ");
214         exit(1);
215     }
216
217
218     rfds = IOMGR_AllocFDSet();
219     wfds = IOMGR_AllocFDSet();
220     efds = IOMGR_AllocFDSet();
221     if (!rfds || !wfds || !efds) {
222         printf("main: Could not alloc fd_set's.\n");
223         exit(1);
224     }
225
226     listen(sockFD, 100) ;
227         
228     
229     while ( 1 ) {
230         FD_ZERO(rfds); FD_ZERO(wfds); FD_ZERO(efds);
231         FD_SET(sockFD, rfds); FD_SET(sockFD, efds);
232
233         Log("Main - going to select.\n");
234         code = IOMGR_Select(sockFD+1, rfds, wfds, efds, (struct timeval*)0);
235         switch(code) {
236         case 0: /* Timer, can't happen here. */
237         case -1:
238         case -2:
239             Log("Oops! select returns %d!\n", code);
240             abort();
241         default:
242             if (FD_ISSET(sockFD, efds)) {
243                 recvOOB(sockFD);
244                 assertNullFDSet(sockFD, rfds);
245                 assertNullFDSet(-1, wfds);
246                 assertNullFDSet(sockFD, efds);
247             }
248             if (FD_ISSET(sockFD, rfds)) {
249                 while (nThreads > MAX_THREADS) {
250                     IOMGR_Sleep(1);
251                 }
252
253                 clientHandle = getClientHandle();
254
255                 addr_len = sizeof(clientHandle->ch_addr);
256                 clientHandle->ch_fd = accept(sockFD,
257                                      (struct sockaddr*)&clientHandle->ch_addr,
258                                           &addr_len);
259                 if (clientHandle->ch_fd < 0) {
260                     perror("accept: ");
261                     exit(1);
262                 }
263
264                 Log("Main - signalling LWP 0x%x\n", &clientHandle->ch_state);
265                 LWP_NoYieldSignal(&clientHandle->ch_state);
266                 assertNullFDSet(sockFD, rfds);
267                 assertNullFDSet(-1, wfds);
268                 assertNullFDSet(-1, efds);
269                 break;
270             }
271             Die(1, "(main) No data to read.\n");
272         }
273     }
274 }
275
276 void handleRequest(char *arg)
277 {
278     clientHandle_t *ch = (clientHandle_t*)arg;
279     selcmd_t sc;
280     struct stat sbuf;
281     int code = 0;
282     int c_errno = 0;
283
284     while ( 1 ) {
285         Log("(handleRequest) going to sleep on 0x%x\n",
286                &ch->ch_state);
287         LWP_WaitProcess(&ch->ch_state);
288         assert(ch->ch_state == CH_INUSE);
289         
290         FD_ZERO(&(ch->ch_read));
291         FD_ZERO(&(ch->ch_write));
292         FD_ZERO(&(ch->ch_except));
293         FD_SET(ch->ch_fd, &(ch->ch_read)); 
294         FD_SET(ch->ch_fd, &(ch->ch_except)); 
295         code = IOMGR_Select(ch->ch_fd + 1, &(ch->ch_read), &(ch->ch_write),
296                              &(ch->ch_except), (struct timeval *)NULL);
297         if (FD_ISSET(ch->ch_fd, &(ch->ch_except))) {
298             Log("Received expception. Read fd_set shows %d\n",
299                 FD_ISSET(ch->ch_fd, &(ch->ch_read)));
300             assertNullFDSet(ch->ch_fd,  &(ch->ch_read));
301             assertNullFDSet(-1,  &(ch->ch_write));
302             assertNullFDSet(ch->ch_fd,  &(ch->ch_except));
303             goto done;
304         }
305         assert(code>0);
306
307         if (read(ch->ch_fd, (char*)&sc, sizeof(sc))!=sizeof(sc)) {
308             Die(1, "(handleRequest) read command");
309         }
310         
311         Log("(handleRequest)cmd=%d\n", sc.sc_cmd);
312         fflush(stdout);
313
314         switch(sc.sc_cmd) {
315         case SC_PROBE:
316             Log("Probed from client at %s\n",
317                    inet_ntoa(ch->ch_addr.sin_addr));
318             break;
319 #ifdef notdef
320         case SC_OOB:
321             nThreads --;
322             ch->ch_fd = 0;
323             ch->ch_state = CH_FREE;
324             return;
325 #endif
326         case SC_WRITE:
327             handleWrite(ch, &sc);
328             break;
329         case SC_END:
330             Log("Ending ungracefully in server.\n");
331             exit(1);
332         default:
333             Log("%d: bad command to handleRequest.\n", sc.sc_cmd);
334             break;
335         }
336         
337     done:
338         /* We're done now, so we can be re-used. */
339         nThreads --;
340         close(ch->ch_fd);
341         ch->ch_fd = 0;
342         ch->ch_state = CH_FREE;
343     }
344 }
345
346 int write_I = 0;
347 void handleWrite(clientHandle_t *ch, selcmd_t *sc)
348 {
349     int i;
350     fd_set *rfds, *wfds, *efds;
351     char *buf;
352     int code;
353     int scode;
354     char c;
355     int nbytes;
356
357     rfds = IOMGR_AllocFDSet();
358     wfds = IOMGR_AllocFDSet();
359     efds = IOMGR_AllocFDSet();
360     assert(rfds && wfds && efds);
361
362     if (sc->sc_delay>0) {
363         IOMGR_Sleep(sc->sc_delay);
364     }
365
366     Log("(handleWrite 0x%x) waking after %d second sleep.\n",
367            ch->ch_pid, sc->sc_delay);
368
369     if (sc->sc_flags & SC_WAIT_OOB)
370         sendOOB(ch->ch_fd);
371
372     buf = (char*)malloc(sc->sc_info);
373     assert(buf);
374     i = 0;
375
376     while ( 1 ) {
377         FD_ZERO(rfds); FD_SET(ch->ch_fd, rfds);
378         FD_ZERO(efds); FD_SET(ch->ch_fd, efds);
379         FD_ZERO(wfds);
380         scode = IOMGR_Select(ch->ch_fd+1, rfds, wfds, efds,
381                            (struct timeval*)0);
382         assert(scode>0);
383
384         if (FD_ISSET(ch->ch_fd, rfds)) {
385
386             assert(i<sc->sc_info);
387
388             code = read(ch->ch_fd, &buf[i], 1);
389             i ++;
390             write_I ++;
391             if (code !=1) {
392                 Log("code =%d\n", code);
393                 assert(code == 1);
394             }
395
396             /* Test for valid fds */
397             assertNullFDSet(ch->ch_fd, rfds);
398             assertNullFDSet(-1, wfds);
399             assertNullFDSet(-1, efds);
400             if (c == END_DATA || i >= sc->sc_info) {
401                 break;
402             }
403        }
404     }
405     Log("Read %d bytes of data.\n", sc->sc_info);
406     nbytes = write(ch->ch_fd, buf, sc->sc_info);
407     assert(nbytes == sc->sc_info);
408     Log("Wrote data back to client.\n");
409     IOMGR_FreeFDSet(rfds);
410     IOMGR_FreeFDSet(wfds);
411     IOMGR_FreeFDSet(efds);
412 }