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