mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-17 16:20:43 +01:00
This patch updates prepareBackground() in tools/rsyslogd.c to reopen any file descriptors used for random number generation in the child process. This fixes an issue on Linux systems where the file descriptor obtained for /dev/urandom by seedRandomNumber() in runtime/srutils.c was left closed after the fork. This could be observed in procfs, where /proc/fd/ would show no open descriptors to /dev/urandom in the forked process. /dev/urandom is reopened as the child may be be operating in a jail, and so should not continue to use file descriptors from outside the jail (i.e. inherited from the parent process). I found that this issue led to rsyslog intermittently hanging during seedIV() in runtime/libgcry.c. After the fork, the closed file descriptor number tended to get re-assigned. randomNumber() would then read from an incorrect (although still valid) file descriptor, and could block (depending on the state of that file descriptor). This gave rise to the intermittent hang that I observed. Signed-off-by: Simon Haggett <simon.haggett@gmail.com>
868 lines
24 KiB
C
868 lines
24 KiB
C
/**\file srUtils.c
|
|
* \brief General utilties that fit nowhere else.
|
|
*
|
|
* The namespace for this file is "srUtil".
|
|
*
|
|
* \author Rainer Gerhards <rgerhards@adiscon.com>
|
|
* \date 2003-09-09
|
|
* Coding begun.
|
|
*
|
|
* Copyright 2003-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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <signal.h>
|
|
#include <assert.h>
|
|
#include <sys/wait.h>
|
|
#include <ctype.h>
|
|
#include <inttypes.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "rsyslog.h"
|
|
#include "srUtils.h"
|
|
#include "obj.h"
|
|
#include "errmsg.h"
|
|
#include "glbl.h"
|
|
|
|
#if _POSIX_TIMERS <= 0
|
|
#include <sys/time.h>
|
|
#endif
|
|
|
|
/* here we host some syslog specific names. There currently is no better place
|
|
* to do it, but over here is also not ideal... -- rgerhards, 2008-02-14
|
|
* rgerhards, 2008-04-16: note in LGPL move: the code tables below exist in
|
|
* the same way in BSD, so it is not a problem to move them from GPLv3 to LGPL.
|
|
* And nobody modified them since it was under LGPL, so we can also move it
|
|
* to ASL 2.0.
|
|
*/
|
|
syslogName_t syslogPriNames[] = {
|
|
{"alert", LOG_ALERT},
|
|
{"crit", LOG_CRIT},
|
|
{"debug", LOG_DEBUG},
|
|
{"emerg", LOG_EMERG},
|
|
{"err", LOG_ERR},
|
|
{"error", LOG_ERR}, /* DEPRECATED */
|
|
{"info", LOG_INFO},
|
|
{"none", INTERNAL_NOPRI}, /* INTERNAL */
|
|
{"notice", LOG_NOTICE},
|
|
{"panic", LOG_EMERG}, /* DEPRECATED */
|
|
{"warn", LOG_WARNING}, /* DEPRECATED */
|
|
{"warning", LOG_WARNING},
|
|
{"*", TABLE_ALLPRI},
|
|
{NULL, -1}
|
|
};
|
|
|
|
#ifndef LOG_AUTHPRIV
|
|
# define LOG_AUTHPRIV LOG_AUTH
|
|
#endif
|
|
syslogName_t syslogFacNames[] = {
|
|
{"auth", LOG_AUTH},
|
|
{"authpriv", LOG_AUTHPRIV},
|
|
{"cron", LOG_CRON},
|
|
{"daemon", LOG_DAEMON},
|
|
{"kern", LOG_KERN},
|
|
{"lpr", LOG_LPR},
|
|
{"mail", LOG_MAIL},
|
|
{"mark", LOG_MARK}, /* INTERNAL */
|
|
{"news", LOG_NEWS},
|
|
{"ntp", (12<<3) }, /* NTP, perhaps BSD-specific? */
|
|
{"security", LOG_AUTH}, /* DEPRECATED */
|
|
{"bsd_security", (13<<3) }, /* BSD-specific, unfortunatly with duplicate name... */
|
|
{"syslog", LOG_SYSLOG},
|
|
{"user", LOG_USER},
|
|
{"uucp", LOG_UUCP},
|
|
#if defined(_AIX) /* AIXPORT : These are necessary for AIX */
|
|
{ "caa", LOG_CAA },
|
|
{ "aso", LOG_ASO },
|
|
#endif
|
|
#if defined(LOG_FTP)
|
|
{"ftp", LOG_FTP},
|
|
#endif
|
|
#if defined(LOG_AUDIT)
|
|
{"audit", LOG_AUDIT},
|
|
#endif
|
|
{"console", (14 << 3)}, /* BSD-specific priority */
|
|
{"local0", LOG_LOCAL0},
|
|
{"local1", LOG_LOCAL1},
|
|
{"local2", LOG_LOCAL2},
|
|
{"local3", LOG_LOCAL3},
|
|
{"local4", LOG_LOCAL4},
|
|
{"local5", LOG_LOCAL5},
|
|
{"local6", LOG_LOCAL6},
|
|
{"local7", LOG_LOCAL7},
|
|
{"invld", LOG_INVLD},
|
|
{NULL, -1},
|
|
};
|
|
|
|
/* ################################################################# *
|
|
* private members *
|
|
* ################################################################# */
|
|
|
|
/* As this is not a "real" object, there won't be any private
|
|
* members in this file.
|
|
*/
|
|
|
|
/* ################################################################# *
|
|
* public members *
|
|
* ################################################################# */
|
|
|
|
rsRetVal srUtilItoA(char *pBuf, int iLenBuf, number_t iToConv)
|
|
{
|
|
int i;
|
|
int bIsNegative;
|
|
char szBuf[64]; /* sufficiently large for my lifespan and those of my children... ;) */
|
|
|
|
assert(pBuf != NULL);
|
|
assert(iLenBuf > 1); /* This is actually an app error and as thus checked for... */
|
|
|
|
if(iToConv < 0)
|
|
{
|
|
bIsNegative = RSTRUE;
|
|
iToConv *= -1;
|
|
}
|
|
else
|
|
bIsNegative = RSFALSE;
|
|
|
|
/* first generate a string with the digits in the reverse direction */
|
|
i = 0;
|
|
do
|
|
{
|
|
szBuf[i++] = iToConv % 10 + '0';
|
|
iToConv /= 10;
|
|
} while(iToConv > 0); /* warning: do...while()! */
|
|
--i; /* undo last increment - we were pointing at NEXT location */
|
|
|
|
/* make sure we are within bounds... */
|
|
if(i + 2 > iLenBuf) /* +2 because: a) i starts at zero! b) the \0 byte */
|
|
return RS_RET_PROVIDED_BUFFER_TOO_SMALL;
|
|
|
|
/* then move it to the right direction... */
|
|
if(bIsNegative == RSTRUE)
|
|
*pBuf++ = '-';
|
|
while(i >= 0)
|
|
*pBuf++ = szBuf[i--];
|
|
*pBuf = '\0'; /* terminate it!!! */
|
|
|
|
return RS_RET_OK;
|
|
}
|
|
|
|
uchar *srUtilStrDup(uchar *pOld, size_t len)
|
|
{
|
|
uchar *pNew;
|
|
|
|
assert(pOld != NULL);
|
|
|
|
if((pNew = malloc(len + 1)) != NULL)
|
|
memcpy(pNew, pOld, len + 1);
|
|
|
|
return pNew;
|
|
}
|
|
|
|
|
|
/* creates a path recursively
|
|
* Return 0 on success, -1 otherwise. On failure, errno * hold the last OS error.
|
|
* Param "mode" holds the mode that all non-existing directories are to be
|
|
* created with.
|
|
* Note that we have a potential race inside that code, a race that even exists
|
|
* outside of the rsyslog process (if multiple instances run, or other programs
|
|
* generate directories): If the directory does not exist, a context switch happens,
|
|
* at that moment another process creates it, then our creation on the context
|
|
* switch back fails. This actually happened in practice, and depending on the
|
|
* configuration it is even likely to happen. We can not solve this situation
|
|
* with a mutex, as that works only within out process space. So the solution
|
|
* is that we take the optimistic approach, try the creation, and if it fails
|
|
* with "already exists" we go back and do one retry of the check/create
|
|
* sequence. That should then succeed. If the directory is still not found but
|
|
* the creation fails in the similar way, we return an error on that second
|
|
* try because otherwise we would potentially run into an endless loop.
|
|
* loop. -- rgerhards, 2010-03-25
|
|
* The likeliest scenario for a prolonged contest of creating the parent directiories
|
|
* is within our process space. This can happen with a high probability when two
|
|
* threads, that want to start logging to files within same directory tree, are
|
|
* started close to each other. We should fix what we can. -- nipakoo, 2017-11-25
|
|
*/
|
|
static int real_makeFileParentDirs(const uchar *const szFile, const size_t lenFile, const mode_t mode,
|
|
const uid_t uid, const gid_t gid, const int bFailOnChownFail)
|
|
{
|
|
uchar *p;
|
|
uchar *pszWork;
|
|
size_t len;
|
|
|
|
assert(szFile != NULL);
|
|
assert(lenFile > 0);
|
|
|
|
len = lenFile + 1; /* add one for '\0'-byte */
|
|
if((pszWork = malloc(len)) == NULL)
|
|
return -1;
|
|
memcpy(pszWork, szFile, len);
|
|
for(p = pszWork+1 ; *p ; p++)
|
|
if(*p == '/') {
|
|
/* temporarily terminate string, create dir and go on */
|
|
*p = '\0';
|
|
int bErr = 0;
|
|
if(mkdir((char*)pszWork, mode) == 0) {
|
|
if(uid != (uid_t) -1 || gid != (gid_t) -1) {
|
|
/* we need to set owner/group */
|
|
if(chown((char*)pszWork, uid, gid) != 0) {
|
|
LogError(errno, RS_RET_DIR_CHOWN_ERROR,
|
|
"chown for directory '%s' failed", pszWork);
|
|
if(bFailOnChownFail) {
|
|
/* ignore if configured to do so */
|
|
bErr = 1;
|
|
}
|
|
}
|
|
}
|
|
} else if(errno != EEXIST) {
|
|
/* EEXIST is ok, means this component exists */
|
|
bErr = 1;
|
|
}
|
|
|
|
if(bErr) {
|
|
int eSave = errno;
|
|
free(pszWork);
|
|
errno = eSave;
|
|
return -1;
|
|
}
|
|
*p = '/';
|
|
}
|
|
free(pszWork);
|
|
return 0;
|
|
}
|
|
/* note: this small function is the stub for the brain-dead POSIX cancel handling */
|
|
int makeFileParentDirs(const uchar *const szFile, const size_t lenFile, const mode_t mode,
|
|
const uid_t uid, const gid_t gid, const int bFailOnChownFail)
|
|
{
|
|
static pthread_mutex_t mutParentDir = PTHREAD_MUTEX_INITIALIZER;
|
|
int r; /* needs to be declared OUTSIDE of pthread_cleanup... macros! */
|
|
pthread_mutex_lock(&mutParentDir);
|
|
pthread_cleanup_push(mutexCancelCleanup, &mutParentDir);
|
|
|
|
r = real_makeFileParentDirs(szFile, lenFile, mode, uid, gid, bFailOnChownFail);
|
|
|
|
pthread_mutex_unlock(&mutParentDir);
|
|
pthread_cleanup_pop(0);
|
|
return r;
|
|
}
|
|
|
|
|
|
/* execute a program with a single argument
|
|
* returns child pid if everything ok, 0 on failure. if
|
|
* it fails, errno is set. if it fails after the fork(), the caller
|
|
* can not be notfied for obvious reasons. if bwait is set to 1,
|
|
* the code waits until the child terminates - that potentially takes
|
|
* a lot of time.
|
|
* implemented 2007-07-20 rgerhards
|
|
*/
|
|
int execProg(uchar *program, int bWait, uchar *arg)
|
|
{
|
|
int pid;
|
|
int sig;
|
|
struct sigaction sigAct;
|
|
|
|
dbgprintf("exec program '%s' with param '%s'\n", program, arg);
|
|
pid = fork();
|
|
if (pid < 0) {
|
|
return 0;
|
|
}
|
|
|
|
if(pid) { /* Parent */
|
|
if(bWait) {
|
|
/* waitpid will fail with errno == ECHILD if the child process has already
|
|
been reaped by the rsyslogd main loop (see rsyslogd.c) */
|
|
int status;
|
|
if(waitpid(pid, &status, 0) == pid) {
|
|
glblReportChildProcessExit(program, pid, status);
|
|
} else if(errno != ECHILD) {
|
|
/* we do not use logerror(), because
|
|
* that might bring us into an endless
|
|
* loop. At some time, we may
|
|
* reconsider this behaviour.
|
|
*/
|
|
dbgprintf("could not wait on child after executing '%s'",
|
|
(char*)program);
|
|
}
|
|
}
|
|
return pid;
|
|
}
|
|
/* Child */
|
|
alarm(0); /* create a clean environment before we exec the real child */
|
|
|
|
memset(&sigAct, 0, sizeof(sigAct));
|
|
sigemptyset(&sigAct.sa_mask);
|
|
sigAct.sa_handler = SIG_DFL;
|
|
|
|
for(sig = 1 ; sig < NSIG ; ++sig)
|
|
sigaction(sig, &sigAct, NULL);
|
|
|
|
execlp((char*)program, (char*) program, (char*)arg, NULL);
|
|
/* In the long term, it's a good idea to implement some enhanced error
|
|
* checking here. However, it can not easily be done. For starters, we
|
|
* may run into endless loops if we log to syslog. The next problem is
|
|
* that output is typically not seen by the user. For the time being,
|
|
* we use no error reporting, which is quite consitent with the old
|
|
* system() way of doing things. rgerhards, 2007-07-20
|
|
*/
|
|
perror("exec");
|
|
fprintf(stderr, "exec program was '%s' with param '%s'\n", program, arg);
|
|
exit(1); /* not much we can do in this case */
|
|
}
|
|
|
|
|
|
/* skip over whitespace in a standard C string. The
|
|
* provided pointer is advanced to the first non-whitespace
|
|
* charater or the \0 byte, if there is none. It is never
|
|
* moved past the \0.
|
|
*/
|
|
void skipWhiteSpace(uchar **pp)
|
|
{
|
|
register uchar *p;
|
|
|
|
assert(pp != NULL);
|
|
assert(*pp != NULL);
|
|
|
|
p = *pp;
|
|
while(*p && isspace((int) *p))
|
|
++p;
|
|
*pp = p;
|
|
}
|
|
|
|
|
|
/* generate a file name from four parts:
|
|
* <directory name>/<name>.<number>
|
|
* If number is negative, it is not used. If any of the strings is
|
|
* NULL, an empty string is used instead. Length must be provided.
|
|
* lNumDigits is the minimum number of digits that lNum should have. This
|
|
* is to pretty-print the file name, e.g. lNum = 3, lNumDigits= 4 will
|
|
* result in "0003" being used inside the file name. Set lNumDigits to 0
|
|
* to use as few space as possible.
|
|
* rgerhards, 2008-01-03
|
|
*/
|
|
PRAGMA_DIAGNOSTIC_PUSH
|
|
PRAGMA_IGNORE_Wformat_nonliteral
|
|
rsRetVal genFileName(uchar **ppName, uchar *pDirName, size_t lenDirName, uchar *pFName,
|
|
size_t lenFName, int64_t lNum, int lNumDigits)
|
|
{
|
|
DEFiRet;
|
|
uchar *pName;
|
|
uchar *pNameWork;
|
|
size_t lenName;
|
|
uchar szBuf[128]; /* buffer for number */
|
|
char szFmtBuf[32]; /* buffer for snprintf format */
|
|
size_t lenBuf;
|
|
|
|
if(lNum < 0) {
|
|
szBuf[0] = '\0';
|
|
lenBuf = 0;
|
|
} else {
|
|
if(lNumDigits > 0) {
|
|
snprintf(szFmtBuf, sizeof(szFmtBuf), ".%%0%d" PRId64, lNumDigits);
|
|
lenBuf = snprintf((char*)szBuf, sizeof(szBuf), szFmtBuf, lNum);
|
|
} else
|
|
lenBuf = snprintf((char*)szBuf, sizeof(szBuf), ".%" PRId64, lNum);
|
|
}
|
|
|
|
lenName = lenDirName + 1 + lenFName + lenBuf + 1; /* last +1 for \0 char! */
|
|
if((pName = malloc(lenName)) == NULL)
|
|
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
|
|
|
|
/* got memory, now construct string */
|
|
memcpy(pName, pDirName, lenDirName);
|
|
pNameWork = pName + lenDirName;
|
|
*pNameWork++ = '/';
|
|
memcpy(pNameWork, pFName, lenFName);
|
|
pNameWork += lenFName;
|
|
if(lenBuf > 0) {
|
|
memcpy(pNameWork, szBuf, lenBuf);
|
|
pNameWork += lenBuf;
|
|
}
|
|
*pNameWork = '\0';
|
|
|
|
*ppName = pName;
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
PRAGMA_DIAGNOSTIC_POP
|
|
|
|
/* get the number of digits required to represent a given number. We use an
|
|
* iterative approach as we do not like to draw in the floating point
|
|
* library just for log(). -- rgerhards, 2008-01-10
|
|
*/
|
|
int getNumberDigits(long lNum)
|
|
{
|
|
int iDig;
|
|
|
|
if(lNum == 0)
|
|
iDig = 1;
|
|
else
|
|
for(iDig = 0 ; lNum != 0 ; ++iDig)
|
|
lNum /= 10;
|
|
|
|
return iDig;
|
|
}
|
|
|
|
|
|
/* compute an absolute time timeout suitable for calls to pthread_cond_timedwait()
|
|
* iTimeout is in milliseconds
|
|
* rgerhards, 2008-01-14
|
|
*/
|
|
rsRetVal
|
|
timeoutComp(struct timespec *pt, long iTimeout)
|
|
{
|
|
# if _POSIX_TIMERS <= 0
|
|
struct timeval tv;
|
|
# endif
|
|
|
|
assert(pt != NULL);
|
|
/* compute timeout */
|
|
|
|
# if _POSIX_TIMERS > 0
|
|
/* this is the "regular" code */
|
|
clock_gettime(CLOCK_REALTIME, pt);
|
|
# else
|
|
gettimeofday(&tv, NULL);
|
|
pt->tv_sec = tv.tv_sec;
|
|
pt->tv_nsec = tv.tv_usec * 1000;
|
|
# endif
|
|
pt->tv_sec += iTimeout / 1000;
|
|
pt->tv_nsec += (iTimeout % 1000) * 1000000; /* think INTEGER arithmetic! */
|
|
if(pt->tv_nsec > 999999999) { /* overrun? */
|
|
pt->tv_nsec -= 1000000000;
|
|
++pt->tv_sec;
|
|
}
|
|
return RS_RET_OK; /* so far, this is static... */
|
|
}
|
|
|
|
long long
|
|
currentTimeMills(void)
|
|
{
|
|
struct timespec tm;
|
|
# if _POSIX_TIMERS <= 0
|
|
struct timeval tv;
|
|
# endif
|
|
|
|
# if _POSIX_TIMERS > 0
|
|
clock_gettime(CLOCK_REALTIME, &tm);
|
|
# else
|
|
gettimeofday(&tv, NULL);
|
|
tm.tv_sec = tv.tv_sec;
|
|
tm.tv_nsec = tv.tv_usec * 1000;
|
|
# endif
|
|
|
|
return ((long long) tm.tv_sec) * 1000 + (tm.tv_nsec / 1000000);
|
|
}
|
|
|
|
|
|
/* This function is kind of the reverse of timeoutComp() - it takes an absolute
|
|
* timeout value and computes how far this is in the future. If the value is already
|
|
* in the past, 0 is returned. The return value is in ms.
|
|
* rgerhards, 2008-01-25
|
|
*/
|
|
long
|
|
timeoutVal(struct timespec *pt)
|
|
{
|
|
struct timespec t;
|
|
long iTimeout;
|
|
# if _POSIX_TIMERS <= 0
|
|
struct timeval tv;
|
|
# endif
|
|
|
|
assert(pt != NULL);
|
|
/* compute timeout */
|
|
# if _POSIX_TIMERS > 0
|
|
/* this is the "regular" code */
|
|
clock_gettime(CLOCK_REALTIME, &t);
|
|
# else
|
|
gettimeofday(&tv, NULL);
|
|
t.tv_sec = tv.tv_sec;
|
|
t.tv_nsec = tv.tv_usec * 1000;
|
|
# endif
|
|
iTimeout = (pt->tv_nsec - t.tv_nsec) / 1000000;
|
|
iTimeout += (pt->tv_sec - t.tv_sec) * 1000;
|
|
|
|
if(iTimeout < 0)
|
|
iTimeout = 0;
|
|
|
|
return iTimeout;
|
|
}
|
|
|
|
|
|
/* cancellation cleanup handler - frees provided mutex
|
|
* rgerhards, 2008-01-14
|
|
*/
|
|
void
|
|
mutexCancelCleanup(void *arg)
|
|
{
|
|
assert(arg != NULL);
|
|
d_pthread_mutex_unlock((pthread_mutex_t*) arg);
|
|
}
|
|
|
|
|
|
/* rsSleep() - a fairly portable way to to sleep. It
|
|
* will wake up when
|
|
* a) the wake-time is over
|
|
* rgerhards, 2008-01-28
|
|
*/
|
|
void
|
|
srSleep(int iSeconds, int iuSeconds)
|
|
{
|
|
struct timeval tvSelectTimeout;
|
|
|
|
tvSelectTimeout.tv_sec = iSeconds;
|
|
tvSelectTimeout.tv_usec = iuSeconds; /* micro seconds */
|
|
select(0, NULL, NULL, NULL, &tvSelectTimeout);
|
|
}
|
|
|
|
|
|
/* From varmojfekoj's mail on why he provided rs_strerror_r():
|
|
* There are two problems with strerror_r():
|
|
* I see you've rewritten some of the code which calls it to use only
|
|
* the supplied buffer; unfortunately the GNU implementation sometimes
|
|
* doesn't use the buffer at all and returns a pointer to some
|
|
* immutable string instead, as noted in the man page.
|
|
*
|
|
* The other problem is that on some systems strerror_r() has a return
|
|
* type of int.
|
|
*
|
|
* So I've written a wrapper function rs_strerror_r(), which should
|
|
* take care of all this and be used instead.
|
|
*
|
|
* Added 2008-01-30
|
|
*/
|
|
char *rs_strerror_r(int errnum, char *buf, size_t buflen) {
|
|
#ifndef HAVE_STRERROR_R
|
|
char *pszErr;
|
|
pszErr = strerror(errnum);
|
|
snprintf(buf, buflen, "%s", pszErr);
|
|
#else
|
|
# ifdef STRERROR_R_CHAR_P
|
|
char *p = strerror_r(errnum, buf, buflen);
|
|
if (p != buf) {
|
|
strncpy(buf, p, buflen);
|
|
buf[buflen - 1] = '\0';
|
|
}
|
|
# else
|
|
strerror_r(errnum, buf, buflen);
|
|
# endif
|
|
#endif /* #ifdef __hpux */
|
|
return buf;
|
|
}
|
|
|
|
|
|
/* Decode a symbolic name to a numeric value */
|
|
int decodeSyslogName(uchar *name, syslogName_t *codetab)
|
|
{
|
|
register syslogName_t *c;
|
|
register uchar *p;
|
|
uchar buf[80];
|
|
|
|
assert(name != NULL);
|
|
assert(codetab != NULL);
|
|
|
|
DBGPRINTF("symbolic name: %s", name);
|
|
if(isdigit((int) *name)) {
|
|
DBGPRINTF("\n");
|
|
return (atoi((char*) name));
|
|
}
|
|
strncpy((char*) buf, (char*) name, 79);
|
|
for(p = buf; *p; p++) {
|
|
if (isupper((int) *p))
|
|
*p = tolower((int) *p);
|
|
}
|
|
for(c = codetab; c->c_name; c++) {
|
|
if(!strcmp((char*) buf, (char*) c->c_name)) {
|
|
DBGPRINTF(" ==> %d\n", c->c_val);
|
|
return (c->c_val);
|
|
}
|
|
}
|
|
DBGPRINTF("\n");
|
|
return (-1);
|
|
}
|
|
|
|
|
|
/**
|
|
* getSubString
|
|
*
|
|
* Copy a string byte by byte until the occurrence
|
|
* of a given separator.
|
|
*
|
|
* \param ppSrc Pointer to a pointer of the source array of characters. If a
|
|
separator detected the Pointer points to the next char after the
|
|
separator. Except if the end of the string is dedected ('\n').
|
|
Then it points to the terminator char.
|
|
* \param pDst Pointer to the destination array of characters. Here the substing
|
|
will be stored.
|
|
* \param DstSize Maximum numbers of characters to store.
|
|
* \param cSep Separator char.
|
|
* \ret int Returns 0 if no error occured.
|
|
*
|
|
* rgerhards, 2008-02-12: some notes are due... I will once again fix this function, this time
|
|
* so that it treats ' ' as a request for whitespace. But in general, the function and its callers
|
|
* should be changed over time, this is not really very good code...
|
|
*/
|
|
int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep)
|
|
{
|
|
uchar *pSrc = *ppSrc;
|
|
int iErr = 0; /* 0 = no error, >0 = error */
|
|
while((cSep == ' ' ? !isspace(*pSrc) : *pSrc != cSep) && *pSrc != '\n' && *pSrc != '\0' && DstSize>1) {
|
|
*pDst++ = *(pSrc)++;
|
|
DstSize--;
|
|
}
|
|
/* check if the Dst buffer was to small */
|
|
if ((cSep == ' ' ? !isspace(*pSrc) : *pSrc != cSep) && *pSrc != '\n' && *pSrc != '\0') {
|
|
dbgprintf("in getSubString, error Src buffer > Dst buffer\n");
|
|
iErr = 1;
|
|
}
|
|
if (*pSrc == '\0' || *pSrc == '\n')
|
|
/* this line was missing, causing ppSrc to be invalid when it
|
|
* was returned in case of end-of-string. rgerhards 2005-07-29
|
|
*/
|
|
*ppSrc = pSrc;
|
|
else
|
|
*ppSrc = pSrc+1;
|
|
*pDst = '\0';
|
|
return iErr;
|
|
}
|
|
|
|
|
|
/* get the size of a file or return appropriate error code. If an error is returned,
|
|
* *pSize content is undefined.
|
|
* rgerhards, 2009-06-12
|
|
*/
|
|
rsRetVal
|
|
getFileSize(uchar *pszName, off_t *pSize)
|
|
{
|
|
int ret;
|
|
struct stat statBuf;
|
|
DEFiRet;
|
|
|
|
ret = stat((char*) pszName, &statBuf);
|
|
if(ret == -1) {
|
|
switch(errno) {
|
|
case EACCES: ABORT_FINALIZE(RS_RET_NO_FILE_ACCESS);
|
|
case ENOTDIR:
|
|
case ENOENT: ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND);
|
|
default: ABORT_FINALIZE(RS_RET_FILE_NO_STAT);
|
|
}
|
|
}
|
|
|
|
*pSize = statBuf.st_size;
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
/* Returns 1 if the given string contains a non-escaped glob(3)
|
|
* wildcard character and 0 otherwise (or if the string is empty).
|
|
*/
|
|
int
|
|
containsGlobWildcard(char *str)
|
|
{
|
|
char *p;
|
|
if(!str) {
|
|
return 0;
|
|
}
|
|
/* From Linux Programmer's Guide:
|
|
* "A string is a wildcard pattern if it contains one of the characters '?', '*', '{' or '['"
|
|
* "One can remove the special meaning of '?', '*', '{' and '[' by preceding them by a backslash"
|
|
*/
|
|
for(p = str; *p != '\0'; p++) {
|
|
if((*p == '?' || *p == '*' || *p == '[' || *p == '{') &&
|
|
(p == str || *(p-1) != '\\')) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void seedRandomInsecureNumber(void)
|
|
{
|
|
struct timespec t;
|
|
timeoutComp(&t, 0);
|
|
long long x = t.tv_sec * 3 + t.tv_nsec * 2;
|
|
srandom((unsigned int) x);
|
|
}
|
|
|
|
static long int randomInsecureNumber(void)
|
|
{
|
|
return random();
|
|
}
|
|
|
|
#ifdef OS_LINUX
|
|
static int fdURandom = -1;
|
|
void seedRandomNumber(void)
|
|
{
|
|
if(fdURandom >= 0) {
|
|
/* Already opened. */
|
|
return;
|
|
}
|
|
fdURandom = open("/dev/urandom", O_RDONLY);
|
|
if(fdURandom == -1) {
|
|
LogError(errno, RS_RET_IO_ERROR, "failed to seed random number generation,"
|
|
" will use fallback (open urandom failed)");
|
|
seedRandomInsecureNumber();
|
|
}
|
|
}
|
|
|
|
void seedRandomNumberForChild(void)
|
|
{
|
|
/* The file descriptor inherited from our parent will have been closed after
|
|
* the fork. Discard this and call seedRandomNumber() to open /dev/urandom
|
|
* again.
|
|
*/
|
|
fdURandom = -1;
|
|
seedRandomNumber();
|
|
}
|
|
|
|
long int randomNumber(void)
|
|
{
|
|
long int ret;
|
|
if(fdURandom >= 0) {
|
|
if(read(fdURandom, &ret, sizeof(long int)) == -1) {
|
|
LogError(errno, RS_RET_IO_ERROR, "failed to generate random number, will"
|
|
" use fallback (read urandom failed)");
|
|
ret = randomInsecureNumber();
|
|
}
|
|
} else {
|
|
ret = randomInsecureNumber();
|
|
}
|
|
return ret;
|
|
}
|
|
#else
|
|
void seedRandomNumber(void)
|
|
{
|
|
seedRandomInsecureNumber();
|
|
}
|
|
|
|
void seedRandomNumberForChild(void)
|
|
{
|
|
seedRandomNumber();
|
|
}
|
|
|
|
long int randomNumber(void)
|
|
{
|
|
return randomInsecureNumber();
|
|
}
|
|
#endif
|
|
|
|
|
|
/* process "binary" parameters where this is needed to execute
|
|
* programs (namely mmexternal and omprog).
|
|
* Most importantly, split them into argv[] and get the binary name
|
|
*/
|
|
rsRetVal ATTR_NONNULL()
|
|
split_binary_parameters(uchar **const szBinary, char ***const __restrict__ aParams,
|
|
int *const iParams, es_str_t *const param_binary)
|
|
{
|
|
es_size_t iCnt;
|
|
es_size_t iStr;
|
|
int iPrm;
|
|
es_str_t *estrParams = NULL;
|
|
es_str_t *estrBinary = param_binary;
|
|
es_str_t *estrTmp = NULL;
|
|
uchar *c;
|
|
int bInQuotes;
|
|
DEFiRet;
|
|
assert(iParams != NULL);
|
|
assert(param_binary != NULL);
|
|
|
|
/* Search for end of binary name */
|
|
c = es_getBufAddr(param_binary);
|
|
iCnt = 0;
|
|
while(iCnt < es_strlen(param_binary) ) {
|
|
if (c[iCnt] == ' ') {
|
|
/* Split binary name from parameters */
|
|
estrBinary = es_newStrFromSubStr( param_binary, 0, iCnt);
|
|
estrParams = es_newStrFromSubStr( param_binary, iCnt+1,
|
|
es_strlen(param_binary));
|
|
break;
|
|
}
|
|
iCnt++;
|
|
}
|
|
*szBinary = (uchar*)es_str2cstr(estrBinary, NULL);
|
|
DBGPRINTF("szBinary = '%s'\n", *szBinary);
|
|
|
|
*iParams = 1; /* we always have argv[0] */
|
|
/* count size of argv[] */
|
|
if (estrParams != NULL) {
|
|
(*iParams)++; /* last parameter is not counted in loop below! */
|
|
if(Debug) {
|
|
char *params = es_str2cstr(estrParams, NULL);
|
|
dbgprintf("szParams = '%s'\n", params);
|
|
free(params);
|
|
}
|
|
c = es_getBufAddr(estrParams);
|
|
for(iCnt = 0 ; iCnt < es_strlen(estrParams) ; ++iCnt) {
|
|
if (c[iCnt] == ' ' && c[iCnt-1] != '\\')
|
|
(*iParams)++;
|
|
}
|
|
}
|
|
DBGPRINTF("iParams %d (+1 for NULL terminator)\n", *iParams);
|
|
|
|
/* create argv[] */
|
|
CHKmalloc(*aParams = malloc((*iParams + 1) * sizeof(char*)));
|
|
iPrm = 0;
|
|
bInQuotes = FALSE;
|
|
/* Set first parameter to binary */
|
|
(*aParams)[iPrm] = strdup((char*)*szBinary);
|
|
iPrm++;
|
|
if (estrParams != NULL) {
|
|
iCnt = iStr = 0;
|
|
c = es_getBufAddr(estrParams); /* Reset to beginning */
|
|
while(iCnt < es_strlen(estrParams) ) {
|
|
if ( c[iCnt] == ' ' && !bInQuotes ) {
|
|
estrTmp = es_newStrFromSubStr( estrParams, iStr, iCnt-iStr);
|
|
} else if ( iCnt+1 >= es_strlen(estrParams) ) {
|
|
estrTmp = es_newStrFromSubStr( estrParams, iStr, iCnt-iStr+1);
|
|
} else if (c[iCnt] == '"') {
|
|
bInQuotes = !bInQuotes;
|
|
}
|
|
|
|
if ( estrTmp != NULL ) {
|
|
(*aParams)[iPrm] = es_str2cstr(estrTmp, NULL);
|
|
iStr = iCnt+1; /* Set new start */
|
|
DBGPRINTF("Param (%d): '%s'\n", iPrm, (*aParams)[iPrm]);
|
|
es_deleteStr( estrTmp );
|
|
estrTmp = NULL;
|
|
iPrm++;
|
|
}
|
|
iCnt++;
|
|
}
|
|
}
|
|
(*aParams)[iPrm] = NULL; /* NULL per argv[] convention */
|
|
|
|
finalize_it:
|
|
if(estrBinary != param_binary) {
|
|
es_deleteStr(estrBinary);
|
|
}
|
|
if(estrParams != NULL) {
|
|
es_deleteStr(estrParams);
|
|
}
|
|
RETiRet;
|
|
}
|