.SHELLFLAGS := -ec
LOGLEVELS := LL_DEBUG,LL_INFO,LL_NOTICE,LL_WARNING,LL_ERROR,LL_CRIT,LL_EMERG
+SEVERITIES := \"debug\",\"info\",\"notice\",\"warning\",\"error\",\"crit\",\"emerg\"
vardir := /var/paraslash
mandir := $(datarootdir)/man/man1
MKDIR_P := mkdir -p
CPPFLAGS += -DCOPYRIGHT_YEAR='"$(COPYRIGHT_YEAR)"'
CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
CPPFLAGS += -DLOGLEVELS='$(LOGLEVELS)'
+CPPFLAGS += -DSEVERITIES=$(SEVERITIES)
CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
CPPFLAGS += -I$(lls_suite_dir)
- The autogen.sh script now only creates the autoconf specific files
but no longer runs configure, make and the test suite.
-
- A stripped down copy of the discontinued libmp4ff library has become
part of the paraslash code base. As a result it is no longer necessary
to install faad from source to get support for aac/m4a files. The
faad decoder package must still be installed.
-
+- The log level of the running daemon can now be changed with the
+ new ll command. It is available for para_server and para_audiod.
- All calls to select(2) have been replaced by calls to poll(2)
to avoid known shortcomings of the select API.
#include "afs.h"
#include "net.h"
#include "server.h"
+#include "daemon.h"
#include "ipc.h"
#include "list.h"
#include "sched.h"
}, s);
}
+static int afs_poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+ mutex_lock(mmd_mutex);
+ daemon_set_loglevel(mmd->loglevel);
+ mutex_unlock(mmd_mutex);
+ return xpoll(fds, nfds, timeout);
+}
+
/**
* Initialize the audio file selector process.
*
init_admissible_files(OPT_STRING_VAL(AFS_INITIAL_MODE));
register_command_task(&s);
s.default_timeout = 1000;
+ s.poll_function = afs_poll;
ret = write(socket_fd, "\0", 1);
if (ret != 1) {
if (ret == 0)
cr->matches = i9e_complete_commands(ci->word, audiod_completers);
}
+static void ll_completer(struct i9e_completion_info *ci,
+ struct i9e_completion_result *cr)
+{
+ i9e_ll_completer(ci, cr);
+}
+
static void version_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
-#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \
- lls_opt(LSG_AUDIOD_PARA_AUDIOD_OPT_ ## _name, CMD_PTR)))
__printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
/** define the array containing all supported audio formats */
para_strerror(-ret));
exit(EXIT_FAILURE);
}
- daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+ daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
n = OPT_GIVEN(USER_ALLOW);
if (n == 0)
return;
ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
if (ret < 0)
goto out;
- daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+ daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
OPT_STRING_VAL(GROUP));
version_handle_flag("audiod", OPT_GIVEN(VERSION));
}
EXPORT_AUDIOD_CMD_HANDLER(help)
+static int com_ll(int fd, struct lls_parse_result *lpr)
+{
+ unsigned ll;
+ char *errctx;
+ const char *sev[] = {SEVERITIES};
+ const char *arg;
+ int ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+
+ if (ret < 0) {
+ char *tmp = make_message("%s\n", errctx);
+ free(errctx);
+ client_write(fd, tmp);
+ free(tmp);
+ return ret;
+ }
+ if (lls_num_inputs(lpr) == 0) {
+ char *msg;
+ ll = daemon_get_loglevel();
+ msg = make_message("%s\n", sev[ll]);
+ ret = client_write(fd, msg);
+ free(msg);
+ return ret;
+ }
+ arg = lls_input(0, lpr);
+ for (ll = 0; ll < NUM_LOGLEVELS; ll++) {
+ if (!strcmp(arg, sev[ll]))
+ break;
+ }
+ if (ll >= NUM_LOGLEVELS)
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ PARA_INFO_LOG("new log level: %s\n", sev[ll]);
+ daemon_set_loglevel(ll);
+ return 1;
+}
+EXPORT_AUDIOD_CMD_HANDLER(ll)
+
static int com_tasks(int fd, __a_unused struct lls_parse_result *lpr)
{
int ret;
static struct i9e_completer completers[];
+static void ll_completer(struct i9e_completion_info *ci,
+ struct i9e_completion_result *cr)
+{
+ i9e_ll_completer(ci, cr);
+}
+
static void help_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
#include <netdb.h>
#include <lopsub.h>
-#include "server.lsg.h"
#include "para.h"
#include "error.h"
#include "lsu.h"
"server_pid: %d\n"
"afs_pid: %d\n"
"connections (active/accepted/total): %u/%u/%u\n"
- "current loglevel: %s\n"
"supported audio formats: %s\n",
ut, mmd->num_played,
(int)getppid(),
mmd->active_connections,
mmd->num_commands,
mmd->num_connects,
- ENUM_STRING_VAL(LOGLEVEL),
AUDIO_FORMAT_HANDLERS
);
mutex_unlock(mmd_mutex);
}
EXPORT_SERVER_CMD_HANDLER(hup);
+static int com_ll(struct command_context *cc, struct lls_parse_result *lpr)
+{
+ unsigned ll, perms;
+ char *errctx;
+ const char *sev[] = {SEVERITIES}, *arg;
+ int ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ if (lls_num_inputs(lpr) == 0) { /* reporting is an unprivileged op. */
+ const char *severity;
+ mutex_lock(mmd_mutex);
+ severity = sev[mmd->loglevel];
+ mutex_unlock(mmd_mutex);
+ return send_sb_va(&cc->scc, SBD_OUTPUT, "%s\n", severity);
+ }
+ /*
+ * Changing the loglevel changes the state of both the afs and the vss,
+ * so we require both AFS_WRITE and VSS_WRITE.
+ */
+ perms = AFS_WRITE | VSS_WRITE;
+ if ((cc->u->perms & perms) != perms)
+ return -ERRNO_TO_PARA_ERROR(EPERM);
+ arg = lls_input(0, lpr);
+ for (ll = 0; ll < NUM_LOGLEVELS; ll++)
+ if (!strcmp(arg, sev[ll]))
+ break;
+ if (ll >= NUM_LOGLEVELS)
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ PARA_INFO_LOG("new log level: %s\n", sev[ll]);
+ /* Ask the server and afs processes to adjust their log level. */
+ mutex_lock(mmd_mutex);
+ mmd->loglevel = ll;
+ mutex_unlock(mmd_mutex);
+ return 1;
+}
+EXPORT_SERVER_CMD_HANDLER(ll);
+
static int com_term(__a_unused struct command_context *cc,
__a_unused struct lls_parse_result *lpr)
{
*/
void daemon_set_log_color_or_die(const char *arg)
{
+ unsigned ll;
+ const char * const sev[] = {SEVERITIES};
char *p = strchr(arg, ':');
- int ret, ll;
if (!p)
goto err;
- ret = get_loglevel_by_name(arg);
- if (ret < 0)
- goto err;
- ll = ret;
- p++;
- color_parse_or_die(p, me->log_colors[ll]);
- return;
+ 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);
}
/**
- * Suppress log messages with severity lower than the given loglevel.
+ * Control the verbosity for logging.
*
- * \param loglevel The smallest level that should be logged.
+ * 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(const char *loglevel)
+void daemon_set_loglevel(int loglevel)
{
- int ret = get_loglevel_by_name(loglevel);
+ assert(loglevel >= 0);
+ assert(loglevel < NUM_LOGLEVELS);
+ me->loglevel = loglevel;
+}
- assert(ret >= 0);
- me->loglevel = ret;
+/**
+ * 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;
}
/**
void daemon_set_logfile(const char *logfile_name);
void daemon_set_hooks(void (*pre_log_hook)(void), void (*post_log_hook)(void));
void daemon_set_flag(unsigned flag);
-void daemon_set_loglevel(const char *loglevel);
+int daemon_get_loglevel(void);
+void daemon_set_loglevel(int loglevel);
bool daemon_init_colors_or_die(int color_arg, int color_arg_auto,
int color_arg_no, bool logfile_given);
void daemon_set_log_color_or_die(const char *arg);
PARA_ERROR(BAD_CT, "invalid chunk table or bad FEC configuration"), \
PARA_ERROR(BAD_FEATURE, "invalid feature request"), \
PARA_ERROR(BAD_FEC_HEADER, "invalid fec header"), \
- PARA_ERROR(BAD_LL, "invalid loglevel"), \
PARA_ERROR(BAD_PATH, "invalid path"), \
PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \
PARA_ERROR(BAD_SAMPLE_FORMAT, "sample format not supported"), \
free(ci.word);
return ret;
}
+
+/**
+ * Complete on severity strings.
+ *
+ * \param ci See struct \ref i9e_completer.
+ * \param cr See struct \ref i9e_completer.
+ *
+ * This is used by para_client and para_audioc which need the same completion
+ * primitive for the ll server/audiod command. Both define their own completer
+ * which is implemented as a trivial wrapper that calls this function.
+ */
+void i9e_ll_completer(struct i9e_completion_info *ci,
+ struct i9e_completion_result *cr)
+{
+ char *sev[] = {SEVERITIES, NULL};
+
+ if (ci->word_num != 1) {
+ cr->matches = NULL;
+ return;
+ }
+ i9e_extract_completions(ci->word, sev, &cr->matches);
+}
struct i9e_completion_result *cr);
int i9e_print_completions(struct i9e_completer *completers);
int i9e_get_error(void);
+void i9e_ll_completer(struct i9e_completion_info *ci,
+ struct i9e_completion_result *cr);
short_opt = o
summary = One-shot mode: Stop grabbing if audio file changes
+m4_include(`com_ll.m4')
+
[subcommand off]
purpose = deactivate para_audiod
[description]
--- /dev/null
+[subcommand ll]
+ purpose = Query or set the log level of the daemon
+ m4_ifelse(SUITE, `server_cmd', `aux_info = NO_PERMISSION_REQUIRED')
+ non-opts-name = [severity]
+ [description]
+ If no argument is given, the command prints the severity string (one
+ of the possible string arguments to --loglevel) which corresponds to
+ the current loglevel. Otherwise, if the given argument is a severity
+ string, the current log level is set accordingly.
+ [/description]
$(call SAY, M4 $<)
$(M4) -Pg -I $(lls_m4_include_dir) -D GIT_VERSION=$(GIT_VERSION) \
-D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) -D LOGLEVELS=$(LOGLEVELS) \
+ -D SUITE=$(basename $(notdir $<)) \
$< > $@
$(lls_suite_dir)/%.lsg.c: $(lls_suite_dir)/%.suite
playlists. Otherwise only the given tables are created.
[/description]
+m4_include(`com_ll.m4')
+
[subcommand jmp]
purpose = reposition the current stream
non-opts-name = n
mmd->active_connections = 0;
mmd->vss_status_flags = VSS_NEXT;
mmd->new_vss_status_flags = VSS_NEXT;
+ mmd->loglevel = OPT_UINT32_VAL(LOGLEVEL);
return;
destroy_mmd_mutex:
mutex_destroy(mmd_mutex);
exit(EXIT_FAILURE);
}
+/** Get a reference to the supercommand of para_server. */
+#define CMD_PTR (lls_cmd(0, server_suite))
+
/**
* (Re-)read the server configuration files.
*
para_strerror(-ret));
exit(EXIT_FAILURE);
}
- daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+ daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
if (OPT_GIVEN(LOGFILE)) {
daemon_set_logfile(OPT_STRING_VAL(LOGFILE));
daemon_open_log_or_die();
if (ret < 0)
goto fail;
server_lpr = cmdline_lpr;
- daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+ daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
OPT_STRING_VAL(GROUP));
version_handle_flag("server", OPT_GIVEN(VERSION));
{
int ret;
+ daemon_set_loglevel(mmd->loglevel);
status_refresh();
mutex_unlock(mmd_mutex);
ret = xpoll(fds, nfds, timeout);
char afs_mode_string[MAXLINE];
/** Used by the sender command. */
struct sender_command_data sender_cmd_data;
+ /** Set by the ll command. */
+ int loglevel;
/** Describes the current audio file. */
struct audio_file_data afd;
};
extern pid_t afs_pid;
extern struct lls_parse_result *server_lpr;
-/**
- * Get a reference to the supercommand of para_server.
- *
- * This is needed for parsing the command line and for the ENUM_STRING_VAL()
- * macro below. The latter macro is used in command.c, so CMD_PTR() can not
- * be made local to server.c.
- */
-#define CMD_PTR (lls_cmd(0, server_suite))
-
/** Get the parse result of an option to para_server. */
#define OPT_RESULT(_name) (lls_opt_result( \
LSG_SERVER_PARA_SERVER_OPT_ ## _name, server_lpr))
/** The (first) argument to a server option of type int32. */
#define OPT_INT32_VAL(_name) (lls_int32_val(0, OPT_RESULT(_name)))
-/** Get the string which corresponds to an enum constant. */
-#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \
- lls_opt(LSG_SERVER_PARA_SERVER_OPT_ ## _name, CMD_PTR)))
-
int handle_connect(int fd);
void parse_config_or_die(bool reload);
char *server_get_tasks(void);
return 1;
}
-static inline int loglevel_equal(const char *arg, const char * const ll)
-{
- return !strncasecmp(arg, ll, strlen(ll));
-}
-
-/**
- * Compute the loglevel number from its name.
- *
- * \param txt The name of the loglevel (debug, info, ...).
- *
- * \return The numeric representation of the loglevel name.
- */
-int get_loglevel_by_name(const char *txt)
-{
- if (loglevel_equal(txt, "debug"))
- return LL_DEBUG;
- if (loglevel_equal(txt, "info"))
- return LL_INFO;
- if (loglevel_equal(txt, "notice"))
- return LL_NOTICE;
- if (loglevel_equal(txt, "warning"))
- return LL_WARNING;
- if (loglevel_equal(txt, "error"))
- return LL_ERROR;
- if (loglevel_equal(txt, "crit"))
- return LL_CRIT;
- if (loglevel_equal(txt, "emerg"))
- return LL_EMERG;
- return -E_BAD_LL;
-}
-
static int get_next_word(const char *buf, const char *delim, char **word)
{
enum line_state_flags {LSF_HAVE_WORD = 1, LSF_BACKSLASH = 2,
__printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...);
int para_atoi64(const char *str, int64_t *result);
int para_atoi32(const char *str, int32_t *value);
-int get_loglevel_by_name(const char *txt);
int read_size_header(const char *buf);
int create_argv(const char *buf, const char *delim, char ***result);
int create_shifted_argv(const char *buf, const char *delim, char ***result);