1 /*
2  * AIDE (Advanced Intrusion Detection Environment)
3  *
4  * Copyright (C) 2020 Hannes von Haugwitz
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 
26 #include "log.h"
27 #include "locale-aide.h"
28 
29 LOG_LEVEL prev_log_level = LOG_LEVEL_UNSET;
30 LOG_LEVEL log_level = LOG_LEVEL_UNSET;
31 
32 typedef struct log_cache {
33     LOG_LEVEL level;
34     char *message;
35 } log_cache;
36 
37 log_cache *cached_lines = NULL;
38 int ncachedlines = 0;
39 
40 struct log_level {
41     LOG_LEVEL log_level;
42     const char *name;
43     const char *log_string;
44 };
45 
46 static struct log_level log_level_array[] = {
47     { LOG_LEVEL_ERROR,         "error",         "  ERROR" },
48     { LOG_LEVEL_WARNING,       "warning",       "WARNING" },
49     { LOG_LEVEL_NOTICE,        "notice",        " NOTICE" },
50     { LOG_LEVEL_INFO,          "info",          "   INFO" },
51     { LOG_LEVEL_RULE,          "rule",          "   RULE" },
52     { LOG_LEVEL_CONFIG,        "config",        " CONFIG" },
53     { LOG_LEVEL_DEBUG,         "debug",         "  DEBUG" },
54     { LOG_LEVEL_TRACE,         "trace",         "  TRACE" },
55     { 0,                       NULL,            NULL      },
56 };
57 
cache_line(LOG_LEVEL level,const char * format,va_list ap)58 static void cache_line(LOG_LEVEL level, const char* format, va_list ap) {
59     int n;
60 
61     void * tmp = realloc(cached_lines, (ncachedlines+1) * sizeof(log_cache)); /* freed in log_cached_lines() */
62     if (tmp == NULL) {
63         log_msg(LOG_LEVEL_ERROR, "realloc() failed: %s", strerror(errno));
64         exit(EXIT_FAILURE);
65     } else {
66         cached_lines = tmp;
67     }
68 
69     cached_lines[ncachedlines].level = level;
70     cached_lines[ncachedlines].message = NULL;
71 
72     va_list aq;
73     va_copy(aq, ap);
74     n = vsnprintf(NULL, 0, format, aq) + 1;
75     va_end(aq);
76 
77     cached_lines[ncachedlines].message = malloc(n * sizeof(char)); /* freed in log_cached_lines() */
78     vsnprintf(cached_lines[ncachedlines].message, n, format, ap);
79     ncachedlines++;
80 }
81 
get_log_level_name(LOG_LEVEL level)82 const char * get_log_level_name(LOG_LEVEL level) {
83     return level?log_level_array[level-1].name:NULL;
84 }
85 
log_cached_lines(void)86 static void log_cached_lines(void) {
87     for(int i = 0; i < ncachedlines; ++i) {
88         log_msg(cached_lines[i].level, "%s", cached_lines[i].message);
89         free(cached_lines[i].message);
90     }
91     ncachedlines = 0;
92     free(cached_lines);
93 }
94 
vlog_msg(LOG_LEVEL level,const char * format,va_list ap)95 static void vlog_msg(LOG_LEVEL level,const char* format, va_list ap) {
96     FILE* url = stderr;
97 
98     if (level == LOG_LEVEL_ERROR || level <= log_level) {
99         fprintf(url, "%s: ", log_level_array[level-1].log_string );
100         vfprintf(url, format, ap);
101         fprintf(url, "\n");
102     } else if (log_level == LOG_LEVEL_UNSET) {
103         cache_line(level, format, ap);
104     }
105 }
106 
is_log_level_unset()107 bool is_log_level_unset() {
108     return log_level == LOG_LEVEL_UNSET;
109 }
110 
get_log_level_from_string(char * val)111 LOG_LEVEL get_log_level_from_string(char* val) {
112     struct log_level *level;
113 
114     for (level = log_level_array; level->log_level != 0; level++) {
115         if (strcmp(val, level->name) == 0) {
116             return level->log_level;
117         }
118     }
119 
120     return LOG_LEVEL_UNSET;
121 }
122 
set_log_level(LOG_LEVEL level)123 void set_log_level(LOG_LEVEL level) {
124     log_level = level;
125     if (ncachedlines && level != LOG_LEVEL_UNSET) {
126         log_cached_lines();
127     }
128 }
129 
toogle_log_level(LOG_LEVEL level)130 LOG_LEVEL toogle_log_level(LOG_LEVEL level) {
131     if (prev_log_level != LOG_LEVEL_UNSET && log_level != level) {
132         set_log_level(level);
133     } else if (log_level != level || prev_log_level != LOG_LEVEL_UNSET) {
134         if (prev_log_level == LOG_LEVEL_UNSET) {
135             prev_log_level = log_level;
136             set_log_level(level);
137         } else {
138             set_log_level(prev_log_level);
139             prev_log_level = LOG_LEVEL_UNSET;
140         }
141     }
142     return log_level;
143 }
144 
log_msg(LOG_LEVEL level,const char * format,...)145 void log_msg(LOG_LEVEL level, const char* format, ...) {
146     va_list argp;
147     va_start(argp, format);
148     vlog_msg(level, format, argp);
149     va_end(argp);
150 }
151