📄 sockd_io.c
字号:
/* * Copyright (c) 1997, 1998, 1999 * Inferno Nettverk A/S, Norway. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. The above copyright notice, this list of conditions and the following * disclaimer must appear in all copies of the software, derivative works * or modified versions, and any portions thereof, aswell as in all * supporting documentation. * 2. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by * Inferno Nettverk A/S, Norway. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Inferno Nettverk A/S requests users of this software to return to * * Software Distribution Coordinator or sdc@inet.no * Inferno Nettverk A/S * Oslo Research Park * Gaustadal閑n 21 * N-0349 Oslo * Norway * * any improvements or extensions that they make and grant Inferno Nettverk A/S * the rights to redistribute these changes. * */#include "common.h"static const char rcsid[] ="$Id: sockd_io.c,v 1.165 1999/12/29 08:58:54 michaels Exp $";/* * Accept io objects from mother and does io on them. We never * send back ancillary data, only ordinary data, so no need for * locking here even on broken systems (#ifdef HAVE_SENDMSG_DEADLOCK). */__BEGIN_DECLSstatic intallocated __P((void));/* * Returns the number of allocated (active) io's */static struct sockd_io_t *io_getset __P((fd_set *set));/* * Goes through our list until it finds a io object where atleast one of the * descriptors is set. * Returns NULL if none found. */static struct sockd_io_t *io_finddescriptor __P((int d));/* * Finds the io object where one of the descriptors matches "fd". */static intio_fillset __P((fd_set *set, int antiflags));/* * Sets all descriptors in our list in the set "set". If "flags" * is set, io's with any of the flags in "flags" set will be excluded. * Returns the highest descriptor in our list, or -1 if we don't * have any descriptors open currently. */static voidio_clearset __P((const struct sockd_io_t *io, fd_set *set));/* * Clears all filedescriptors in "io" from "set". */static voiddoio __P((int mother, struct sockd_io_t *io, fd_set *rset, fd_set *wset, int flags));/* * Does i/o over the descriptors in "io", in to out and out to in. * "mother" is write connection to mother if we need to send a ack. * "io" is the object to do i/o over, * "flags" is the flags to set on the actual i/o calls * (read()/write(), recvfrom()/sendto()), currently only MSG_OOB. * If any of the calls fails the "io" is deleted. */static intio_rw __P((struct sockd_io_direction_t *in, struct sockd_io_direction_t *out, int *bad, char *buf, int flags));/* * Transfers data from "in" to "out" using flag "flags". The data * transfered uses "buf" as a buffer, which must be big enough to * hold the data transfered. * The function never transfers more that the receive low watermark of * "out". * Returns: * On success: bytes transfered or 0 for eof. * On failure: -1. "bad" is set to the value of the descriptor that * failure was first detected on. */static voiddelete_io __P((int mother, struct sockd_io_t *io, int fd, int status));/* * deletes the io object "io". "fd" is the descriptor on which "status" * was returned. If "fd" is negative, it's ignored. * If "mother" is >= 0 the deletion of "io" is ACK'ed to her. * "status" can have one of these values and is normally intended to be the * result from a io call (read/write/etc). * IO_ERRORUNKNOWN: unknown error. * IO_TIMEOUT : connection timed out. ("fd" argument is ignored). * IO_ERROR : error using "fd". * IO_CLOSE : socket was closed. * > 0 : short read/write */static voidproctitleupdate __P((void));/* * Updates the title of this process. */static struct timeval *io_gettimeout __P((struct timeval *timeout));/* * If there is a timeout on the current clients for how long to exist * without doing i/o, this function fills in "timeout" with the appropriate * timeout. * Returns: * If there is a timeout: pointer to filled in "timeout". * If there is no timeout: NULL. */static struct sockd_io_t *io_gettimedout __P((void));/* * Scans all clients for one that has timed out according to config * settings. * Returns: * If timed out client found: pointer to it. * Else: NULL. */static voidcheckmother __P((struct sockd_mother_t *mother, fd_set *readset));/* * Checks if "mother" is set in "readset" and if so receives * a io from "mother". Closes "mother" if there is an error. */static voidsiginfo __P((int sig));/* * Print information about our current connections. *//* Solaris sometimes fails to return srcaddress in recvfrom(). */#define UDPFROMLENCHECK(socket, fromlen) \ do { \ if (fromlen == 0) { \ static int failures; \\ swarnx("%s: system error: did not get address in recvfrom()", \ function); \\ if (++failures > 5) { \ swarnx("%s: running Solaris <= 2.5.1 are we? " \ "giving up after %d failures", function, failures); \ delete_io(mother, io, (socket), IO_ERROR); \ failures = 0; \ } \ return; \ } \ } while (lintnoloop_sockd_h)__END_DECLSstatic struct sockd_io_t iov[SOCKD_IOMAX]; /* each child has these io's. */static int ioc = ELEMENTS(iov);voidrun_io(mother) struct sockd_mother_t *mother;{ const char *function = "run_io()"; struct sigaction sigact; int p; sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigact.sa_handler = siginfo;#if HAVE_SIGNAL_SIGINFO if (sigaction(SIGINFO, &sigact, NULL) != 0) serr(EXIT_FAILURE, "%s: sigaction(SIGINFO)", function);#endif /* HAVE_SIGNAL_SIGINFO */ /* same handler, for systems without SIGINFO. */ if (sigaction(SIGUSR1, &sigact, NULL) != 0) serr(EXIT_FAILURE, "%s: sigaction(SIGINFO)", function); proctitleupdate(); /* CONSTCOND */ while (1) { int rbits, bits; fd_set rset, wset, xset, newrset, controlset, tmpset; struct sockd_io_t *io; struct timeval timeout; io_fillset(&xset, MSG_OOB); rbits = io_fillset(&rset, 0); if (mother->s != -1) { FD_SET(mother->s, &rset); rbits = MAX(rbits, mother->s); } else /* no mother. Do we have any other descriptors to work with? */ if (rbits == -1) { SASSERTX(allocated() == 0); slog(LOG_DEBUG, "%s: can't find mother and no io's", function); sockdexit(-EXIT_FAILURE); } /* * first find descriptors that are readable; we won't write if * we can't read. Also select for exceptions so we can tell * the i/o function if there's one pending later. */ ++rbits; switch (selectn(rbits, &rset, NULL, &xset, io_gettimeout(&timeout))) { case -1: SERR(-1); /* NOTREACHED */ case 0: if ((io = io_gettimedout()) != NULL) delete_io(mother->ack, io, -1, IO_TIMEOUT); /* else: should only be possible if sighup received. */ continue; } checkmother(mother, &rset); /* * This is tricky but we need to check for write separately to * avoid busylooping. * The problem is that if the descriptor is ready for reading but * the corresponding descriptor to write out on is not ready we will * be busylooping; above select will keep returning descriptors set, * but we will not be able to write (and thus read) them. * We therefore only set in wset the descriptors that have the * corresponding read descriptor readable so that when the * below select() returns, the io objects we get from wset will * be both readable and writable. * * Another problem is that if while we wait for writability, a new * descriptor becomes readable, we thus can't block forever here. * We solve this by in the below select() also checking for * readability, but now only the descriptors that were not found * to be readable in the previous select(). * This means that a positive return from below select does not * necessarily indicate we have i/o to do, but it does mean we * either have it or a new descriptor became readable; in either * case, something has happened. * Reason we do not check for exceptions in this select is that * there is nothing we do about them until the descriptor becomes * readable too, thus any new exceptions will be in newrset before * we have reason to care about them. */ /* descriptors to check for readability; those not already set. */ bits = io_fillset(&tmpset, 0); bits = fdsetop(bits + 1, '^', &rset, &tmpset, &newrset); if (mother->s != -1) { /* mother status may change too. */ FD_SET(mother->s, &newrset); bits = MAX(bits, mother->s); } /* * descriptors to check for writability aswell as * controldescriptors to check for readability. */ FD_ZERO(&wset); FD_ZERO(&controlset); for (p = 0; p < rbits; ++p) { if (!FD_ISSET(p, &rset)) { /* only write after read. */ FD_CLR(p, &xset); /* don't care about xset without rset */ continue; } io = io_finddescriptor(p); SASSERTX(io != NULL); if (io->in.s == p) { /* read from in requires out to be writable. */ FD_SET(io->out.s, &wset); bits = MAX(bits, io->out.s); } else if (io->out.s == p) { /* read from out requires in to be writable. */ FD_SET(io->in.s, &wset); bits = MAX(bits, io->in.s); } else { SASSERTX(io->control.s == p); FD_SET(io->control.s, &controlset); /* also readable without matching writable. */ FD_SET(io->control.s, &newrset); bits = MAX(bits, io->control.s); } } if (bits++ < 0) { SASSERTX(allocated() == 0 && mother->s == mother->ack && mother->s < 0); continue; } switch (selectn(bits, &newrset, &wset, NULL, io_gettimeout(&timeout))) { case -1: SERR(-1); /* NOTREACHED */ case 0: if ((io = io_gettimedout()) != NULL) delete_io(mother->ack, io, -1, IO_TIMEOUT); /* else: should only be possible if sighup received. */ continue; } checkmother(mother, &rset); tmpset = controlset; fdsetop(bits, '&', &newrset, &tmpset, &controlset); /* * newrset; descriptors readable, all new apart from controldescriptors. * Don't do anything with them here, loop around and check for * writability first. * * controlset; subset of newrset containing control descriptors * that are readable. * * rset; descriptors readable, not necessarily with a match in wset. * * xset; subset of rset with exceptions pending. * * wset; descriptors writable with a matching in rset/xset, * what we can do i/o over. */ /* * First check all io's which have an exception pending. * Getting a io here does not mean we can do i/o over it * however. */ while ((io = io_getset(&xset)) != NULL) { slog(LOG_DEBUG, "select(): exception set"); doio(mother->ack, io, &xset, &wset, MSG_OOB); io_clearset(io, &xset); io_clearset(io, &wset); /* xset is subset of rset so clear rset too. */ io_clearset(io, &rset); } /* * Get all io's which are writable. They will have a matching * descriptor that is readable. */ while ((io = io_getset(&wset)) != NULL) { doio(mother->ack, io, &rset, &wset, 0); io_clearset(io, &rset); io_clearset(io, &wset); /* xset is subset of rset so clear xset too. */ io_clearset(io, &xset); } /* * Get all io's which have controldescriptors that are readable. */ while ((io = io_getset(&controlset)) != NULL) { fd_set nullset; FD_ZERO(&nullset); doio(mother->ack, io, &controlset, &nullset, 0); io_clearset(io, &controlset); /* controlset is subset of newrset so clear newrset too. */ io_clearset(io, &newrset); } /* possible future optimization: if newrset not empty, use it? */ }}static voiddelete_io(mother, io, fd, status) int mother; struct sockd_io_t *io; int fd; int status;{ const char *function = "delete_io()"; const int errno_s = errno;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -