summaryrefslogtreecommitdiffstats
path: root/src/util/debug.c
blob: 54fb8011be7a4c787b201314f1f08d1b46d3b66c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/*
    Authors:
        Simo Sorce <ssorce@redhat.com>
        Stephen Gallagher <sgallagh@redhat.com>

    Copyright (C) 2009 Red Hat

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "config.h"

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <fcntl.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>

#include "util/util.h"

const char *debug_prg_name = "sssd";

int debug_level = SSSDBG_UNRESOLVED;
int debug_timestamps = SSSDBG_TIMESTAMP_UNRESOLVED;
int debug_microseconds = SSSDBG_MICROSECONDS_UNRESOLVED;
int debug_to_file = 0;
const char *debug_log_file = "sssd";
FILE *debug_file = NULL;

errno_t set_debug_file_from_fd(const int fd)
{
    FILE *dummy;
    errno_t ret;

    errno = 0;
    dummy = fdopen(fd, "a");
    if (dummy == NULL) {
        ret = errno;
        DEBUG(1, ("fdopen failed [%d][%s].\n", ret, strerror(ret)));
        sss_log(SSS_LOG_ERR,
                "Could not open debug file descriptor [%d]. "
                "Debug messages will not be written to the file "
                "for this child process [%s][%s]\n",
                fd, debug_prg_name, strerror(ret));
        return ret;
    }

    debug_file = dummy;

    return EOK;
}

int debug_convert_old_level(int old_level)
{
    if ((old_level != 0) && !(old_level & 0x000F))
        return old_level;

    int new_level = SSSDBG_FATAL_FAILURE;

    if (old_level <= 0)
        return new_level;

    if (old_level >= 1)
        new_level |= SSSDBG_CRIT_FAILURE;

    if (old_level >= 2)
        new_level |= SSSDBG_OP_FAILURE;

    if (old_level >= 3)
        new_level |= SSSDBG_MINOR_FAILURE;

    if (old_level >= 4)
        new_level |= SSSDBG_CONF_SETTINGS;

    if (old_level >= 5)
        new_level |= SSSDBG_FUNC_DATA;

    if (old_level >= 6)
        new_level |= SSSDBG_TRACE_FUNC;

    if (old_level >= 7)
        new_level |= SSSDBG_TRACE_LIBS;

    if (old_level >= 8)
        new_level |= SSSDBG_TRACE_INTERNAL;

    if (old_level >= 9)
        new_level |= SSSDBG_TRACE_ALL;

    return new_level;
}

void debug_fn(const char *format, ...)
{
    va_list ap;

    va_start(ap, format);

    vfprintf(debug_file ? debug_file : stderr, format, ap);
    fflush(debug_file ? debug_file : stderr);

    va_end(ap);
}

int debug_get_level(int old_level)
{
    if ((old_level != 0) && !(old_level & 0x000F))
        return old_level;

    if ((old_level > 9) || (old_level < 0))
        return SSSDBG_FATAL_FAILURE;

    int levels[] = {
        SSSDBG_FATAL_FAILURE,   /* 0 */
        SSSDBG_CRIT_FAILURE,
        SSSDBG_OP_FAILURE,
        SSSDBG_MINOR_FAILURE,
        SSSDBG_CONF_SETTINGS,
        SSSDBG_FUNC_DATA,
        SSSDBG_TRACE_FUNC,
        SSSDBG_TRACE_LIBS,
        SSSDBG_TRACE_INTERNAL,
        SSSDBG_TRACE_ALL        /* 9 */
    };

    return levels[old_level];
}

void ldb_debug_messages(void *context, enum ldb_debug_level level,
                        const char *fmt, va_list ap)
{
    int loglevel = SSSDBG_UNRESOLVED;
    int ret;
    char * message = NULL;

    switch(level) {
    case LDB_DEBUG_FATAL:
        loglevel = SSSDBG_FATAL_FAILURE;
        break;
    case LDB_DEBUG_ERROR:
        loglevel = SSSDBG_CRIT_FAILURE;
        break;
    case LDB_DEBUG_WARNING:
        loglevel = SSSDBG_TRACE_FUNC;
        break;
    case LDB_DEBUG_TRACE:
        loglevel = SSSDBG_TRACE_ALL;
        break;
    }

    ret = vasprintf(&message, fmt, ap);
    if (ret < 0) {
        /* ENOMEM */
        return;
    }

    DEBUG_MSG(loglevel, "ldb", message);

    free(message);
}

