wtiCancelThrd(wti_t *pThis, const uchar *const cancelobj) { DEFiRet; ISOBJ_TYPE_assert(pThis, wti); if(wtiGetState(pThis)) { LogMsg(0, RS_RET_ERR, LOG_WARNING, "%s: need to do cooperative cancellation " "- some data may be lost, increase timeout?", cancelobj); /* we first try the cooperative "cancel" interface */ pthread_kill(pThis->thrdID, SIGTTIN); DBGPRINTF("sent SIGTTIN to worker thread %p, giving it a chance to terminate\n", (void *) pThis->thrdID); srSleep(0, 10000); } if(wtiGetState(pThis)) { LogMsg(0, RS_RET_ERR, LOG_WARNING, "%s: need to do hard cancellation", cancelobj); DBGPRINTF("cooperative worker termination failed, using cancellation...\n"); DBGOPRINT((obj_t*) pThis, "canceling worker thread\n"); pthread_cancel(pThis->thrdID); /* now wait until the thread terminates... */ while(wtiGetState(pThis)) { srSleep(0, 10000); } } RETiRet; }
/* Cancel the thread. If the thread is not running. But it is save and legal to * call wtiCancelThrd() in such situations. This function only returns when the * thread has terminated. Else we may get race conditions all over the code... * Note that when waiting for the thread to terminate, we do a busy wait, checking * progress every 10ms. It is very unlikely that we will ever cancel a thread * and, if so, it will only happen at the end of the rsyslog run. So doing this * kind of non-optimal wait is considered preferable over using condition variables. * rgerhards, 2008-02-26 */ rsRetVal wtiCancelThrd(wti_t *pThis) { DEFiRet; ISOBJ_TYPE_assert(pThis, wti); if(wtiGetState(pThis)) { /* we first try the cooperative "cancel" interface */ pthread_kill(pThis->thrdID, SIGTTIN); DBGPRINTF("sent SIGTTIN to worker thread 0x%x, giving it a chance to terminate\n", (unsigned) pThis->thrdID); srSleep(0, 10000); } if(wtiGetState(pThis)) { DBGPRINTF("cooperative worker termination failed, using cancellation...\n"); DBGOPRINT((obj_t*) pThis, "canceling worker thread\n"); pthread_cancel(pThis->thrdID); /* now wait until the thread terminates... */ while(wtiGetState(pThis)) { srSleep(0, 10000); } } RETiRet; }
/* do the physical open() call on a file. */ static rsRetVal doPhysOpen(strm_t *pThis) { int iFlags = 0; DEFiRet; ISOBJ_TYPE_assert(pThis, strm); /* compute which flags we need to provide to open */ switch(pThis->tOperationsMode) { case STREAMMODE_READ: iFlags = O_CLOEXEC | O_NOCTTY | O_RDONLY; break; case STREAMMODE_WRITE: /* legacy mode used inside queue engine */ iFlags = O_CLOEXEC | O_NOCTTY | O_WRONLY | O_CREAT; break; case STREAMMODE_WRITE_TRUNC: iFlags = O_CLOEXEC | O_NOCTTY | O_WRONLY | O_CREAT | O_TRUNC; break; case STREAMMODE_WRITE_APPEND: iFlags = O_CLOEXEC | O_NOCTTY | O_WRONLY | O_CREAT | O_APPEND; break; default:assert(0); break; } if(pThis->sType == STREAMTYPE_NAMED_PIPE) { DBGPRINTF("Note: stream '%s' is a named pipe, open with O_NONBLOCK\n", pThis->pszCurrFName); iFlags |= O_NONBLOCK; } pThis->fd = open((char*)pThis->pszCurrFName, iFlags, pThis->tOpenMode); DBGPRINTF("file '%s' opened as #%d with mode %d\n", pThis->pszCurrFName, pThis->fd, pThis->tOpenMode); if(pThis->fd == -1) { char errStr[1024]; int err = errno; rs_strerror_r(err, errStr, sizeof(errStr)); DBGOPRINT((obj_t*) pThis, "open error %d, file '%s': %s\n", errno, pThis->pszCurrFName, errStr); if(err == ENOENT) ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND); else ABORT_FINALIZE(RS_RET_IO_ERROR); } else { if(!ustrcmp(pThis->pszCurrFName, UCHAR_CONSTANT(_PATH_CONSOLE)) || isatty(pThis->fd)) { DBGPRINTF("file %d is a tty-type file\n", pThis->fd); pThis->bIsTTY = 1; } else { pThis->bIsTTY = 0; } } finalize_it: RETiRet; }
/* open a strm file * It is OK to call this function when the stream is already open. In that * case, it returns immediately with RS_RET_OK */ static rsRetVal strmOpenFile(strm_t *pThis) { DEFiRet; ASSERT(pThis != NULL); if(pThis->fd != -1) ABORT_FINALIZE(RS_RET_OK); if(pThis->pszFName == NULL) ABORT_FINALIZE(RS_RET_FILE_PREFIX_MISSING); if(pThis->sType == STREAMTYPE_FILE_CIRCULAR) { CHKiRet(genFileName(&pThis->pszCurrFName, pThis->pszDir, pThis->lenDir, pThis->pszFName, pThis->lenFName, pThis->iCurrFNum, pThis->iFileNumDigits)); } else { if(pThis->pszDir == NULL) { if((pThis->pszCurrFName = (uchar*) strdup((char*) pThis->pszFName)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); } else { CHKiRet(genFileName(&pThis->pszCurrFName, pThis->pszDir, pThis->lenDir, pThis->pszFName, pThis->lenFName, -1, 0)); } } CHKiRet(doPhysOpen(pThis)); pThis->iCurrOffs = 0; if(pThis->tOperationsMode == STREAMMODE_WRITE_APPEND) { /* we need to obtain the current offset */ off_t offset; CHKiRet(getFileSize(pThis->pszCurrFName, &offset)); pThis->iCurrOffs = offset; } DBGOPRINT((obj_t*) pThis, "opened file '%s' for %s as %d\n", pThis->pszCurrFName, (pThis->tOperationsMode == STREAMMODE_READ) ? "READ" : "WRITE", pThis->fd); finalize_it: RETiRet; }
/* wait for queue to become non-empty or timeout * helper to wtiWorker. Note the the predicate is * re-tested by the caller, so it is OK to NOT do it here. * rgerhards, 2009-05-20 */ static void doIdleProcessing(wti_t *pThis, wtp_t *pWtp, int *pbInactivityTOOccured) { struct timespec t; BEGINfunc DBGPRINTF("%s: worker IDLE, waiting for work.\n", wtiGetDbgHdr(pThis)); if(pThis->bAlwaysRunning) { /* never shut down any started worker */ d_pthread_cond_wait(&pThis->pcondBusy, pWtp->pmutUsr); } else { timeoutComp(&t, pWtp->toWrkShutdown);/* get absolute timeout */ if(d_pthread_cond_timedwait(&pThis->pcondBusy, pWtp->pmutUsr, &t) != 0) { DBGPRINTF("%s: inactivity timeout, worker terminating...\n", wtiGetDbgHdr(pThis)); *pbInactivityTOOccured = 1; /* indicate we had a timeout */ } } DBGOPRINT((obj_t*) pThis, "worker awoke from idle processing\n"); ENDfunc }
rsRetVal wtiWorker(wti_t *pThis) { wtp_t *pWtp; /* our worker thread pool */ int bInactivityTOOccured = 0; rsRetVal localRet; rsRetVal terminateRet; int iCancelStateSave; DEFiRet; ISOBJ_TYPE_assert(pThis, wti); pWtp = pThis->pWtp; /* shortcut */ ISOBJ_TYPE_assert(pWtp, wtp); dbgSetThrdName(pThis->pszDbgHdr); pthread_cleanup_push(wtiWorkerCancelCleanup, pThis); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave); /* now we have our identity, on to real processing */ while(1) { /* loop will be broken below - need to do mutex locks */ if(pWtp->pfRateLimiter != NULL) { /* call rate-limiter, if defined */ pWtp->pfRateLimiter(pWtp->pUsr); } d_pthread_mutex_lock(pWtp->pmutUsr); /* first check if we are in shutdown process (but evaluate a bit later) */ terminateRet = wtpChkStopWrkr(pWtp, MUTEX_ALREADY_LOCKED); if(terminateRet == RS_RET_TERMINATE_NOW) { /* we now need to free the old batch */ localRet = pWtp->pfObjProcessed(pWtp->pUsr, pThis); DBGOPRINT((obj_t*) pThis, "terminating worker because of TERMINATE_NOW mode, del iRet %d\n", localRet); d_pthread_mutex_unlock(pWtp->pmutUsr); break; } /* try to execute and process whatever we have */ /* Note that this function releases and re-aquires the mutex. The returned * information on idle state must be processed before releasing the mutex again. */ localRet = pWtp->pfDoWork(pWtp->pUsr, pThis); if(localRet == RS_RET_ERR_QUEUE_EMERGENCY) { d_pthread_mutex_unlock(pWtp->pmutUsr); break; /* end of loop */ } else if(localRet == RS_RET_IDLE) { if(terminateRet == RS_RET_TERMINATE_WHEN_IDLE || bInactivityTOOccured) { d_pthread_mutex_unlock(pWtp->pmutUsr); DBGOPRINT((obj_t*) pThis, "terminating worker terminateRet=%d, bInactivityTOOccured=%d\n", terminateRet, bInactivityTOOccured); break; /* end of loop */ } doIdleProcessing(pThis, pWtp, &bInactivityTOOccured); d_pthread_mutex_unlock(pWtp->pmutUsr); continue; /* request next iteration */ } d_pthread_mutex_unlock(pWtp->pmutUsr); bInactivityTOOccured = 0; /* reset for next run */ } /* indicate termination */ pthread_cleanup_pop(0); /* remove cleanup handler */ pthread_setcancelstate(iCancelStateSave, NULL); RETiRet; }