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