2
* \brief General utilties that fit nowhere else.
4
* The namespace for this file is "srUtil".
6
* \author Rainer Gerhards <rgerhards@adiscon.com>
10
* Copyright 2003-2008 Rainer Gerhards and Adiscon GmbH.
12
* This file is part of the rsyslog runtime library.
14
* The rsyslog runtime library is free software: you can redistribute it and/or modify
15
* it under the terms of the GNU Lesser General Public License as published by
16
* the Free Software Foundation, either version 3 of the License, or
17
* (at your option) any later version.
19
* The rsyslog runtime library is distributed in the hope that it will be useful,
20
* but WITHOUT ANY WARRANTY; without even the implied warranty of
21
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
* GNU Lesser General Public License for more details.
24
* You should have received a copy of the GNU Lesser General Public License
25
* along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
27
* A copy of the GPL can be found in the file "COPYING" in this distribution.
28
* A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
39
#include <sys/types.h>
50
/* here we host some syslog specific names. There currently is no better place
51
* to do it, but over here is also not ideal... -- rgerhards, 2008-02-14
52
* rgerhards, 2008-04-16: note in LGPL move: the code tables below exist in
53
* the same way in BSD, so it is not a problem to move them from GPLv3 to LGPL.
55
syslogName_t syslogPriNames[] = {
61
{"error", LOG_ERR}, /* DEPRECATED */
63
{"none", INTERNAL_NOPRI}, /* INTERNAL */
64
{"notice", LOG_NOTICE},
65
{"panic", LOG_EMERG}, /* DEPRECATED */
66
{"warn", LOG_WARNING}, /* DEPRECATED */
67
{"warning", LOG_WARNING},
73
# define LOG_AUTHPRIV LOG_AUTH
75
syslogName_t syslogFacNames[] = {
77
{"authpriv", LOG_AUTHPRIV},
79
{"daemon", LOG_DAEMON},
83
{"mark", LOG_MARK}, /* INTERNAL */
85
{"security", LOG_AUTH}, /* DEPRECATED */
86
{"syslog", LOG_SYSLOG},
92
{"local0", LOG_LOCAL0},
93
{"local1", LOG_LOCAL1},
94
{"local2", LOG_LOCAL2},
95
{"local3", LOG_LOCAL3},
96
{"local4", LOG_LOCAL4},
97
{"local5", LOG_LOCAL5},
98
{"local6", LOG_LOCAL6},
99
{"local7", LOG_LOCAL7},
103
/* ################################################################# *
105
* ################################################################# */
107
/* As this is not a "real" object, there won't be any private
108
* members in this file.
111
/* ################################################################# *
113
* ################################################################# */
115
rsRetVal srUtilItoA(char *pBuf, int iLenBuf, number_t iToConv)
119
char szBuf[64]; /* sufficiently large for my lifespan and those of my children... ;) */
121
assert(pBuf != NULL);
122
assert(iLenBuf > 1); /* This is actually an app error and as thus checked for... */
132
/* first generate a string with the digits in the reverse direction */
136
szBuf[i++] = iToConv % 10 + '0';
138
} while(iToConv > 0); /* warning: do...while()! */
139
--i; /* undo last increment - we were pointing at NEXT location */
141
/* make sure we are within bounds... */
142
if(i + 2 > iLenBuf) /* +2 because: a) i starts at zero! b) the \0 byte */
143
return RS_RET_PROVIDED_BUFFER_TOO_SMALL;
145
/* then move it to the right direction... */
146
if(bIsNegative == TRUE)
149
*pBuf++ = szBuf[i--];
150
*pBuf = '\0'; /* terminate it!!! */
155
uchar *srUtilStrDup(uchar *pOld, size_t len)
159
assert(pOld != NULL);
161
if((pNew = malloc(len + 1)) != NULL)
162
memcpy(pNew, pOld, len + 1);
168
/* creates a path recursively
169
* Return 0 on success, -1 otherwise. On failure, errno
170
* hold the last OS error.
171
* Param "mode" holds the mode that all non-existing directories
172
* are to be created with.
174
int makeFileParentDirs(uchar *szFile, size_t lenFile, mode_t mode,
175
uid_t uid, gid_t gid, int bFailOnChownFail)
182
assert(szFile != NULL);
185
len = lenFile + 1; /* add one for '\0'-byte */
186
if((pszWork = malloc(sizeof(uchar) * len)) == NULL)
188
memcpy(pszWork, szFile, len);
189
for(p = pszWork+1 ; *p ; p++)
191
/* temporarily terminate string, create dir and go on */
193
if(access((char*)pszWork, F_OK)) {
194
if(mkdir((char*)pszWork, mode) == 0) {
195
if(uid != (uid_t) -1 || gid != (gid_t) -1) {
196
/* we need to set owner/group */
197
if(chown((char*)pszWork, uid, gid) != 0)
200
/* silently ignore if configured
220
/* execute a program with a single argument
221
* returns child pid if everything ok, 0 on failure. if
222
* it fails, errno is set. if it fails after the fork(), the caller
223
* can not be notfied for obvious reasons. if bwait is set to 1,
224
* the code waits until the child terminates - that potentially takes
226
* implemented 2007-07-20 rgerhards
228
int execProg(uchar *program, int bWait, uchar *arg)
232
struct sigaction sigAct;
234
dbgprintf("exec program '%s' with param '%s'\n", program, arg);
240
if(pid) { /* Parent */
242
if(waitpid(pid, NULL, 0) == -1)
243
if(errno != ECHILD) {
244
/* we do not use logerror(), because
245
* that might bring us into an endless
246
* loop. At some time, we may
247
* reconsider this behaviour.
249
dbgprintf("could not wait on child after executing '%s'",
255
alarm(0); /* create a clean environment before we exec the real child */
257
memset(&sigAct, 0, sizeof(sigAct));
258
sigemptyset(&sigAct.sa_mask);
259
sigAct.sa_handler = SIG_DFL;
261
for(sig = 1 ; sig < NSIG ; ++sig)
262
sigaction(sig, &sigAct, NULL);
264
execlp((char*)program, (char*) program, (char*)arg, NULL);
265
/* In the long term, it's a good idea to implement some enhanced error
266
* checking here. However, it can not easily be done. For starters, we
267
* may run into endless loops if we log to syslog. The next problem is
268
* that output is typically not seen by the user. For the time being,
269
* we use no error reporting, which is quite consitent with the old
270
* system() way of doing things. rgerhards, 2007-07-20
273
exit(1); /* not much we can do in this case */
277
/* skip over whitespace in a standard C string. The
278
* provided pointer is advanced to the first non-whitespace
279
* charater or the \0 byte, if there is none. It is never
282
void skipWhiteSpace(uchar **pp)
290
while(*p && isspace((int) *p))
296
/* generate a file name from four parts:
297
* <directory name>/<name>.<number>
298
* If number is negative, it is not used. If any of the strings is
299
* NULL, an empty string is used instead. Length must be provided.
300
* lNumDigits is the minimum number of digits that lNum should have. This
301
* is to pretty-print the file name, e.g. lNum = 3, lNumDigits= 4 will
302
* result in "0003" being used inside the file name. Set lNumDigits to 0
303
* to use as few space as possible.
304
* rgerhards, 2008-01-03
306
rsRetVal genFileName(uchar **ppName, uchar *pDirName, size_t lenDirName, uchar *pFName,
307
size_t lenFName, long lNum, int lNumDigits)
313
uchar szBuf[128]; /* buffer for number */
314
char szFmtBuf[32]; /* buffer for snprintf format */
322
snprintf(szFmtBuf, sizeof(szFmtBuf), ".%%0%dld", lNumDigits);
323
lenBuf = snprintf((char*)szBuf, sizeof(szBuf), szFmtBuf, lNum);
325
lenBuf = snprintf((char*)szBuf, sizeof(szBuf), ".%ld", lNum);
328
lenName = lenDirName + 1 + lenFName + lenBuf + 1; /* last +1 for \0 char! */
329
if((pName = malloc(sizeof(uchar) * lenName)) == NULL)
330
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
332
/* got memory, now construct string */
333
memcpy(pName, pDirName, lenDirName);
334
pNameWork = pName + lenDirName;
336
memcpy(pNameWork, pFName, lenFName);
337
pNameWork += lenFName;
339
memcpy(pNameWork, szBuf, lenBuf);
350
/* get the number of digits required to represent a given number. We use an
351
* iterative approach as we do not like to draw in the floating point
352
* library just for log(). -- rgerhards, 2008-01-10
354
int getNumberDigits(long lNum)
361
for(iDig = 0 ; lNum != 0 ; ++iDig)
368
/* compute an absolute time timeout suitable for calls to pthread_cond_timedwait()
369
* rgerhards, 2008-01-14
372
timeoutComp(struct timespec *pt, long iTimeout)
375
/* compute timeout */
376
clock_gettime(CLOCK_REALTIME, pt);
377
pt->tv_nsec += (iTimeout % 1000) * 1000000; /* think INTEGER arithmetic! */
378
if(pt->tv_nsec > 999999999) { /* overrun? */
379
pt->tv_nsec -= 1000000000;
381
pt->tv_sec += iTimeout / 1000;
382
return RS_RET_OK; /* so far, this is static... */
386
/* This function is kind of the reverse of timeoutComp() - it takes an absolute
387
* timeout value and computes how far this is in the future. If the value is already
388
* in the past, 0 is returned. The return value is in ms.
389
* rgerhards, 2008-01-25
392
timeoutVal(struct timespec *pt)
398
/* compute timeout */
399
clock_gettime(CLOCK_REALTIME, &t);
400
iTimeout = (pt->tv_nsec - t.tv_nsec) / 1000000;
401
iTimeout += (pt->tv_sec - t.tv_sec) * 1000;
410
/* cancellation cleanup handler - frees provided mutex
411
* rgerhards, 2008-01-14
414
mutexCancelCleanup(void *arg)
418
d_pthread_mutex_unlock((pthread_mutex_t*) arg);
423
/* rsSleep() - a fairly portable way to to sleep. It
425
* a) the wake-time is over
426
* rgerhards, 2008-01-28
429
srSleep(int iSeconds, int iuSeconds)
431
struct timeval tvSelectTimeout;
434
tvSelectTimeout.tv_sec = iSeconds;
435
tvSelectTimeout.tv_usec = iuSeconds; /* micro seconds */
436
select(0, NULL, NULL, NULL, &tvSelectTimeout);
441
/* From varmojfekoj's mail on why he provided rs_strerror_r():
442
* There are two problems with strerror_r():
443
* I see you've rewritten some of the code which calls it to use only
444
* the supplied buffer; unfortunately the GNU implementation sometimes
445
* doesn't use the buffer at all and returns a pointer to some
446
* immutable string instead, as noted in the man page.
448
* The other problem is that on some systems strerror_r() has a return
451
* So I've written a wrapper function rs_strerror_r(), which should
452
* take care of all this and be used instead.
456
char *rs_strerror_r(int errnum, char *buf, size_t buflen) {
459
pszErr = strerror(errnum);
460
snprintf(buf, buflen, "%s", pszErr);
462
# ifdef STRERROR_R_CHAR_P
463
char *p = strerror_r(errnum, buf, buflen);
465
strncpy(buf, p, buflen);
466
buf[buflen - 1] = '\0';
469
strerror_r(errnum, buf, buflen);
471
#endif /* #ifdef __hpux */
476
/* Decode a symbolic name to a numeric value
478
int decodeSyslogName(uchar *name, syslogName_t *codetab)
480
register syslogName_t *c;
484
ASSERT(name != NULL);
485
ASSERT(codetab != NULL);
487
dbgprintf("symbolic name: %s", name);
488
if (isdigit((int) *name))
491
return (atoi((char*) name));
493
strncpy((char*) buf, (char*) name, 79);
494
for (p = buf; *p; p++)
495
if (isupper((int) *p))
496
*p = tolower((int) *p);
497
for (c = codetab; c->c_name; c++)
498
if (!strcmp((char*) buf, (char*) c->c_name))
500
dbgprintf(" ==> %d\n", c->c_val);
510
* Copy a string byte by byte until the occurrence
511
* of a given separator.
513
* \param ppSrc Pointer to a pointer of the source array of characters. If a
514
separator detected the Pointer points to the next char after the
515
separator. Except if the end of the string is dedected ('\n').
516
Then it points to the terminator char.
517
* \param pDst Pointer to the destination array of characters. Here the substing
519
* \param DstSize Maximum numbers of characters to store.
520
* \param cSep Separator char.
521
* \ret int Returns 0 if no error occured.
523
* rgerhards, 2008-02-12: some notes are due... I will once again fix this function, this time
524
* so that it treats ' ' as a request for whitespace. But in general, the function and its callers
525
* should be changed over time, this is not really very good code...
527
int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep)
529
uchar *pSrc = *ppSrc;
530
int iErr = 0; /* 0 = no error, >0 = error */
531
while((cSep == ' ' ? !isspace(*pSrc) : *pSrc != cSep) && *pSrc != '\n' && *pSrc != '\0' && DstSize>1) {
535
/* check if the Dst buffer was to small */
536
if ((cSep == ' ' ? !isspace(*pSrc) : *pSrc != cSep) && *pSrc != '\n' && *pSrc != '\0') {
537
dbgprintf("in getSubString, error Src buffer > Dst buffer\n");
540
if (*pSrc == '\0' || *pSrc == '\n')
541
/* this line was missing, causing ppSrc to be invalid when it
542
* was returned in case of end-of-string. rgerhards 2005-07-29