Extract common logic in OpenSSL-specific code
[tinc] / src / logger.c
1 /*
2     logger.c -- logging code
3     Copyright (C) 2004-2022 Guus Sliepen <guus@tinc-vpn.org>
4                   2004-2005 Ivo Timmermans
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU 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 "system.h"
22
23 #include "conf.h"
24 #include "meta.h"
25 #include "names.h"
26 #include "logger.h"
27 #include "connection.h"
28 #include "control_common.h"
29 #include "process.h"
30 #include "sptps.h"
31 #include "compression.h"
32
33 debug_t debug_level = DEBUG_NOTHING;
34 static logmode_t logmode = LOGMODE_STDERR;
35 static pid_t logpid;
36 static FILE *logfile = NULL;
37 #ifdef HAVE_MINGW
38 static HANDLE loghandle = NULL;
39 #endif
40 static const char *logident = NULL;
41 bool logcontrol = false; // controlled by REQ_LOG <level>
42 int umbilical = 0;
43
44 static bool should_log(debug_t level) {
45         return (level <= debug_level && logmode != LOGMODE_NULL) || logcontrol;
46 }
47
48 static void real_logger(debug_t level, int priority, const char *message) {
49         char timestr[32] = "";
50         static bool suppress = false;
51
52         if(suppress) {
53                 return;
54         }
55
56         if(level <= debug_level) {
57                 switch(logmode) {
58                 case LOGMODE_STDERR:
59                         fprintf(stderr, "%s\n", message);
60                         fflush(stderr);
61                         break;
62
63                 case LOGMODE_FILE:
64                         if(!now.tv_sec) {
65                                 gettimeofday(&now, NULL);
66                         }
67
68                         time_t now_sec = now.tv_sec;
69                         strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", localtime(&now_sec));
70                         fprintf(logfile, "%s %s[%ld]: %s\n", timestr, logident, (long)logpid, message);
71                         fflush(logfile);
72                         break;
73
74                 case LOGMODE_SYSLOG:
75 #ifdef HAVE_MINGW
76                         {
77                                 const char *messages[] = {message};
78                                 ReportEvent(loghandle, priority, 0, 0, NULL, 1, 0, messages, NULL);
79                         }
80
81 #else
82 #ifdef HAVE_SYSLOG_H
83                         syslog(priority, "%s", message);
84 #endif
85 #endif
86                         break;
87
88                 case LOGMODE_NULL:
89                 default:
90                         break;
91                 }
92
93                 if(umbilical && do_detach) {
94                         size_t len = strlen(message);
95
96                         if(write(umbilical, message, len) != (ssize_t)len || write(umbilical, "\n", 1) != 1) {
97                                 // Other end broken, nothing we can do about it.
98                         }
99                 }
100         }
101
102         if(logcontrol) {
103                 suppress = true;
104                 logcontrol = false;
105
106                 for list_each(connection_t, c, &connection_list) {
107                         if(!c->status.log) {
108                                 continue;
109                         }
110
111                         logcontrol = true;
112
113                         if(level > (c->outcompression >= COMPRESS_NONE ? c->outcompression : debug_level)) {
114                                 continue;
115                         }
116
117                         size_t len = strlen(message);
118
119                         if(send_request(c, "%d %d %lu", CONTROL, REQ_LOG, (unsigned long)len)) {
120                                 send_meta(c, message, len);
121                         }
122                 }
123
124                 suppress = false;
125         }
126 }
127
128 void logger(debug_t level, int priority, const char *format, ...) {
129         va_list ap;
130         char message[1024] = "";
131
132         if(!should_log(level)) {
133                 return;
134         }
135
136         va_start(ap, format);
137         int len = vsnprintf(message, sizeof(message), format, ap);
138         message[sizeof(message) - 1] = 0;
139         va_end(ap);
140
141         if(len > 0 && (size_t)len < sizeof(message) - 1 && message[len - 1] == '\n') {
142                 message[len - 1] = 0;
143         }
144
145         real_logger(level, priority, message);
146 }
147
148 static void sptps_logger(sptps_t *s, int s_errno, const char *format, va_list ap) {
149         (void)s_errno;
150         char message[1024];
151         size_t msglen = sizeof(message);
152
153         if(!should_log(DEBUG_ALWAYS)) {
154                 return;
155         }
156
157         int len = vsnprintf(message, msglen, format, ap);
158         message[sizeof(message) - 1] = 0;
159
160         if(len > 0 && (size_t)len < sizeof(message) - 1) {
161                 if(message[len - 1] == '\n') {
162                         message[--len] = 0;
163                 }
164
165                 // WARNING: s->handle can point to a connection_t or a node_t,
166                 // but both types have the name and hostname fields at the same offsets.
167                 connection_t *c = s->handle;
168
169                 if(c) {
170                         snprintf(message + len, sizeof(message) - len, " from %s (%s)", c->name, c->hostname);
171                 }
172         }
173
174         real_logger(DEBUG_ALWAYS, LOG_ERR, message);
175 }
176
177 void openlogger(const char *ident, logmode_t mode) {
178         logident = ident;
179         logmode = mode;
180
181         switch(mode) {
182         case LOGMODE_STDERR:
183                 logpid = getpid();
184                 break;
185
186         case LOGMODE_FILE:
187                 logpid = getpid();
188                 logfile = fopen(logfilename, "a");
189
190                 if(!logfile) {
191                         fprintf(stderr, "Could not open log file %s: %s\n", logfilename, strerror(errno));
192                         logmode = LOGMODE_NULL;
193                 }
194
195                 break;
196
197         case LOGMODE_SYSLOG:
198 #ifdef HAVE_MINGW
199                 loghandle = RegisterEventSource(NULL, logident);
200
201                 if(!loghandle) {
202                         fprintf(stderr, "Could not open log handle!\n");
203                         logmode = LOGMODE_NULL;
204                 }
205
206                 break;
207 #else
208 #ifdef HAVE_SYSLOG_H
209                 openlog(logident, LOG_CONS | LOG_PID, LOG_DAEMON);
210                 break;
211 #endif
212 #endif
213
214         case LOGMODE_NULL:
215         default:
216                 break;
217         }
218
219         if(logmode != LOGMODE_NULL) {
220                 sptps_log = sptps_logger;
221         } else {
222                 sptps_log = sptps_log_quiet;
223         }
224 }
225
226 void reopenlogger(void) {
227         if(logmode != LOGMODE_FILE) {
228                 return;
229         }
230
231         fflush(logfile);
232         FILE *newfile = fopen(logfilename, "a");
233
234         if(!newfile) {
235                 logger(DEBUG_ALWAYS, LOG_ERR, "Unable to reopen log file %s: %s", logfilename, strerror(errno));
236                 return;
237         }
238
239         fclose(logfile);
240         logfile = newfile;
241 }
242
243
244 void closelogger(void) {
245         switch(logmode) {
246         case LOGMODE_FILE:
247                 fclose(logfile);
248                 break;
249
250         case LOGMODE_SYSLOG:
251 #ifdef HAVE_MINGW
252                 DeregisterEventSource(loghandle);
253                 break;
254 #else
255 #ifdef HAVE_SYSLOG_H
256                 closelog();
257                 break;
258 #endif
259 #endif
260
261         case LOGMODE_NULL:
262         case LOGMODE_STDERR:
263         default:
264                 break;
265         }
266 }