]> git.pond.sub.org Git - empserver/blob - src/lib/gen/io.c
Simple POSIX I/O emulation layer to work around Windows's defective
[empserver] / src / lib / gen / io.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2007, 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 files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future 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 <config.h>
43
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <stdlib.h>
47 #include <sys/types.h>
48 #ifdef _WIN32
49 #include <winsock2.h>
50 #else
51 #include <sys/uio.h>
52 #include <sys/file.h>
53 #include <sys/socket.h>
54 #endif
55 #include <unistd.h>
56 #include <time.h>
57
58 #include "empio.h"
59 #include "ioqueue.h"
60 #include "misc.h"
61 #include "queue.h"
62 #include "server.h"
63
64 #include "empthread.h"
65
66 struct iop {
67     int fd;
68     struct ioqueue *input;
69     struct ioqueue *output;
70     int flags;
71     int bufsize;
72 };
73
74 void
75 io_init(void)
76 {
77 }
78
79 struct iop *
80 io_open(int fd, int flags, int bufsize)
81 {
82     struct iop *iop;
83
84     flags = flags & (IO_READ | IO_WRITE | IO_NBLOCK | IO_NEWSOCK);
85     if ((flags & (IO_READ | IO_WRITE)) == 0)
86         return NULL;
87     iop = malloc(sizeof(struct iop));
88     if (!iop)
89         return NULL;
90     iop->fd = fd;
91     iop->input = 0;
92     iop->output = 0;
93     iop->flags = 0;
94     iop->bufsize = bufsize;
95     if ((flags & IO_READ) && (flags & IO_NEWSOCK) == 0)
96         iop->input = ioq_create(bufsize);
97     if ((flags & IO_WRITE) && (flags & IO_NEWSOCK) == 0)
98         iop->output = ioq_create(bufsize);
99     if (flags & IO_NBLOCK)
100         io_noblocking(iop, 1);  /* FIXME check success */
101     iop->flags = flags;
102     return iop;
103 }
104
105 void
106 io_close(struct iop *iop)
107 {
108
109     if (iop->input != 0)
110         ioq_destroy(iop->input);
111     if (iop->output != 0)
112         ioq_destroy(iop->output);
113     (void)close(iop->fd);
114     free(iop);
115 }
116
117 int
118 io_input(struct iop *iop, int waitforinput)
119 {
120     char buf[IO_BUFSIZE];
121     int cc;
122
123     /* Not a read IOP */
124     if ((iop->flags & IO_READ) == 0)
125         return -1;
126     /* IOP is markes as in error. */
127     if (iop->flags & IO_ERROR)
128         return -1;
129     /* Wait for the file to have input. */
130     if (waitforinput) {
131         empth_select(iop->fd, EMPTH_FD_READ);
132     }
133     /* Do the actual read. */
134     cc = read(iop->fd, buf, sizeof(buf));
135     if (cc < 0) {
136         /* would block, so nothing to read. */
137         if (errno == EAGAIN || errno == EWOULDBLOCK)
138             return 0;
139
140         /* Some form of file error occurred... */
141         iop->flags |= IO_ERROR;
142         return -1;
143     }
144
145     /* We eof'd */
146     if (cc == 0) {
147         iop->flags |= IO_EOF;
148         return 0;
149     }
150
151     /* Append the input to the IOQ. */
152     ioq_append(iop->input, buf, cc);
153     return cc;
154 }
155
156 int
157 io_inputwaiting(struct iop *iop)
158 {
159     return ioq_qsize(iop->input);
160 }
161
162 int
163 io_outputwaiting(struct iop *iop)
164 {
165     return ioq_qsize(iop->output);
166 }
167
168 int
169 io_output(struct iop *iop, int waitforoutput)
170 {
171 #if !defined(_WIN32)
172     struct iovec iov[16];
173 #else
174     char buf[IO_BUFSIZE];
175 #endif
176     int cc;
177     int n;
178     int remain;
179
180     /* If there is no output waiting. */
181     if (!io_outputwaiting(iop))
182         return 0;
183
184     /* If the iop is not write enabled. */
185     if ((iop->flags & IO_WRITE) == 0)
186         return -1;
187
188     /* If the io is marked as in error... */
189     if (iop->flags & IO_ERROR)
190         return -1;
191
192 #if !defined(_WIN32)
193     /* make the iov point to the data in the queue. */
194     /* I.E., each of the elements in the queue. */
195     /* returns the number of elements in the iov. */
196     n = ioq_makeiov(iop->output, iov, IO_BUFSIZE);
197 #else
198     /* Make a buffer containing the output to write. */
199     n = ioq_makebuf(iop->output, buf, sizeof(buf));
200 #endif
201
202     if (n <= 0) {
203         return 0;
204     }
205
206     /* wait for the file to be output ready. */
207     if (waitforoutput != IO_NOWAIT) {
208         /* This waits for the file to be ready for writing, */
209         /* and lets other threads run. */
210         empth_select(iop->fd, EMPTH_FD_WRITE);
211     }
212
213     /* Do the actual write. */
214 #if !defined(_WIN32)
215     cc = writev(iop->fd, iov, n);
216 #else
217     cc = write(iop->fd, buf, n);
218 #endif
219
220     /* if it failed.... */
221     if (cc < 0) {
222         /* Hmm, it would block.  file is opened noblock, soooooo.. */
223         if (errno == EAGAIN || errno == EWOULDBLOCK) {
224             /* If there are remaining bytes, set the IO as remaining.. */
225             remain = ioq_qsize(iop->output);
226             return remain;
227         }
228         iop->flags |= IO_ERROR;
229         return -1;
230     }
231
232     /* If no bytes were written, something happened..  Like an EOF. */
233     if (cc == 0) {
234         iop->flags |= IO_EOF;
235         return 0;
236     }
237
238     /* Remove the number of written bytes from the queue. */
239     ioq_dequeue(iop->output, cc);
240
241     return cc;
242 }
243
244 int
245 io_peek(struct iop *iop, char *buf, int nbytes)
246 {
247     if ((iop->flags & IO_READ) == 0)
248         return -1;
249     return ioq_peek(iop->input, buf, nbytes);
250 }
251
252 int
253 io_read(struct iop *iop, char *buf, int nbytes)
254 {
255     int cc;
256
257     if ((iop->flags & IO_READ) == 0)
258         return -1;
259     cc = ioq_peek(iop->input, buf, nbytes);
260     if (cc > 0)
261         ioq_dequeue(iop->input, cc);
262     return cc;
263 }
264
265 int
266 io_write(struct iop *iop, char *buf, int nbytes, int doWait)
267 {
268     int len;
269
270     if ((iop->flags & IO_WRITE) == 0)
271         return -1;
272     ioq_append(iop->output, buf, nbytes);
273     len = ioq_qsize(iop->output);
274     if (len > iop->bufsize) {
275         if (doWait) {
276             io_output_all(iop);
277         } else {
278             /* only try a write every BUFSIZE characters */
279             if (((len - nbytes) % iop->bufsize) < (len % iop->bufsize))
280                 io_output(iop, IO_NOWAIT);
281         }
282     }
283     return nbytes;
284 }
285
286 int
287 io_output_all(struct iop *iop)
288 {
289     int n;
290
291     /*
292      * Mustn't block a player thread while update is pending, or else
293      * a malicous player could delay the update indefinitely
294      */
295     while ((n = io_output(iop, IO_NOWAIT)) > 0 && !play_wrlock_wanted)
296         empth_select(iop->fd, EMPTH_FD_WRITE);
297
298     return n;
299 }
300
301 int
302 io_gets(struct iop *iop, char *buf, int nbytes)
303 {
304     if ((iop->flags & IO_READ) == 0)
305         return -1;
306     return ioq_gets(iop->input, buf, nbytes);
307 }
308
309 int
310 io_puts(struct iop *iop, char *buf)
311 {
312     if ((iop->flags & IO_WRITE) == 0)
313         return -1;
314     return ioq_puts(iop->output, buf);
315 }
316
317 int
318 io_shutdown(struct iop *iop, int flags)
319 {
320     flags &= (IO_READ | IO_WRITE);
321     if ((iop->flags & flags) != flags)
322         return -1;
323     if (flags & IO_READ) {
324         shutdown(iop->fd, 0);
325         ioq_drain(iop->input);
326     }
327     if (flags & IO_WRITE) {
328         shutdown(iop->fd, 1);
329         ioq_drain(iop->output);
330     }
331     return 0;
332 }
333
334 int
335 io_noblocking(struct iop *iop, int value)
336 {
337     int flags;
338
339     flags = fcntl(iop->fd, F_GETFL, 0);
340     if (flags < 0)
341         return -1;
342     if (value == 0)
343         flags &= ~O_NONBLOCK;
344     else
345         flags |= O_NONBLOCK;
346     if (fcntl(iop->fd, F_SETFL, flags) < 0)
347         return -1;
348     if (value == 0)
349         iop->flags &= ~IO_NBLOCK;
350     else
351         iop->flags |= IO_NBLOCK;
352     return 0;
353 }
354
355 int
356 io_error(struct iop *iop)
357 {
358     return iop->flags & IO_ERROR;
359 }
360
361 int
362 io_eof(struct iop *iop)
363 {
364     return iop->flags & IO_EOF;
365 }
366
367 int
368 io_fileno(struct iop *iop)
369 {
370     return iop->fd;
371 }