~arges/ubuntu/quantal/rsyslog/fix-lp1059592

« back to all changes in this revision

Viewing changes to runtime/srutils.c

  • Committer: Bazaar Package Importer
  • Author(s): Michael Biebl
  • Date: 2009-02-15 21:56:23 UTC
  • mto: (3.2.4 squeeze) (1.1.9 upstream)
  • mto: This revision was merged to the branch mainline in revision 14.
  • Revision ID: james.westby@ubuntu.com-20090215215623-xsycf628eo3kguc0
Tags: upstream-3.20.4
ImportĀ upstreamĀ versionĀ 3.20.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**\file srUtils.c
 
2
 * \brief General utilties that fit nowhere else.
 
3
 *
 
4
 * The namespace for this file is "srUtil".
 
5
 *
 
6
 * \author  Rainer Gerhards <rgerhards@adiscon.com>
 
7
 * \date    2003-09-09
 
8
 *          Coding begun.
 
9
 *
 
10
 * Copyright 2003-2008 Rainer Gerhards and Adiscon GmbH.
 
11
 *
 
12
 * This file is part of the rsyslog runtime library.
 
13
 *
 
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.
 
18
 *
 
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.
 
23
 *
 
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/>.
 
26
 *
 
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.
 
29
 */
 
30
#include "config.h"
 
31
 
 
32
#include "rsyslog.h"
 
33
#include <stdio.h>
 
34
#include <stdlib.h>
 
35
#include <string.h>
 
36
#include <unistd.h>
 
37
#include <errno.h>
 
38
#include <sys/stat.h>
 
39
#include <sys/types.h>
 
40
#include <signal.h>
 
41
#include <assert.h>
 
42
#include <sys/wait.h>
 
43
#include <ctype.h>
 
44
#define TRUE 1
 
45
#define FALSE 0
 
46
#include "srUtils.h"
 
47
#include "obj.h"
 
48
 
 
49
 
 
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.
 
54
 */
 
55
syslogName_t    syslogPriNames[] = {
 
56
        {"alert",       LOG_ALERT},
 
57
        {"crit",        LOG_CRIT},
 
58
        {"debug",       LOG_DEBUG},
 
59
        {"emerg",       LOG_EMERG},
 
60
        {"err",         LOG_ERR},
 
61
        {"error",       LOG_ERR},               /* DEPRECATED */
 
62
        {"info",        LOG_INFO},
 
63
        {"none",        INTERNAL_NOPRI},        /* INTERNAL */
 
64
        {"notice",      LOG_NOTICE},
 
65
        {"panic",       LOG_EMERG},             /* DEPRECATED */
 
66
        {"warn",        LOG_WARNING},           /* DEPRECATED */
 
67
        {"warning",     LOG_WARNING},
 
68
        {"*",           TABLE_ALLPRI},
 
69
        {NULL,          -1}
 
70
};
 
71
 
 
72
#ifndef LOG_AUTHPRIV
 
73
#       define LOG_AUTHPRIV LOG_AUTH
 
74
#endif
 
75
syslogName_t    syslogFacNames[] = {
 
76
        {"auth",         LOG_AUTH},
 
77
        {"authpriv",     LOG_AUTHPRIV},
 
78
        {"cron",         LOG_CRON},
 
79
        {"daemon",       LOG_DAEMON},
 
80
        {"kern",         LOG_KERN},
 
81
        {"lpr",          LOG_LPR},
 
82
        {"mail",         LOG_MAIL},
 
83
        {"mark",         LOG_MARK},             /* INTERNAL */
 
84
        {"news",         LOG_NEWS},
 
85
        {"security",     LOG_AUTH},             /* DEPRECATED */
 
86
        {"syslog",       LOG_SYSLOG},
 
87
        {"user",         LOG_USER},
 
88
        {"uucp",         LOG_UUCP},
 
89
#if defined(LOG_FTP)
 
90
        {"ftp",          LOG_FTP},
 
91
#endif
 
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},
 
100
        {NULL,           -1},
 
101
};
 
102
 
 
103
/* ################################################################# *
 
104
 * private members                                                   *
 
105
 * ################################################################# */
 
106
 
 
107
/* As this is not a "real" object, there won't be any private
 
108
 * members in this file.
 
109
 */
 
110
 
 
111
/* ################################################################# *
 
112
 * public members                                                    *
 
113
 * ################################################################# */
 
114
 
 
115
rsRetVal srUtilItoA(char *pBuf, int iLenBuf, number_t iToConv)
 
116
{
 
117
        int i;
 
118
        int bIsNegative;
 
119
        char szBuf[64]; /* sufficiently large for my lifespan and those of my children... ;) */
 
120
 
 
121
        assert(pBuf != NULL);
 
122
        assert(iLenBuf > 1);    /* This is actually an app error and as thus checked for... */
 
123
 
 
124
        if(iToConv < 0)
 
125
        {
 
126
                bIsNegative = TRUE;
 
127
                iToConv *= -1;
 
128
        }
 
129
        else
 
130
                bIsNegative = FALSE;
 
131
 
 
132
        /* first generate a string with the digits in the reverse direction */
 
133
        i = 0;
 
134
        do
 
135
        {
 
136
                szBuf[i++] = iToConv % 10 + '0';
 
137
                iToConv /= 10;
 
138
        } while(iToConv > 0);   /* warning: do...while()! */
 
139
        --i; /* undo last increment - we were pointing at NEXT location */
 
140
 
 
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;
 
144
 
 
145
        /* then move it to the right direction... */
 
146
        if(bIsNegative == TRUE)
 
147
                *pBuf++ = '-';
 
148
        while(i >= 0)
 
149
                *pBuf++ = szBuf[i--];
 
150
        *pBuf = '\0';   /* terminate it!!! */
 
151
 
 
152
        return RS_RET_OK;
 
153
}
 
154
 
 
155
uchar *srUtilStrDup(uchar *pOld, size_t len)
 
156
{
 
157
        uchar *pNew;
 
158
 
 
159
        assert(pOld != NULL);
 
160
        
 
161
        if((pNew = malloc(len + 1)) != NULL)
 
162
                memcpy(pNew, pOld, len + 1);
 
163
 
 
164
        return pNew;
 
165
}
 
166
 
 
167
 
 
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.
 
173
 */
 
174
int makeFileParentDirs(uchar *szFile, size_t lenFile, mode_t mode,
 
175
                       uid_t uid, gid_t gid, int bFailOnChownFail)
 
176
{
 
177
        uchar *p;
 
178
        uchar *pszWork;
 
179
        size_t len;
 
180
        int bErr = 0;
 
181
 
 
182
        assert(szFile != NULL);
 
183
        assert(lenFile > 0);
 
184
 
 
185
        len = lenFile + 1; /* add one for '\0'-byte */
 
186
        if((pszWork = malloc(sizeof(uchar) * len)) == NULL)
 
187
                return -1;
 
188
        memcpy(pszWork, szFile, len);
 
189
        for(p = pszWork+1 ; *p ; p++)
 
190
                if(*p == '/') {
 
191
                        /* temporarily terminate string, create dir and go on */
 
192
                        *p = '\0';
 
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)
 
198
                                                        if(bFailOnChownFail)
 
199
                                                                bErr = 1;
 
200
                                                        /* silently ignore if configured
 
201
                                                         * to do so.
 
202
                                                         */
 
203
                                        }
 
204
                                } else
 
205
                                        bErr = 1;
 
206
                                if(bErr) {
 
207
                                        int eSave = errno;
 
208
                                        free(pszWork);
 
209
                                        errno = eSave;
 
210
                                        return -1;
 
211
                                }
 
212
                        }
 
213
                        *p = '/';
 
214
                }
 
215
        free(pszWork);
 
216
        return 0;
 
217
}
 
218
 
 
219
 
 
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
 
225
 * a lot of time.
 
226
 * implemented 2007-07-20 rgerhards
 
227
 */
 
228
int execProg(uchar *program, int bWait, uchar *arg)
 
229
{
 
230
        int pid;
 
231
        int sig;
 
232
        struct sigaction sigAct;
 
233
 
 
234
        dbgprintf("exec program '%s' with param '%s'\n", program, arg);
 
235
        pid = fork();
 
236
        if (pid < 0) {
 
237
                return 0;
 
238
        }
 
239
 
 
240
        if(pid) {       /* Parent */
 
241
                if(bWait)
 
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.
 
248
                                         */
 
249
                                        dbgprintf("could not wait on child after executing '%s'",
 
250
                                                (char*)program);
 
251
                                }
 
252
                return pid;
 
253
        }
 
