Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_log.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
26#include "nilorea/n_common.h"
27#include "nilorea/n_log.h"
28
29#include <pthread.h>
30#include <string.h>
31#include <inttypes.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <fcntl.h>
35
36#ifndef __windows__
37#include <syslog.h>
38#else
39#include <time.h>
40#include "nilorea/n_windows.h"
41#endif
42
44
45#define COLOR_RESET "\033[0m"
46#define COLOR_RED "\033[31m"
47#define COLOR_GREEN "\033[32m"
48#define COLOR_YELLOW "\033[33m"
49#define COLOR_BLUE "\033[34m"
50#define COLOR_MAGENTA "\033[35m"
51#define COLOR_CYAN "\033[36m"
52#define COLOR_WHITE "\033[37m"
53
55typedef struct LOG_LEVELS {
57 char* c_name;
58 int c_val; // cppcheck-suppress unusedStructMember -- used by downstream log consumers
59 char* w_name; // cppcheck-suppress unusedStructMember -- used by downstream log consumers
60
62
65 {
66 {"EMERG", LOG_EMERG, "ERROR"},
67 {"ALERT", LOG_ALERT, "ERROR"},
68 {"CRITICAL", LOG_CRIT, "ERROR"},
69 {"ERR", LOG_ERR, "ERROR"},
70 {"WARNING", LOG_WARNING, "WARNING"},
71 {"NOTICE", LOG_NOTICE, "SUCCESS"},
72 {"INFO", LOG_INFO, "INFORMATION"},
73 {"DEBUG", LOG_DEBUG, "INFORMATION"},
74 {NULL, -1, NULL}};
75
77static int LOG_LEVEL = LOG_NULL;
78
80static int LOG_TYPE = LOG_STDERR;
81
83static FILE* log_file = NULL;
84
86static char* proc_name = NULL;
87
93char* open_sysjrnl(const char* identity) {
94 __n_assert(identity, return NULL);
95#ifndef __windows__
96 /* LOG_CONS: log to console if no syslog available
97 * LOG_PID: add pid of calling process to log
98 * Local use 7 : compat with older logging systems
99 */
100 openlog(identity, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL7);
101#endif
102 proc_name = strdup(identity);
103 return proc_name;
104} /* open_sysjrnl */
105
109void close_sysjrnl(void) {
110#ifndef __windows__
111 closelog();
112#endif
114} /* close_sysjrnl */
115
120void set_log_level(const int log_level) {
121 if (log_level == LOG_FILE) {
123 return;
124 }
125 if (log_level == LOG_STDERR) {
127 return;
128 }
129 if (log_level == LOG_SYSJRNL) {
131 return;
132 }
134#ifdef __windows__
135 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
136 if (hOut != INVALID_HANDLE_VALUE) {
137 DWORD dwMode = 0;
138 if (GetConsoleMode(hOut, &dwMode)) {
139 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
140 if (SetConsoleMode(hOut, dwMode)) {
141 // color support is enabled
143 }
144 }
145 }
146#else
147 // Probably a terminal, may support colors
148 if (isatty(fileno(stdout))) {
150 }
151#endif
152} /* set_log_level() */
153
158int get_log_level(void) {
159 return LOG_LEVEL;
160} /* get_log_level() */
161
167int set_log_file(char* file) {
168 __n_assert(file, return FALSE);
169
170 if (log_file) {
171 fclose(log_file);
172 log_file = NULL;
173 }
174
175 int fd = open(file, O_CREAT | O_APPEND | O_WRONLY, 0600); // rw------- permissions
176 if (fd == -1) {
177 n_log(LOG_ERR, "couldn't create %s with 0600 permission", _str(file));
178 return FALSE;
179 }
180
181 log_file = fdopen(fd, "a");
182 if (!log_file) {
183 close(fd);
184 n_log(LOG_ERR, "fdopen returned an invalid file descriptor pointer for %s:%d", _str(file), fd);
185 return FALSE;
186 }
187
189
190 return TRUE;
191} /* set_log_file */
192
197FILE* get_log_file(void) {
198 return log_file;
199} /*get_log_level() */
200
201#ifndef _vscprintf
208int _vscprintf_so(const char* format, va_list pargs) {
209 int retval;
210 va_list argcopy;
211 va_copy(argcopy, pargs);
212 retval = vsnprintf(NULL, 0, format, argcopy);
213 va_end(argcopy);
214 return retval;
215}
216#endif
217
218#ifndef vasprintf
226int vasprintf(char** strp, const char* fmt, va_list ap) {
227 long long int len = _vscprintf_so(fmt, ap);
228 if (len == -1)
229 return -1;
230 char* str = NULL;
231 Malloc(str, char, (size_t)len + 1 + sizeof(void*)); // len + EndOfString + padding
232 if (!str)
233 return -1;
234 int r = vsnprintf(str, (size_t)(len + 1), fmt, ap); /* "secure" version of vsprintf */
235 if (r == -1)
236 return free(str), -1;
237 *strp = str;
238 return r;
239}
240#endif
241
242#ifndef asprintf
250int asprintf(char* strp[], const char* fmt, ...) {
251 va_list ap;
252 va_start(ap, fmt);
253 int r = vasprintf(strp, fmt, ap);
254 va_end(ap);
255 return r;
256}
257#endif
258
267void _n_log(int level, const char* file, const char* func, int line, const char* format, ...) {
268 FILE* out = NULL;
269
270 if (level == LOG_NULL)
271 return;
272
273 if (!log_file)
274 out = stderr;
275 else
276 out = log_file;
277
278 int log_level = get_log_level();
279
280 if (level <= log_level) {
281 va_list args;
282 char* syslogbuffer = NULL;
283 char* eventbuffer = NULL;
284 char* syslogsafebuffer = NULL;
285#ifdef __windows__
286 size_t needed = 0;
287 const char* name = "NULL";
288 if (proc_name)
289 name = proc_name;
290#endif
291
292 const char* color = "";
293 const char* color_reset = "";
294
296 color_reset = COLOR_RESET;
297 switch (level) {
298 case LOG_EMERG:
299 color = COLOR_RED;
300 break;
301 case LOG_ALERT:
302 color = COLOR_MAGENTA;
303 break;
304 case LOG_CRIT:
305 color = COLOR_RED;
306 break;
307 case LOG_ERR:
308 color = COLOR_RED;
309 break;
310 case LOG_WARNING:
311 color = COLOR_YELLOW;
312 break;
313 case LOG_NOTICE:
314 color = COLOR_GREEN;
315 break;
316 case LOG_INFO:
317 color = COLOR_CYAN;
318 break;
319 case LOG_DEBUG:
320 color = COLOR_BLUE;
321 break;
322 default:
323 color = COLOR_WHITE;
324 break;
325 }
326 }
327 switch (LOG_TYPE) {
328 case LOG_SYSJRNL:
329 va_start(args, format);
330 if (vasprintf(&syslogbuffer, format, args) == -1) {
331 syslogbuffer = NULL;
332 }
333 va_end(args);
334 // Fallback if vasprintf is NULL */
335 syslogsafebuffer = syslogbuffer ? syslogbuffer : "<log: vasprintf failed>";
336#ifdef __windows__
337 /* Sanitize syslog content to prevent command injection via system().
338 * Only mutate when syslogbuffer is non-NULL: syslogsafebuffer then
339 * points to heap memory. When syslogbuffer is NULL it points to a
340 * read-only string literal and must not be written to. */
341 if (syslogbuffer) {
342 for (char* p = syslogsafebuffer; *p; p++) {
343 if (*p == '"' || *p == '&' || *p == '|' || *p == '<' || *p == '>' || *p == '^') *p = '_';
344 }
345 }
346 int snprintf_ret = snprintf(NULL, 0, "start /B EventCreate /t %s /id 666 /l APPLICATION /so %s /d \"%s\" > NUL 2>&1", prioritynames[level].w_name, name, syslogsafebuffer);
347 if (snprintf_ret > 0) {
348 needed = (unsigned long long)snprintf_ret;
349 Malloc(eventbuffer, char, needed + 4);
350 if (eventbuffer) {
351 snprintf(eventbuffer, needed + 4, "start /B EventCreate /t %s /id 666 /l APPLICATION /so %s /d \"%s\" > NUL 2>&1", prioritynames[level].w_name, name, syslogsafebuffer);
352 system(eventbuffer);
353 }
354 }
355#else
356 syslog(level, "%s->%s:%d %s", _str(file), _str(func), line, syslogsafebuffer);
357#endif
358 FreeNoLog(syslogbuffer);
359 FreeNoLog(eventbuffer);
360 break;
361 default:
362 fprintf(out, "%s%s:%jd:%s->%s:%d ", color, prioritynames[level].c_name, (intmax_t)time(NULL), _str(file), _str(func), line);
363 if (format) {
364 va_start(args, format);
365 vfprintf(out, format, args);
366 va_end(args);
367 }
368 fprintf(out, "%s\n", color_reset);
369 break;
370 }
371 fflush(out);
372 }
373} /* _n_log( ... ) */
374
382int open_safe_logging(TS_LOG** log, char* pathname, char* opt) {
383 if (!log) {
384 n_log(LOG_ERR, "Invalid log pointer (NULL)");
385 return FALSE;
386 }
387
388 if (*log) {
389 if ((*log)->file) {
390 n_log(LOG_ERR, "Log file '%s' already open", _str(pathname));
391 return -1000; // already open
392 }
393 n_log(LOG_ERR, "Log struct already allocated without file");
394 return FALSE;
395 }
396
397 if (!pathname || !opt) {
398 n_log(LOG_ERR, "Invalid pathname or mode (pathname=%s, opt=%s)", _str(pathname), _str(opt));
399 return FALSE;
400 }
401
402 Malloc((*log), TS_LOG, 1);
403 if (!(*log)) {
404 n_log(LOG_ERR, "Failed to allocate TS_LOG structure");
405 return FALSE;
406 }
407
408 pthread_mutex_init(&(*log)->LOG_MUTEX, NULL);
409
410 int flags = 0;
411 if (strcmp(opt, "a") == 0 || strcmp(opt, "a+") == 0) {
412 flags = O_CREAT | O_APPEND | O_WRONLY;
413 } else if (strcmp(opt, "w") == 0 || strcmp(opt, "w+") == 0) {
414 flags = O_CREAT | O_TRUNC | O_WRONLY;
415 } else {
416 n_log(LOG_ERR, "Unsupported open mode '%s' for pathname '%s'", _str(opt), _str(pathname));
417 pthread_mutex_destroy(&(*log)->LOG_MUTEX);
418 Free(*log);
419 *log = NULL;
420 return FALSE;
421 }
422
423 int fd = open(pathname, flags, 0600);
424 if (fd == -1) {
425 n_log(LOG_ERR, "Failed to open '%s' with 0600 permissions", _str(pathname));
426 pthread_mutex_destroy(&(*log)->LOG_MUTEX);
427 Free(*log);
428 *log = NULL;
429 return FALSE;
430 }
431
432 (*log)->file = fdopen(fd, opt);
433 if (!(*log)->file) {
434 n_log(LOG_ERR, "fdopen failed for '%s' (fd=%d, mode='%s')", _str(pathname), fd, _str(opt));
435 close(fd);
436 pthread_mutex_destroy(&(*log)->LOG_MUTEX);
437 Free(*log);
438 *log = NULL;
439 return FALSE;
440 }
441
442 return TRUE;
443} /* open_safe_logging */
444
451int write_safe_log(TS_LOG* log, char* pat, ...) {
452 /* argument list */
453 va_list arg;
454 char str[2048] = "";
455
456 if (!log)
457 return FALSE;
458
459 va_start(arg, pat);
460
461 vsnprintf(str, sizeof(str), pat, arg);
462
463 va_end(arg);
464
465 pthread_mutex_lock(&log->LOG_MUTEX);
466 fprintf(log->file, "%s", str);
467 fflush(log->file);
468 pthread_mutex_unlock(&log->LOG_MUTEX);
469
470 return TRUE;
471
472} /* write_safe_log( ... ) */
473
480 if (!log)
481 return FALSE;
482
483 pthread_mutex_lock(&log->LOG_MUTEX);
484 fflush(log->file);
485 pthread_mutex_unlock(&log->LOG_MUTEX);
486
487 pthread_mutex_destroy(&log->LOG_MUTEX);
488
489 fclose(log->file);
490
491 Free(log);
492
493 return TRUE;
494
495} /* close_safe_logging( ...) */
int log_level
Definition ex_fluid.c:61
#define FreeNoLog(__ptr)
Free Handler without log.
Definition n_common.h:271
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:203
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:278
#define _str(__PTR)
define true
Definition n_common.h:192
#define Free(__ptr)
Free Handler to get errors.
Definition n_common.h:262
pthread_mutex_t LOG_MUTEX
mutex for thread-safe writting
Definition n_log.h:96
FILE * file
File handler.
Definition n_log.h:98
#define LOG_ALERT
action must be taken immediately
Definition n_log.h:71
#define LOG_SYSJRNL
to sysjrnl
Definition n_log.h:51
FILE * get_log_file(void)
return the current log_file
Definition n_log.c:197
int write_safe_log(TS_LOG *log, char *pat,...)
write to a thread-safe logging file
Definition n_log.c:451
int open_safe_logging(TS_LOG **log, char *pathname, char *opt)
Open a thread-safe logging file.
Definition n_log.c:382
#define LOG_EMERG
system is unusable
Definition n_log.h:69
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:88
#define LOG_FILE
internal, logging to file
Definition n_log.h:47
void close_sysjrnl(void)
Close syslog connection or clean internals for event log.
Definition n_log.c:109
#define LOG_DEBUG
debug-level messages
Definition n_log.h:83
#define LOG_ERR
error conditions
Definition n_log.h:75
char * open_sysjrnl(const char *identity)
Open connection to syslog or create internals for event log.
Definition n_log.c:93
#define LOG_STDERR
internal, default LOG_TYPE
Definition n_log.h:49
#define LOG_CRIT
critical conditions
Definition n_log.h:73
int close_safe_logging(TS_LOG *log)
close a thread-safe logging file
Definition n_log.c:479
int set_log_file(char *file)
Set the logging to a file instead of stderr.
Definition n_log.c:167
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
Definition n_log.c:120
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:79
#define LOG_WARNING
warning conditions
Definition n_log.h:77
#define LOG_NULL
no log output
Definition n_log.h:45
#define LOG_INFO
informational
Definition n_log.h:81
void _n_log(int level, const char *file, const char *func, int line, const char *format,...)
Logging function.
Definition n_log.c:267
int get_log_level(void)
Get the global log level value.
Definition n_log.c:158
ThreadSafe LOGging structure.
Definition n_log.h:94
Common headers and low-level functions & define.
#define COLOR_RESET
Definition n_log.c:45
static LOG_LEVELS prioritynames[]
array of log levels
Definition n_log.c:64
#define COLOR_BLUE
Definition n_log.c:49
static char * proc_name
static proc name, for windows event log
Definition n_log.c:86
int c_val
Definition n_log.c:58
static int LOG_LEVEL
static global maximum wanted log level value
Definition n_log.c:77
#define COLOR_YELLOW
Definition n_log.c:48
static int LOG_TYPE
static global logging type ( STDERR, FILE, SYSJRNL )
Definition n_log.c:80
int vasprintf(char **strp, const char *fmt, va_list ap)
snprintf from a va_list, helper for asprintf
Definition n_log.c:226
int asprintf(char *strp[], const char *fmt,...)
snprintf from a va_list
Definition n_log.c:250
static int terminal_support_colors
Definition n_log.c:43
#define COLOR_CYAN
Definition n_log.c:51
#define COLOR_MAGENTA
Definition n_log.c:50
#define COLOR_WHITE
Definition n_log.c:52
static FILE * log_file
static FILE handling if logging to file is enabled
Definition n_log.c:83
char * w_name
Definition n_log.c:59
char * c_name
string of log type
Definition n_log.c:57
#define COLOR_RED
Definition n_log.c:46
#define COLOR_GREEN
Definition n_log.c:47
int _vscprintf_so(const char *format, va_list pargs)
compute the size of a string made with format 'fmt' and arguments in the va_list 'ap',...
Definition n_log.c:208
internal struct to handle log types
Definition n_log.c:55
Generic log system.