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