A single commit that was cooking for several months.
* refs/heads/t/image_id_mm:
server: Add mood methods image_id and lyrics_id.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
-SEARCHENGINE = YES
+SEARCHENGINE = NO
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
# implemented using a web server instead of a web client using Javascript. There
+# Implicit rules are implemented in make as suffix rules. The following rule
+# empties the suffix list to disable the predefined implicit rules. This
+# increases performance and avoids hard-to-debug behaviour.
+.SUFFIXES:
+MAKEFLAGS += -Rr
+ifeq ("$(origin CC)", "default")
+ CC := cc
+endif
+
vardir := /var/paraslash
mandir := $(datarootdir)/man/man1
STRIP := $(CROSS_COMPILE)strip
uname_rs := $(shell uname -rs)
cc_version := $(shell $(CC) --version | head -n 1)
GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h)
+build_date := $(shell date)
ifeq ("$(origin O)", "command line")
build_dir := $(O)
$(subst z,Z,$1))))))))))))))))))))))))))
CPPFLAGS += -DBINDIR='"$(bindir)"'
-CPPFLAGS += -DBUILD_DATE='"$(shell date)"'
-CPPFLAGS += -DUNAME_RS='"$(shell uname -rs)"'
-CPPFLAGS += -DCC_VERSION='"$(shell $(CC) --version | head -n 1)"'
+CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
+CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
+CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
CPPFLAGS += -DMAIN_INPUT_FILE_IS_$(*F)
-CPPFLAGS += $(arch_cppflags)
CPPFLAGS += -I/usr/local/include
CPPFLAGS += -I$(cmdline_dir)
CPPFLAGS += -I$(cmdlist_dir)
$(man_dir)/para_audiod.1: man_util_command_lists := $(audiod_command_lists)
$(man_dir)/para_play.1: man_util_command_lists := $(play_command_lists)
-$(man_dir)/para_%.1: $(ggo_dir)/%.ggo man_util.bash | $(man_dir) $(help2man_dir)
+$(man_dir)/para_%.1: $(ggo_dir)/%.ggo man_util.bash \
+ git-version.h | $(man_dir) $(help2man_dir)
@[ -z "$(Q)" ] || echo 'MAN $<'
$(Q) \
COMMAND_LISTS="$(man_util_command_lists)" \
current master branch "cascading gradient"
------------------------------------------
+The highlight of this release is the new -m flag for para_afh which
+lets it modify the meta tags of the given audio file(s). This feature
+is supported for all audio formats. Many small cleanups and bug fixes
+not mentioned here have accumulated and are also part of the release.
+
- para_afh learned to modify meta tags of mp3 wma ogg spx
opus flac aac files.
- afs commands propagate error codes to the client.
- The check command now also checks the attribute table for
inconsistencies.
- New -v flag for the version command (print verbose version string)
-
+ - New option --priority for para_server and para_audiod.
+ - New mood methods: image_id and lyrics_id.
--------------------------------------
0.5.5 (2015-09-20) "magnetic momentum"
static int atom_cmp(const unsigned char *buf1, const char *buf2)
{
- const unsigned char *b2 = (unsigned char *)buf2;
-
- if (buf1[0] != b2[0])
- return 1;
- if (buf1[1] != b2[1])
- return 1;
- if (buf1[2] != b2[2])
- return 1;
- if (buf1[3] != b2[3])
- return 1;
- return 0;
+ return memcmp(buf1, buf2, 4)? 1 : 0;
}
static int read_atom_header(unsigned char *buf, uint64_t *subsize, unsigned char type[5])
{
int i;
- uint64_t size = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
+ uint64_t size = aac_read_int32(buf);
memcpy(type, buf + 4, 4);
type[4] = '\0';
if (ret < 0)
return ret;
afhi->chunks_total = ret;
- PARA_DEBUG_LOG("sz table has %lu entries\n", afhi->chunks_total);
+ PARA_DEBUG_LOG("sz table has %" PRIu32 " entries\n", afhi->chunks_total);
afhi->chunk_table = para_malloc((afhi->chunks_total + 1) * sizeof(size_t));
for (i = 1; i <= afhi->chunks_total; i++) {
if (skip + 4 > numbytes)
}
static int aac_set_chunk_tv(struct afh_info *afhi,
- mp4AudioSpecificConfig *mp4ASC, long unsigned *seconds)
+ mp4AudioSpecificConfig *mp4ASC, uint32_t *seconds)
{
float tmp = mp4ASC->sbr_present_flag == 1? 2047 : 1023;
struct timeval total;
ms = 1000.0 * afhi->chunks_total * tmp / mp4ASC->samplingFrequency;
ms2tv(ms, &total);
tv_divide(afhi->chunks_total, &total, &afhi->chunk_tv);
- PARA_INFO_LOG("%luHz, %lus (%lu x %lums)\n",
+ PARA_INFO_LOG("%luHz, %lus (%" PRIu32 " x %lums)\n",
mp4ASC->samplingFrequency, ms / 1000,
afhi->chunks_total, tv2ms(&afhi->chunk_tv));
if (ms < 1000)
/** Audio format dependent information. */
struct afh_info {
/** The number of chunks this audio file contains. */
- long unsigned chunks_total;
+ uint32_t chunks_total;
/** The length of the audio file in seconds. */
- long unsigned seconds_total;
+ uint32_t seconds_total;
/** Audio handler specific info about the file. */
char *techinfo;
/** Id3 tags, vorbis comments, aac tags. */
"%s: %s\n" /* format */
"%s: %dHz\n" /* frequency */
"%s: %d\n" /* channels */
- "%s: %lu\n" /* seconds total */
+ "%s: %" PRIu32 "\n" /* seconds total */
"%s: %lu: %lu\n" /* chunk time */
- "%s: %lu\n" /* num chunks */
+ "%s: %" PRIu32 "\n" /* num chunks */
"%s: %s\n" /* techinfo */
"%s: %s\n" /* artist */
"%s: %s\n" /* title */
*result = NULL;
if (!strcmp(cmd, "seconds_total")) {
- *result = make_message("%lu", pard->afhi.seconds_total);
+ *result = make_message("%" PRIu32, pard->afhi.seconds_total);
return 1;
}
if (!strcmp(cmd, "chunks_total")) {
- *result = make_message("%lu", pard->afhi.chunks_total);
+ *result = make_message("%" PRIu32, pard->afhi.chunks_total);
return 1;
}
if (!strcmp(cmd, "afhi")) {
/**
* A random number used to "authenticate" the connection.
*
- * para_server picks this number by random before forking the afs process. The
- * command handlers write this number together with the id of the shared memory
- * area containing the query. This way, a malicious local user has to know this
- * number to be able to cause the afs process to crash by sending fake queries.
+ * para_server picks this number by random before it forks the afs process. The
+ * command handlers know this number as well and write it to the afs socket,
+ * together with the id of the shared memory area which contains the payload of
+ * the afs command. A local process has to know this number to abuse the afs
+ * service provided by the local socket.
*/
extern uint32_t afs_socket_cookie;
{
struct msghdr msg = {.msg_iov = NULL};
struct cmsghdr *cmsg;
- char control[255];
+ char control[255] __a_aligned(8);
int ret;
struct iovec iov;
get_database_dir();
ret = para_mkdir(database_dir, 0777);
- if (ret >= 0 || is_errno(-ret, EEXIST))
+ if (ret >= 0 || ret == -ERRNO_TO_PARA_ERROR(EEXIST))
return 1;
return ret;
}
/** \file afs.h Exported symbols of the audio file selector. */
-#include <regex.h>
-
/** Audio file selector data stored in the audio file table. */
struct afs_info {
/** Seconds since the epoch. */
static int get_local_time(uint64_t *seconds, char *buf, size_t size,
time_t current_time, enum ls_listing_mode lm)
{
- struct tm t;
+ struct tm *tm;
+ /*
+ * Omit year but show time if the given value is closer to the current
+ * time than this many seconds.
+ */
+ const time_t m = 6 * 30 * 24 * 3600; /* six months */
- if (!localtime_r((time_t *)seconds, &t))
+ tm = localtime((time_t *)seconds);
+ if (!tm)
return -E_LOCALTIME;
if (lm == LS_MODE_MBOX) {
- if (!strftime(buf, size, "%c", &t))
+ if (!strftime(buf, size, "%c", tm))
return -E_STRFTIME;
return 1;
}
- if (*seconds + 6 * 30 * 24 * 3600 > current_time) {
- if (!strftime(buf, size, "%b %e %k:%M", &t))
+ if (*seconds > current_time - m && *seconds < current_time + m) {
+ if (!strftime(buf, size, "%b %e %k:%M", tm))
return -E_STRFTIME;
return 1;
}
- if (!strftime(buf, size, "%b %e %Y", &t))
+ if (!strftime(buf, size, "%b %e %Y", tm))
return -E_STRFTIME;
return 1;
}
WRITE_STATUS_ITEM(b, SI_FREQUENCY, "%dHz\n", afhi->frequency);
WRITE_STATUS_ITEM(b, SI_CHANNELS, "%d\n", afhi->channels);
WRITE_STATUS_ITEM(b, SI_DURATION, "%s\n", duration_buf);
- WRITE_STATUS_ITEM(b, SI_SECONDS_TOTAL, "%lu\n", afhi->seconds_total);
+ WRITE_STATUS_ITEM(b, SI_SECONDS_TOTAL, "%" PRIu32 "\n",
+ afhi->seconds_total);
WRITE_STATUS_ITEM(b, SI_LAST_PLAYED, "%s\n", last_played_time);
WRITE_STATUS_ITEM(b, SI_NUM_PLAYED, "%d\n", afsi->num_played);
WRITE_STATUS_ITEM(b, SI_AMPLIFICATION, "%u\n", afsi->amp);
WRITE_STATUS_ITEM(b, SI_CHUNK_TIME, "%lu\n", tv2ms(&afhi->chunk_tv));
- WRITE_STATUS_ITEM(b, SI_NUM_CHUNKS, "%lu\n", afhi->chunks_total);
+ WRITE_STATUS_ITEM(b, SI_NUM_CHUNKS, "%" PRIu32 "\n",
+ afhi->chunks_total);
WRITE_STATUS_ITEM(b, SI_TECHINFO, "%s\n", afhi->techinfo);
WRITE_STATUS_ITEM(b, SI_ARTIST, "%s\n", afhi->tags.artist);
WRITE_STATUS_ITEM(b, SI_TITLE, "%s\n", afhi->tags.title);
objs[AFTCOL_AFSI].size = AFSI_SIZE;
save_afsi(&default_afsi, &objs[AFTCOL_AFSI]);
ret = osl(osl_add_and_get_row(audio_file_table, objs, &aft_row));
+ if (ret < 0)
+ goto out;
ret = afs_event(AUDIO_FILE_ADD, &aca->pbout, aft_row);
out:
if (ret < 0)
) {
char c;
unsigned char bitnum;
+ uint64_t one = 1;
len = strlen(p);
ret = -E_ATTR_SYNTAX;
goto out;
}
if (c == '+')
- cad.add_mask |= (1UL << bitnum);
+ cad.add_mask |= (one << bitnum);
else
- cad.del_mask |= (1UL << bitnum);
+ cad.del_mask |= (one << bitnum);
}
ret = -E_ATTR_SYNTAX;
if (!cad.add_mask && !cad.del_mask)
return;
for (i = a->num_filters - 1; i >= 0; i--) {
struct filter_node *fn = s->fns + i;
- struct filter *f;
+ const struct filter *f;
if (!fn)
continue;
- f = filters + fn->filter_num;
+ f = filter_get(fn->filter_num);
if (f->close)
f->close(fn);
btr_remove_node(&fn->btrn);
parent = s->receiver_node->btrn;
for (i = 0; i < nf; i++) {
char buf[20];
- struct filter *f = filters + a->filter_nums[i];
+ const struct filter *f = filter_get(a->filter_nums[i]);
fn = s->fns + i;
fn->filter_num = a->filter_nums[i];
fn->conf = a->filter_conf[i];
a->filter_conf[nf] = cfg;
a->num_filters++;
PARA_INFO_LOG("%s filter %d: %s\n", audio_formats[format], nf,
- filters[filter_num].name);
+ filter_get(filter_num)->name);
return filter_num;
}
}
/* add "dec" to audio format name */
tmp = make_message("%sdec", audio_formats[i]);
- for (j = 0; filters[j].name; j++)
- if (!strcmp(tmp, filters[j].name))
+ for (j = 0; filter_get(j); j++)
+ if (!strcmp(tmp, filter_get(j)->name))
break;
free(tmp);
ret = -E_UNSUPPORTED_FILTER;
- if (!filters[j].name)
+ if (!filter_get(j))
goto out;
- tmp = para_strdup(filters[j].name);
+ tmp = para_strdup(filter_get(j)->name);
ret = add_filter(i, tmp);
free(tmp);
if (ret < 0)
goto out;
PARA_INFO_LOG("%s -> default filter: %s\n", audio_formats[i],
- filters[j].name);
+ filter_get(j)->name);
}
out:
return ret;
writer_init();
if (conf.help_given || conf.detailed_help_given)
print_help_and_die();
+ daemon_set_priority(conf.priority_arg);
daemon_drop_privileges_or_die(conf.user_arg, conf.group_arg);
parse_config_or_die();
daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no,
I9E_DUMMY_COMPLETER(play);
I9E_DUMMY_COMPLETER(si);
I9E_DUMMY_COMPLETER(term);
-I9E_DUMMY_COMPLETER(version);
I9E_DUMMY_COMPLETER(stop);
I9E_DUMMY_COMPLETER(addatt);
I9E_DUMMY_COMPLETER(init);
result->matches = i9e_complete_commands(ci->word, completers);
}
+static void version_completer(struct i9e_completion_info *ci,
+ struct i9e_completion_result *cr)
+{
+ char *opts[] = {"-v", NULL};
+ i9e_complete_option(opts, ci, cr);
+}
+
static void stat_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
struct i9e_completion_result *cr)
{
char *opts[] = {"-i", "-l", "-r", NULL};
-
- if (ci->word[0] == '-')
- i9e_complete_option(opts, ci, cr);
- else
- complete_attributes(ci->word, &cr->matches);
+ i9e_complete_option(opts, ci, cr);
}
static void mvatt_completer(struct i9e_completion_info *ci,
btr_consume(ct->btrn[1], sz);
}
}
- /* fall though */
+ /* fall through */
case CL_EXECUTING:
if (ct->btrn[0]) {
ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT);
{
}
-static void mmd_dup(struct misc_meta_data *new_mmd)
-{
- mutex_lock(mmd_mutex);
- *new_mmd = *mmd;
- mutex_unlock(mmd_mutex);
-}
-
/*
- * Compute human readable string containing vss status for given integer value.
+ * Compute human readable vss status text.
*
- * We don't want to use vss_playing() and friends here because we take a
- * snapshot of the mmd struct and use the copy for computing the state of the
- * vss. If the real data were used, we would take the mmd lock for a rather
- * long time or risk to get an inconsistent view.
+ * We can't call vss_playing() and friends here because those functions read
+ * the flags from the primary mmd structure, so calling them from command
+ * handler context would require to take the mmd lock. At the time the function
+ * is called we already took a copy of the mmd structure and want to use the
+ * flags value of the copy for computing the vss status text.
*/
static char *vss_status_tohuman(unsigned int flags)
{
static int check_sender_args(int argc, char * const * argv, struct sender_command_data *scd)
{
int i;
- /* this has to match sender.h */
- const char *subcmds[] = {"add", "delete", "allow", "deny", "on", "off", NULL};
+ const char *subcmds[] = {SENDER_SUBCOMMANDS NULL};
scd->sender_num = -1;
if (argc < 3)
return -E_COMMAND_SYNTAX;
if (!senders[scd->sender_num].client_cmds[scd->cmd_num])
return -E_SENDER_CMD;
switch (scd->cmd_num) {
- case SENDER_ON:
- case SENDER_OFF:
+ case SENDER_on:
+ case SENDER_off:
if (argc != 3)
return -E_COMMAND_SYNTAX;
break;
- case SENDER_DENY:
- case SENDER_ALLOW:
+ case SENDER_deny:
+ case SENDER_allow:
if (argc != 4 || parse_cidr(argv[3], scd->host,
sizeof(scd->host), &scd->netmask) == NULL)
return -E_COMMAND_SYNTAX;
break;
- case SENDER_ADD:
- case SENDER_DELETE:
+ case SENDER_add:
+ case SENDER_delete:
if (argc != 4)
return -E_COMMAND_SYNTAX;
return parse_fec_url(argv[3], scd);
}
switch (scd.cmd_num) {
- case SENDER_ADD:
- case SENDER_DELETE:
+ case SENDER_add:
+ case SENDER_delete:
assert(senders[scd.sender_num].resolve_target);
ret = senders[scd.sender_num].resolve_target(cc->argv[3], &scd);
if (ret < 0)
if (i != cc->argc)
return -E_COMMAND_SYNTAX;
for (;;) {
- mmd_dup(nmmd);
+ /*
+ * Copy the mmd structure to minimize the time we hold the mmd
+ * lock.
+ */
+ mutex_lock(mmd_mutex);
+ *nmmd = *mmd;
+ mutex_unlock(mmd_mutex);
ret = get_status(nmmd, parser_friendly, &s);
ret = send_sb(&cc->scc, s, ret, SBD_OUTPUT, false);
if (ret < 0)
AC_SUBST($1_ldflags)
])
-AC_PATH_PROG(UNAMEPATH, uname, no)
-if test "$UNAMEPATH" = "no"; then
- AC_MSG_ERROR(unable to determine system type)
-fi
-AC_MSG_CHECKING(os type)
-OSTYPE="`$UNAMEPATH -s`"
-AC_MSG_RESULT("$OSTYPE")
-
-if test "$OSTYPE" = "SunOS"; then
- # needed on SunOS for socket magic
- arch_cppflags="-D_XOPEN_SOURCE=500 -D__EXTENSIONS__"
- AC_SUBST(arch_cppflags)
-fi
-
AC_C_BIGENDIAN()
AC_PATH_PROG([GENGETOPT], [gengetopt])
#include <sys/types.h> /* getgrnam() */
#include <grp.h>
#include <signal.h>
+#include <sys/resource.h>
#include "para.h"
#include "daemon.h"
PARA_INFO_LOG("welcome to para_%s-" PACKAGE_VERSION " \n", name);
}
+/**
+ * Renice the calling process.
+ *
+ * \param prio The priority value to set.
+ *
+ * Errors are not considered fatal, but a warning message is logged if the
+ * underlying call to setpriority(2) fails.
+ */
+void daemon_set_priority(int prio)
+{
+ if (setpriority(PRIO_PROCESS, 0, prio) < 0)
+ PARA_WARNING_LOG("could not set priority to %d: %s\n", prio,
+ strerror(errno));
+}
+
/**
* Give up superuser privileges.
*
void daemon_open_log_or_die(void);
void daemon_close_log(void);
void daemon_log_welcome(const char *whoami);
+void daemon_set_priority(int prio);
void daemon_drop_privileges_or_die(const char *username, const char *groupname);
void daemon_set_start_time(void);
time_t daemon_get_uptime(const struct timeval *current_time);
s->shutdown_clients = dccp_shutdown_clients;
s->resolve_target = NULL;
s->help = generic_sender_help;
- s->client_cmds[SENDER_ON] = dccp_com_on;
- s->client_cmds[SENDER_OFF] = dccp_com_off;
- s->client_cmds[SENDER_DENY] = dccp_com_deny;
- s->client_cmds[SENDER_ALLOW] = dccp_com_allow;
- s->client_cmds[SENDER_ADD] = NULL;
- s->client_cmds[SENDER_DELETE] = NULL;
+ s->client_cmds[SENDER_on] = dccp_com_on;
+ s->client_cmds[SENDER_off] = dccp_com_off;
+ s->client_cmds[SENDER_deny] = dccp_com_deny;
+ s->client_cmds[SENDER_allow] = dccp_com_allow;
+ s->client_cmds[SENDER_add] = NULL;
+ s->client_cmds[SENDER_delete] = NULL;
k = conf.dccp_data_slices_per_group_arg;
n = conf.dccp_slices_per_group_arg;
PARA_ERROR(SIZE_PREFIX, "bad size prefix"), \
PARA_ERROR(REGEX, "regular expression error"), \
PARA_ERROR(ARG_NOT_FOUND, "argument not found in arg vector"), \
+ PARA_ERROR(BAD_LL, "invalid loglevel"), \
#define EXEC_ERRORS \
PARA_ERROR(MP4ASC, "audio spec config error"), \
PARA_ERROR(AAC_AFH_INIT, "failed to init aac decoder"), \
PARA_ERROR(MP4V2, "mp4v2 library error"), \
- PARA_ERROR(NO_AUDIO_TRACK, "file contains no valid audio track"), \
#define AAC_COMMON_ERRORS \
PARA_ERROR(ESDS, "did not find esds atom"), \
/** Set the osl error bit for the given number. */
#define OSL_ERRNO_TO_PARA_ERROR(num) ((num) | (1 << OSL_ERROR_BIT))
-/** Check whether a given number is a system error number.
- *
- * \param num The value to be checked.
- * \param _errno The system error number.
- *
- * \return True if \a num is paraslash's representation of the system
- * error identified by \a _errno.
- */
-_static_inline_ bool is_errno(int num, int _errno)
-{
- assert(num > 0 && _errno > 0);
- return ERRNO_TO_PARA_ERROR(_errno) == num;
-}
+static const char *weak_osl_strerror(int) __attribute__ ((weakref("osl_strerror")));
/**
* Paraslash's version of strerror(3).
*
_static_inline_ const char *para_strerror(int num)
{
assert(num > 0);
-#ifdef _OSL_H
- if (IS_OSL_ERROR(num))
- return osl_strerror(num & ((1 << OSL_ERROR_BIT) - 1));
-#endif
+ if (IS_OSL_ERROR(num)) {
+ assert(weak_osl_strerror);
+ return weak_osl_strerror(num & ~(1U << OSL_ERROR_BIT));
+ }
if (IS_SYSTEM_ERROR(num))
- return strerror(num & ((1 << SYSTEM_ERROR_BIT) - 1));
+ return strerror(num & ~(1U << SYSTEM_ERROR_BIT));
return para_errlist[ERRNUM_TO_SS(num)][ERRNUM_TO_INDEX(num)];
}
{
static struct sched s;
int i, ret;
- struct filter *f;
+ const struct filter *f;
struct btr_node *parent;
struct filter_node **fns;
goto out_cleanup;
}
fn->filter_num = ret;
- f = filters + fn->filter_num;
+ f = filter_get(fn->filter_num);
PARA_DEBUG_LOG("filter #%d: %s\n", i, f->name);
fn->btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.name = f->name, .parent = parent,
for (i--; i >= 0; i--) {
struct filter_node *fn = fns[i];
- f = filters + fn->filter_num;
+ f = filter_get(fn->filter_num);
if (f->close)
f->close(fn);
btr_remove_node(&fn->btrn);
/**
* Set scheduler timeout and add file descriptors to fd sets.
*
- * This function is used to control the timeout value for select. It
- * only allowed to decrease the current value. The second purpose of
- * this function is to set file descriptors to be watched by the
- * subsequent select call to the two fd sets.
+ * This function controls the timeout value for the next call to
+ * select(2). It may decrease the current timeout but shall never
+ * increase it. The second purpose of this function is to add file
+ * descriptors to the two fd sets of the sched structure. The
+ * descriptors in these sets will be watched by the subsequent
+ * select(2) call.
*/
void (*pre_select)(struct sched *s, void *context);
/**
DECLARE_FILTER_INITS
-/** Iterate over the array of supported filters. */
-#define FOR_EACH_SUPPORTED_FILTER(j) for (j = 0; j < NUM_SUPPORTED_FILTERS; j++)
-
/** The filter array, one structure for each supported filter. */
-extern struct filter filters[NUM_SUPPORTED_FILTERS];
+const struct filter *filter_get(int filter_num);
#include "error.h"
#include "string.h"
+/** Iterate over the array of supported filters. */
+#define FOR_EACH_SUPPORTED_FILTER(j) for (j = 0; j < NUM_SUPPORTED_FILTERS; j++)
+
/** The array of supported filters. */
-struct filter filters[NUM_SUPPORTED_FILTERS] = {FILTER_ARRAY};
+static struct filter filters[NUM_SUPPORTED_FILTERS] = {FILTER_ARRAY};
+
+const struct filter *filter_get(int filter_num)
+{
+ assert(filter_num >= 0);
+ assert(filter_num < NUM_SUPPORTED_FILTERS);
+ return filters + filter_num;
+}
/**
* Call the init function of each supported filter.
int i;
FOR_EACH_SUPPORTED_FILTER(i)
- filters[i].init(filters + i);
+ filter_get(i)->init((struct filter *)filter_get(i));
}
/*
*/
static int parse_filter_args(int filter_num, char *options, void **conf)
{
- struct filter *f = &filters[filter_num];
+ const struct filter *f = filter_get(filter_num);
int ret, argc;
char **argv;
*conf = NULL;
// PARA_DEBUG_LOG("arg: %s\n", fa);
FOR_EACH_SUPPORTED_FILTER(j) {
- const char *name = filters[j].name;
+ const char *name = filter_get(j)->name;
size_t len = strlen(name);
char c;
if (strlen(fa) < len)
c = fa[len];
if (c && c != ' ')
continue;
- if (c && !filters[j].parse_config)
+ if (c && !filter_get(j)->parse_config)
return -E_BAD_FILTER_OPTIONS;
return parse_filter_args(j, c? fa + len + 1 :
fa + strlen(fa), conf);
printf_or_die("\n ");
num = 0;
}
- num += printf_or_die("%s%s", i? " " : "", filters[i].name);
+ num += printf_or_die("%s%s", i? " " : "", filter_get(i)->name);
}
printf_or_die("\n");
FOR_EACH_SUPPORTED_FILTER(i) {
- struct filter *f = filters + i;
+ struct filter *f = (struct filter *)filter_get(i);
if (!f->help.short_help)
continue;
waddstr(win, str);
} else {
add_spaces(win, num / 2);
- waddstr(win, str[0]? str: "");
+ waddstr(win, str);
add_spaces(win, num - num / 2);
}
return 1;
{
if (curses_active())
return;
- if (top.win && refresh() == ERR) /* refresh is really needed */
+ if (refresh() == ERR) /* refresh is really needed */
die(EXIT_FAILURE, "refresh() failed\n");
if (LINES < theme.lines_min || COLS < theme.cols_min)
die(EXIT_FAILURE, "Terminal (%dx%d) too small"
s->shutdown_clients = http_shutdown_clients;
s->resolve_target = NULL;
s->help = generic_sender_help;
- s->client_cmds[SENDER_ON] = http_com_on;
- s->client_cmds[SENDER_OFF] = http_com_off;
- s->client_cmds[SENDER_DENY] = http_com_deny;
- s->client_cmds[SENDER_ALLOW] = http_com_allow;
- s->client_cmds[SENDER_ADD] = NULL;
- s->client_cmds[SENDER_DELETE] = NULL;
+ s->client_cmds[SENDER_on] = http_com_on;
+ s->client_cmds[SENDER_off] = http_com_off;
+ s->client_cmds[SENDER_deny] = http_com_deny;
+ s->client_cmds[SENDER_allow] = http_com_allow;
+ s->client_cmds[SENDER_add] = NULL;
+ s->client_cmds[SENDER_delete] = NULL;
init_sender_status(hss, conf.http_access_arg, conf.http_access_given,
conf.http_port_arg, conf.http_max_clients_arg,
struct i9e_client_info *ici;
FILE *stderr_stream;
int num_columns;
+ int num_key_bindings;
char empty_line[1000];
struct task *task;
struct btr_node *stdout_btrn;
bool last_write_was_status;
- bool line_handler_running;
bool input_eof;
bool caught_sigint;
bool caught_sigterm;
i9ep->empty_line[i9ep->num_columns] = '\0';
}
-/**
- * Defined key sequences are mapped to keys starting with this offset. I.e.
- * pressing the first defined key sequence yields the key number \p KEY_OFFSET.
- */
-#define KEY_OFFSET 64
-
-static int dispatch_key(__a_unused int count, int key)
+static int dispatch_key(__a_unused int count, __a_unused int key)
{
- int ret;
+ int i, ret;
- assert(key >= KEY_OFFSET);
- ret = i9ep->ici->key_handler(key - KEY_OFFSET);
- return ret < 0? ret : 0;
+ for (i = i9ep->num_key_bindings - 1; i >= 0; i--) {
+ if (strcmp(rl_executing_keyseq, i9ep->ici->bound_keyseqs[i]))
+ continue;
+ ret = i9ep->ici->key_handler(i);
+ return ret < 0? ret : 0;
+ }
+ assert(0);
}
/**
if (ici->bound_keyseqs) {
char *seq;
int i;
- /* FIXME: This is an arbitrary constant. */
- for (i = 0; i < 32 && (seq = ici->bound_keyseqs[i]); i++) {
- char buf[2] = {KEY_OFFSET + i, '\0'};
- /* readline needs an allocated buffer for the macro */
- rl_generic_bind(ISMACR, seq, para_strdup(buf), i9ep->bare_km);
- rl_bind_key_in_map(KEY_OFFSET + i, dispatch_key, i9ep->bare_km);
- }
+ /* bind each key sequence to the our dispatcher */
+ for (i = 0; (seq = ici->bound_keyseqs[i]); i++)
+ rl_generic_bind(ISFUNC, seq, (char *)dispatch_key,
+ i9ep->bare_km);
+ i9ep->num_key_bindings = i;
}
if (ici->history_file)
read_history(ici->history_file);
if (ici->producer) {
rl_callback_handler_install("", i9e_line_handler);
i9e_attach_to_stdout(ici->producer);
- rl_set_keymap(i9ep->bare_km);
} else
rl_callback_handler_install(i9ep->ici->prompt, i9e_line_handler);
return 1;
* Tell i9e that the caller received a signal.
*
* \param sig_num The number of the signal received.
- *
- * Currently the function only cares about \p SIGINT, but this may change.
*/
void i9e_signal_dispatch(int sig_num)
{
option "begin-chunk" b
#~~~~~~~~~~~~~~~~~~~~~
-"skip a number of chunks"
+"skip the beginning of the file"
int typestr = "chunk_num"
default = "0"
optional
details = "
The chunk_num argument must be between -num_chunks and
- num_chunks - 1 inclusively where num_chunks is the total number
- of chunks which is printed when using the --info option. If
- chunk_num is negative, the given number of chunks are counted
- backwards from the end of the file. For example --begin-chunk
- -100 instructs para_afh to start output at chunk num_chunks
- - 100. This is mainly useful for cutting off the end of an
- audio file.
+ num_chunks - 1, inclusively, where num_chunks is the total
+ number of chunks of the audio file given by the argument to
+ --filename. If chunk_num is negative, the given number of
+ chunks are counted backwards from the end of the file. For
+ example --begin-chunk -100 instructs the afh receiver to
+ start output at chunk num_chunks - 100. This is useful for
+ selecting the last part of an audio file.
"
option "end-chunk" e
include(daemon.m4)
include(user.m4)
include(group.m4)
+include(priority.m4)
<qu>
########################
--- /dev/null
+option "priority" -
+#~~~~~~~~~~~~~~~~~~
+"adjust scheduling priority"
+int typestr = "prio"
+default = "0"
+optional
+details = "
+ The priority (also known as nice value) is a value in the range -20
+ to 19. Lower priorities cause more favorable scheduling. Since only
+ privileged processes may request a negative priority, specifying
+ a negative value works only if the daemon is started with root
+ privileges.
+
+ Failure to set the given priority value is not considered an error
+ but a log message is printed in this case.
+"
include(daemon.m4)
include(user.m4)
include(group.m4)
+include(priority.m4)
<qu>
option "port" p
{
if (!n || !qd)
return 0;
- return 100 * (n * x - sum) / (int64_t)int_sqrt(n * qd);
+ return 100 * (n * x - sum) / (int64_t)int_sqrt(n) / (int64_t)int_sqrt(qd);
}
static long compute_score(struct afs_info *afsi, long mood_score)
static int add_afs_statistics(const struct osl_row *row)
{
- uint64_t n, x, s;
+ uint64_t n, x, s, q;
struct afs_info afsi;
int ret;
n = statistics.num;
x = afsi.last_played;
s = statistics.last_played_sum;
- if (n > 0)
- statistics.last_played_qd += (x - s / n) * (x - s / n) * n / (n + 1);
+ if (n > 0) {
+ q = (x > s / n)? x - s / n : s / n - x;
+ statistics.last_played_qd += q * q * n / (n + 1);
+ }
statistics.last_played_sum += x;
x = afsi.num_played;
s = statistics.num_played_sum;
- if (n > 0)
- statistics.num_played_qd += (x - s / n) * (x - s / n) * n / (n + 1);
+ if (n > 0) {
+ q = (x > s / n)? x - s / n : s / n - x;
+ statistics.num_played_qd += q * q * n / (n + 1);
+ }
statistics.num_played_sum += x;
statistics.num++;
return 1;
* the last number a_n was replaced by b) may be computed in O(1) time in terms
* of n, q, a_n, b, and S as
*
- * q' = q + d * s - (2 * S + d) * d / n,
+ * q' = q + d * s - (2 * S + d) * d / n
+ * = q + d * (s - 2 * S / n - d /n),
*
* where d = b - a_n, and s = b + a_n.
*
{
int64_t delta = new_val - old_val;
int64_t sigma = new_val + old_val;
- return old_qd + delta * sigma - (2 * old_sum + delta) * delta / n;
+ return old_qd + delta * (sigma - 2 * old_sum / n - delta / n);
}
static int update_afs_statistics(struct afs_info *old_afsi, struct afs_info *new_afsi)
afhi->channels = header_channels(&header);
afhi->seconds_total = (tv2ms(&total_time) + 500) / 1000;
tv_divide(afhi->chunks_total, &total_time, &afhi->chunk_tv);
- PARA_DEBUG_LOG("%lu chunks, each %lums\n", afhi->chunks_total,
+ PARA_DEBUG_LOG("%" PRIu32 "chunks, each %lums\n", afhi->chunks_total,
tv2ms(&afhi->chunk_tv));
ret = mp3_get_id3(map, numbytes, fd, &afhi->tags);
afhi->techinfo = make_message("%cbr, %s, %s tags", vbr? 'v' : 'c',
*/
int recv_cred_buffer(int fd, char *buf, size_t size)
{
- char control[255];
+ char control[255] __a_aligned(8);
struct msghdr msg;
struct cmsghdr *cmsg;
struct iovec iov;
afhi->seconds_total = num_frames / afhi->frequency;
/* use roughly one page per chunk */
frames_per_chunk = num_frames / i;
- PARA_INFO_LOG("%lu seconds, %d frames/chunk\n",
+ PARA_INFO_LOG("%" PRIu32 "seconds, %d frames/chunk\n",
afhi->seconds_total, frames_per_chunk);
ct_size = 250;
afhi->chunk_table = para_malloc(ct_size * sizeof(uint32_t));
if (ret < 0)
return ret;
afhi->channels = oh->channels;
- afhi->techinfo = make_message("header version %d, input sample rate: %dHz",
+ afhi->techinfo = make_message(
+ "header version %d, input sample rate: %" PRIu32 "Hz",
oh->version, oh->input_sample_rate);
/*
* The input sample rate is irrelevant for afhi->frequency as
if (!read_chars(&p, &ch, 1))
return -E_OPUS_HEADER;
h->version = ch;
- if((h->version & 240) != 0) /* Only major version 0 supported. */
+ if ((h->version & 240) != 0) /* Only major version 0 supported. */
return -E_OPUS_HEADER;
if (!read_chars(&p, &ch, 1))
*
* 2. The wav header (para_write only).
*
- * 3. The --format option of para_write.
+ * 3. The --sample-format option of para_write.
*/
#define SAMPLE_FORMATS \
SAMPLE_FORMAT(SF_S8, "8 bit signed"), \
static int eof_cleanup(struct play_task *pt)
{
struct writer *w = writers + DEFAULT_WRITER;
- struct filter *decoder = filters + pt->fn.filter_num;
+ const struct filter *decoder = filter_get(pt->fn.filter_num);
int ret;
ret = get_playback_error(pt);
const char *af;
char *tmp, buf[20];
int ret;
- struct filter *decoder;
+ const struct filter *decoder;
btr_remove_node(&pt->rn.btrn);
if (!pt->rn.receiver || pt->next_file != pt->current_file) {
if (ret < 0)
goto fail;
pt->fn.filter_num = ret;
- decoder = filters + ret;
+ decoder = filter_get(ret);
pt->fn.btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.name = decoder->name, .parent = pt->rn.btrn,
.handler = decoder->execute, .context = &pt->fn));
return ret;
if (percent < 0 || percent > 100)
return -ERRNO_TO_PARA_ERROR(EINVAL);
+ if (percent == 100)
+ return com_next(pt, 1, (char *[]){"next", NULL});
if (pt->playing && !pt->fn.btrn)
return 0;
pt->start_chunk = percent * pt->num_chunks / 100;
* stderr. Once the i9e subsystem has been initialized, we switch to the i9e
* log facility.
*/
-static void session_open(__a_unused struct play_task *pt)
+static void session_open(struct play_task *pt)
{
int ret;
char *history_file;
char *new_path;
const struct osl_row *row = data;
- if (!current_playlist.name)
- return 1;
if (event == AUDIO_FILE_RENAME) {
ret = row_belongs_to_score_table(row, NULL);
if (ret < 0)
int ret;
struct afsi_change_event_data *aced = data;
- switch(event) {
+ if (!current_playlist.name)
+ return 1;
+ switch (event) {
case AFSI_CHANGE:
return playlist_update_audio_file(aced->aft_row);
case AUDIO_FILE_RENAME:
* \param \ra string of the form receiver_name:options
* \param receiver_num contains the number of the receiver upon success
*
- * This function checks whether \a ra starts with the name of a supported
- * paraslash receiver, optionally followed by a colon and any options for that
- * receiver. If a valid receiver name was found the remaining part of \a ra is
- * passed to the receiver's config parser.
+ * This function checks whether \a ra starts with the name of a receiver,
+ * optionally followed by options for that receiver. If a valid receiver name
+ * was found the remaining part of \a ra is passed to the receiver's config
+ * parser.
*
* \return On success, a pointer to the receiver-specific gengetopt args info
* struct is returned and \a receiver_num contains the number of the receiver.
/** \file send.h Sender-related defines and structures. */
-/** The sender subcommands. */
+#define SENDER_SUBCOMMANDS \
+ SENDER_SUBCOMMAND(add) /**< Add a target (udp only). */ \
+ SENDER_SUBCOMMAND(delete) /**< Delete a target (udp only). */ \
+ SENDER_SUBCOMMAND(allow) /**< Allow connections from given IP address(es). */ \
+ SENDER_SUBCOMMAND(deny) /**< Deny connections from given IP address(es). */ \
+ SENDER_SUBCOMMAND(on) /**< Activate the sender. */ \
+ SENDER_SUBCOMMAND(off) /**< Deactivate the sender. */ \
+
+#define SENDER_SUBCOMMAND(_name) SENDER_ ## _name,
enum sender_subcommand {
- SENDER_ADD, /**< Add a target (udp only). */
- SENDER_DELETE, /**< Delete a target (udp only). */
- SENDER_ALLOW, /**< Allow connections from given IP address(es). */
- SENDER_DENY, /**< Deny connections from given IP address(es). */
- SENDER_ON, /**< Activate the sender. */
- SENDER_OFF, /**< Deactivate the sender. */
+ SENDER_SUBCOMMANDS
NUM_SENDER_CMDS /**< Used as array size in struct \ref sender. */
};
+#undef SENDER_SUBCOMMAND
+#define SENDER_SUBCOMMAND(_name) #_name,
/**
* Describes one supported sender of para_server.
version_handle_flag("server", conf.version_given);
if (conf.help_given || conf.detailed_help_given)
print_help_and_die();
+ daemon_set_priority(conf.priority_arg);
daemon_drop_privileges_or_die(conf.user_arg, conf.group_arg);
/* parse config file, open log and set defaults */
parse_config_or_die(0);
#define le_short(s) ((short) (s))
#endif
+/**
+ * Size of the output buffer.
+ *
+ * Valid streams have frame sizes in the range from 160 to 640. To avoid buffer
+ * overflows, we bail out if the decoder reports a value bigger than this.
+ */
#define MAX_FRAME_SIZE 2000
+
/* Copy Ogg packet to Speex bitstream */
static int speexdec_write_frames(int packet_no,
struct private_spxdec_data *psd, int skip_samples,
for (j = 0; j != psd->shi.nframes; j++) {
short output[MAX_FRAME_SIZE], *btr_output;
int skip = skip_samples + psd->lookahead, skip_idx = 0;
- int samples, new_frame_size = psd->shi.frame_size;
+ int samples, this_frame_size,
+ new_frame_size = psd->shi.frame_size;
+
+ if (speex_decoder_ctl(psd->shi.state, SPEEX_GET_FRAME_SIZE,
+ &this_frame_size) == 0) {
+ if (this_frame_size > MAX_FRAME_SIZE)
+ return -E_SPX_DECODE_OVERFLOW;
+ };
if (speex_decode_int(psd->shi.state, &psd->bits, output) < 0)
return -E_SPX_DECODE;
return LL_CRIT;
if (loglevel_equal(txt, "emerg"))
return LL_EMERG;
- return -1;
+ return -E_BAD_LL;
}
static int get_next_word(const char *buf, const char *delim, char **word)
return -ERRNO_TO_PARA_ERROR(errno);
if (num_wchars == 0)
return 0;
- dest = para_malloc(num_wchars * sizeof(*dest));
+ dest = para_malloc((num_wchars + 1) * sizeof(*dest));
src = s;
memset(&state, 0, sizeof(state));
num_wchars = mbsrtowcs(dest, &src, num_wchars, &state);
-RM ?= rm -f
+RM = rm -f
results_dir := $(test_dir)/test-results
trash_dir := $(test_dir)/trashes
let i++
commands[$i]="ls_ogg"
required_objects[$i]='ogg_afh'
-cmdline[$i]="ls -lv ${oggs_base[@]}"
+cmdline[$i]="ls -l=v ${oggs_base[@]}"
good[$i]='^basename:'
let i++
if [[ -n "$result" ]]; then
test_skip 'para_audiod' "missing object(s): $result"
else
- test_expect_success 'para_audiod: recv/filter/writer options' \
- "grep_man '$regex' audiod"
+ test_expect_success 'para_audiod: receivers' \
+ "grep_man 'Options for the http receiver' audiod"
+ test_expect_success 'para_audiod: filters' \
+ "grep_man 'Options for the compress filter' audiod"
+ test_expect_success 'para_audiod: writers' \
+ "grep_man 'Options for the file writer' audiod"
fi
# check various command lists
# para_play is always built
regex='LIST OF COMMANDS.\{100,\}'
test_expect_success 'para_play: play commands' "grep_man '$regex' play"
-regex="$rfw_regex"
test_done
return -ERRNO_TO_PARA_ERROR(errno);
}
-static void udp_init_session(struct sender_client *sc)
-{
- PARA_NOTICE_LOG("sending to udp %s\n", sc->name);
-}
-
static void udp_shutdown_targets(void)
{
struct sender_client *sc, *tmp;
static int udp_com_on(__a_unused struct sender_command_data *scd)
{
- sender_status = SENDER_ON;
+ sender_status = SENDER_on;
return 1;
}
static int udp_com_off(__a_unused struct sender_command_data *scd)
{
udp_shutdown_targets();
- sender_status = SENDER_OFF;
+ sender_status = SENDER_off;
return 1;
}
{
int mps;
- udp_init_session(sc);
+ PARA_NOTICE_LOG("sending to udp %s\n", sc->name);
mps = generic_max_transport_msg_size(sc->fd) - sizeof(struct udphdr);
PARA_INFO_LOG("current MPS = %d bytes\n", mps);
return mps;
{
int ret;
- if (sender_status == SENDER_OFF)
+ if (sender_status == SENDER_off)
return;
if (len == 0)
return;
"status: %s\n"
"port: %s\n"
"targets: %s\n",
- (sender_status == SENDER_ON)? "on" : "off",
+ (sender_status == SENDER_on)? "on" : "off",
stringify_port(conf.udp_default_port_arg, "udp"),
tgts? tgts : "(none)"
);
s->post_select = NULL;
s->shutdown_clients = udp_shutdown_targets;
s->resolve_target = udp_resolve_target;
- s->client_cmds[SENDER_ON] = udp_com_on;
- s->client_cmds[SENDER_OFF] = udp_com_off;
- s->client_cmds[SENDER_DENY] = NULL;
- s->client_cmds[SENDER_ALLOW] = NULL;
- s->client_cmds[SENDER_ADD] = udp_com_add;
- s->client_cmds[SENDER_DELETE] = udp_com_delete;
- sender_status = SENDER_OFF;
+ s->client_cmds[SENDER_on] = udp_com_on;
+ s->client_cmds[SENDER_off] = udp_com_off;
+ s->client_cmds[SENDER_deny] = NULL;
+ s->client_cmds[SENDER_allow] = NULL;
+ s->client_cmds[SENDER_add] = udp_com_add;
+ s->client_cmds[SENDER_delete] = udp_com_delete;
+ sender_status = SENDER_off;
udp_init_target_list();
if (!conf.udp_no_autostart_given)
- sender_status = SENDER_ON;
+ sender_status = SENDER_on;
PARA_DEBUG_LOG("udp sender init complete\n");
}
static int recv_afs_msg(int afs_socket, int *fd, uint32_t *code, uint32_t *data)
{
- char control[255], buf[8];
+ char control[255] __a_aligned(8), buf[8];
struct msghdr msg = {.msg_iov = NULL};
struct cmsghdr *cmsg;
struct iovec iov;
~~~~~~~
Doxygen is a documentation system for various programming
-languages. The paraslash project uses Doxygen for generating the API
-reference on the web pages, but good source code documentation is
-also beneficial to people trying to understand the code structure
-and the interactions between the various source files.
+languages. The API reference on the paraslash web page is generated
+by doxygen.
It is more illustrative to look at the source code for examples than
-to describe the conventions for documenting the source in this manual,
-so we only describe which parts of the code need doxygen comments,
-but leave out details on documentation conventions.
+to describe the conventions in this manual, so we only describe which
+parts of the code need doxygen comments, but leave out details on
+documentation conventions.
As a rule, only the public part of the C source is documented with
Doxygen. This includes structures, defines and enumerations in header
files as well as public (non-static) C functions. These should be
-documented completely. For example each parameter and the return
-value of a public function should get a descriptive comment.
+documented completely. For example, each parameter and the return
+value of a public function should get a descriptive doxygen comment.
No doxygen comments are necessary for static functions and for
structures and enumerations in C files (which are used only within