From ae657a3b13b4ff76187dad39fede40b98d7cb6d7 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Fri, 16 Jan 2009 19:28:57 +0100 Subject: [PATCH] Move color logging code to daemon.c and use it also for para_audiod. This patch also introduces the para_log() function of daemon.c so that both para_server and para_audiod can use it. --- NEWS | 3 +- audiod.c | 88 +++++++++--------- audiod.ggo | 18 ++++ color.h | 1 + configure.ac | 2 +- daemon.c | 248 ++++++++++++++++++++++++++++++++++++++++++++------- daemon.h | 24 ++++- server.c | 131 +++++---------------------- server.ggo | 19 +++- 9 files changed, 341 insertions(+), 193 deletions(-) diff --git a/NEWS b/NEWS index 1913094c..fdc4c071 100644 --- a/NEWS +++ b/NEWS @@ -5,7 +5,7 @@ NEWS 0.3.4 (to be announced) "elliptic inheritance" ---------------------------------------------- -The new udp sender and several small improvements. +The new udp sender and various other improvements. - The udp sender replaces the ortp sender. The new code is both smaller and cleaner than the old ortp sender/receiver @@ -13,6 +13,7 @@ The new udp sender and several small improvements. libraries, it is built unconditionally. The default port for udp streaming now defaults to 8000, like for the http and the dccp senders/receivers. + - para_server/para_audiod: Color support for log messages. - new options for mp3dec: --ignore-crc, --bufsize - new audiod option: --config-file. - Improved help/man pages: The documentation of para_audiod, diff --git a/audiod.c b/audiod.c index 3205400c..b781527c 100644 --- a/audiod.c +++ b/audiod.c @@ -83,7 +83,6 @@ int audiod_status = AUDIOD_ON; struct audiod_args_info conf; static char *socket_name; -static FILE *logfile; static struct audio_format_info afi[NUM_AUDIO_FORMATS]; static struct signal_task signal_task_struct, *sig_task = &signal_task_struct; @@ -181,42 +180,27 @@ out: ); } -/** - * the log function of para_audiod - * - * \param ll loglevel - * \param fmt the format string - */ -__printf_2_3 void para_log(int ll, const char* fmt,...) +static int want_colors(void) { - va_list argp; - FILE *outfd; - struct tm *tm; - time_t t1; - char str[MAXLINE] = ""; - static char *hostname; - - if (ll < conf.loglevel_arg) - return; - if (!logfile && conf.daemon_given) - return; - if (!hostname) - hostname = para_hostname(); - outfd = logfile? logfile : stderr; - time(&t1); - tm = localtime(&t1); - strftime(str, MAXLINE, "%b %d %H:%M:%S", tm); - fprintf(outfd, "%s %s ", str, hostname); - if (conf.loglevel_arg <= LL_INFO) - fprintf(outfd, "%i ", ll); - va_start(argp, fmt); - vfprintf(outfd, fmt, argp); - va_end(argp); + if (conf.color_arg == color_arg_no) + return 0; + if (conf.color_arg == color_arg_yes) + return 1; + if (conf.logfile_given) + return 0; + return isatty(STDERR_FILENO); } static void parse_config_or_die(void) { char *config_file; + struct audiod_cmdline_parser_params params = { + .override = 0, + .initialize = 0, + .check_required = 1, + .check_ambiguity = 0, + .print_errors = 1 + }; if (conf.config_file_given) config_file = para_strdup(conf.config_file_arg); @@ -229,20 +213,12 @@ static void parse_config_or_die(void) PARA_EMERG_LOG("can not read config file %s\n", config_file); goto err; } - if (config_file) { - struct audiod_cmdline_parser_params params = { - .override = 0, - .initialize = 0, - .check_required = 1, - .check_ambiguity = 0, - .print_errors = 1 - }; - if (audiod_cmdline_parser_config_file(config_file, &conf, ¶ms)) { - PARA_EMERG_LOG("parse error in config file\n"); - goto err; - } + if (audiod_cmdline_parser_config_file(config_file, &conf, ¶ms)) { + PARA_EMERG_LOG("parse error in config file\n"); + goto err; } free(config_file); + daemon_set_loglevel(conf.loglevel_arg); return; err: free(config_file); @@ -1109,6 +1085,20 @@ __noreturn static void print_help_and_die(void) exit(0); } +static void init_colors_or_die(void) +{ + int ret, i; + + if (!want_colors()) + return; + daemon_set_default_log_colors(); + daemon_set_flag(DF_COLOR_LOG); + for (i = 0; i < conf.log_color_given; i++) { + ret = daemon_set_log_color(conf.log_color_arg[i]); + if (ret < 0) + exit(EXIT_FAILURE); + } +} /** * the main function of para_audiod @@ -1138,11 +1128,17 @@ int main(int argc, char *argv[]) HANDLE_VERSION_FLAG("audiod", conf); drop_privileges_or_die(conf.user_arg, conf.group_arg); parse_config_or_die(); - if (conf.logfile_given) - logfile = open_log(conf.logfile_arg); - i = init_stream_io(); if (conf.help_given || conf.detailed_help_given) print_help_and_die(); + daemon_set_flag(DF_LOG_TIME); + daemon_set_flag(DF_LOG_HOSTNAME); + daemon_set_flag(DF_LOG_LL); + if (conf.logfile_given) { + daemon_set_logfile(conf.logfile_arg); + daemon_open_log_or_die(); + } + init_colors_or_die(); + i = init_stream_io(); if (i < 0) { PARA_EMERG_LOG("init stream io error: %s\n", para_strerror(-i)); exit(EXIT_FAILURE); diff --git a/audiod.ggo b/audiod.ggo index cba65d00..6048b874 100644 --- a/audiod.ggo +++ b/audiod.ggo @@ -14,6 +14,24 @@ int typestr="level" default="3" optional +option "color" C +#~~~~~~~~~~~~~~~ +"activate color output" +enum typestr="when" +values = "yes","no","auto" +default = "auto" +optional + +option "log_color" - +#~~~~~~~~~~~~~~~~~~~ +"select a color for one type of log message" +string typestr="color_spec" +multiple +optional +details=" + Example: --log_color \"INFO:yellow black bold\" +" + option "config_file" c #~~~~~~~~~~~~~~~~~~~~~ "(default='~/.paraslash/audiod.conf'" diff --git a/color.h b/color.h index 10b4c28a..12972b3a 100644 --- a/color.h +++ b/color.h @@ -4,6 +4,7 @@ /** "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ #define COLOR_MAXLEN 24 +/** Switch back to normal colors. */ #define COLOR_RESET "\033[m" int color_parse(const char *value, char *dst); diff --git a/configure.ac b/configure.ac index 776478c5..afb993af 100644 --- a/configure.ac +++ b/configure.ac @@ -111,7 +111,7 @@ audiod_cmdline_objs="audiod.cmdline grab_client.cmdline compress_filter.cmdline audiod_errlist_objs="audiod signal string daemon stat net time grab_client filter_common wav_filter compress_filter amp_filter http_recv dccp_recv recv_common fd sched write_common file_write audiod_command crypt - client_common ggo udp_recv" + client_common ggo udp_recv color" audiod_ldflags="" audiod_audio_formats="" diff --git a/daemon.c b/daemon.c index e35deba2..2f706b01 100644 --- a/daemon.c +++ b/daemon.c @@ -8,12 +8,157 @@ #include "para.h" #include "daemon.h" #include - -/* getgrnam() */ -#include +#include /* getgrnam() */ #include #include "string.h" +#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]; +}; + +static struct daemon the_daemon, *me = &the_daemon; + +/** + * Activate default log colors. + * + * This should be called early if color support is wanted. + */ +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++) { + int ret = color_parse(default_log_colors[i], me->log_colors[i]); + assert(ret >= 0); + } +} + +static int get_loglevel_by_name(const char *txt, size_t n) +{ + if (!strncasecmp(txt, "debug", n)) + return LL_DEBUG; + if (!strncasecmp(txt, "info", n)) + return LL_INFO; + if (!strncasecmp(txt, "notice", n)) + return LL_NOTICE; + if (!strncasecmp(txt, "warning", n)) + return LL_WARNING; + if (!strncasecmp(txt, "error", n)) + return LL_ERROR; + if (!strncasecmp(txt, "crit", n)) + return LL_CRIT; + if (!strncasecmp(txt, "emerg", n)) + return LL_EMERG; + return -1; +} + +/** + * Set the color for one loglevel. + * + * \param arg The loglevel/color specifier. + * + * \a arg must be of the form "ll:[fg [bg]] [attr]". + * + * \return 1 On success, -1 on errors. + */ +int daemon_set_log_color(char const *arg) +{ + char *p = strchr(arg, ':'); + int ret, ll; + + if (!p) + goto err; + ret = get_loglevel_by_name(arg, p - arg); + if (ret < 0) + goto err; + ll = ret; + p++; + ret = color_parse(p, me->log_colors[ll]); + if (ret < 0) + goto err; + return 1; +err: + PARA_ERROR_LOG("%s: color syntax error\n", arg); + return -1; + +} + +/** + * Init or change the name of the log file. + * + * \param logfile_name The full path of the logfile. + */ +void daemon_set_logfile(char *logfile_name) +{ + free(me->logfile_name); + me->logfile_name = NULL; + if (logfile_name) + me->logfile_name = para_strdup(logfile_name); +} + +/** + * Supress log messages with severity lower than the given loglevel. + * + * \param loglevel The smallest level that should be logged. + */ +void daemon_set_loglevel(int loglevel) +{ + me->loglevel = loglevel; +} + +/** + * 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; +} + +/** + * Clear one of the daemon config flags. + * + * \param flag The flag to clear. + * + * \sa \ref daemon_flags. + */ +void daemon_clear_flag(unsigned flag) +{ + me->flags &= ~flag; +} + +static unsigned daemon_test_flag(unsigned flag) +{ + return me->flags & flag; +} /** * Do the usual stuff to become a daemon. @@ -56,40 +201,34 @@ err: } /** - * fopen() the given file in append mode. - * - * \param logfile_name The name of the file to open. - * - * \return Either calls exit() or returns a valid file handle. + * Close the log file of the daemon. */ -FILE *open_log(const char *logfile_name) +void daemon_close_log(void) { - FILE *logfile; - - assert(logfile_name); - logfile = fopen(logfile_name, "a"); - if (!logfile) { - PARA_EMERG_LOG("can not open %s: %s\n", logfile_name, - strerror(errno)); - exit(EXIT_FAILURE); - } - setlinebuf(logfile); - return logfile; + if (!me->logfile) + return; + PARA_INFO_LOG("closing logfile\n"); + fclose(me->logfile); + me->logfile = NULL; } /** - * Close the log file of the daemon. - * - * \param logfile The log file handle. + * fopen() the logfile in append mode. * - * It's OK to call this with logfile == \p NULL. + * \return Either succeeds or exits. */ -void close_log(FILE* logfile) +void daemon_open_log_or_die(void) { - if (!logfile) + daemon_close_log(); + if (!me->logfile_name) return; - PARA_INFO_LOG("closing logfile\n"); - fclose(logfile); + me->logfile = fopen(me->logfile_name, "a"); + if (!me->logfile) { + PARA_EMERG_LOG("can not open %s: %s\n", me->logfile_name, + strerror(errno)); + exit(EXIT_FAILURE); + } + setlinebuf(me->logfile); } /** @@ -168,16 +307,15 @@ void drop_privileges_or_die(const char *username, const char *groupname) */ time_t server_uptime(enum uptime set_or_get) { - static time_t startuptime; time_t now; double diff; if (set_or_get == UPTIME_SET) { - time(&startuptime); + time(&me->startuptime); return 0; } time(&now); - diff = difftime(now, startuptime); + diff = difftime(now, me->startuptime); return (time_t) diff; } @@ -194,3 +332,51 @@ __malloc char *uptime_str(void) return make_message("%li:%02li:%02li", t / 86400, (t / 3600) % 24, (t / 60) % 60); } + +/** + * The log function for para_server and para_audiod. + * + * \param ll The log level. + * \param fmt The format string describing the log message. + */ +__printf_2_3 void para_log(int ll, const char* fmt,...) +{ + va_list argp; + FILE *fp; + struct tm *tm; + time_t t1; + char *color, str[MAXLINE] = ""; + + ll = PARA_MIN(ll, NUM_LOGLEVELS - 1); + ll = PARA_MAX(ll, LL_DEBUG); + if (ll < me->loglevel) + return; + + fp = me->logfile? me->logfile : stderr; + color = daemon_test_flag(DF_COLOR_LOG)? me->log_colors[ll] : NULL; + if (color) + fprintf(fp, "%s", color); + if (daemon_test_flag(DF_LOG_TIME)) { + /* date and time */ + time(&t1); + tm = localtime(&t1); + strftime(str, MAXLINE, "%b %d %H:%M:%S", tm); + fprintf(fp, "%s ", str); + } + if (daemon_test_flag(DF_LOG_HOSTNAME)) { + if (!me->hostname) + me->hostname = para_hostname(); + fprintf(fp, "%s ", me->hostname); + } + if (daemon_test_flag(DF_LOG_LL)) /* log loglevel */ + fprintf(fp, "(%d) ", ll); + if (daemon_test_flag(DF_LOG_PID)) { /* log pid */ + pid_t mypid = getpid(); + fprintf(fp, "(%d) ", (int)mypid); + } + va_start(argp, fmt); + vfprintf(fp, fmt, argp); + va_end(argp); + if (color) + fprintf(fp, "%s", COLOR_RESET); +} diff --git a/daemon.h b/daemon.h index ebf32f82..c0a9f20c 100644 --- a/daemon.h +++ b/daemon.h @@ -2,11 +2,31 @@ void daemonize(void); -FILE *open_log(const char *logfile_name); -void close_log(FILE* logfile); +void daemon_open_log_or_die(void); +void daemon_close_log(void); void log_welcome(const char *whoami, int loglevel); void drop_privileges_or_die(const char *username, const char *groupname); /** used for server_uptime() */ enum uptime {UPTIME_SET, UPTIME_GET}; time_t server_uptime(enum uptime set_or_get); __malloc char *uptime_str(void); +void daemon_set_logfile(char *logfile_name); +void daemon_set_flag(unsigned flag); +void daemon_clear_flag(unsigned flag); +void daemon_set_loglevel(int loglevel); +void daemon_set_default_log_colors(void); +int daemon_set_log_color(char const *arg); + +/** Daemon log configuration flags. */ +enum daemon_flags { + /** Whether the hostname should be logged. */ + DF_LOG_HOSTNAME = 1, + /** Whether the PID should be logged. */ + DF_LOG_PID = 2, + /** Prepend log message with date and time. */ + DF_LOG_TIME = 4, + /** Also print the loglevel for each message. */ + DF_LOG_LL = 8, + /** Use colored output. */ + DF_COLOR_LOG = 16, +}; diff --git a/server.c b/server.c index 7b823dc9..c11fb58a 100644 --- a/server.c +++ b/server.c @@ -113,8 +113,6 @@ uint32_t afs_socket_cookie; /** The mutex protecting the shared memory area containing the mmd struct. */ int mmd_mutex; -/* global variables for server-internal use */ -static FILE *logfile; /** The file containing user information (public key, permissions). */ static char *user_list_file = NULL; static int mmd_shm_id; @@ -138,112 +136,24 @@ static int want_colors(void) return 0; if (conf.color_arg == color_arg_yes) return 1; - if (logfile) + if (conf.logfile_given) return 0; return isatty(STDERR_FILENO); } -static int get_loglevel_by_name(const char *txt, size_t n) -{ - if (!strncasecmp(txt, "debug", n)) - return LL_DEBUG; - if (!strncasecmp(txt, "info", n)) - return LL_INFO; - if (!strncasecmp(txt, "notice", n)) - return LL_NOTICE; - if (!strncasecmp(txt, "warning", n)) - return LL_WARNING; - if (!strncasecmp(txt, "error", n)) - return LL_ERROR; - if (!strncasecmp(txt, "crit", n)) - return LL_CRIT; - if (!strncasecmp(txt, "emerg", n)) - return LL_EMERG; - return -1; -} - -static char log_colors[NUM_LOGLEVELS][COLOR_MAXLEN]; - static void init_colors_or_die(void) { int ret, i; - static const char *default_log_colors[NUM_LOGLEVELS] = { - [LL_DEBUG] = "normal", - [LL_INFO] = "white bold", - [LL_NOTICE] = "cyan bold", - [LL_WARNING] = "green bold", - [LL_ERROR] = "yellow bold", - [LL_CRIT] = "magenta bold", - [LL_EMERG] = "red bold", - }; - - for (i = 0; i < NUM_LOGLEVELS; i++) { - ret = color_parse(default_log_colors[i], log_colors[i]); - assert(ret >= 0); - } + if (!want_colors()) + return; + daemon_set_flag(DF_COLOR_LOG); + daemon_set_default_log_colors(); for (i = 0; i < conf.log_color_given; i++) { - char *arg = conf.log_color_arg[i], *p = strchr(arg, ':'); - int ll; - if (!p) - goto err; - ret = get_loglevel_by_name(arg, p - arg); - if (ret < 0) - goto err; - ll = ret; - p++; - ret = color_parse(p, log_colors[ll]); + ret = daemon_set_log_color(conf.log_color_arg[i]); if (ret < 0) - goto err; - } - return; -err: - PARA_EMERG_LOG("color syntax error, arg %d (%s)\n", i, - conf.log_color_arg[i]); - exit(EXIT_FAILURE); -} - -/** - * Para_server's log function. - * - * \param ll The log level. - * \param fmt The format string describing the log message. - */ -__printf_2_3 void para_log(int ll, const char* fmt,...) -{ - va_list argp; - FILE *fp; - struct tm *tm; - time_t t1; - char *color, str[MAXLINE] = ""; - - ll = PARA_MIN(ll, NUM_LOGLEVELS - 1); - ll = PARA_MAX(ll, LL_DEBUG); - if (ll < conf.loglevel_arg) - return; - - fp = logfile? logfile : stderr; - color = want_colors()? log_colors[ll] : NULL; - - if (color) - fprintf(fp, "%s", color); - /* date and time */ - time(&t1); - tm = localtime(&t1); - strftime(str, MAXLINE, "%b %d %H:%M:%S", tm); - fprintf(fp, "%s ", str); - /* loglevel */ - if (conf.loglevel_arg <= LL_INFO) - fprintf(fp, "%i: ", ll); - if (conf.loglevel_arg <= LL_INFO) { /* log pid */ - pid_t mypid = getpid(); - fprintf(fp, "(%d) ", (int)mypid); + exit(EXIT_FAILURE); } - va_start(argp, fmt); - vfprintf(fp, fmt, argp); - va_end(argp); - if (color) - fprintf(fp, "%s", COLOR_RESET); } /* @@ -292,12 +202,10 @@ err_out: void parse_config_or_die(int override) { char *home = para_homedir(); - struct stat statbuf; - int ret; + int ret, ll = conf.loglevel_arg; char *cf; - close_log(logfile); - logfile = NULL; + daemon_close_log(); if (conf.config_file_given) cf = para_strdup(conf.config_file_arg); else @@ -307,13 +215,13 @@ void parse_config_or_die(int override) user_list_file = make_message("%s/.paraslash/server.users", home); else user_list_file = para_strdup(conf.user_list_arg); - ret = stat(cf, &statbuf); - if (ret && conf.config_file_given) { + ret = file_exists(cf); + if (conf.config_file_given && !ret) { ret = -1; - PARA_EMERG_LOG("can not stat config file %s\n", cf); + PARA_EMERG_LOG("can not read config file %s\n", cf); goto out; } - if (!ret) { + if (ret) { int tmp = conf.daemon_given; struct server_cmdline_parser_params params = { .override = override, @@ -325,10 +233,15 @@ void parse_config_or_die(int override) server_cmdline_parser_config_file(cf, &conf, ¶ms); conf.daemon_given = tmp; } - if (conf.logfile_given) - logfile = open_log(conf.logfile_arg); - if (want_colors()) - init_colors_or_die(); + if (conf.logfile_given) { + daemon_set_logfile(conf.logfile_arg); + daemon_open_log_or_die(); + } + daemon_set_loglevel(ll); + init_colors_or_die(); + daemon_set_flag(DF_LOG_PID); + daemon_set_flag(DF_LOG_LL); + daemon_set_flag(DF_LOG_TIME); ret = 1; out: free(cf); diff --git a/server.ggo b/server.ggo index fc606372..055dbc10 100644 --- a/server.ggo +++ b/server.ggo @@ -16,7 +16,7 @@ details=" while level 3 (warning) logs unexpected events that can be handled. Unhandled error conditions are logged with loglevel 4 (error) and crititcal errors are logged using loglevel 5 - (crit). Finally, loglevel 6 (emerg) is reserved for messages + (crit). Finally, loglevel 6 (emerg) is reserved for messages that cause para_server to terminate immediately. " @@ -35,7 +35,20 @@ string typestr="color_spec" multiple optional details=" - Example: --log_color \"INFO:yellow black bold\" + The format of \"color_spec\" is [fg [bg]] [attr]. + + Valid colors for \"fg\" and \"bg\" are \"normal\", \"black\", + \"red\", \"green\", \"yellow\", \"blue\", \"magenta\", + \"cyan\", and \"white\". + + The \"attr\" value must be one of \"bold\", \"dim\", \"ul\", + \"blink\", \"reverse\". + + Examples: + + --log_color \"debug:green\" + --log_color \"info:yellow bold\" + --log_color \"notice:white red bold\" " option "port" p @@ -76,7 +89,7 @@ details=" of 'name'. As the configuration file is read afterwards, those options that have a default value depending on the UID (e.g. the directory for the configuration file) are computed - by using the uid of 'name'. This option has no effect if + by using the uid of 'name'. This option has no effect if para_server is started as a non-root user (i.e. EUID != 0) " -- 2.39.5