Line data Source code
1 : /*
2 : * lwan - web server
3 : * Copyright (c) 2021 L. A. F. Pereira <l@tia.mat.br>
4 : *
5 : * This program is free software; you can redistribute it and/or
6 : * modify it under the terms of the GNU General Public License
7 : * as published by the Free Software Foundation; either version 2
8 : * of the License, or any later version.
9 : *
10 : * This program is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : * GNU General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU General Public License
16 : * along with this program; if not, write to the Free Software
17 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18 : * USA.
19 : */
20 : #define _GNU_SOURCE
21 : #include <errno.h>
22 : #include <libgen.h>
23 : #include <pthread.h>
24 : #include <stdarg.h>
25 : #include <stdio.h>
26 : #include <stdlib.h>
27 : #include <string.h>
28 : #include <sys/types.h>
29 : #include <unistd.h>
30 :
31 : #include "lwan-private.h"
32 : #include "lwan-status.h"
33 :
34 : enum lwan_status_type {
35 : STATUS_INFO = 0,
36 : STATUS_WARNING = 1,
37 : STATUS_ERROR = 2,
38 : STATUS_DEBUG = 3,
39 : STATUS_PERROR = 4,
40 : STATUS_NONE = 5,
41 : /* [6,7] are unused so that CRITICAL can be ORed with previous items */
42 : STATUS_CRITICAL = 8,
43 : };
44 :
45 : static bool can_use_colors(void);
46 :
47 : static volatile bool quiet = false;
48 : static bool use_colors;
49 :
50 184 : void lwan_status_init(struct lwan *l)
51 : {
52 : #ifdef NDEBUG
53 : quiet = l->config.quiet;
54 : #else
55 184 : quiet = false;
56 : (void)l;
57 : #endif
58 184 : use_colors = can_use_colors();
59 184 : }
60 :
61 0 : void lwan_status_shutdown(struct lwan *l __attribute__((unused))) {}
62 :
63 184 : static bool can_use_colors(void)
64 : {
65 : const char *term;
66 :
67 184 : if (!isatty(fileno(stdout)))
68 184 : return false;
69 :
70 0 : term = secure_getenv("TERM");
71 0 : if (term && streq(term, "dumb"))
72 0 : return false;
73 :
74 0 : return true;
75 : }
76 :
77 2904 : static int status_index(enum lwan_status_type type)
78 : {
79 2904 : return use_colors ? (int)type : STATUS_NONE;
80 : }
81 :
82 : #define V(c) { .value = c, .len = sizeof(c) - 1 }
83 : static const struct lwan_value start_colors[] = {
84 : [STATUS_INFO] = V("\033[36m"),
85 : [STATUS_WARNING] = V("\033[33m"),
86 : [STATUS_DEBUG] = V("\033[37m"),
87 : [STATUS_PERROR] = V("\033[35m"),
88 : [STATUS_CRITICAL] = V("\033[31;1m"),
89 : [STATUS_NONE] = V(""),
90 : [STATUS_ERROR] = V("\033[35m"),
91 : [STATUS_CRITICAL | STATUS_PERROR] = V("\033[31;1m"),
92 : };
93 :
94 2904 : static inline struct lwan_value start_color(enum lwan_status_type type)
95 : {
96 2904 : return start_colors[status_index(type)];
97 : }
98 :
99 2904 : static inline struct lwan_value end_color(void)
100 : {
101 2904 : return use_colors ? (struct lwan_value)V("\033[0m\n")
102 2904 : : (struct lwan_value)V("\n");
103 : }
104 : #undef V
105 :
106 0 : static inline char *strerror_thunk_r(int error_number, char *buffer, size_t len)
107 : {
108 : #if defined(__GLIBC__) && defined(_GNU_SOURCE)
109 0 : return strerror_r(error_number, buffer, len);
110 : #else /* XSI-compliant strerror_r() */
111 : if (!strerror_r(error_number, buffer, len))
112 : return buffer;
113 : return "Unknown";
114 : #endif
115 : }
116 :
117 : #ifndef NDEBUG
118 2904 : static long gettid_cached(void)
119 : {
120 : #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
121 : /* Workaround for:
122 : * https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=15216 */
123 : return gettid();
124 : #else
125 : static __thread long tid;
126 :
127 2904 : if (!tid)
128 276 : tid = gettid();
129 :
130 2904 : return tid;
131 : #endif
132 : }
133 : #endif
134 :
135 : #define FORMAT_WITH_COLOR(fmt, color) "\033[" color "m" fmt "\033[0m"
136 :
137 : #ifdef LWAN_HAVE_SYSLOG
138 :
139 : #include <lwan-strbuf.h>
140 : #include <syslog.h>
141 :
142 : static int status_to_syslog_prio[] = {
143 : [STATUS_CRITICAL | STATUS_PERROR] = LOG_CRIT,
144 : [STATUS_CRITICAL] = LOG_CRIT,
145 : [STATUS_ERROR] = LOG_ERR,
146 : [STATUS_WARNING] = LOG_WARNING,
147 : [STATUS_INFO] = LOG_INFO,
148 : [STATUS_DEBUG] = LOG_DEBUG,
149 : };
150 :
151 : void lwan_syslog_status_out(
152 : #ifndef NDEBUG
153 : const char *file,
154 : const int line,
155 : const char *func,
156 : const long tid,
157 : #endif
158 : enum lwan_status_type type,
159 : int saved_errno,
160 : const char *fmt,
161 : va_list values)
162 : {
163 : char syslog_buffer[256];
164 : va_list copied_values;
165 : struct lwan_strbuf buf;
166 :
167 : lwan_strbuf_init_with_fixed_buffer(&buf, syslog_buffer,
168 : sizeof(syslog_buffer));
169 :
170 : #ifndef NDEBUG
171 : if (!lwan_strbuf_append_printf(&buf, "%ld %s:%d %s() ", tid,
172 : basename(strdupa(file)), line, func))
173 : goto out;
174 : #endif
175 :
176 : va_copy(copied_values, values);
177 : if (!lwan_strbuf_append_vprintf(&buf, fmt, copied_values))
178 : goto out;
179 :
180 : if (type & STATUS_PERROR) {
181 : char errbuf[128];
182 :
183 : if (!lwan_strbuf_append_strz(
184 : &buf,
185 : strerror_thunk_r(saved_errno, errbuf, sizeof(errbuf) - 1)))
186 : goto out;
187 : }
188 :
189 : syslog(status_to_syslog_prio[type], "%.*s",
190 : (int)lwan_strbuf_get_length(&buf), lwan_strbuf_get_buffer(&buf));
191 :
192 : out:
193 : lwan_strbuf_free(&buf);
194 : }
195 :
196 : __attribute__((constructor)) static void register_lwan_to_syslog(void)
197 : {
198 : openlog("lwan", LOG_NDELAY | LOG_PID | LOG_CONS, LOG_USER);
199 : }
200 : #else
201 : #define lwan_syslog_status_out(...)
202 : #endif
203 :
204 2904 : static void status_out(
205 : #ifndef NDEBUG
206 : const char *file,
207 : const int line,
208 : const char *func,
209 : #endif
210 : enum lwan_status_type type,
211 : const char *fmt,
212 : va_list values)
213 : {
214 2904 : struct lwan_value start = start_color(type);
215 2904 : struct lwan_value end = end_color();
216 2904 : int saved_errno = errno;
217 :
218 : #ifndef NDEBUG
219 : lwan_syslog_status_out(file, line, func, gettid_cached(), type, saved_errno,
220 : fmt, values);
221 : #else
222 : lwan_syslog_status_out(type, saved_errno, fmt, values);
223 : #endif
224 :
225 2904 : flockfile(stdout);
226 :
227 : #ifndef NDEBUG
228 2904 : char *base_name = basename(strdupa(file));
229 2904 : if (LIKELY(use_colors)) {
230 0 : printf(FORMAT_WITH_COLOR("%ld ", "32;1"), gettid_cached());
231 0 : printf(FORMAT_WITH_COLOR("%s:%d ", "3"), base_name, line);
232 0 : printf(FORMAT_WITH_COLOR("%s() ", "33"), func);
233 : } else {
234 2904 : printf("%ld %s:%d %s() ", gettid_cached(), base_name, line, func);
235 : }
236 : #endif
237 :
238 2904 : fwrite_unlocked(start.value, start.len, 1, stdout);
239 2904 : vprintf(fmt, values);
240 :
241 2904 : if (UNLIKELY(type & STATUS_PERROR)) {
242 : char errbuf[64];
243 : char *errmsg =
244 0 : strerror_thunk_r(saved_errno, errbuf, sizeof(errbuf) - 1);
245 :
246 0 : printf(": %s (error number %d)", errmsg, saved_errno);
247 : }
248 :
249 2904 : fwrite_unlocked(end.value, end.len, 1, stdout);
250 :
251 2904 : funlockfile(stdout);
252 :
253 2904 : errno = saved_errno;
254 2904 : }
255 :
256 : #undef FORMAT_WITH_COLOR
257 :
258 : #ifdef NDEBUG
259 : #define IMPLEMENT_FUNCTION(fn_name_, type_) \
260 : void lwan_status_##fn_name_(const char *fmt, ...) \
261 : { \
262 : if (LIKELY(!quiet)) { \
263 : va_list values; \
264 : va_start(values, fmt); \
265 : status_out(type_, fmt, values); \
266 : va_end(values); \
267 : } \
268 : if (UNLIKELY((type_)&STATUS_CRITICAL)) \
269 : exit(1); \
270 : }
271 : #else
272 : #define IMPLEMENT_FUNCTION(fn_name_, type_) \
273 : void lwan_status_##fn_name_##_debug(const char *file, const int line, \
274 : const char *func, const char *fmt, \
275 : ...) \
276 : { \
277 : if (LIKELY(!quiet)) { \
278 : va_list values; \
279 : va_start(values, fmt); \
280 : status_out(file, line, func, type_, fmt, values); \
281 : va_end(values); \
282 : } \
283 : if (UNLIKELY((type_)&STATUS_CRITICAL)) \
284 : abort(); \
285 : }
286 :
287 2536 : IMPLEMENT_FUNCTION(debug, STATUS_DEBUG)
288 : #endif
289 :
290 368 : IMPLEMENT_FUNCTION(info, STATUS_INFO)
291 0 : IMPLEMENT_FUNCTION(warning, STATUS_WARNING)
292 0 : IMPLEMENT_FUNCTION(error, STATUS_ERROR)
293 0 : IMPLEMENT_FUNCTION(perror, STATUS_PERROR)
294 :
295 0 : IMPLEMENT_FUNCTION(critical, STATUS_CRITICAL)
296 0 : IMPLEMENT_FUNCTION(critical_perror, STATUS_CRITICAL | STATUS_PERROR)
297 :
298 : #undef IMPLEMENT_FUNCTION
|