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