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
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,
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;
);
}
-/**
- * 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);
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);
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
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);
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'"
/** "\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);
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=""
#include "para.h"
#include "daemon.h"
#include <pwd.h>
-
-/* getgrnam() */
-#include <sys/types.h>
+#include <sys/types.h> /* getgrnam() */
#include <grp.h>
#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.
}
/**
- * 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);
}
/**
*/
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;
}
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);
+}
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,
+};
/** 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;
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);
}
/*
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
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,
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);
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.
"
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
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)
"