]> git.pond.sub.org Git - empserver/blob - src/lib/gen/io.c
bf77d0f27698abf6299fd43721e848b6d5ae33e7
[empserver] / src / lib / gen / io.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2005, 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 #define WIN32
56 #include <winsock2.h>
57 #undef NS_ALL
58 #endif
59
60 #include "misc.h"
61 #include "queue.h"
62 #include "ioqueue.h"
63 #include "empio.h"
64 #include "gen.h"                /* getfdtablesize */
65 #include "server.h"
66
67 #include "empthread.h"
68
69 struct iop {
70     int fd;
71     struct ioqueue *input;
72     struct ioqueue *output;
73     int flags;
74     s_char *assoc;
75     int bufsize;
76     int (*notify)(void);
77 };
78
79 void
80 io_init(void)
81 {
82 }
83
84 struct iop *
85 io_open(int fd, int flags, int bufsize, int (*notify)(void),
86         s_char *assoc)
87 {
88     struct iop *iop;
89
90     flags = flags & (IO_READ | IO_WRITE | IO_NBLOCK | IO_NEWSOCK);
91     if ((flags & (IO_READ | IO_WRITE)) == 0)
92         return NULL;
93     iop = malloc(sizeof(struct iop));
94     if (!iop)
95         return NULL;
96     iop->fd = fd;
97     iop->input = 0;
98     iop->output = 0;
99     iop->flags = 0;
100     iop->bufsize = bufsize;
101     if ((flags & IO_READ) && (flags & IO_NEWSOCK) == 0)
102         iop->input = ioq_create(bufsize);
103     if ((flags & IO_WRITE) && (flags & IO_NEWSOCK) == 0)
104         iop->output = ioq_create(bufsize);
105     if (flags & IO_NBLOCK)
106         io_noblocking(iop, 1);
107     iop->flags = flags;
108     iop->assoc = assoc;
109     iop->notify = notify;
110     return iop;
111 }
112
113 void
114 io_close(struct iop *iop)
115 {
116
117     if (iop->input != 0)
118         ioq_destroy(iop->input);
119     if (iop->output != 0)
120         ioq_destroy(iop->output);
121 #if !defined(_WIN32)
122     (void)close(iop->fd);
123 #else
124     closesocket(iop->fd);
125 #endif
126     free(iop);
127 }
128
129 int
130 io_input(struct iop *iop, int waitforinput)
131 {
132     s_char buf[IO_BUFSIZE];
133     int cc;
134
135     /* Not a read IOP */
136     if ((iop->flags & IO_READ) == 0)
137         return -1;
138     /* IOP is markes as in error. */
139     if (iop->flags & IO_ERROR)
140         return -1;
141     /* Wait for the file to have input. */
142     if (waitforinput) {
143         empth_select(iop->fd, EMPTH_FD_READ);
144     }
145 #if !defined(_WIN32)
146     /* Do the actual read. */
147     cc = read(iop->fd, buf, sizeof(buf));
148     if (cc < 0) {
149         /* would block, so nothing to read. */
150         if (errno == EWOULDBLOCK)
151             return 0;
152
153         /* Some form of file error occurred... */
154         iop->flags |= IO_ERROR;
155         return -1;
156     }
157 #else
158     cc = recv(iop->fd, buf, sizeof(buf), 0);
159     if (cc == SOCKET_ERROR) {
160         int err = WSAGetLastError();
161         /* Hmm, it would block.  file is opened noblock, soooooo.. */
162         if (err == WSAEWOULDBLOCK)
163             return 0;
164
165         /* Some form of file error occurred... */
166         iop->flags |= IO_ERROR;
167         return -1;
168     }
169 #endif
170
171     /* We eof'd */
172     if (cc == 0) {
173         iop->flags |= IO_EOF;
174         return 0;
175     }
176
177     /* Append the input to the IOQ. */
178     ioq_append(iop->input, buf, cc);
179     return cc;
180 }
181
182 int
183 io_inputwaiting(struct iop *iop)
184 {
185     return ioq_qsize(iop->input);
186 }
187
188 int
189 io_outputwaiting(struct iop *iop)
190 {
191     return ioq_qsize(iop->output);
192 }
193
194 int
195 io_output(struct iop *iop, int waitforoutput)
196 {
197 #if !defined(_WIN32)
198     struct iovec iov[16];
199 #else
200     s_char buf[IO_BUFSIZE];
201 #endif
202     int cc;
203     int n;
204     int remain;
205
206     /* If there is no output waiting. */
207     if (!io_outputwaiting(iop))
208         return 0;
209
210     /* If the iop is not write enabled. */
211     if ((iop->flags & IO_WRITE) == 0)
212         return -1;
213
214     /* If the io is marked as in error... */
215     if (iop->flags & IO_ERROR)
216         return -1;
217
218     /* This is the same test as io_outputwaiting.... */
219     if (ioq_qsize(iop->output) == 0)
220         return 0;
221
222 #if !defined(_WIN32)
223     /* make the iov point to the data in the queue. */
224     /* I.E., each of the elements in the queue. */
225     /* returns the number of elements in the iov. */
226     n = ioq_makeiov(iop->output, iov, IO_BUFSIZE);
227 #else
228     /* Make a buffer containing the output to write. */
229     n = ioq_makebuf(iop->output, buf, sizeof(buf));
230 #endif
231
232     if (n <= 0) {
233         return 0;
234     }
235
236     /* wait for the file to be output ready. */
237     if (waitforoutput != IO_NOWAIT) {
238         /* This waits for the file to be ready for writing, */
239         /* and lets other threads run. */
240         empth_select(iop->fd, EMPTH_FD_WRITE);
241     }
242
243     /* Do the actual write. */
244 #if !defined(_WIN32)
245     cc = writev(iop->fd, iov, n);
246
247     /* if it failed.... */
248     if (cc < 0) {
249         /* Hmm, it would block.  file is opened noblock, soooooo.. */
250         if (errno == EWOULDBLOCK) {
251             /* If there are remaining bytes, set the IO as remaining.. */
252             remain = ioq_qsize(iop->output);
253             return remain;
254         }
255         iop->flags |= IO_ERROR;
256         return -1;
257     }
258 #else
259     cc = send(iop->fd, buf, n, 0);
260
261     /* if it failed.... */
262     if (cc == SOCKET_ERROR) {
263         int err = WSAGetLastError();
264         /* Hmm, it would block.  file is opened noblock, soooooo.. */
265         if (err == WSAEWOULDBLOCK) {
266             /* If there are remaining bytes, set the IO as remaining.. */
267             remain = ioq_qsize(iop->output);
268             return remain;
269         }
270         iop->flags |= IO_ERROR;
271         return -1;
272     }
273 #endif
274
275     /* If no bytes were written, something happened..  Like an EOF. */
276 #ifndef hpux
277     if (cc == 0) {
278         iop->flags |= IO_EOF;
279         return 0;
280     }
281 #else
282     if (cc == 0) {
283         remain = ioq_qsize(iop->output);
284         return remain;
285     }
286 #endif /* hpux */
287
288     /* Remove the number of written bytes from the queue. */
289     ioq_dequeue(iop->output, cc);
290
291     return cc;
292 }
293
294 int
295 io_peek(struct iop *iop, s_char *buf, int nbytes)
296 {
297     if ((iop->flags & IO_READ) == 0)
298         return -1;
299     return ioq_peek(iop->input, buf, nbytes);
300 }
301
302 int
303 io_read(struct iop *iop, s_char *buf, int nbytes)
304 {
305     int cc;
306
307     if ((iop->flags & IO_READ) == 0)
308         return -1;
309     cc = ioq_peek(iop->input, buf, nbytes);
310     if (cc > 0)
311         ioq_dequeue(iop->input, cc);
312     return cc;
313 }
314
315 int
316 io_write(struct iop *iop, s_char *buf, int nbytes, int doWait)
317 {
318     int len;
319
320     if ((iop->flags & IO_WRITE) == 0)
321         return -1;
322     ioq_append(iop->output, buf, nbytes);
323     len = ioq_qsize(iop->output);
324     if (len > iop->bufsize) {
325         if (doWait) {
326             io_output_all(iop);
327         } else {
328             /* only try a write every BUFSIZE characters */
329             if (((len - nbytes) % iop->bufsize) < (len % iop->bufsize))
330                 io_output(iop, IO_NOWAIT);
331         }
332     }
333     return nbytes;
334 }
335
336 int
337 io_output_all(struct iop *iop)
338 {
339     int n;
340
341     /*
342      * Mustn't block a player thread while update is pending, or else
343      * a malicous player could delay the update indefinitely
344      */
345     while (((n = io_output(iop, IO_NOWAIT)) > 0) && !update_pending) {
346         empth_select(iop->fd, EMPTH_FD_WRITE);
347     }
348     return n;
349 }
350
351 int
352 io_gets(struct iop *iop, s_char *buf, int nbytes)
353 {
354     if ((iop->flags & IO_READ) == 0)
355         return -1;
356     return ioq_gets(iop->input, buf, nbytes);
357 }
358
359 int
360 io_puts(struct iop *iop, s_char *buf)
361 {
362     if ((iop->flags & IO_WRITE) == 0)
363         return -1;
364     return ioq_puts(iop->output, buf);
365 }
366
367 int
368 io_shutdown(struct iop *iop, int flags)
369 {
370     flags &= (IO_READ | IO_WRITE);
371     if ((iop->flags & flags) != flags)
372         return -1;
373     if (flags & IO_READ) {
374         shutdown(iop->fd, 0);
375         ioq_drain(iop->input);
376     }
377     if (flags & IO_WRITE) {
378         shutdown(iop->fd, 1);
379         ioq_drain(iop->output);
380     }
381     return 0;
382 }
383
384 int
385 io_noblocking(struct iop *iop, int value)
386 {
387 #if !defined(_WIN32)
388     int flags;
389
390     flags = fcntl(iop->fd, F_GETFL, 0);
391     if (flags < 0)
392         return -1;
393     if (value == 0)
394         flags &= ~FNDELAY;
395     else
396         flags |= FNDELAY;
397     if (fcntl(iop->fd, F_SETFL, flags) < 0)
398         return -1;
399 #else
400     u_long arg = value;
401     ioctlsocket(iop->fd, FIONBIO, &arg);
402 #endif
403     if (value == 0)
404         iop->flags &= ~IO_NBLOCK;
405     else
406         iop->flags |= IO_NBLOCK;
407     return 0;
408 }
409
410 int
411 io_conn(struct iop *iop)
412 {
413     return iop->flags & IO_CONN;
414 }
415
416 int
417 io_error(struct iop *iop)
418 {
419     return iop->flags & IO_ERROR;
420 }
421
422 int
423 io_eof(struct iop *iop)
424 {
425     return iop->flags & IO_EOF;
426 }
427
428 int
429 io_fileno(struct iop *iop)
430 {
431     return iop->fd;
432 }