jabberd2  2.2.16
mio/mio_impl.h
Go to the documentation of this file.
00001 /*
00002  * jabberd - Jabber Open Source Server
00003  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
00004  *                    Ryan Eatmon, Robert Norris, Christof Meerwald
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, MA02111-1307USA
00019  */
00020 
00021 /*
00022    MIO -- Managed Input/Output
00023    ---------------------------
00024 */
00025 
00026 #include "util/inaddr.h"
00027 
00028 /* win32 wrappers around strerror */
00029 #ifdef _WIN32
00030 #define close(x) closesocket(x)
00031 JABBERD2_API char *mio_strerror(int code)
00032 {
00033   static char buff[1024];
00034   if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, buff, sizeof(buff), NULL))
00035     return buff;
00036   return strerror(code);
00037 }
00038 #endif /* _WIN32 */
00039 
00041 typedef enum { 
00042     type_CLOSED = 0x00, 
00043     type_NORMAL = 0x01, 
00044     type_LISTEN = 0x02, 
00045     type_CONNECT = 0x10, 
00046     type_CONNECT_READ = 0x11,
00047     type_CONNECT_WRITE = 0x12
00048 } mio_type_t;
00049 typedef struct mio_priv_fd_st
00050 {
00051     struct mio_fd_st mio_fd;
00052 
00053     mio_type_t type;
00054     /* app event handler and data */
00055     mio_handler_t app;
00056     void *arg;
00057 
00058     MIO_FD_VARS
00059 } *mio_priv_fd_t;
00060 
00062 typedef struct mio_priv_st
00063 {
00064     struct mio_st *mio;
00065 
00066     int maxfd;
00067     MIO_VARS
00068 } *mio_priv_t;
00069 
00070 /* lazy factor */
00071 #define MIO(m) ((mio_priv_t) m)
00072 #define FD(m,f) ((mio_priv_fd_t) f)
00073 #define ACT(m,f,a,d) (*(FD(m,f)->app))(m,a,&FD(m,f)->mio_fd,d,FD(m,f)->arg)
00074 
00075 /* temp debug outputter */
00076 #define ZONE __LINE__
00077 #ifndef MIO_DEBUG
00078 #define MIO_DEBUG 0
00079 #endif
00080 #define mio_debug if(MIO_DEBUG) _mio_debug
00081 static void _mio_debug(int line, const char *msgfmt, ...)
00082 {
00083     va_list ap;
00084     va_start(ap,msgfmt);
00085     fprintf(stderr,"mio.c#%d: ",line);
00086     vfprintf(stderr,msgfmt,ap);
00087     va_end(ap);
00088     fprintf(stderr,"\n");
00089 }
00090 
00091 MIO_FUNCS
00092 
00094 static mio_fd_t _mio_setup_fd(mio_t m, int fd, mio_handler_t app, void *arg)
00095 {
00096     int flags;
00097     mio_fd_t mio_fd;
00098 
00099     mio_debug(ZONE, "adding fd #%d", fd);
00100 
00101     mio_fd = MIO_ALLOC_FD(m, fd);
00102     if (mio_fd == NULL) return NULL;
00103 
00104     /* ok to process this one, welcome to the family */
00105     FD(m,mio_fd)->type = type_NORMAL;
00106     FD(m,mio_fd)->app = app;
00107     FD(m,mio_fd)->arg = arg;
00108 
00109     /* set the socket to non-blocking */
00110 #if defined(HAVE_FCNTL)
00111     flags = fcntl(fd, F_GETFL);
00112     flags |= O_NONBLOCK;
00113     fcntl(fd, F_SETFL, flags);
00114 #elif defined(HAVE_IOCTL)
00115     flags = 1;
00116     ioctl(fd, FIONBIO, &flags);
00117 #endif
00118 
00119     return mio_fd;
00120 }
00121 
00123 static void _mio_close(mio_t m, mio_fd_t fd)
00124 {
00125     if(FD(m,fd)->type == type_CLOSED)
00126         return;
00127 
00128     mio_debug(ZONE,"actually closing fd #%d", fd->fd);
00129 
00130     /* take out of poll sets */
00131     MIO_REMOVE_FD(m, FD(m,fd));
00132 
00133     /* let the app know, it must process any waiting write data it has and free it's arg */
00134     if (FD(m,fd)->app != NULL)
00135         ACT(m, fd, action_CLOSE, NULL);
00136 
00137     /* close the socket, and reset all memory */
00138     close(fd->fd);
00139     FD(m,fd)->type = type_CLOSED;
00140     FD(m,fd)->app = NULL;
00141     FD(m,fd)->arg = NULL;
00142 
00143     if (MIO_CAN_FREE(m))
00144     {
00145         MIO_FREE_FD(m, fd);
00146     }
00147 }
00148 
00150 static void _mio_accept(mio_t m, mio_fd_t fd)
00151 {
00152     struct sockaddr_storage serv_addr;
00153     socklen_t addrlen = (socklen_t) sizeof(serv_addr);
00154     int newfd;
00155     mio_fd_t mio_fd;
00156     char ip[INET6_ADDRSTRLEN];
00157 
00158     mio_debug(ZONE, "accepting on fd #%d", fd->fd);
00159 
00160     /* pull a socket off the accept queue and check */
00161     newfd = accept(fd->fd, (struct sockaddr*)&serv_addr, &addrlen);
00162     if(newfd <= 0) return;
00163     if(addrlen <= 0) {
00164         close(newfd);
00165         return;
00166     }
00167 
00168     j_inet_ntop(&serv_addr, ip, sizeof(ip));
00169     mio_debug(ZONE, "new socket accepted fd #%d, %s:%d", newfd, ip, j_inet_getport(&serv_addr));
00170 
00171     /* set up the entry for this new socket */
00172     mio_fd = _mio_setup_fd(m, newfd, FD(m,fd)->app, FD(m,fd)->arg);
00173 
00174     if(!mio_fd) {
00175         close(newfd);
00176         return;
00177     }
00178 
00179     /* tell the app about the new socket, if they reject it clean up */
00180     if (ACT(m, mio_fd, action_ACCEPT, ip))
00181     {
00182         mio_debug(ZONE, "accept was rejected for %s:%d", ip, newfd);
00183         MIO_REMOVE_FD(m, FD(m,mio_fd));
00184 
00185         /* close the socket, and reset all memory */
00186         close(newfd);
00187         MIO_FREE_FD(m, mio_fd);
00188     }
00189 
00190     return;
00191 }
00192 
00194 static void _mio__connect(mio_t m, mio_fd_t fd)
00195 {
00196     mio_type_t type = FD(m,fd)->type;
00197 
00198     mio_debug(ZONE, "connect processing for fd #%d", fd->fd);
00199 
00200     /* reset type and clear the "write" event that flags connect() is done */
00201     FD(m,fd)->type = type_NORMAL;
00202     MIO_UNSET_WRITE(m,FD(m,fd));
00203 
00204     /* if the app had asked to do anything in the meantime, do those now */
00205     if(type & type_CONNECT_READ) mio_read(m,fd);
00206     if(type & type_CONNECT_WRITE) mio_write(m,fd);
00207 }
00208 
00210 static void _mio_app(mio_t m, mio_fd_t fd, mio_handler_t app, void *arg)
00211 {
00212     FD(m,fd)->app = app;
00213     FD(m,fd)->arg = arg;
00214 }
00215 
00217 static void _mio_run(mio_t m, int timeout)
00218 {
00219     int retval;
00220     MIO_INIT_ITERATOR(iter);
00221 
00222     mio_debug(ZONE, "mio running for %d sec", timeout);
00223 
00224     /* wait for a socket event */
00225     retval = MIO_CHECK(m, timeout);
00226 
00227     /* nothing to do */
00228     if(retval == 0) return;
00229 
00230     /* an error */
00231     if(retval < 0)
00232     {
00233         mio_debug(ZONE, "MIO_CHECK returned an error (%d)", MIO_ERROR);
00234 
00235         return;
00236     }
00237 
00238     mio_debug(ZONE,"mio processing %d file descriptors", retval);
00239 
00240     /* loop through the sockets, check for stuff to do */
00241     MIO_ITERATE_RESULTS(m, retval, iter)
00242     {
00243         mio_fd_t fd = MIO_ITERATOR_FD(m,iter);
00244         if (fd == NULL) continue;
00245 
00246         /* skip already dead slots */ 
00247         if(FD(m,fd)->type == type_CLOSED) continue; 
00248 
00249         /* new conns on a listen socket */
00250         if(FD(m,fd)->type == type_LISTEN && MIO_CAN_READ(m,iter))
00251         {
00252             _mio_accept(m, fd);
00253             goto deferred;
00254         }
00255 
00256         /* check for connecting sockets */
00257         if(FD(m,fd)->type & type_CONNECT &&
00258            (MIO_CAN_READ(m,iter) || MIO_CAN_WRITE(m,iter)))
00259         {
00260             _mio__connect(m, fd);
00261             goto deferred;
00262         }
00263 
00264         /* read from ready sockets */
00265         if(FD(m,fd)->type == type_NORMAL && MIO_CAN_READ(m,iter))
00266         {
00267             /* if they don't want to read any more right now */
00268             if(ACT(m, fd, action_READ, NULL) == 0)
00269                 MIO_UNSET_READ(m, FD(m,fd));
00270         }
00271 
00272         /* write to ready sockets */
00273         if(FD(m,fd)->type == type_NORMAL && MIO_CAN_WRITE(m,iter))
00274         {
00275             /* don't wait for writeability if nothing to write anymore */
00276             if(ACT(m, fd, action_WRITE, NULL) == 0)
00277                 MIO_UNSET_WRITE(m, FD(m,fd));
00278         }
00279 
00280     deferred:
00281         /* deferred closing fd
00282          * one of previous actions might change the state of fd */ 
00283         if(FD(m,fd)->type == type_CLOSED)
00284         {
00285             MIO_FREE_FD(m, fd);
00286         }
00287     }
00288 }
00289 
00291 static void _mio_read(mio_t m, mio_fd_t fd)
00292 {
00293     if(m == NULL || fd == NULL) return;
00294 
00295     /* if connecting, do this later */
00296     if(FD(m,fd)->type & type_CONNECT)
00297     {
00298         FD(m,fd)->type |= type_CONNECT_READ;
00299         return;
00300     }
00301 
00302     MIO_SET_READ(m, FD(m,fd));
00303 }
00304 
00306 static void _mio_write(mio_t m, mio_fd_t fd)
00307 {
00308     if(m == NULL || fd == NULL) return;
00309 
00310     /* if connecting, do this later */
00311     if(FD(m,fd)->type & type_CONNECT)
00312     {
00313         FD(m,fd)->type |= type_CONNECT_WRITE;
00314         return;
00315     }
00316 
00317     if(FD(m,fd)->type != type_NORMAL)
00318         return;
00319 
00320     if(ACT(m, fd, action_WRITE, NULL) == 0) return;
00321 
00322     /* not all written, do more l8r */
00323     MIO_SET_WRITE(m, FD(m,fd));
00324 }
00325 
00327 static mio_fd_t _mio_listen(mio_t m, int port, char *sourceip, mio_handler_t app, void *arg)
00328 {
00329     int fd, flag = 1;
00330     mio_fd_t mio_fd;
00331     struct sockaddr_storage sa;
00332 
00333     if(m == NULL) return NULL;
00334 
00335     mio_debug(ZONE, "mio to listen on %d [%s]", port, sourceip);
00336 
00337     memset(&sa, 0, sizeof(sa));
00338 
00339     /* if we specified an ip to bind to */
00340     if(sourceip != NULL && !j_inet_pton(sourceip, &sa))
00341         return NULL;
00342 
00343     if(sa.ss_family == 0)
00344         sa.ss_family = AF_INET;
00345     
00346     /* attempt to create a socket */
00347     if((fd = socket(sa.ss_family,SOCK_STREAM,0)) < 0) return NULL;
00348     if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&flag, sizeof(flag)) < 0) return NULL;
00349 
00350     /* set up and bind address info */
00351     j_inet_setport(&sa, port);
00352     if(bind(fd,(struct sockaddr*)&sa,j_inet_addrlen(&sa)) < 0)
00353     {
00354         close(fd);
00355         return NULL;
00356     }
00357 
00358     /* start listening with a max accept queue specified by kern.ipc.somaxconn sysctl */
00359     if(listen(fd, -1) < 0)
00360     {
00361         close(fd);
00362         return NULL;
00363     }
00364 
00365     /* now set us up the bomb */
00366     mio_fd = _mio_setup_fd(m, fd, app, arg);
00367     if(mio_fd == NULL)
00368     {
00369         close(fd);
00370         return NULL;
00371     }
00372     FD(m,mio_fd)->type = type_LISTEN;
00373     /* by default we read for new sockets */
00374     mio_read(m,mio_fd);
00375 
00376     return mio_fd;
00377 }
00378 
00380 static mio_fd_t _mio_connect(mio_t m, int port, char *hostip, char *srcip, mio_handler_t app, void *arg)
00381 {
00382     int fd, flag, flags;
00383     mio_fd_t mio_fd;
00384     struct sockaddr_storage sa, src;
00385 
00386     memset(&sa, 0, sizeof(sa));
00387 
00388     if(m == NULL || port <= 0 || hostip == NULL) return NULL;
00389 
00390     mio_debug(ZONE, "mio connecting to %s, port=%d",hostip,port);
00391 
00392     /* convert the hostip */
00393     if(j_inet_pton(hostip, &sa)<=0) {
00394         MIO_SETERROR(EFAULT);
00395         return NULL;
00396     }
00397 
00398     if(!sa.ss_family) sa.ss_family = AF_INET;
00399     
00400     /* attempt to create a socket */
00401     if((fd = socket(sa.ss_family,SOCK_STREAM,0)) < 0) return NULL;
00402 
00403     /* Bind to the given source IP if it was specified */
00404     if (srcip != NULL) {
00405         /* convert the srcip */
00406         if(j_inet_pton(srcip, &src)<=0) {
00407             MIO_SETERROR(EFAULT);
00408             return NULL;
00409         }
00410         if(!src.ss_family) src.ss_family = AF_INET;
00411         j_inet_setport(&src, INADDR_ANY);
00412         if(bind(fd,(struct sockaddr*)&src,j_inet_addrlen(&src)) < 0) {
00413             close(fd);
00414             return NULL;
00415         }
00416     }
00417 
00418     /* set the socket to non-blocking before connecting */
00419 #if defined(HAVE_FCNTL)
00420     flags = fcntl(fd, F_GETFL);
00421     flags |= O_NONBLOCK;
00422     fcntl(fd, F_SETFL, flags);
00423 #elif defined(HAVE_IOCTL)
00424     flags = 1;
00425     ioctl(fd, FIONBIO, &flags);
00426 #endif
00427 
00428     /* set up address info */
00429     j_inet_setport(&sa, port);
00430 
00431     /* try to connect */
00432     flag = connect(fd,(struct sockaddr*)&sa,j_inet_addrlen(&sa));
00433 
00434     mio_debug(ZONE, "connect returned %d and %s", flag, MIO_STRERROR(MIO_ERROR));
00435 
00436     /* already connected?  great! */
00437     if(flag == 0)
00438     {
00439         mio_fd = _mio_setup_fd(m,fd,app,arg);
00440         if(mio_fd != NULL) return mio_fd;
00441     }
00442 
00443     /* gotta wait till later */
00444 #ifdef _WIN32
00445     if(flag == -1 && WSAGetLastError() == WSAEWOULDBLOCK)
00446 #else
00447     if(flag == -1 && errno == EINPROGRESS)
00448 #endif
00449     {
00450         mio_fd = _mio_setup_fd(m,fd,app,arg);
00451         if(mio_fd != NULL)
00452         {
00453             mio_debug(ZONE, "connect processing non-blocking mode");
00454 
00455             FD(m,mio_fd)->type = type_CONNECT;
00456             MIO_SET_WRITE(m,FD(m,mio_fd));
00457             return mio_fd;
00458         }
00459     }
00460 
00461     /* bummer dude */
00462     close(fd);
00463     return NULL;
00464 }
00465 
00466 
00468 static void _mio_free(mio_t m)
00469 {
00470     MIO_FREE_VARS(m);
00471 
00472     free(m);
00473 }
00474 
00476 static mio_t _mio_new(int maxfd)
00477 {
00478     static struct mio_st mio_impl = {
00479         _mio_free,
00480         _mio_listen, _mio_connect, _mio_setup_fd,
00481         _mio_app,
00482         _mio_close,
00483         _mio_write, _mio_read,
00484         _mio_run
00485     };
00486     mio_t m;
00487 
00488     /* init winsock if we are in Windows */
00489 #ifdef _WIN32
00490     WSADATA wsaData;
00491     if (WSAStartup(MAKEWORD( 1, 1 ), &wsaData))
00492         return NULL;
00493 #endif
00494 
00495     /* allocate and zero out main memory */
00496     if((m = calloc(1, sizeof(struct mio_priv_st))) == NULL) {
00497         fprintf(stderr,"Cannot allocate MIO memory! Exiting.\n");
00498         exit(EXIT_FAILURE);
00499     }
00500 
00501     /* set up our internal vars */
00502     *m = &mio_impl;
00503     MIO(m)->maxfd = maxfd;
00504 
00505     MIO_INIT_VARS(m);
00506 
00507     return m;
00508 }