+#include "color.h"
+
+/** The internal state of the daemon. */
+struct daemon {
+ /** See \ref daemon_flags. */
+ unsigned flags;
+ /** Set by \ref daemon_set_logfile(). */
+ char *logfile_name;
+ /** Current loglevel, see \ref daemon_set_loglevel(). */
+ int loglevel;
+ /** Used by \ref server_uptime() and \ref uptime_str(). */
+ time_t startuptime;
+ /** The file pointer if the logfile is open. */
+ FILE *logfile;
+ /** Used by para_log() if \p DF_LOG_HOSTNAME is set. */
+ char *hostname;
+ /** Used for colored log messages. */
+ char log_colors[NUM_LOGLEVELS][COLOR_MAXLEN];
+ char *old_cwd;
+ /*
+ * If these pointers are non-NULL, the functions are called from
+ * daemon_log() before and after writing each log message.
+ */
+ void (*pre_log_hook)(void);
+ void (*post_log_hook)(void);
+};
+
+static struct daemon the_daemon, *me = &the_daemon;
+
+static void daemon_set_default_log_colors(void)
+{
+ int i;
+ static const char *default_log_colors[NUM_LOGLEVELS] = {
+ [LL_DEBUG] = "normal",
+ [LL_INFO] = "normal",
+ [LL_NOTICE] = "normal",
+ [LL_WARNING] = "yellow",
+ [LL_ERROR] = "red",
+ [LL_CRIT] = "magenta bold",
+ [LL_EMERG] = "red bold",
+ };
+ for (i = 0; i < NUM_LOGLEVELS; i++)
+ color_parse_or_die(default_log_colors[i], me->log_colors[i]);
+}
+
+/**
+ * Set the color for log messages of the given severity level.
+ *
+ * \param arg Must be of the form "severity:[fg [bg]] [attr]".
+ */
+void daemon_set_log_color_or_die(const char *arg)
+{
+ unsigned ll;
+ const char * const sev[] = {SEVERITIES};
+ char *p = strchr(arg, ':');
+
+ if (!p)
+ goto err;
+ for (ll = 0; ll < NUM_LOGLEVELS; ll++) {
+ const char *name = sev[ll];
+ /*
+ * Parse only the first part of the string so that, for
+ * example, the argument "info:something_else" is recognized.
+ * Note that the string comparison is performed
+ * case-insensitively.
+ */
+ if (strncasecmp(arg, name, strlen(name)))
+ continue;
+ return color_parse_or_die(p + 1, me->log_colors[ll]);
+ }
+err:
+ PARA_EMERG_LOG("%s: invalid color argument\n", arg);
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * Initialize color mode if necessary.
+ *
+ * \param color_arg The argument given to --color.
+ * \param color_arg_auto The value for automatic color detection.
+ * \param color_arg_no The value to disable colored log messages.
+ * \param logfile_given In auto mode colors are disabled if this value is true.
+ *
+ * If color_arg equals color_arg_no, color mode is disabled. That is, calls to
+ * para_log() will not produce colored output. If color_arg equals
+ * color_arg_auto, the function detects automatically whether to activate
+ * colors. Otherwise color mode is enabled.
+ *
+ * If color mode is to be enabled, the default colors are set for each
+ * loglevel. They can be overwritten by calling daemon_set_log_color_or_die().
+ *
+ * \return Whether colors have been enabled by the function.
+ */
+bool daemon_init_colors_or_die(int color_arg, int color_arg_auto,
+ int color_arg_no, bool logfile_given)
+{
+ if (color_arg == color_arg_no)
+ return false;
+ if (color_arg == color_arg_auto) {
+ if (logfile_given)
+ return false;
+ if (!isatty(STDERR_FILENO))
+ return false;
+ }
+ daemon_set_flag(DF_COLOR_LOG);
+ daemon_set_default_log_colors();
+ return true;
+}
+
+/**
+ * Init or change the name of the log file.
+ *
+ * \param logfile_name The full path of the logfile.
+ */
+void daemon_set_logfile(const char *logfile_name)
+{
+ free(me->logfile_name);
+ me->logfile_name = NULL;
+ if (!logfile_name)
+ return;
+ if (me->old_cwd && logfile_name[0] != '/')
+ me->logfile_name = make_message("%s/%s", me->old_cwd,
+ logfile_name);
+ else
+ me->logfile_name = para_strdup(logfile_name);
+}
+
+/**
+ * Control the verbosity for logging.
+ *
+ * This instructs the daemon to not log subsequent messages whose severity is
+ * lower than the given value.
+ *
+ * \param loglevel The new log level.
+ */
+void daemon_set_loglevel(int loglevel)
+{
+ assert(loglevel >= 0);
+ assert(loglevel < NUM_LOGLEVELS);
+ me->loglevel = loglevel;
+}
+
+/**
+ * Get the current log level of the daemon.
+ *
+ * \return Greater or equal than zero and less than NUM_LOGLEVELS. This
+ * function never fails.
+ */
+int daemon_get_loglevel(void)
+{
+ return me->loglevel;
+}
+
+/**
+ * Register functions to be called before and after a message is logged.
+ *
+ * \param pre_log_hook Called before the message is logged.
+ * \param post_log_hook Called after the message is logged.
+ *
+ * The purpose of this function is to provide a primitive for multi-threaded
+ * applications to serialize the access to the log facility, preventing
+ * interleaving log messages. This can be achieved by having the pre-log hook
+ * acquire a lock which blocks the other threads on the attempt to log a
+ * message at the same time. The post-log hook is responsible for releasing
+ * the lock.
+ *
+ * If these hooks are unnecessary, for example because the application is
+ * single-threaded, this function does not need to be called.
+ */
+void daemon_set_hooks(void (*pre_log_hook)(void), void (*post_log_hook)(void))
+{
+ me->pre_log_hook = pre_log_hook;
+ me->post_log_hook = post_log_hook;
+}
+
+/**
+ * Set one of the daemon config flags.
+ *
+ * \param flag The flag to set.
+ *
+ * \sa \ref daemon_flags.
+ */
+void daemon_set_flag(unsigned flag)
+{
+ me->flags |= flag;
+}
+
+static bool daemon_test_flag(unsigned flag)
+{
+ return me->flags & flag;
+}