speex_cppflags := @speex_cppflags@
opus_cppflags := @opus_cppflags@
vorbis_cppflags := @vorbis_cppflags@
-arch_cppflags := @arch_cppflags@
osl_cppflags := @osl_cppflags@
id3tag_cppflags := @id3tag_cppflags@
openssl_cppflags := @openssl_cppflags@
readline_ldflags := @readline_ldflags@
samplerate_ldflags := @samplerate_ldflags@
osl_ldflags := @osl_ldflags@
-socket_ldflags := @socket_ldflags@
-nsl_ldflags := @nsl_ldflags@
curses_ldflags := @curses_ldflags@
core_audio_ldflags := @core_audio_ldflags@
crypto_ldflags := @crypto_ldflags@
uname_rs := $(shell uname -rs)
cc_version := $(shell $(CC) --version | head -n 1)
GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h)
+COPYRIGHT_YEAR := 2016
ifeq ("$(origin O)", "command line")
build_dir := $(O)
tarball_delete := $(addprefix $(tarball_pfx)/, web .gitignore)
tarball := $(tarball_pfx).tar.bz2
-.PHONY: dep all clean clean2 distclean maintainer-clean install man tarball
-all: dep $(prefixed_executables) $(man_pages)
-dep: $(deps)
+.PHONY: all clean clean2 distclean maintainer-clean install man tarball
+all: $(prefixed_executables) $(man_pages)
man: $(man_pages)
tarball: $(tarball)
$(subst z,Z,$1))))))))))))))))))))))))))
CPPFLAGS += -DBINDIR='"$(bindir)"'
+CPPFLAGS += -DCOPYRIGHT_YEAR='"$(COPYRIGHT_YEAR)"'
CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
STRICT_CFLAGS += -Wno-sign-compare -Wno-unknown-pragmas
STRICT_CFLAGS += -Wformat -Wformat-security
STRICT_CFLAGS += -Wmissing-format-attribute
+STRICT_CFLAGS += -Wdeclaration-after-statement
LDFLAGS += $(clock_gettime_ldflags)
: LDFLAGS += \
$(mp4v2_ldflags)
-para_server \
-para_client \
-para_audioc \
-para_audiod \
-para_recv \
-: LDFLAGS += \
- $(socket_ldflags) $(nsl_ldflags)
-
para_afh para_recv para_server para_play: LDFLAGS += $(iconv_ldflags)
$(foreach exe,$(executables),$(eval para_$(exe): $$($(exe)_objs)))
*/
int main(int argc, char **argv)
{
- int i, ret, audio_format_num, fd;
+ int i, ret = 0, audio_format_num, fd;
void *audio_file_data;
size_t audio_file_size;
struct afh_info afhi;
afh_cmdline_parser(argc, argv, &conf);
loglevel = get_loglevel_by_name(conf.loglevel_arg);
version_handle_flag("afh", conf.version_given);
- if (conf.help_given || conf.detailed_help_given)
+ if (conf.help_given || conf.detailed_help_given || conf.inputs_num == 0)
print_help_and_die();
afh_init();
- ret = -E_AFH_SYNTAX;
- if (conf.inputs_num == 0)
- goto out;
for (i = 0; i < conf.inputs_num; i++) {
int ret2;
ret = mmap_full_file(conf.inputs[i], O_RDONLY, &audio_file_data,
return -E_AUDIO_FORMAT;
}
+/**
+ * Get the name of the given audio format.
+ *
+ * \param i The audio format number.
+ *
+ * \return This returns a pointer to statically allocated memory so it
+ * must not be freed by the caller.
+ */
+const char *audio_format_name(int i)
+{
+ if (i < 0 || i >= ARRAY_SIZE(afl) - 1)
+ return "???";
+ return afl[i].name;
+}
+
+static int get_file_info(int format, const char *path, char *data,
+ size_t size, int fd, struct afh_info *afhi)
+{
+ int ret;
+ const char *fmt = audio_format_name(format);
+
+ memset(afhi, 0, sizeof(*afhi));
+ ret = afl[format].get_file_info(data, size, fd, afhi);
+ if (ret < 0) {
+ PARA_WARNING_LOG("%s: %s format not detected: %s\n",
+ path, fmt, para_strerror(-ret));
+ return ret;
+ }
+ PARA_NOTICE_LOG("%s: detected %s format\n", path, fmt);
+ return format;
+}
+
/**
* Call get_file_info() to obtain an afhi structure.
*
{
int ret, i, format;
- afhi->header_len = 0;
- afhi->techinfo = NULL;
- afhi->tags.artist = NULL;
- afhi->tags.title = NULL;
- afhi->tags.year = NULL;
- afhi->tags.album = NULL;
- afhi->tags.comment = NULL;
format = guess_audio_format(path);
-
if (format >= 0) {
- ret = afl[format].get_file_info(data, size, fd, afhi);
- if (ret >= 0) {
- ret = format;
+ ret = get_file_info(format, path, data, size, fd, afhi);
+ if (ret >= 0)
goto success;
- }
}
FOR_EACH_AUDIO_FORMAT(i) {
if (i == format) /* we already tried this one to no avail */
continue;
- ret = afl[i].get_file_info(data, size, fd, afhi);
- if (ret >= 0) {
- ret = i;
+ ret = get_file_info(i, path, data, size, fd, afhi);
+ if (ret >= 0)
goto success;
- }
- PARA_WARNING_LOG("%s\n", para_strerror(-ret));
}
return -E_AUDIO_FORMAT;
success:
free(afhi->tags.comment);
}
-/**
- * Get the name of the given audio format.
- *
- * \param i The audio format number.
- *
- * \return This returns a pointer to statically allocated memory so it
- * must not be freed by the caller.
- */
-const char *audio_format_name(int i)
-{
- if (i < 0 || i >= ARRAY_SIZE(afl) - 1)
- return "???";
- return afl[i].name;
-}
-
static inline size_t get_chunk_len(long unsigned chunk_num,
const struct afh_info *afhi)
{
if (ret >= 0)
goto out;
/* ignore subsequent errors (but log them) */
- para_printf(&aca->pbout, "could not activate %s: %s\n"
- "switching back to %s\n",
- arg, para_strerror(-ret), current_mop? current_mop : "dummy");
- ret = activate_mood_or_playlist(current_mop, &num_admissible);
- if (ret >= 0)
- goto out;
- para_printf(&aca->pbout, "could not activate %s: %s\nswitching to dummy\n",
- current_mop, para_strerror(-ret));
+ para_printf(&aca->pbout, "could not activate %s\n", arg);
+ if (current_mop) {
+ int ret2;
+ para_printf(&aca->pbout, "switching back to %s\n", current_mop);
+ ret2 = activate_mood_or_playlist(current_mop, &num_admissible);
+ if (ret2 >= 0)
+ goto out;
+ para_printf(&aca->pbout, "could not reactivate %s: %s\n",
+ current_mop, para_strerror(-ret2));
+ }
+ para_printf(&aca->pbout, "activating dummy mood\n");
activate_mood_or_playlist(NULL, &num_admissible);
out:
para_printf(&aca->pbout, "activated %s (%d admissible files)\n",
ret = create_local_socket(socket_name, 0);
if (ret < 0) {
ret = create_local_socket(socket_name,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IWOTH);
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IROTH);
if (ret < 0) {
PARA_EMERG_LOG("%s: %s\n", para_strerror(-ret),
socket_name);
* the gengetopt args_info struct that holds information on all command line
* arguments
*/
-struct audiod_args_info conf;
+static struct audiod_args_info conf;
static char *socket_name;
static struct audio_format_info afi[NUM_AUDIO_FORMATS];
EMBRACE(.name = f->name, .parent = parent,
.handler = f->execute, .context = fn));
- f->open(fn);
+ if (f->open)
+ f->open(fn);
sprintf(buf, "%s (slot %d)", f->name, (int)(s - slot));
fn->task = task_register(&(struct task_info) {
.name = buf,
unlink(socket_name);
ct->fd[0] = create_local_socket(socket_name, 0);
ct->fd[1] = create_local_socket(socket_name,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IWOTH);
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (ct->fd[0] >= 0 || ct->fd[1] >= 0)
return;
PARA_EMERG_LOG("%s\n", para_strerror(-ct->fd[1]));
for (i = 0; i < 2; i++) {
if (ct->fd[i] < 0)
continue;
- ret = handle_connect(ct->fd[i], &s->rfds, uid_whitelist);
+ ret = handle_connect(ct->fd[i], &s->rfds);
if (ret < 0) {
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
if (ret == -E_AUDIOD_TERM) {
exit(0);
}
+/**
+ * Lookup the given UID in the whitelist.
+ *
+ * The whitelist is the array of arguments to the --user-allow opion. If the
+ * option was not given, the array is empty, in which case the check succeeds.
+ *
+ * \param uid User ID to look up.
+ *
+ * \return True if --user-allow was not given, or if uid matches an element of
+ * the whitelist.
+ */
+bool uid_is_whitelisted(uid_t uid)
+{
+ int i;
+
+ if (!conf.user_allow_given)
+ return true;
+ for (i = 0; i < conf.user_allow_given; i++)
+ if (uid == uid_whitelist[i])
+ return true;
+ return false;
+}
+
/**
* the main function of para_audiod
*
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,
- conf.logfile_given, conf.log_color_arg, conf.log_color_given);
+ if (daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no,
+ conf.logfile_given)) {
+ for (i = 0; i < conf.log_color_given; i++)
+ daemon_set_log_color_or_die(conf.log_color_arg[i]);
+ }
init_random_seed_or_die();
daemon_set_flag(DF_LOG_TIME);
daemon_set_flag(DF_LOG_HOSTNAME);
*/
enum audiod_status_info {AUDIOD_OFF, AUDIOD_ON, AUDIOD_STANDBY};
-extern struct audiod_args_info conf;
extern int audiod_status;
-int handle_connect(int accept_fd, fd_set *rfds, uid_t *uid_whitelist);
-void audiod_status_dump(bool force);
+/* defined in audiod.c */
+struct btr_node *audiod_get_btr_root(void);
__malloc char *audiod_get_decoder_flags(void);
+void clear_and_dump_items(void);
char *get_time_string(void);
-struct btr_node *audiod_get_btr_root(void);
+bool uid_is_whitelisted(uid_t uid);
-void stat_client_write_item(int item_num);
-void clear_and_dump_items(void);
+/* defined in audiod_command.c */
+void audiod_status_dump(bool force);
void close_stat_clients(void);
+int handle_connect(int accept_fd, fd_set *rfds);
+void stat_client_write_item(int item_num);
return ret < 0? ret : 0;
}
-static int check_perms(uid_t uid, uid_t *whitelist)
-{
- int i;
-
- if (!conf.user_allow_given)
- return 1;
- for (i = 0; i < conf.user_allow_given; i++)
- if (uid == whitelist[i])
- return 1;
- return -E_UCRED_PERM;
-}
-
/**
* Handle arriving connections on the local socket.
*
* \param accept_fd The fd to accept connections on.
* \param rfds If \a accept_fd is not set in \a rfds, do nothing.
- * \param uid_whitelist Array of UIDs which are allowed to connect.
*
* This is called in each iteration of the select loop. If there is an incoming
* connection on \a accept_fd, this function reads the command sent by the peer,
*
* \sa para_accept(), recv_cred_buffer()
* */
-int handle_connect(int accept_fd, fd_set *rfds, uid_t *uid_whitelist)
+int handle_connect(int accept_fd, fd_set *rfds)
{
int i, argc, ret, clifd;
char buf[MAXLINE], **argv = NULL;
if (ret < 0)
goto out;
uid = ret;
- PARA_INFO_LOG("connection from user %i, buf: %s\n", ret, buf);
- ret = check_perms(uid, uid_whitelist);
- if (ret < 0)
+ PARA_INFO_LOG("connection from UID %d, buf: %s\n", ret, buf);
+ ret = -E_UCRED_PERM;
+ if (!uid_is_whitelisted(uid))
goto out;
ret = create_argv(buf, "\n", &argv);
if (ret <= 0)
;;
esac
AC_SUBST(crypto_ldflags)
-########################################################################### libsocket
-AC_CHECK_LIB([c], [socket],
- [socket_ldlflags=],
- [socket_ldflags="-lsocket"]
-)
-AC_SUBST(socket_ldflags)
########################################################################## iconv
STASH_FLAGS
LIBS=
AC_DEFINE_UNQUOTED(ICONV_CAST, $cast, [cast for second arg to iconv()])
AC_MSG_RESULT($msg)
UNSTASH_FLAGS
-########################################################################### libnsl
-AC_CHECK_LIB([c], [gethostbyname],
- [nsl_ldflags=],
- [nsl_ldflags="-lnsl"]
-)
-AC_SUBST(nsl_ldflags)
########################################################################### ucred
AC_MSG_CHECKING(for struct ucred)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
color_parse_or_die(default_log_colors[i], me->log_colors[i]);
}
-/*
+/**
* Set the color for one loglevel.
*
- * arg must be of the form "ll:[fg [bg]] [attr]".
+ * \param arg Must be of the form "ll:[fg [bg]] [attr]".
*/
-static void daemon_set_log_color_or_die(char const *arg)
+void daemon_set_log_color_or_die(const char *arg)
{
char *p = strchr(arg, ':');
int ret, ll;
* \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.
- * \param log_color_argv Color specifiers given to --log-color.
- * \param argc Number of color specifiers in \a log_color_argv.
*
- * If \a color_arg equals \a color_arg_no, color mode is disabled, i.e., calls
- * to \a para_log() will not produce colored output. If \a color_arg == \a
- * color_arg_auto, the function autodetects whether to activate colors.
- * Otherwise color mode is enabled.
+ * 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().
*
- * If color mode is to be enabled, the strings in \a log_color_argv are parsed
- * and the color scheme is updated accordingly. For each loglevel, the default
- * colors apply if there is no log_color_argv for this loglevel.
+ * \return Whether colors have been enabled by the function.
*/
-void daemon_init_colors_or_die(int color_arg, int color_arg_auto,
- int color_arg_no, bool logfile_given, char **log_color_argv,
- int argc)
+bool daemon_init_colors_or_die(int color_arg, int color_arg_auto,
+ int color_arg_no, bool logfile_given)
{
- int i;
-
if (color_arg == color_arg_no)
- return;
+ return false;
if (color_arg == color_arg_auto) {
if (logfile_given)
- return;
+ return false;
if (!isatty(STDERR_FILENO))
- return;
+ return false;
}
daemon_set_flag(DF_COLOR_LOG);
daemon_set_default_log_colors();
- for (i = 0; i < argc; i++)
- daemon_set_log_color_or_die(log_color_argv[i]);
+ return true;
}
/**
void daemon_set_logfile(const char *logfile_name);
void daemon_set_flag(unsigned flag);
void daemon_set_loglevel(const char *loglevel);
-void daemon_init_colors_or_die(int color_arg, int color_arg_auto,
- int color_arg_no, bool logfile_given, char **log_color_argv,
- int argc);
+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);
__printf_2_3 void daemon_log(int ll, const char* fmt,...);
/** Daemon log configuration flags. */
#define AFH_RECV_ERRORS \
- PARA_ERROR(AFH_RECV_BAD_FILENAME, "invalid file name"), \
+ PARA_ERROR(AFH_RECV_BAD_FILENAME, "no file name given"), \
#define OGG_AFH_COMMON_ERRORS \
ti.pre_select = f->pre_select;
ti.post_select = f->post_select;
ti.context = fn;
- f->open(fn);
+ if (f->open)
+ f->open(fn);
fn->task = task_register(&ti, &s);
parent = fn->btrn;
}
/**
* Open one instance of this filter.
*
- * This should allocate the output buffer of the given filter node and do any
- * other filter-specific preparations like initializing the private_data member
- * of \a fn suitably. The open function is assumed to succeed.
+ * This should allocate the output buffer of the given filter node and
+ * do any other filter-specific preparations like initializing the
+ * private_data member of \a fn suitably. The open function is
+ * optional, If it is provided, it is assumed to succeed.
*/
void (*open)(struct filter_node *fn);
/**
* Close one instance of this filter.
*
- * Free all resources of associated with \a fn that were previously allocated
- * by the open() function. It's OK to leave this alone if the filter does not
- * need any cleanups.
+ * Free all resources associated with \a fn that were previously
+ * allocated by the open() function. It's OK to set this to NULL if the
+ * filter does not need to perform any cleanup operation.
*/
void (*close)(struct filter_node *fn);
/**
};
void filter_init(void);
-int check_filter_arg(char *filter_arg, void **conf);
+int check_filter_arg(const char *fa, void **conf);
void print_filter_helps(unsigned flags);
void generic_filter_pre_select(struct sched *s, void *context);
int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels,
* If the filter has a command line parser and options is not NULL, run it.
* Returns filter_num on success, negative on errors
*/
-static int parse_filter_args(int filter_num, char *options, void **conf)
+static int parse_filter_args(int filter_num, const char *options, void **conf)
{
const struct filter *f = filter_get(filter_num);
int ret, argc;
/**
* Check the filter command line options.
*
- * \param fa The command line options.
+ * \param fa The filter argument.
* \param conf Points to the filter configuration upon successful return.
*
- * Check if \a fa starts with a the name of a supported filter, followed by
- * a colon. If yes, call the command line parser of that filter.
+ * Check if the given filter argument starts with the name of a supported
+ * filter, optionally followed by options for this filter. If yes, call the
+ * command line parser of that filter.
*
* \return On success, the number of the filter is returned and \a conf
* is initialized to point to the filter configuration determined by \a fa.
*
* \sa filter::parse_config
*/
-int check_filter_arg(char *fa, void **conf)
+int check_filter_arg(const char *fa, void **conf)
{
int j;
ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
if (ret < 0)
- return ret;
+ goto out;
ret = -E_KEY_MARKER;
if (strncmp(map, header_str, strlen(header_str)))
goto unmap;
free(blob);
blob = NULL;
}
+out:
*result = blob;
return ret;
}
gcry_sexp_t in, out, priv_key;
size_t nbytes;
+ ret = check_key_file(key_file, true);
+ if (ret < 0)
+ return ret;
PARA_INFO_LOG("decrypting %d byte input\n", inlen);
/* key_file -> asymmetric key priv */
ret = get_private_key(key_file, &priv);
#include "mm.h"
#include "sideband.h"
#include "mood.h"
+#include "sched.h"
/**
* Contains statistical data of the currently admissible audio files.
static void log_statistics(void)
{
unsigned n = statistics.num;
+ int mean_days, sigma_days;
+ /*
+ * We can not use the "now" pointer from sched.c here because we are
+ * called before schedule(), which initializes "now".
+ */
+ struct timeval rnow;
+ assert(current_mood);
+ PARA_NOTICE_LOG("loaded mood %s\n", current_mood->name?
+ current_mood->name : "(dummy)");
if (!n) {
- PARA_NOTICE_LOG("no admissible files\n");
+ PARA_WARNING_LOG("no admissible files\n");
return;
}
- PARA_INFO_LOG("last_played mean: %lli, last_played sigma: %llu\n",
- (long long int)(statistics.last_played_sum / n),
- (long long unsigned)int_sqrt(statistics.last_played_qd / n));
- PARA_INFO_LOG("num_played mean: %lli, num_played sigma: %llu\n",
- (long long int)statistics.num_played_sum / n,
+ PARA_NOTICE_LOG("%u admissible files\n", statistics.num);
+ clock_get_realtime(&rnow);
+ mean_days = (rnow.tv_sec - statistics.last_played_sum / n) / 3600 / 24;
+ sigma_days = int_sqrt(statistics.last_played_qd / n) / 3600 / 24;
+ PARA_NOTICE_LOG("last_played mean/sigma: %d/%d days\n", mean_days, sigma_days);
+ PARA_NOTICE_LOG("num_played mean/sigma: %llu/%llu\n",
+ (long long unsigned)statistics.num_played_sum / n,
(long long unsigned)int_sqrt(statistics.num_played_qd / n));
}
ret = audio_file_loop(&aa, add_if_admissible);
if (ret < 0)
return ret;
- log_statistics();
- PARA_INFO_LOG("%d admissible files\n", statistics.num);
for (i = 0; i < statistics.num; i++) {
struct admissible_file_info *a = aa.array + i;
ret = add_to_score_table(a->aft_row, a->score);
if (ret < 0)
goto out;
}
- PARA_NOTICE_LOG("loaded mood %s\n", mood_name? mood_name : "(dummy)");
+ log_statistics();
ret = statistics.num;
out:
free(aa.array);
header_mode(&header), tag_versions[ret]);
return 1;
err_out:
- PARA_ERROR_LOG("%s\n", para_strerror(-ret));
free(afhi->chunk_table);
return ret;
}
}
if (*o == ':')
- if (para_atoi32(++o, port) < 0 ||
- *port < 0 || *port > 0xffff)
+ if (para_atoi32(++o, port) < 0 || *port < 0 || *port > 0xffff)
goto failed;
if (host_string_ok(host))
return host;
/**
* Stringify port number, resolve into service name where defined.
+ *
* \param port 2-byte port number, in host-byte-order.
* \param transport Transport protocol name (e.g. "udp", "tcp"), or NULL.
* \return Pointer to static result buffer.
pt->fn.btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.name = decoder->name, .parent = pt->rn.btrn,
.handler = decoder->execute, .context = &pt->fn));
- decoder->open(&pt->fn);
+ if (decoder->open)
+ decoder->open(&pt->fn);
/* setup default writer */
pt->wn.conf = check_writer_arg_or_die(NULL, &pt->wn.writer_num);
daemon_open_log_or_die();
}
- daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no,
- conf.logfile_given, conf.log_color_arg, conf.log_color_given);
+ if (daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no,
+ conf.logfile_given)) {
+ int i;
+ for (i = 0; i < conf.log_color_given; i++)
+ daemon_set_log_color_or_die(conf.log_color_arg[i]);
+ }
daemon_set_flag(DF_LOG_PID);
daemon_set_flag(DF_LOG_LL);
daemon_set_flag(DF_LOG_TIME);
* Split a buffer into words.
*
* This parser honors single and double quotes, backslash-escaped characters
- * and special characters like \p \\n. The result contains pointers to copies
- * of the words contained in \a buf and has to be freed by using \ref
- * free_argv().
+ * and special characters like \\n. The result contains pointers to copies of
+ * the words contained in buf and has to be freed by using \ref free_argv().
*
* \param buf The buffer to be split.
* \param delim Each character in this string is treated as a separator.
* \param result The array of words is returned here.
*
- * \return Number of words in \a buf, negative on errors.
+ * It's OK to pass NULL as the buffer argument. This is equivalent to passing
+ * the empty string.
+ *
+ * \return Number of words in buf, negative on errors. The array returned
+ * through the result pointer is NULL terminated.
*/
int create_argv(const char *buf, const char *delim, char ***result)
{
--user-list "$user_list" \
--http-port "$stream_port" \
--dccp-port "$stream_port"
+ (($? != 0)) && exit 1
fi
for ((i=0; i < ${#commands[@]}; i++)); do
test_duration()
{
local t=$(exec 2>&1 1>/dev/null; time -p "$@")
- result=$(awk '{print $2 * 1000}' <<< $t)
+ result=$(awk '{print $2 * 1000; exit 0}' <<< "$t")
}
test_expect_success()
static char buf[512];
snprintf(buf, sizeof(buf) - 1, "%s\n"
- "Copyright (C) 2002-2016 Andre Noll\n"
+ "Copyright (C) " COPYRIGHT_YEAR " Andre Noll\n"
"This is free software with ABSOLUTELY NO WARRANTY."
" See COPYING for details.\n"
"Report bugs to <maan@tuebingen.mpg.de>.\n"