]> git.pond.sub.org Git - empserver/blob - src/lib/gen/io.c
fae8b84ad91ec7aa65978dcfe32015deca981a31
[empserver] / src / lib / gen / io.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2000, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23  *  related information and legal notices. It is expected that any future
24  *  projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  io.c: Arrange for input and output on a file descriptor to be queued.
29  * 
30  *  Known contributors to this file:
31  *      Doug Hay, 1998
32  *      Steve McClure, 1998
33  */
34
35 /*
36  * Arrange for input and output on a file descriptor
37  * to be queued.  Provide main loop -- a mechanism for
38  * blocking across all registered file descriptors, and
39  * reading or writing when appropriate.
40  */
41
42 #include <errno.h>
43 #include <sys/types.h>
44 #if !defined(_WIN32)
45 #include <sys/uio.h>
46 #include <sys/file.h>
47 #include <unistd.h> /* close read shutdown select */
48 #include <sys/socket.h>
49 #endif
50 #include <time.h>
51 #include <fcntl.h>
52 #include <stdlib.h> /* malloc calloc free */
53
54 #if defined(_WIN32)
55 #include <winsock.h>
56 #endif
57
58 #include "misc.h"
59 #include "bit.h"
60 #include "queue.h"
61 #include "ioqueue.h"
62 #include "io_mask.h"
63 #include "empio.h"
64 #include "gen.h" /* getfdtablesize */
65
66 #include "empthread.h"
67
68 extern struct player *player;   /* XXX */
69
70 static  struct iop **io_list;
71 static  struct io_mask *iom;
72 static  bit_fdmask newoutput;
73
74 struct iop {
75         int fd;
76         struct ioqueue *input;
77         struct ioqueue *output;
78         int flags;
79         s_char *assoc;
80         int bufsize;
81         int (*notify)();
82 };
83
84 extern  int errno;
85
86 void
87 io_init(void)
88 {
89         iom = iom_create(IO_READ|IO_WRITE);
90         io_list = (struct iop **) calloc(getfdtablesize(), sizeof(*io_list));
91         newoutput = bit_newfdmask();
92 }
93
94 struct iop *
95 io_open(int fd, int flags, int bufsize, int (*notify) (void), s_char *assoc)
96 {
97         struct  iop *iop;
98
99         if (io_list[fd] != 0) {
100                 /* already exists */
101                 return 0;
102         }
103         flags = flags & (IO_READ|IO_WRITE|IO_NBLOCK|IO_NEWSOCK);
104         if ((flags & (IO_READ|IO_WRITE)) == 0)
105                 return 0;
106         iop = (struct iop *) malloc(sizeof(struct iop));
107         iop->fd = fd;
108         iop->input = 0;
109         iop->output = 0;
110         iop->flags = 0;
111         iop->bufsize = bufsize;
112         if ((flags & IO_READ) && (flags & IO_NEWSOCK) == 0)
113                 iop->input = ioq_create(bufsize);
114         if ((flags & IO_WRITE) && (flags & IO_NEWSOCK) == 0)
115                 iop->output = ioq_create(bufsize);
116         if (flags & IO_NBLOCK)
117                 io_noblocking(iop, 1);
118         iop->flags = flags;
119         iop->assoc = assoc;
120         iop->notify = notify;
121         io_list[fd] = iop;
122         iom_set(iom, flags, fd);
123         return iop;
124 }
125
126 void
127 io_close(struct iop *iop)
128 {
129         
130         if (iop->input != 0) 
131                 ioq_destroy(iop->input);
132         if (iop->output != 0) 
133                 ioq_destroy(iop->output);
134         iom_clear(iom, iop->flags, iop->fd);
135         BIT_CLRB(iop->fd, newoutput);
136         io_list[iop->fd] = 0;
137 #if !defined(_WIN32)
138         (void) close(iop->fd);
139 #else
140     closesocket(iop->fd);
141 #endif
142         free((s_char *)iop);
143 }
144
145 int
146 io_input(struct iop *iop, int waitforinput)
147 {
148         s_char  buf[IO_BUFSIZE];
149         int     cc;
150
151     /* Not a read IOP */
152         if ((iop->flags & IO_READ) == 0)
153                 return -1;
154     /* IOP is markes as in error. */
155         if (iop->flags & IO_ERROR)
156                 return -1;
157     /* Wait for the file to have input. */
158         if (waitforinput) {
159                 empth_select(iop->fd, EMPTH_FD_READ);
160         }
161 #if !defined(_WIN32)
162     /* Do the actual read. */
163         cc = read(iop->fd, buf, sizeof(buf));
164         if (cc < 0) {
165         /* would block, so nothing to read. */
166                 if (errno == EWOULDBLOCK)
167                         return 0;
168
169         /* Some form of file error occurred... */
170                 iop->flags |= IO_ERROR;
171                 iom_clear(iom, IO_READ, iop->fd);
172                 return -1;
173         }
174 #else
175     cc = recv(iop->fd, buf, sizeof(buf), 0);
176     if (cc == SOCKET_ERROR) {
177         int err =  WSAGetLastError();
178         /* Hmm, it would block.  file is opened noblock, soooooo.. */
179         if (err == WSAEWOULDBLOCK)
180             return 0;
181
182         /* Some form of file error occurred... */
183                 iop->flags |= IO_ERROR;
184                 iom_clear(iom, IO_READ, iop->fd);
185                 return -1;
186     }
187 #endif
188
189     /* We eof'd */
190         if (cc == 0) {
191                 iop->flags |= IO_EOF;
192                 return 0;
193         }
194
195     /* Append the input to the IOQ. */
196         ioq_append(iop->input, buf, cc);
197         return cc;
198 }
199
200 int
201 io_inputwaiting(struct iop *iop)
202 {
203         return ioq_qsize(iop->input);
204 }
205
206 int
207 io_outputwaiting(struct iop *iop)
208 {
209         return ioq_qsize(iop->output);
210 }
211
212 int
213 io_output(struct iop *iop, int waitforoutput)
214 {
215 #if !defined(_WIN32)
216         struct  iovec iov[16];
217 #else
218         s_char  buf[IO_BUFSIZE];
219 #endif
220         int     cc;
221         int     n;
222         int     remain;
223
224     /* If there is no output waiting. */
225         if (!io_outputwaiting(iop))
226                 return 0;
227
228     /* bit clear */
229         BIT_CLRB(iop->fd, newoutput);
230
231     /* If the iop is not write enabled. */
232         if ((iop->flags & IO_WRITE) == 0)
233                 return -1;
234
235     /* If the io is marked as in error... */
236         if (iop->flags & IO_ERROR)
237                 return -1;
238
239     /* This is the same test as io_outputwaiting.... */
240         if (ioq_qsize(iop->output) == 0)
241                 return 0;
242
243 #if !defined(_WIN32)
244     /* make the iov point to the data in the queue. */
245     /* I.E., each of the elements in the queue. */
246     /* returns the number of elements in the iov. */
247         n = ioq_makeiov(iop->output, iov, IO_BUFSIZE);
248 #else
249     /* Make a buffer containing the output to write. */
250         n = ioq_makebuf(iop->output, buf, sizeof(buf));
251 #endif
252
253         if (n <= 0) {
254         /* If we got no elements, we have no output.... */
255                 iom_clear(iom, IO_WRITE, iop->fd);
256                 return 0;
257         }
258
259     /* wait for the file to be output ready. */
260         if (waitforoutput != IO_NOWAIT) {
261         /* This waits for the file to be ready for writing, */
262         /* and lets other threads run. */
263             empth_select(iop->fd, EMPTH_FD_WRITE); 
264         }
265
266     /* Do the actual write. */
267 #if !defined(_WIN32)
268         cc = writev(iop->fd, iov, n);
269
270     /* if it failed.... */
271         if (cc < 0) {
272         /* Hmm, it would block.  file is opened noblock, soooooo.. */
273                 if (errno == EWOULDBLOCK) {
274             /* If there are remaining bytes, set the IO as remaining.. */
275                         remain = ioq_qsize(iop->output);
276                         if (remain > 0)
277                                 iom_set(iom, IO_WRITE, iop->fd);
278                         return remain;
279                 }
280                 iop->flags |= IO_ERROR;
281                 iom_clear(iom, IO_WRITE, iop->fd);
282                 return -1;
283         }
284 #else
285         cc = send(iop->fd, buf, n, 0);
286
287     /* if it failed.... */
288         if (cc == SOCKET_ERROR) {
289         int err =  WSAGetLastError();
290         /* Hmm, it would block.  file is opened noblock, soooooo.. */
291         if (err == WSAEWOULDBLOCK) {
292             /* If there are remaining bytes, set the IO as remaining.. */
293                         remain = ioq_qsize(iop->output);
294                         if (remain > 0)
295                                 iom_set(iom, IO_WRITE, iop->fd);
296                         return remain;
297                 }
298                 iop->flags |= IO_ERROR;
299                 iom_clear(iom, IO_WRITE, iop->fd);
300                 return -1;
301         }
302 #endif
303
304
305     /* If no bytes were written, something happened..  Like an EOF. */
306 #ifndef hpux
307         if (cc == 0) {
308                 iop->flags |= IO_EOF;
309                 return 0;
310         }
311 #else
312         if (cc == 0) {
313                 remain = ioq_qsize(iop->output);
314                 if (remain > 0)
315                         iom_set(iom, IO_WRITE, iop->fd);
316                 return remain;
317         }
318 #endif  /* hpux */
319
320     /* Remove the number of written bytes from the queue. */
321         ioq_dequeue(iop->output, cc);
322
323     /* If the queue has stuff remaining, set it still needing output. */
324         remain = ioq_qsize(iop->output);
325         if (remain == 0) {
326                 iom_clear(iom, IO_WRITE, iop->fd);
327         } else {
328                 iom_set(iom, IO_WRITE, iop->fd);
329         }
330         return cc;
331 }
332
333 int
334 io_select(struct timeval *tv)
335 {
336         bit_fdmask readmask;
337         bit_fdmask writemask;
338         int     n;
339         int     nfds;
340         int     fd;
341         struct iop *iop;
342
343         iom_getmask(iom, &nfds, &readmask, &writemask);
344         n = select(nfds + 1, (fd_set *)readmask, (fd_set *)writemask, 0, tv);
345         if (n <= 0) {
346                 if (errno == EINTR)
347                         return 0;
348                 return -1;
349         }
350         while ((fd = bit_fd(readmask)) >= 0) {
351                 iop = io_list[fd];
352                 if ((iop->flags & IO_NEWSOCK) == 0)
353                         (void) io_input(iop, IO_NOWAIT);
354                 if (iop->notify != 0)
355                         iop->notify(iop, IO_READ, iop->assoc);
356                 BIT_CLRB(fd, readmask);
357         }
358         while ((fd = bit_fd(writemask)) >= 0) {
359                 iop = io_list[fd];
360                 if (io_output(iop, IO_NOWAIT) < 0 && iop->notify != 0)
361                         iop->notify(iop, IO_WRITE, iop->assoc);
362                 BIT_CLRB(fd, writemask);
363         }
364         return n;
365 }
366
367 void
368 io_flush(int doWait)
369 {
370         int     fd;
371         struct  iop *iop;
372
373         while ((fd = bit_fd(newoutput)) >= 0) {
374                 iop = io_list[fd];
375                 if (io_output(iop, doWait) < 0 && iop->notify != 0)
376                         iop->notify(iop, IO_WRITE, iop->assoc);
377         }
378 }
379
380 int
381 io_peek(struct iop *iop, s_char *buf, int nbytes)
382 {
383         if ((iop->flags & IO_READ) == 0)
384                 return -1;
385         return ioq_peek(iop->input, buf, nbytes);
386 }
387
388 int
389 io_read(struct iop *iop, s_char *buf, int nbytes)
390 {
391         int     cc;
392
393         if ((iop->flags & IO_READ) == 0)
394                 return -1;
395         cc = ioq_peek(iop->input, buf, nbytes);
396         if (cc > 0)
397                 ioq_dequeue(iop->input, cc);
398         return cc;
399 }
400
401 int
402 io_write(struct iop *iop, s_char *buf, int nbytes, int doWait)
403 {
404         int     len;
405
406         if ((iop->flags & IO_WRITE) == 0)
407                 return -1;
408         ioq_append(iop->output, buf, nbytes);
409         BIT_SETB(iop->fd, newoutput);
410         len = ioq_qsize(iop->output);
411         if (len > iop->bufsize) {
412                 if (doWait) {
413                         io_output_all(iop);
414                 } else {
415                         /* only try a write every BUFSIZE characters */
416                         if (((len-nbytes) % iop->bufsize) <
417                             (len % iop->bufsize))
418                                 io_output(iop, 0);
419                 }
420         }
421         return nbytes;
422 }
423
424 int
425 io_output_all(struct iop *iop)
426 {
427         int     n;
428
429         while ((n = io_output(iop, IO_NOWAIT)) > 0) {
430             empth_select(iop->fd, EMPTH_FD_WRITE);
431         }
432         return n;
433 }
434
435 int
436 io_gets(struct iop *iop, s_char *buf, int nbytes)
437 {
438         if ((iop->flags & IO_READ) == 0)
439                 return -1;
440         return ioq_gets(iop->input, buf, nbytes);
441 }
442
443 int
444 io_puts(struct iop *iop, s_char *buf)
445 {
446         if ((iop->flags & IO_WRITE) == 0)
447                 return -1;
448         BIT_SETB(iop->fd, newoutput);
449         return ioq_puts(iop->output, buf);
450 }
451
452 int
453 io_shutdown(struct iop *iop, int flags)
454 {
455         flags &= (IO_READ|IO_WRITE);
456         if ((iop->flags & flags) != flags)
457                 return -1;
458         if (flags & IO_READ) {
459                 shutdown(iop->fd, 0);
460                 ioq_drain(iop->input);
461         }
462         if (flags & IO_WRITE) {
463                 shutdown(iop->fd, 1);
464                 ioq_drain(iop->output);
465         }
466         return 0;
467 }
468
469 int
470 io_noblocking(struct iop *iop, int value)
471 {
472 #if !defined(_WIN32)
473         int     flags;
474
475         flags = fcntl(iop->fd, F_GETFL, 0);
476         if (flags < 0)
477                 return -1;
478         if (value == 0)
479                 flags &= ~FNDELAY;
480         else
481                 flags |= FNDELAY;
482         if (fcntl(iop->fd, F_SETFL, flags) < 0)
483                 return -1;
484 #else
485     u_long arg = value;
486     ioctlsocket(iop->fd, FIONBIO, &arg);
487 #endif
488         if (value == 0)
489                 iop->flags &= ~IO_NBLOCK;
490         else
491                 iop->flags |= IO_NBLOCK;
492         return 0;
493 }
494
495 int
496 io_conn(struct iop *iop)
497 {
498         return (iop->flags & IO_CONN);
499 }
500
501 int
502 io_error(struct iop *iop)
503 {
504         return (iop->flags & IO_ERROR);
505 }
506
507 int
508 io_eof(struct iop *iop)
509 {
510         return (iop->flags & IO_EOF);
511 }
512
513 int
514 io_fileno(struct iop *iop)
515 {
516         return iop->fd;
517 }
518
519 struct iop *
520 io_iopfromfd(int fd)
521 {
522         return io_list[fd];
523 }
524
525
526
527
528
529
530
531
532
533
534
535