From: Andre Noll Date: Sun, 15 Mar 2020 14:51:20 +0000 (+0100) Subject: Merge branch 'refs/heads/t/mixer' X-Git-Tag: v0.6.3~24 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=e2e1adca824585eeecd091f54955e51212927533;hp=ac289a22fd438a97d5eda86793a47377d0d570ce Merge branch 'refs/heads/t/mixer' A single patch for para_mixer which makes the subcommands which sleep a bit more robust. The merge conflicted due to commit d6b25bf854c1 (mixer: fade: Handle empty mood strings gracefully) from half a year ago, but the resolution was obvious. Cooking for nine months. * refs/heads/t/mixer: mixer: sleep/snooze: Close mixer during sleep. --- diff --git a/Makefile.real b/Makefile.real index 5df27bad..7d4eff4f 100644 --- a/Makefile.real +++ b/Makefile.real @@ -6,6 +6,8 @@ MAKEFLAGS += -Rr ifeq ("$(origin CC)", "default") CC := cc endif +.ONESHELL: +.SHELLFLAGS := -ec LOGLEVELS := LL_DEBUG,LL_INFO,LL_NOTICE,LL_WARNING,LL_ERROR,LL_CRIT,LL_EMERG vardir := /var/paraslash @@ -102,7 +104,7 @@ endif $(object_dir) $(man_dir) $(dep_dir) $(m4depdir) $(lls_suite_dir) \ $(yy_build_dir): - $(Q) $(MKDIR_P) $@ + @$(MKDIR_P) $@ CPPFLAGS += -DBINDIR='"$(bindir)"' CPPFLAGS += -DCOPYRIGHT_YEAR='"$(COPYRIGHT_YEAR)"' @@ -148,9 +150,9 @@ STRICT_CFLAGS += $(call cc-option, -Wdiscarded-qualifiers) # To put more focus on warnings, be less verbose as default # Use 'make V=1' to see the full commands ifeq ("$(origin V)", "command line") - Q := + SAY = else - Q := @ + SAY = @echo '$(strip $(1))' endif audiod_commands := $(addprefix $(lls_suite_dir)/, \ @@ -177,9 +179,9 @@ $(man_dir)/para_write.1: all_commands := $(write_commands) $(man_dir)/para_%.1: $(lls_suite_dir)/%.lsg.man \ $(lls_m4_dir)/copyright.m4 | $(man_dir) - @[ -z "$(Q)" ] || echo 'LLSMAN $<' - $(Q) cat $< $(all_commands) > $@ - $(Q) $(M4) -D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) $(lls_m4_dir)/copyright.m4 >> $@ + $(call SAY, LLSMAN $<) + cat $< $(all_commands) > $@ + $(M4) -D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) $(lls_m4_dir)/copyright.m4 >> $@ $(object_dir)/%.o: %.c | $(object_dir) @@ -247,8 +249,8 @@ $(object_dir)/mm.o \ $(object_dir)/compress_filter.o: CFLAGS += -O3 $(object_dir)/%.o: %.c | $(object_dir) $(dep_dir) $(lsg_h) $(yy_h) - @[ -z "$(Q)" ] || echo 'CC $<' - $(Q) $(CC) -c -o $@ -MMD -MF $(dep_dir)/$(*F).d -MT $@ $(CPPFLAGS) \ + $(call SAY, CC $<) + $(CC) -c -o $@ -MMD -MF $(dep_dir)/$(*F).d -MT $@ $(CPPFLAGS) \ $(STRICT_CFLAGS) $(CFLAGS) $< para_recv para_afh para_play para_server: LDFLAGS += $(id3tag_ldflags) @@ -313,24 +315,24 @@ para_afh para_recv para_server para_play: LDFLAGS += $(iconv_ldflags) $(foreach exe,$(executables),$(eval para_$(exe): $$($(exe)_objs))) $(prefixed_executables): - @[ -z "$(Q)" ] || echo 'LD $@' - $(Q) $(CC) $^ -o $@ $(LDFLAGS) + $(call SAY, LD $@) + $(CC) $^ -o $@ $(LDFLAGS) mostlyclean: - @[ -z "$(Q)" ] || echo 'MOSTLYCLEAN' - $(Q) rm -f para_* - $(Q) rm -rf $(object_dir) + $(call SAY, MOSTLYCLEAN) + rm -f para_* + rm -rf $(object_dir) clean: mostlyclean - @[ -z "$(Q)" ] || echo 'CLEAN' - $(Q) rm -rf $(build_dir) + $(call SAY, CLEAN) + rm -rf $(build_dir) distclean: clean - @[ -z "$(Q)" ] || echo 'DISTCLEAN' - $(Q) rm -f Makefile autoscan.log config.status config.log - $(Q) rm -f config.h configure config.h.in + $(call SAY, DISTCLEAN) + rm -f Makefile autoscan.log config.status config.log + rm -f config.h configure config.h.in maintainer-clean: distclean - @[ -z "$(Q)" ] || echo 'MAINTAINER-CLEAN' - $(Q) rm -f *.tar.bz2 *.tar.xz - $(Q) rm -f GPATH GRTAGS GSYMS GTAGS + $(call SAY, MAINTAINER-CLEAN) + rm -f *.tar.bz2 *.tar.xz + rm -f GPATH GRTAGS GSYMS GTAGS INSTALL ?= install INSTALL_PROGRAM ?= $(INSTALL) @@ -346,14 +348,15 @@ install install-strip: all man $(MKDIR_P) $(DESTDIR)$(vardir) >/dev/null 2>&1 || true # not fatal, so don't complain $(tarball) dist tarball: - $(Q) rm -rf $(tarball) $(tarball_pfx) - $(Q) git archive --format=tar --prefix=$(tarball_pfx)/ HEAD \ - | tar --delete $(tarball_delete) > $(tarball_pfx).tar - $(Q) $(MKDIR_P) $(tarball_pfx) - $(Q) ./GIT-VERSION-GEN > $(tarball_pfx)/VERSION - $(Q) cp $(autocrap) $(tarball_pfx) - $(Q) tar rf $(tarball_pfx).tar $(tarball_pfx)/* - $(Q) xz -9 $(tarball_pfx).tar - $(Q) ls -l $(tarball) - $(Q) ln -sf $(tarball) paraslash-git.tar.xz - $(Q) rm -rf $(tarball_pfx) + $(call SAY, DIST) + rm -rf $(tarball) $(tarball_pfx) + git archive --format=tar --prefix=$(tarball_pfx)/ HEAD \ + | tar --delete $(tarball_delete) > $(tarball_pfx).tar + $(MKDIR_P) $(tarball_pfx) + ./GIT-VERSION-GEN > $(tarball_pfx)/VERSION + cp $(autocrap) $(tarball_pfx) + tar rf $(tarball_pfx).tar $(tarball_pfx)/* + xz -9 $(tarball_pfx).tar + ls -l $(tarball) + ln -sf $(tarball) paraslash-git.tar.xz + rm -rf $(tarball_pfx) diff --git a/NEWS.md b/NEWS.md index 55ef3be9..15496bd2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,32 @@ NEWS ==== +---------------------------------------------- +0.6.3 (to be announced) "generalized activity" +---------------------------------------------- + +- The ff command now accepts a negative argument to instruct the + virtual streaming system to jump backwards in the current audio + stream. The old syntax (e.g., "ff 30-") is still supported but it + is deprecated and no longer documented. The compatibility code is + sheduled for removal after 0.7.0. +- para_afh: New option: --preserve to reset the modification time to + the value of the original file after meta data modification. +- Overhaul of the compress filter code. The refined algorithm should + reduce clipping. The meaning of --aggressiveness has changed, see the + updated and extended documentation of the compress filter for details. +- Cleanup of the audio format handler code. +- We now build the tree using the .ONESHELL feature of GNU make, + which results in a significant speedup. +- Two robustness fixes for FreeBSD. +- para_client now supports RFC4716 private keys as generated with + ssh-keygen -m RFC4716. In fact, this key format has been made the + default, and the former PEM keys will be depreciated at some point. +- The ogg audio format handlers learned to detect holes and now report + the correct duration also if ogg pages are missing in the file. This + affects ogg/vorbis ogg/speex and ogg/opus. +- Robustness improvements for para_mixer. + -------------------------------------- 0.6.2 (2018-06-30) "elastic diversity" -------------------------------------- diff --git a/aac_afh.c b/aac_afh.c index 3315a42d..7f2a22a2 100644 --- a/aac_afh.c +++ b/aac_afh.c @@ -333,17 +333,17 @@ close: } static const char * const aac_suffixes[] = {"m4a", "mp4", NULL}; + /** - * the init function of the aac audio format handler + * The audio format handler for the Advanced Audio Codec. * - * \param afh pointer to the struct to initialize + * This is only compiled in if the faad library is installed. */ -void aac_afh_init(struct audio_format_handler *afh) -{ - afh->get_file_info = aac_get_file_info, - afh->suffixes = aac_suffixes; - afh->rewrite_tags = aac_afh_rewrite_tags; - afh->open = aac_afh_open; - afh->get_chunk = aac_afh_get_chunk; - afh->close = aac_afh_close; -} +const struct audio_format_handler aac_afh = { + .get_file_info = aac_get_file_info, + .suffixes = aac_suffixes, + .rewrite_tags = aac_afh_rewrite_tags, + .open = aac_afh_open, + .get_chunk = aac_afh_get_chunk, + .close = aac_afh_close, +}; diff --git a/afh.c b/afh.c index aa6570e1..567b560a 100644 --- a/afh.c +++ b/afh.c @@ -113,6 +113,24 @@ static int rewrite_tags(const char *name, int input_fd, void *map, goto out; } ret = xrename(tmp_name, name); + if (ret < 0) + goto out; + if (OPT_GIVEN(PRESERVE)) { + struct timespec times[2]; /* [0]: atime, [1]: mtime */ + times[0].tv_nsec = UTIME_OMIT; + times[1] = sb.st_mtim; + /* + * We might well have written a file of identical size. If we + * keep the mtime as well, we might fool backup applications + * like rsync which skip files whose size and mtime haven't + * changed. So we change the mtime slightly. + */ + times[1].tv_sec++; + if (futimens(output_fd, times) < 0) { + ret = -ERRNO_TO_PARA_ERROR(errno); + goto out; + } + } out: if (ret < 0 && output_fd >= 0) unlink(tmp_name); /* ignore errors */ @@ -203,7 +221,6 @@ int main(int argc, char **argv) loglevel = OPT_UINT32_VAL(LOGLEVEL); version_handle_flag("afh", OPT_GIVEN(VERSION)); handle_help_flags(); - afh_init(); for (i = 0; i < lls_num_inputs(lpr); i++) { int ret2; const char *path = lls_input(i, lpr); diff --git a/afh.h b/afh.h index d2ac93ae..881db3c2 100644 --- a/afh.h +++ b/afh.h @@ -80,14 +80,6 @@ struct audio_file_data { * in the other part of this struct. */ struct audio_format_handler { - /** Name of the audio format. */ - const char *name; - /** - * Pointer to the audio format handler's init function. - * - * Must initialize all function pointers and is assumed to succeed. - */ - void (*init)(struct audio_format_handler*); /** Typical file endings for files that can be handled by this afh. */ const char * const *suffixes; /** @@ -135,7 +127,6 @@ struct audio_format_handler { int output_fd, const char *filename); }; -void afh_init(void); int guess_audio_format(const char *name); int compute_afhi(const char *path, char *data, size_t size, int fd, struct afh_info *afhi); diff --git a/afh_common.c b/afh_common.c index bab4d415..a267f58b 100644 --- a/afh_common.c +++ b/afh_common.c @@ -11,108 +11,69 @@ #include "string.h" #include "afh.h" -typedef void afh_init_func(struct audio_format_handler *); - -/* - * Declaration of the audio format handler init functions. - * - * These symbols are referenced in the afl array below. - * - * Most audio format handlers depend on an external library and are not - * compiled in if the library is not installed. Hence it is well possible that - * not all of these functions are defined. It does not hurt to declare them - * anyway, and this avoids another set of ifdefs. - */ -extern afh_init_func mp3_afh_init, ogg_afh_init, aac_afh_init, wma_afh_init, - spx_afh_init, flac_afh_init, opus_afh_init; - /** The list of all status items */ const char *status_item_list[] = {STATUS_ITEMS}; /** - * The list of supported audio formats. + * For each audio file the number of its audio format is stored in the + * database. Therefore this list, in particular its order, is part of the ABI. + * So it's only OK to append new audio formats. All audio formats are listed + * here, regardless of whether the audio format handler is compiled in. + */ +#define ALL_AUDIO_FORMATS \ + AUDIO_FORMAT(mp3) \ + AUDIO_FORMAT(ogg) \ + AUDIO_FORMAT(aac) \ + AUDIO_FORMAT(wma) \ + AUDIO_FORMAT(spx) \ + AUDIO_FORMAT(flac) \ + AUDIO_FORMAT(opus) \ + +/** \cond audio_format_handler */ +#define AUDIO_FORMAT(_fmt) #_fmt, +static const char * const audio_format_names[] = {ALL_AUDIO_FORMATS}; +#undef AUDIO_FORMAT +/* Weak declarations must be public. */ +#define AUDIO_FORMAT(_fmt) \ + struct audio_format_handler _fmt ## _afh __attribute__ ((weak)) \ + = {.get_file_info = NULL}; +ALL_AUDIO_FORMATS +#undef AUDIO_FORMAT +#define AUDIO_FORMAT(_fmt) & _fmt ## _afh, +static struct audio_format_handler *afl[] = {ALL_AUDIO_FORMATS}; +#undef AUDIO_FORMAT +#define NUM_AUDIO_FORMATS (ARRAY_SIZE(afl)) +/** \endcond audio_format_handler */ + +/** + * Get the name of the given audio format. * - * We always define the full array of audio formats even if some audio formats - * were not compiled in. This is because for each audio file the number of its - * audio format is stored in the database. We don't want these numbers to become - * stale just because the user installed a new version of paraslash that - * supports a different set of audio formats. + * \param i The audio format number. * - * It can still be easily detected whether an audio format is compiled in by - * checking if the init function pointer is not \p NULL. + * \return This returns a pointer to statically allocated memory so it + * must not be freed by the caller. */ -static struct audio_format_handler afl[] = { - { - .name = "mp3", - .init = mp3_afh_init, - }, - { - .name = "ogg", -#if defined(HAVE_OGG) && defined(HAVE_VORBIS) - .init = ogg_afh_init, -#endif - }, - { - .name = "aac", -#if defined(HAVE_FAAD) - .init = aac_afh_init, -#endif - }, - { - .name = "wma", - .init = wma_afh_init, - }, - { - .name = "spx", -#if defined(HAVE_OGG) && defined(HAVE_SPEEX) - .init = spx_afh_init, -#endif - }, - { - .name = "flac", -#if defined(HAVE_OGG) && defined(HAVE_FLAC) - .init = flac_afh_init, -#endif - }, - { - .name = "opus", -#if defined(HAVE_OGG) && defined(HAVE_OPUS) - .init = opus_afh_init, -#endif - }, - { - .name = NULL, - } -}; +const char *audio_format_name(int i) +{ + if (i < 0 || i >= NUM_AUDIO_FORMATS) + return "???"; + return audio_format_names[i]; +} static inline int next_audio_format(int format) { for (;;) { - if (!afl[format].name) - return format; format++; - if (afl[format].init) + if (format >= NUM_AUDIO_FORMATS) + return format; + if (afl[format]->get_file_info) return format; } } /** Iterate over each supported audio format. */ -#define FOR_EACH_AUDIO_FORMAT(i) for (i = 0; afl[i].name; i = next_audio_format(i)) - -/** - * Call the init function of each supported audio format handler. - */ -void afh_init(void) -{ - int i; - - PARA_NOTICE_LOG("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS); - FOR_EACH_AUDIO_FORMAT(i) { - PARA_INFO_LOG("initializing %s handler\n", - audio_format_name(i)); - afl[i].init(&afl[i]); - } -} +#define FOR_EACH_AUDIO_FORMAT(i) \ + for (i = 0; i < NUM_AUDIO_FORMATS; i = next_audio_format(i)) /** * Tell whether an audio format handler provides chunk tables. @@ -127,7 +88,7 @@ void afh_init(void) */ bool afh_supports_dynamic_chunks(int audio_format_id) { - return afl[audio_format_id].get_chunk; + return afl[audio_format_id]->get_chunk; } /** @@ -144,8 +105,8 @@ int guess_audio_format(const char *name) int i,j, len = strlen(name); FOR_EACH_AUDIO_FORMAT(i) { - for (j = 0; afl[i].suffixes[j]; j++) { - const char *p = afl[i].suffixes[j]; + for (j = 0; afl[i]->suffixes[j]; j++) { + const char *p = afl[i]->suffixes[j]; int plen = strlen(p); if (len < plen + 1) continue; @@ -160,21 +121,6 @@ int guess_audio_format(const char *name) 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) { @@ -182,7 +128,7 @@ static int get_file_info(int format, const char *path, char *data, const char *fmt = audio_format_name(format); memset(afhi, 0, sizeof(*afhi)); - ret = afl[format].get_file_info(data, size, fd, 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)); @@ -303,7 +249,7 @@ __must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi, uint8_t audio_format_id, const void *map, size_t mapsize, const char **buf, size_t *len, void **afh_context) { - struct audio_format_handler *afh = afl + audio_format_id; + struct audio_format_handler *afh = afl[audio_format_id]; if (afh_supports_dynamic_chunks(audio_format_id)) { int ret; @@ -313,7 +259,7 @@ __must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi, if (ret < 0) return ret; } - ret = afl[audio_format_id].get_chunk(chunk_num, *afh_context, + ret = afh->get_chunk(chunk_num, *afh_context, buf, len); if (ret < 0) { afh->close(*afh_context); @@ -340,7 +286,7 @@ __must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi, */ void afh_close(void *afh_context, uint8_t audio_format_id) { - struct audio_format_handler *afh = afl + audio_format_id; + struct audio_format_handler *afh = afl[audio_format_id]; if (!afh_supports_dynamic_chunks(audio_format_id)) return; @@ -403,7 +349,7 @@ int32_t afh_get_start_chunk(int32_t approx_chunk_num, void afh_get_header(struct afh_info *afhi, uint8_t audio_format_id, void *map, size_t mapsize, char **buf, size_t *len) { - struct audio_format_handler *afh = afl + audio_format_id; + struct audio_format_handler *afh = afl[audio_format_id]; if (!map || !afhi || !afhi->header_len) { *buf = NULL; @@ -426,7 +372,7 @@ void afh_get_header(struct afh_info *afhi, uint8_t audio_format_id, */ void afh_free_header(char *header_buf, uint8_t audio_format_id) { - struct audio_format_handler *afh = afl + audio_format_id; + struct audio_format_handler *afh = afl[audio_format_id]; if (afh->get_header) free(header_buf); @@ -530,7 +476,7 @@ void set_max_chunk_size(struct afh_info *afhi) int afh_rewrite_tags(int audio_format_id, void *map, size_t mapsize, struct taginfo *tags, int output_fd, const char *filename) { - struct audio_format_handler *afh = afl + audio_format_id; + struct audio_format_handler *afh = afl[audio_format_id]; if (!afh->rewrite_tags) return -ERRNO_TO_PARA_ERROR(ENOTSUP); diff --git a/afh_recv.c b/afh_recv.c index 6525209b..4f8ff497 100644 --- a/afh_recv.c +++ b/afh_recv.c @@ -238,9 +238,7 @@ out: return ret; } -/** See \ref recv_init(). */ const struct receiver lsg_recv_cmd_com_afh_user_data = { - .init = afh_init, .open = afh_recv_open, .close = afh_recv_close, .pre_select = afh_recv_pre_select, diff --git a/afs.c b/afs.c index d9cddeb8..04eed550 100644 --- a/afs.c +++ b/afs.c @@ -366,8 +366,8 @@ int for_each_matching_row(struct pattern_match_data *pmd) */ int string_compare(const struct osl_object *obj1, const struct osl_object *obj2) { - const char *str1 = (const char *)obj1->data; - const char *str2 = (const char *)obj2->data; + const char *str1 = obj1->data; + const char *str2 = obj2->data; return strncmp(str1, str2, PARA_MIN(obj1->size, obj2->size)); } diff --git a/audiod.c b/audiod.c index b93f29de..a5a77437 100644 --- a/audiod.c +++ b/audiod.c @@ -1451,7 +1451,6 @@ int main(int argc, char *argv[]) parse_config_or_die(); crypt_init(); daemon_set_priority(OPT_UINT32_VAL(PRIORITY)); - recv_init(); if (daemon_init_colors_or_die(OPT_UINT32_VAL(COLOR), COLOR_AUTO, COLOR_NO, OPT_GIVEN(LOGFILE))) { for (i = 0; i < OPT_GIVEN(LOG_COLOR); i++) diff --git a/command.c b/command.c index 67f6bf3c..63f0f165 100644 --- a/command.c +++ b/command.c @@ -457,6 +457,7 @@ EXPORT_SERVER_CMD_HANDLER(version); ITEM(chunk_time) \ ITEM(num_chunks) \ ITEM(amplification) \ + ITEM(play_time) \ /* * Create a set of audio-file related status items with empty values. These are @@ -663,8 +664,7 @@ EXPORT_SERVER_CMD_HANDLER(nomore); static int com_ff(struct command_context *cc, struct lls_parse_result *lpr) { long promille; - int ret, backwards = 0; - unsigned i; + int i, ret; char c, *errctx; ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx)); @@ -672,20 +672,33 @@ static int com_ff(struct command_context *cc, struct lls_parse_result *lpr) send_errctx(cc, errctx); return ret; } - if (!(ret = sscanf(lls_input(0, lpr), "%u%c", &i, &c))) - return -E_COMMAND_SYNTAX; - if (ret > 1 && c == '-') - backwards = 1; /* jmp backwards */ + ret = para_atoi32(lls_input(0, lpr), &i); + if (ret < 0) { + if (ret != -E_ATOI_JUNK_AT_END) + return ret; + /* + * Compatibility code to keep the historic syntax (ff 30-) + * working. This can be removed after 0.7.0. + */ + ret = sscanf(lls_input(0, lpr), "%i%c", &i, &c); + if (ret <= 0) + return -E_COMMAND_SYNTAX; + if (ret > 1 && c == '-') { + PARA_WARNING_LOG("use of obsolete syntax\n"); + i = -i; + } + } mutex_lock(mmd_mutex); ret = -E_NO_AUDIO_FILE; if (!mmd->afd.afhi.chunks_total || !mmd->afd.afhi.seconds_total) goto out; ret = 1; promille = (1000 * mmd->current_chunk) / mmd->afd.afhi.chunks_total; - if (backwards) - promille -= 1000 * i / mmd->afd.afhi.seconds_total; - else - promille += 1000 * i / mmd->afd.afhi.seconds_total; + /* + * We need this cast because without it the expression on the right + * hand side is of unsigned type. + */ + promille += 1000 * i / (int)mmd->afd.afhi.seconds_total; if (promille < 0) promille = 0; if (promille > 1000) { diff --git a/compress_filter.c b/compress_filter.c index 69a982ef..15bed6df 100644 --- a/compress_filter.c +++ b/compress_filter.c @@ -48,8 +48,7 @@ static int compress_post_select(__a_unused struct sched *s, void *context) size_t length, i; int16_t *ip, *op; uint32_t inertia = U32_OPTVAL(INERTIA, fn->lpr); - unsigned gain_shift = inertia + U32_OPTVAL(DAMP, fn->lpr), - mask = (1U << U32_OPTVAL(BLOCKSIZE, fn->lpr)) - 1U; + unsigned mask = (1U << U32_OPTVAL(BLOCKSIZE, fn->lpr)) - 1U; //inplace = false; next_buffer: ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL); @@ -78,19 +77,22 @@ next_buffer: neg = true; } sample *= pcd->current_gain; - sample >>= gain_shift; + sample >>= inertia + 1; if (sample > 32767) { /* clip */ + PARA_WARNING_LOG("clip: %d\n", sample); sample = 32767; pcd->current_gain = (3 * pcd->current_gain + (1 << inertia)) / 4; pcd->peak = 0; } else if (sample > pcd->peak) pcd->peak = sample; + sample >>= U32_OPTVAL(DAMP, fn->lpr); op[i] = neg? -sample : sample; if (++pcd->num_samples & mask) continue; // PARA_DEBUG_LOG("gain: %u, peak: %u\n", pcd->current_gain, // pcd->peak); + if (pcd->peak < U32_OPTVAL(TARGET_LEVEL, fn->lpr)) { if (pcd->current_gain < pcd->max_gain) pcd->current_gain++; @@ -121,10 +123,43 @@ static void compress_open(struct filter_node *fn) fn->private_data = pcd; fn->min_iqs = 2; /* 16 bit audio */ pcd->current_gain = 1U << inertia; - pcd->max_gain = 1U << (inertia + aggressiveness); + pcd->max_gain = (1U << inertia) * (1.0 + 3.0 * aggressiveness / 10.0); +} + +static void *compress_setup(const struct lls_parse_result *lpr) +{ + uint32_t val; + + val = U32_OPTVAL(BLOCKSIZE, lpr); + if (val == 0 || val > 31) { + PARA_EMERG_LOG("blocksize (%u) out of range\n", val); + exit(EXIT_FAILURE); + } + val = U32_OPTVAL(AGGRESSIVENESS, lpr); + if (val > 10) { + PARA_EMERG_LOG("aggressiveness (%u) out of range\n", val); + exit(EXIT_FAILURE); + } + val = U32_OPTVAL(INERTIA, lpr); + if (val == 0 || val > 14) { + PARA_EMERG_LOG("inertia (%u) out of range\n", val); + exit(EXIT_FAILURE); + } + val = U32_OPTVAL(TARGET_LEVEL, lpr); + if (val > 32767) { + PARA_EMERG_LOG("target-level (%u) out of range\n", val); + exit(EXIT_FAILURE); + } + val = U32_OPTVAL(DAMP, lpr); + if (val > 16) { + PARA_EMERG_LOG("damp (%u) out of range\n", val); + exit(EXIT_FAILURE); + } + return NULL; /* no need for a config structure */ } const struct filter lsg_filter_cmd_com_compress_user_data = { + .setup = compress_setup, .open = compress_open, .close = compress_close, .pre_select = generic_filter_pre_select, diff --git a/configure.ac b/configure.ac index 053996d1..f518f89a 100644 --- a/configure.ac +++ b/configure.ac @@ -96,8 +96,33 @@ AC_CHECK_HEADER(openssl/ssl.h, [], [HAVE_OPENSSL=no]) AC_CHECK_LIB([crypto], [RAND_bytes], [], [HAVE_OPENSSL=no]) LIB_SUBST_FLAGS(openssl) if test $HAVE_OPENSSL = yes; then - AC_CHECK_LIB([crypto], [RSA_set0_key], - AC_DEFINE([HAVE_RSA_SET0_KEY], [1], [openssl-1.1])) + HAVE_RSA_SET0_KEY=yes + AC_CHECK_DECL([RSA_set0_key], [], [], [#include ]) + AC_CHECK_LIB([crypto], [RSA_set0_key], [], []) + if test "$ac_cv_have_decl_RSA_set0_key" != "$ac_cv_lib_crypto_RSA_set0_key"; then + AC_MSG_ERROR([openssl header/library mismatch]) + fi + test "$ac_cv_have_decl_RSA_set0_key" = yes && + AC_DEFINE([HAVE_RSA_SET0_KEY], [1], [openssl >= 1.1]) + + HAVE_CRYPTO_CLEANUP_ALL_EX_DATA=yes + AC_CHECK_DECL([CRYPTO_cleanup_all_ex_data], [], + [HAVE_CRYPTO_CLEANUP_ALL_EX_DATA=no], + [#include ]) + AC_CHECK_LIB([crypto], [CRYPTO_cleanup_all_ex_data], [], + [HAVE_CRYPTO_CLEANUP_ALL_EX_DATA=no]) + test $HAVE_CRYPTO_CLEANUP_ALL_EX_DATA = yes && + AC_DEFINE([HAVE_CRYPTO_CLEANUP_ALL_EX_DATA], [1], + [not available on FreeBSD 12]) + HAVE_OPENSSL_THREAD_STOP=yes + AC_CHECK_DECL([OPENSSL_thread_stop], [], + [HAVE_OPENSSL_THREAD_STOP=no], + [#include ]) + AC_CHECK_LIB([crypto], [OPENSSL_thread_stop], [], + [HAVE_OPENSSL_THREAD_STOP=no]) + test $HAVE_OPENSSL_THREAD_STOP = yes && + AC_DEFINE([HAVE_OPENSSL_THREAD_STOP], [1], + [not available on openssl-1.0]) fi UNSTASH_FLAGS ######################################################################### gcrypt diff --git a/crypt_backend.h b/crypt_backend.h index 175a6881..b0998d85 100644 --- a/crypt_backend.h +++ b/crypt_backend.h @@ -7,6 +7,13 @@ /** AES block size in bytes. */ #define AES_CRT128_BLOCK_SIZE 16 -int decode_ssh_key(const char *filename, unsigned char **blob, +int decode_public_key(const char *filename, unsigned char **blob, size_t *decoded_size); +int decode_private_key(const char *key_file, unsigned char **result, + size_t *blob_size); +/** Legacy PEM keys (openssh-7.7 and earlier, paraslash.0.6.2 and earlier) */ +#define PKT_PEM (0) +/** OPENSSH keys (since openssh-7.8, paraslash.0.6.3) */ +#define PKT_OPENSSH (1) int check_private_key_file(const char *file); +int find_openssh_bignum_offset(const unsigned char *data, int len); diff --git a/crypt_common.c b/crypt_common.c index 235b8b8d..c1e40d92 100644 --- a/crypt_common.c +++ b/crypt_common.c @@ -85,7 +85,7 @@ static int check_ssh_key_header(const unsigned char *blob, int blen) * * \sa \ref uudecode(). */ -int decode_ssh_key(const char *filename, unsigned char **blob, +int decode_public_key(const char *filename, unsigned char **blob, size_t *decoded_size) { int ret, ret2; @@ -159,3 +159,161 @@ int hash_compare(unsigned char *h1, unsigned char *h2) } return 0; } + +/** + * Check header of an openssh private key and compute bignum offset. + * + * \param data The base64-decoded key. + * \param len The size of the decoded key. + * + * Several assumptions are made about the key. Most notably, we only support + * single unencrypted keys without comments. + * + * \return The offset at which the first bignum of the private key (the public + * exponent n) starts. Negative error code on failure. + */ +int find_openssh_bignum_offset(const unsigned char *data, int len) +{ + /* + * Unencrypted keys without comments always start with the below byte + * sequence. See PROTOCOL.key of the openssh package. + */ + static const unsigned char valid_openssh_header[] = { + /* string "openssh-key-v1" */ + 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2d, 0x6b, 0x65, + 0x79, 0x2d, 0x76, 0x31, + /* length of the cipher name */ + 0x00, 0x00, 0x00, 0x00, 0x04, + /* cipher name: "none" */ + 0x6e, 0x6f, 0x6e, 0x65, + /* length of the kdfname (only used for encrypted keys) */ + 0x00, 0x00, 0x00, 0x04, + /* kdfname: "none" */ + 0x6e, 0x6f, 0x6e, 0x65, + /* length of kdfoptions */ + 0x00, 0x00, 0x00, 0x00, + /* number of keys */ + 0x00, 0x00, 0x00, 0x01, + }; + uint32_t val; + const unsigned char *p, *end = data + len; + + if (len <= sizeof(valid_openssh_header) + 4) + return -E_OPENSSH_PARSE; + if (memcmp(data, valid_openssh_header, sizeof(valid_openssh_header))) + return -E_OPENSSH_PARSE; + p = data + sizeof(valid_openssh_header); + /* length of public key */ + val = read_u32_be(p); + if (val > end - p - 4) + return -E_OPENSSH_PARSE; + p += val + 4; + /* length of private key */ + val = read_u32_be(p); + if (val > end - p - 4) + return -E_OPENSSH_PARSE; + p += 4; + /* two equal random integers ("checkint") */ + if (p + 8 > end) + return -E_OPENSSH_PARSE; + if (read_u32_be(p) != read_u32_be(p + 4)) + return -E_OPENSSH_PARSE; + p += 8; + /* length of name of key type "ssh-rsa" */ + if (p + 11 > end) + return -E_OPENSSH_PARSE; + if (read_u32_be(p) != 7) + return -E_OPENSSH_PARSE; + if (memcmp(p + 4, "ssh-rsa", 7)) + return -E_OPENSSH_PARSE; + p += 11; + return p - data; +} + +/** Private PEM keys (legacy format) start with this header. */ +#define PRIVATE_PEM_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----" +/** Private OPENSSH keys (RFC4716) start with this header. */ +#define PRIVATE_OPENSSH_KEY_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----" +/** Private PEM keys (legacy format) end with this footer. */ +#define PRIVATE_PEM_KEY_FOOTER "-----END RSA PRIVATE KEY-----" +/** Private OPENSSH keys (RFC4716) end with this footer. */ +#define PRIVATE_OPENSSH_KEY_FOOTER "-----END OPENSSH PRIVATE KEY-----" + +/** + * Decode an openssh-v1 (aka RFC4716) or PEM (aka ASN.1) private key. + * + * \param key_file The private key file (usually id_rsa). + * \param result Pointer to base64-decoded blob is returned here. + * \param blob_size The size of the decoded blob. + * + * This only checks header and footer and base64-decodes the part in between. + * No attempt to read the decoded part is made. + * + * \return Negative on errors, PKT_PEM or PKT_OPENSSH on success, indicating + * the type of key. + */ +int decode_private_key(const char *key_file, unsigned char **result, + size_t *blob_size) +{ + int ret, ret2, i, j, key_type; + void *map; + size_t map_size, key_size; + unsigned char *blob = NULL; + char *begin, *footer, *key; + + ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL); + if (ret < 0) + goto out; + ret = -E_KEY_MARKER; + if (strncmp(map, PRIVATE_PEM_KEY_HEADER, + strlen(PRIVATE_PEM_KEY_HEADER)) == 0) { + key_type = PKT_PEM; + begin = map + strlen(PRIVATE_PEM_KEY_HEADER); + footer = strstr(map, PRIVATE_PEM_KEY_FOOTER); + PARA_INFO_LOG("detected legacy PEM key %s\n", key_file); + } else if (strncmp(map, PRIVATE_OPENSSH_KEY_HEADER, + strlen(PRIVATE_OPENSSH_KEY_HEADER)) == 0) { + key_type = PKT_OPENSSH; + begin = map + strlen(PRIVATE_OPENSSH_KEY_HEADER); + footer = strstr(map, PRIVATE_OPENSSH_KEY_FOOTER); + PARA_INFO_LOG("detected openssh key %s\n", key_file); + } else + goto unmap; + if (!footer) + goto unmap; + /* skip whitespace at the beginning */ + for (; begin < footer; begin++) { + if (para_isspace(*begin)) + continue; + break; + } + ret = -E_KEY_MARKER; + if (begin >= footer) + goto unmap; + + key_size = footer - begin; + key = para_malloc(key_size + 1); + for (i = 0, j = 0; begin + i < footer; i++) { + if (para_isspace(begin[i])) + continue; + key[j++] = begin[i]; + } + key[j] = '\0'; + ret = base64_decode(key, j, (char **)&blob, blob_size); + free(key); + if (ret < 0) + goto unmap; + ret = key_type; +unmap: + ret2 = para_munmap(map, map_size); + if (ret >= 0 && ret2 < 0) + ret = ret2; + if (ret < 0) { + free(blob); + blob = NULL; + } +out: + *result = blob; + return ret; +} + diff --git a/dccp_recv.c b/dccp_recv.c index 8a08504a..639c93fc 100644 --- a/dccp_recv.c +++ b/dccp_recv.c @@ -151,7 +151,6 @@ out: return ret; } -/** See \ref recv_init(). */ const struct receiver lsg_recv_cmd_com_dccp_user_data = { .open = dccp_recv_open, .close = dccp_recv_close, diff --git a/dccp_send.c b/dccp_send.c index 496895a5..55f189ad 100644 --- a/dccp_send.c +++ b/dccp_send.c @@ -87,6 +87,7 @@ static void dccp_shutdown(void) { dccp_shutdown_clients(); generic_acl_deplete(&dss->acl); + free_sender_status(dss); } /** * Obtain current MPS according to RFC 4340, sec. 14. */ diff --git a/error.h b/error.h index 8f4f9bb1..8ef9e37d 100644 --- a/error.h +++ b/error.h @@ -167,6 +167,7 @@ PARA_ERROR(OGG_PACKET_IN, "ogg_stream_packetin() failed"), \ PARA_ERROR(OGG_STREAM_FLUSH, "ogg_stream_flush() failed"), \ PARA_ERROR(OGG_SYNC, "internal ogg storage overflow"), \ + PARA_ERROR(OPENSSH_PARSE, "could not parse openssh private key"), \ PARA_ERROR(OPUS_COMMENT, "invalid or corrupted opus comment"), \ PARA_ERROR(OPUS_DECODE, "opus decode error"), \ PARA_ERROR(OPUS_HEADER, "invalid opus header"), \ @@ -266,11 +267,10 @@ extern const char * const para_errlist[]; */ #define SYSTEM_ERROR_BIT 30 -/** - * Like the SYSTEM_ERROR_BIT, but indicates an error from the osl library. - */ +/** Like SYSTEM_ERROR_BIT, but for errors from the osl library. */ #define OSL_ERROR_BIT 29 +/** Like SYSTEM_ERROR_BIT, but for errors from the lopsub library. */ #define LLS_ERROR_BIT 28 /** Check whether the system error bit is set. */ diff --git a/flac_afh.c b/flac_afh.c index 45373ea1..6e236839 100644 --- a/flac_afh.c +++ b/flac_afh.c @@ -515,13 +515,12 @@ free_pfad: static const char * const flac_suffixes[] = {"flac", NULL}; /** - * The init function of the flac audio format handler. + * The audio format handler for flac (free lossless audio decoder). * - * \param afh pointer to the struct to initialize + * It depends on libflac and on libogg. */ -void flac_afh_init(struct audio_format_handler *afh) -{ - afh->get_file_info = flac_get_file_info, - afh->suffixes = flac_suffixes; - afh->rewrite_tags = flac_rewrite_tags; -} +const struct audio_format_handler flac_afh = { + .get_file_info = flac_get_file_info, + .suffixes = flac_suffixes, + .rewrite_tags = flac_rewrite_tags, +}; diff --git a/gcrypt.c b/gcrypt.c index 694c0adb..dbe49008 100644 --- a/gcrypt.c +++ b/gcrypt.c @@ -106,66 +106,6 @@ static const char *gcrypt_strerror(gcry_error_t gret) return gcry_strerror(gcry_err_code(gret)); } -static int decode_key(const char *key_file, const char *header_str, - const char *footer_str, unsigned char **result) -{ - int ret, ret2, i, j; - void *map; - size_t map_size, key_size, blob_size; - unsigned char *blob = NULL; - char *begin, *footer, *key; - - ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL); - if (ret < 0) - goto out; - ret = -E_KEY_MARKER; - if (strncmp(map, header_str, strlen(header_str))) - goto unmap; - footer = strstr(map, footer_str); - ret = -E_KEY_MARKER; - if (!footer) - goto unmap; - begin = map + strlen(header_str); - /* skip whitespace at the beginning */ - for (; begin < footer; begin++) { - if (para_isspace(*begin)) - continue; - break; - } - ret = -E_KEY_MARKER; - if (begin >= footer) - goto unmap; - - key_size = footer - begin; - key = para_malloc(key_size + 1); - for (i = 0, j = 0; begin + i < footer; i++) { - if (para_isspace(begin[i])) - continue; - key[j++] = begin[i]; - } - key[j] = '\0'; - ret = base64_decode(key, j, (char **)&blob, &blob_size); - free(key); - if (ret < 0) - goto free_unmap; - ret = blob_size; - goto unmap; -free_unmap: - free(blob); - blob = NULL; -unmap: - ret2 = para_munmap(map, map_size); - if (ret >= 0 && ret2 < 0) - ret = ret2; - if (ret < 0) { - free(blob); - blob = NULL; - } -out: - *result = blob; - return ret; -} - /** ASN Types and their code. */ enum asn1_types { /** The next object is an integer. */ @@ -207,11 +147,11 @@ static inline int get_long_form_num_length_bytes(unsigned char c) /* * Returns: Number of bytes scanned. This may differ from the value returned via - * bn_bytes because the latter does not include the ASN.1 prefix and a leading - * zero is not considered as an additional byte for bn_bytes. + * bitsp because the latter does not include the ASN.1 prefix and a leading + * zero is not considered as an additional byte for the number of bits. */ -static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn, - int *bn_bytes) +static int read_pem_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn, + unsigned *bitsp) { int i, bn_size; gcry_error_t gret; @@ -249,8 +189,8 @@ static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn, cp++; bn_size--; } - if (bn_bytes) - *bn_bytes = bn_size; + if (bitsp) + *bitsp = bn_size * 8; cp += bn_size; // unsigned char *buf; // gcry_mpi_aprint(GCRYMPI_FMT_HEX, &buf, NULL, *bn); @@ -258,7 +198,55 @@ static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn, return cp - start; } -static int find_privkey_bignum_offset(const unsigned char *data, int len) +struct rsa_params { + gcry_mpi_t n, e, d, p, q, u; +}; + +static int read_pem_rsa_params(unsigned char *start, unsigned char *end, + struct rsa_params *p) +{ + unsigned char *cp = start; + unsigned bits; + int ret; + + ret = read_pem_bignum(cp, end, &p->n, &bits); + if (ret < 0) + return ret; + cp += ret; + ret = read_pem_bignum(cp, end, &p->e, NULL); + if (ret < 0) + goto release_n; + cp += ret; + ret = read_pem_bignum(cp, end, &p->d, NULL); + if (ret < 0) + goto release_e; + cp += ret; + ret = read_pem_bignum(cp, end, &p->p, NULL); + if (ret < 0) + goto release_d; + cp += ret; + ret = read_pem_bignum(cp, end, &p->q, NULL); + if (ret < 0) + goto release_p; + cp += ret; + ret = read_pem_bignum(cp, end, &p->u, NULL); + if (ret < 0) + goto release_q; + return bits; +release_q: + gcry_mpi_release(p->q); +release_p: + gcry_mpi_release(p->p); +release_d: + gcry_mpi_release(p->d); +release_e: + gcry_mpi_release(p->e); +release_n: + gcry_mpi_release(p->n); + return ret; +} + +static int find_pem_bignum_offset(const unsigned char *data, int len) { const unsigned char *p = data, *end = data + len; @@ -290,96 +278,131 @@ static int find_privkey_bignum_offset(const unsigned char *data, int len) return p - data; } -/** Private keys start with this header. */ -#define PRIVATE_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----" -/** Private keys end with this footer. */ -#define PRIVATE_KEY_FOOTER "-----END RSA PRIVATE KEY-----" - -static int get_private_key(const char *key_file, struct asymmetric_key **result) +static int read_openssh_bignum(unsigned char *start, unsigned char *end, + gcry_mpi_t *bn, unsigned *bitsp) { - gcry_mpi_t n = NULL, e = NULL, d = NULL, p = NULL, q = NULL, - u = NULL; - unsigned char *blob, *cp, *end; - int blob_size, ret, n_size; gcry_error_t gret; - size_t erroff; - gcry_sexp_t sexp; - struct asymmetric_key *key; + size_t nscanned; + unsigned bits; - *result = NULL; - ret = decode_key(key_file, PRIVATE_KEY_HEADER, PRIVATE_KEY_FOOTER, - &blob); - if (ret < 0) - return ret; - blob_size = ret; - end = blob + blob_size; - ret = find_privkey_bignum_offset(blob, blob_size); - if (ret < 0) - goto free_blob; - PARA_INFO_LOG("reading RSA params at offset %d\n", ret); - cp = blob + ret; + gret = gcry_mpi_scan(bn, GCRYMPI_FMT_SSH, start, end - start, &nscanned); + if (gret) { + PARA_ERROR_LOG("gcry_mpi_scan: %s\n", + gcry_strerror(gcry_err_code(gret))); + return -E_MPI_SCAN; + } + bits = (nscanned - 4 - (start[4] == '\0')) * 8; + if (bitsp) + *bitsp = bits; + PARA_DEBUG_LOG("scanned %u-bit bignum\n", bits); + return nscanned; +} - ret = read_bignum(cp, end, &n, &n_size); +static int read_openssh_rsa_params(unsigned char *start, unsigned char *end, + struct rsa_params *p) +{ + unsigned char *cp = start; + unsigned bits; + int ret; + + ret = read_openssh_bignum(cp, end, &p->n, &bits); if (ret < 0) - goto free_blob; + return ret; cp += ret; - - ret = read_bignum(cp, end, &e, NULL); + ret = read_openssh_bignum(cp, end, &p->e, NULL); if (ret < 0) goto release_n; cp += ret; - - ret = read_bignum(cp, end, &d, NULL); + ret = read_openssh_bignum(cp, end, &p->d, NULL); if (ret < 0) goto release_e; cp += ret; - - ret = read_bignum(cp, end, &p, NULL); + ret = read_openssh_bignum(cp, end, &p->u, NULL); if (ret < 0) goto release_d; cp += ret; - - ret = read_bignum(cp, end, &q, NULL); + ret = read_openssh_bignum(cp, end, &p->p, NULL); if (ret < 0) - goto release_p; + goto release_u; cp += ret; - ret = read_bignum(cp, end, &u, NULL); + ret = read_openssh_bignum(cp, end, &p->q, NULL); if (ret < 0) - goto release_q; + goto release_p; + return bits; +release_p: + gcry_mpi_release(p->p); +release_u: + gcry_mpi_release(p->u); +release_d: + gcry_mpi_release(p->d); +release_e: + gcry_mpi_release(p->e); +release_n: + gcry_mpi_release(p->n); + return ret; +} + +static int get_private_key(const char *key_file, struct asymmetric_key **result) +{ + struct rsa_params params; + unsigned char *blob, *end; + unsigned bits; + int ret, key_type; + gcry_error_t gret; + size_t erroff, blob_size; + gcry_sexp_t sexp; + struct asymmetric_key *key; + + *result = NULL; + ret = decode_private_key(key_file, &blob, &blob_size); + if (ret < 0) + return ret; + key_type = ret; + end = blob + blob_size; + if (key_type == PKT_PEM) + ret = find_pem_bignum_offset(blob, blob_size); + else + ret = find_openssh_bignum_offset(blob, blob_size); + if (ret < 0) + goto free_blob; + PARA_INFO_LOG("reading RSA params at offset %d\n", ret); + if (key_type == PKT_PEM) + ret = read_pem_rsa_params(blob + ret, end, ¶ms); + else + ret = read_openssh_rsa_params(blob + ret, end, ¶ms); + if (ret < 0) + goto free_blob; + bits = ret; /* * OpenSSL uses slightly different parameters than gcrypt. To use these * parameters we need to swap the values of p and q and recompute u. */ - if (gcry_mpi_cmp(p, q) > 0) { - gcry_mpi_swap(p, q); - gcry_mpi_invm(u, p, q); + if (gcry_mpi_cmp(params.p, params.q) > 0) { + gcry_mpi_swap(params.p, params.q); + gcry_mpi_invm(params.u, params.p, params.q); } - gret = gcry_sexp_build(&sexp, &erroff, RSA_PRIVKEY_SEXP, - n, e, d, p, q, u); + gret = gcry_sexp_build(&sexp, &erroff, RSA_PRIVKEY_SEXP, params.n, + params.e, params.d, params.p, params.q, params.u); if (gret) { PARA_ERROR_LOG("offset %zu: %s\n", erroff, gcry_strerror(gcry_err_code(gret))); ret = -E_SEXP_BUILD; - goto release_u; + goto free_params; } key = para_malloc(sizeof(*key)); key->sexp = sexp; *result = key; - ret = n_size * 8; + ret = bits; PARA_INFO_LOG("succesfully read %d bit private key\n", ret); -release_u: - gcry_mpi_release(u); -release_q: - gcry_mpi_release(q); -release_p: - gcry_mpi_release(p); -release_d: - gcry_mpi_release(d); -release_e: - gcry_mpi_release(e); -release_n: - gcry_mpi_release(n); +free_params: + gcry_mpi_release(params.n); + gcry_mpi_release(params.e); + gcry_mpi_release(params.d); + gcry_mpi_release(params.u); + gcry_mpi_release(params.p); + gcry_mpi_release(params.q); + free_blob: free(blob); return ret; @@ -390,34 +413,25 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result) unsigned char *blob, *p, *end; int ret; gcry_error_t gret; - size_t nr_scanned, erroff, decoded_size; + size_t erroff, decoded_size; gcry_mpi_t e, n; gcry_sexp_t sexp; struct asymmetric_key *key; + unsigned bits; - ret = decode_ssh_key(key_file, &blob, &decoded_size); + ret = decode_public_key(key_file, &blob, &decoded_size); if (ret < 0) return ret; p = blob + ret; end = blob + decoded_size; PARA_DEBUG_LOG("scanning modulus and public exponent\n"); - gret = gcry_mpi_scan(&e, GCRYMPI_FMT_SSH, p, end - p, &nr_scanned); - if (gret) { - ret = -E_MPI_SCAN; - PARA_CRIT_LOG("%s\n", gcry_strerror(gcry_err_code(gret))); + ret = read_openssh_bignum(p, end, &e, NULL); + if (ret < 0) goto free_blob; - } - PARA_DEBUG_LOG("scanned e (%zu bytes)\n", nr_scanned); - p += nr_scanned; - if (p >= end) - goto release_e; - gret = gcry_mpi_scan(&n, GCRYMPI_FMT_SSH, p, end - p, &nr_scanned); - if (gret) { - ret = -E_MPI_SCAN; - PARA_ERROR_LOG("%s\n", gcry_strerror(gcry_err_code(gret))); + p += ret; + ret = read_openssh_bignum(p, end, &n, &bits); + if (ret < 0) goto release_e; - } - PARA_DEBUG_LOG("scanned n (%zu bytes)\n", nr_scanned); gret = gcry_sexp_build(&sexp, &erroff, RSA_PUBKEY_SEXP, n, e); if (gret) { PARA_ERROR_LOG("offset %zu: %s\n", erroff, @@ -425,12 +439,12 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result) ret = -E_SEXP_BUILD; goto release_n; } - ret = ROUND_DOWN(nr_scanned, 32); - PARA_INFO_LOG("successfully read %d bit ssh public key\n", ret * 8); + PARA_INFO_LOG("successfully read %u bit ssh public key\n", bits); key = para_malloc(sizeof(*key)); key->num_bytes = ret; key->sexp = sexp; *result = key; + ret = bits; release_n: gcry_mpi_release(n); release_e: diff --git a/http_recv.c b/http_recv.c index 3a723299..1fb60bad 100644 --- a/http_recv.c +++ b/http_recv.c @@ -167,7 +167,6 @@ static int http_recv_open(struct receiver_node *rn) return 1; } -/** See \ref recv_init(). */ const struct receiver lsg_recv_cmd_com_http_user_data = { .open = http_recv_open, .close = http_recv_close, diff --git a/http_send.c b/http_send.c index 330b45ac..c6b9decc 100644 --- a/http_send.c +++ b/http_send.c @@ -80,6 +80,7 @@ static void http_shutdown(void) { http_shutdown_clients(); generic_acl_deplete(&hss->acl); + free_sender_status(hss); } static int queue_chunk_or_shutdown(struct sender_client *sc, diff --git a/ipc.c b/ipc.c index 85013a63..8e9dd51a 100644 --- a/ipc.c +++ b/ipc.c @@ -7,7 +7,6 @@ #include "ipc.h" #include #include -#include #include #include @@ -194,9 +193,8 @@ int shm_detach(void *addr) } # if defined __FreeBSD__ || defined __NetBSD__ +#include # define SYSCTL_SHMMAX_VARIABLE "kern.ipc.shmmax" -# elif defined __APPLE__ -# define SYSCTL_SHMMAX_VARIABLE "kern.sysv.shmmax" # else # undef SYSCTL_SHMMAX_VARIABLE # endif diff --git a/m4/lls/afh.suite.m4 b/m4/lls/afh.suite.m4 index cf83b972..7ebf208c 100644 --- a/m4/lls/afh.suite.m4 +++ b/m4/lls/afh.suite.m4 @@ -62,6 +62,12 @@ version-string = GIT_VERSION() The backup suffix is '~'. That is, a single tilde character is appended to the given file name. [/help] + [option preserve] + summary = preserve modification time + [help] + If this option is given, the mtime of the modified file is set to + the value prior to the modification. + [/help] [option year] short_opt = y summary = set the year tag diff --git a/m4/lls/filter_cmd.suite.m4 b/m4/lls/filter_cmd.suite.m4 index d269d237..c026a628 100644 --- a/m4/lls/filter_cmd.suite.m4 +++ b/m4/lls/filter_cmd.suite.m4 @@ -25,7 +25,7 @@ caption = filters purpose = dynamically adjust the volume of an audio stream [option blocksize] short_opt = b - summary = use blocks of size 2**bits + summary = adjust volume after each block of size 2**bits (1-31) typestr = bits arg_info = required_arg arg_type = uint32 @@ -35,32 +35,52 @@ caption = filters [/help] [option aggressiveness] short_opt = a - summary = controls the maximum amount to amplify by + summary = controls the maximum amount to amplify by (0-10) typestr = bits arg_info = required_arg arg_type = uint32 default_val = 4 + [help] + This controls the maximal gain factor. Zero means to not amplify + at all while the value 10 corresponds to maximal gain factor which + results in a 4-fold increase in volume. + [/help] [option inertia] short_opt = i - summary = how much inertia ramping has + summary = how much inertia ramping has (1-14) typestr = bits arg_info = required_arg arg_type = uint32 default_val = 6 + [help] + Larger values cause smaller volume adjustments. + [/help] [option target-level] short_opt = t - summary = target signal level (0-32768) + summary = target signal level (0-32767) typestr = level arg_info = required_arg arg_type = uint32 - default_val = 20000 + default_val = 16384 + [help] + If the peak of the previous block is less than the target level, + volume is increased slightly for the next block. Otherwise it is + decreased. The default value is chosen to minimize clipping. There + is usually no reason to change it. + [/help] [option damp] short_opt = d - summary = if non-zero, scale down after normalizing + summary = if non-zero, scale down after normalizing (0-16) typestr = bits arg_info = required_arg arg_type = uint32 default_val = 0 + [help] + This scales down the volume of the audio stream by factor 2**bits. + This is mostly useful if another audio application (e.g., a video + game) is running in parallel and the relative volume of the audio + stream is too high. + [/help] [subcommand fecdec] purpose = decode a (lossy) input stream using forward error correction [subcommand flacdec] diff --git a/m4/lls/makefile b/m4/lls/makefile index dd86b51d..daf6de92 100644 --- a/m4/lls/makefile +++ b/m4/lls/makefile @@ -2,29 +2,29 @@ lls_m4_include_dir := $(lls_m4_dir)/include $(lls_suite_dir)/%.m4d: $(lls_m4_dir)/%.suite.m4 | $(lls_suite_dir) - @[ -z "$(Q)" ] || echo 'M4D $<' - $(Q) $(M4) -Pg -I $(lls_m4_include_dir) -s $< \ + $(call SAY, M4D $<) + $(M4) -Pg -I $(lls_m4_include_dir) -s $< \ | awk '{if ($$1 ~ /#line/) {gsub(/"/, "", $$3); if ($$3 != "$<") \ print "$(lls_suite_dir)/$(*F).suite: " $$3}}' | sort | uniq > $@ $(lls_suite_dir)/%.suite: $(lls_m4_dir)/%.suite.m4 | $(lls_suite_dir) - @[ -z "$(Q)" ] || echo 'M4 $<' - $(Q) $(M4) -Pg -I $(lls_m4_include_dir) -D GIT_VERSION=$(GIT_VERSION) \ + $(call SAY, M4 $<) + $(M4) -Pg -I $(lls_m4_include_dir) -D GIT_VERSION=$(GIT_VERSION) \ -D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) -D LOGLEVELS=$(LOGLEVELS) \ $< > $@ $(lls_suite_dir)/%.lsg.c: $(lls_suite_dir)/%.suite - @[ -z "$(Q)" ] || echo 'LSGC $<' - $(Q) $(LOPSUBGEN) --gen-c --output-dir $(lls_suite_dir) < $< + $(call SAY, LSGC $<) + $(LOPSUBGEN) --gen-c --output-dir $(lls_suite_dir) < $< $(lls_suite_dir)/%.lsg.h: $(lls_suite_dir)/%.suite - @[ -z "$(Q)" ] || echo 'LSGH $<' - $(Q) $(LOPSUBGEN) --gen-header --output-dir $(lls_suite_dir) < $< + $(call SAY, LSGH $<) + $(LOPSUBGEN) --gen-header --output-dir $(lls_suite_dir) < $< $(lls_suite_dir)/%.lsg.man: $(lls_suite_dir)/%.suite - @[ -z "$(Q)" ] || echo 'LSGM $<' - $(Q) $(LOPSUBGEN) --gen-man --output-dir $(lls_suite_dir) < $< + $(call SAY, LSGM $<) + $(LOPSUBGEN) --gen-man --output-dir $(lls_suite_dir) < $< $(object_dir)/%.o: $(lls_suite_dir)/%.c | $(object_dir) - @[ -z "$(Q)" ] || echo 'CC $<' - $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(STRICT_CFLAGS) $< + $(call SAY, CC $<) + $(CC) -c -o $@ $(CPPFLAGS) $(STRICT_CFLAGS) $< diff --git a/m4/lls/server_cmd.suite.m4 b/m4/lls/server_cmd.suite.m4 index 079589d1..2a39907c 100644 --- a/m4/lls/server_cmd.suite.m4 +++ b/m4/lls/server_cmd.suite.m4 @@ -124,18 +124,18 @@ aux_info_prefix = Permissions: summary = enable verbose mode [subcommand ff] - purpose = jump N seconds forward or backward - synopsis = n[-] + purpose = jump forward or backward in the current audio file + synopsis = seconds aux_info = VSS_READ | VSS_WRITE [description] - This sets the 'R' (reposition request) bit of the vss status flags - which enqueues a request to jump n seconds forwards or backwards. - - Example: - - para_client ff 30- - - jumps 30 seconds backwards. + This enqueues a request to reposition the audio stream according to + the argument, which may be a signed or an unsigned integer. Negative + values correspond to backward jumps. + + If a negative number is given whose absolute value exceeds the current + postition of the stream, a jump to the beginning of the audio file + is performed. If a positive amount of seconds is given which exceeds + the remaining time of the audio file, the next audio file is loaded. [/description] diff --git a/mixer.c b/mixer.c index 55282e77..eae89291 100644 --- a/mixer.c +++ b/mixer.c @@ -318,7 +318,7 @@ static int com_sleep(const struct mixer *m) PARA_INFO_LOG("waketime: %d:%02d\n", tm->tm_hour, tm->tm_min); client_cmd("stop"); sleep(1); - if (fot && fo_mood) { + if (fot && fo_mood && *fo_mood) { ret = set_initial_volume(m, h); if (ret < 0) goto close_mixer; @@ -335,14 +335,14 @@ static int com_sleep(const struct mixer *m) if (ret < 0) goto close_mixer; } - if (sleep_mood) { + if (sleep_mood && *sleep_mood) { change_afs_mode(sleep_mood); if (!fot || !fo_mood) /* currently stopped */ client_cmd("play"); - } else if (fot && fo_mood) /* currently playing */ + } else if (fot && fo_mood && *fo_mood) /* currently playing */ client_cmd("stop"); m->close(&h); - if (!fit || !fi_mood) /* nothing to do */ + if (!fit || !fi_mood || !*fi_mood) /* nothing to do */ return 1; for (;;) { time(&t1); @@ -355,7 +355,7 @@ static int com_sleep(const struct mixer *m) sleep(delay); } change_afs_mode(fi_mood); - if (sleep_mood) /* currently playing */ + if (sleep_mood && *sleep_mood) /* currently playing */ client_cmd("next"); else /* currently stopped */ client_cmd("play"); diff --git a/mp3_afh.c b/mp3_afh.c index 471efd99..728b25b8 100644 --- a/mp3_afh.c +++ b/mp3_afh.c @@ -652,7 +652,7 @@ static int mp3_read_info(unsigned char *map, size_t numbytes, int fd, 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("%" PRIu32 "chunks, each %lums\n", afhi->chunks_total, + PARA_DEBUG_LOG("%" PRIu32 " chunks, each %lums\n", afhi->chunks_total, tv2ms(&afhi->chunk_tv)); set_max_chunk_size(afhi); ret = mp3_get_id3(map, numbytes, fd, &afhi->tags); @@ -675,7 +675,7 @@ static int mp3_get_file_info(char *map, size_t numbytes, int fd, ret = mp3_read_info((unsigned char *)map, numbytes, fd, afhi); if (ret < 0) return ret; - if (afhi->seconds_total < 2 || !afhi->chunks_total) + if (afhi->chunks_total == 0) return -E_MP3_INFO; return 1; } @@ -683,15 +683,14 @@ static int mp3_get_file_info(char *map, size_t numbytes, int fd, static const char * const mp3_suffixes[] = {"mp3", NULL}; /** - * the init function of the mp3 audio format handler + * The mp3 audio format handler. * - * \param afh pointer to the struct to initialize + * It does not depend on any libraries and is hence always compiled in. */ -void mp3_afh_init(struct audio_format_handler *afh) -{ - afh->get_file_info = mp3_get_file_info; - afh->suffixes = mp3_suffixes; +const struct audio_format_handler mp3_afh = { + .get_file_info = mp3_get_file_info, + .suffixes = mp3_suffixes, #ifdef HAVE_LIBID3TAG - afh->rewrite_tags = mp3_rewrite_tags; + .rewrite_tags = mp3_rewrite_tags, #endif /* HAVE_LIBID3TAG */ -} +}; diff --git a/ogg_afh.c b/ogg_afh.c index 93ad14bb..5da339fe 100644 --- a/ogg_afh.c +++ b/ogg_afh.c @@ -173,14 +173,15 @@ static int vorbis_rewrite_tags(const char *map, size_t mapsize, static const char * const ogg_suffixes[] = {"ogg", NULL}; /** - * The init function of the ogg vorbis audio format handler. + * The ogg vorbis audio format handler. * - * \param afh Pointer to the struct to initialize. + * It should actually be called vorbis because the ogg container format is also + * employed for the speex and flac codecs. Both the ogg and the vorbis + * libraries must be installed to get this audio format handler. */ -void ogg_afh_init(struct audio_format_handler *afh) -{ - afh->get_file_info = ogg_vorbis_get_file_info; - afh->get_header = vorbis_get_header; - afh->suffixes = ogg_suffixes; - afh->rewrite_tags = vorbis_rewrite_tags; -} +const struct audio_format_handler ogg_afh = { + .get_file_info = ogg_vorbis_get_file_info, + .get_header = vorbis_get_header, + .suffixes = ogg_suffixes, + .rewrite_tags = vorbis_rewrite_tags +}; diff --git a/ogg_afh_common.c b/ogg_afh_common.c index 62cde3d4..3e36bdd5 100644 --- a/ogg_afh_common.c +++ b/ogg_afh_common.c @@ -124,8 +124,9 @@ int oac_get_file_info(char *map, size_t numbytes, struct afh_info *afhi, ogg_sync_state oss; ogg_page op; char *buf; - int ret, i, j, frames_per_chunk, ct_size; - long long unsigned num_frames = 0; + int ret, i, j, frames_per_chunk, ct_size, prev_pageno = 0; + long long unsigned granule_skip = 0, num_frames = 0; + int64_t granule = 0, prev_granule = 0; ogg_sync_init(&oss); ret = -E_OGG_SYNC; @@ -145,8 +146,17 @@ int oac_get_file_info(char *map, size_t numbytes, struct afh_info *afhi, oss.returned = 0; oss.fill = numbytes; /* count ogg pages and get duration of the file */ - for (i = 0; ogg_sync_pageseek(&oss, &op) > 0; i++) - num_frames = ogg_page_granulepos(&op); + for (i = 0; ogg_sync_pageseek(&oss, &op) > 0; i++) { + int this_pageno = ogg_page_pageno(&op); + int64_t this_granule = ogg_page_granulepos(&op); + if (this_granule >= 0) + granule = this_granule; + if (i > 0 && this_pageno != prev_pageno + 1) /* hole */ + granule_skip += granule - prev_granule; + prev_pageno = this_pageno; + prev_granule = granule; + } + num_frames = granule - granule_skip; PARA_INFO_LOG("%d pages, %llu frames\n", i, num_frames); ret = -E_OGG_EMPTY; if (i == 0) @@ -163,7 +173,7 @@ int oac_get_file_info(char *map, size_t numbytes, struct afh_info *afhi, oss.returned = afhi->header_len; oss.fill = numbytes; for (j = 1; ogg_sync_pageseek(&oss, &op) > 0; /* nothing */) { - int granule = ogg_page_granulepos(&op); + granule = ogg_page_granulepos(&op); while (granule >= (j + 1) * frames_per_chunk) { j++; diff --git a/openssl.c b/openssl.c index 4895e176..0ad9d7db 100644 --- a/openssl.c +++ b/openssl.c @@ -57,27 +57,15 @@ void crypt_init(void) void crypt_shutdown(void) { +#ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA CRYPTO_cleanup_all_ex_data(); -} - -static int get_private_key(const char *path, RSA **rsa) -{ - EVP_PKEY *pkey; - BIO *bio = BIO_new(BIO_s_file()); - - *rsa = NULL; - if (!bio) - return -E_PRIVATE_KEY; - if (BIO_read_filename(bio, path) <= 0) - goto bio_free; - pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); - if (!pkey) - goto bio_free; - *rsa = EVP_PKEY_get1_RSA(pkey); - EVP_PKEY_free(pkey); -bio_free: - BIO_free(bio); - return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY; +#endif +#ifdef HAVE_OPENSSL_THREAD_STOP /* openssl-1.1 or later */ + OPENSSL_thread_stop(); +#else /* openssl-1.0 */ + ERR_remove_thread_state(NULL); +#endif + EVP_cleanup(); } /* @@ -144,6 +132,152 @@ free_rsa: return ret; } +static int read_pem_private_key(const char *path, RSA **rsa) +{ + EVP_PKEY *pkey; + BIO *bio = BIO_new(BIO_s_file()); + + *rsa = NULL; + if (!bio) + return -E_PRIVATE_KEY; + if (BIO_read_filename(bio, path) <= 0) + goto bio_free; + pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + if (!pkey) + goto bio_free; + *rsa = EVP_PKEY_get1_RSA(pkey); + EVP_PKEY_free(pkey); +bio_free: + BIO_free(bio); + return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY; +} + +static int read_private_rsa_params(const unsigned char *blob, + const unsigned char *end, RSA **result) +{ + int ret; + RSA *rsa; + BN_CTX *ctx; + BIGNUM *n, *e, *d, *iqmp, *p, *q; /* stored in the key file */ + BIGNUM *dmp1, *dmq1; /* these will be computed */ + BIGNUM *tmp; + const unsigned char *cp = blob; + + rsa = RSA_new(); + if (!rsa) + return -E_BIGNUM; + ret = -E_BIGNUM; + tmp = BN_new(); + if (!tmp) + goto free_rsa; + ctx = BN_CTX_new(); + if (!ctx) + goto free_tmp; + dmp1 = BN_new(); + if (!dmp1) + goto free_ctx; + dmq1 = BN_new(); + if (!dmq1) + goto free_dmp1; + ret = read_bignum(cp, end - cp, &n); + if (ret < 0) + goto free_dmq1; + cp += ret; + ret = read_bignum(cp, end - cp, &e); + if (ret < 0) + goto free_n; + cp += ret; + ret = read_bignum(cp, end - cp, &d); + if (ret < 0) + goto free_e; + cp += ret; + ret = read_bignum(cp, end - cp, &iqmp); + if (ret < 0) + goto free_d; + cp += ret; + ret = read_bignum(cp, end - cp, &p); + if (ret < 0) + goto free_iqmp; + cp += ret; + ret = read_bignum(cp, end - cp, &q); + if (ret < 0) + goto free_p; + ret = -E_BIGNUM; + if (!BN_sub(tmp, q, BN_value_one())) + goto free_q; + if (!BN_mod(dmp1, d, tmp, ctx)) + goto free_q; + if (!BN_sub(tmp, q, BN_value_one())) + goto free_q; + if (!BN_mod(dmq1, d, tmp, ctx)) + goto free_q; +#ifdef HAVE_RSA_SET0_KEY + RSA_set0_key(rsa, n, e, d); + RSA_set0_factors(rsa, p, q); + RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp); +#else + rsa->n = n; + rsa->e = e; + rsa->d = d; + rsa->p = p; + rsa->q = q; + rsa->dmp1 = dmp1; + rsa->dmq1 = dmq1; + rsa->iqmp = iqmp; +#endif + *result = rsa; + ret = 1; + goto free_ctx; +free_q: + BN_clear_free(q); +free_p: + BN_clear_free(p); +free_iqmp: + BN_clear_free(iqmp); +free_d: + BN_clear_free(d); +free_e: + BN_free(e); +free_n: + BN_free(n); +free_dmq1: + BN_clear_free(dmq1); +free_dmp1: + BN_clear_free(dmp1); +free_ctx: + BN_CTX_free(ctx); +free_tmp: + BN_clear_free(tmp); +free_rsa: + if (ret < 0) + RSA_free(rsa); + return ret; +} + +static int get_private_key(const char *path, RSA **rsa) +{ + int ret; + unsigned char *blob, *end; + size_t blob_size; + + *rsa = NULL; + ret = decode_private_key(path, &blob, &blob_size); + if (ret < 0) + return ret; + end = blob + blob_size; + if (ret == PKT_OPENSSH) { + ret = find_openssh_bignum_offset(blob, blob_size); + if (ret < 0) + goto free_blob; + PARA_INFO_LOG("reading RSA params at offset %d\n", ret); + ret = read_private_rsa_params(blob + ret, end, rsa); + } else + ret = read_pem_private_key(path, rsa); +free_blob: + free(blob); + return ret; +} + int apc_get_pubkey(const char *key_file, struct asymmetric_key **result) { unsigned char *blob; @@ -151,7 +285,7 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result) int ret; struct asymmetric_key *key = para_malloc(sizeof(*key)); - ret = decode_ssh_key(key_file, &blob, &decoded_size); + ret = decode_public_key(key_file, &blob, &decoded_size); if (ret < 0) goto out; ret = read_rsa_bignums(blob + ret, decoded_size - ret, &key->rsa); diff --git a/opus_afh.c b/opus_afh.c index ed6fe5c8..dca6cfba 100644 --- a/opus_afh.c +++ b/opus_afh.c @@ -291,14 +291,15 @@ static void opus_get_header(void *map, size_t mapsize, char **buf, } /** - * The init function of the ogg/opus audio format handler. + * The audio format handler for ogg/opus. * - * \param afh Pointer to the struct to initialize. + * The opus codec was standardized by the Internet Engineering Task Force and + * is described in RFC 6716 (2012). The audio format handler depends on the ogg + * and the opus libraries. */ -void opus_afh_init(struct audio_format_handler *afh) -{ - afh->get_file_info = opus_get_file_info, - afh->get_header = opus_get_header; - afh->suffixes = opus_suffixes; - afh->rewrite_tags = opus_rewrite_tags; -} +const struct audio_format_handler opus_afh = { + .get_file_info = opus_get_file_info, + .get_header = opus_get_header, + .suffixes = opus_suffixes, + .rewrite_tags = opus_rewrite_tags, +}; diff --git a/play.c b/play.c index 647367a3..86edc4d4 100644 --- a/play.c +++ b/play.c @@ -1238,12 +1238,8 @@ int main(int argc, char *argv[]) int ret; unsigned num_inputs; - /* needed this early to make help work */ - recv_init(); - sched.default_timeout.tv_sec = 5; parse_config_or_die(argc, argv); - AFH_RECV->init(); session_open(); num_inputs = lls_num_inputs(play_lpr); init_shuffle_map(); diff --git a/recv.c b/recv.c index 7b46d78c..10d55d21 100644 --- a/recv.c +++ b/recv.c @@ -79,7 +79,6 @@ int main(int argc, char *argv[]) loglevel = OPT_UINT32_VAL(LOGLEVEL, lpr); version_handle_flag("recv", OPT_GIVEN(VERSION, lpr)); handle_help_flag(lpr); - recv_init(); memset(&rn, 0, sizeof(struct receiver_node)); ret = check_receiver_arg(OPT_STRING_VAL(RECEIVER, lpr), &receiver_lpr); if (ret < 0) diff --git a/recv.h b/recv.h index f88ebd84..36b0f1db 100644 --- a/recv.h +++ b/recv.h @@ -39,12 +39,6 @@ struct receiver_node { * \sa \ref http_recv.c, \ref udp_recv.c. */ struct receiver { - /** - * The optional receiver init function. - * - * Performs any initialization needed before the receiver can be opened. - */ - void (*init)(void); /** * Open one instance of the receiver. * @@ -114,7 +108,6 @@ struct receiver { /** Iterate over all available receivers. */ #define FOR_EACH_RECEIVER(i) for (i = 1; lls_cmd(i, recv_cmd_suite); i++) -void recv_init(void); int check_receiver_arg(const char *ra, struct lls_parse_result **lprp); void print_receiver_helps(bool detailed); int generic_recv_pre_select(struct sched *s, struct receiver_node *rn); diff --git a/recv_common.c b/recv_common.c index 948de474..31fd81f1 100644 --- a/recv_common.c +++ b/recv_common.c @@ -15,27 +15,6 @@ #include "recv.h" #include "string.h" -/** - * Call the init function of each paraslash receiver. - * - * Receivers employ the user_data feature of the lopsub library: Each receiver - * of the recv_cmd suite defines a struct receiver as its user data. - * recv_init() obtains a pointer to this structure by calling lls_user_data(). - * If the receiver has an init function (i.e., if ->init is not NULL), ->init() - * is called to initialize the receiver. - */ -void recv_init(void) -{ - int i; - - FOR_EACH_RECEIVER(i) { - const struct lls_command *cmd = RECV_CMD(i); - const struct receiver *r = lls_user_data(cmd); - if (r && r->init) - r->init(); - } -} - /** * Check if the given string is a valid receiver specifier. * diff --git a/send.h b/send.h index 67b47e48..f6aafbb4 100644 --- a/send.h +++ b/send.h @@ -2,6 +2,10 @@ /** \file send.h Sender-related defines and structures. */ +/** + * A little preprocessor fu helps to create the sender_subcommand enumeration + * below and the list of sender name strings without duplicating the commands. + */ #define SENDER_SUBCOMMANDS \ SENDER_SUBCOMMAND(add) /**< Add a target (udp only). */ \ SENDER_SUBCOMMAND(delete) /**< Delete a target (udp only). */ \ @@ -10,12 +14,27 @@ SENDER_SUBCOMMAND(on) /**< Activate the sender. */ \ SENDER_SUBCOMMAND(off) /**< Deactivate the sender. */ \ +/** Concatenate "SENDER_" and the given arg and append a comma. */ #define SENDER_SUBCOMMAND(_name) SENDER_ ## _name, + +/** + * Each sender subcommand gets an SENDER_xxx identifier. The identifier is + * passed from the sender command handler to the server process via shared + * memory. + */ enum sender_subcommand { - SENDER_SUBCOMMANDS + SENDER_SUBCOMMANDS /**< List of SENDER_xxx identifiers. */ NUM_SENDER_CMDS /**< Used as array size in struct \ref sender. */ }; + #undef SENDER_SUBCOMMAND + +/** + * Redefine it to expand to the stringified name of the sender so that + * SENDER_SUBCOMMANDS above now expands to the comma-separated list of sender + * name strings. This is used in command.c to define and initialize an array of + * char pointers. + */ #define SENDER_SUBCOMMAND(_name) #_name, /** @@ -193,6 +212,7 @@ void init_sender_status(struct sender_status *ss, const struct lls_opt_result *acl_opt_result, const struct lls_opt_result *listen_address_opt_result, int default_port, int max_clients, int default_deny); +void free_sender_status(const struct sender_status *ss); char *generic_sender_status(struct sender_status *ss, const char *name); void generic_com_allow(struct sender_command_data *scd, struct sender_status *ss); diff --git a/send_common.c b/send_common.c index 24b14ab8..ea494d9a 100644 --- a/send_common.c +++ b/send_common.c @@ -154,6 +154,25 @@ void init_sender_status(struct sender_status *ss, ss->default_deny = default_deny; } +/** + * Deallocate all resources allocated in \ref init_sender_status(). + * + * \param ss The structure whose components should be freed. + * + * This frees the dynamically allocated parts of the structure which was + * initialized by an earlier call to \ref init_sender_status(). It does *not* + * call free(ss), though. + */ +void free_sender_status(const struct sender_status *ss) +{ + int i; + + free(ss->listen_fds); + FOR_EACH_LISTEN_FD(i, ss) + free(ss->listen_addresses[i]); + free(ss->listen_addresses); +} + /** * Return a string containing the current status of a sender. * diff --git a/server.c b/server.c index 6370cefd..e0d50f4f 100644 --- a/server.c +++ b/server.c @@ -583,9 +583,6 @@ static void server_init(int argc, char **argv, struct server_command_task *sct) init_ipc_or_die(); /* init mmd struct, mmd and log mutex */ daemon_set_start_time(); daemon_set_hooks(pre_log_hook, post_log_hook); - PARA_NOTICE_LOG("initializing audio format handlers\n"); - afh_init(); - /* * Although afs uses its own signal handling we must ignore SIGUSR1 * _before_ the afs child process gets born by init_afs() below. It's diff --git a/spx_afh.c b/spx_afh.c index e2323647..caeacb19 100644 --- a/spx_afh.c +++ b/spx_afh.c @@ -248,13 +248,14 @@ static int spx_rewrite_tags(const char *map, size_t mapsize, static const char * const speex_suffixes[] = {"spx", "speex", NULL}; /** - * The init function of the ogg/speex audio format handler. + * The ogg/speex audio format handler. * - * \param afh Pointer to the struct to initialize. + * This codec is considered obsolete because the opus codec surpasses its + * performance in all areas. It is only compiled in if both the ogg and the + * speex library are installed. */ -void spx_afh_init(struct audio_format_handler *afh) -{ - afh->get_file_info = spx_get_file_info, - afh->suffixes = speex_suffixes; - afh->rewrite_tags = spx_rewrite_tags; -} +const struct audio_format_handler spx_afh = { + .get_file_info = spx_get_file_info, + .suffixes = speex_suffixes, + .rewrite_tags = spx_rewrite_tags, +}; diff --git a/string.c b/string.c index 65d22408..198e9f1d 100644 --- a/string.c +++ b/string.c @@ -199,17 +199,22 @@ __must_check __printf_1_2 __malloc char *make_message(const char *fmt, ...) } /** - * Free the content of a pointer and set it to \p NULL. + * Free the content of a pointer and set it to NULL. * - * This is equivalent to "free(*arg); *arg = NULL;". + * \param arg A pointer to the pointer whose content should be freed. * - * \param arg The pointer whose content should be freed. + * If arg is NULL, the function returns immediately. Otherwise it frees the + * memory pointed to by *arg and sets *arg to NULL. Hence callers have to pass + * the *address* of the pointer variable that points to the memory which should + * be freed. */ void freep(void *arg) { - void **ptr = (void **)arg; - free(*ptr); - *ptr = NULL; + if (arg) { + void **ptr = arg; + free(*ptr); + *ptr = NULL; + } } /** diff --git a/t/makefile.test b/t/makefile.test index a71963c9..904c7793 100644 --- a/t/makefile.test +++ b/t/makefile.test @@ -22,10 +22,11 @@ check: $(tests) test: $(tests) $(tests): all - $(Q) $@ $(test_options) + $(call SAY, $(@)) + $@ $(test_options) test-help: - $(Q) for t in $(tests); do $$t $(test_options) -h; done + @for t in $(tests); do $$t $(test_options) -h; done test-clean: $(RM) -r $(results_dir) diff --git a/udp_recv.c b/udp_recv.c index 67846b14..58d45ab4 100644 --- a/udp_recv.c +++ b/udp_recv.c @@ -186,7 +186,6 @@ err: return ret; } -/** See \ref recv_init(). */ const struct receiver lsg_recv_cmd_com_udp_user_data = { .open = udp_recv_open, .close = udp_recv_close, diff --git a/vss.c b/vss.c index 73c72311..023b2c1d 100644 --- a/vss.c +++ b/vss.c @@ -959,11 +959,12 @@ static void recv_afs_result(struct vss_task *vsst, fd_set *rfds) if (ret < 0) goto err; vsst->afsss = AFS_SOCKET_READY; - PARA_DEBUG_LOG("fd: %d, code: %u, shmid: %u\n", passed_fd, afs_code, - afs_data); ret = -E_NOFD; - if (afs_code != NEXT_AUDIO_FILE) + if (afs_code != NEXT_AUDIO_FILE) { + PARA_ERROR_LOG("afs code: %u, expected: %d\n", afs_code, + NEXT_AUDIO_FILE); goto err; + } if (passed_fd < 0) goto err; shmid = afs_data; diff --git a/web/manual.md b/web/manual.md index 2c806614..44799e91 100644 --- a/web/manual.md +++ b/web/manual.md @@ -446,7 +446,7 @@ following commands: Next, change to the "bar" account on client_host and generate the key pair with the commands - ssh-keygen -q -t rsa -b 2048 -N '' -m PEM + ssh-keygen -q -t rsa -b 2048 -N '' -m RFC4716 This generates the two files id_rsa and id_rsa.pub in ~/.ssh. Note that para_server won't accept keys shorter than 2048 bits. Moreover, diff --git a/wma_afh.c b/wma_afh.c index e6048a86..63e49677 100644 --- a/wma_afh.c +++ b/wma_afh.c @@ -646,13 +646,13 @@ out: static const char * const wma_suffixes[] = {"wma", NULL}; /** - * The init function of the wma audio format handler. + * The audio format handler for Windows Media Audio. * - * \param afh Pointer to the struct to initialize. + * Only WMA version 2 is supported. This audio format handler does not depend + * on any third party libraries and is therefore always compiled in. */ -void wma_afh_init(struct audio_format_handler *afh) -{ - afh->get_file_info = wma_get_file_info; - afh->suffixes = wma_suffixes; - afh->rewrite_tags = wma_rewrite_tags; -} +const struct audio_format_handler wma_afh = { + .get_file_info = wma_get_file_info, + .suffixes = wma_suffixes, + .rewrite_tags = wma_rewrite_tags, +};