/src/tarantool/src/lib/core/say.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file. |
3 | | * |
4 | | * Redistribution and use in source and binary forms, with or |
5 | | * without modification, are permitted provided that the following |
6 | | * conditions are met: |
7 | | * |
8 | | * 1. Redistributions of source code must retain the above |
9 | | * copyright notice, this list of conditions and the |
10 | | * following disclaimer. |
11 | | * |
12 | | * 2. Redistributions in binary form must reproduce the above |
13 | | * copyright notice, this list of conditions and the following |
14 | | * disclaimer in the documentation and/or other materials |
15 | | * provided with the distribution. |
16 | | * |
17 | | * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND |
18 | | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
19 | | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
20 | | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
21 | | * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, |
22 | | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 | | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
24 | | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
25 | | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
26 | | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
28 | | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 | | * SUCH DAMAGE. |
30 | | */ |
31 | | #include "say.h" |
32 | | #include "fiber.h" |
33 | | #include "errinj.h" |
34 | | #include "tt_static.h" |
35 | | #include "tt_strerror.h" |
36 | | |
37 | | #include <errno.h> |
38 | | #include <stdarg.h> |
39 | | #include <stdio.h> |
40 | | #include <string.h> |
41 | | #include <netdb.h> |
42 | | #include <netinet/in.h> |
43 | | #include <sys/types.h> |
44 | | #include <unistd.h> |
45 | | #include <fcntl.h> |
46 | | #include <syslog.h> |
47 | | #include <sys/socket.h> |
48 | | #include <sys/un.h> |
49 | | #include <arpa/inet.h> |
50 | | #include <coio_task.h> |
51 | | |
52 | | pid_t log_pid = 0; |
53 | | /** |
54 | | * The global log level. Used as an optimization by the say() macro to |
55 | | * avoid unnecessary calls to say_default(). |
56 | | * Calculated as MAX(log_default->level, log_level_flightrec). |
57 | | */ |
58 | | int log_level = S_INFO; |
59 | | /** |
60 | | * Log level of flight recorder. Log is passed to flight recorder only if |
61 | | * it's log level not less that this. -1 is used if we should not pass logs |
62 | | * to flight recorder (it is currently disabled). Used in Lua via FFI. |
63 | | */ |
64 | | int log_level_flightrec = -1; |
65 | | /** |
66 | | * This function is called for every log which log level is not less than |
67 | | * log_level_flightrec. |
68 | | */ |
69 | | void |
70 | | (*log_write_flightrec)(int level, const char *filename, int line, |
71 | | const char *error, const char *format, va_list ap); |
72 | | |
73 | | enum say_format log_format = SF_PLAIN; |
74 | | enum { SAY_SYSLOG_DEFAULT_PORT = 512 }; |
75 | | |
76 | | /** List of logs to rotate */ |
77 | | static RLIST_HEAD(log_rotate_list); |
78 | | |
79 | | static const char logger_syntax_reminder[] = |
80 | | "expecting a file name or a prefix, such as '|', 'pipe:', 'syslog:'"; |
81 | | /** |
82 | | * True if Tarantool process runs in background mode, i.e. has no |
83 | | * controlling terminal. |
84 | | */ |
85 | | static bool log_background; |
86 | | |
87 | | static void |
88 | | say_default(int level, const char *filename, int line, const char *error, |
89 | | const char *format, ...); |
90 | | |
91 | | static int |
92 | | say_format_boot(struct log *log, char *buf, int len, int level, |
93 | | const char *module, const char *filename, int line, |
94 | | const char *error, const char *format, va_list ap); |
95 | | |
96 | | /* |
97 | | * Callbacks called before/after writing to stderr. |
98 | | */ |
99 | | static say_stderr_callback_t before_stderr_callback; |
100 | | static say_stderr_callback_t after_stderr_callback; |
101 | | |
102 | | /** Default logger used before logging subsystem is initialized. */ |
103 | | static struct log log_boot = { |
104 | | .fd = STDERR_FILENO, |
105 | | .level = S_INFO, |
106 | | .type = SAY_LOGGER_BOOT, |
107 | | .path = NULL, /* iff type == SAY_LOGGER_FILE */ |
108 | | .nonblock = false, |
109 | | .format_func = say_format_boot, |
110 | | .pid = 0, |
111 | | .syslog_ident = NULL, |
112 | | }; |
113 | | |
114 | | /** Default logger used after bootstrap. */ |
115 | | static struct log log_std; |
116 | | |
117 | | static struct log *log_default = &log_boot; |
118 | | |
119 | | sayfunc_t _say = say_default; |
120 | | |
121 | | const char * |
122 | | _say_strerror(int errnum) |
123 | 0 | { |
124 | 0 | return tt_strerror(errnum); |
125 | 0 | } |
126 | | |
127 | | static const char level_chars[] = { |
128 | | [S_FATAL] = 'F', |
129 | | [S_SYSERROR] = '!', |
130 | | [S_ERROR] = 'E', |
131 | | [S_CRIT] = 'C', |
132 | | [S_WARN] = 'W', |
133 | | [S_INFO] = 'I', |
134 | | [S_VERBOSE] = 'V', |
135 | | [S_DEBUG] = 'D', |
136 | | }; |
137 | | |
138 | | static_assert(lengthof(level_chars) == say_level_MAX, |
139 | | "level_chars is not defined for one of log levels"); |
140 | | |
141 | | static const char *level_strs[] = { |
142 | | [S_FATAL] = "FATAL", |
143 | | [S_SYSERROR] = "SYSERROR", |
144 | | [S_ERROR] = "ERROR", |
145 | | [S_CRIT] = "CRIT", |
146 | | [S_WARN] = "WARN", |
147 | | [S_INFO] = "INFO", |
148 | | [S_VERBOSE] = "VERBOSE", |
149 | | [S_DEBUG] = "DEBUG", |
150 | | }; |
151 | | |
152 | | static_assert(lengthof(level_strs) == say_level_MAX, |
153 | | "level_strs is not defined for one of log levels"); |
154 | | |
155 | | const char* |
156 | | say_log_level_str(int level) |
157 | 0 | { |
158 | 0 | if (level < 0 || level >= (int)lengthof(level_strs)) |
159 | 0 | return NULL; |
160 | 0 | return level_strs[level]; |
161 | 0 | } |
162 | | |
163 | | static int |
164 | | level_to_syslog_priority(int level) |
165 | 0 | { |
166 | 0 | switch (level) { |
167 | 0 | case S_FATAL: |
168 | 0 | return LOG_ERR; |
169 | 0 | case S_SYSERROR: |
170 | 0 | return LOG_ERR; |
171 | 0 | case S_ERROR: |
172 | 0 | return LOG_ERR; |
173 | 0 | case S_CRIT: |
174 | 0 | return LOG_ERR; |
175 | 0 | case S_WARN: |
176 | 0 | return LOG_WARNING; |
177 | 0 | case S_INFO: |
178 | 0 | return LOG_INFO; |
179 | 0 | case S_VERBOSE: |
180 | 0 | return LOG_INFO; |
181 | 0 | case S_DEBUG: |
182 | 0 | return LOG_DEBUG; |
183 | 0 | default: |
184 | 0 | return LOG_ERR; |
185 | 0 | } |
186 | 0 | } |
187 | | |
188 | | /** |
189 | | * Helper function that fills the given tm struct with the current local time. |
190 | | * tm_sec is set to the number of seconds that passed since the last minute |
191 | | * (0-60). Note, in contrast to tm.tm_sec, it has a fractional part. |
192 | | */ |
193 | | static void |
194 | | get_current_time(struct tm *tm, double *tm_sec) |
195 | 0 | { |
196 | | /* Don't use ev_now() since it requires a working event loop. */ |
197 | 0 | ev_tstamp now = ev_time(); |
198 | 0 | time_t now_seconds = (time_t)now; |
199 | 0 | localtime_r(&now_seconds, tm); |
200 | 0 | *tm_sec = now - now_seconds + tm->tm_sec; |
201 | 0 | } |
202 | | |
203 | | enum say_logger_type |
204 | | log_type(void) |
205 | 0 | { |
206 | 0 | return log_default->type; |
207 | 0 | } |
208 | | |
209 | | int |
210 | | log_get_fd(void) |
211 | 0 | { |
212 | 0 | return log_default->fd; |
213 | 0 | } |
214 | | |
215 | | void |
216 | | log_set_fd(int new_fd) |
217 | 0 | { |
218 | 0 | log_default->fd = new_fd; |
219 | 0 | } |
220 | | |
221 | | void |
222 | | log_set_level(struct log *log, enum say_level level) |
223 | 0 | { |
224 | 0 | log->level = level; |
225 | 0 | } |
226 | | |
227 | | void |
228 | | log_set_format(struct log *log, log_format_func_t format_func) |
229 | 0 | { |
230 | 0 | log->format_func = format_func; |
231 | 0 | } |
232 | | |
233 | | void |
234 | | say_set_log_level(int new_level) |
235 | 0 | { |
236 | 0 | log_level = MAX(new_level, log_level_flightrec); |
237 | 0 | log_set_level(log_default, (enum say_level) new_level); |
238 | 0 | } |
239 | | |
240 | | int |
241 | | say_get_log_level(void) |
242 | 0 | { |
243 | 0 | return log_default->level; |
244 | 0 | } |
245 | | |
246 | | void |
247 | | say_set_log_format(enum say_format format) |
248 | 0 | { |
249 | 0 | log_format_func_t format_func; |
250 | |
|
251 | 0 | switch (format) { |
252 | 0 | case SF_JSON: |
253 | 0 | format_func = say_format_json; |
254 | 0 | break; |
255 | 0 | case SF_PLAIN: |
256 | 0 | format_func = say_format_plain; |
257 | 0 | break; |
258 | 0 | default: |
259 | 0 | unreachable(); |
260 | 0 | } |
261 | | |
262 | 0 | log_set_format(log_default, format_func); |
263 | 0 | log_format = format; |
264 | 0 | } |
265 | | |
266 | | void |
267 | | say_set_flightrec_log_level(int new_level) |
268 | 0 | { |
269 | 0 | log_level = MAX(new_level, log_default->level); |
270 | 0 | log_level_flightrec = new_level; |
271 | 0 | } |
272 | | |
273 | | static const char *say_format_strs[] = { |
274 | | [SF_PLAIN] = "plain", |
275 | | [SF_JSON] = "json", |
276 | | [say_format_MAX] = "unknown" |
277 | | }; |
278 | | |
279 | | enum say_format |
280 | | say_format_by_name(const char *format) |
281 | 0 | { |
282 | 0 | return STR2ENUM(say_format, format); |
283 | 0 | } |
284 | | |
285 | | /** |
286 | | * Sets O_NONBLOCK flag in case if lognonblock is set. |
287 | | */ |
288 | | static void |
289 | | log_set_nonblock(struct log *log) |
290 | 0 | { |
291 | 0 | if (!log->nonblock) |
292 | 0 | return; |
293 | 0 | int flags; |
294 | 0 | if ((flags = fcntl(log->fd, F_GETFL, 0)) < 0 || |
295 | 0 | fcntl(log->fd, F_SETFL, flags | O_NONBLOCK) < 0) { |
296 | 0 | say_syserror("fcntl, fd=%i", log->fd); |
297 | 0 | } |
298 | 0 | } |
299 | | |
300 | | /** |
301 | | * Rotate logs on SIGHUP |
302 | | */ |
303 | | static int |
304 | | log_rotate(struct log *log) |
305 | 0 | { |
306 | 0 | if (pm_atomic_load(&log->type) != SAY_LOGGER_FILE) |
307 | 0 | return 0; |
308 | | |
309 | 0 | ERROR_INJECT(ERRINJ_LOG_ROTATE, { usleep(10); }); |
310 | | |
311 | 0 | int fd = open(log->path, O_WRONLY | O_APPEND | O_CREAT, |
312 | 0 | S_IRUSR | S_IWUSR | S_IRGRP); |
313 | 0 | if (fd < 0) { |
314 | 0 | diag_set(SystemError, "logrotate can't open %s", log->path); |
315 | 0 | return -1; |
316 | 0 | } |
317 | | /* |
318 | | * The whole charade's purpose is to avoid log->fd changing. |
319 | | * Remember, we are a signal handler. |
320 | | */ |
321 | 0 | dup2(fd, log->fd); |
322 | 0 | close(fd); |
323 | |
|
324 | 0 | log_set_nonblock(log); |
325 | | |
326 | | /* We are in ev signal handler |
327 | | * so we don't have to be worry about async signal safety |
328 | | */ |
329 | 0 | log_say(log, S_INFO, __FILE__, __LINE__, NULL, |
330 | 0 | "log file has been reopened"); |
331 | | /* |
332 | | * log_background applies only to log_default logger |
333 | | */ |
334 | 0 | if (log == log_default && log_background && |
335 | 0 | log->type == SAY_LOGGER_FILE) { |
336 | 0 | dup2(log_default->fd, STDOUT_FILENO); |
337 | 0 | dup2(log_default->fd, STDERR_FILENO); |
338 | 0 | } |
339 | |
|
340 | 0 | return 0; |
341 | 0 | } |
342 | | |
343 | | struct rotate_task { |
344 | | struct coio_task base; |
345 | | struct log *log; |
346 | | struct ev_loop *loop; |
347 | | }; |
348 | | |
349 | | static int |
350 | | logrotate_cb(struct coio_task *ptr) |
351 | 0 | { |
352 | 0 | struct rotate_task *task = (struct rotate_task *) ptr; |
353 | 0 | struct log *log = task->log; |
354 | 0 | if (log_rotate(log) < 0) |
355 | 0 | diag_log(); |
356 | 0 | tt_pthread_mutex_lock(&log->rotate_mutex); |
357 | 0 | assert(log->rotating_threads > 0); |
358 | 0 | log->rotating_threads--; |
359 | 0 | if (log->rotating_threads == 0) |
360 | 0 | tt_pthread_cond_signal(&log->rotate_cond); |
361 | 0 | tt_pthread_mutex_unlock(&log->rotate_mutex); |
362 | 0 | return 0; |
363 | 0 | } |
364 | | |
365 | | static int |
366 | | logrotate_cleanup_cb(struct coio_task *ptr) |
367 | 0 | { |
368 | 0 | struct rotate_task *task = (struct rotate_task *) ptr; |
369 | 0 | coio_task_destroy(&task->base); |
370 | 0 | free(task); |
371 | 0 | return 0; |
372 | 0 | } |
373 | | |
374 | | void |
375 | | say_logrotate(struct ev_loop *loop, struct ev_signal *w, int revents) |
376 | 0 | { |
377 | 0 | (void) loop; |
378 | 0 | (void) w; |
379 | 0 | (void) revents; |
380 | 0 | int saved_errno = errno; |
381 | 0 | struct log *log; |
382 | 0 | rlist_foreach_entry(log, &log_rotate_list, in_log_list) { |
383 | 0 | struct rotate_task *task = |
384 | 0 | (struct rotate_task *) calloc(1, sizeof(*task)); |
385 | 0 | if (task == NULL) { |
386 | 0 | diag_set(OutOfMemory, sizeof(*task), "malloc", |
387 | 0 | "say_logrotate"); |
388 | 0 | diag_log(); |
389 | 0 | continue; |
390 | 0 | } |
391 | 0 | tt_pthread_mutex_lock(&log->rotate_mutex); |
392 | 0 | log->rotating_threads++; |
393 | 0 | tt_pthread_mutex_unlock(&log->rotate_mutex); |
394 | 0 | coio_task_create(&task->base, logrotate_cb, logrotate_cleanup_cb); |
395 | 0 | task->log = log; |
396 | 0 | task->loop = loop(); |
397 | 0 | coio_task_post(&task->base); |
398 | 0 | } |
399 | 0 | errno = saved_errno; |
400 | 0 | } |
401 | | |
402 | | /** |
403 | | * Initialize the logger pipe: a standalone |
404 | | * process which is fed all log messages. |
405 | | */ |
406 | | static int |
407 | | log_pipe_init(struct log *log, const char *init_str) |
408 | 0 | { |
409 | 0 | int pipefd[2]; |
410 | 0 | char cmd[] = { "/bin/sh" }; |
411 | 0 | char args[] = { "-c" }; |
412 | 0 | char *argv[] = { cmd, args, (char *) init_str, NULL }; |
413 | 0 | log->type = SAY_LOGGER_PIPE; |
414 | 0 | sigset_t mask; |
415 | 0 | sigemptyset(&mask); |
416 | 0 | sigaddset(&mask, SIGCHLD); |
417 | |
|
418 | 0 | if (pthread_sigmask(SIG_BLOCK, &mask, NULL) == -1) |
419 | 0 | say_syserror("pthread_sigmask"); |
420 | |
|
421 | 0 | if (pipe(pipefd) == -1) { |
422 | 0 | diag_set(SystemError, "failed to create pipe"); |
423 | 0 | return -1; |
424 | 0 | } |
425 | | |
426 | | /* flush buffers to avoid multiple output */ |
427 | | /* https://github.com/tarantool/tarantool/issues/366 */ |
428 | 0 | fflush(stdout); |
429 | 0 | fflush(stderr); |
430 | |
|
431 | 0 | log->pid = fork(); |
432 | 0 | if (log->pid == -1) { |
433 | 0 | diag_set(SystemError, "failed to create process"); |
434 | 0 | return -1; |
435 | 0 | } |
436 | | |
437 | 0 | if (log->pid == 0) { |
438 | 0 | pthread_sigmask(SIG_UNBLOCK, &mask, NULL); |
439 | |
|
440 | 0 | close(pipefd[1]); |
441 | 0 | dup2(pipefd[0], STDIN_FILENO); |
442 | | /* |
443 | | * Move to an own process group, to not |
444 | | * receive signals from the controlling |
445 | | * tty. This keeps the log open as long as |
446 | | * the parent is around. When the parent |
447 | | * dies, we get SIGPIPE and terminate. |
448 | | */ |
449 | 0 | setpgid(0, 0); |
450 | 0 | execv(argv[0], argv); /* does not return */ |
451 | 0 | diag_set(SystemError, "can't start logger: %s", init_str); |
452 | 0 | return -1; |
453 | 0 | } |
454 | 0 | #if !TARGET_OS_DARWIN |
455 | | /* |
456 | | * A courtesy to a DBA who might have |
457 | | * misconfigured the logger option: check whether |
458 | | * or not the logger process has started, and if |
459 | | * it didn't, abort. Notice, that if the logger |
460 | | * makes a slow start this is futile. |
461 | | */ |
462 | 0 | struct timespec timeout; |
463 | 0 | timeout.tv_sec = 0; |
464 | 0 | timeout.tv_nsec = 1; /* Mostly to trigger preemption. */ |
465 | | #if defined(__OpenBSD__) |
466 | | int sig = 0; |
467 | | sigwait(&mask, &sig); |
468 | | if (sig ==SIGCHLD) { |
469 | | diag_set(IllegalParams, "logger process died"); |
470 | | return -1; |
471 | | } |
472 | | #else |
473 | 0 | if (sigtimedwait(&mask, NULL, &timeout) == SIGCHLD) { |
474 | 0 | diag_set(IllegalParams, "logger process died"); |
475 | 0 | return -1; |
476 | 0 | } |
477 | 0 | #endif |
478 | 0 | #endif |
479 | | /* OK, let's hope for the best. */ |
480 | 0 | pthread_sigmask(SIG_UNBLOCK, &mask, NULL); |
481 | 0 | close(pipefd[0]); |
482 | 0 | log->fd = pipefd[1]; |
483 | 0 | return 0; |
484 | 0 | } |
485 | | |
486 | | /** |
487 | | * Initialize logging to a file and set up a log |
488 | | * rotation signal. |
489 | | */ |
490 | | static int |
491 | | log_file_init(struct log *log, const char *init_str) |
492 | 0 | { |
493 | 0 | int fd; |
494 | 0 | log->path = abspath(init_str); |
495 | 0 | log->type = SAY_LOGGER_FILE; |
496 | 0 | if (log->path == NULL) { |
497 | 0 | diag_set(OutOfMemory, strlen(init_str), "malloc", "abspath"); |
498 | 0 | return -1; |
499 | 0 | } |
500 | 0 | fd = open(log->path, O_WRONLY | O_APPEND | O_CREAT, |
501 | 0 | S_IRUSR | S_IWUSR | S_IRGRP); |
502 | 0 | if (fd < 0) { |
503 | 0 | diag_set(SystemError, "can't open log file: %s", log->path); |
504 | 0 | return -1; |
505 | 0 | } |
506 | 0 | log->fd = fd; |
507 | 0 | return 0; |
508 | 0 | } |
509 | | |
510 | | /** |
511 | | * Connect to syslogd using UNIX socket. |
512 | | * @param path UNIX socket path. |
513 | | * @retval not 0 Socket descriptor. |
514 | | * @retval -1 Socket error. |
515 | | */ |
516 | | static inline int |
517 | | syslog_connect_unix(const char *path) |
518 | 0 | { |
519 | 0 | int fd = socket(PF_UNIX, SOCK_DGRAM, 0); |
520 | 0 | if (fd < 0) { |
521 | 0 | diag_set(SystemError, "socket"); |
522 | 0 | return -1; |
523 | 0 | } |
524 | 0 | struct sockaddr_un un; |
525 | 0 | memset(&un, 0, sizeof(un)); |
526 | 0 | snprintf(un.sun_path, sizeof(un.sun_path), "%s", path); |
527 | 0 | un.sun_family = AF_UNIX; |
528 | 0 | if (connect(fd, (struct sockaddr *) &un, sizeof(un)) != 0) { |
529 | 0 | diag_set(SystemError, "connect"); |
530 | 0 | close(fd); |
531 | 0 | return -1; |
532 | 0 | } |
533 | 0 | return fd; |
534 | 0 | } |
535 | | |
536 | | /** |
537 | | * Connect to remote syslogd using server:port. |
538 | | * @param server_address address of remote host. |
539 | | * @retval not 0 Socket descriptor. |
540 | | * @retval -1 Socket error. |
541 | | */ |
542 | | static int |
543 | | syslog_connect_remote(const char *server_address) |
544 | 0 | { |
545 | 0 | struct addrinfo *inf, hints, *ptr; |
546 | 0 | char *remote; |
547 | 0 | char buf[10]; |
548 | 0 | char *portnum, *copy; |
549 | 0 | int fd = -1; |
550 | 0 | int ret; |
551 | |
|
552 | 0 | copy = strdup(server_address); |
553 | 0 | if (copy == NULL) { |
554 | 0 | diag_set(OutOfMemory, strlen(server_address), "malloc", |
555 | 0 | "stslog server address"); |
556 | 0 | return fd; |
557 | 0 | } |
558 | 0 | portnum = copy; |
559 | 0 | remote = strsep(&portnum, ":"); |
560 | 0 | if (portnum == NULL) { |
561 | 0 | snprintf(buf, sizeof(buf), "%d", SAY_SYSLOG_DEFAULT_PORT); |
562 | 0 | portnum = buf; |
563 | 0 | } |
564 | 0 | memset(&hints, 0, sizeof(hints)); |
565 | 0 | hints.ai_family = AF_INET; |
566 | 0 | hints.ai_socktype = SOCK_DGRAM; |
567 | 0 | hints.ai_protocol = IPPROTO_UDP; |
568 | |
|
569 | 0 | ret = getaddrinfo(remote, portnum, &hints, &inf); |
570 | 0 | if (ret != 0) { |
571 | 0 | errno = EIO; |
572 | 0 | diag_set(SystemError, "getaddrinfo: %s", |
573 | 0 | gai_strerror(ret)); |
574 | 0 | goto out; |
575 | 0 | } |
576 | 0 | for (ptr = inf; ptr; ptr = ptr->ai_next) { |
577 | 0 | fd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); |
578 | 0 | if (fd < 0) { |
579 | 0 | diag_set(SystemError, "socket"); |
580 | 0 | continue; |
581 | 0 | } |
582 | 0 | if (connect(fd, inf->ai_addr, inf->ai_addrlen) != 0) { |
583 | 0 | close(fd); |
584 | 0 | fd = -1; |
585 | 0 | diag_set(SystemError, "connect"); |
586 | 0 | continue; |
587 | 0 | } |
588 | 0 | break; |
589 | 0 | } |
590 | 0 | freeaddrinfo(inf); |
591 | 0 | out: |
592 | 0 | free(copy); |
593 | 0 | return fd; |
594 | 0 | } |
595 | | |
596 | | static inline int |
597 | | log_syslog_connect(struct log *log) |
598 | 0 | { |
599 | | /* |
600 | | * If server option is not set use '/dev/log' for Linux and |
601 | | * '/var/run/syslog' for Mac. |
602 | | */ |
603 | 0 | switch (log->syslog_server_type) { |
604 | 0 | case SAY_SYSLOG_UNIX: |
605 | 0 | log->fd = syslog_connect_unix(log->path); |
606 | 0 | break; |
607 | 0 | case SAY_SYSLOG_REMOTE: |
608 | 0 | log->fd = syslog_connect_remote(log->path); |
609 | 0 | break; |
610 | 0 | default: |
611 | 0 | log->fd = syslog_connect_unix("/dev/log"); |
612 | 0 | if (log->fd < 0) |
613 | 0 | log->fd = syslog_connect_unix("/var/run/syslog"); |
614 | |
|
615 | 0 | } |
616 | 0 | return log->fd; |
617 | 0 | } |
618 | | |
619 | | /** Initialize logging to syslog */ |
620 | | static int |
621 | | log_syslog_init(struct log *log, const char *init_str) |
622 | 0 | { |
623 | 0 | struct say_syslog_opts opts; |
624 | 0 | log->type = SAY_LOGGER_SYSLOG; |
625 | |
|
626 | 0 | if (say_parse_syslog_opts(init_str, &opts) < 0) |
627 | 0 | return -1; |
628 | | |
629 | 0 | log->syslog_server_type = opts.server_type; |
630 | 0 | if (log->syslog_server_type != SAY_SYSLOG_DEFAULT) { |
631 | 0 | log->path = strdup(opts.server_path); |
632 | 0 | if (log->path == NULL) { |
633 | 0 | diag_set(OutOfMemory, strlen(opts.server_path), |
634 | 0 | "malloc", "server address"); |
635 | 0 | return -1; |
636 | 0 | } |
637 | 0 | } |
638 | 0 | if (opts.identity == NULL) |
639 | 0 | log->syslog_ident = strdup("tarantool"); |
640 | 0 | else |
641 | 0 | log->syslog_ident = strdup(opts.identity); |
642 | 0 | if (log->syslog_ident == NULL) { |
643 | 0 | diag_set(OutOfMemory, strlen(opts.identity), "malloc", |
644 | 0 | "log->syslog_ident"); |
645 | 0 | return -1; |
646 | 0 | } |
647 | | |
648 | 0 | if (opts.facility == syslog_facility_MAX) |
649 | 0 | log->syslog_facility = SYSLOG_LOCAL7; |
650 | 0 | else |
651 | 0 | log->syslog_facility = opts.facility; |
652 | 0 | say_free_syslog_opts(&opts); |
653 | 0 | log->fd = log_syslog_connect(log); |
654 | 0 | if (log->fd < 0) { |
655 | 0 | diag_log(); |
656 | | /* syslog indent is freed in atexit(). */ |
657 | 0 | diag_set(SystemError, "syslog logger"); |
658 | 0 | return -1; |
659 | 0 | } |
660 | 0 | return 0; |
661 | 0 | } |
662 | | |
663 | | /** |
664 | | * Initialize logging subsystem to use in daemon mode. |
665 | | */ |
666 | | int |
667 | | log_create(struct log *log, const char *init_str, int nonblock) |
668 | 0 | { |
669 | 0 | log->pid = 0; |
670 | 0 | log->syslog_ident = NULL; |
671 | 0 | log->path = NULL; |
672 | 0 | log->format_func = say_format_plain; |
673 | 0 | log->level = S_INFO; |
674 | 0 | log->rotating_threads = 0; |
675 | 0 | tt_pthread_mutex_init(&log->rotate_mutex, NULL); |
676 | 0 | tt_pthread_cond_init(&log->rotate_cond, NULL); |
677 | 0 | setvbuf(stderr, NULL, _IONBF, 0); |
678 | |
|
679 | 0 | if (init_str != NULL) { |
680 | 0 | enum say_logger_type type; |
681 | 0 | if (say_parse_logger_type(&init_str, &type)) |
682 | 0 | return -1; |
683 | | |
684 | 0 | int rc; |
685 | 0 | switch (type) { |
686 | 0 | case SAY_LOGGER_PIPE: |
687 | 0 | log->nonblock = (nonblock >= 0)? nonblock: true; |
688 | 0 | rc = log_pipe_init(log, init_str); |
689 | 0 | break; |
690 | 0 | case SAY_LOGGER_SYSLOG: |
691 | 0 | log->nonblock = (nonblock >= 0)? nonblock: true; |
692 | 0 | rc = log_syslog_init(log, init_str); |
693 | 0 | break; |
694 | 0 | case SAY_LOGGER_FILE: |
695 | 0 | default: |
696 | 0 | log->nonblock = (nonblock >= 0)? nonblock: false; |
697 | 0 | rc = log_file_init(log, init_str); |
698 | 0 | break; |
699 | 0 | } |
700 | 0 | if (rc < 0) { |
701 | 0 | return -1; |
702 | 0 | } |
703 | | /* |
704 | | * Set non-blocking mode if a non-default log |
705 | | * output is set. Avoid setting stdout to |
706 | | * non-blocking: this will garble interactive |
707 | | * console output. |
708 | | */ |
709 | 0 | log_set_nonblock(log); |
710 | 0 | } else { |
711 | 0 | log->type = SAY_LOGGER_STDERR; |
712 | 0 | log->fd = STDERR_FILENO; |
713 | 0 | } |
714 | 0 | if (log->type == SAY_LOGGER_FILE) |
715 | 0 | rlist_add_entry(&log_rotate_list, log, in_log_list); |
716 | 0 | else |
717 | 0 | rlist_create(&log->in_log_list); |
718 | 0 | return 0; |
719 | 0 | } |
720 | | |
721 | | bool |
722 | | say_logger_initialized(void) |
723 | 0 | { |
724 | 0 | return log_default == &log_std; |
725 | 0 | } |
726 | | |
727 | | void |
728 | | say_logger_init(const char *init_str, int level, int nonblock, |
729 | | const char *format) |
730 | 0 | { |
731 | | /* |
732 | | * The logger may be early configured |
733 | | * by hands without configuing the whole box. |
734 | | */ |
735 | 0 | if (say_logger_initialized()) { |
736 | 0 | say_set_log_level(level); |
737 | 0 | say_set_log_format(say_format_by_name(format)); |
738 | 0 | return; |
739 | 0 | } |
740 | | |
741 | 0 | if (log_create(&log_std, init_str, nonblock) < 0) |
742 | 0 | goto fail; |
743 | | |
744 | 0 | log_default = &log_std; |
745 | |
|
746 | 0 | switch (log_default->type) { |
747 | 0 | case SAY_LOGGER_PIPE: |
748 | 0 | fprintf(stderr, "started logging into a pipe," |
749 | 0 | " SIGHUP log rotation disabled\n"); |
750 | 0 | break; |
751 | 0 | case SAY_LOGGER_SYSLOG: |
752 | 0 | fprintf(stderr, "started logging into a syslog," |
753 | 0 | " SIGHUP log rotation disabled\n"); |
754 | 0 | default: |
755 | 0 | break; |
756 | 0 | } |
757 | 0 | _say = say_default; |
758 | 0 | say_set_log_level(level); |
759 | 0 | log_pid = log_default->pid; |
760 | 0 | say_set_log_format(say_format_by_name(format)); |
761 | |
|
762 | 0 | return; |
763 | 0 | fail: |
764 | 0 | diag_log(); |
765 | 0 | panic("failed to initialize logging subsystem"); |
766 | 0 | } |
767 | | |
768 | | int |
769 | | say_set_background(void) |
770 | 0 | { |
771 | 0 | assert(say_logger_initialized()); |
772 | | |
773 | 0 | if (log_background) |
774 | 0 | return 0; |
775 | | |
776 | 0 | log_background = true; |
777 | |
|
778 | 0 | fflush(stderr); |
779 | 0 | fflush(stdout); |
780 | |
|
781 | 0 | int fd; |
782 | 0 | int fd_null = -1; |
783 | 0 | if (log_default->fd == STDERR_FILENO) { |
784 | 0 | fd_null = open("/dev/null", O_WRONLY); |
785 | 0 | if (fd_null < 0) { |
786 | 0 | diag_set(SystemError, "open(/dev/null)"); |
787 | 0 | return -1; |
788 | 0 | } |
789 | 0 | fd = fd_null; |
790 | 0 | } else { |
791 | 0 | fd = log_default->fd; |
792 | 0 | } |
793 | | |
794 | 0 | dup2(fd, STDERR_FILENO); |
795 | 0 | dup2(fd, STDOUT_FILENO); |
796 | 0 | if (fd_null != -1) |
797 | 0 | close(fd_null); |
798 | |
|
799 | 0 | return 0; |
800 | 0 | } |
801 | | |
802 | | void |
803 | | say_logger_free(void) |
804 | 0 | { |
805 | 0 | if (say_logger_initialized()) |
806 | 0 | log_destroy(&log_std); |
807 | 0 | } |
808 | | |
809 | | /** {{{ Formatters */ |
810 | | |
811 | | /** |
812 | | * Format the log message in compact form: |
813 | | * MESSAGE: ERROR |
814 | | * |
815 | | * Used during boot time, e.g. without box.cfg(). |
816 | | */ |
817 | | static int |
818 | | say_format_boot(struct log *log, char *buf, int len, int level, |
819 | | const char *module, const char *filename, int line, |
820 | | const char *error, const char *format, va_list ap) |
821 | 0 | { |
822 | 0 | (void)log; |
823 | 0 | (void)module; |
824 | 0 | (void)filename; |
825 | 0 | (void)line; |
826 | 0 | (void)level; |
827 | 0 | int total = 0; |
828 | 0 | SNPRINT(total, vsnprintf, buf, len, format, ap); |
829 | 0 | if (error != NULL) |
830 | 0 | SNPRINT(total, snprintf, buf, len, ": %s", error); |
831 | 0 | SNPRINT(total, snprintf, buf, len, "\n"); |
832 | 0 | return total; |
833 | 0 | } |
834 | | |
835 | | /** |
836 | | * Format the log message in Tarantool format: |
837 | | * YYYY-MM-DD hh:mm:ss.ms [PID]: CORD/FID/FIBERNAME/MODULENAME LEVEL> MSG |
838 | | */ |
839 | | int |
840 | | say_format_plain(struct log *log, char *buf, int len, int level, |
841 | | const char *module, const char *filename, int line, |
842 | | const char *error, const char *format, va_list ap) |
843 | 0 | { |
844 | 0 | int total = 0; |
845 | | |
846 | | /* |
847 | | * Every message written to syslog has a header that contains |
848 | | * the current time and pid (see format_syslog_header) so we |
849 | | * exclude them from the message body. |
850 | | */ |
851 | 0 | if (log->type != SAY_LOGGER_SYSLOG) { |
852 | 0 | struct tm tm; |
853 | 0 | double tm_sec; |
854 | 0 | get_current_time(&tm, &tm_sec); |
855 | | /* Print time in format 2012-08-07 18:30:00.634 */ |
856 | 0 | SNPRINT(total, strftime, buf, len, "%F %H:%M", &tm); |
857 | 0 | SNPRINT(total, snprintf, buf, len, ":%06.3f ", tm_sec); |
858 | | /* Print pid */ |
859 | 0 | SNPRINT(total, snprintf, buf, len, "[%i] ", getpid()); |
860 | 0 | } |
861 | | |
862 | 0 | SNPRINT(total, snprintf, buf, len, "%s", cord()->name); |
863 | 0 | if (fiber() && fiber()->fid != FIBER_ID_SCHED) { |
864 | 0 | SNPRINT(total, snprintf, buf, len, "/%llu/%s", |
865 | 0 | (long long)fiber()->fid, fiber_name(fiber())); |
866 | 0 | } |
867 | | |
868 | 0 | if (module != NULL) |
869 | 0 | SNPRINT(total, snprintf, buf, len, "/%s", module); |
870 | | |
871 | 0 | if (level == S_WARN || level == S_ERROR || level == S_SYSERROR) { |
872 | | /* Primitive basename(filename) */ |
873 | 0 | if (filename) { |
874 | 0 | for (const char *f = filename; *f; f++) |
875 | 0 | if (*f == '/' && *(f + 1) != '\0') |
876 | 0 | filename = f + 1; |
877 | 0 | SNPRINT(total, snprintf, buf, len, " %s:%i", filename, |
878 | 0 | line); |
879 | 0 | } |
880 | 0 | } |
881 | | |
882 | 0 | SNPRINT(total, snprintf, buf, len, " %c> ", level_chars[level]); |
883 | | |
884 | 0 | SNPRINT(total, vsnprintf, buf, len, format, ap); |
885 | 0 | if (error != NULL) |
886 | 0 | SNPRINT(total, snprintf, buf, len, ": %s", error); |
887 | | |
888 | 0 | SNPRINT(total, snprintf, buf, len, "\n"); |
889 | 0 | return total; |
890 | 0 | } |
891 | | |
892 | | /** |
893 | | * Format log message in json format: |
894 | | * {"time": 1507026445.23232, "level": "WARN", "message": <message>, |
895 | | * "pid": <pid>, "cord_name": <name>, "fiber_id": <id>, |
896 | | * "fiber_name": <fiber_name>, file": <filename>, "line": <fds>, |
897 | | * "module": <module_name>} |
898 | | */ |
899 | | int |
900 | | say_format_json(struct log *log, char *buf, int len, int level, |
901 | | const char *module, const char *filename, int line, |
902 | | const char *error, const char *format, va_list ap) |
903 | 0 | { |
904 | 0 | (void) log; |
905 | 0 | int total = 0; |
906 | |
|
907 | 0 | SNPRINT(total, snprintf, buf, len, "{\"time\": \""); |
908 | | |
909 | 0 | struct tm tm; |
910 | 0 | double tm_sec; |
911 | 0 | get_current_time(&tm, &tm_sec); |
912 | 0 | int written = strftime(buf, len, "%FT%H:%M", &tm); |
913 | 0 | buf += written, len -= written, total += written; |
914 | 0 | SNPRINT(total, snprintf, buf, len, ":%06.3f", tm_sec); |
915 | 0 | written = strftime(buf, len, "%z", &tm); |
916 | 0 | buf += written, len -= written, total += written; |
917 | 0 | SNPRINT(total, snprintf, buf, len, "\", "); |
918 | | |
919 | 0 | SNPRINT(total, snprintf, buf, len, "\"level\": \"%s\", ", |
920 | 0 | level_strs[level]); |
921 | | |
922 | 0 | if (strncmp(format, "json", sizeof("json")) == 0) { |
923 | | /* |
924 | | * Message is already JSON-formatted. |
925 | | * Get rid of {} brackets and append to the output buffer. |
926 | | */ |
927 | 0 | const char *str = va_arg(ap, const char *); |
928 | 0 | assert(str != NULL); |
929 | 0 | int str_len = strlen(str); |
930 | 0 | assert(str_len > 2 && str[0] == '{' && str[str_len - 1] == '}'); |
931 | 0 | SNPRINT(total, snprintf, buf, len, "%.*s, ", |
932 | 0 | str_len - 2, str + 1); |
933 | 0 | } else { |
934 | | /* Format message */ |
935 | 0 | char *tmp = tt_static_buf(); |
936 | 0 | if (vsnprintf(tmp, TT_STATIC_BUF_LEN, format, ap) < 0) |
937 | 0 | return -1; |
938 | 0 | SNPRINT(total, snprintf, buf, len, "\"message\": \""); |
939 | | /* Escape and print message */ |
940 | 0 | SNPRINT(total, json_escape, buf, len, tmp); |
941 | 0 | SNPRINT(total, snprintf, buf, len, "\", "); |
942 | 0 | } |
943 | | |
944 | | /* in case of system errors */ |
945 | 0 | if (error) { |
946 | 0 | SNPRINT(total, snprintf, buf, len, "\"error\": \""); |
947 | 0 | SNPRINT(total, json_escape, buf, len, error); |
948 | 0 | SNPRINT(total, snprintf, buf, len, "\", "); |
949 | 0 | } |
950 | | |
951 | 0 | SNPRINT(total, snprintf, buf, len, "\"pid\": %i ", getpid()); |
952 | 0 | SNPRINT(total, snprintf, buf, len, ", \"cord_name\": \""); |
953 | 0 | SNPRINT(total, json_escape, buf, len, cord()->name); |
954 | 0 | SNPRINT(total, snprintf, buf, len, "\""); |
955 | 0 | if (fiber() && fiber()->fid != FIBER_ID_SCHED) { |
956 | 0 | SNPRINT(total, snprintf, buf, len, ", \"fiber_id\": %llu, ", |
957 | 0 | (long long)fiber()->fid); |
958 | 0 | SNPRINT(total, snprintf, buf, len, "\"fiber_name\": \""); |
959 | 0 | SNPRINT(total, json_escape, buf, len, fiber()->name); |
960 | 0 | SNPRINT(total, snprintf, buf, len, "\""); |
961 | 0 | } |
962 | | |
963 | 0 | if (filename) { |
964 | 0 | SNPRINT(total, snprintf, buf, len, ", \"file\": \""); |
965 | 0 | SNPRINT(total, json_escape, buf, len, filename); |
966 | 0 | SNPRINT(total, snprintf, buf, len, "\", \"line\": %i", line); |
967 | 0 | } |
968 | 0 | if (module != NULL) { |
969 | 0 | SNPRINT(total, snprintf, buf, len, ", \"module\": \""); |
970 | 0 | SNPRINT(total, json_escape, buf, len, module); |
971 | 0 | SNPRINT(total, snprintf, buf, len, "\""); |
972 | 0 | } |
973 | 0 | SNPRINT(total, snprintf, buf, len, "}\n"); |
974 | 0 | return total; |
975 | 0 | } |
976 | | |
977 | | /** Wrapper around log->format_func to be used with SNPRINT. */ |
978 | | static int |
979 | | format_func_adapter(char *buf, int len, struct log *log, int level, |
980 | | const char *module, const char *filename, int line, |
981 | | const char *error, const char *format, va_list ap) |
982 | 0 | { |
983 | 0 | return log->format_func(log, buf, len, level, module, filename, line, |
984 | 0 | error, format, ap); |
985 | 0 | } |
986 | | |
987 | | /** |
988 | | * Format the header of a log message in syslog format. |
989 | | * |
990 | | * See RFC 5424 and RFC 3164. RFC 3164 is compatible with RFC 5424, |
991 | | * so it is implemented. |
992 | | * Protocol: |
993 | | * <PRIORITY_VALUE>TIMESTAMP IDENTATION[PID]: CORD/FID/FIBERNAME LEVEL> MSG |
994 | | * - Priority value is encoded as message subject * 8 and bitwise |
995 | | * OR with message level; |
996 | | * - Timestamp must be encoded in the format: Mmm dd hh:mm:ss; |
997 | | * Mmm - moth abbreviation; |
998 | | * - Identation is application name. By default it is "tarantool"; |
999 | | */ |
1000 | | static int |
1001 | | format_syslog_header(char *buf, int len, int level, |
1002 | | enum syslog_facility facility, const char *ident) |
1003 | 0 | { |
1004 | 0 | struct tm tm; |
1005 | 0 | double tm_sec; |
1006 | 0 | get_current_time(&tm, &tm_sec); |
1007 | |
|
1008 | 0 | int total = 0; |
1009 | | |
1010 | | /* Format syslog header according to RFC */ |
1011 | 0 | int prio = (facility << 3) | level_to_syslog_priority(level); |
1012 | 0 | SNPRINT(total, snprintf, buf, len, "<%d>", prio); |
1013 | 0 | SNPRINT(total, strftime, buf, len, "%h %e %T ", &tm); |
1014 | 0 | SNPRINT(total, snprintf, buf, len, "%s[%d]: ", ident, |
1015 | 0 | getpid()); |
1016 | 0 | return total; |
1017 | 0 | } |
1018 | | |
1019 | | /** Formatters }}} */ |
1020 | | |
1021 | | /** {{{ Loggers */ |
1022 | | |
1023 | | /* |
1024 | | * From pipe(7): |
1025 | | * POSIX.1 says that write(2)s of less than PIPE_BUF bytes must be atomic: |
1026 | | * the output data is written to the pipe as a contiguous sequence. Writes |
1027 | | * of more than PIPE_BUF bytes may be nonatomic: the kernel may interleave |
1028 | | * the data with data written by other processes. PIPE_BUF is 4k on Linux. |
1029 | | * |
1030 | | * Nevertheless, let's ignore the fact that messages can be interleaved in |
1031 | | * some situations and set SAY_BUF_LEN_MAX to 16k for now. |
1032 | | */ |
1033 | | enum { SAY_BUF_LEN_MAX = 16 * 1024 }; |
1034 | | static __thread char say_buf[SAY_BUF_LEN_MAX]; |
1035 | | |
1036 | | /** |
1037 | | * Wrapper over write which ensures, that writes not more than buffer size. |
1038 | | */ |
1039 | | static ssize_t |
1040 | | safe_write(int fd, const char *buf, int size) |
1041 | 0 | { |
1042 | 0 | assert(size >= 0); |
1043 | | /* Writes at most SAY_BUF_LEN_MAX - 1 |
1044 | | * (1 byte was taken for 0 byte in vsnprintf). |
1045 | | */ |
1046 | 0 | return write(fd, buf, MIN(size, SAY_BUF_LEN_MAX - 1)); |
1047 | 0 | } |
1048 | | |
1049 | | /** |
1050 | | * Common part of say_default() and say_from_lua(). |
1051 | | */ |
1052 | | static void |
1053 | | say_internal(int level, bool check_level, const char *module, |
1054 | | const char *filename, int line, const char *error, |
1055 | | const char *format, va_list ap) |
1056 | 0 | { |
1057 | 0 | int errsv = errno; |
1058 | 0 | int total = log_vsay(log_default, level, check_level, module, filename, |
1059 | 0 | line, error, format, ap); |
1060 | 0 | if (total > 0 && |
1061 | 0 | level == S_FATAL && log_default->fd != STDERR_FILENO) { |
1062 | 0 | ssize_t r = safe_write(STDERR_FILENO, say_buf, total); |
1063 | 0 | (void)r; |
1064 | 0 | } |
1065 | 0 | errno = errsv; /* Preserve the errno. */ |
1066 | 0 | } |
1067 | | |
1068 | | /** |
1069 | | * Default say function. |
1070 | | */ |
1071 | | static void |
1072 | | say_default(int level, const char *filename, int line, const char *error, |
1073 | | const char *format, ...) |
1074 | 0 | { |
1075 | 0 | va_list ap; |
1076 | 0 | va_start(ap, format); |
1077 | 0 | if (level <= log_level_flightrec) { |
1078 | 0 | assert(log_write_flightrec != NULL); |
1079 | 0 | int errsv = errno; |
1080 | 0 | va_list ap_copy; |
1081 | 0 | va_copy(ap_copy, ap); |
1082 | 0 | log_write_flightrec(level, filename, line, error, format, |
1083 | 0 | ap_copy); |
1084 | 0 | va_end(ap_copy); |
1085 | 0 | errno = errsv; /* Preserve the errno. */ |
1086 | 0 | } |
1087 | 0 | say_internal(level, true, NULL, filename, line, error, format, ap); |
1088 | 0 | va_end(ap); |
1089 | 0 | } |
1090 | | |
1091 | | /** |
1092 | | * Format and print a message to the default Tarantool log. |
1093 | | * This function is used by Lua say(). |
1094 | | * Unlike say_default(), it doesn't compare level against log->level, the check |
1095 | | * is performed in Lua, because each module can have its own log level. |
1096 | | * Also it takes module name, and doesn't take an error argument. |
1097 | | */ |
1098 | | void |
1099 | | say_from_lua(int level, const char *module, const char *filename, int line, |
1100 | | const char *format, ...) |
1101 | 0 | { |
1102 | 0 | va_list ap; |
1103 | 0 | va_start(ap, format); |
1104 | 0 | say_internal(level, false, module, filename, line, NULL, format, ap); |
1105 | 0 | va_end(ap); |
1106 | 0 | } |
1107 | | |
1108 | | /** |
1109 | | * A variadic wrapper over log_write_flightrec(), used by Lua say(). |
1110 | | */ |
1111 | | void |
1112 | | log_write_flightrec_from_lua(int level, const char *filename, int line, ...) |
1113 | 0 | { |
1114 | 0 | assert(log_write_flightrec != NULL); |
1115 | 0 | int errsv = errno; |
1116 | 0 | va_list ap; |
1117 | 0 | va_start(ap, line); |
1118 | | /* ap contains a single string, which is already formatted. */ |
1119 | 0 | log_write_flightrec(level, filename, line, NULL, "%s", ap); |
1120 | 0 | errno = errsv; /* Preserve the errno. */ |
1121 | 0 | va_end(ap); |
1122 | 0 | } |
1123 | | |
1124 | | /** |
1125 | | * File and pipe logger |
1126 | | */ |
1127 | | static void |
1128 | | write_to_file(struct log *log, int total) |
1129 | 0 | { |
1130 | 0 | assert(log->type == SAY_LOGGER_FILE || |
1131 | 0 | log->type == SAY_LOGGER_PIPE || |
1132 | 0 | log->type == SAY_LOGGER_STDERR); |
1133 | 0 | assert(total >= 0); |
1134 | 0 | ssize_t r = safe_write(log->fd, say_buf, total); |
1135 | 0 | (void) r; /* silence gcc warning */ |
1136 | 0 | } |
1137 | | |
1138 | | /** |
1139 | | * Syslog logger |
1140 | | */ |
1141 | | static void |
1142 | | write_to_syslog(struct log *log, int total) |
1143 | 0 | { |
1144 | 0 | assert(log->type == SAY_LOGGER_SYSLOG); |
1145 | 0 | assert(total >= 0); |
1146 | 0 | if (log->fd < 0 || safe_write(log->fd, say_buf, total) <= 0) { |
1147 | | /* |
1148 | | * Try to reconnect, if write to syslog has |
1149 | | * failed. Syslog write can fail, if, for example, |
1150 | | * syslogd is restarted. In such a case write to |
1151 | | * UNIX socket starts return -1 even for UDP. |
1152 | | */ |
1153 | 0 | if (log->fd >= 0) |
1154 | 0 | close(log->fd); |
1155 | 0 | log->fd = log_syslog_connect(log); |
1156 | 0 | if (log->fd >= 0) { |
1157 | 0 | log_set_nonblock(log); |
1158 | | /* |
1159 | | * In a case or error the log message is |
1160 | | * lost. We can not wait for connection - |
1161 | | * it would block thread. Try to reconnect |
1162 | | * on next vsay(). |
1163 | | */ |
1164 | 0 | ssize_t r = safe_write(log->fd, say_buf, total); |
1165 | 0 | (void) r; /* silence gcc warning */ |
1166 | 0 | } |
1167 | 0 | } |
1168 | 0 | } |
1169 | | |
1170 | | /** Loggers }}} */ |
1171 | | |
1172 | | /* |
1173 | | * Init string parser(s) |
1174 | | */ |
1175 | | |
1176 | | /** |
1177 | | * @retval string after prefix if a prefix is found, |
1178 | | * *str also is advanced to the prefix |
1179 | | * NULL a prefix is not found, str is left intact |
1180 | | */ |
1181 | | static const char * |
1182 | | say_parse_prefix(const char **str, const char *prefix) |
1183 | 0 | { |
1184 | 0 | size_t len = strlen(prefix); |
1185 | 0 | if (strncmp(*str, prefix, len) == 0) { |
1186 | 0 | *str = *str + len; |
1187 | 0 | return *str; |
1188 | 0 | } |
1189 | 0 | return NULL; |
1190 | 0 | } |
1191 | | |
1192 | | int |
1193 | | say_parse_logger_type(const char **str, enum say_logger_type *type) |
1194 | 0 | { |
1195 | 0 | if (say_parse_prefix(str, "|")) |
1196 | 0 | *type = SAY_LOGGER_PIPE; |
1197 | 0 | else if (say_parse_prefix(str, "file:")) |
1198 | 0 | *type = SAY_LOGGER_FILE; |
1199 | 0 | else if (say_parse_prefix(str, "pipe:")) |
1200 | 0 | *type = SAY_LOGGER_PIPE; |
1201 | 0 | else if (say_parse_prefix(str, "syslog:")) |
1202 | 0 | *type = SAY_LOGGER_SYSLOG; |
1203 | 0 | else if (strchr(*str, ':') == NULL) |
1204 | 0 | *type = SAY_LOGGER_FILE; |
1205 | 0 | else { |
1206 | 0 | diag_set(IllegalParams, logger_syntax_reminder); |
1207 | 0 | return -1; |
1208 | 0 | } |
1209 | 0 | return 0; |
1210 | 0 | } |
1211 | | |
1212 | | static const char *syslog_facility_strs[] = { |
1213 | | [SYSLOG_KERN] = "kern", |
1214 | | [SYSLOG_USER] = "user", |
1215 | | [SYSLOG_MAIL] = "mail", |
1216 | | [SYSLOG_DAEMON] = "daemon", |
1217 | | [SYSLOG_AUTH] = "auth", |
1218 | | [SYSLOG_INTERN] = "intern", |
1219 | | [SYSLOG_LPR] = "lpr", |
1220 | | [SYSLOG_NEWS] = "news", |
1221 | | [SYSLOG_UUCP] = "uucp", |
1222 | | [SYSLOG_CLOCK] = "clock", |
1223 | | [SYSLOG_AUTHPRIV] = "authpriv", |
1224 | | [SYSLOG_FTP] = "ftp", |
1225 | | [SYSLOG_NTP] = "ntp", |
1226 | | [SYSLOG_AUDIT] = "audit", |
1227 | | [SYSLOG_ALERT] = "alert", |
1228 | | [SYSLOG_CRON] = "cron", |
1229 | | [SYSLOG_LOCAL0] = "local0", |
1230 | | [SYSLOG_LOCAL1] = "local1", |
1231 | | [SYSLOG_LOCAL2] = "local2", |
1232 | | [SYSLOG_LOCAL3] = "local3", |
1233 | | [SYSLOG_LOCAL4] = "local4", |
1234 | | [SYSLOG_LOCAL5] = "local5", |
1235 | | [SYSLOG_LOCAL6] = "local6", |
1236 | | [SYSLOG_LOCAL7] = "local7", |
1237 | | [syslog_facility_MAX] = "unknown", |
1238 | | }; |
1239 | | |
1240 | | enum syslog_facility |
1241 | | say_syslog_facility_by_name(const char *facility) |
1242 | 0 | { |
1243 | 0 | return STR2ENUM(syslog_facility, facility); |
1244 | 0 | } |
1245 | | |
1246 | | int |
1247 | | say_parse_syslog_opts(const char *init_str, struct say_syslog_opts *opts) |
1248 | 0 | { |
1249 | 0 | opts->server_path = NULL; |
1250 | 0 | opts->server_type = SAY_SYSLOG_DEFAULT; |
1251 | 0 | opts->identity = NULL; |
1252 | 0 | opts->facility = syslog_facility_MAX; |
1253 | 0 | opts->copy = strdup(init_str); |
1254 | 0 | if (opts->copy == NULL) { |
1255 | 0 | diag_set(OutOfMemory, strlen(init_str), "malloc", "opts->copy"); |
1256 | 0 | return -1; |
1257 | 0 | } |
1258 | 0 | char *ptr = opts->copy; |
1259 | 0 | const char *option, *value; |
1260 | | |
1261 | | /* strsep() overwrites the separator with '\0' */ |
1262 | 0 | while ((option = strsep(&ptr, ","))) { |
1263 | 0 | if (*option == '\0') |
1264 | 0 | break; |
1265 | | |
1266 | 0 | value = option; |
1267 | 0 | if (say_parse_prefix(&value, "server=")) { |
1268 | 0 | if (opts->server_path != NULL || |
1269 | 0 | opts->server_type != SAY_SYSLOG_DEFAULT) |
1270 | 0 | goto duplicate; |
1271 | 0 | if (say_parse_prefix(&value, "unix:")) { |
1272 | 0 | opts->server_type = SAY_SYSLOG_UNIX; |
1273 | 0 | opts->server_path = value; |
1274 | 0 | } else { |
1275 | 0 | opts->server_type = SAY_SYSLOG_REMOTE; |
1276 | 0 | opts->server_path = value; |
1277 | 0 | } |
1278 | 0 | } else if (say_parse_prefix(&value, "identity=")) { |
1279 | 0 | if (opts->identity != NULL) |
1280 | 0 | goto duplicate; |
1281 | 0 | opts->identity = value; |
1282 | 0 | } else if (say_parse_prefix(&value, "facility=")) { |
1283 | 0 | if (opts->facility != syslog_facility_MAX) |
1284 | 0 | goto duplicate; |
1285 | 0 | opts->facility = say_syslog_facility_by_name(value); |
1286 | 0 | if (opts->facility == syslog_facility_MAX) { |
1287 | 0 | diag_set(IllegalParams, "bad syslog facility option '%s'", |
1288 | 0 | value); |
1289 | 0 | goto error; |
1290 | 0 | } |
1291 | 0 | } else { |
1292 | 0 | diag_set(IllegalParams, "bad option '%s'", option); |
1293 | 0 | goto error; |
1294 | 0 | } |
1295 | 0 | } |
1296 | 0 | return 0; |
1297 | 0 | duplicate: |
1298 | | /* Terminate the "bad" option, by overwriting '=' sign */ |
1299 | 0 | ((char *)value)[-1] = '\0'; |
1300 | 0 | diag_set(IllegalParams, "duplicate option '%s'", option); |
1301 | 0 | error: |
1302 | 0 | free(opts->copy); opts->copy = NULL; |
1303 | 0 | return -1; |
1304 | 0 | } |
1305 | | |
1306 | | void |
1307 | | say_free_syslog_opts(struct say_syslog_opts *opts) |
1308 | 0 | { |
1309 | 0 | free(opts->copy); |
1310 | 0 | opts->copy = NULL; |
1311 | 0 | } |
1312 | | |
1313 | | void |
1314 | | log_destroy(struct log *log) |
1315 | 0 | { |
1316 | 0 | assert(log != NULL); |
1317 | 0 | tt_pthread_mutex_lock(&log->rotate_mutex); |
1318 | 0 | while(log->rotating_threads > 0) |
1319 | 0 | tt_pthread_cond_wait(&log->rotate_cond, &log->rotate_mutex); |
1320 | 0 | tt_pthread_mutex_unlock(&log->rotate_mutex); |
1321 | 0 | pm_atomic_store(&log->type, SAY_LOGGER_BOOT); |
1322 | |
|
1323 | 0 | if (log->fd != -1) |
1324 | 0 | close(log->fd); |
1325 | 0 | free(log->syslog_ident); |
1326 | 0 | free(log->path); |
1327 | 0 | rlist_del_entry(log, in_log_list); |
1328 | 0 | tt_pthread_mutex_destroy(&log->rotate_mutex); |
1329 | 0 | tt_pthread_cond_destroy(&log->rotate_cond); |
1330 | 0 | } |
1331 | | |
1332 | | /** |
1333 | | * Format a line of log. |
1334 | | */ |
1335 | | static int |
1336 | | format_log_entry(struct log *log, int level, const char *module, |
1337 | | const char *filename, int line, const char *error, |
1338 | | const char *format, va_list ap) |
1339 | 0 | { |
1340 | 0 | int total = 0; |
1341 | 0 | char *buf = say_buf; |
1342 | 0 | int len = SAY_BUF_LEN_MAX; |
1343 | |
|
1344 | 0 | if (log->type == SAY_LOGGER_SYSLOG) { |
1345 | 0 | SNPRINT(total, format_syslog_header, buf, len, |
1346 | 0 | level, log->syslog_facility, log->syslog_ident); |
1347 | 0 | } |
1348 | 0 | SNPRINT(total, format_func_adapter, buf, len, log, level, module, |
1349 | 0 | filename, line, error, format, ap); |
1350 | | |
1351 | 0 | return total; |
1352 | 0 | } |
1353 | | |
1354 | | int |
1355 | | log_vsay(struct log *log, int level, bool check_level, const char *module, |
1356 | | const char *filename, int line, const char *error, const char *format, |
1357 | | va_list ap) |
1358 | 0 | { |
1359 | 0 | int errsv = errno; |
1360 | 0 | int total = 0; |
1361 | |
|
1362 | 0 | assert(level >= 0 && level < say_level_MAX); |
1363 | | |
1364 | 0 | if (check_level && level > log->level) |
1365 | 0 | goto out; |
1366 | | |
1367 | 0 | total = format_log_entry(log, level, module, filename, line, error, |
1368 | 0 | format, ap); |
1369 | 0 | if (total <= 0) |
1370 | 0 | goto out; |
1371 | | |
1372 | 0 | switch (log->type) { |
1373 | 0 | case SAY_LOGGER_FILE: |
1374 | 0 | case SAY_LOGGER_PIPE: |
1375 | 0 | write_to_file(log, total); |
1376 | 0 | break; |
1377 | 0 | case SAY_LOGGER_STDERR: |
1378 | 0 | if (before_stderr_callback != NULL) |
1379 | 0 | before_stderr_callback(); |
1380 | 0 | write_to_file(log, total); |
1381 | 0 | if (after_stderr_callback != NULL) |
1382 | 0 | after_stderr_callback(); |
1383 | 0 | break; |
1384 | 0 | case SAY_LOGGER_SYSLOG: |
1385 | 0 | write_to_syslog(log, total); |
1386 | 0 | if (level == S_FATAL && log->fd != STDERR_FILENO) |
1387 | 0 | (void)safe_write(STDERR_FILENO, say_buf, total); |
1388 | 0 | break; |
1389 | 0 | case SAY_LOGGER_BOOT: |
1390 | 0 | { |
1391 | 0 | if (before_stderr_callback != NULL) |
1392 | 0 | before_stderr_callback(); |
1393 | 0 | ssize_t r = safe_write(STDERR_FILENO, say_buf, total); |
1394 | 0 | (void) r; /* silence gcc warning */ |
1395 | 0 | if (after_stderr_callback != NULL) |
1396 | 0 | after_stderr_callback(); |
1397 | 0 | break; |
1398 | 0 | } |
1399 | 0 | default: |
1400 | 0 | unreachable(); |
1401 | 0 | } |
1402 | 0 | out: |
1403 | 0 | errno = errsv; /* Preserve the errno. */ |
1404 | 0 | return total; |
1405 | 0 | } |
1406 | | |
1407 | | void |
1408 | | say_set_stderr_callback(say_stderr_callback_t before, |
1409 | | say_stderr_callback_t after) |
1410 | 0 | { |
1411 | 0 | before_stderr_callback = before; |
1412 | 0 | after_stderr_callback = after; |
1413 | 0 | } |