/* * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include "LogInit.hxx" #include "LogBackend.hxx" #include "Log.hxx" #include "ConfigData.hxx" #include "ConfigGlobal.hxx" #include "ConfigOption.hxx" #include "system/FatalError.hxx" #include "fs/AllocatedPath.hxx" #include "fs/FileSystem.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "system/FatalError.hxx" #ifdef HAVE_GLIB #include #endif #include #include #include #include #include #include #define LOG_LEVEL_SECURE LogLevel::INFO #define LOG_DATE_BUF_SIZE 16 #define LOG_DATE_LEN (LOG_DATE_BUF_SIZE - 1) static constexpr Domain log_domain("log"); static int out_fd; static AllocatedPath out_path = AllocatedPath::Null(); static void redirect_logs(int fd) { assert(fd >= 0); if (dup2(fd, STDOUT_FILENO) < 0) FatalSystemError("Failed to dup2 stdout"); if (dup2(fd, STDERR_FILENO) < 0) FatalSystemError("Failed to dup2 stderr"); } static int open_log_file(void) { assert(!out_path.IsNull()); return OpenFile(out_path, O_CREAT | O_WRONLY | O_APPEND, 0666); } static bool log_init_file(unsigned line, Error &error) { assert(!out_path.IsNull()); out_fd = open_log_file(); if (out_fd < 0) { const std::string out_path_utf8 = out_path.ToUTF8(); error.FormatErrno("failed to open log file \"%s\" (config line %u)", out_path_utf8.c_str(), line); return false; } EnableLogTimestamp(); return true; } static inline LogLevel parse_log_level(const char *value, unsigned line) { if (0 == strcmp(value, "default")) return LogLevel::DEFAULT; if (0 == strcmp(value, "secure")) return LOG_LEVEL_SECURE; else if (0 == strcmp(value, "verbose")) return LogLevel::DEBUG; else { FormatFatalError("unknown log level \"%s\" at line %u", value, line); } } void log_early_init(bool verbose) { if (verbose) SetLogThreshold(LogLevel::DEBUG); } bool log_init(bool verbose, bool use_stdout, Error &error) { const struct config_param *param; #ifdef HAVE_GLIB const char *charset; g_get_charset(&charset); SetLogCharset(charset); #endif if (verbose) SetLogThreshold(LogLevel::DEBUG); else if ((param = config_get_param(CONF_LOG_LEVEL)) != nullptr) SetLogThreshold(parse_log_level(param->value.c_str(), param->line)); if (use_stdout) { return true; } else { param = config_get_param(CONF_LOG_FILE); if (param == nullptr) { #ifdef HAVE_SYSLOG /* no configuration: default to syslog (if available) */ LogInitSysLog(); return true; #else error.Set(log_domain, "config parameter 'log_file' not found"); return false; #endif #ifdef HAVE_SYSLOG } else if (strcmp(param->value.c_str(), "syslog") == 0) { LogInitSysLog(); return true; #endif } else { out_path = config_get_path(CONF_LOG_FILE, error); return !out_path.IsNull() && log_init_file(param->line, error); } } } static void close_log_files(void) { #ifdef HAVE_SYSLOG LogFinishSysLog(); #endif } void log_deinit(void) { close_log_files(); out_path = AllocatedPath::Null(); } void setup_log_output(bool use_stdout) { if (use_stdout) return; fflush(nullptr); if (out_fd < 0) { #ifdef WIN32 return; #else out_fd = open("/dev/null", O_WRONLY); if (out_fd < 0) return; #endif } redirect_logs(out_fd); close(out_fd); out_fd = -1; SetLogCharset(nullptr); } int cycle_log_files(void) { int fd; if (out_path.IsNull()) return 0; FormatDebug(log_domain, "Cycling log files"); close_log_files(); fd = open_log_file(); if (fd < 0) { const std::string out_path_utf8 = out_path.ToUTF8(); FormatError(log_domain, "error re-opening log file: %s", out_path_utf8.c_str()); return -1; } redirect_logs(fd); FormatDebug(log_domain, "Done cycling log files"); return 0; }