1 /*
2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2021, Dave Pare, Jeff Bailey, Thomas Ruschak,
4 * Ken Stevens, Steve McClure, Markus Armbruster
5 *
6 * Empire 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 3 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, see <http://www.gnu.org/licenses/>.
18 *
19 * ---
20 *
21 * See files README, COPYING and CREDITS in the root of the source
22 * tree for related information and legal notices. It is expected
23 * that future projects/authors will amend these files as needed.
24 *
25 * ---
26 *
27 * io.c: Arrange for input and output on a file descriptor to be queued.
28 *
29 * Known contributors to this file:
30 * Doug Hay, 1998
31 * Steve McClure, 1998
32 * Markus Armbruster, 2004-2012
33 * Ron Koenderink, 2009
34 */
35
36 /*
37 * Arrange for input and output on a file descriptor
38 * to be queued. Provide main loop -- a mechanism for
39 * blocking across all registered file descriptors, and
40 * reading or writing when appropriate.
41 */
42
43 #include <config.h>
44
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <stdlib.h>
48 #include <sys/socket.h>
49 #include <sys/types.h>
50 #include <sys/uio.h>
51 #include <unistd.h>
52 #include "empio.h"
53 #include "empthread.h"
54 #include "file.h"
55 #include "ioqueue.h"
56 #include "misc.h"
57
58 struct iop {
59 int fd;
60 struct ioqueue *input;
61 struct ioqueue *output;
62 int flags;
63 int bufsize;
64 int last_out;
65 };
66
67 static struct timeval *io_timeout(struct timeval *, time_t);
68
69 void
io_init(void)70 io_init(void)
71 {
72 }
73
74 struct iop *
io_open(int fd,int flags,int bufsize)75 io_open(int fd, int flags, int bufsize)
76 {
77 int fdfl;
78 struct iop *iop;
79
80 flags = flags & (IO_READ | IO_WRITE);
81 if ((flags & (IO_READ | IO_WRITE)) == 0)
82 return NULL;
83
84 fdfl = fcntl(fd, F_GETFL, 0);
85 if (fdfl < 0)
86 return NULL;
87 fdfl |= O_NONBLOCK;
88 if (fcntl(fd, F_SETFL, fdfl) < 0)
89 return NULL;
90
91 iop = malloc(sizeof(struct iop));
92 if (!iop)
93 return NULL;
94 iop->fd = fd;
95 iop->input = NULL;
96 iop->output = NULL;
97 iop->flags = flags;
98 iop->last_out = 0;
99 iop->bufsize = bufsize;
100 if (flags & IO_READ)
101 iop->input = ioq_create(bufsize);
102 if (flags & IO_WRITE)
103 iop->output = ioq_create(bufsize);
104 return iop;
105 }
106
107 /*
108 * Close @iop.
109 * Flush output and wait for the client to close the connection.
110 * Wait at most until @deadline. (time_t)-1 means wait as long as it
111 * takes (no timeout).
112 * Both the flush and the wait can be separately cut short by
113 * empth_wakeup(). This is almost certainly not what you want. If
114 * you need early wakeup, better fix this function not to go to sleep
115 * after wakeup during flush.
116 */
117 void
io_close(struct iop * iop,time_t deadline)118 io_close(struct iop *iop, time_t deadline)
119 {
120 struct timeval timeout;
121 char buf[IO_BUFSIZE];
122 int ret;
123
124 while (io_output(iop, deadline) > 0) ;
125 shutdown(iop->fd, SHUT_WR);
126 while (empth_select(iop->fd, EMPTH_FD_READ,
127 io_timeout(&timeout, deadline)) > 0) {
128 ret = read(iop->fd, buf, sizeof(buf));
129 if (ret <= 0)
130 break;
131 }
132 if (iop->input)
133 ioq_destroy(iop->input);
134 if (iop->output)
135 ioq_destroy(iop->output);
136 (void)close(iop->fd);
137 free(iop);
138 }
139
140 static struct timeval *
io_timeout(struct timeval * timeout,time_t deadline)141 io_timeout(struct timeval *timeout, time_t deadline)
142 {
143 struct timeval now;
144
145 if (deadline == (time_t)-1)
146 return NULL; /* no deadline */
147
148 gettimeofday(&now, NULL);
149 if (now.tv_sec >= deadline) {
150 /* deadline reached already */
151 timeout->tv_sec = 0;
152 timeout->tv_usec = 0;
153 } else {
154 /* deadline in future */
155 timeout->tv_sec = deadline - now.tv_sec - 1;
156 timeout->tv_usec = 999999 - now.tv_usec;
157 /* yes, this is 1usec early; sue me */
158 }
159
160 return timeout;
161 }
162
163 /*
164 * Read input from @iop and enqueue it.
165 * Wait at most until @deadline for input to arrive. (time_t)-1 means
166 * wait as long as it takes (no timeout).
167 * Does not yield the processor when @deadline is zero.
168 * A wait for input can be cut short by empth_wakeup().
169 * Return number of bytes read on success, -1 on error.
170 * In particular, return zero on timeout, early wakeup or EOF. Use
171 * io_eof() to distinguish timeout and early wakeup from EOF.
172 */
173 int
io_input(struct iop * iop,time_t deadline)174 io_input(struct iop *iop, time_t deadline)
175 {
176 struct timeval timeout;
177 char buf[IO_BUFSIZE];
178 int cc;
179 int res;
180
181 if ((iop->flags & IO_READ) == 0)
182 return -1;
183 if (iop->flags & IO_ERROR)
184 return -1;
185 if (iop->flags & IO_EOF)
186 return 0;
187
188 if (deadline) {
189 res = empth_select(iop->fd, EMPTH_FD_READ,
190 io_timeout(&timeout, deadline));
191 if (res < 0) {
192 iop->flags |= IO_ERROR;
193 return -1;
194 } else if (res == 0)
195 return 0;
196 }
197
198 cc = read(iop->fd, buf, sizeof(buf));
199 if (cc < 0) {
200 if (errno == EAGAIN || errno == EWOULDBLOCK)
201 return 0;
202 iop->flags |= IO_ERROR;
203 return -1;
204 }
205 if (cc == 0) {
206 iop->flags |= IO_EOF;
207 return 0;
208 }
209
210 ioq_append(iop->input, buf, cc);
211 return cc;
212 }
213
214 int
io_inputwaiting(struct iop * iop)215 io_inputwaiting(struct iop *iop)
216 {
217 return ioq_qsize(iop->input);
218 }
219
220 int
io_outputwaiting(struct iop * iop)221 io_outputwaiting(struct iop *iop)
222 {
223 return ioq_qsize(iop->output);
224 }
225
226 /*
227 * Write output queued in @iop.
228 * Wait at most until @deadline for input to arrive. (time_t)-1 means
229 * wait as long as it takes (no timeout).
230 * Does not yield the processor when @deadline is zero.
231 * A wait for output can be cut short by empth_wakeup().
232 * Return number of bytes written on success, -1 on error.
233 * In particular, return zero when nothing was written because the
234 * queue is empty, and on timeout or early wakeup. Use
235 * io_outputwaiting() to distinguish timeout and early wakeup from
236 * empty queue.
237 */
238 int
io_output(struct iop * iop,time_t deadline)239 io_output(struct iop *iop, time_t deadline)
240 {
241 struct timeval timeout;
242 struct iovec iov[16];
243 int n, res, cc;
244
245 if (deadline)
246 ef_make_stale();
247
248 if ((iop->flags & IO_WRITE) == 0)
249 return -1;
250
251 if (iop->flags & IO_ERROR)
252 return -1;
253
254 if (!ioq_qsize(iop->output))
255 return 0;
256
257 if (deadline) {
258 res = empth_select(iop->fd, EMPTH_FD_WRITE,
259 io_timeout(&timeout, deadline));
260 if (res == 0)
261 return 0;
262 if (res < 0) {
263 iop->flags |= IO_ERROR;
264 return -1;
265 }
266 }
267
268 n = ioq_makeiov(iop->output, iov, IO_BUFSIZE);
269 cc = writev(iop->fd, iov, n);
270 if (cc < 0) {
271 if (errno == EAGAIN || errno == EWOULDBLOCK)
272 return 0;
273 iop->flags |= IO_ERROR;
274 return -1;
275 }
276
277 ioq_dequeue(iop->output, cc);
278 iop->last_out = ioq_qsize(iop->output);
279 return cc;
280 }
281
282 /*
283 * Write output queued in @iop if enough have been enqueued.
284 * Write if at least one buffer has been filled since the last write.
285 * Wait at most until @deadline for output to be accepted. (time_t)-1
286 * means wait as long as it takes (no timeout).
287 * Does not yield the processor when @deadline is zero.
288 * A wait for output can be cut short by empth_wakeup().
289 * Return number of bytes written on success, -1 on error.
290 * In particular, return zero when nothing was written because the
291 * queue isn't long, and on timeout or early wakeup.
292 */
293 int
io_output_if_queue_long(struct iop * iop,time_t deadline)294 io_output_if_queue_long(struct iop *iop, time_t deadline)
295 {
296 int len = ioq_qsize(iop->output);
297
298 if (CANT_HAPPEN(iop->last_out > len))
299 iop->last_out = 0;
300 if (len - iop->last_out < iop->bufsize) {
301 if (deadline)
302 ef_make_stale();
303 return 0;
304 }
305 return io_output(iop, deadline);
306 }
307
308 int
io_peek(struct iop * iop,char * buf,int nbytes)309 io_peek(struct iop *iop, char *buf, int nbytes)
310 {
311 if ((iop->flags & IO_READ) == 0)
312 return -1;
313 return ioq_peek(iop->input, buf, nbytes);
314 }
315
316 int
io_read(struct iop * iop,char * buf,int nbytes)317 io_read(struct iop *iop, char *buf, int nbytes)
318 {
319 int cc;
320
321 if ((iop->flags & IO_READ) == 0)
322 return -1;
323 cc = ioq_peek(iop->input, buf, nbytes);
324 if (cc > 0)
325 ioq_dequeue(iop->input, cc);
326 return cc;
327 }
328
329 int
io_write(struct iop * iop,char * buf,int nbytes)330 io_write(struct iop *iop, char *buf, int nbytes)
331 {
332 if ((iop->flags & IO_WRITE) == 0)
333 return -1;
334 ioq_append(iop->output, buf, nbytes);
335 return nbytes;
336 }
337
338 int
io_gets(struct iop * iop,char * buf,int nbytes)339 io_gets(struct iop *iop, char *buf, int nbytes)
340 {
341 if ((iop->flags & IO_READ) == 0)
342 return -1;
343 return ioq_gets(iop->input, buf, nbytes);
344 }
345
346 int
io_puts(struct iop * iop,char * buf)347 io_puts(struct iop *iop, char *buf)
348 {
349 if ((iop->flags & IO_WRITE) == 0)
350 return -1;
351 return ioq_puts(iop->output, buf);
352 }
353
354 int
io_error(struct iop * iop)355 io_error(struct iop *iop)
356 {
357 return iop->flags & IO_ERROR;
358 }
359
360 int
io_eof(struct iop * iop)361 io_eof(struct iop *iop)
362 {
363 return iop->flags & IO_EOF;
364 }
365
366 /*
367 * Discard @iop's buffered input and set its EOF flag.
368 * No more input can be read from @iop.
369 */
370 void
io_set_eof(struct iop * iop)371 io_set_eof(struct iop *iop)
372 {
373 ioq_drain(iop->input);
374 iop->flags |= IO_EOF;
375 }
376
377 int
io_fileno(struct iop * iop)378 io_fileno(struct iop *iop)
379 {
380 return iop->fd;
381 }
382