Add server-side TLS support, through kTLS.
[cubemap] / log.cpp
1 #include <assert.h>
2 #include <errno.h>
3 #include <stdarg.h>
4 #include <stddef.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include <syslog.h>
8 #include <time.h>
9 #include <string>
10 #include <vector>
11
12 #include "tlse.h"
13
14 #include "log.h"
15
16 using namespace std;
17
18 // Yes, it's a bit ugly.
19 #define SYSLOG_FAKE_FILE (static_cast<FILE *>(NULL))
20
21 bool logging_started = false;
22 vector<FILE *> log_destinations;
23
24 void add_log_destination_file(const string &filename)
25 {
26         FILE *fp = fopen(filename.c_str(), "a");
27         if (fp == NULL) {
28                 perror(filename.c_str());
29                 return;
30         }
31
32         log_destinations.push_back(fp); 
33 }
34
35 void add_log_destination_console()
36 {
37         log_destinations.push_back(stderr);
38 }
39
40 void add_log_destination_syslog()
41 {
42         openlog("cubemap", LOG_PID, LOG_DAEMON);
43         log_destinations.push_back(SYSLOG_FAKE_FILE);
44 }
45
46 void start_logging()
47 {
48         logging_started = true;
49 }
50
51 void shut_down_logging()
52 {
53         for (size_t i = 0; i < log_destinations.size(); ++i) {
54                 if (log_destinations[i] == SYSLOG_FAKE_FILE) {
55                         closelog();
56                 } else if (log_destinations[i] != stderr) {
57                         if (fclose(log_destinations[i]) != 0) {
58                                 perror("fclose");
59                         }
60                 }
61         }
62         log_destinations.clear();
63         logging_started = false;
64 }
65
66 void log(LogLevel log_level, const char *fmt, ...)
67 {
68         char formatted_msg[4096];
69         va_list ap;
70         va_start(ap, fmt);
71         vsnprintf(formatted_msg, sizeof(formatted_msg), fmt, ap);
72         va_end(ap);
73
74         time_t now = time(NULL);
75         struct tm lt;
76         struct tm *ltime = localtime_r(&now, &lt);
77         char timestamp[1024];
78         if (ltime == NULL) {
79                 strcpy(timestamp, "???");
80         } else {
81                 strftime(timestamp, sizeof(timestamp), "%a, %d %b %Y %T %z", ltime);
82         }
83
84         const char *log_level_str;
85         int syslog_level;
86
87         switch (log_level) {
88         case INFO:
89                 log_level_str = "INFO:    ";
90                 syslog_level = LOG_INFO;
91                 break;
92         case WARNING:
93                 log_level_str = "WARNING: ";
94                 syslog_level = LOG_WARNING;
95                 break;
96         case ERROR:
97                 log_level_str = "ERROR:   ";
98                 syslog_level = LOG_ERR;
99                 break;
100         default:
101                 assert(false);
102         }
103
104         // Log to stderr if logging hasn't been set up yet. Note that this means
105         // that such messages will come even if there are no “error_log” lines.
106         if (!logging_started) {
107                 fprintf(stderr, "[%s] %s%s\n", timestamp, log_level_str, formatted_msg);
108                 return;
109         }
110
111         for (size_t i = 0; i < log_destinations.size(); ++i) {
112                 if (log_destinations[i] == SYSLOG_FAKE_FILE) {
113                         syslog(syslog_level, "%s", formatted_msg);
114                 } else {
115                         int err = fprintf(log_destinations[i], "[%s] %s%s\n", timestamp, log_level_str, formatted_msg);
116                         if (err < 0) {
117                                 perror("fprintf");
118                         }
119                         if (log_destinations[i] != stderr) {
120                                 fflush(log_destinations[i]);
121                         }
122                 }
123         }
124 }
125
126 void log_perror(const char *msg)
127 {
128         char errbuf[4096];
129         log(ERROR, "%s: %s", msg, strerror_r(errno, errbuf, sizeof(errbuf)));
130 }
131
132 void log_tls_error(const char *msg, int tls_err)
133 {
134         switch (tls_err) {
135         case TLS_NEED_MORE_DATA:
136                 log(ERROR, "%s: Need more data (TLS)", msg);
137                 break;
138         case TLS_GENERIC_ERROR:
139                 log(ERROR, "%s: Generic TLS error", msg);
140                 break;
141         case TLS_BROKEN_PACKET:
142                 log(ERROR, "%s: Broken TLS packet", msg);
143                 break;
144         case TLS_NOT_UNDERSTOOD:
145                 log(ERROR, "%s: Not understood (TLS)", msg);
146                 break;
147         case TLS_NOT_SAFE:
148                 log(ERROR, "%s: Not safe (TLS)", msg);
149                 break;
150         case TLS_NO_COMMON_CIPHER:
151                 log(ERROR, "%s: No common TLS cipher", msg);
152                 break;
153         case TLS_UNEXPECTED_MESSAGE:
154                 log(ERROR, "%s: Unexpected TLS message", msg);
155                 break;
156         case TLS_CLOSE_CONNECTION:
157                 log(ERROR, "%s: Close TLS connection", msg);
158                 break;
159         case TLS_COMPRESSION_NOT_SUPPORTED:
160                 log(ERROR, "%s: TLS compression not supported", msg);
161                 break;
162         case TLS_NO_MEMORY:
163                 log(ERROR, "%s: No TLS memory", msg);
164                 break;
165         case TLS_NOT_VERIFIED:
166                 log(ERROR, "%s: Not verified (TLS)", msg);
167                 break;
168         case TLS_INTEGRITY_FAILED:
169                 log(ERROR, "%s: TLS integrity failed", msg);
170                 break;
171         case TLS_ERROR_ALERT:
172                 log(ERROR, "%s: TLS alert", msg);
173                 break;
174         case TLS_BROKEN_CONNECTION:
175                 log(ERROR, "%s: Broken TLS connection", msg);
176                 break;
177         case TLS_BAD_CERTIFICATE:
178                 log(ERROR, "%s: Bad TLS certificate", msg);
179                 break;
180         case TLS_UNSUPPORTED_CERTIFICATE:
181                 log(ERROR, "%s: Unsupported TLS certificate", msg);
182                 break;
183         case TLS_NO_RENEGOTIATION:
184                 log(ERROR, "%s: No TLS renegotiation", msg);
185                 break;
186         case TLS_FEATURE_NOT_SUPPORTED:
187                 log(ERROR, "%s: TLS feature not supported", msg);
188                 break;
189         default:
190                 log(ERROR, "%s: Unknown TLS error %d", msg, tls_err);
191                 break;
192         }
193 }