src/lib/gen/io.c

Go to the documentation of this file.
00001 /*
00002  *  Empire - A multi-player, client/server Internet based war game.
00003  *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
00004  *                           Ken Stevens, Steve McClure
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  *  ---
00021  *
00022  *  See files README, COPYING and CREDITS in the root of the source
00023  *  tree for related information and legal notices.  It is expected
00024  *  that future projects/authors will amend these files as needed.
00025  *
00026  *  ---
00027  *
00028  *  io.c: Arrange for input and output on a file descriptor to be queued.
00029  * 
00030  *  Known contributors to this file:
00031  *      Doug Hay, 1998
00032  *      Steve McClure, 1998
00033  */
00034 
00035 /*
00036  * Arrange for input and output on a file descriptor
00037  * to be queued.  Provide main loop -- a mechanism for
00038  * blocking across all registered file descriptors, and
00039  * reading or writing when appropriate.
00040  */
00041 
00042 #include <config.h>
00043 
00044 #include <errno.h>
00045 #include <fcntl.h>
00046 #include <stdlib.h>
00047 #include <sys/socket.h>
00048 #include <sys/types.h>
00049 #include <sys/uio.h>
00050 #include <unistd.h>
00051 #include "empio.h"
00052 #include "empthread.h"
00053 #include "ioqueue.h"
00054 #include "misc.h"
00055 #include "queue.h"
00056 #include "server.h"
00057 
00058 struct iop {
00059     int fd;
00060     struct ioqueue *input;
00061     struct ioqueue *output;
00062     int flags;
00063     int bufsize;
00064 };
00065 
00066 void
00067 io_init(void)
00068 {
00069 }
00070 
00071 struct iop *
00072 io_open(int fd, int flags, int bufsize)
00073 {
00074     struct iop *iop;
00075 
00076     flags = flags & (IO_READ | IO_WRITE | IO_NBLOCK | IO_NEWSOCK);
00077     if ((flags & (IO_READ | IO_WRITE)) == 0)
00078         return NULL;
00079     iop = malloc(sizeof(struct iop));
00080     if (!iop)
00081         return NULL;
00082     iop->fd = fd;
00083     iop->input = 0;
00084     iop->output = 0;
00085     iop->flags = 0;
00086     iop->bufsize = bufsize;
00087     if ((flags & IO_READ) && (flags & IO_NEWSOCK) == 0)
00088         iop->input = ioq_create(bufsize);
00089     if ((flags & IO_WRITE) && (flags & IO_NEWSOCK) == 0)
00090         iop->output = ioq_create(bufsize);
00091     if (flags & IO_NBLOCK)
00092         io_noblocking(iop, 1);  /* FIXME check success */
00093     iop->flags = flags;
00094     return iop;
00095 }
00096 
00097 void
00098 io_close(struct iop *iop)
00099 {
00100 
00101     if (iop->input != 0)
00102         ioq_destroy(iop->input);
00103     if (iop->output != 0)
00104         ioq_destroy(iop->output);
00105     (void)close(iop->fd);
00106     free(iop);
00107 }
00108 
00109 int
00110 io_input(struct iop *iop, int waitforinput)
00111 {
00112     char buf[IO_BUFSIZE];
00113     int cc;
00114 
00115     /* Not a read IOP */
00116     if ((iop->flags & IO_READ) == 0)
00117         return -1;
00118     /* IOP is markes as in error. */
00119     if (iop->flags & IO_ERROR)
00120         return -1;
00121     /* Wait for the file to have input. */
00122     if (waitforinput) {
00123         empth_select(iop->fd, EMPTH_FD_READ);
00124     }
00125     /* Do the actual read. */
00126     cc = read(iop->fd, buf, sizeof(buf));
00127     if (cc < 0) {
00128         /* would block, so nothing to read. */
00129         if (errno == EAGAIN || errno == EWOULDBLOCK)
00130             return 0;
00131 
00132         /* Some form of file error occurred... */
00133         iop->flags |= IO_ERROR;
00134         return -1;
00135     }
00136 
00137     /* We eof'd */
00138     if (cc == 0) {
00139         iop->flags |= IO_EOF;
00140         return 0;
00141     }
00142 
00143     /* Append the input to the IOQ. */
00144     ioq_append(iop->input, buf, cc);
00145     return cc;
00146 }
00147 
00148 int
00149 io_inputwaiting(struct iop *iop)
00150 {
00151     return ioq_qsize(iop->input);
00152 }
00153 
00154 int
00155 io_outputwaiting(struct iop *iop)
00156 {
00157     return ioq_qsize(iop->output);
00158 }
00159 
00160 int
00161 io_output(struct iop *iop, int waitforoutput)
00162 {
00163     struct iovec iov[16];
00164     int cc;
00165     int n;
00166     int remain;
00167 
00168     /* If there is no output waiting. */
00169     if (!io_outputwaiting(iop))
00170         return 0;
00171 
00172     /* If the iop is not write enabled. */
00173     if ((iop->flags & IO_WRITE) == 0)
00174         return -1;
00175 
00176     /* If the io is marked as in error... */
00177     if (iop->flags & IO_ERROR)
00178         return -1;
00179 
00180     /* make the iov point to the data in the queue. */
00181     /* I.E., each of the elements in the queue. */
00182     /* returns the number of elements in the iov. */
00183     n = ioq_makeiov(iop->output, iov, IO_BUFSIZE);
00184 
00185     if (n <= 0) {
00186         return 0;
00187     }
00188 
00189     /* wait for the file to be output ready. */
00190     if (waitforoutput != IO_NOWAIT) {
00191         /* This waits for the file to be ready for writing, */
00192         /* and lets other threads run. */
00193         empth_select(iop->fd, EMPTH_FD_WRITE);
00194     }
00195 
00196     /* Do the actual write. */
00197     cc = writev(iop->fd, iov, n);
00198 
00199     /* if it failed.... */
00200     if (cc < 0) {
00201         /* Hmm, it would block.  file is opened noblock, soooooo.. */
00202         if (errno == EAGAIN || errno == EWOULDBLOCK) {
00203             /* If there are remaining bytes, set the IO as remaining.. */
00204             remain = ioq_qsize(iop->output);
00205             return remain;
00206         }
00207         iop->flags |= IO_ERROR;
00208         return -1;
00209     }
00210 
00211     /* If no bytes were written, something happened..  Like an EOF. */
00212     if (cc == 0) {
00213         iop->flags |= IO_EOF;
00214         return 0;
00215     }
00216 
00217     /* Remove the number of written bytes from the queue. */
00218     ioq_dequeue(iop->output, cc);
00219 
00220     return cc;
00221 }
00222 
00223 int
00224 io_peek(struct iop *iop, char *buf, int nbytes)
00225 {
00226     if ((iop->flags & IO_READ) == 0)
00227         return -1;
00228     return ioq_peek(iop->input, buf, nbytes);
00229 }
00230 
00231 int
00232 io_read(struct iop *iop, char *buf, int nbytes)
00233 {
00234     int cc;
00235 
00236     if ((iop->flags & IO_READ) == 0)
00237         return -1;
00238     cc = ioq_peek(iop->input, buf, nbytes);
00239     if (cc > 0)
00240         ioq_dequeue(iop->input, cc);
00241     return cc;
00242 }
00243 
00244 int
00245 io_write(struct iop *iop, char *buf, int nbytes, int doWait)
00246 {
00247     int len;
00248 
00249     if ((iop->flags & IO_WRITE) == 0)
00250         return -1;
00251     ioq_append(iop->output, buf, nbytes);
00252     len = ioq_qsize(iop->output);
00253     if (len > iop->bufsize) {
00254         if (doWait) {
00255             io_output_all(iop);
00256         } else {
00257             /* only try a write every BUFSIZE characters */
00258             if (((len - nbytes) % iop->bufsize) < (len % iop->bufsize))
00259                 io_output(iop, IO_NOWAIT);
00260         }
00261     }
00262     return nbytes;
00263 }
00264 
00265 int
00266 io_output_all(struct iop *iop)
00267 {
00268     int n;
00269 
00270     /*
00271      * Mustn't block a player thread while update is pending, or else
00272      * a malicous player could delay the update indefinitely
00273      */
00274     while ((n = io_output(iop, IO_NOWAIT)) > 0 && !play_wrlock_wanted)
00275         empth_select(iop->fd, EMPTH_FD_WRITE);
00276 
00277     return n;
00278 }
00279 
00280 int
00281 io_gets(struct iop *iop, char *buf, int nbytes)
00282 {
00283     if ((iop->flags & IO_READ) == 0)
00284         return -1;
00285     return ioq_gets(iop->input, buf, nbytes);
00286 }
00287 
00288 int
00289 io_puts(struct iop *iop, char *buf)
00290 {
00291     if ((iop->flags & IO_WRITE) == 0)
00292         return -1;
00293     return ioq_puts(iop->output, buf);
00294 }
00295 
00296 int
00297 io_shutdown(struct iop *iop, int flags)
00298 {
00299     flags &= (IO_READ | IO_WRITE);
00300     if ((iop->flags & flags) != flags)
00301         return -1;
00302     if (flags & IO_READ) {
00303         shutdown(iop->fd, 0);
00304         ioq_drain(iop->input);
00305     }
00306     if (flags & IO_WRITE) {
00307         shutdown(iop->fd, 1);
00308         ioq_drain(iop->output);
00309     }
00310     return 0;
00311 }
00312 
00313 int
00314 io_noblocking(struct iop *iop, int value)
00315 {
00316     int flags;
00317 
00318     flags = fcntl(iop->fd, F_GETFL, 0);
00319     if (flags < 0)
00320         return -1;
00321     if (value == 0)
00322         flags &= ~O_NONBLOCK;
00323     else
00324         flags |= O_NONBLOCK;
00325     if (fcntl(iop->fd, F_SETFL, flags) < 0)
00326         return -1;
00327     if (value == 0)
00328         iop->flags &= ~IO_NBLOCK;
00329     else
00330         iop->flags |= IO_NBLOCK;
00331     return 0;
00332 }
00333 
00334 int
00335 io_error(struct iop *iop)
00336 {
00337     return iop->flags & IO_ERROR;
00338 }
00339 
00340 int
00341 io_eof(struct iop *iop)
00342 {
00343     return iop->flags & IO_EOF;
00344 }
00345 
00346 int
00347 io_fileno(struct iop *iop)
00348 {
00349     return iop->fd;
00350 }

Generated on Fri Mar 28 11:01:14 2008 for empserver by  doxygen 1.5.2