254
        /* Child */
 
255
        alarm(0); /* create a clean environment before we exec the real child */
 
256
 
 
257
        memset(&sigAct, 0, sizeof(sigAct));
 
258
        sigemptyset(&sigAct.sa_mask);
 
259
        sigAct.sa_handler = SIG_DFL;
 
260
 
 
261
        for(sig = 1 ; sig < NSIG ; ++sig)
 
262
                sigaction(sig, &sigAct, NULL);
 
263
 
 
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
 
271
         */
 
272
        perror("exec");
 
273
        exit(1); /* not much we can do in this case */
 
274
}
 
275
 
 
276
 
 
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
 
280
 * moved past the \0.
 
281
 */
 
282
void skipWhiteSpace(uchar **pp)
 
283
{
 
284
        register uchar *p;
 
285
 
 
286
        assert(pp != NULL);
 
287
        assert(*pp != NULL);
 
288
 
 
289
        p = *pp;
 
290
        while(*p && isspace((int) *p))
 
291
                ++p;
 
292
        *pp = p;
 
293
}
 
294
 
 
295
 
 
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
 
305
 */
 
306
rsRetVal genFileName(uchar **ppName, uchar *pDirName, size_t lenDirName, uchar *pFName,
 
307
                     size_t lenFName, long lNum, int lNumDigits)
 
308
{
 
309
        DEFiRet;
 
310
        uchar *pName;
 
311
        uchar *pNameWork;
 
312
        size_t lenName;
 
313
        uchar szBuf[128];       /* buffer for number */
 
314
        char szFmtBuf[32];      /* buffer for snprintf format */
 
315
        size_t lenBuf;
 
316
 
 
317
        if(lNum < 0) {
 
318
                szBuf[0] = '\0';
 
319
                lenBuf = 0;
 
320
        } else {
 
321
                if(lNumDigits > 0) {
 
322
                        snprintf(szFmtBuf, sizeof(szFmtBuf), ".%%0%dld", lNumDigits);
 
323
                        lenBuf = snprintf((char*)szBuf, sizeof(szBuf), szFmtBuf, lNum);
 
324
                } else
 
325
                        lenBuf = snprintf((char*)szBuf, sizeof(szBuf), ".%ld", lNum);
 
326
        }
 
327
 
 
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);
 
331
        
 
332
        /* got memory, now construct string */
 
333
        memcpy(pName, pDirName, lenDirName);
 
334
        pNameWork = pName + lenDirName;
 
335
        *pNameWork++ = '/';
 
336
        memcpy(pNameWork, pFName, lenFName);
 
337
        pNameWork += lenFName;
 
338
        if(lenBuf > 0) {
 
339
                memcpy(pNameWork, szBuf, lenBuf);
 
340
                pNameWork += lenBuf;
 
341
        }
 
342
        *pNameWork = '\0';
 
343
 
 
344
        *ppName = pName;
 
345
 
 
346
finalize_it:
 
347
        RETiRet;
 
348
}
 
349
 
 
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
 
353
 */
 
354
int getNumberDigits(long lNum)
 
355
{
 
356
        int iDig;
 
357
 
 
358
        if(lNum == 0)
 
359
                iDig = 1;
 
360
        else
 
361
                for(iDig = 0 ; lNum != 0 ; ++iDig)
 
362
                        lNum /= 10;
 
363
 
 
364
        return iDig;
 
365
}
 
366
 
 
367
 
 
368
/* compute an absolute time timeout suitable for calls to pthread_cond_timedwait()
 
369
 * rgerhards, 2008-01-14
 
370
 */
 
371
rsRetVal
 
372
timeoutComp(struct timespec *pt, long iTimeout)
 
373
{
 
374
        assert(pt != NULL);
 
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;
 
380
        }
 
381
        pt->tv_sec += iTimeout / 1000;
 
382
        return RS_RET_OK; /* so far, this is static... */
 
383
}
 
384
 
 
385
 
 
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
 
390
 */
 
391
long
 
392
timeoutVal(struct timespec *pt)
 
393
{
 
394
        struct timespec t;
 
395
        long iTimeout;
 
396
 
 
397
        assert(pt != NULL);
 
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;
 
402
 
 
403
        if(iTimeout < 0)
 
404
                iTimeout = 0;
 
405
 
 
406
        return iTimeout;
 
407
}
 
