Fix use-after-free in final log message on tincd exit.
[tinc] / src / logger.c
1 /*
2     logger.c -- logging code
3     Copyright (C) 2004-2017 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
32 int debug_level = DEBUG_NOTHING;
33 static logmode_t logmode = LOGMODE_STDERR;
34 static pid_t logpid;
35 static FILE *logfile = NULL;
36 #ifdef HAVE_MINGW
37 static HANDLE loghandle = NULL;
38 #endif
39 static const char *logident = NULL;
40 bool logcontrol = false; // controlled by REQ_LOG <level>
41 int umbilical = 0;
42
43 static bool should_log(int level) {
44         return (level <= debug_level && logmode != LOGMODE_NULL) || logcontrol;
45 }
46
47 static void real_logger(int level, int priority, const char *message) {
48         char timestr[32] = "";
49         static bool suppress = false;
50
51         if(suppress) {
52                 return;
53         }
54
55         if(level <= debug_level) {
56                 switch(logmode) {
57                 case LOGMODE_STDERR:
58                         fprintf(stderr, "%s\n", message);
59                         fflush(stderr);
60                         break;
61
62                 case LOGMODE_FILE:
63                         if(!now.tv_sec) {
64                                 gettimeofday(&now, NULL);
65                         }
66
67                         time_t now_sec = now.tv_sec;
68                         strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", localtime(&now_sec));
69                         fprintf(logfile, "%s %s[%ld]: %s\n", timestr, logident, (long)logpid, message);
70                         fflush(logfile);
71                         break;
72
73                 case LOGMODE_SYSLOG:
74 #ifdef HAVE_MINGW
75                         {
76                                 const char *messages[] = {message};
77                                 ReportEvent(loghandle, priority, 0, 0, NULL, 1, 0, messages, NULL);
78                         }
79
80 #else
81 #ifdef HAVE_SYSLOG_H
82                         syslog(priority, "%s", message);
83 #endif
84 #endif
85                         break;
86
87                 case LOGMODE_NULL:
88                         break;
89                 }
90
91                 if(umbilical && do_detach) {
92                         write(umbilical, message, strlen(message));
93                         write(umbilical, "\n", 1);
94                 }
95         }
96
97         if(logcontrol && connection_list) {
98                 suppress = true;
99                 logcontrol = false;
100
101                 for list_each(connection_t, c, connection_list) {
102                         if(!c->status.log) {
103                                 continue;
104                         }
105
106                         logcontrol = true;
107
108                         if(level > (c->outcompression >= 0 ? c->outcompression : debug_level)) {
109                                 continue;
110                         }
111
112                         int len = strlen(message);
113
114                         if(send_request(c, "%d %d %d", CONTROL, REQ_LOG, len)) {
115                                 send_meta(c, message, len);
116                         }
117                 }
118
119                 suppress = false;
120         }
121 }
122
123 void logger(int level, int priority, const char *format, ...) {
124         va_list ap;
125         char message[1024] = "";
126
127         if(!should_log(level)) {
128                 return;
129         }
130
131         va_start(ap, format);
132         int len = vsnprintf(message, sizeof(message), format, ap);
133         message[sizeof(message) - 1] = 0;
134         va_end(ap);
135
136         if(len > 0 && (size_t)len < sizeof(message) - 1 && message[len - 1] == '\n') {
137                 message[len - 1] = 0;
138         }
139
140         real_logger(level, priority, message);
141 }
142
143 static void sptps_logger(sptps_t *s, int s_errno, const char *format, va_list ap) {
144         (void)s_errno;
145         char message[1024];
146         size_t msglen = sizeof(message);
147
148         if(!should_log(DEBUG_ALWAYS)) {
149                 return;
150         }
151
152         int len = vsnprintf(message, msglen, format, ap);
153         message[sizeof(message) - 1] = 0;
154
155         if(len > 0 && (size_t)len < sizeof(message) - 1) {
156                 if(message[len - 1] == '\n') {
157                         message[--len] = 0;
158                 }
159
160                 // WARNING: s->handle can point to a connection_t or a node_t,
161                 // but both types have the name and hostname fields at the same offsets.
162                 connection_t *c = s->handle;
163
164                 if(c) {
165                         snprintf(message + len, sizeof(message) - len, " from %s (%s)", c->name, c->hostname);
166                 }
167         }
168
169         real_logger(DEBUG_ALWAYS, LOG_ERR, message);
170 }
171
172 void openlogger(const char *ident, logmode_t mode) {
173         logident = ident;
174         logmode = mode;
175
176         switch(mode) {
177         case LOGMODE_STDERR:
178                 logpid = getpid();
179                 break;
180
181         case LOGMODE_FILE:
182                 logpid = getpid();
183                 logfile = fopen(logfilename, "a");
184
185                 if(!logfile) {
186                         fprintf(stderr, "Could not open log file %s: %s\n", logfilename, strerror(errno));
187                         logmode = LOGMODE_NULL;
188                 }
189
190                 break;
191
192         case LOGMODE_SYSLOG:
193 #ifdef HAVE_MINGW
194                 loghandle = RegisterEventSource(NULL, logident);
195
196                 if(!loghandle) {
197                         fprintf(stderr, "Could not open log handle!\n");
198                         logmode = LOGMODE_NULL;
199                 }
200
201                 break;
202 #else
203 #ifdef HAVE_SYSLOG_H
204                 openlog(logident, LOG_CONS | LOG_PID, LOG_DAEMON);
205                 break;
206 #endif
207 #endif
208
209         case LOGMODE_NULL:
210                 break;
211         }
212
213         if(logmode != LOGMODE_NULL) {
214                 sptps_log = sptps_logger;
215         } else {
216                 sptps_log = sptps_log_quiet;
217         }
218 }
219
220 void reopenlogger() {
221         if(logmode != LOGMODE_FILE) {
222                 return;
223         }
224
225         fflush(logfile);
226         FILE *newfile = fopen(logfilename, "a");
227
228         if(!newfile) {
229                 logger(DEBUG_ALWAYS, LOG_ERR, "Unable to reopen log file %s: %s", logfilename, strerror(errno));
230                 return;
231         }
232
233         fclose(logfile);
234         logfile = newfile;
235 }
236
237
238 void closelogger(void) {
239         switch(logmode) {
240         case LOGMODE_FILE:
241                 fclose(logfile);
242                 break;
243
244         case LOGMODE_SYSLOG:
245 #ifdef HAVE_MINGW
246                 DeregisterEventSource(loghandle);
247                 break;
248 #else
249 #ifdef HAVE_SYSLOG_H
250                 closelog();
251                 break;
252 #endif
253 #endif
254
255         case LOGMODE_NULL:
256         case LOGMODE_STDERR:
257                 break;
258         }
259 }