int open_debug_file_ex(const char *filename, FILE **filep)
{
    FILE *f = NULL;
    char *logpath;
    const char *log_file;
    mode_t old_umask;
    int ret;
    int debug_fd;
    int flags;

    if (filename == NULL) {
        log_file = debug_log_file;
    } else {
        log_file = filename;
    }

    ret = asprintf(&logpath, "%s/%s.log", LOG_PATH, log_file);
    if (ret == -1) {
        return ENOMEM;
    }

    if (debug_file && !filep) fclose(debug_file);

    old_umask = umask(0177);
    errno = 0;
    f = fopen(logpath, "a");
    if (f == NULL) {
        sss_log(SSS_LOG_EMERG, "Could not open file [%s]. Error: [%d][%s]\n",
                               logpath, errno, strerror(errno));
        free(logpath);
        return EIO;
    }
    umask(old_umask);

    debug_fd = fileno(f);
    if (debug_fd == -1) {
        fclose(f);
        free(logpath);
        return EIO;
    }

    flags = fcntl(debug_fd, F_GETFD, 0);
    (void) fcntl(debug_fd, F_SETFD, flags | FD_CLOEXEC);

    if (filep == NULL) {
        debug_file = f;
    } else {
        *filep = f;
    }
    free(logpath);
    return EOK;
}

int open_debug_file(void)
{
    return open_debug_file_ex(NULL, NULL);
}

int rotate_debug_files(void)
{
    int ret;
    errno_t error;

    if (!debug_to_file) return EOK;

    do {
        error = 0;
        ret = fclose(debug_file);
        if (ret != 0) {
            error = errno;
        }

        /* Check for EINTR, which means we should retry
         * because the system call was interrupted by a
         * signal
         */
    } while (error == EINTR);

    if (error != 0) {
        /* Even if we were unable to close the debug log, we need to make
         * sure that we open up a new one. Log rotation will remove the
         * current file, so all debug messages will be disappearing.
         *
         * We should write an error to the syslog warning of the resource
         * leak and then proceed with opening the new file.
         */
        sss_log(SSS_LOG_ALERT, "Could not close debug file [%s]. [%d][%s]\n",
                               debug_log_file, error, strerror(error));
        sss_log(SSS_LOG_ALERT, "Attempting to open new file anyway. "
                               "Be aware that this is a resource leak\n");
    }

    debug_file = NULL;

    return open_debug_file();
}

