summaryrefslogtreecommitdiffstats
path: root/common/eurephia_log.c
blob: 80633213a5edbc0a19d6fbc8d4a3125b7075d3c4 (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
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
/*  eurephia_log.c  --  eurephia logging
 *
 *  GPLv2 only - Copyright (C) 2008 - 2012
 *               David Sommerseth <dazo@users.sourceforge.net>
 *
 *  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; version 2
 *  of the License.
 *
 *  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, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */

/**
 * @file   eurephia_log.c
 * @author David Sommerseth <dazo@users.sourceforge.net>
 * @date   2008-08-06
 *
 * @brief  Function for unified logging
 *
 */


#include <stdio.h>
#include <stdarg.h>
#include <syslog.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <assert.h>

#include <eurephia_context.h>
#include <eurephia_log.h>
#include <eurephia_nullsafe.h>

/**
 * Maps eurephia log types (priorities) to string values
 *
 * @param prio eurephia logtype, such as LOG_INFO, LOG_DEBUG, LOG_FATAL, etc
 *
 * @return String containing the log type/prio
 */
static inline const char *logprio_str(int prio) {
        switch( prio ) {
        case LOG_INFO:
                return "-- INFO --      \0";
        case LOG_DEBUG:
                return "-- DEBUG --     \0";
        case LOG_WARNING:
                return "** WARNING **   \0";
        case LOG_ERROR:
                return "** ERROR **     \0";
        case LOG_CRITICAL:
                return "** CRITICAL **  \0";
        case LOG_FATAL:
                return "** - FATAL - ** \0";
        case LOG_PANIC:
                return "** * PANIC * ** \0";
        default:
                return "[[ UNKNOWN ]]\0";
        }
}


/**
 *  Mapping table for eurephia log types to syslog log types
 */
static const int syslog_priority[] = {
        -1,
        LOG_INFO,       /**< LOG_INFO     */
        LOG_DEBUG,      /**< LOG_DEBUG    */
        LOG_WARNING,    /**< LOG_WARNING  */
        LOG_ERR,        /**< LOG_ERROR    */
        LOG_CRIT,       /**< LOG_CRITICAL */
        LOG_ALERT,      /**< LOG_FATAL    */
        LOG_EMERG       /**< LOG_PANIC    */
};


/**
 * Converts eurephiaLOGTYPE value to a string
 *
 * @param lt eurephiaLOGTYPE, must be either logFILE or logSYSLOG
 *
 * @return Returns a static string with log type
 */
static inline const char *logtype_str(eurephiaLOGTYPE lt) {
        switch( lt ) {
        case logFILE:
                return "file\0";
        case logSYSLOG:
                return "syslog\0";
        }
        return NULL;
}


/**
 *  Converts a string of log destination/facilities to syslog values
 *
 * @param dest String containing the log destination
 *
 * @return Returns a syslog compatible value, such as LOG_AUTHPRIV, LOG_LOCAL{0-7}, LOG_USER, etc.
 *         Unknown types will be set to syslog default, LOG_USER.
 *
 * @remark The following list will list up facilities which will be ignored and LOG_USER will be used
 *         instead: LOG_CRON, LOG_FTP, LOG_KERN, LOG_LPR, LOG_MAIL, LOG_NEWS, LOG_SYSLOG, LOG_UUCP.
 *
 */
static const int syslog_logdest(const char *dest) {
        if( dest == NULL ) {
                return LOG_USER;
        }

        if( strcasecmp(dest, "auth") == 0 ) {
                return LOG_AUTHPRIV;  // LOG_AUTH is deprecated, and LOG_AUTHPRIV should be used instead
        } else if( strcasecmp(dest, "authpriv") == 0 ) {
                return LOG_AUTHPRIV;
        } else if( strcasecmp(dest, "daemon") == 0 ) {
                return LOG_DAEMON;
        } else if( strcasecmp(dest, "local0") == 0 ) {
                return LOG_LOCAL0;
        } else if( strcasecmp(dest, "local1") == 0 ) {
                return LOG_LOCAL1;
        } else if( strcasecmp(dest, "local2") == 0 ) {
                return LOG_LOCAL2;
        } else if( strcasecmp(dest, "local3") == 0 ) {
                return LOG_LOCAL3;
        } else if( strcasecmp(dest, "local4") == 0 ) {
                return LOG_LOCAL4;
        } else if( strcasecmp(dest, "local5") == 0 ) {
                return LOG_LOCAL5;
        } else if( strcasecmp(dest, "local6") == 0 ) {
                return LOG_LOCAL6;
        } else if( strcasecmp(dest, "local7") == 0 ) {
                return LOG_LOCAL7;
        } else {
                return LOG_USER;
        }
}


/**
 * POSIX Mutex to avoid simultaneously logging activity from
 * several threads at the same time
 */
pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;


/**
 * Simple function for logging entries to a file with timestamp
 *
 * @param log     FILE pointer to a log file
 * @param logdst  Log destiation/priority
 * @param loglvl  Log level of the message
 * @param file String containing file name of the place this function was called.  Usually the __FILE__ macro.
 * @param line Line number of the source file this function was called.  Usually the __LINE__ macro.
 * @param fmt     stdarg, format string
 * @param ap      stdarg va_list, prepared by va_start()
 */
static void file_log(FILE *log, int logdst, int loglvl, const char *file, const int line,
                     const char *fmt, va_list ap)
{
        char tstmp_str[200];
        time_t tstmp;
        struct tm *loctstmp;

        if( log == NULL ) {
                return;
        }

        // Get time stamp
        memset(&tstmp_str, 0, 200);
        tstmp = time(NULL);
        loctstmp = localtime(&tstmp);
        if( loctstmp != NULL ) {
                if( strftime(tstmp_str, 198, "%Y-%m-%d %H:%M:%S %Z", loctstmp) == 0 ) {
                        snprintf(tstmp_str, 198, "(error getting time stamp string)");
                }
        } else {
                snprintf(tstmp_str, 198, "(error getting timestamp)");
        }

        // Do the logging
        pthread_mutex_lock(&log_mutex); // Block other threads from writing when we write
#ifdef ENABLE_DEBUG
        fprintf(log, "[%s] %s [%i] {%s:%i} ", tstmp_str, logprio_str(logdst), loglvl, file, line);
#else
        fprintf(log, "[%s] %s [%i] ", tstmp_str, logprio_str(logdst), loglvl);
#endif
        vfprintf(log, fmt, ap);
        fprintf(log, "\n");
        fflush(log);
        pthread_mutex_unlock(&log_mutex); // Unblock other threads
}


/**
 * Internal function.  This function should normally be called via the veurephia_log() function.
 *
 * @param ctx    eurephiaCTX
 * @param logdst Log destination, can be LOG_INFO, LOG_DEBUG, LOG_WARNING, LOG_ERROR, 
 *        LOG_CRITICAL, LOG_FATAL or LOG_PANIC
 * @param loglvl Log level of the message.  If the eurephiaCTX has a lower log level setup
 *        than what this parameter is set to, the message will not be logged.
 * @param file String containing file name of the place this function was called.  Usually the __FILE__ macro.
 * @param line Line number of the source file this function was called.  Usually the __LINE__ macro.
 * @param ap   stdarg's va_list data
 * @param fmt  stdarg's char *fmt pointer
 */
void _veurephia_log_func(eurephiaCTX *ctx, int logdst, int loglvl, const char *file, const int line,
                         va_list ap, const char *fmt)
{
        // Only log if we have an open log file and which has high enough log level
        if( (ctx != NULL) && (ctx->log != NULL) && (ctx->log->opened == 1)
            && (ctx->log->loglevel >= loglvl) ) {
                switch( ctx->log->logtype ) {
                case logFILE:
                        file_log(ctx->log->logfile, logdst, loglvl, file, line, fmt, ap);
                        break;
                case logSYSLOG:
                        vsyslog(syslog_priority[logdst], fmt, ap);
                        break;
                }
        }
}


/**
 * Closes an eurephia log context
 *
 * @param ctx eurephiaCTX containing the log context to be closed
 *
 */
void eurephia_log_close(eurephiaCTX *ctx) {
        if( (ctx == NULL) || (ctx->log == NULL) ) {
                return;
        }

        eurephia_log(ctx, LOG_INFO, 2, "Closing %s logging (%s).",
                     logtype_str(ctx->log->logtype), ctx->log->destination);
        if( ctx->log->opened == 1 ) {
                switch( ctx->log->logtype ) {
                case logFILE:
                        if( ctx->log->logfile != NULL ) {
                                fflush(ctx->log->logfile);
                                fclose(ctx->log->logfile);
                        }
                        ctx->log->logfile = NULL;
                        break;

                case logSYSLOG:
                        closelog();
                        break;
                }
                ctx->log->opened = 0;
        }
        free_nullsafe(ctx, ctx->log->destination);
        free_nullsafe(ctx, ctx->log);
}

/**
 * Initialises and preapres the log device
 *
 * @param ctx      eurephiaCTX where the log context will be associated
 * @param ident    Log ident.  Used when logging to syslog primarily, to identify log entries in syslog logs
 * @param dest     Log destination.  For can be a file name, "syslog:", "stdout:", "stderr:" or "none:".
 *                 If it is "syslog:" it must continue with a string describing log facility such as
 *                 "syslog:authpriv", "syslog:local0", "syslog:local1", "syslog:user", etc.  If the
 *                 facility is unknown, it will default to "user"
 * @param loglevel Sets the verbosity level for the log file.  The higher number, the more information
 *                 will be logged.
 *
 * @return Returns 1 on success, otherwise 0;
 */
int eurephia_log_init(eurephiaCTX *ctx, const char *ident, const char *dest, int loglevel) {

        assert( (ctx != NULL) && (dest != NULL) );

        // Create log context
        ctx->log = (eurephiaLOG *) malloc_nullsafe(ctx, sizeof(eurephiaLOG)+2);
        if( ctx->log == NULL ) {
                return 0;
        }

        if( strncmp(dest, "syslog:", 7) == 0 ) {
                ctx->log->logtype = logSYSLOG;
                ctx->log->destination = strdup(dest+7);
        } else {
                ctx->log->logtype = logFILE;
                ctx->log->destination = strdup(dest);
        }
        if( ctx->log->destination == NULL) {
                free_nullsafe(ctx, ctx->log);
                return 0;
        }
        ctx->log->loglevel = loglevel;

        switch( ctx->log->logtype ) {
        case logFILE: // Open log file
                if( strcmp(dest, "stdout:") == 0 ) {
                        ctx->log->logfile = stdout;
                } else if( strcmp(dest, "stderr:") == 0 ) {
                        ctx->log->logfile = stderr;
                } else if( strcmp(dest, "none:") == 0 ) {
                        ctx->log->logfile = NULL;
                        ctx->log->loglevel = 0;
                        ctx->log->opened = 0;
                        return 1;
                } else if( (ctx->log->logfile = fopen(dest, "aw")) == NULL ) {
                        fprintf(stderr, "ERROR: Could not open log file: %s\n", ctx->log->destination);
                        free_nullsafe(ctx, ctx->log->destination);
                        free_nullsafe(ctx, ctx->log);
                        return 0;
                }
                break;

        case logSYSLOG: // Open syslog
                openlog(ident, LOG_PID, syslog_logdest(ctx->log->destination));
                break;
        }
        ctx->log->opened = 1;
        eurephia_log(ctx, LOG_INFO, 1, "Logging to %s (%s) started",
                     logtype_str(ctx->log->logtype), ctx->log->destination);
        return 1;
}