/* * Copyright 2023 Vsevolod Stakhov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "config.h" #include "logger.h" #include "libserver/cfg_file.h" #include "libcryptobox/cryptobox.h" #include "unix-std.h" #include "logger_private.h" #define CONSOLE_LOG_QUARK g_quark_from_static_string("console_logger") static const char lf_chr = '\n'; struct rspamd_console_logger_priv { int fd; int crit_fd; }; /* Copy & paste :( */ static inline void log_time(double now, rspamd_logger_t *rspamd_log, char *timebuf, size_t len) { time_t sec = (time_t) now; gsize r; struct tm tms; rspamd_localtime(sec, &tms); r = strftime(timebuf, len, "%F %H:%M:%S", &tms); if (rspamd_log->flags & RSPAMD_LOG_FLAG_USEC) { char usec_buf[16]; rspamd_snprintf(usec_buf, sizeof(usec_buf), "%.5f", now - (double) sec); rspamd_snprintf(timebuf + r, len - r, "%s", usec_buf + 1); } } void * rspamd_log_console_init(rspamd_logger_t *logger, struct rspamd_config *cfg, uid_t uid, gid_t gid, GError **err) { struct rspamd_console_logger_priv *priv; priv = g_malloc0(sizeof(*priv)); if (logger->flags & RSPAMD_LOG_FLAG_RSPAMADM) { priv->fd = dup(STDOUT_FILENO); priv->crit_fd = dup(STDERR_FILENO); } else { priv->fd = dup(STDERR_FILENO); priv->crit_fd = priv->fd; } if (priv->fd == -1) { g_set_error(err, CONSOLE_LOG_QUARK, errno, "open_log: cannot dup console fd: %s\n", strerror(errno)); rspamd_log_console_dtor(logger, priv); return NULL; } if (!isatty(priv->fd)) { if (logger->flags & RSPAMD_LOG_FLAG_COLOR) { /* Disable colors for not a tty */ logger->flags &= ~RSPAMD_LOG_FLAG_COLOR; } } return priv; } void * rspamd_log_console_reload(rspamd_logger_t *logger, struct rspamd_config *cfg, gpointer arg, uid_t uid, gid_t gid, GError **err) { struct rspamd_console_logger_priv *npriv; npriv = rspamd_log_console_init(logger, cfg, uid, gid, err); if (npriv) { /* Close old */ rspamd_log_console_dtor(logger, arg); } return npriv; } void rspamd_log_console_dtor(rspamd_logger_t *logger, gpointer arg) { struct rspamd_console_logger_priv *priv = (struct rspamd_console_logger_priv *) arg; if (priv->fd != -1) { if (priv->fd != priv->crit_fd) { /* Two different FD case */ if (close(priv->crit_fd) == -1) { rspamd_fprintf(stderr, "cannot close log crit_fd %d: %s\n", priv->crit_fd, strerror(errno)); } } if (close(priv->fd) == -1) { rspamd_fprintf(stderr, "cannot close log fd %d: %s\n", priv->fd, strerror(errno)); } /* Avoid the next if to be executed as crit_fd is equal to fd */ priv->crit_fd = -1; } if (priv->crit_fd != -1) { if (close(priv->crit_fd) == -1) { rspamd_fprintf(stderr, "cannot close log crit_fd %d: %s\n", priv->crit_fd, strerror(errno)); } } g_free(priv); } bool rspamd_log_console_log(const char *module, const char *id, const char *function, int level_flags, const char *message, gsize mlen, rspamd_logger_t *rspamd_log, gpointer arg) { struct rspamd_console_logger_priv *priv = (struct rspamd_console_logger_priv *) arg; int fd, r; double now; if (level_flags & G_LOG_LEVEL_CRITICAL) { fd = priv->crit_fd; } else { /* Use stderr if we are in rspamadm mode and severity is more than WARNING */ if ((rspamd_log->flags & RSPAMD_LOG_FLAG_RSPAMADM) && (level_flags & G_LOG_LEVEL_WARNING)) { fd = priv->crit_fd; } else { fd = priv->fd; } } #ifndef DISABLE_PTHREAD_MUTEX if (rspamd_log->mtx) { rspamd_mempool_lock_mutex(rspamd_log->mtx); } else { rspamd_file_lock(fd, FALSE); } #else rspamd_file_lock(fd, FALSE); #endif now = rspamd_get_calendar_ticks(); struct rspamd_logger_iov_ctx iov_ctx; memset(&iov_ctx, 0, sizeof(iov_ctx)); rspamd_log_fill_iov(&iov_ctx, now, module, id, function, level_flags, message, mlen, rspamd_log); again: r = writev(fd, iov_ctx.iov, iov_ctx.niov); if (r == -1) { if (errno == EAGAIN || errno == EINTR) { goto again; } if (rspamd_log->mtx) { rspamd_mempool_unlock_mutex(rspamd_log->mtx); } else { rspamd_file_unlock(fd, FALSE); } rspamd_log_iov_free(&iov_ctx); return false; } if (rspamd_log->mtx) { rspamd_mempool_unlock_mutex(rspamd_log->mtx); } else { rspamd_file_unlock(fd, FALSE); } rspamd_log_iov_free(&iov_ctx); return true; }