void talloc_log_fn(const char *message)
{
    DEBUG(SSSDBG_FATAL_FAILURE, (message));
}
om"> */ gettimeofday(&tp, &tz); # else gettimeofday(&tp, NULL); # endif if(ttSeconds != NULL) *ttSeconds = tp.tv_sec; tm = localtime_r((time_t*) &(tp.tv_sec), &tmBuf); t->year = tm->tm_year + 1900; t->month = tm->tm_mon + 1; t->day = tm->tm_mday; t->hour = tm->tm_hour; t->minute = tm->tm_min; t->second = tm->tm_sec; t->secfrac = tp.tv_usec; t->secfracPrecision = 6; # if __sun /* Solaris uses a different method of exporting the time zone. * It is UTC - localtime, which is the opposite sign of mins east of GMT. */ lBias = -(daylight ? altzone : timezone); # elif defined(__hpux) lBias = tz.tz_dsttime ? - tz.tz_minuteswest : 0; # else lBias = tm->tm_gmtoff; # endif if(lBias < 0) { t->OffsetMode = '-'; lBias *= -1; } else t->OffsetMode = '+'; t->OffsetHour = lBias / 3600; t->OffsetMinute = lBias % 3600; t->timeType = TIME_TYPE_RFC5424; /* we have a high precision timestamp */ } /* A fast alternative to getCurrTime() and time() that only obtains * a timestamp like time() does. I was told that gettimeofday(), at * least under Linux, is much faster than time() and I could confirm * this testing. So I created that function as a replacement. * rgerhards, 2009-11-12 */ static time_t getTime(time_t *ttSeconds) { struct timeval tp; if(gettimeofday(&tp, NULL) == -1) return -1; if(ttSeconds != NULL) *ttSeconds = tp.tv_sec; return tp.tv_sec; } /******************************************************************* * BEGIN CODE-LIBLOGGING * ******************************************************************* * Code in this section is borrowed from liblogging. This is an * interim solution. Once liblogging is fully integrated, this is * to be removed (see http://www.monitorware.com/liblogging for * more details. 2004-11-16 rgerhards * * Please note that the orginal liblogging code is modified so that * it fits into the context of the current version of syslogd.c. * * DO NOT PUT ANY OTHER CODE IN THIS BEGIN ... END BLOCK!!!! */ /** * Parse a 32 bit integer number from a string. * * \param ppsz Pointer to the Pointer to the string being parsed. It * must be positioned at the first digit. Will be updated * so that on return it points to the first character AFTER * the integer parsed. * \param pLenStr pointer to string length, decremented on exit by * characters processed * Note that if an empty string (len < 1) is passed in, * the method always returns zero. * \retval The number parsed. */ static int srSLMGParseInt32(uchar** ppsz, int *pLenStr) { register int i; i = 0; while(*pLenStr > 0 && isdigit((int) **ppsz)) { i = i * 10 + **ppsz - '0'; ++(*ppsz); --(*pLenStr); } return i; } /** * Parse a TIMESTAMP-3339. * updates the parse pointer position. The pTime parameter * is guranteed to be updated only if a new valid timestamp * could be obtained (restriction added 2008-09-16 by rgerhards). * This method now also checks the maximum string length it is passed. * If a *valid* timestamp is found, the string length is decremented * by the number of characters processed. If it is not a valid timestamp, * the length is kept unmodified. -- rgerhards, 2009-09-23 */ static rsRetVal ParseTIMESTAMP3339(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr) { uchar *pszTS = *ppszTS; /* variables to temporarily hold time information while we parse */ int year; int month; int day; int hour; /* 24 hour clock */ int minute; int second; int secfrac; /* fractional seconds (must be 32 bit!) */ int secfracPrecision; char OffsetMode; /* UTC offset + or - */ char OffsetHour; /* UTC offset in hours */ int OffsetMinute; /* UTC offset in minutes */ int lenStr; /* end variables to temporarily hold time information while we parse */ DEFiRet; assert(pTime != NULL); assert(ppszTS != NULL); assert(pszTS != NULL); lenStr = *pLenStr; year = srSLMGParseInt32(&pszTS, &lenStr); /* We take the liberty to accept slightly malformed timestamps e.g. in * the format of 2003-9-1T1:0:0. This doesn't hurt on receiving. Of course, * with the current state of affairs, we would never run into this code * here because at postion 11, there is no "T" in such cases ;) */ if(lenStr == 0 || *pszTS++ != '-') ABORT_FINALIZE(RS_RET_INVLD_TIME); --lenStr; month = srSLMGParseInt32(&pszTS, &lenStr); if(month < 1 || month > 12) ABORT_FINALIZE(RS_RET_INVLD_TIME); if(lenStr == 0 || *pszTS++ != '-') ABORT_FINALIZE(RS_RET_INVLD_TIME); --lenStr; day = srSLMGParseInt32(&pszTS, &lenStr); if(day < 1 || day > 31) ABORT_FINALIZE(RS_RET_INVLD_TIME); if(lenStr == 0 || *pszTS++ != 'T') ABORT_FINALIZE(RS_RET_INVLD_TIME); --lenStr; hour = srSLMGParseInt32(&pszTS, &lenStr); if(hour < 0 || hour > 23) ABORT_FINALIZE(RS_RET_INVLD_TIME); if(lenStr == 0 || *pszTS++ != ':') ABORT_FINALIZE(RS_RET_INVLD_TIME); --lenStr; minute = srSLMGParseInt32(&pszTS, &lenStr); if(minute < 0 || minute > 59) ABORT_FINALIZE(RS_RET_INVLD_TIME); if(lenStr == 0 || *pszTS++ != ':') ABORT_FINALIZE(RS_RET_INVLD_TIME); --lenStr; second = srSLMGParseInt32(&pszTS, &lenStr); if(second < 0 || second > 60) ABORT_FINALIZE(RS_RET_INVLD_TIME); /* Now let's see if we have secfrac */ if(lenStr > 0 && *pszTS == '.') { --lenStr; uchar *pszStart = ++pszTS; secfrac = srSLMGParseInt32(&pszTS, &lenStr); secfracPrecision = (int) (pszTS - pszStart); } else { secfracPrecision = 0; secfrac = 0; } /* check the timezone */ if(lenStr == 0) ABORT_FINALIZE(RS_RET_INVLD_TIME); if(*pszTS == 'Z') { --lenStr; pszTS++; /* eat Z */ OffsetMode = 'Z'; OffsetHour = 0; OffsetMinute = 0; } else if((*pszTS == '+') || (*pszTS == '-')) { OffsetMode = *pszTS; --lenStr; pszTS++; OffsetHour = srSLMGParseInt32(&pszTS, &lenStr); if(OffsetHour < 0 || OffsetHour > 23) ABORT_FINALIZE(RS_RET_INVLD_TIME); if(lenStr == 0 || *pszTS++ != ':') ABORT_FINALIZE(RS_RET_INVLD_TIME); OffsetMinute = srSLMGParseInt32(&pszTS, &lenStr); if(OffsetMinute < 0 || OffsetMinute > 59) ABORT_FINALIZE(RS_RET_INVLD_TIME); } else { /* there MUST be TZ information */ ABORT_FINALIZE(RS_RET_INVLD_TIME); } /* OK, we actually have a 3339 timestamp, so let's indicated this */ if(lenStr > 0) { if(*pszTS != ' ') /* if it is not a space, it can not be a "good" time - 2010-02-22 rgerhards */ ABORT_FINALIZE(RS_RET_INVLD_TIME); ++pszTS; /* just skip past it */ --lenStr; } /* we had success, so update parse pointer and caller-provided timestamp */ *ppszTS = pszTS; pTime->timeType = 2; pTime->year = year; pTime->month = month; pTime->day = day; pTime->hour = hour; pTime->minute = minute; pTime->second = second; pTime->secfrac = secfrac; pTime->secfracPrecision = secfracPrecision; pTime->OffsetMode = OffsetMode; pTime->OffsetHour = OffsetHour; pTime->OffsetMinute = OffsetMinute; *pLenStr = lenStr; finalize_it: RETiRet; } /** * Parse a TIMESTAMP-3164. The pTime parameter * is guranteed to be updated only if a new valid timestamp * could be obtained (restriction added 2008-09-16 by rgerhards). This * also means the caller *must* provide a valid (probably current) * timstamp in pTime when calling this function. a 3164 timestamp contains * only partial information and only that partial information is updated. * So the "output timestamp" is a valid timestamp only if the "input * timestamp" was valid, too. The is actually an optimization, as it * permits us to use a pre-aquired timestamp and thus avoids to do * a (costly) time() call. Thanks to David Lang for insisting on * time() call reduction ;). * This method now also checks the maximum string length it is passed. * If a *valid* timestamp is found, the string length is decremented * by the number of characters processed. If it is not a valid timestamp, * the length is kept unmodified. -- rgerhards, 2009-09-23 */ static rsRetVal ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr) { /* variables to temporarily hold time information while we parse */ int month; int day; int year = 0; /* 0 means no year provided */ int hour; /* 24 hour clock */ int minute; int second; /* end variables to temporarily hold time information while we parse */ int lenStr; uchar *pszTS; DEFiRet; assert(ppszTS != NULL); pszTS = *ppszTS; assert(pszTS != NULL); assert(pTime != NULL); assert(pLenStr != NULL); lenStr = *pLenStr; /* If we look at the month (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec), * we may see the following character sequences occur: * * J(an/u(n/l)), Feb, Ma(r/y), A(pr/ug), Sep, Oct, Nov, Dec * * We will use this for parsing, as it probably is the * fastest way to parse it. * * 2009-08-17: we now do case-insensitive comparisons, as some devices obviously do not * obey to the RFC-specified case. As we need to guess in any case, we can ignore case * in the first place -- rgerhards * * 2005-07-18, well sometimes it pays to be a bit more verbose, even in C... * Fixed a bug that lead to invalid detection of the data. The issue was that * we had an if(++pszTS == 'x') inside of some of the consturcts below. However, * there were also some elseifs (doing the same ++), which than obviously did not * check the orginal character but the next one. Now removed the ++ and put it * into the statements below. Was a really nasty bug... I didn't detect it before * june, when it first manifested. This also lead to invalid parsing of the rest * of the message, as the time stamp was not detected to be correct. - rgerhards */ if(lenStr < 3) ABORT_FINALIZE(RS_RET_INVLD_TIME); switch(*pszTS++) { case 'j': case 'J': if(*pszTS == 'a' || *pszTS == 'A') { ++pszTS; if(*pszTS == 'n' || *pszTS == 'N') { ++pszTS; month = 1; } else ABORT_FINALIZE(RS_RET_INVLD_TIME); } else if(*pszTS == 'u' || *pszTS == 'U') { ++pszTS; if(*pszTS == 'n' || *pszTS == 'N') { ++pszTS; month = 6; } else if(*pszTS == 'l' || *pszTS == 'L') { ++pszTS; month = 7; } else ABORT_FINALIZE(RS_RET_INVLD_TIME); } else ABORT_FINALIZE(RS_RET_INVLD_TIME); break; case 'f': case 'F': if(*pszTS == 'e' || *pszTS == 'E') { ++pszTS; if(*pszTS == 'b' || *pszTS == 'B') { ++pszTS; month = 2; } else ABORT_FINALIZE(RS_RET_INVLD_TIME); } else ABORT_FINALIZE(RS_RET_INVLD_TIME); break; case 'm': case 'M': if(*pszTS == 'a' || *pszTS == 'A') { ++pszTS; if(*pszTS == 'r' || *pszTS == 'R') { ++pszTS; month = 3; } else if(*pszTS == 'y' || *pszTS == 'Y') { ++pszTS; month = 5; } else ABORT_FINALIZE(RS_RET_INVLD_TIME); } else ABORT_FINALIZE(RS_RET_INVLD_TIME); break; case 'a': case 'A': if(*pszTS == 'p' || *pszTS == 'P') { ++pszTS; if(*pszTS == 'r' || *pszTS == 'R') { ++pszTS; month = 4; } else ABORT_FINALIZE(RS_RET_INVLD_TIME); } else if(*pszTS == 'u' || *pszTS == 'U') { ++pszTS; if(*pszTS == 'g' || *pszTS == 'G') { ++pszTS; month = 8; } else ABORT_FINALIZE(RS_RET_INVLD_TIME); } else ABORT_FINALIZE(RS_RET_INVLD_TIME); break; case 's': case 'S': if(*pszTS == 'e' || *pszTS == 'E') { ++pszTS; if(*pszTS == 'p' || *pszTS == 'P') { ++pszTS; month = 9; } else ABORT_FINALIZE(RS_RET_INVLD_TIME); } else ABORT_FINALIZE(RS_RET_INVLD_TIME); break; case 'o': case 'O': if(*pszTS == 'c' || *pszTS == 'C') { ++pszTS; if(*pszTS == 't' || *pszTS == 'T') { ++pszTS; month = 10; } else ABORT_FINALIZE(RS_RET_INVLD_TIME); } else ABORT_FINALIZE(RS_RET_INVLD_TIME); break; case 'n': case 'N': if(*pszTS == 'o' || *pszTS == 'O') { ++pszTS; if(*pszTS == 'v' || *pszTS == 'V') { ++pszTS; month = 11; } else ABORT_FINALIZE(RS_RET_INVLD_TIME); } else ABORT_FINALIZE(RS_RET_INVLD_TIME); break; case 'd': case 'D': if(*pszTS == 'e' || *pszTS == 'E') { ++pszTS; if(*pszTS == 'c' || *pszTS == 'C') { ++pszTS; month = 12; } else ABORT_FINALIZE(RS_RET_INVLD_TIME); } else ABORT_FINALIZE(RS_RET_INVLD_TIME); break; default: ABORT_FINALIZE(RS_RET_INVLD_TIME); } lenStr -= 3; /* done month */ if(lenStr == 0 || *pszTS++ != ' ') ABORT_FINALIZE(RS_RET_INVLD_TIME); --lenStr; /* we accept a slightly malformed timestamp when receiving. This is * we accept one-digit days */ if(*pszTS == ' ') { --lenStr; ++pszTS; } day = srSLMGParseInt32(&pszTS, &lenStr); if(day < 1 || day > 31) ABORT_FINALIZE(RS_RET_INVLD_TIME); if(lenStr == 0 || *pszTS++ != ' ') ABORT_FINALIZE(RS_RET_INVLD_TIME); --lenStr; /* time part */ hour = srSLMGParseInt32(&pszTS, &lenStr); if(hour > 1970 && hour < 2100) { /* if so, we assume this actually is a year. This is a format found * e.g. in Cisco devices. * (if you read this 2100+ trying to fix a bug, congratulate me * to how long the code survived - me no longer ;)) -- rgerhards, 2008-11-18 */ year = hour; /* re-query the hour, this time it must be valid */ if(lenStr == 0 || *pszTS++ != ' ') ABORT_FINALIZE(RS_RET_INVLD_TIME); --lenStr; hour = srSLMGParseInt32(&pszTS, &lenStr); } if(hour < 0 || hour > 23) ABORT_FINALIZE(RS_RET_INVLD_TIME); if(lenStr == 0 || *pszTS++ != ':') ABORT_FINALIZE(RS_RET_INVLD_TIME); --lenStr; minute = srSLMGParseInt32(&pszTS, &lenStr); if(minute < 0 || minute > 59) ABORT_FINALIZE(RS_RET_INVLD_TIME); if(lenStr == 0 || *pszTS++ != ':') ABORT_FINALIZE(RS_RET_INVLD_TIME); --lenStr; second = srSLMGParseInt32(&pszTS, &lenStr); if(second < 0 || second > 60) ABORT_FINALIZE(RS_RET_INVLD_TIME); /* we provide support for an extra ":" after the date. While this is an * invalid format, it occurs frequently enough (e.g. with Cisco devices) * to permit it as a valid case. -- rgerhards, 2008-09-12 */ if(lenStr > 0 && *pszTS == ':') { ++pszTS; /* just skip past it */ --lenStr; } if(lenStr > 0) { if(*pszTS != ' ') /* if it is not a space, it can not be a "good" time - 2010-02-22 rgerhards */ ABORT_FINALIZE(RS_RET_INVLD_TIME); ++pszTS; /* just skip past it */ --lenStr; } /* we had success, so update parse pointer and caller-provided timestamp * fields we do not have are not updated in the caller's timestamp. This * is the reason why the caller must pass in a correct timestamp. */ *ppszTS = pszTS; /* provide updated parse position back to caller */ pTime->timeType = 1; pTime->month = month; if(year > 0) pTime->year = year; /* persist year if detected */ pTime->day = day; pTime->hour = hour; pTime->minute = minute; pTime->second = second; pTime->secfracPrecision = 0; pTime->secfrac = 0; *pLenStr = lenStr; finalize_it: RETiRet; } /******************************************************************* * END CODE-LIBLOGGING * *******************************************************************/ /** * Format a syslogTimestamp into format required by MySQL. * We are using the 14 digits format. For example 20041111122600 * is interpreted as '2004-11-11 12:26:00'. * The caller must provide the timestamp as well as a character * buffer that will receive the resulting string. The function * returns the size of the timestamp written in bytes (without * the string terminator). If 0 is returend, an error occured. */ int formatTimestampToMySQL(struct syslogTime *ts, char* pBuf) { /* currently we do not consider localtime/utc. This may later be * added. If so, I recommend using a property replacer option * and/or a global configuration option. However, we should wait * on user requests for this feature before doing anything. * rgerhards, 2007-06-26 */ assert(ts != NULL); assert(pBuf != NULL); pBuf[0] = (ts->year / 1000) % 10 + '0'; pBuf[1] = (ts->year / 100) % 10 + '0'; pBuf[2] = (ts->year / 10) % 10 + '0'; pBuf[3] = ts->year % 10 + '0'; pBuf[4] = (ts->month / 10) % 10 + '0'; pBuf[5] = ts->month % 10 + '0'; pBuf[6] = (ts->day / 10) % 10 + '0'; pBuf[7] = ts->day % 10 + '0'; pBuf[8] = (ts->hour / 10) % 10 + '0'; pBuf[9] = ts->hour % 10 + '0'; pBuf[10] = (ts->minute / 10) % 10 + '0'; pBuf[11] = ts->minute % 10 + '0'; pBuf[12] = (ts->second / 10) % 10 + '0'; pBuf[13] = ts->second % 10 + '0'; pBuf[14] = '\0'; return 15; } int formatTimestampToPgSQL(struct syslogTime *ts, char *pBuf) { /* see note in formatTimestampToMySQL, applies here as well */ assert(ts != NULL); assert(pBuf != NULL); pBuf[0] = (ts->year / 1000) % 10 + '0'; pBuf[1] = (ts->year / 100) % 10 + '0'; pBuf[2] = (ts->year / 10) % 10 + '0'; pBuf[3] = ts->year % 10 + '0'; pBuf[4] = '-'; pBuf[5] = (ts->month / 10) % 10 + '0'; pBuf[6] = ts->month % 10 + '0'; pBuf[7] = '-'; pBuf[8] = (ts->day / 10) % 10 + '0'; pBuf[9] = ts->day % 10 + '0'; pBuf[10] = ' '; pBuf[11] = (ts->hour / 10) % 10 + '0'; pBuf[12] = ts->hour % 10 + '0'; pBuf[13] = ':'; pBuf[14] = (ts->minute / 10) % 10 + '0'; pBuf[15] = ts->minute % 10 + '0'; pBuf[16] = ':'; pBuf[17] = (ts->second / 10) % 10 + '0'; pBuf[18] = ts->second % 10 + '0'; pBuf[19] = '\0'; return 19; } /** * Format a syslogTimestamp to just the fractional seconds. * The caller must provide the timestamp as well as a character * buffer that will receive the resulting string. The function * returns the size of the timestamp written in bytes (without * the string terminator). If 0 is returend, an error occured. * The buffer must be at least 7 bytes large. * rgerhards, 2008-06-06 */ int formatTimestampSecFrac(struct syslogTime *ts, char* pBuf) { int iBuf; int power; int secfrac; short digit; assert(ts != NULL); assert(pBuf != NULL); iBuf = 0; if(ts->secfracPrecision > 0) { power = tenPowers[(ts->secfracPrecision - 1) % 6]; secfrac = ts->secfrac; while(power > 0) { digit = secfrac / power; secfrac -= digit * power; power /= 10; pBuf[iBuf++] = digit + '0'; } } else { pBuf[iBuf++] = '0'; } pBuf[iBuf] = '\0'; return iBuf; } /** * Format a syslogTimestamp to a RFC3339 timestamp string (as * specified in syslog-protocol). * The caller must provide the timestamp as well as a character * buffer that will receive the resulting string. The function * returns the size of the timestamp written in bytes (without * the string terminator). If 0 is returend, an error occured. */ int formatTimestamp3339(struct syslogTime *ts, char* pBuf) { int iBuf; int power; int secfrac; short digit; BEGINfunc assert(ts != NULL); assert(pBuf != NULL); /* start with fixed parts */ /* year yyyy */ pBuf[0] = (ts->year / 1000) % 10 + '0'; pBuf[1] = (ts->year / 100) % 10 + '0'; pBuf[2] = (ts->year / 10) % 10 + '0'; pBuf[3] = ts->year % 10 + '0'; pBuf[4] = '-'; /* month */ pBuf[5] = (ts->month / 10) % 10 + '0'; pBuf[6] = ts->month % 10 + '0'; pBuf[7] = '-'; /* day */ pBuf[8] = (ts->day / 10) % 10 + '0'; pBuf[9] = ts->day % 10 + '0'; pBuf[10] = 'T'; /* hour */ pBuf[11] = (ts->hour / 10) % 10 + '0'; pBuf[12] = ts->hour % 10 + '0'; pBuf[13] = ':'; /* minute */ pBuf[14] = (ts->minute / 10) % 10 + '0'; pBuf[15] = ts->minute % 10 + '0'; pBuf[16] = ':'; /* second */ pBuf[17] = (ts->second / 10) % 10 + '0'; pBuf[18] = ts->second % 10 + '0'; iBuf = 19; /* points to next free entry, now it becomes dynamic! */ if(ts->secfracPrecision > 0) { pBuf[iBuf++] = '.'; power = tenPowers[(ts->secfracPrecision - 1) % 6]; secfrac = ts->secfrac; while(power > 0) { digit = secfrac / power; secfrac -= digit * power; power /= 10; pBuf[iBuf++] = digit + '0'; } } if(ts->OffsetMode == 'Z') { pBuf[iBuf++] = 'Z'; } else { pBuf[iBuf++] = ts->OffsetMode; pBuf[iBuf++] = (ts->OffsetHour / 10) % 10 + '0'; pBuf[iBuf++] = ts->OffsetHour % 10 + '0'; pBuf[iBuf++] = ':'; pBuf[iBuf++] = (ts->OffsetMinute / 10) % 10 + '0'; pBuf[iBuf++] = ts->OffsetMinute % 10 + '0'; } pBuf[iBuf] = '\0'; ENDfunc return iBuf; } /** * Format a syslogTimestamp to a RFC3164 timestamp sring. * The caller must provide the timestamp as well as a character * buffer that will receive the resulting string. The function * returns the size of the timestamp written in bytes (without * the string termnator). If 0 is returend, an error occured. * rgerhards, 2010-03-05: Added support to for buggy 3164 dates, * where a zero-digit is written instead of a space for the first * day character if day < 10. syslog-ng seems to do that, and some * parsing scripts (in migration cases) rely on that. */ int formatTimestamp3164(struct syslogTime *ts, char* pBuf, int bBuggyDay) { static char* monthNames[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; int iDay; assert(ts != NULL); assert(pBuf != NULL); pBuf[0] = monthNames[(ts->month - 1)% 12][0]; pBuf[1] = monthNames[(ts->month - 1) % 12][1]; pBuf[2] = monthNames[(ts->month - 1) % 12][2]; pBuf[3] = ' '; iDay = (ts->day / 10) % 10; /* we need to write a space if the first digit is 0 */ pBuf[4] = (bBuggyDay || iDay > 0) ? iDay + '0' : ' '; pBuf[5] = ts->day % 10 + '0'; pBuf[6] = ' '; pBuf[7] = (ts->hour / 10) % 10 + '0'; pBuf[8] = ts->hour % 10 + '0'; pBuf[9] = ':'; pBuf[10] = (ts->minute / 10) % 10 + '0'; pBuf[11] = ts->minute % 10 + '0'; pBuf[12] = ':'; pBuf[13] = (ts->second / 10) % 10 + '0'; pBuf[14] = ts->second % 10 + '0'; pBuf[15] = '\0'; return 16; /* traditional: number of bytes written */ } /* queryInterface function * rgerhards, 2008-03-05 */ BEGINobjQueryInterface(datetime) CODESTARTobjQueryInterface(datetime) if(pIf->ifVersion != datetimeCURR_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->getCurrTime = getCurrTime; pIf->GetTime = getTime; pIf->ParseTIMESTAMP3339 = ParseTIMESTAMP3339; pIf->ParseTIMESTAMP3164 = ParseTIMESTAMP3164; pIf->formatTimestampToMySQL = formatTimestampToMySQL; pIf->formatTimestampToPgSQL = formatTimestampToPgSQL; pIf->formatTimestampSecFrac = formatTimestampSecFrac; pIf->formatTimestamp3339 = formatTimestamp3339; pIf->formatTimestamp3164 = formatTimestamp3164; finalize_it: ENDobjQueryInterface(datetime) /* Initialize the datetime class. Must be called as the very first method * before anything else is called inside this class. * rgerhards, 2008-02-19 */ BEGINAbstractObjClassInit(datetime, 1, OBJ_IS_CORE_MODULE) /* class, version */ /* request objects we use */ CHKiRet(objUse(errmsg, CORE_COMPONENT)); ENDObjClassInit(datetime) /* vi:set ai: */