/* nsdsel_ptcp.c * * An implementation of the nsd select() interface for plain tcp sockets. * * Copyright 2008-2018 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 <stdlib.h> #include <assert.h> #include <errno.h> #include <string.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" #include "unlimited_select.h" #define FDSET_INCREMENT 1024 /* increment for struct pollfds array allocation */ /* static data */ DEFobjStaticHelpers DEFobjCurrIf(glbl) /* Standard-Constructor */ BEGINobjConstruct(nsdsel_ptcp) /* be sure to specify the object type also in END macro! */ pThis->currfds = 0; pThis->maxfds = FDSET_INCREMENT; CHKmalloc(pThis->fds = calloc(FDSET_INCREMENT, sizeof(struct pollfd))); finalize_it: 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) free(pThis->fds); ENDobjDestruct(nsdsel_ptcp) /* Add a socket to the select set */ static rsRetVal ATTR_NONNULL() Add(nsdsel_t *const pNsdsel, nsd_t *const pNsd, const nsdsel_waitOp_t waitOp) { DEFiRet; nsdsel_ptcp_t *const pThis = (nsdsel_ptcp_t*) pNsdsel; const nsd_ptcp_t *const pSock = (nsd_ptcp_t*) pNsd; ISOBJ_TYPE_assert(pSock, nsd_ptcp); ISOBJ_TYPE_assert(pThis, nsdsel_ptcp); if(pThis->currfds == pThis->maxfds) { struct pollfd *newfds; CHKmalloc(newfds = realloc(pThis->fds, sizeof(struct pollfd) * (pThis->maxfds + FDSET_INCREMENT))); pThis->maxfds += FDSET_INCREMENT; pThis->fds = newfds; } switch(waitOp) { case NSDSEL_RD: pThis->fds[pThis->currfds].events = POLLIN; break; case NSDSEL_WR: pThis->fds[pThis->currfds].events = POLLOUT; break; case NSDSEL_RDWR: pThis->fds[pThis->currfds].events = POLLIN | POLLOUT; break; } pThis->fds[pThis->currfds].fd = pSock->sock; ++pThis->currfds; finalize_it: RETiRet; } /* perform the select() piNumReady returns how many descriptors are ready for IO * TODO: add timeout! */ static rsRetVal ATTR_NONNULL() Select(nsdsel_t *const pNsdsel, int *const piNumReady) { DEFiRet; nsdsel_ptcp_t *pThis = (nsdsel_ptcp_t*) pNsdsel; ISOBJ_TYPE_assert(pThis, nsdsel_ptcp); assert(piNumReady != NULL); /* Output debug first*/ if(Debug) { dbgprintf("--------<NSDSEL_PTCP> calling poll, active fds (%d): ", pThis->currfds); for(uint32_t i = 0; i <= pThis->currfds; ++i) dbgprintf("%d ", pThis->fds[i].fd); dbgprintf("\n"); } assert(pThis->currfds >= 1); /* now do the select */ *piNumReady = poll(pThis->fds, pThis->currfds, -1); if(*piNumReady < 0) { if(errno == EINTR) { DBGPRINTF("nsdsel_ptcp received EINTR\n"); } else { LogMsg(errno, RS_RET_POLL_ERR, LOG_WARNING, "ndssel_ptcp: poll system call failed, may cause further troubles"); } *piNumReady = 0; } RETiRet; } /* check if a socket is ready for IO */ static rsRetVal ATTR_NONNULL() IsReady(nsdsel_t *const pNsdsel, nsd_t *const pNsd, const nsdsel_waitOp_t waitOp, int *const pbIsReady) { DEFiRet; const nsdsel_ptcp_t *const pThis = (nsdsel_ptcp_t*) pNsdsel; const nsd_ptcp_t *const pSock = (nsd_ptcp_t*) pNsd; ISOBJ_TYPE_assert(pThis, nsdsel_ptcp); ISOBJ_TYPE_assert(pSock, nsd_ptcp); const int sock = pSock->sock; // TODO: consider doing a binary search uint32_t idx; for(idx = 0 ; idx < pThis->currfds ; ++idx) { if(pThis->fds[idx].fd == sock) break; } if(idx >= pThis->currfds) { LogMsg(0, RS_RET_INTERNAL_ERROR, LOG_ERR, "ndssel_ptcp: could not find socket %d which should be present", sock); ABORT_FINALIZE(RS_RET_INTERNAL_ERROR); } const short revent = pThis->fds[idx].revents; if (revent & POLLNVAL) { DBGPRINTF("ndssel_ptcp: revent & POLLNVAL is TRUE, we had a race, ignoring, revent = %d", revent); *pbIsReady = 0; } switch(waitOp) { case NSDSEL_RD: *pbIsReady = revent & POLLIN; break; case NSDSEL_WR: *pbIsReady = revent & POLLOUT; break; case NSDSEL_RDWR: *pbIsReady = revent & (POLLIN | POLLOUT); break; } finalize_it: 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_CORE_MODULE) /* CHANGE class also in END MACRO! */ CODESTARTObjClassExit(nsdsel_ptcp) /* release objects we no longer need */ objRelease(glbl, 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_CORE_MODULE) /* class, version */ CHKiRet(objUse(glbl, CORE_COMPONENT)); ENDObjClassInit(nsdsel_ptcp)