Fix UBSAN warnings about conversions and overflows.
[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 #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                         write(umbilical, message, strlen(message));
95                         write(umbilical, "\n", 1);
96                 }
97         }
98
99         if(logcontrol) {
100                 suppress = true;
101                 logcontrol = false;
102
103                 for list_each(connection_t, c, &connection_list) {
104                         if(!c->status.log) {
105                                 continue;
106                         }
107
108                         logcontrol = true;
109
110                         if(level > (c->outcompression >= COMPRESS_NONE ? c->outcompression : debug_level)) {
111                                 continue;
112                         }
113
114                         size_t len = strlen(message);
115
116                         if(send_request(c, "%d %d %zu", CONTROL, REQ_LOG, len)) {
117                                 send_meta(c, message, len);
118                         }
119                 }
120
121                 suppress = false;
122         }
123 }
124
125 void logger(debug_t level, int priority, const char *format, ...) {
126         va_list ap;
127         char message[1024] = "";
128
129         if(!should_log(level)) {
130                 return;
131         }
132
133         va_start(ap, format);
134         int len = vsnprintf(message, sizeof(message), format, ap);
135         message[sizeof(message) - 1] = 0;
136         va_end(ap);
137
138         if(len > 0 && (size_t)len < sizeof(message) - 1 && message[len - 1] == '\n') {
139                 message[len - 1] = 0;
140         }
141
142         real_logger(level, priority, message);
143 }
144
145 static void sptps_logger(sptps_t *s, int s_errno, const char *format, va_list ap) {
146         (void)s_errno;
147         char message[1024];
148         size_t msglen = sizeof(message);
149
150         if(!should_log(DEBUG_ALWAYS)) {
151                 return;
152         }
153
154         int len = vsnprintf(message, msglen, format, ap);
155         message[sizeof(message) - 1] = 0;
156
157         if(len > 0 && (size_t)len < sizeof(message) - 1) {
158                 if(message[len - 1] == '\n') {
159                         message[--len] = 0;
160                 }
161
162                 // WARNING: s->handle can point to a connection_t or a node_t,
163                 // but both types have the name and hostname fields at the same offsets.
164                 connection_t *c = s->handle;
165
166                 if(c) {
167                         snprintf(message + len, sizeof(message) - len, " from %s (%s)", c->name, c->hostname);
168                 }
169         }
170
171         real_logger(DEBUG_ALWAYS, LOG_ERR, message);
172 }
173
174 void openlogger(const char *ident, logmode_t mode) {
175         logident = ident;
176         logmode = mode;
177
178         switch(mode) {
179         case LOGMODE_STDERR:
180                 logpid = getpid();
181                 break;
182
183         case LOGMODE_FILE:
184                 logpid = getpid();
185                 logfile = fopen(logfilename, "a");
186
187                 if(!logfile) {
188                         fprintf(stderr, "Could not open log file %s: %s\n", logfilename, strerror(errno));
189                         logmode = LOGMODE_NULL;
190                 }
191
192                 break;
193
194         case LOGMODE_SYSLOG:
195 #ifdef HAVE_MINGW
196                 loghandle = RegisterEventSource(NULL, logident);
197
198                 if(!loghandle) {
199                         fprintf(stderr, "Could not open log handle!\n");
200                         logmode = LOGMODE_NULL;
201                 }
202
203                 break;
204 #else
205 #ifdef HAVE_SYSLOG_H
206                 openlog(logident, LOG_CONS | LOG_PID, LOG_DAEMON);
207                 break;
208 #endif
209 #endif
210
211         case LOGMODE_NULL:
212         default:
213                 break;
214         }
215
216         if(logmode != LOGMODE_NULL) {
217                 sptps_log = sptps_logger;
218         } else {
219                 sptps_log = sptps_log_quiet;
220         }
221 }
222
223 void reopenlogger() {
224         if(logmode != LOGMODE_FILE) {
225                 return;
226         }
227
228         fflush(logfile);
229         FILE *newfile = fopen(logfilename, "a");
230
231         if(!newfile) {
232                 logger(DEBUG_ALWAYS, LOG_ERR, "Unable to reopen log file %s: %s", logfilename, strerror(errno));
233                 return;
234         }
235
236         fclose(logfile);
237         logfile = newfile;
238 }
239
240
241 void closelogger(void) {
242         switch(logmode) {
243         case LOGMODE_FILE:
244                 fclose(logfile);
245                 break;
246
247         case LOGMODE_SYSLOG:
248 #ifdef HAVE_MINGW
249                 DeregisterEventSource(loghandle);
250                 break;
251 #else
252 #ifdef HAVE_SYSLOG_H
253                 closelog();
254                 break;
255 #endif
256 #endif
257
258         case LOGMODE_NULL:
259         case LOGMODE_STDERR:
260         default:
261                 break;
262         }
263 }