408
 
 
409
 
 
410
/* cancellation cleanup handler - frees provided mutex
 
411
 * rgerhards, 2008-01-14
 
412
 */
 
413
void
 
414
mutexCancelCleanup(void *arg)
 
415
{
 
416
        BEGINfunc
 
417
        assert(arg != NULL);
 
418
        d_pthread_mutex_unlock((pthread_mutex_t*) arg);
 
419
        ENDfunc
 
420
}
 
421
 
 
422
 
 
423
/* rsSleep() - a fairly portable way to to sleep. It 
 
424
 * will wake up when
 
425
 * a) the wake-time is over
 
426
 * rgerhards, 2008-01-28
 
427
 */
 
428
void
 
429
srSleep(int iSeconds, int iuSeconds)
 
430
{
 
431
        struct timeval tvSelectTimeout;
 
432
 
 
433
        BEGINfunc
 
434
        tvSelectTimeout.tv_sec = iSeconds;
 
435
        tvSelectTimeout.tv_usec = iuSeconds; /* micro seconds */
 
436
        select(0, NULL, NULL, NULL, &tvSelectTimeout);
 
437
        ENDfunc
 
438
}
 
439
 
 
440
 
 
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.
 
447
 *
 
448
 * The other problem is that on some systems strerror_r() has a return
 
449
 * type of int.
 
450
 *
 
451
 * So I've written a wrapper function rs_strerror_r(), which should
 
452
 * take care of all this and be used instead.
 
453
 *
 
454
 * Added 2008-01-30
 
455
 */
 
456
char *rs_strerror_r(int errnum, char *buf, size_t buflen) {
 
457
#ifdef  __hpux
 
458
        char *pszErr;
 
459
        pszErr = strerror(errnum);
 
460
        snprintf(buf, buflen, "%s", pszErr);
 
461
#else
 
462
#       ifdef STRERROR_R_CHAR_P
 
463
                char *p = strerror_r(errnum, buf, buflen);
 
464
                if (p != buf) {
 
465
                        strncpy(buf, p, buflen);
 
466
                        buf[buflen - 1] = '\0';
 
467
                }
 
468
#       else
 
469
                strerror_r(errnum, buf, buflen);
 
470
#       endif
 
471
#endif /* #ifdef __hpux */
 
472
        return buf;
 
473
}
 
474
 
 
475
 
 
476
/*  Decode a symbolic name to a numeric value
 
477
 */
 
478
int decodeSyslogName(uchar *name, syslogName_t *codetab)
 
479
{
 
480
        register syslogName_t *c;
 
481
        register uchar *p;
 
482
        uchar buf[80];
 
483
 
 
484
        ASSERT(name != NULL);
 
485
        ASSERT(codetab != NULL);
 
486
 
 
487
        dbgprintf("symbolic name: %s", name);
 
488
        if (isdigit((int) *name))
 
489
        {
 
490
                dbgprintf("\n");
 
491
                return (atoi((char*) name));
 
492
        }
 
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))
 
499
                {
 
500
                        dbgprintf(" ==> %d\n", c->c_val);
 
501
                        return (c->c_val);
 
502
                }
 
503
        return (-1);
 
504
}
 
505
 
 
506
 
 
507
/**
 
508
 * getSubString
 
509
 *
 
510
 * Copy a string byte by byte until the occurrence  
 
511
 * of a given separator.
 
512
 *
 
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
 
518
                        will be stored.
 
519
 * \param DstSize       Maximum numbers of characters to store.
 
520
 * \param cSep          Separator char.
 
521
 * \ret int             Returns 0 if no error occured.
 
522
 *
 
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...
 
526
 */
 
527
int getSubString(uchar **ppSrc,  char *pDst, size_t DstSize, char cSep)
 
528
{
 
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) {
 
532
                *pDst++ = *(pSrc)++;
 
533
                DstSize--;
 
534
        }
 
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");
 
538
                iErr = 1;
 
539
        }       
 
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
 
543
                 */
 
544
                *ppSrc = pSrc;
 
545
        else
 
546
                *ppSrc = pSrc+1;
 
547
        *pDst = '\0';
 
548
        return iErr;
 
549
}
 
550
 
 
551
 
 
552
 
 
553
/* vim:set ai:
 
554
 */