summaryrefslogtreecommitdiff
path: root/src/Log.cxx
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2013-01-09 22:33:06 +0100
committerMax Kellermann <max@duempel.org>2013-01-09 22:33:06 +0100
commit8eacd13ce7f528adc52614ce3cc5faf963b3fc5a (patch)
tree5466d2e8407f00efc8ad1419509833ef3a937f2d /src/Log.cxx
parentd3e1b72d381dc7dd01926188b6d62729788b76b9 (diff)
sig_handlers, log: convert to C++
Diffstat (limited to 'src/Log.cxx')
-rw-r--r--src/Log.cxx341
1 files changed, 341 insertions, 0 deletions
diff --git a/src/Log.cxx b/src/Log.cxx
new file mode 100644
index 000000000..7ca64197f
--- /dev/null
+++ b/src/Log.cxx
@@ -0,0 +1,341 @@
+/*
+ * 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 "Log.hxx"
+#include "conf.h"
+#include "utils.h"
+#include "fd_util.h"
+#include "mpd_error.h"
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <glib.h>
+
+#ifdef HAVE_SYSLOG
+#include <syslog.h>
+#endif
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "log"
+
+#define LOG_LEVEL_SECURE G_LOG_LEVEL_INFO
+
+#define LOG_DATE_BUF_SIZE 16
+#define LOG_DATE_LEN (LOG_DATE_BUF_SIZE - 1)
+
+static GLogLevelFlags log_threshold = G_LOG_LEVEL_MESSAGE;
+
+static const char *log_charset;
+
+static bool stdout_mode = true;
+static int out_fd;
+static char *out_filename;
+
+static void redirect_logs(int fd)
+{
+ assert(fd >= 0);
+ if (dup2(fd, STDOUT_FILENO) < 0)
+ MPD_ERROR("problems dup2 stdout : %s\n", strerror(errno));
+ if (dup2(fd, STDERR_FILENO) < 0)
+ MPD_ERROR("problems dup2 stderr : %s\n", strerror(errno));
+}
+
+static const char *log_date(void)
+{
+ static char buf[LOG_DATE_BUF_SIZE];
+ time_t t = time(NULL);
+ strftime(buf, LOG_DATE_BUF_SIZE, "%b %d %H:%M : ", localtime(&t));
+ return buf;
+}
+
+/**
+ * Determines the length of the string excluding trailing whitespace
+ * characters.
+ */
+static int
+chomp_length(const char *p)
+{
+ size_t length = strlen(p);
+
+ while (length > 0 && g_ascii_isspace(p[length - 1]))
+ --length;
+
+ return (int)length;
+}
+
+static void
+file_log_func(const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message, G_GNUC_UNUSED gpointer user_data)
+{
+ char *converted;
+
+ if (log_level > log_threshold)
+ return;
+
+ if (log_charset != NULL) {
+ converted = g_convert_with_fallback(message, -1,
+ log_charset, "utf-8",
+ NULL, NULL, NULL, NULL);
+ if (converted != NULL)
+ message = converted;
+ } else
+ converted = NULL;
+
+ if (log_domain == NULL)
+ log_domain = "";
+
+ fprintf(stderr, "%s%s%s%.*s\n",
+ stdout_mode ? "" : log_date(),
+ log_domain, *log_domain == 0 ? "" : ": ",
+ chomp_length(message), message);
+
+ g_free(converted);
+}
+
+static void
+log_init_stdout(void)
+{
+ g_log_set_default_handler(file_log_func, NULL);
+}
+
+static int
+open_log_file(void)
+{
+ assert(out_filename != NULL);
+
+ return open_cloexec(out_filename, O_CREAT | O_WRONLY | O_APPEND, 0666);
+}
+
+static bool
+log_init_file(unsigned line, GError **error_r)
+{
+ assert(out_filename != NULL);
+
+ out_fd = open_log_file();
+ if (out_fd < 0) {
+ g_set_error(error_r, log_quark(), errno,
+ "failed to open log file \"%s\" (config line %u): %s",
+ out_filename, line, g_strerror(errno));
+ return false;
+ }
+
+ g_log_set_default_handler(file_log_func, NULL);
+ return true;
+}
+
+#ifdef HAVE_SYSLOG
+
+static int
+glib_to_syslog_level(GLogLevelFlags log_level)
+{
+ switch (log_level & G_LOG_LEVEL_MASK) {
+ case G_LOG_LEVEL_ERROR:
+ case G_LOG_LEVEL_CRITICAL:
+ return LOG_ERR;
+
+ case G_LOG_LEVEL_WARNING:
+ return LOG_WARNING;
+
+ case G_LOG_LEVEL_MESSAGE:
+ return LOG_NOTICE;
+
+ case G_LOG_LEVEL_INFO:
+ return LOG_INFO;
+
+ case G_LOG_LEVEL_DEBUG:
+ return LOG_DEBUG;
+
+ default:
+ return LOG_NOTICE;
+ }
+}
+
+static void
+syslog_log_func(const gchar *log_domain,
+ GLogLevelFlags log_level, const gchar *message,
+ G_GNUC_UNUSED gpointer user_data)
+{
+ if (stdout_mode) {
+ /* fall back to the file log function during
+ startup */
+ file_log_func(log_domain, log_level,
+ message, user_data);
+ return;
+ }
+
+ if (log_level > log_threshold)
+ return;
+
+ if (log_domain == NULL)
+ log_domain = "";
+
+ syslog(glib_to_syslog_level(log_level), "%s%s%.*s",
+ log_domain, *log_domain == 0 ? "" : ": ",
+ chomp_length(message), message);
+}
+
+static void
+log_init_syslog(void)
+{
+ assert(out_filename == NULL);
+
+ openlog(PACKAGE, 0, LOG_DAEMON);
+ g_log_set_default_handler(syslog_log_func, NULL);
+}
+
+#endif
+
+static inline GLogLevelFlags
+parse_log_level(const char *value, unsigned line)
+{
+ if (0 == strcmp(value, "default"))
+ return G_LOG_LEVEL_MESSAGE;
+ if (0 == strcmp(value, "secure"))
+ return LOG_LEVEL_SECURE;
+ else if (0 == strcmp(value, "verbose"))
+ return G_LOG_LEVEL_DEBUG;
+ else {
+ MPD_ERROR("unknown log level \"%s\" at line %u\n",
+ value, line);
+ return G_LOG_LEVEL_MESSAGE;
+ }
+}
+
+void
+log_early_init(bool verbose)
+{
+ if (verbose)
+ log_threshold = G_LOG_LEVEL_DEBUG;
+
+ log_init_stdout();
+}
+
+bool
+log_init(bool verbose, bool use_stdout, GError **error_r)
+{
+ const struct config_param *param;
+
+ g_get_charset(&log_charset);
+
+ if (verbose)
+ log_threshold = G_LOG_LEVEL_DEBUG;
+ else if ((param = config_get_param(CONF_LOG_LEVEL)) != NULL)
+ log_threshold = parse_log_level(param->value, param->line);
+
+ if (use_stdout) {
+ log_init_stdout();
+ return true;
+ } else {
+ param = config_get_param(CONF_LOG_FILE);
+ if (param == NULL) {
+#ifdef HAVE_SYSLOG
+ /* no configuration: default to syslog (if
+ available) */
+ log_init_syslog();
+ return true;
+#else
+ g_set_error(error_r, log_quark(), 0,
+ "config parameter \"%s\" not found",
+ CONF_LOG_FILE);
+ return false;
+#endif
+#ifdef HAVE_SYSLOG
+ } else if (strcmp(param->value, "syslog") == 0) {
+ log_init_syslog();
+ return true;
+#endif
+ } else {
+ out_filename = config_dup_path(CONF_LOG_FILE, error_r);
+ return out_filename != NULL &&
+ log_init_file(param->line, error_r);
+ }
+ }
+}
+
+static void
+close_log_files(void)
+{
+ if (stdout_mode)
+ return;
+
+#ifdef HAVE_SYSLOG
+ if (out_filename == NULL)
+ closelog();
+#endif
+}
+
+void
+log_deinit(void)
+{
+ close_log_files();
+ g_free(out_filename);
+}
+
+
+void setup_log_output(bool use_stdout)
+{
+ fflush(NULL);
+ if (!use_stdout) {
+#ifndef WIN32
+ if (out_filename == NULL)
+ out_fd = open("/dev/null", O_WRONLY);
+#endif
+
+ if (out_fd >= 0) {
+ redirect_logs(out_fd);
+ close(out_fd);
+ }
+
+ stdout_mode = false;
+ log_charset = NULL;
+ }
+}
+
+int cycle_log_files(void)
+{
+ int fd;
+
+ if (stdout_mode || out_filename == NULL)
+ return 0;
+ assert(out_filename);
+
+ g_debug("Cycling log files...\n");
+ close_log_files();
+
+ fd = open_log_file();
+ if (fd < 0) {
+ g_warning("error re-opening log file: %s\n", out_filename);
+ return -1;
+ }
+
+ redirect_logs(fd);
+ g_debug("Done cycling log files\n");
+ return 0;
+}