diff options
author | Rainer Gerhards <rgerhards@adiscon.com> | 2008-04-23 09:59:01 +0200 |
---|---|---|
committer | Rainer Gerhards <rgerhards@adiscon.com> | 2008-04-23 09:59:01 +0200 |
commit | 2be459c4d7645ad12f83723be7bb26199fe98b82 (patch) | |
tree | 749db3a20481dea667b5405961c173c4317d9e0d /runtime | |
parent | 22ad77a627fe813acd286b48aaf44945c0480f49 (diff) | |
download | rsyslog-2be459c4d7645ad12f83723be7bb26199fe98b82.tar.gz rsyslog-2be459c4d7645ad12f83723be7bb26199fe98b82.tar.xz rsyslog-2be459c4d7645ad12f83723be7bb26199fe98b82.zip |
objects for receive-side socket abstraction specified
The objects for receiver-side socket abstraction have now be
specified. The project as whole does not yet compile and
definitely not run, but I'd like to commit some milestones along
this way.
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/Makefile.am | 17 | ||||
-rw-r--r-- | runtime/netstrm.c | 82 | ||||
-rw-r--r-- | runtime/netstrm.h | 34 | ||||
-rw-r--r-- | runtime/nsd.h | 20 | ||||
-rw-r--r-- | runtime/nsd_ptcp.c | 187 | ||||
-rw-r--r-- | runtime/nsd_ptcp.h | 2 | ||||
-rw-r--r-- | runtime/nsdsel_ptcp.c | 216 | ||||
-rw-r--r-- | runtime/nsdsel_ptcp.h | 46 | ||||
-rw-r--r-- | runtime/nssel.c | 222 | ||||
-rw-r--r-- | runtime/nssel.h | 55 | ||||
-rw-r--r-- | runtime/rsyslog.h | 5 |
11 files changed, 739 insertions, 147 deletions
diff --git a/runtime/Makefile.am b/runtime/Makefile.am index 077310c6..61ede1d7 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -81,7 +81,7 @@ lmregexp_la_LIBADD = endif if ENABLE_INET -pkglib_LTLIBRARIES += lmnet.la lmnetstrm.la +pkglib_LTLIBRARIES += lmnet.la lmnetstrm.la lmnssel.la # # network support # @@ -96,12 +96,25 @@ lmnetstrm_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) lmnetstrm_la_LDFLAGS = -module -avoid-version lmnetstrm_la_LIBADD = +# network stream select support (a helper class) +lmnssel_la_SOURCES = nssel.c nssel.h +lmnssel_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) +lmnssel_la_LDFLAGS = -module -avoid-version +lmnssel_la_LIBADD = + # netstream drivers -# plain tcp driver +# plain tcp driver - main driver pkglib_LTLIBRARIES += lmnsd_ptcp.la lmnsd_ptcp_la_SOURCES = nsd_ptcp.c nsd_ptcp.h lmnsd_ptcp_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) lmnsd_ptcp_la_LDFLAGS = -module -avoid-version lmnsd_ptcp_la_LIBADD = + +# select interface for ptcp driver +pkglib_LTLIBRARIES += lmnsdsel_ptcp.la +lmnsdsel_ptcp_la_SOURCES = nsdsel_ptcp.c nsdsel_ptcp.h +lmnsdsel_ptcp_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) +lmnsdsel_ptcp_la_LDFLAGS = -module -avoid-version +lmnsdsel_ptcp_la_LIBADD = endif # if ENABLE_INET diff --git a/runtime/netstrm.c b/runtime/netstrm.c index 274a92d7..bdd2636c 100644 --- a/runtime/netstrm.c +++ b/runtime/netstrm.c @@ -144,73 +144,65 @@ AbortDestruct(netstrm_t **ppThis) } -#if 0 -This is not yet working - wait until we arrive at the receiver side (distracts too much at the moment) - -/* accept an incoming connection request, pNsdLstn provides the "listen socket" on which we can - * accept the new session. - * rgerhards, 2008-03-17 +/* accept an incoming connection request + * The netstrm instance that had the incoming request must be provided. If + * the connection request succeeds, a new netstrm object is created and + * passed back to the caller. The caller is responsible for destructing it. + * pReq is the nsd_t obj that has the accept request. + * rgerhards, 2008-04-21 */ static rsRetVal -AcceptConnReq(netstrm_t **ppThis, nsd_t *pNsdLstn) +AcceptConnReq(netstrm_t *pThis, nsd_t *pReqNsd, netstrm_t **ppNew) { - netstrm_t *pThis = NULL; - nsd_t *pNsd; + netstrm_t *pNew = NULL; + nsd_t *pNewNsd = NULL; DEFiRet; - assert(ppThis != NULL); + ISOBJ_TYPE_assert(pThis, netstrm); + assert(pReqNsd != NULL); + assert(ppNew != NULL); + + /* accept the new connection */ + CHKiRet(pThis->Drvr.AcceptConnReq(pReqNsd, &pNewNsd)); /* construct our object so that we can use it... */ - CHKiRet(netstrmConstruct(&pThis)); - - /* TODO: obtain hostname, normalize (callback?), save it */ - CHKiRet(FillRemHost(pThis, (struct sockaddr*) &addr)); - - /* set the new socket to non-blocking IO */ - if((sockflags = fcntl(iNewSock, F_GETFL)) != -1) { - sockflags |= O_NONBLOCK; - /* SETFL could fail too, so get it caught by the subsequent - * error check. - */ - sockflags = fcntl(iNewSock, F_SETFL, sockflags); - } - if(sockflags == -1) { - dbgprintf("error %d setting fcntl(O_NONBLOCK) on tcp socket %d", errno, iNewSock); - ABORT_FINALIZE(RS_RET_IO_ERROR); - } + CHKiRet(netstrmConstruct(&pNew)); - pThis->sock = iNewSock; + pNew->pDrvrData = pNewNsd; + if(pThis->pDrvrName == NULL) { + pNew->pDrvrName = NULL; + } else { + CHKmalloc(pNew->pDrvrName = (uchar*) strdup((char*)pThis->pDrvrName)); + } + CHKiRet(loadDrvr(pNew)); - *ppThis = pThis; + *ppNew = pNew; finalize_it: if(iRet != RS_RET_OK) { - if(pThis != NULL) - netstrmDestruct(&pThis); + if(pNew != NULL) + netstrmDestruct(&pNew); /* the close may be redundant, but that doesn't hurt... */ - if(iNewSock >= 0) - close(iNewSock); + if(pNewNsd != NULL) + pThis->Drvr.Destruct(&pNewNsd); } - RETiRet; } -#endif -/* initialize the tcp socket for a listner - * pLstnPort must point to a port name or number. NULL is NOT permitted - * (hint: we need to be careful when we use this module together with librelp, - * there NULL indicates the default port - * default is used. - * gerhards, 2008-03-17 +/* make the netstrm listen to specified port and IP. + * pLstnIP points to the port to listen to (NULL means "all"), + * iMaxSess has the maximum number of sessions permitted (this ist just a hint). + * pLstnPort must point to a port name or number. NULL is NOT permitted. + * rgerhards, 2008-04-22 */ static rsRetVal -LstnInit(netstrm_t *pThis, uchar *pLstnPort) +LstnInit(netstrm_t *pThis, uchar *pLstnPort, uchar *pLstnIP, int iSessMax) { DEFiRet; ISOBJ_TYPE_assert(pThis, netstrm); assert(pLstnPort != NULL); - CHKiRet(pThis->Drvr.LstnInit(pThis->pDrvrData, pLstnPort)); + CHKiRet(pThis->Drvr.LstnInit(&pThis->parrLstn, &pThis->isizeLstnArr, pLstnPort, pLstnIP, iSessMax)); finalize_it: RETiRet; @@ -284,11 +276,11 @@ CODESTARTobjQueryInterface(netstrm) pIf->ConstructFinalize = netstrmConstructFinalize; pIf->Destruct = netstrmDestruct; pIf->AbortDestruct = AbortDestruct; - pIf->LstnInit = LstnInit; - // TODO: add later: pIf->AcceptConnReq = AcceptConnReq; pIf->Rcv = Rcv; pIf->Send = Send; pIf->Connect = Connect; + pIf->LstnInit = LstnInit; + pIf->AcceptConnReq = AcceptConnReq; finalize_it: ENDobjQueryInterface(netstrm) diff --git a/runtime/netstrm.h b/runtime/netstrm.h index 75b7c457..a3719f93 100644 --- a/runtime/netstrm.h +++ b/runtime/netstrm.h @@ -29,11 +29,34 @@ /* the netstrm object */ struct netstrm_s { BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ - nsd_if_t Drvr; /**< our stream driver */ nsd_t *pDrvrData; /**< the driver's data elements */ uchar *pDrvrName; /**< nsd driver name to use, or NULL if system default */ + nsd_if_t Drvr; /**< our stream driver */ + /* for listeners, we need to have the capablity to listen on multiple "sockets". This + * is needed to support IPv6. We do this by specifying an array of nsd_t objects to + * handle this case. + */ + int isizeLstnArr; + nsd_t **parrLstn; }; +/* a helper object enabling us to wait on a set of streams to become + * ready for IO - this is modelled after select(). We need this, because + * stream drivers may have different concepts. Consequently, + * the structure must contain nsd_t's from the same stream driver type + * only. This is implemented as a singly-linked list where every + * new element is added at the top of the list. -- rgerhards, 2008-04-22 + */ +typedef struct netstrm_iowaiter_s netstrm_iowaiter_t; +struct netstrm_iowaiter_s { + netstrm_iowaiter_t *pNext; + nsd_t *pNsd; + enum { + NETSTRM_IOWAIT_RD = 1, + NETSTRM_IOWAIT_WR = 2, + NETSTRM_IOWAIT_RDWR = 3 + } waitOp; /**< the operation we wait for */ +}; /* interface */ BEGINinterface(netstrm) /* name must also be changed in ENDinterface macro! */ @@ -41,11 +64,16 @@ BEGINinterface(netstrm) /* name must also be changed in ENDinterface macro! */ rsRetVal (*ConstructFinalize)(netstrm_t *pThis); rsRetVal (*Destruct)(netstrm_t **ppThis); rsRetVal (*AbortDestruct)(netstrm_t **ppThis); - rsRetVal (*LstnInit)(netstrm_t *pThis, unsigned char *pLstnPort); - rsRetVal (*AcceptConnReq)(netstrm_t **ppThis, int sock); + rsRetVal (*LstnInit)(netstrm_t *pThis, uchar *pLstnPort, uchar *pLstnIP, int iSessMax); + rsRetVal (*AcceptConnReq)(netstrm_t *pThis, nsd_t *pReqNsd, netstrm_t **ppNew); rsRetVal (*Rcv)(netstrm_t *pThis, uchar *pRcvBuf, ssize_t *pLenBuf); rsRetVal (*Send)(netstrm_t *pThis, uchar *pBuf, ssize_t *pLenBuf); rsRetVal (*Connect)(netstrm_t *pThis, int family, unsigned char *port, unsigned char *host); + rsRetVal (*SelectInit)(nsdsel_t **ppSel, netstrm_t *pThis); + rsRetVal (*SelectAdd)(nsdsel_t *pSel, netstrm_t *pThis); + rsRetVal (*SelectWait)(nsdsel_t *pSel, int *piNumReady); + rsRetVal (*SelectIsReady)(nsdsel_t *pSel, int *piNumReady); + rsRetVal (*SelectExit)(nsdsel_t **ppSel); ENDinterface(netstrm) #define netstrmCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ diff --git a/runtime/nsd.h b/runtime/nsd.h index 52c36dcf..2fb883ac 100644 --- a/runtime/nsd.h +++ b/runtime/nsd.h @@ -28,6 +28,12 @@ #ifndef INCLUDED_NSD_H #define INCLUDED_NSD_H +enum nsdsel_waitOp_e { + NSDSEL_RD = 1, + NSDSEL_WR = 2, + NSDSEL_RDWR = 3 +}; /**< the operation we wait for */ + /* nsd_t is actually obj_t (which is somewhat better than void* but in essence * much the same). */ @@ -37,12 +43,22 @@ BEGINinterface(nsd) /* name must also be changed in ENDinterface macro! */ rsRetVal (*Construct)(nsd_t **ppThis); rsRetVal (*Destruct)(nsd_t **ppThis); rsRetVal (*Abort)(nsd_t *pThis); - rsRetVal (*LstnInit)(nsd_t *pThis, unsigned char *pLstnPort); - rsRetVal (*AcceptConnReq)(nsd_t **ppThis, int sock); rsRetVal (*Rcv)(nsd_t *pThis, uchar *pRcvBuf, ssize_t *pLenBuf); rsRetVal (*Send)(nsd_t *pThis, uchar *pBuf, ssize_t *pLenBuf); rsRetVal (*Connect)(nsd_t *pThis, int family, unsigned char *port, unsigned char *host); + rsRetVal (*LstnInit)(nsd_t ***parrLstn, int *pLstnArrSize, uchar *pLstnPort, uchar *pLstnIP, int iSessMax); + rsRetVal (*AcceptConnReq)(nsd_t *pThis, nsd_t **ppThis); ENDinterface(nsd) #define nsdCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ +/* interface for the select call */ +BEGINinterface(nsdsel) /* name must also be changed in ENDinterface macro! */ + rsRetVal (*Construct)(nsdsel_t **ppThis); + rsRetVal (*Destruct)(nsdsel_t **ppThis); + rsRetVal (*Add)(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp); + rsRetVal (*Select)(nsdsel_t *pNsdsel, int *piNumReady); + rsRetVal (*IsReady)(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp); +ENDinterface(nsdsel) +#define nsdselCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ + #endif /* #ifndef INCLUDED_NSD_H */ diff --git a/runtime/nsd_ptcp.c b/runtime/nsd_ptcp.c index 6f7dd04d..c737c168 100644 --- a/runtime/nsd_ptcp.c +++ b/runtime/nsd_ptcp.c @@ -56,30 +56,29 @@ DEFobjCurrIf(glbl) DEFobjCurrIf(net) +/* a few deinit helpers */ + +/* close socket if open (may always be called) */ +static void +sockClose(int *pSock) +{ + if(*pSock >= 0) { + close(*pSock); + *pSock = -1; + } +} + /* Standard-Constructor */ BEGINobjConstruct(nsd_ptcp) /* be sure to specify the object type also in END macro! */ pThis->sock = -1; - pThis->iSessMax = 500; /* default max nbr of sessions -TODO:make configurable--rgerhards, 2008-04-17*/ ENDobjConstruct(nsd_ptcp) /* destructor for the nsd_ptcp object */ BEGINobjDestruct(nsd_ptcp) /* be sure to specify the object type also in END and CODESTART macros! */ - int i; CODESTARTobjDestruct(nsd_ptcp) - if(pThis->sock != -1) { - close(pThis->sock); - pThis->sock = -1; - } - - if(pThis->socks != NULL) { - /* if we have some sockets at this stage, we need to close them */ - for(i = 1 ; i <= pThis->socks[0] ; ++i) - close(pThis->socks[i]); - free(pThis->socks); - } - + sockClose(&pThis->sock); if(pThis->pRemHostIP != NULL) free(pThis->pRemHostIP); if(pThis->pRemHostName != NULL) @@ -189,33 +188,34 @@ finalize_it: -/* accept an incoming connection request, sock provides the socket on which we can - * accept the new session. - * rgerhards, 2008-03-17 +/* accept an incoming connection request + * rgerhards, 2008-04-22 */ static rsRetVal -AcceptConnReq(nsd_t **ppThis, int sock) +AcceptConnReq(nsd_t *pNsd, nsd_t **ppNew) { int sockflags; + nsd_ptcp_t *pThis = (nsd_ptcp_t*) pNsd; struct sockaddr_storage addr; socklen_t addrlen = sizeof(addr); - nsd_ptcp_t *pThis = NULL; + nsd_ptcp_t *pNew = NULL; int iNewSock = -1; DEFiRet; - assert(ppThis != NULL); + assert(ppNew != NULL); + ISOBJ_TYPE_assert(pThis, nsd_ptcp_t); - iNewSock = accept(sock, (struct sockaddr*) &addr, &addrlen); + iNewSock = accept(pThis->sock, (struct sockaddr*) &addr, &addrlen); if(iNewSock < 0) { ABORT_FINALIZE(RS_RET_ACCEPT_ERR); } /* construct our object so that we can use it... */ - CHKiRet(nsd_ptcpConstruct(&pThis)); + CHKiRet(nsd_ptcpConstruct(&pNew)); - CHKiRet(FillRemHost(pThis, (struct sockaddr*) &addr)); + CHKiRet(FillRemHost(pNew, (struct sockaddr*) &addr)); - /* set the new socket to non-blocking IO */ + /* set the new socket to non-blocking IO -TODO:do we really need to do this here? Do we always want it? */ if((sockflags = fcntl(iNewSock, F_GETFL)) != -1) { sockflags |= O_NONBLOCK; /* SETFL could fail too, so get it caught by the subsequent @@ -228,41 +228,41 @@ AcceptConnReq(nsd_t **ppThis, int sock) ABORT_FINALIZE(RS_RET_IO_ERROR); } - pThis->sock = iNewSock; - - *ppThis = (nsd_t*) pThis; + pNew->sock = iNewSock; + *ppNew = (nsd_t*) pNew; finalize_it: if(iRet != RS_RET_OK) { - if(pThis != NULL) - nsd_ptcpDestruct(&pThis); + if(pNew != NULL) + nsd_ptcpDestruct(&pNew); /* the close may be redundant, but that doesn't hurt... */ - if(iNewSock >= 0) - close(iNewSock); + sockClose(&iNewSock); } RETiRet; } -/* initialize the tcp socket for a listner - * pLstnPort must point to a port name or number. NULL is NOT permitted - * (hint: we need to be careful when we use this module together with librelp, - * there NULL indicates the default port - * default is used. - * gerhards, 2008-03-17 +/* initialize tcp sockets for a listner. This function returns an array of nds_t + * objects. The size of this array is returend in pLstnArrSize. + * pLstnPort must point to a port name or number. NULL is NOT permitted. pLstnIP + * points to the port to listen to (NULL means "all"), iMaxSess has the maximum + * number of sessions permitted. + * rgerhards, 2008-04-22 */ static rsRetVal -LstnInit(nsd_t *pNsd, uchar *pLstnPort) +LstnInit(nsd_t ***parrLstnNsd, int *pLstnArrSize, uchar *pLstnPort, uchar *pLstnIP, int iSessMax) { - nsd_ptcp_t *pThis = (nsd_ptcp_t*) pNsd; - struct addrinfo hints, *res, *r; - int error, maxs, *s, on = 1; + DEFiRet; + struct addrinfo hints, *res = NULL, *r; + nsd_ptcp_t **arrLstn = NULL; + int error, maxs, on = 1; + int sock; int sockflags; - DEFiRet; - ISOBJ_TYPE_assert(pThis, nsd_ptcp); + assert(parrLstnNsd != NULL); assert(pLstnPort != NULL); + assert(iSessMax >= 0); dbgprintf("creating tcp listen socket on port %s\n", pLstnPort); @@ -271,7 +271,7 @@ LstnInit(nsd_t *pNsd, uchar *pLstnPort) hints.ai_family = glbl.GetDefPFFamily(); hints.ai_socktype = SOCK_STREAM; - error = getaddrinfo(NULL, (char*) pLstnPort, &hints, &res); + error = getaddrinfo((char*)pLstnIP, (char*) pLstnPort, &hints, &res); if(error) { dbgprintf("error %d querying port '%s'\n", error, pLstnPort); ABORT_FINALIZE(RS_RET_INVALID_PORT); @@ -280,20 +280,14 @@ LstnInit(nsd_t *pNsd, uchar *pLstnPort) /* Count max number of sockets we may open */ for(maxs = 0, r = res; r != NULL ; r = r->ai_next, maxs++) /* EMPTY */; - pThis->socks = malloc((maxs+1) * sizeof(int)); - if (pThis->socks == NULL) { - dbgprintf("couldn't allocate memory for TCP listen sockets, suspending RELP message reception."); - freeaddrinfo(res); - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - } + CHKmalloc(arrLstn = (nsd_ptcp_t**) malloc((maxs+1) * sizeof(nsd_ptcp_t*))); - *pThis->socks = 0; /* num of sockets counter at start of array */ - s = pThis->socks + 1; + *pLstnArrSize = 0; /* num of sockets counter at start of array */ for(r = res; r != NULL ; r = r->ai_next) { - *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); - if (*s < 0) { + sock = socket(r->ai_family, r->ai_socktype, r->ai_protocol); + if(sock < 0) { if(!(r->ai_family == PF_INET6 && errno == EAFNOSUPPORT)) - dbgprintf("creating tcp listen socket"); + dbgprintf("error %d creating tcp listen socket", errno); /* it is debatable if PF_INET with EAFNOSUPPORT should * also be ignored... */ @@ -301,35 +295,32 @@ LstnInit(nsd_t *pNsd, uchar *pLstnPort) } #ifdef IPV6_V6ONLY - if (r->ai_family == AF_INET6) { + if(r->ai_family == AF_INET6) { int iOn = 1; - if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, + if(setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&iOn, sizeof (iOn)) < 0) { - close(*s); - *s = -1; - continue; + close(sock); + continue; } } #endif - if(setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0 ) { + if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0 ) { dbgprintf("error %d setting tcp socket option\n", errno); - close(*s); - *s = -1; + close(sock); continue; } /* We use non-blocking IO! */ - if((sockflags = fcntl(*s, F_GETFL)) != -1) { + if((sockflags = fcntl(sock, F_GETFL)) != -1) { sockflags |= O_NONBLOCK; /* SETFL could fail too, so get it caught by the subsequent * error check. */ - sockflags = fcntl(*s, F_SETFL, sockflags); + sockflags = fcntl(sock, F_SETFL, sockflags); } if(sockflags == -1) { dbgprintf("error %d setting fcntl(O_NONBLOCK) on tcp socket", errno); - close(*s); - *s = -1; + close(sock); continue; } @@ -340,62 +331,75 @@ LstnInit(nsd_t *pNsd, uchar *pLstnPort) */ #ifndef BSD if(net.should_use_so_bsdcompat()) { - if (setsockopt(*s, SOL_SOCKET, SO_BSDCOMPAT, + if (setsockopt(sock, SOL_SOCKET, SO_BSDCOMPAT, (char *) &on, sizeof(on)) < 0) { errmsg.LogError(NO_ERRCODE, "TCP setsockopt(BSDCOMPAT)"); - close(*s); - *s = -1; + close(sock); continue; } } #endif - if( (bind(*s, r->ai_addr, r->ai_addrlen) < 0) + if( (bind(sock, r->ai_addr, r->ai_addrlen) < 0) #ifndef IPV6_V6ONLY && (errno != EADDRINUSE) #endif ) { + /* TODO: check if *we* bound the socket - else we *have* an error! */ dbgprintf("error %d while binding tcp socket", errno); - close(*s); - *s = -1; + close(sock); continue; } - if(listen(*s,pThis->iSessMax / 10 + 5) < 0) { + if(listen(sock, iSessMax / 10 + 5) < 0) { /* If the listen fails, it most probably fails because we ask * for a too-large backlog. So in this case we first set back * to a fixed, reasonable, limit that should work. Only if * that fails, too, we give up. */ dbgprintf("listen with a backlog of %d failed - retrying with default of 32.", - pThis->iSessMax / 10 + 5); - if(listen(*s, 32) < 0) { + iSessMax / 10 + 5); + if(listen(sock, 32) < 0) { dbgprintf("tcp listen error %d, suspending\n", errno); - close(*s); - *s = -1; + close(sock); continue; } } - (*pThis->socks)++; - s++; + /* if we reach this point, we were able to obtain a valid socket, which we + * now can save to the array of listen sockets. -- rgerhards, 2008-04-22 + */ + CHKiRet(nsd_ptcpConstruct(arrLstn+*pLstnArrSize)); + arrLstn[*pLstnArrSize]->sock = sock; + ++(*pLstnArrSize); } if(res != NULL) freeaddrinfo(res); - if(*pThis->socks != maxs) - dbgprintf("We could initialize %d RELP TCP listen sockets out of %d we received " - "- this may or may not be an error indication.\n", *pThis->socks, maxs); + if(*pLstnArrSize != maxs) + dbgprintf("We could initialize %d TCP listen sockets out of %d we received " + "- this may or may not be an error indication.\n", *pLstnArrSize, maxs); - if(*pThis->socks == 0) { - dbgprintf("No RELP TCP listen socket could successfully be initialized, " - "message reception via RELP disabled.\n"); - free(pThis->socks); + if(*pLstnArrSize == 0) { + dbgprintf("No TCP listen sockets could successfully be initialized, " + "message reception disabled.\n"); ABORT_FINALIZE(RS_RET_COULD_NOT_BIND); } + *parrLstnNsd = (nsd_t**) arrLstn; + arrLstn = NULL; /* prevent from being freed in error handler */ + finalize_it: + if(iRet != RS_RET_OK) { + if(res != NULL) + freeaddrinfo(res); + if(arrLstn != NULL) { + for(maxs = 0 ; maxs < *pLstnArrSize ; ++maxs) + nsd_ptcpDestruct(arrLstn+*pLstnArrSize); + } + } + RETiRet; } @@ -494,10 +498,7 @@ finalize_it: freeaddrinfo(res); if(iRet != RS_RET_OK) { - if(pThis->sock != -1) { - close(pThis->sock); - pThis->sock = -1; - } + sockClose(&pThis->sock); } RETiRet; @@ -519,10 +520,10 @@ CODESTARTobjQueryInterface(nsd_ptcp) pIf->Construct = (rsRetVal(*)(nsd_t**)) nsd_ptcpConstruct; pIf->Destruct = (rsRetVal(*)(nsd_t**)) nsd_ptcpDestruct; pIf->Abort = Abort; - pIf->LstnInit = LstnInit; - pIf->AcceptConnReq = AcceptConnReq; pIf->Rcv = Rcv; pIf->Send = Send; + pIf->LstnInit = LstnInit; + pIf->AcceptConnReq = AcceptConnReq; pIf->Connect = Connect; finalize_it: ENDobjQueryInterface(nsd_ptcp) diff --git a/runtime/nsd_ptcp.h b/runtime/nsd_ptcp.h index d4848314..ac11d528 100644 --- a/runtime/nsd_ptcp.h +++ b/runtime/nsd_ptcp.h @@ -33,8 +33,6 @@ struct nsd_ptcp_s { uchar *pRemHostIP; /**< IP address of remote peer (currently used in server mode, only) */ uchar *pRemHostName; /**< host name of remote peer (currently used in server mode, only) */ int sock; /**< the socket we use for regular, single-socket, operations */ - int *socks; /**< the socket(s) we use for listeners, element 0 has nbr of socks */ - int iSessMax; /**< maximum number of sessions permitted */ }; /* interface is defined in nsd.h, we just implement it! */ diff --git a/runtime/nsdsel_ptcp.c b/runtime/nsdsel_ptcp.c new file mode 100644 index 00000000..67f3c62a --- /dev/null +++ b/runtime/nsdsel_ptcp.c @@ -0,0 +1,216 @@ +/* nsdsel_ptcp.c + * + * An implementation of the nsd select() interface for plain tcp sockets. + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#include "config.h" + +#include "rsyslog.h" +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <assert.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <ctype.h> +#include <netdb.h> +#include <fnmatch.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/select.h> + +#include "rsyslog.h" +#include "module-template.h" +#include "obj.h" +#include "errmsg.h" +#include "nsd_ptcp.h" +#include "nsdsel_ptcp.h" + +MODULE_TYPE_LIB + +/* static data */ +DEFobjStaticHelpers +DEFobjCurrIf(errmsg) +DEFobjCurrIf(glbl) + + +/* Standard-Constructor + */ +BEGINobjConstruct(nsdsel_ptcp) /* be sure to specify the object type also in END macro! */ + pThis->maxfds = 0; + FD_ZERO(&pThis->readfds); + FD_ZERO(&pThis->writefds); +ENDobjConstruct(nsdsel_ptcp) + + +/* destructor for the nsdsel_ptcp object */ +BEGINobjDestruct(nsdsel_ptcp) /* be sure to specify the object type also in END and CODESTART macros! */ +CODESTARTobjDestruct(nsdsel_ptcp) +ENDobjDestruct(nsdsel_ptcp) + + +/* Add a socket to the select set */ +static rsRetVal +Add(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp) +{ + DEFiRet; + nsdsel_ptcp_t *pThis = (nsdsel_ptcp_t*) pNsdsel; + nsd_ptcp_t *pSock = (nsd_ptcp_t*) pNsd; + + ISOBJ_TYPE_assert(pSock, nsd_ptcp); + ISOBJ_TYPE_assert(pThis, nsdsel_ptcp); + + switch(waitOp) { + case NSDSEL_RD: + FD_SET(pSock->sock, &pThis->readfds); + break; + case NSDSEL_WR: + FD_SET(pSock->sock, &pThis->writefds); + break; + case NSDSEL_RDWR: + FD_SET(pSock->sock, &pThis->readfds); + FD_SET(pSock->sock, &pThis->writefds); + break; + } + + if(pSock->sock > pThis->maxfds) + pThis->maxfds = pSock->sock; + + RETiRet; +} + + +/* perform the select() piNumReady returns how many descriptors are ready for IO + * TODO: add timeout! + */ +static rsRetVal +Select(nsdsel_t *pNsdsel, int *piNumReady) +{ + DEFiRet; + int i; + nsdsel_ptcp_t *pThis = (nsdsel_ptcp_t*) pNsdsel; + + ISOBJ_TYPE_assert(pThis, nsdsel_ptcp); + assert(piNumReady != NULL); + + if(Debug) { // TODO: debug setting! + // TODO: name in dbgprintf! + dbgprintf("--------<NSDSEL_PTCP> calling select, active fds (max %d): ", pThis->maxfds); + for(i = 0; i <= pThis->maxfds; ++i) + if(FD_ISSET(i, &pThis->readfds) || FD_ISSET(i, &pThis->writefds)) + dbgprintf("%d ", i); + dbgprintf("\n"); + } + + /* now do the select */ + *piNumReady = select(pThis->maxfds+1, &pThis->readfds, &pThis->writefds, NULL, NULL); + + RETiRet; +} + + +/* check if a socket is ready for IO */ +static rsRetVal +IsReady(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp) +{ + DEFiRet; + nsdsel_ptcp_t *pThis = (nsdsel_ptcp_t*) pNsdsel; + nsd_ptcp_t *pSock = (nsd_ptcp_t*) pNsd; + + ISOBJ_TYPE_assert(pThis, nsdsel_ptcp); + ISOBJ_TYPE_assert(pSock, nsd_ptcp); + RETiRet; +} + + +/* ------------------------------ end support for the select() interface ------------------------------ */ + + +/* queryInterface function */ +BEGINobjQueryInterface(nsdsel_ptcp) +CODESTARTobjQueryInterface(nsdsel_ptcp) + if(pIf->ifVersion != nsdCURR_IF_VERSION) {/* check for current version, increment on each change */ + ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); + } + + /* ok, we have the right interface, so let's fill it + * Please note that we may also do some backwards-compatibility + * work here (if we can support an older interface version - that, + * of course, also affects the "if" above). + */ + pIf->Construct = (rsRetVal(*)(nsdsel_t**)) nsdsel_ptcpConstruct; + pIf->Destruct = (rsRetVal(*)(nsdsel_t**)) nsdsel_ptcpDestruct; + pIf->Add = Add; + pIf->Select = Select; + pIf->IsReady = IsReady; +finalize_it: +ENDobjQueryInterface(nsdsel_ptcp) + + +/* exit our class + */ +BEGINObjClassExit(nsdsel_ptcp, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */ +CODESTARTObjClassExit(nsdsel_ptcp) + /* release objects we no longer need */ + objRelease(glbl, CORE_COMPONENT); + objRelease(errmsg, CORE_COMPONENT); +ENDObjClassExit(nsdsel_ptcp) + + +/* Initialize the nsdsel_ptcp class. Must be called as the very first method + * before anything else is called inside this class. + * rgerhards, 2008-02-19 + */ +BEGINObjClassInit(nsdsel_ptcp, 1, OBJ_IS_LOADABLE_MODULE) /* class, version */ + /* request objects we use */ + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); + + /* set our own handlers */ +ENDObjClassInit(nsdsel_ptcp) + + +/* --------------- here now comes the plumbing that makes as a library module --------------- */ + + +BEGINmodExit +CODESTARTmodExit + nsdsel_ptcpClassExit(); +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_LIB_QUERIES +ENDqueryEtryPt + + +BEGINmodInit() +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ + + /* Initialize all classes that are in our module - this includes ourselfs */ + CHKiRet(nsdsel_ptcpClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */ +ENDmodInit +/* vi:set ai: + */ diff --git a/runtime/nsdsel_ptcp.h b/runtime/nsdsel_ptcp.h new file mode 100644 index 00000000..39294d7d --- /dev/null +++ b/runtime/nsdsel_ptcp.h @@ -0,0 +1,46 @@ +/* An implementation of the nsd select interface for plain tcp sockets. + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ + +#ifndef INCLUDED_NSDSEL_PTCP_H +#define INCLUDED_NSDSEL_PTCP_H + +#include "nsd.h" +typedef nsdsel_if_t nsdsel_ptcp_if_t; /* we just *implement* this interface */ + +/* the nsdsel_ptcp object */ +struct nsdsel_ptcp_s { + BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ + int maxfds; + fd_set readfds; + fd_set writefds; +}; + +/* interface is defined in nsd.h, we just implement it! */ + +/* prototypes */ +PROTOTYPEObj(nsdsel_ptcp); + +/* the name of our library binary */ +#define LM_NSDSEL_PTCP_FILENAME "lmnsdsel_ptcp" + +#endif /* #ifndef INCLUDED_NSDSEL_PTCP_H */ diff --git a/runtime/nssel.c b/runtime/nssel.c new file mode 100644 index 00000000..f2844872 --- /dev/null +++ b/runtime/nssel.c @@ -0,0 +1,222 @@ +/* nssel.c + * + * The io waiter is a helper object enabling us to wait on a set of streams to become + * ready for IO - this is modelled after select(). We need this, because + * stream drivers may have different concepts. Consequently, + * the structure must contain nsd_t's from the same stream driver type + * only. This is implemented as a singly-linked list where every + * new element is added at the top of the list. + * + * Work on this module begun 2008-04-22 by Rainer Gerhards. + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#include "config.h" + +#include "rsyslog.h" +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <errno.h> +#include <string.h> + +#include "rsyslog.h" +#include "obj.h" +#include "module-template.h" +#include "nssel.h" + +MODULE_TYPE_LIB + +/* static data */ +DEFobjStaticHelpers +DEFobjCurrIf(glbl) + + +/* load our low-level driver. This must be done before any + * driver-specific functions (allmost all...) can be carried + * out. Note that the driver's .ifIsLoaded is correctly + * initialized by calloc() and we depend on that. + * rgerhards, 2008-04-18 + */ +static rsRetVal +loadDrvr(nssel_t *pThis) +{ + uchar *pDrvrName; + DEFiRet; + + pDrvrName = pThis->pDrvrName; + if(pDrvrName == NULL) /* if no drvr name is set, use system default */ + pDrvrName = glbl.GetDfltNetstrmDrvr(); + + pThis->Drvr.ifVersion = nsdCURR_IF_VERSION; + /* The pDrvrName+2 below is a hack to obtain the object name. It + * safes us to have yet another variable with the name without "lm" in + * front of it. If we change the module load interface, we may re-think + * about this hack, but for the time being it is efficient and clean + * enough. -- rgerhards, 2008-04-18 + */ + CHKiRet(obj.UseObj(__FILE__, pDrvrName+2, pDrvrName, (void*) &pThis->Drvr)); +finalize_it: + RETiRet; +} + + +/* Standard-Constructor */ +BEGINobjConstruct(nssel) /* be sure to specify the object type also in END macro! */ +ENDobjConstruct(nssel) + + +/* destructor for the nssel object */ +BEGINobjDestruct(nssel) /* be sure to specify the object type also in END and CODESTART macros! */ +CODESTARTobjDestruct(nssel) +ENDobjDestruct(nssel) + + +/* ConstructionFinalizer */ +static rsRetVal +ConstructFinalize(nssel_t *pThis) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, nssel); + CHKiRet(loadDrvr(pThis)); + CHKiRet(pThis->Drvr.Construct(&pThis->pDrvrData)); +finalize_it: + RETiRet; +} + + +/* Add a stream object to the current IOW. Note that a single stream may + * have multiple "sockets" if it is a listener. If so, all of them are + * begin added. + */ +static rsRetVal +Add(nssel_t *pThis, netstrm_t *pStrm) +{ + DEFiRet; + + ISOBJ_TYPE_assert(pThis, nssel); + ISOBJ_TYPE_assert(pStrm, netstrm); + + +finalize_it: + RETiRet; +} + + +/* wait for IO to happen on one of our netstreams. iNumReady has + * the number of ready "sockets" after the call. This function blocks + * until some are ready. EAGAIN is retried. + */ +static rsRetVal +Wait(nssel_t *pThis, int *piNumReady) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, nssel); + assert(piNumReady != NULL); + RETiRet; +} + + +/* Check if a stream is ready for IO. *piNumReady contains the remaining number + * of ready streams. Note that this function may say the stream is not ready + * but still decrement *piNumReady. This can happen when (e.g. with TLS) the low + * level driver requires some IO which is hidden from the upper layer point of view. + * rgerhards, 2008-04-23 + */ +static rsRetVal +IsReady(nssel_t *pThis, netstrm_t *pStrm, int *pbIsReady, int *piNumReady) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, nssel); + ISOBJ_TYPE_assert(pStrm, netstrm); + assert(pbIsReady != NULL); + assert(piNumReady != NULL); + RETiRet; +} + + +/* queryInterface function */ +BEGINobjQueryInterface(nssel) +CODESTARTobjQueryInterface(nssel) + if(pIf->ifVersion != nsselCURR_IF_VERSION) {/* check for current version, increment on each change */ + ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); + } + + /* ok, we have the right interface, so let's fill it + * Please note that we may also do some backwards-compatibility + * work here (if we can support an older interface version - that, + * of course, also affects the "if" above). + */ + pIf->Construct = nsselConstruct; + pIf->ConstructFinalize = ConstructFinalize; + pIf->Destruct = nsselDestruct; + pIf->Add = Add; + pIf->Wait = Wait; + pIf->IsReady = IsReady; +finalize_it: +ENDobjQueryInterface(nssel) + + +/* exit our class + */ +BEGINObjClassExit(nssel, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */ +CODESTARTObjClassExit(nssel) + /* release objects we no longer need */ + objRelease(glbl, CORE_COMPONENT); +ENDObjClassExit(nssel) + + +/* Initialize the nssel class. Must be called as the very first method + * before anything else is called inside this class. + * rgerhards, 2008-02-19 + */ +BEGINObjClassInit(nssel, 1, OBJ_IS_CORE_MODULE) /* class, version */ + /* request objects we use */ + CHKiRet(objUse(glbl, CORE_COMPONENT)); + + /* set our own handlers */ +ENDObjClassInit(nssel) + + +/* --------------- here now comes the plumbing that makes us a library module --------------- */ + + +BEGINmodExit +CODESTARTmodExit + nsselClassExit(); +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_LIB_QUERIES +ENDqueryEtryPt + + +BEGINmodInit() +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ + + /* Initialize all classes that are in our module - this includes ourselfs */ + CHKiRet(nsselClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */ +ENDmodInit +/* vi:set ai: + */ diff --git a/runtime/nssel.h b/runtime/nssel.h new file mode 100644 index 00000000..16919b1f --- /dev/null +++ b/runtime/nssel.h @@ -0,0 +1,55 @@ +/* Definitions for the nssel IO waiter. + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ + +#ifndef INCLUDED_NSSEL_H +#define INCLUDED_NSSEL_H + +#include "nsd.h" + +/* the nssel object */ +struct nssel_s { + BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ + nsd_t *pDrvrData; /**< the driver's data elements */ + uchar *pDrvrName; /**< nsd driver name to use, or NULL if system default */ + nsdsel_if_t Drvr; /**< our stream driver */ +}; + + +/* interface */ +BEGINinterface(nssel) /* name must also be changed in ENDinterface macro! */ + rsRetVal (*Construct)(nssel_t **ppThis); + rsRetVal (*ConstructFinalize)(nssel_t *pThis); + rsRetVal (*Destruct)(nssel_t **ppThis); + rsRetVal (*Add)(nssel_t *pThis, netstrm_t *pStrm); + rsRetVal (*Wait)(nssel_t *pThis, int *pNumReady); + rsRetVal (*IsReady)(nssel_t *pThis, netstrm_t *pStrm, int *pbIsReady, int *pNumRead); +ENDinterface(nssel) +#define nsselCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ + +/* prototypes */ +PROTOTYPEObj(nssel); + +/* the name of our library binary */ +#define LM_NSSEL_FILENAME "lmnssel" + +#endif /* #ifndef INCLUDED_NSSEL_H */ diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index f7824006..4f62ca3c 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -65,8 +65,12 @@ typedef struct obj_s obj_t; typedef struct filed selector_t;/* TODO: this so far resides in syslogd.c, think about modularization */ typedef struct NetAddr netAddr_t; typedef struct netstrm_s netstrm_t; +typedef struct nssel_s nssel_t; +typedef enum nsdsel_waitOp_e nsdsel_waitOp_t; typedef struct nsd_ptcp_s nsd_ptcp_t; +typedef struct nsdsel_ptcp_s nsdsel_ptcp_t; typedef obj_t nsd_t; +typedef obj_t nsdsel_t; typedef struct msg msg_t; typedef struct interface_s interface_t; typedef struct objInfo_s objInfo_t; @@ -206,6 +210,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_INVALID_HNAME = -2075, /**< remote peer's hostname invalid or unobtainable */ RS_RET_INVALID_PORT = -2076, /**< invalid port value */ RS_RET_COULD_NOT_BIND = -2077, /**< could not bind socket, defunct */ + RS_RET_MAX_SESS_REACHED = -2078, /**< max nbr of sessions reached, can not create more */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ |