From: Andre Noll Date: Fri, 23 Aug 2013 17:33:00 +0000 (+0200) Subject: Merge branch 't/image-0.5' X-Git-Tag: v0.5.0~2 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=5cb5db1fbf8f9b96c3b5a1e0cc7de9e710757540;hp=8b7b7727dfe40fbf40df4ca3046247676aa2e166 Merge branch 't/image-0.5' After almost three months we finally got there. 8b7b77 The overwhelming new logo for paraslash.0.5. --- diff --git a/.gitignore b/.gitignore index 9091a9c2..d340f207 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,7 @@ TODO *_command_list.h *_command_list.man paraslash-git.tar.bz2 -skencil/overview.pdf +web/dia/overview.pdf *.swp error2.h web_sync diff --git a/Doxyfile b/Doxyfile index 3fed0c2e..04d8f326 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1306,7 +1306,8 @@ PREDEFINED = HAVE_MAD \ HAVE_OGGVORBIS \ __GNUC__=4 \ __GNUC_MINOR__=4 \ - HAVE_UCRED + HAVE_UCRED \ + HAVE_CLOCK_GETTIME # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. diff --git a/FEATURES b/FEATURES index 6ad420df..395d0f15 100644 --- a/FEATURES +++ b/FEATURES @@ -5,7 +5,7 @@ Features * Runs on Linux, Mac OS, FreeBSD, NetBSD, Solaris and probably other Unixes - * Mp3, ogg/vorbis, ogg/speex, aac (m4a), wma and flac support + * Mp3, ogg/vorbis, ogg/speex, aac (m4a), wma, flac and ogg/opus support * Native Alsa, OSS, CoreAudio output support * Support for ESD, Pulseaudio, AIX, Solaris, IRIX through libao * Local or remote http, dccp and udp network audio streaming diff --git a/Makefile.in b/Makefile.in index f3dfea00..cf8b9f71 100644 --- a/Makefile.in +++ b/Makefile.in @@ -8,7 +8,8 @@ MANDIR := @datarootdir@/man/man1 PACKAGE_VERSION := @PACKAGE_VERSION@ PACKAGE_STRING := @PACKAGE_STRING@ install_sh := @install_sh@ -executables := @executables@ +executables := $(addprefix para_, @executables@) +ggo_descriptions_declared := @ggo_descriptions_declared@ GENGETOPT := @gengetopt@ HELP2MAN := @help2man@ @@ -18,7 +19,6 @@ build_date := $(shell date) uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS") uname_rs := $(shell uname -rs) cc_version := $(shell $(CC) --version | head -n 1) -codename := spectral gravity GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h) @@ -34,6 +34,7 @@ object_dir := $(build_dir)/objects dep_dir := $(build_dir)/deps man_dir := $(build_dir)/man/man1 cmdline_dir := $(build_dir)/cmdline +m4depdir := $(build_dir)/m4deps DEBUG_CPPFLAGS += -g -Wunused -Wundef -W DEBUG_CPPFLAGS += -Wredundant-decls @@ -67,7 +68,6 @@ CPPFLAGS += -Wchar-subscripts CPPFLAGS += -DBINDIR='"$(BINDIR)"' CPPFLAGS += -DBUILD_DATE='"$(build_date)"' CPPFLAGS += -DUNAME_RS='"$(uname_rs)"' -CPPFLAGS += -DCODENAME='"$(codename)"' CPPFLAGS += -DCC_VERSION='"$(cc_version)"' CPPFLAGS += -Werror-implicit-function-declaration CPPFLAGS += -Wmissing-noreturn @@ -81,13 +81,12 @@ CPPFLAGS += @osl_cppflags@ LDFLAGS += @clock_gettime_ldflags@ -man_pages := $(patsubst %, $(man_dir)/%.1, @executables@) +man_pages := $(patsubst %, $(man_dir)/%.1, $(executables)) autocrap := config.h.in configure tarball_pfx := @PACKAGE_TARNAME@-$(PACKAGE_VERSION) tarball_delete := $(addprefix $(tarball_pfx)/,\ - web .changelog_before_cvs .changelog_cvs .gitignore\ - skencil) + web .changelog_before_cvs .changelog_cvs .gitignore) tarball := @PACKAGE_TARNAME@-$(PACKAGE_VERSION).tar.bz2 # To put more focus on warnings, be less verbose as default @@ -99,12 +98,12 @@ else endif .PHONY: dep all clean distclean maintainer-clean install man tarball -all: dep @executables@ $(man_pages) +all: dep $(executables) $(man_pages) dep: $(deps) man: $(man_pages) tarball: $(tarball) -$(object_dir) $(man_dir) $(ggo_dir) $(cmdline_dir) $(dep_dir): +$(object_dir) $(man_dir) $(ggo_dir) $(cmdline_dir) $(dep_dir) $(m4depdir): $(Q) $(MKDIR_P) $@ -include $(m4_ggo_dir)/makefile @@ -182,6 +181,10 @@ $(object_dir)/mp3dec_filter.o: mp3dec_filter.c | $(object_dir) @[ -z "$(Q)" ] || echo 'CC $<' $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @mad_cppflags@ $< +$(object_dir)/compress_filter.o: compress_filter.c | $(object_dir) + @[ -z "$(Q)" ] || echo 'CC $<' + $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) -O3 $< + $(object_dir)/aacdec_filter.o: aacdec_filter.c | $(object_dir) @[ -z "$(Q)" ] || echo 'CC $<' $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @faad_cppflags@ $< @@ -227,6 +230,7 @@ all_objs := @recv_objs@ @filter_objs@ @client_objs@ @gui_objs@ \ @audiod_objs@ @audioc_objs@ @fade_objs@ @server_objs@ \ @write_objs@ @afh_objs@ @play_objs@ deps := $(addprefix $(dep_dir)/, $(all_objs:.o=.d)) +m4_deps := $(addprefix $(m4depdir)/, $(addsuffix .m4d, @executables@)) recv_objs := $(addprefix $(object_dir)/, @recv_objs@) filter_objs := $(addprefix $(object_dir)/, @filter_objs@) @@ -242,6 +246,7 @@ play_objs := $(addprefix $(object_dir)/, @play_objs@) ifeq ($(findstring clean, $(MAKECMDGOALS)),) -include $(deps) +-include $(m4_deps) endif para_recv: $(recv_objs) @@ -290,7 +295,7 @@ para_play: $(play_objs) clean: @[ -z "$(Q)" ] || echo 'CLEAN' - $(Q) rm -f @executables@ + $(Q) rm -f $(executables) $(Q) rm -rf $(object_dir) clean2: clean @@ -304,14 +309,12 @@ distclean: clean2 test-clean $(Q) rm -f GPATH GRTAGS GSYMS GTAGS maintainer-clean: distclean - rm -f *.tar.bz2 \ - config.h configure \ - config.h.in skencil/*.pdf skencil/*.ps + rm -f *.tar.bz2 config.h configure config.h.in rm -rf web_sync install: all man $(MKDIR_P) $(BINDIR) $(MANDIR) - $(install_sh) -s -m 755 @executables@ $(BINDIR) + $(install_sh) -s -m 755 $(executables) $(BINDIR) $(install_sh) -m 644 $(man_pages) $(MANDIR) $(MKDIR_P) $(VARDIR) >/dev/null 2>&1 || true # not fatal, so don't complain diff --git a/NEWS b/NEWS index 56f84753..5cd0d374 100644 --- a/NEWS +++ b/NEWS @@ -1,11 +1,46 @@ ------------------------------------------- -0.?.? (to be announced) "spectral gravity" ------------------------------------------- +--------------------------------------------- +0.5.0 (to be announced) "invertible validity" +--------------------------------------------- + + - The sideband compatibility code has been removed, hence + sideband connections (introduced in 0.4.11) are now mandatory. + - Addblob commands can produce output. + - The stat command no longer sends garbage when para_server was + compiled against libgcrypt. + - Dependencies for gengetopt files are computed automatically. + This eliminates a constant source of build bugs. + - The setatt command now accepts file name patterns rather than only + path names. + - overview.pdf is now based on dia, a simple diagram creation program. + The new version is much more detailed and contains descriptions of + the various programs of the paraslash package. + - The separator of all multi-word options has been changed from + underscore to dash. For example --log_color becomes --log-color. + - New logo. + +-------------------------------------- +0.4.13 (2013-07-29) "spectral gravity" +-------------------------------------- + +One more 0.4.x release before the API-breaking changes for 0.5.0 go +in. The main features of this release are the ogg/opus audio format, +and UTF-8 support, but it includes also tons of other improvements +and fixes all over the place. + - New audio format: ogg/opus. - UTF8 support for para_gui and the mp3 audio format handler. - Scheduler improvements and fixes. - The obsolete gettimeofday() function has been replaced by clock_gettime() on systems which support it. + - Speed and usability improvements for para_gui. + - para_client now restores the fd flags of stdin and stdout + on shutdown. + - Improved manual pages. + - Consistent version strings for all executables. + - Reduced dependencies on generated files result in fewer + recompilations on changes. + - Performance improvements for the compress filter. + - Improved downloads web page. ----------------------------------------- 0.4.12 (2012-12-20) "volatile relativity" diff --git a/afh.c b/afh.c index 4c65d7c1..8e707505 100644 --- a/afh.c +++ b/afh.c @@ -15,6 +15,7 @@ #include "afh.h" #include "error.h" #include "version.h" +#include "ggo.h" static struct afh_args_info conf; INIT_AFH_ERRLISTS; @@ -56,6 +57,17 @@ static void print_chunk_table(struct afh_info *afhi) } } +__noreturn static void print_help_and_die(void) +{ + struct ggo_help h = DEFINE_GGO_HELP(afh); + int d = conf.detailed_help_given; + unsigned flags = d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS; + + ggo_print_help(&h, flags); + printf("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS); + exit(EXIT_SUCCESS); +} + /** * The main function of para_afh. * @@ -72,12 +84,14 @@ int main(int argc, char **argv) struct afh_info afhi; afh_cmdline_parser(argc, argv, &conf); - HANDLE_VERSION_FLAG("afh", conf); loglevel = get_loglevel_by_name(conf.loglevel_arg); + version_handle_flag("afh", conf.version_given); + if (conf.help_given || conf.detailed_help_given) + print_help_and_die(); + afh_init(); ret = -E_AFH_SYNTAX; if (conf.inputs_num == 0) goto out; - afh_init(); for (i = 0; i < conf.inputs_num; i++) { int ret2; ret = mmap_full_file(conf.inputs[i], O_RDONLY, &audio_file_data, diff --git a/afh_common.c b/afh_common.c index 6c161a7c..5be43550 100644 --- a/afh_common.c +++ b/afh_common.c @@ -31,6 +31,10 @@ void mp3_init(struct audio_format_handler *); void flac_afh_init(struct audio_format_handler *); #endif +#ifdef HAVE_OPUS + void opus_afh_init(struct audio_format_handler *); +#endif + void wma_afh_init(struct audio_format_handler *); /** The list of all status items */ @@ -79,6 +83,12 @@ static struct audio_format_handler afl[] = { .name = "flac", #ifdef HAVE_FLAC .init = flac_afh_init, +#endif + }, + { + .name = "opus", +#ifdef HAVE_OPUS + .init = opus_afh_init, #endif }, { diff --git a/afh_recv.c b/afh_recv.c index 774ae88c..5c17dab7 100644 --- a/afh_recv.c +++ b/afh_recv.c @@ -70,10 +70,8 @@ static void *afh_recv_parse_config(int argc, char **argv) { struct afh_recv_args_info *tmp = para_calloc(sizeof(*tmp)); - if (!afh_recv_cmdline_parser(argc, argv, tmp)) - return tmp; - free(tmp); - return NULL; + afh_recv_cmdline_parser(argc, argv, tmp); + return tmp; } static void afh_recv_free_config(void *conf) @@ -256,9 +254,6 @@ void afh_recv_init(struct receiver *r) r->parse_config = afh_recv_parse_config; r->free_config = afh_recv_free_config; r->execute = afh_execute; - r->help = (struct ggo_help) { - .short_help = afh_recv_args_info_help, - .detailed_help = afh_recv_args_info_detailed_help - }; + r->help = (struct ggo_help)DEFINE_GGO_HELP(afh_recv); afh_recv_cmdline_parser_free(&dummy); } diff --git a/afs.c b/afs.c index 2f521291..2f9df845 100644 --- a/afs.c +++ b/afs.c @@ -546,6 +546,28 @@ static int activate_mood_or_playlist(char *arg, int *num_admissible) return 1; } +/** + * Result handler for sending data to the para_client process. + * + * \param result The data to be sent. + * \param band The band designator. + * \param private Pointer to the command context. + * + * \return The return value of the underlying call to \ref command.c::send_sb. + * + * \sa \ref callback_result_handler, \ref command.c::send_sb. + */ +int afs_cb_result_handler(struct osl_object *result, uint8_t band, + void *private) +{ + struct command_context *cc = private; + + assert(cc); + if (!result->size) + return 1; + return send_sb(&cc->scc, result->data, result->size, band, true); +} + static void com_select_callback(int fd, const struct osl_object *query) { struct para_buffer pb = { @@ -588,31 +610,6 @@ out: free(pb.buf); } -/** - * Result handler for sending data to the para_client process. - * - * \param result The data to be sent. - * \param band The band designator. - * \param private Pointer to the command context. - * - * \return The return value of the underlying call to \ref command.c::send_sb. - * - * \sa \ref callback_result_handler, \ref command.c::send_sb. - */ -int afs_cb_result_handler(struct osl_object *result, uint8_t band, - void *private) -{ - struct command_context *cc = private; - - assert(cc); - if (!result->size) - return 1; - if (cc->use_sideband) - return send_sb(&cc->scc, result->data, result->size, band, - true); - return sc_send_bin_buffer(&cc->scc, result->data, result->size); -} - int com_select(struct command_context *cc) { struct osl_object query; @@ -807,7 +804,7 @@ static void command_pre_select(struct sched *s, struct task *t) * \return Zero if \a buf is \p NULL or \a size is zero. Negative on errors, * and positive on success. */ -int pass_buffer_as_shm(int fd, uint8_t band, char *buf, size_t size) +int pass_buffer_as_shm(int fd, uint8_t band, const char *buf, size_t size) { int ret, shmid; void *shm; @@ -1068,9 +1065,6 @@ int com_init(struct command_context *cc) } ret = send_callback_request(create_tables_callback, &query, afs_cb_result_handler, cc); - if (ret < 0 && !cc->use_sideband) - /* ignore return value */ - sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret)); return ret; } diff --git a/afs.cmd b/afs.cmd index f0bd26f1..82b6dc48 100644 --- a/afs.cmd +++ b/afs.cmd @@ -210,7 +210,7 @@ H: normalize the volume of the audio file. A value of zero means H: no amplification, 64 means the amplitude should be multiplied H: by a factor of two, 128 by three and so on. H: -H: This value is used by the compress filter. +H: This value is used by the amp filter. H: H: -v Verbose mode. Explain what is being done. H: diff --git a/afs.h b/afs.h index b415bfec..a63968f1 100644 --- a/afs.h +++ b/afs.h @@ -207,7 +207,7 @@ typedef void callback_function(int fd, const struct osl_object *); */ typedef int callback_result_handler(struct osl_object *result, uint8_t band, void *private); int afs_cb_result_handler(struct osl_object *result, uint8_t band, void *private); -int pass_buffer_as_shm(int fd, uint8_t band, char *buf, size_t size); +int pass_buffer_as_shm(int fd, uint8_t band, const char *buf, size_t size); /** Structure passed to the AFS max_size handler. */ struct afs_max_size_handler_data { diff --git a/aft.c b/aft.c index 097b2b59..377740d1 100644 --- a/aft.c +++ b/aft.c @@ -280,7 +280,7 @@ static struct osl_table_description audio_file_table_desc = { .column_descriptions = aft_cols }; -/* We don't want * dot or dot-dot anywhere. */ +/* We don't want dot or dot-dot anywhere. */ static int verify_dotfile(const char *rest) { /* @@ -1694,7 +1694,7 @@ static void com_add_callback(int fd, const struct osl_object *query) uint16_t afhi_offset, chunks_offset; hash = (unsigned char *)buf + CAB_HASH_OFFSET; - hash_to_asc(hash, asc);; + hash_to_asc(hash, asc); objs[AFTCOL_HASH].data = buf + CAB_HASH_OFFSET; objs[AFTCOL_HASH].size = HASH_SIZE; @@ -1876,12 +1876,8 @@ static int add_one_audio_file(const char *path, void *private_data) ret = 1; if (pb && (pad->flags & ADD_FLAG_LAZY)) { /* lazy is really cheap */ if (pad->flags & ADD_FLAG_VERBOSE) - send_ret = pad->cc->use_sideband? - send_sb_va(&pad->cc->scc, SBD_OUTPUT, - "lazy-ignore: %s\n", path) - : - sc_send_va_buffer(&pad->cc->scc, - "lazy-ignore: %s\n", path); + send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT, + "lazy-ignore: %s\n", path); goto out_free; } /* We still want to add this file. Compute its hash. */ @@ -1901,12 +1897,8 @@ static int add_one_audio_file(const char *path, void *private_data) ret = 1; if (pb && hs && hs == pb && !(pad->flags & ADD_FLAG_FORCE)) { if (pad->flags & ADD_FLAG_VERBOSE) - send_ret = pad->cc->use_sideband? - send_sb_va(&pad->cc->scc, SBD_OUTPUT, - "%s exists, not forcing update\n", path) - : - sc_send_va_buffer(&pad->cc->scc, - "%s exists, not forcing update\n", path); + send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT, + "%s exists, not forcing update\n", path); goto out_unmap; } /* @@ -1923,12 +1915,8 @@ static int add_one_audio_file(const char *path, void *private_data) munmap(map.data, map.size); close(fd); if (pad->flags & ADD_FLAG_VERBOSE) { - send_ret = pad->cc->use_sideband? - send_sb_va(&pad->cc->scc, SBD_OUTPUT, - "adding %s\n", path) - : - sc_send_va_buffer(&pad->cc->scc, - "adding %s\n", path); + send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT, + "adding %s\n", path); if (send_ret < 0) goto out_free; } @@ -1943,14 +1931,8 @@ out_unmap: munmap(map.data, map.size); out_free: if (ret < 0 && send_ret >= 0) - send_ret = pad->cc->use_sideband? - send_sb_va(&pad->cc->scc, SBD_ERROR_LOG, - "failed to add %s (%s)\n", path, - para_strerror(-ret)) - : - sc_send_va_buffer(&pad->cc->scc, - "failed to add %s (%s)\n", path, - para_strerror(-ret)); + send_ret = send_sb_va(&pad->cc->scc, SBD_ERROR_LOG, + "failed to add %s (%s)\n", path, para_strerror(-ret)); free(obj.data); clear_afhi(afhi_ptr); /* Stop adding files only on send errors. */ @@ -1994,11 +1976,7 @@ int com_add(struct command_context *cc) char *path; ret = verify_path(cc->argv[i], &path); if (ret < 0) { - ret = cc->use_sideband? - send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s: %s\n", - cc->argv[i], para_strerror(-ret)) - : - sc_send_va_buffer(&cc->scc, "%s: %s\n", + ret = send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s: %s\n", cc->argv[i], para_strerror(-ret)); if (ret < 0) return ret; @@ -2006,14 +1984,9 @@ int com_add(struct command_context *cc) } ret = stat(path, &statbuf); if (ret < 0) { - ret = cc->use_sideband? - send_sb_va(&cc->scc, SBD_ERROR_LOG, - "failed to stat %s (%s)\n", path, - strerror(errno)) - : - sc_send_va_buffer(&cc->scc, - "failed to stat %s (%s)\n", path, - strerror(errno)); + ret = send_sb_va(&cc->scc, SBD_ERROR_LOG, + "failed to stat %s (%s)\n", path, + strerror(errno)); free(path); if (ret < 0) return ret; @@ -2025,12 +1998,8 @@ int com_add(struct command_context *cc) else ret = add_one_audio_file(path, &pad); if (ret < 0) { - if (cc->use_sideband) - send_sb_va(&cc->scc, SBD_OUTPUT, "%s: %s\n", path, - para_strerror(-ret)); - else - sc_send_va_buffer(&cc->scc, "%s: %s\n", path, - para_strerror(-ret)); + send_sb_va(&cc->scc, SBD_OUTPUT, "%s: %s\n", path, + para_strerror(-ret)); free(path); return ret; } @@ -2502,6 +2471,108 @@ int com_cpsi(struct command_context *cc) return ret; } +struct change_atts_data { + uint64_t add_mask, del_mask; + struct para_buffer pb; +}; + +static int change_atts(__a_unused struct osl_table *table, + struct osl_row *row, __a_unused const char *name, void *data) +{ + int ret; + struct osl_object obj; + struct afs_info old_afsi, new_afsi; + struct afsi_change_event_data aced = { + .aft_row = row, + .old_afsi = &old_afsi + }; + struct change_atts_data *cad = data; + + ret = get_afsi_object_of_row(row, &obj); + if (ret < 0) + return ret; + ret = load_afsi(&old_afsi, &obj); + if (ret < 0) + return ret; + new_afsi = old_afsi; + new_afsi.attributes |= cad->add_mask; + new_afsi.attributes &= ~cad->del_mask; + save_afsi(&new_afsi, &obj); /* in-place update */ + afs_event(AFSI_CHANGE, &cad->pb, &aced); + return 1; +} + +static void com_setatt_callback(int fd, const struct osl_object *query) +{ + char *p; + int ret; + size_t len; + struct change_atts_data cad = { + .pb = { + .max_size = shm_get_shmmax(), + .max_size_handler = afs_max_size_handler, + .private_data = &(struct afs_max_size_handler_data) { + .fd = fd, + .band = SBD_OUTPUT + } + } + }; + struct pattern_match_data pmd = { + .table = audio_file_table, + .loop_col_num = AFTCOL_HASH, + .match_col_num = AFTCOL_PATH, + .pm_flags = PM_SKIP_EMPTY_NAME, + .data = &cad, + .action = change_atts + }; + + for (p = query->data; p < (char *)query->data + query->size; p += len + 1) { + char c; + unsigned char bitnum; + + len = strlen(p); + ret = -E_ATTR_SYNTAX; + if (len == 0) + goto out; + c = p[len - 1]; + if (c != '+' && c != '-') + break; + p[len - 1] = '\0'; + ret = get_attribute_bitnum_by_name(p, &bitnum); + if (ret < 0) + goto out; + if (c == '+') + cad.add_mask |= (1UL << bitnum); + else + cad.del_mask |= (1UL << bitnum); + } + ret = -E_ATTR_SYNTAX; + if (!cad.add_mask && !cad.del_mask) + goto out; + pmd.patterns.data = p; + assert(p < (char *)query->data + query->size); + pmd.patterns.size = (char *)query->data + query->size - p; + ret = for_each_matching_row(&pmd); + if (ret < 0) + goto out; + if (pmd.num_matches == 0) + para_printf(&cad.pb, "no matches\n"); +out: + if (ret < 0) + para_printf(&cad.pb, "%s\n", para_strerror(-ret)); + if (cad.pb.offset) + pass_buffer_as_shm(fd, SBD_OUTPUT, cad.pb.buf, cad.pb.offset); + free(cad.pb.buf); +} + +int com_setatt(struct command_context *cc) +{ + if (cc->argc < 3) + return -E_ATTR_SYNTAX; + return send_standard_callback_request(cc->argc - 1, cc->argv + 1, + com_setatt_callback, afs_cb_result_handler, cc); +} + static void afs_stat_callback(int fd, const struct osl_object *query) { int *parser_friendly = query->data; diff --git a/alsa_write.c b/alsa_write.c index 88e48b45..a3912c11 100644 --- a/alsa_write.c +++ b/alsa_write.c @@ -361,9 +361,6 @@ void alsa_write_init(struct writer *w) w->post_select = alsa_write_post_select; w->parse_config_or_die = alsa_parse_config_or_die; w->free_config = alsa_free_config; - w->help = (struct ggo_help) { - .short_help = alsa_write_args_info_help, - .detailed_help = alsa_write_args_info_detailed_help - }; + w->help = (struct ggo_help)DEFINE_GGO_HELP(alsa_write); alsa_write_cmdline_parser_free(&dummy); } diff --git a/amp_filter.c b/amp_filter.c index a6bfca25..f62ea8a2 100644 --- a/amp_filter.c +++ b/amp_filter.c @@ -137,8 +137,5 @@ void amp_filter_init(struct filter *f) f->post_select = amp_post_select; f->parse_config = amp_parse_config; f->free_config = amp_free_config; - f->help = (struct ggo_help) { - .short_help = amp_filter_args_info_help, - .detailed_help = amp_filter_args_info_detailed_help - }; + f->help = (struct ggo_help)DEFINE_GGO_HELP(amp_filter); } diff --git a/ao_write.c b/ao_write.c index 9d204ff3..fdae8eea 100644 --- a/ao_write.c +++ b/ao_write.c @@ -371,9 +371,7 @@ void ao_write_init(struct writer *w) w->post_select = aow_post_select; w->parse_config_or_die = aow_parse_config_or_die; w->free_config = aow_free_config; - w->help = (struct ggo_help) { - .short_help = ao_write_args_info_help, - }; + w->help = (struct ggo_help)DEFINE_GGO_HELP(ao_write); /* create detailed help containing all supported drivers/options */ for (i = 0; ao_write_args_info_detailed_help[i]; i++) ; /* nothing */ diff --git a/attribute.c b/attribute.c index 96b54c99..867e24b9 100644 --- a/attribute.c +++ b/attribute.c @@ -212,85 +212,10 @@ int com_lsatt(struct command_context *cc) if (ret < 0) send_strerror(cc, -ret); else if (ret == 0 && cc->argc > 1) - ret = cc->use_sideband? - send_sb_va(&cc->scc, SBD_ERROR_LOG, "no matches\n") - : - sc_send_va_buffer(&cc->scc, "no matches\n"); + ret = send_sb_va(&cc->scc, SBD_ERROR_LOG, "no matches\n"); return ret; } -static void com_setatt_callback(__a_unused int fd, const struct osl_object *query) -{ - char *p; - uint64_t add_mask = 0, del_mask = 0; - int ret; - size_t len; - struct osl_object obj; - struct osl_row *row; - - for (p = query->data; p < (char *)query->data + query->size; p += len + 1) { - char c; - - len = strlen(p); - ret = -E_ATTR_SYNTAX; - if (!*p) - goto out; - c = p[len - 1]; - if (c != '+' && c != '-') - break; - p[len - 1] = '\0'; - obj.data = p; - obj.size = len + 1; - ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row)); - if (ret < 0) - goto out; - ret = osl(osl_get_object(attribute_table, row, ATTCOL_BITNUM, - &obj)); - if (ret < 0) - goto out; - if (c == '+') - add_mask |= (1UL << *(unsigned char *)obj.data); - else - del_mask |= (1UL << *(unsigned char *)obj.data); - } - ret = -E_ATTR_SYNTAX; - if (!add_mask && !del_mask) - goto out; - PARA_DEBUG_LOG("masks: %llx:%llx\n",(long long unsigned)add_mask, - (long long unsigned)del_mask); - for (; p < (char *)query->data + query->size; p += len + 1) { /* TODO: fnmatch */ - struct afs_info old_afsi, new_afsi; - struct afsi_change_event_data aced = {.old_afsi = &old_afsi}; - - len = strlen(p); - ret = aft_get_row_of_path(p, &aced.aft_row); - if (ret < 0) - goto out; - ret = get_afsi_object_of_row(aced.aft_row, &obj); - if (ret < 0) - goto out; - ret = load_afsi(&old_afsi, &obj); - if (ret < 0) - goto out; - new_afsi = old_afsi; - new_afsi.attributes |= add_mask; - new_afsi.attributes &= ~del_mask; - save_afsi(&new_afsi, &obj); /* in-place update */ - afs_event(AFSI_CHANGE, NULL, &aced); - } -out: - if (ret < 0) - PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); -} - -int com_setatt(struct command_context *cc) -{ - if (cc->argc < 3) - return -E_ATTR_SYNTAX; - return send_standard_callback_request(cc->argc - 1, cc->argv + 1, - com_setatt_callback, NULL, NULL); -} - struct addatt_event_data { const char *name; unsigned char bitnum; diff --git a/audioc.c b/audioc.c index 74fb11cb..0edab366 100644 --- a/audioc.c +++ b/audioc.c @@ -17,6 +17,7 @@ #include "net.h" #include "string.h" #include "fd.h" +#include "ggo.h" #include "version.h" INIT_AUDIOC_ERRLISTS; @@ -42,6 +43,25 @@ static char *concat_args(unsigned argc, char * const *argv) return buf; } +static int connect_audiod(const char *sname, char *args) +{ + int fd = -1, ret; + + ret = connect_local_socket(sname); + if (ret < 0) + goto fail; + fd = ret; + ret = send_cred_buffer(fd, args); + if (ret < 0) + goto fail; + return fd; +fail: + PARA_ERROR_LOG("could not connect %s\n", sname); + if (fd >= 0) + close(fd); + return ret; +} + #ifdef HAVE_READLINE #include "list.h" #include "sched.h" @@ -146,8 +166,6 @@ static int audioc_i9e_line_handler(char *line) { char *args = NULL; int ret; - if (!line || !*line) - return 0; PARA_DEBUG_LOG("line: %s\n", line); ret = create_argv(line, " ", &conf.inputs); @@ -156,15 +174,14 @@ static int audioc_i9e_line_handler(char *line) conf.inputs_num = ret; args = concat_args(conf.inputs_num, conf.inputs); free_argv(conf.inputs); + if (!args) + return 0; conf.inputs_num = 0; /* required for audioc_cmdline_parser_free() */ - ret = connect_local_socket(socket_name); + ret = connect_audiod(socket_name, args); if (ret < 0) goto out; at->fd = ret; ret = mark_fd_nonblocking(at->fd); - if (ret < 0) - goto close; - ret = send_cred_buffer(at->fd, args); if (ret < 0) goto close; free(args); @@ -194,7 +211,7 @@ __noreturn static void interactive_session(void) .loglevel = loglevel, .completers = audiod_completers, }; - PARA_NOTICE_LOG("\n%s\n", VERSION_TEXT("audioc")); + PARA_NOTICE_LOG("\n%s\n", version_text("audioc")); if (conf.history_file_given) history_file = para_strdup(conf.history_file_arg); else { @@ -263,6 +280,15 @@ static char *configfile_exists(void) return NULL; } +__noreturn static void print_help_and_die(void) +{ + struct ggo_help h = DEFINE_GGO_HELP(audioc); + bool d = conf.detailed_help_given; + + ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS); + exit(0); +} + /** * The client program to connect to para_audiod. * @@ -281,29 +307,29 @@ static char *configfile_exists(void) */ int main(int argc, char *argv[]) { - int ret = -E_AUDIOC_SYNTAX, fd; + int ret, fd; char *cf, *buf = NULL, *args = NULL; size_t bufsize; - if (audioc_cmdline_parser(argc, argv, &conf)) - goto out; - HANDLE_VERSION_FLAG("audioc", conf); + audioc_cmdline_parser(argc, argv, &conf); + loglevel = get_loglevel_by_name(conf.loglevel_arg); + version_handle_flag("audioc", conf.version_given); + if (conf.help_given || conf.detailed_help_given) + print_help_and_die(); cf = configfile_exists(); if (cf) { struct audioc_cmdline_parser_params params = { .override = 0, .initialize = 0, .check_required = 0, - .check_ambiguity = 0 + .check_ambiguity = 0, + .print_errors = 1, + }; - ret = audioc_cmdline_parser_config_file(cf, &conf, ¶ms); + audioc_cmdline_parser_config_file(cf, &conf, ¶ms); free(cf); - if (ret) { - fprintf(stderr, "parse error in config file\n"); - exit(EXIT_FAILURE); - } + loglevel = get_loglevel_by_name(conf.loglevel_arg); } - loglevel = get_loglevel_by_name(conf.loglevel_arg); if (conf.socket_given) socket_name = para_strdup(conf.socket_arg); else { @@ -320,21 +346,16 @@ int main(int argc, char *argv[]) interactive_session(); args = concat_args(conf.inputs_num, conf.inputs); - ret = connect_local_socket(socket_name); + ret = connect_audiod(socket_name, args); free(socket_name); - if (ret < 0) { - PARA_EMERG_LOG("failed to connect to local socket\n"); + if (ret < 0) goto out; - } fd = ret; - ret = send_cred_buffer(fd, args); + ret = mark_fd_blocking(STDOUT_FILENO); if (ret < 0) goto out; bufsize = conf.bufsize_arg; buf = para_malloc(bufsize); - ret = mark_fd_blocking(STDOUT_FILENO); - if (ret < 0) - goto out; do { size_t n = ret = recv_bin_buffer(fd, buf, bufsize); if (ret <= 0) diff --git a/audiod.c b/audiod.c index d2c66f2b..611e72d7 100644 --- a/audiod.c +++ b/audiod.c @@ -343,10 +343,11 @@ static void parse_config_or_die(void) PARA_EMERG_LOG("can not read config file %s\n", config_file); goto err; } - if (ret) + if (ret) { audiod_cmdline_parser_config_file(config_file, &conf, ¶ms); + daemon_set_loglevel(conf.loglevel_arg); + } free(config_file); - daemon_set_loglevel(conf.loglevel_arg); return; err: free(config_file); @@ -427,7 +428,7 @@ static void close_filters(struct slot_info *s) struct audio_format_info *a = afi + s->format; if (a->num_filters == 0) return; - for (i = 0; i < a->num_filters; i++) { + for (i = a->num_filters - 1; i >= 0; i--) { struct filter_node *fn = s->fns + i; struct filter *f; @@ -1215,7 +1216,7 @@ static int status_post_select(struct sched *s, struct task *t) close_stat_pipe(); goto out; } - if (st->ct->status != CL_RECEIVING) + if (st->ct->status != CL_EXECUTING) goto out; ret = btr_node_status(st->btrn, st->min_iqs, BTR_NT_LEAF); if (ret <= 0) { @@ -1300,18 +1301,17 @@ static void set_initial_status(void) __noreturn static void print_help_and_die(void) { - int d = conf.detailed_help_given; - const char **p = d? audiod_args_info_detailed_help - : audiod_args_info_help; - - printf_or_die("%s\n\n", AUDIOD_CMDLINE_PARSER_PACKAGE "-" - AUDIOD_CMDLINE_PARSER_VERSION); - printf_or_die("%s\n\n", audiod_args_info_usage); - for (; *p; p++) - printf_or_die("%s\n", *p); - print_receiver_helps(d); - print_filter_helps(d); - print_writer_helps(d); + struct ggo_help h = DEFINE_GGO_HELP(audiod); + bool d = conf.detailed_help_given; + unsigned flags; + + flags = d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS; + ggo_print_help(&h, flags); + + flags = d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS; + print_receiver_helps(flags); + print_filter_helps(flags); + print_writer_helps(flags); exit(0); } @@ -1350,9 +1350,9 @@ int main(int argc, char *argv[]) }; valid_fd_012(); - if (audiod_cmdline_parser_ext(argc, argv, &conf, ¶ms)) - exit(EXIT_FAILURE); - HANDLE_VERSION_FLAG("audiod", conf); + audiod_cmdline_parser_ext(argc, argv, &conf, ¶ms); + daemon_set_loglevel(conf.loglevel_arg); + version_handle_flag("audiod", conf.version_given); /* init receivers/filters/writers early to make help work */ recv_init(); filter_init(); diff --git a/audiod_command.c b/audiod_command.c index 2f3726fa..b49d659e 100644 --- a/audiod_command.c +++ b/audiod_command.c @@ -442,7 +442,7 @@ int handle_connect(int accept_fd, fd_set *rfds) if (ret < 0) goto out; ret = create_argv(buf, "\n", &argv); - if (ret < 0) + if (ret <= 0) goto out; argc = ret; //PARA_INFO_LOG("argv[0]: %s, argc = %d\n", argv[0], argc); diff --git a/blob.c b/blob.c index cd571d74..373d8d00 100644 --- a/blob.c +++ b/blob.c @@ -311,12 +311,12 @@ static int com_rmblob(callback_function *f, struct command_context *cc) afs_cb_result_handler, cc); } -static void com_addblob_callback(struct osl_table *table, __a_unused int fd, +static void com_addblob_callback(struct osl_table *table, int fd, const struct osl_object *query) { struct osl_object objs[NUM_BLOB_COLUMNS]; - char *name = query->data; - size_t name_len = strlen(name) + 1; + char *name = query->data, *msg; + size_t name_len = strlen(name) + 1, msg_len; uint32_t id; unsigned num_rows; int ret; @@ -344,6 +344,10 @@ static void com_addblob_callback(struct osl_table *table, __a_unused int fd, if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) goto out; if (ret >= 0) { /* we already have a blob with this name */ + ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj)); + if (ret < 0) + goto out; + id = *(uint32_t *)obj.data; obj.data = name + name_len; obj.size = query->size - name_len; ret = osl(osl_update_object(table, row, BLOBCOL_DEF, &obj)); @@ -377,81 +381,88 @@ static void com_addblob_callback(struct osl_table *table, __a_unused int fd, afs_event(BLOB_ADD, NULL, table); out: if (ret < 0) - PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); + msg_len = xasprintf(&msg, "could not add %s: %s\n", name, + para_strerror(-ret)); + else + msg_len = xasprintf(&msg, "added %s as id %u\n", name, id); + pass_buffer_as_shm(fd, SBD_OUTPUT, msg, msg_len); + free(msg); } -/* - * write input from fd to dynamically allocated buffer, - * but maximal max_size byte. - */ -static int fd2buf(struct stream_cipher_context *scc, unsigned max_size, struct osl_object *obj) +/* Write input from fd to dynamically allocated buffer, but maximal 10M. */ +static int fd2buf(struct stream_cipher_context *scc, struct osl_object *obj) { - const size_t chunk_size = 1024; - size_t size = 2048, received = 0; + size_t max_size = 10 * 1024 * 1024; int ret; - char *buf = para_malloc(size); + struct iovec iov; - for (;;) { - ret = sc_recv_bin_buffer(scc, buf + received, chunk_size); - if (ret <= 0) - break; - received += ret; - if (received + chunk_size >= size) { - size *= 2; - ret = -E_INPUT_TOO_LARGE; - if (size > max_size) - break; - buf = para_realloc(buf, size); - } + obj->data = NULL; + obj->size = 0; +again: + do { + ret = recv_sb(scc, SBD_BLOB_DATA, max_size, &iov); + } while (ret == 0); + + if (ret < 0) { + free(obj->data); + obj->data = NULL; + obj->size = 0; + return ret; } - obj->data = buf; - obj->size = received; - if (ret < 0) - free(buf); - return ret; + if (iov.iov_len == 0) /* end of blob */ + return 1; + if (!obj->data) { + obj->data = iov.iov_base; + obj->size = iov.iov_len; + } else { + obj->data = para_realloc(obj->data, obj->size + iov.iov_len); + memcpy(obj->data + obj->size, iov.iov_base, iov.iov_len); + obj->size += iov.iov_len; + free(iov.iov_base); + max_size -= iov.iov_len; + } + goto again; + return 1; } /* * Read data from a file descriptor, and send it to the afs process. * - * \param scc crypt context containing the file descriptor to read data from. + * \param cc Contains the file descriptor to read data from. * \param arg_obj Pointer to the arguments to \a f. * \param f The callback function. - * \param max_len Don't read more than that many bytes from stdin. * \param result_handler See \ref send_callback_request. * \param private_result_data See \ref send_callback_request. * - * This function is used by commands that wish to let para_server store - * arbitrary data specified by the user (for instance the add_blob family of - * commands). First, at most \a max_len bytes are read and decrypted from the - * file descriptor given by \a scc. The result is concatenated with the buffer - * given by \a arg_obj, and the combined buffer is made available to the afs - * process via the callback method. See \ref send_callback_request for details. + * This function is used by the addblob commands that instruct para_server to + * store arbitrary data in a blob table. Input data is read and decrypted from + * the file descriptor given by \a cc. This data is concatenated with the + * buffer given by \a arg_obj, and the combined buffer is made available to the + * afs process via the callback method. See \ref send_callback_request for + * details. * * \return Negative on errors, the return value of the underlying call to * send_callback_request() otherwise. */ static int stdin_command(struct command_context *cc, struct osl_object *arg_obj, - callback_function *f, unsigned max_len, - callback_result_handler *result_handler, + callback_function *f, callback_result_handler *result_handler, void *private_result_data) { struct osl_object query, stdin_obj; int ret; - if (cc->use_sideband) - ret = send_sb(&cc->scc, NULL, 0, SBD_AWAITING_DATA, false); - else - ret = sc_send_buffer(&cc->scc, AWAITING_DATA_MSG); + ret = send_sb(&cc->scc, NULL, 0, SBD_AWAITING_DATA, false); if (ret < 0) return ret; - ret = fd2buf(&cc->scc, max_len, &stdin_obj); + ret = fd2buf(&cc->scc, &stdin_obj); if (ret < 0) return ret; query.size = arg_obj->size + stdin_obj.size; query.data = para_malloc(query.size); memcpy(query.data, arg_obj->data, arg_obj->size); - memcpy((char *)query.data + arg_obj->size, stdin_obj.data, stdin_obj.size); + if (stdin_obj.size > 0) + memcpy((char *)query.data + arg_obj->size, stdin_obj.data, + stdin_obj.size); free(stdin_obj.data); ret = send_callback_request(f, &query, result_handler, private_result_data); free(query.data); @@ -468,7 +479,7 @@ static int com_addblob(callback_function *f, struct command_context *cc) return -E_BLOB_SYNTAX; arg_obj.size = strlen(cc->argv[1]) + 1; arg_obj.data = (char *)cc->argv[1]; - return stdin_command(cc, &arg_obj, f, 10 * 1024 * 1024, NULL, NULL); + return stdin_command(cc, &arg_obj, f, afs_cb_result_handler, cc); } /* FIXME: Print output to client, not to log file */ diff --git a/buffer_tree.c b/buffer_tree.c index f0a7b224..fd0a59ca 100644 --- a/buffer_tree.c +++ b/buffer_tree.c @@ -1230,6 +1230,16 @@ void btr_get_node_start(struct btr_node *btrn, struct timeval *tv) *tv = btrn->start; } +/** + * Get the parent node of a buffer tree node. + * + * \param btrn The node whose parent should be returned. + * + * \a btrn must not be \p NULL. + * + * \return The parent of \a btrn, or \p NULL if \a btrn is the + * root node of the buffer tree. + */ struct btr_node *btr_parent(struct btr_node *btrn) { return btrn->parent; diff --git a/client.c b/client.c index 90dc432b..873edc16 100644 --- a/client.c +++ b/client.c @@ -445,16 +445,14 @@ static int client_i9e_line_handler(char *line) int ret; client_disconnect(ct); - if (!line || !*line) - return 0; - PARA_DEBUG_LOG("line handler: %s\n", line); + PARA_DEBUG_LOG("line: %s\n", line); ret = make_client_argv(line); - if (ret < 0) + if (ret <= 0) return ret; ret = client_connect(ct, &sched, NULL, NULL); if (ret < 0) return ret; - i9e_attach_to_stdout(ct->btrn); + i9e_attach_to_stdout(ct->btrn[0]); return 1; } @@ -477,7 +475,7 @@ __noreturn static void interactive_session(void) .completers = completers, }; - PARA_NOTICE_LOG("\n%s\n", VERSION_TEXT("client")); + PARA_NOTICE_LOG("\n%s\n", version_text("client")); if (ct->conf.history_file_given) history_file = para_strdup(ct->conf.history_file_arg); else { @@ -530,26 +528,37 @@ __noreturn static void print_completions(void) #endif /* HAVE_READLINE */ -static int supervisor_post_select(struct sched *s, __a_unused struct task *t) +struct supervisor_task { + bool stdout_task_started; + struct task task; +}; + +static int supervisor_post_select(struct sched *s, struct task *t) { + struct supervisor_task *svt = container_of(t, struct supervisor_task, + task); + if (ct->task.error < 0) return ct->task.error; + if (!svt->stdout_task_started && ct->status == CL_EXECUTING) { + stdout_set_defaults(&sot); + register_task(s, &sot.task); + svt->stdout_task_started = true; + return 1; + } if (ct->status == CL_SENDING) { stdin_set_defaults(&sit); register_task(s, &sit.task); return -E_TASK_STARTED; } - if (ct->status == CL_RECEIVING) { - stdout_set_defaults(&sot); - register_task(s, &sot.task); - return -E_TASK_STARTED; - } return 0; } -static struct task svt = { - .post_select = supervisor_post_select, - .status = "supervisor task" +static struct supervisor_task supervisor_task = { + .task = { + .post_select = supervisor_post_select, + .status = "supervisor task" + } }; /** @@ -597,8 +606,8 @@ int main(int argc, char *argv[]) if (ret < 0) goto out; sot.btrn = btr_new_node(&(struct btr_node_description) - EMBRACE(.name = "stdout", .parent = ct->btrn)); - register_task(&sched, &svt); + EMBRACE(.name = "stdout", .parent = ct->btrn[0])); + register_task(&sched, &supervisor_task.task); ret = schedule(&sched); if (ret >= 0 && ct->task.error < 0) { switch(ct->task.error) { diff --git a/client.h b/client.h index 92e14b15..6e438f7e 100644 --- a/client.h +++ b/client.h @@ -20,12 +20,10 @@ enum { CL_SENT_CH_RESPONSE, /** Server accepts this authentication. */ CL_RECEIVED_PROCEED, - /** Client sends the command. */ - CL_SENT_COMMAND, - /** Server expects data. */ + /** Command is executing. */ + CL_EXECUTING, + /** Server is expecting data (addblob commands only). */ CL_SENDING, - /** Client expects data. */ - CL_RECEIVING, }; /** Data specific to a client task. */ @@ -34,10 +32,10 @@ struct client_task { int status; /** The file descriptor and the session keys. */ struct stream_cipher_context scc; - /** True if this connections uses the sideband API. */ - bool use_sideband; - /** The sideband context, ignored if \a use_sideband is false. */ - struct sb_context *sbc; + /** The sideband contexts for receiving/sending. */ + struct sb_context *sbc[2]; + /** The buffer tree nodes for receiving/sending. */ + struct btr_node *btrn[2]; /** The hash value of the decrypted challenge. */ unsigned char *challenge_hash; /** The configuration (including the command). */ @@ -50,8 +48,6 @@ struct client_task { char *user; /** The client task structure. */ struct task task; - /** The buffer tree node of the client task. */ - struct btr_node *btrn; /** List of features supported by the server. */ char **features; }; diff --git a/client_common.c b/client_common.c index 8ea41922..c19b7121 100644 --- a/client_common.c +++ b/client_common.c @@ -13,7 +13,6 @@ #include "error.h" #include "list.h" #include "sched.h" -#include "client.cmdline.h" #include "crypt.h" #include "net.h" #include "fd.h" @@ -23,16 +22,17 @@ #include "client.h" #include "buffer_tree.h" #include "version.h" +#include "ggo.h" /** The size of the receiving buffer. */ #define CLIENT_BUFSIZE 4000 /** - * Close the connection to para_server and deallocate per-command ressources. + * Close the connection to para_server and deallocate per-command resources. * * \param ct The client task. * - * This frees all ressources of the current command but keeps the configuration + * This frees all resources of the current command but keeps the configuration * in \p ct->conf. * * \sa \ref client_close(). @@ -49,7 +49,8 @@ void client_disconnect(struct client_task *ct) ct->scc.recv = NULL; sc_free(ct->scc.send); ct->scc.send = NULL; - btr_remove_node(&ct->btrn); + btr_remove_node(&ct->btrn[0]); + btr_remove_node(&ct->btrn[1]); } /** @@ -69,7 +70,8 @@ void client_close(struct client_task *ct) free(ct->key_file); client_cmdline_parser_free(&ct->conf); free(ct->challenge_hash); - sb_free(ct->sbc); + sb_free(ct->sbc[0]); + sb_free(ct->sbc[1]); free(ct); } @@ -91,7 +93,6 @@ static void client_pre_select(struct sched *s, struct task *t) { int ret; struct client_task *ct = container_of(t, struct client_task, task); - struct btr_node *btrn = ct->btrn; if (ct->scc.fd < 0) return; @@ -99,7 +100,6 @@ static void client_pre_select(struct sched *s, struct task *t) case CL_CONNECTED: case CL_SENT_AUTH: case CL_SENT_CH_RESPONSE: - case CL_SENT_COMMAND: para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno); return; @@ -109,76 +109,49 @@ static void client_pre_select(struct sched *s, struct task *t) para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno); return; - case CL_RECEIVING: - ret = btr_node_status(btrn, 0, BTR_NT_ROOT); - if (ret != 0) { + case CL_SENDING: + if (ct->btrn[1]) { + ret = btr_node_status(ct->btrn[1], 0, BTR_NT_LEAF); if (ret < 0) sched_min_delay(s); - else - para_fd_set(ct->scc.fd, &s->rfds, - &s->max_fileno); + else if (ret > 0) + para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno); } - return; - case CL_SENDING: - ret = btr_node_status(btrn, 0, BTR_NT_LEAF); - if (ret != 0) { + /* fall though */ + case CL_EXECUTING: + if (ct->btrn[0]) { + ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT); if (ret < 0) sched_min_delay(s); - else - para_fd_set(ct->scc.fd, &s->wfds, - &s->max_fileno); + else if (ret > 0) + para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno); } return; } } -static int client_recv_buffer(struct client_task *ct, fd_set *rfds, - char *buf, size_t sz, size_t *n) -{ - int ret; - - if (ct->status < CL_SENT_CH_RESPONSE) - return read_nonblock(ct->scc.fd, buf, sz, rfds, n); - - *n = 0; - ret = sc_recv_buffer(&ct->scc, buf, sz); - /* - * sc_recv_buffer is used with blocking fds elsewhere, so it - * does not use the nonblock-API. Therefore we need to - * check for EOF and EAGAIN. - */ - if (ret == 0) - return -E_SERVER_EOF; - if (ret == -ERRNO_TO_PARA_ERROR(EAGAIN)) - return 0; - if (ret < 0) - return ret; - *n = ret; - return 0; -} - -static int send_sb(struct client_task *ct, void *buf, size_t numbytes, +static int send_sb(struct client_task *ct, int channel, void *buf, size_t numbytes, enum sb_designator band, bool dont_free) { int ret, fd = ct->scc.fd; struct iovec iov[2]; - if (!ct->sbc) { + if (!ct->sbc[channel]) { struct sb_buffer sbb; sb_transformation trafo = ct->status < CL_RECEIVED_PROCEED? NULL : sc_trafo; sbb = (typeof(sbb))SBB_INIT(band, buf, numbytes); - ct->sbc = sb_new_send(&sbb, dont_free, trafo, ct->scc.send); + ct->sbc[channel] = sb_new_send(&sbb, dont_free, trafo, ct->scc.send); } - ret = sb_get_send_buffers(ct->sbc, iov); + ret = sb_get_send_buffers(ct->sbc[channel], iov); ret = xwritev(fd, iov, ret); if (ret < 0) { - sb_free(ct->sbc); - ct->sbc = NULL; + sb_free(ct->sbc[channel]); + ct->sbc[channel] = NULL; return ret; } - if (sb_sent(ct->sbc, ret)) { - ct->sbc = NULL; + if (sb_sent(ct->sbc[channel], ret)) { + ct->sbc[channel] = NULL; return 1; } return 0; @@ -201,21 +174,21 @@ static int recv_sb(struct client_task *ct, fd_set *rfds, trafo = sc_trafo; trafo_context = ct->scc.recv; } - if (!ct->sbc) - ct->sbc = sb_new_recv(0, trafo, trafo_context); + if (!ct->sbc[0]) + ct->sbc[0] = sb_new_recv(0, trafo, trafo_context); again: - sb_get_recv_buffer(ct->sbc, &iov); + sb_get_recv_buffer(ct->sbc[0], &iov); ret = read_nonblock(ct->scc.fd, iov.iov_base, iov.iov_len, rfds, &n); if (ret < 0) { - sb_free(ct->sbc); - ct->sbc = NULL; + sb_free(ct->sbc[0]); + ct->sbc[0] = NULL; return ret; } if (n == 0) return 0; - if (!sb_received(ct->sbc, n, result)) + if (!sb_received(ct->sbc[0], n, result)) goto again; - ct->sbc = NULL; + ct->sbc[0] = NULL; return 1; } @@ -251,10 +224,14 @@ static int dispatch_sbb(struct client_task *ct, struct sb_buffer *sbb) PARA_DEBUG_LOG("band: %s\n", designator[sbb->band]); switch (sbb->band) { + case SBD_AWAITING_DATA: + ct->status = CL_SENDING; + ret = 1; + goto out; case SBD_OUTPUT: if (iov_valid(&sbb->iov)) btr_add_output(sbb->iov.iov_base, sbb->iov.iov_len, - ct->btrn); + ct->btrn[0]); ret = 1; goto out; case SBD_DEBUG_LOG: @@ -299,8 +276,8 @@ static int send_sb_command(struct client_task *ct) char *command, *p; size_t len = 0; - if (ct->sbc) - return send_sb(ct, NULL, 0, 0, false); + if (ct->sbc[1]) + return send_sb(ct, 0, NULL, 0, 0, false); for (i = 0; i < ct->conf.inputs_num; i++) len += strlen(ct->conf.inputs[i]) + 1; @@ -310,7 +287,7 @@ static int send_sb_command(struct client_task *ct) p += strlen(ct->conf.inputs[i]) + 1; } PARA_DEBUG_LOG("--> %s\n", command); - return send_sb(ct, command, len, SBD_COMMAND, false); + return send_sb(ct, 0, command, len, SBD_COMMAND, false); } /** @@ -329,7 +306,6 @@ static int send_sb_command(struct client_task *ct) static int client_post_select(struct sched *s, struct task *t) { struct client_task *ct = container_of(t, struct client_task, task); - struct btr_node *btrn = ct->btrn; int ret = 0; size_t n; char buf[CLIENT_BUFSIZE]; @@ -341,20 +317,21 @@ static int client_post_select(struct sched *s, struct task *t) return 0; switch (ct->status) { case CL_CONNECTED: /* receive welcome message */ - ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n); + ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &s->rfds, &n); if (ret < 0 || n == 0) goto out; ct->features = parse_features(buf); + if (!has_feature("sideband", ct)) { + PARA_ERROR_LOG("server has no sideband support\n"); + ret = -E_INCOMPAT_FEAT; + goto out; + } ct->status = CL_RECEIVED_WELCOME; return 0; case CL_RECEIVED_WELCOME: /* send auth command */ if (!FD_ISSET(ct->scc.fd, &s->wfds)) return 0; - if (has_feature("sideband", ct)) { - ct->use_sideband = true; - sprintf(buf, AUTH_REQUEST_MSG "%s sideband", ct->user); - } else - sprintf(buf, AUTH_REQUEST_MSG "%s", ct->user); + sprintf(buf, AUTH_REQUEST_MSG "%s sideband", ct->user); PARA_INFO_LOG("--> %s\n", buf); ret = write_buffer(ct->scc.fd, buf); if (ret < 0) @@ -369,35 +346,23 @@ static int client_post_select(struct sched *s, struct task *t) { /* decrypted challenge/session key buffer */ unsigned char crypt_buf[1024]; - /* the SHA1 of the decrypted challenge */ + struct sb_buffer sbb; - if (ct->use_sideband) { - struct sb_buffer sbb; - ret = recv_sb(ct, &s->rfds, &sbb); - if (ret <= 0) - goto out; - if (sbb.band != SBD_CHALLENGE) { - ret = -E_BAD_BAND; - free(sbb.iov.iov_base); - goto out; - } - n = sbb.iov.iov_len; - PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n); - ret = priv_decrypt(ct->key_file, crypt_buf, - sbb.iov.iov_base, n); + ret = recv_sb(ct, &s->rfds, &sbb); + if (ret <= 0) + goto out; + if (sbb.band != SBD_CHALLENGE) { + ret = -E_BAD_BAND; free(sbb.iov.iov_base); - if (ret < 0) - goto out; - } else { - ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n); - if (ret < 0 || n == 0) - goto out; - PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n); - ret = priv_decrypt(ct->key_file, crypt_buf, - (unsigned char *)buf, n); - if (ret < 0) goto out; } + n = sbb.iov.iov_len; + PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n); + ret = priv_decrypt(ct->key_file, crypt_buf, + sbb.iov.iov_base, n); + free(sbb.iov.iov_base); + if (ret < 0) + goto out; ct->challenge_hash = para_malloc(HASH_SIZE); hash_function((char *)crypt_buf, CHALLENGE_SIZE, ct->challenge_hash); ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN); @@ -409,162 +374,100 @@ static int client_post_select(struct sched *s, struct task *t) return 0; } case CL_RECEIVED_CHALLENGE: - if (ct->use_sideband) { - ret = send_sb(ct, ct->challenge_hash, HASH_SIZE, - SBD_CHALLENGE_RESPONSE, false); - if (ret != 0) - ct->challenge_hash = NULL; - if (ret <= 0) - goto out; - } else { - ret = write_all(ct->scc.fd, (char *)ct->challenge_hash, HASH_SIZE); - if (ret < 0) - goto out; - } + ret = send_sb(ct, 0, ct->challenge_hash, HASH_SIZE, + SBD_CHALLENGE_RESPONSE, false); + if (ret != 0) + ct->challenge_hash = NULL; + if (ret <= 0) + goto out; ct->status = CL_SENT_CH_RESPONSE; goto out; case CL_SENT_CH_RESPONSE: /* read server response */ { - if (ct->use_sideband) { - struct sb_buffer sbb; - ret = recv_sb(ct, &s->rfds, &sbb); - if (ret <= 0) - goto out; - free(sbb.iov.iov_base); - if (sbb.band != SBD_PROCEED) - ret = -E_BAD_BAND; - else - ct->status = CL_RECEIVED_PROCEED; - goto out; - } - ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n); - if (ret < 0 || n == 0) - goto out; - /* check if server has sent "Proceed" message */ - ret = -E_CLIENT_AUTH; - if (n < PROCEED_MSG_LEN) - goto out; - if (!strstr(buf, PROCEED_MSG)) + struct sb_buffer sbb; + ret = recv_sb(ct, &s->rfds, &sbb); + if (ret <= 0) goto out; - ct->status = CL_RECEIVED_PROCEED; - return 0; + free(sbb.iov.iov_base); + if (sbb.band != SBD_PROCEED) + ret = -E_BAD_BAND; + else + ct->status = CL_RECEIVED_PROCEED; + goto out; } case CL_RECEIVED_PROCEED: /* concat args and send command */ { - int i; - char *command = NULL; if (!FD_ISSET(ct->scc.fd, &s->wfds)) return 0; - if (ct->use_sideband) { - ret = send_sb_command(ct); - if (ret <= 0) - goto out; - ct->status = CL_SENT_COMMAND; - return 0; - } - for (i = 0; i < ct->conf.inputs_num; i++) { - char *tmp = command; - command = make_message("%s\n%s", command? - command : "", ct->conf.inputs[i]); - free(tmp); - } - command = para_strcat(command, EOC_MSG "\n"); - PARA_DEBUG_LOG("--> %s\n", command); - ret = sc_send_buffer(&ct->scc, command); - free(command); - if (ret < 0) + ret = send_sb_command(ct); + if (ret <= 0) goto out; - ct->status = CL_SENT_COMMAND; + ct->status = CL_EXECUTING; return 0; } - case CL_SENT_COMMAND: - { - char *buf2; - if (ct->use_sideband) { - struct sb_buffer sbb; - ret = recv_sb(ct, &s->rfds, &sbb); - if (ret <= 0) - goto out; - if (sbb.band == SBD_AWAITING_DATA) { - ct->status = CL_SENDING; - free(sbb.iov.iov_base); - goto out; + case CL_SENDING: + if (ct->btrn[1]) { + char *buf2; + size_t sz; + ret = btr_node_status(ct->btrn[1], 0, BTR_NT_LEAF); + if (ret == -E_BTR_EOF) { + /* empty blob data packet indicates EOF */ + PARA_INFO_LOG("blob sent\n"); + ret = send_sb(ct, 1, NULL, 0, SBD_BLOB_DATA, true); + if (ret >= 0) + ret = -E_BTR_EOF; } - ct->status = CL_RECEIVING; - ret = dispatch_sbb(ct, &sbb); - goto out; - } - /* can not use "buf" here because we need a malloced buffer */ - buf2 = para_malloc(CLIENT_BUFSIZE); - ret = client_recv_buffer(ct, &s->rfds, buf2, CLIENT_BUFSIZE, &n); - if (n > 0) { - if (strstr(buf2, AWAITING_DATA_MSG)) { - free(buf2); - ct->status = CL_SENDING; - return 0; + if (ret < 0) + goto close1; + if (ret > 0 && FD_ISSET(ct->scc.fd, &s->wfds)) { + sz = btr_next_buffer(ct->btrn[1], &buf2); + assert(sz); + ret = send_sb(ct, 1, buf2, sz, SBD_BLOB_DATA, true); + if (ret < 0) + goto close1; + if (ret > 0) + btr_consume(ct->btrn[1], sz); } - ct->status = CL_RECEIVING; - btr_add_output(buf2, n, btrn); - } else - free(buf2); - goto out; - } - case CL_SENDING: - { - char *buf2; - size_t sz; - ret = btr_node_status(btrn, 0, BTR_NT_LEAF); - if (ret < 0) - goto out; - if (ret == 0) - return 0; - if (!FD_ISSET(ct->scc.fd, &s->wfds)) - return 0; - sz = btr_next_buffer(btrn, &buf2); - ret = sc_send_bin_buffer(&ct->scc, buf2, sz); - if (ret < 0) - goto out; - btr_consume(btrn, sz); - return 0; } - case CL_RECEIVING: - { - char *buf2; - ret = btr_node_status(btrn, 0, BTR_NT_ROOT); - if (ret < 0) - goto out; - if (ret == 0) - return 0; - /* - * The FD_ISSET() is not strictly necessary, but is allows us - * to skip the malloc below if there is nothing to read anyway. - */ - if (!FD_ISSET(ct->scc.fd, &s->rfds)) - return 0; - if (ct->use_sideband) { - struct sb_buffer sbb; - ret = recv_sb(ct, &s->rfds, &sbb); - if (ret > 0) - ret = dispatch_sbb(ct, &sbb); - goto out; + /* fall though */ + case CL_EXECUTING: + if (ct->btrn[0]) { + ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT); + if (ret < 0) + goto close0; + if (ret > 0 && FD_ISSET(ct->scc.fd, &s->rfds)) { + struct sb_buffer sbb; + ret = recv_sb(ct, &s->rfds, &sbb); + if (ret < 0) + goto close0; + if (ret > 0) { + ret = dispatch_sbb(ct, &sbb); + if (ret < 0) + goto close0; + } + } } - buf2 = para_malloc(CLIENT_BUFSIZE); - ret = client_recv_buffer(ct, &s->rfds, buf2, CLIENT_BUFSIZE, &n); - if (n > 0) { - buf2 = para_realloc(buf2, n); - btr_add_output(buf2, n, btrn); - } else - free(buf2); + ret = 0; goto out; - } } +close1: + PARA_INFO_LOG("channel 1: %s\n", para_strerror(-ret)); + btr_remove_node(&ct->btrn[1]); + if (ct->btrn[0]) + return 0; + goto out; +close0: + PARA_INFO_LOG("channel 0: %s\n", para_strerror(-ret)); + btr_remove_node(&ct->btrn[0]); + if (ct->btrn[1] && ct->status == CL_SENDING) + return 0; out: - if (ret < 0) { - if (!ct->use_sideband && ret != -E_SERVER_EOF && - ret != -E_BTR_EOF && ret != -E_EOF) - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); - btr_remove_node(&ct->btrn); - } + if (ret >= 0) + return 0; + btr_remove_node(&ct->btrn[0]); + btr_remove_node(&ct->btrn[1]); + if (ret != -E_SERVER_CMD_SUCCESS && ret != -E_SERVER_CMD_FAILURE) + PARA_ERROR_LOG("%s\n", para_strerror(-ret)); return ret; } @@ -598,8 +501,10 @@ int client_connect(struct client_task *ct, struct sched *s, if (ret < 0) goto err_out; ct->status = CL_CONNECTED; - ct->btrn = btr_new_node(&(struct btr_node_description) - EMBRACE(.name = "client", .parent = parent, .child = child)); + ct->btrn[0] = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = "client recv", .parent = NULL, .child = child)); + ct->btrn[1] = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = "client send", .parent = parent, .child = NULL)); ct->task.pre_select = client_pre_select; ct->task.post_select = client_post_select; ct->task.error = 0; @@ -612,6 +517,15 @@ err_out: return ret; } +__noreturn static void print_help_and_die(struct client_task *ct) +{ + struct ggo_help h = DEFINE_GGO_HELP(client); + bool d = ct->conf.detailed_help_given; + + ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS); + exit(0); +} + /** * Parse a client configuration. * @@ -642,7 +556,9 @@ int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr, ret = -E_CLIENT_SYNTAX; if (client_cmdline_parser(argc, argv, &ct->conf)) goto out; - HANDLE_VERSION_FLAG("client", ct->conf); + version_handle_flag("client", ct->conf.version_given); + if (ct->conf.help_given || ct->conf.detailed_help_given) + print_help_and_die(ct); ct->config_file = ct->conf.config_file_given? para_strdup(ct->conf.config_file_arg) : diff --git a/command.c b/command.c index ec822c82..4bbf494c 100644 --- a/command.c +++ b/command.c @@ -268,10 +268,7 @@ __printf_3_4 int send_sb_va(struct stream_cipher_context *scc, int band, */ int send_strerror(struct command_context *cc, int err) { - return cc->use_sideband? - send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", para_strerror(err)) - : - sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(err)); + return send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", para_strerror(err)); } /** @@ -338,23 +335,14 @@ static int com_sender(struct command_context *cc) free(msg); msg = tmp; } - if (cc->use_sideband) - return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false); - ret = sc_send_buffer(&cc->scc, msg); - free(msg); - return ret; + return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false); } ret = check_sender_args(cc->argc, cc->argv, &scd); if (ret < 0) { if (scd.sender_num < 0) return ret; msg = senders[scd.sender_num].help(); - if (cc->use_sideband) - return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, - false); - ret = sc_send_buffer(&cc->scc, msg); - free(msg); - return ret; + return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false); } switch (scd.cmd_num) { @@ -395,7 +383,8 @@ static int com_si(struct command_context *cc) free(info); } ut = get_server_uptime_str(now); - ret = xasprintf(&msg, "version: " GIT_VERSION "\n" + ret = xasprintf(&msg, + "version: %s\n" "up: %s\nplayed: %u\n" "server_pid: %d\n" "afs_pid: %d\n" @@ -403,6 +392,7 @@ static int com_si(struct command_context *cc) "current loglevel: %s\n" "supported audio formats: %s\n" "%s", + version_git(), ut, mmd->num_played, (int)getppid(), (int)mmd->afs_pid, @@ -416,11 +406,7 @@ static int com_si(struct command_context *cc) mutex_unlock(mmd_mutex); free(ut); free(sender_info); - if (cc->use_sideband) - return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false); - ret = sc_send_bin_buffer(&cc->scc, msg, ret); - free(msg); - return ret; + return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false); } /* version */ @@ -431,12 +417,8 @@ static int com_version(struct command_context *cc) if (cc->argc != 1) return -E_COMMAND_SYNTAX; - msg = VERSION_TEXT("server") "built: " BUILD_DATE "\n" UNAME_RS - ", " CC_VERSION "\n"; - len = strlen(msg); - if (cc->use_sideband) - return send_sb(&cc->scc, msg, len, SBD_OUTPUT, true); - return sc_send_bin_buffer(&cc->scc, msg, len); + len = xasprintf(&msg, "%s", version_text("server")); + return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false); } #define EMPTY_STATUS_ITEMS \ @@ -474,11 +456,8 @@ static int com_version(struct command_context *cc) */ static unsigned empty_status_items(int parser_friendly, char **result) { - static char *esi; - static unsigned len; - - if (esi) - goto out; + char *esi; + unsigned len; if (parser_friendly) len = xasprintf(&esi, @@ -498,7 +477,6 @@ static unsigned empty_status_items(int parser_friendly, char **result) EMPTY_STATUS_ITEMS #undef ITEM ); -out: *result = esi; return len; } @@ -509,7 +487,7 @@ static int com_stat(struct command_context *cc) { int i, ret; struct misc_meta_data tmp, *nmmd = &tmp; - char *s, *esi = NULL; + char *s; int32_t num = 0; int parser_friendly = 0; @@ -540,21 +518,13 @@ static int com_stat(struct command_context *cc) for (;;) { mmd_dup(nmmd); ret = get_status(nmmd, parser_friendly, &s); - if (cc->use_sideband) - ret = send_sb(&cc->scc, s, ret, SBD_OUTPUT, false); - else { - ret = sc_send_bin_buffer(&cc->scc, s, ret); - free(s); - } + ret = send_sb(&cc->scc, s, ret, SBD_OUTPUT, false); if (ret < 0) goto out; if (nmmd->vss_status_flags & VSS_NEXT) { + char *esi; ret = empty_status_items(parser_friendly, &esi); - if (cc->use_sideband) - ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT, - true); - else - ret = sc_send_bin_buffer(&cc->scc, esi, ret); + ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT, false); if (ret < 0) goto out; } else @@ -568,14 +538,12 @@ static int com_stat(struct command_context *cc) goto out; } out: - free(esi); return ret; } static int send_list_of_commands(struct command_context *cc, struct server_command *cmd, const char *handler) { - int ret; char *msg = NULL; for (; cmd->name; cmd++) { @@ -586,11 +554,7 @@ static int send_list_of_commands(struct command_context *cc, struct server_comma msg = para_strcat(msg, tmp); free(tmp); } - if (cc->use_sideband) - return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false); - ret = sc_send_buffer(&cc->scc, msg); - free(msg); - return ret; + return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false); } /* returns string that must be freed by the caller */ @@ -646,11 +610,7 @@ static int com_help(struct command_context *cc) ); free(perms); free(handler); - if (cc->use_sideband) - return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false); - ret = sc_send_buffer(&cc->scc, buf); - free(buf); - return ret; + return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false); } /* hup */ @@ -812,58 +772,6 @@ static int check_perms(unsigned int perms, struct server_command *cmd_ptr) return (cmd_ptr->perms & perms) < cmd_ptr->perms ? -E_PERM : 0; } -/* - * Parse first string from *cmd and lookup in table of valid commands. - * On error, NULL is returned. - */ -static struct server_command *parse_cmd(const char *cmdstr) -{ - char buf[255]; - int n = 0; - - sscanf(cmdstr, "%200s%n", buf, &n); - if (!n) - return NULL; - buf[n] = '\0'; - return get_cmd_ptr(buf, NULL); -} - -static int read_command(struct stream_cipher_context *scc, char **result) -{ - int ret; - char buf[4096]; - char *command = NULL; - - for (;;) { - size_t numbytes; - char *p; - - ret = sc_recv_buffer(scc, buf, sizeof(buf)); - if (ret < 0) - goto out; - if (!ret) - break; - numbytes = ret; - ret = -E_COMMAND_SYNTAX; - if (command && numbytes + strlen(command) > MAX_COMMAND_LEN) /* DOS */ - goto out; - command = para_strcat(command, buf); - p = strstr(command, EOC_MSG); - if (p) { - *p = '\0'; - break; - } - } - ret = command? 1 : -E_COMMAND_SYNTAX; -out: - if (ret < 0) - free(command); - else - *result = command; - return ret; - -} - static void reset_signals(void) { para_sigaction(SIGCHLD, SIG_IGN); @@ -872,15 +780,14 @@ static void reset_signals(void) para_sigaction(SIGHUP, SIG_DFL); } -static int parse_auth_request(char *buf, int len, struct user **u, - bool *use_sideband) +static int parse_auth_request(char *buf, int len, struct user **u) { int ret; char *p, *username, **features = NULL; size_t auth_rq_len = strlen(AUTH_REQUEST_MSG); + bool sideband_requested = false; *u = NULL; - *use_sideband = false; if (len < auth_rq_len + 2) return -E_AUTH_REQUEST; if (strncmp(buf, AUTH_REQUEST_MSG, auth_rq_len) != 0) @@ -896,15 +803,19 @@ static int parse_auth_request(char *buf, int len, struct user **u, create_argv(p, ",", &features); for (i = 0; features[i]; i++) { if (strcmp(features[i], "sideband") == 0) - *use_sideband = true; + sideband_requested = true; else { ret = -E_BAD_FEATURE; goto out; } } } - PARA_DEBUG_LOG("received auth request for user %s (sideband = %s)\n", - username, *use_sideband? "true" : "false"); + if (sideband_requested == false) { /* sideband is mandatory */ + PARA_ERROR_LOG("client did not request sideband\n"); + ret = -E_BAD_FEATURE; + goto out; + } + PARA_DEBUG_LOG("received auth request for user %s\n", username); *u = lookup_user(username); ret = 1; out: @@ -981,6 +892,7 @@ __noreturn void handle_connect(int fd, const char *peername) char *p, *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */; size_t numbytes; struct command_context cc_struct = {.peer = peername}, *cc = &cc_struct; + struct iovec iov; cc->scc.fd = fd; reset_signals(); @@ -999,7 +911,7 @@ __noreturn void handle_connect(int fd, const char *peername) ret = recv_buffer(fd, buf, HANDSHAKE_BUFSIZE); if (ret < 0) goto net_err; - ret = parse_auth_request(buf, ret, &cc->u, &cc->use_sideband); + ret = parse_auth_request(buf, ret, &cc->u); if (ret < 0) goto net_err; p = buf + strlen(AUTH_REQUEST_MSG); @@ -1023,28 +935,16 @@ __noreturn void handle_connect(int fd, const char *peername) } PARA_DEBUG_LOG("sending %u byte challenge + rc4 keys (%zu bytes)\n", CHALLENGE_SIZE, numbytes); - if (cc->use_sideband) { - struct iovec iov; - ret = send_sb(&cc->scc, buf, numbytes, SBD_CHALLENGE, false); - buf = NULL; - if (ret < 0) - goto net_err; - ret = recv_sb(&cc->scc, SBD_CHALLENGE_RESPONSE, - HANDSHAKE_BUFSIZE, &iov); - if (ret < 0) - goto net_err; - buf = iov.iov_base; - numbytes = iov.iov_len; - } else { - ret = write_all(fd, buf, numbytes); - if (ret < 0) - goto net_err; - /* recv challenge response */ - ret = recv_bin_buffer(fd, buf, HASH_SIZE); - if (ret < 0) - goto net_err; - numbytes = ret; - } + ret = send_sb(&cc->scc, buf, numbytes, SBD_CHALLENGE, false); + buf = NULL; + if (ret < 0) + goto net_err; + ret = recv_sb(&cc->scc, SBD_CHALLENGE_RESPONSE, + HANDSHAKE_BUFSIZE, &iov); + if (ret < 0) + goto net_err; + buf = iov.iov_base; + numbytes = iov.iov_len; PARA_DEBUG_LOG("received %zu bytes challenge response\n", numbytes); ret = -E_BAD_USER; if (!cc->u) @@ -1065,41 +965,16 @@ __noreturn void handle_connect(int fd, const char *peername) /* init stream cipher keys with the second part of the random buffer */ cc->scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN); cc->scc.send = sc_new(rand_buf + CHALLENGE_SIZE + SESSION_KEY_LEN, SESSION_KEY_LEN); - if (cc->use_sideband) - ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false); - else - ret = sc_send_buffer(&cc->scc, PROCEED_MSG); + ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false); if (ret < 0) goto net_err; - if (cc->use_sideband) { - struct iovec iov; - ret = recv_sb(&cc->scc, SBD_COMMAND, MAX_COMMAND_LEN, &iov); - if (ret < 0) - goto net_err; - ret = parse_sb_command(cc, &iov); - if (ret < 0) - goto err_out; - cc->argc = ret; - } else { - ret = read_command(&cc->scc, &command); - if (ret == -E_COMMAND_SYNTAX) - goto err_out; - if (ret < 0) - goto net_err; - ret = -E_BAD_CMD; - cc->cmd = parse_cmd(command); - if (!cc->cmd) - goto err_out; - /* valid command, check permissions */ - ret = check_perms(cc->u->perms, cc->cmd); - if (ret < 0) - goto err_out; - /* valid command and sufficient perms */ - ret = create_argv(command, "\n", &cc->argv); - if (ret < 0) - goto err_out; - cc->argc = ret; - } + ret = recv_sb(&cc->scc, SBD_COMMAND, MAX_COMMAND_LEN, &iov); + if (ret < 0) + goto net_err; + ret = parse_sb_command(cc, &iov); + if (ret < 0) + goto err_out; + cc->argc = ret; PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cc->cmd->name, cc->u->name, peername); ret = cc->cmd->handler(cc); @@ -1110,7 +985,7 @@ __noreturn void handle_connect(int fd, const char *peername) if (ret >= 0) goto out; err_out: - if (send_strerror(cc, -ret) >= 0 && cc->use_sideband) + if (send_strerror(cc, -ret) >= 0) send_sb(&cc->scc, NULL, 0, SBD_EXIT__FAILURE, true); net_err: PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); @@ -1122,7 +997,7 @@ out: mmd->events++; mmd->active_connections--; mutex_unlock(mmd_mutex); - if (ret >= 0 && cc->use_sideband) { + if (ret >= 0) { ret = send_sb(&cc->scc, NULL, 0, SBD_EXIT__SUCCESS, true); if (ret < 0) PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); diff --git a/command.h b/command.h index e4159e6b..6d220207 100644 --- a/command.h +++ b/command.h @@ -14,8 +14,6 @@ struct command_context { struct server_command *cmd; /** File descriptor and crypto keys. */ struct stream_cipher_context scc; - /** Whether to use the sideband API for this command. */ - bool use_sideband; }; /** diff --git a/compress_filter.c b/compress_filter.c index d7162791..0c0200e7 100644 --- a/compress_filter.c +++ b/compress_filter.c @@ -74,18 +74,23 @@ next_buffer: op = para_malloc(length); for (i = 0; i < length / 2; i++) { /* be careful in that heat, my dear */ - int sample = *ip++, adjusted_sample = (PARA_ABS(sample) * - pcd->current_gain) >> gain_shift; - if (adjusted_sample > 32767) { /* clip */ - PARA_NOTICE_LOG("clip: sample: %d, adjusted sample: %d\n", - sample, adjusted_sample); - adjusted_sample = 32767; + int sample = *ip++; + bool neg = false; + + if (sample < 0) { + sample = -sample; + neg = true; + } + sample *= pcd->current_gain; + sample >>= gain_shift; + if (sample > 32767) { /* clip */ + sample = 32767; pcd->current_gain = (3 * pcd->current_gain + (1 << pcd->conf->inertia_arg)) / 4; pcd->peak = 0; - } else - pcd->peak = PARA_MAX(pcd->peak, adjusted_sample); - op[i] = sample >= 0? adjusted_sample : -adjusted_sample; + } else if (sample > pcd->peak) + pcd->peak = sample; + op[i] = neg? -sample : sample; if (++pcd->num_samples & mask) continue; // PARA_DEBUG_LOG("gain: %u, peak: %u\n", pcd->current_gain, @@ -153,8 +158,5 @@ void compress_filter_init(struct filter *f) f->post_select = compress_post_select; f->parse_config = compress_parse_config; f->free_config = compress_free_config; - f->help = (struct ggo_help) { - .short_help = compress_filter_args_info_help, - .detailed_help = compress_filter_args_info_detailed_help - }; + f->help = (struct ggo_help)DEFINE_GGO_HELP(compress_filter); } diff --git a/configure.ac b/configure.ac index 135a0bd9..87e41d71 100644 --- a/configure.ac +++ b/configure.ac @@ -9,7 +9,6 @@ AC_CONFIG_HEADER([config.h]) AC_CONFIG_FILES([Makefile]) AC_DEFUN([add_dot_o],[$(for i in $@; do printf "$i.o "; done)]) -AC_DEFUN([add_para],[$(for i in $@; do printf "para_$i "; done)]) AC_DEFUN([objlist_to_errlist],[$(for i in $@; do printf "DEFINE_ERRLIST($(echo $i| tr 'a-z' 'A-Z'));"; done) [const char **para_errlist[[]]] = {$(for i in $@; do printf "PARA_ERRLIST($(echo $i | tr 'a-z' 'A-Z')), "; done) }]) AC_PATH_PROG(UNAMEPATH, uname, no) if test "$UNAMEPATH" = "no"; then @@ -102,7 +101,7 @@ all_errlist_objs="mp3_afh afh_common net string signal time daemon exec send_common ggo udp_recv color fec fecdec_filter prebuffer_filter bitstream imdct check_wav wma_afh wma_common wmadec_filter buffer_tree crypt_common - gui gui_theme sideband afh_recv play" + gui gui_theme sideband afh_recv play version" executables="recv filter audioc write client afh audiod play" @@ -111,49 +110,83 @@ recv_cmdline_objs="add_cmdline(recv http_recv dccp_recv udp_recv afh_recv)" recv_errlist_objs=" http_recv recv_common recv time string net dccp_recv fd sched stdout ggo udp_recv buffer_tree afh_recv afh_common - wma_afh wma_common mp3_afh + wma_afh wma_common mp3_afh version " recv_ldflags="" filter_cmdline_objs="add_cmdline(filter compress_filter amp_filter prebuffer_filter)" filter_errlist_objs="filter_common wav_filter compress_filter filter string - stdin stdout sched fd amp_filter ggo fecdec_filter fec + stdin stdout sched fd amp_filter ggo fecdec_filter fec version prebuffer_filter time bitstream imdct wma_common wmadec_filter buffer_tree" filter_ldflags="-lm" filters=" compress wav amp fecdec wmadec prebuffer" audioc_cmdline_objs="add_cmdline(audioc)" -audioc_errlist_objs="audioc string net fd" +audioc_errlist_objs=" + audioc + string + net + fd + version + ggo +" audioc_ldflags="" audiod_cmdline_objs="add_cmdline(audiod compress_filter http_recv dccp_recv file_write client amp_filter udp_recv prebuffer_filter)" audiod_errlist_objs="audiod signal string daemon stat net crypt_common sideband time grab_client filter_common wav_filter compress_filter amp_filter http_recv dccp_recv recv_common fd sched write_common file_write audiod_command fecdec_filter - client_common ggo udp_recv color fec prebuffer_filter + client_common ggo udp_recv color fec prebuffer_filter version bitstream imdct wma_common wmadec_filter buffer_tree" audiod_ldflags="-lm" audiod_audio_formats="wma" afh_cmdline_objs="add_cmdline(afh)" -afh_errlist_objs="afh string fd mp3_afh afh_common time wma_afh wma_common" +afh_errlist_objs="afh string fd mp3_afh afh_common time wma_afh wma_common + version ggo" afh_ldflags="" write_cmdline_objs="add_cmdline(write file_write)" write_errlist_objs="write write_common file_write time fd string sched stdin - buffer_tree ggo check_wav" + buffer_tree ggo check_wav version" write_ldflags="" writers=" file" default_writer="FILE_WRITE" client_cmdline_objs="add_cmdline(client)" -client_errlist_objs="client net string fd sched stdin stdout time sideband - client_common buffer_tree crypt_common" +client_errlist_objs=" + client + net + string + fd + sched + stdin + stdout + time + sideband + client_common + buffer_tree + crypt_common + version + ggo +" client_ldflags="" gui_cmdline_objs="add_cmdline(gui)" -gui_errlist_objs="exec signal string stat ringbuffer fd gui gui_theme" +gui_errlist_objs=" + exec + signal + string + stat + ringbuffer + fd + gui + gui_theme + time + version + ggo +" gui_objs="$gui_cmdline_objs $gui_errlist_objs" play_errlist_objs="play fd sched ggo buffer_tree time string net afh_recv afh_common @@ -163,6 +196,7 @@ play_errlist_objs="play fd sched ggo buffer_tree time string net wav_filter compress_filter amp_filter prebuffer_filter fecdec_filter wmadec_filter write_common file_write + version " play_cmdline_objs="add_cmdline(http_recv dccp_recv udp_recv afh_recv compress_filter amp_filter prebuffer_filter file_write play)" play_ldflags="-lm" @@ -290,11 +324,43 @@ else extras="$extras server" executables="$executables server" server_cmdline_objs="add_cmdline(server)" - server_errlist_objs="server afh_common mp3_afh vss command net - string signal time daemon http_send close_on_fork mm - crypt_common ipc dccp_send fd user_list chunk_queue - afs aft mood score attribute blob playlist sched acl - send_common udp_send color fec wma_afh wma_common sideband" + server_errlist_objs=" + server + afh_common + mp3_afh + vss command + net + string + signal + time + daemon + http_send + close_on_fork + mm + crypt_common + ipc dccp_send + fd + user_list + chunk_queue + afs + aft + mood + score + attribute + blob + playlist + sched + acl + send_common + udp_send + color + fec + wma_afh + wma_common + sideband + version + ggo + " all_errlist_objs="$all_errlist_objs server vss command http_send close_on_fork mm ipc dccp_send user_list chunk_queue afs aft mood score attribute blob playlist @@ -462,7 +528,15 @@ AC_MSG_RESULT($have_ucred) if test ${have_ucred} = yes; then AC_DEFINE(HAVE_UCRED, 1, define to 1 you have struct ucred) fi - +########################################################################### gengetopt +echo 'option "z" z "" flag off' | $gengetopt --file-name conftest-ggo && +AC_CHECK_DECL( + [gengetopt_args_info_description], + [ggo_descriptions_declared=yes], + [ggo_descriptions_declared=no], + [#include "conftest-ggo.h"] +) +AC_SUBST(ggo_descriptions_declared) ########################################################################### curses have_curses="yes" OLD_CPPFLAGS="$CPPFLAGS" @@ -554,7 +628,7 @@ if test ${have_core_audio} = yes; then default_writer="OSX_WRITE" AC_DEFINE(HAVE_CORE_AUDIO, 1, define to 1 on Mac Os X) fi -########################################################### ogg/vorbis/speex +####################################################### ogg/vorbis/speex/opus have_ogg="yes" OLD_CPPFLAGS="$CPPFLAGS" OLD_LD_FLAGS="$LDFLAGS" @@ -571,6 +645,10 @@ AC_ARG_WITH(speex_headers, [AS_HELP_STRING(--with-speex-headers=dir, [look for speex headers also in dir])]) AC_ARG_WITH(speex_libs, [AS_HELP_STRING(--with-speex-libs=dir, [look for speex libs also in dir])]) +AC_ARG_WITH(opus_headers, [AS_HELP_STRING(--with-opus-headers=dir, + [look for opus headers also in dir])]) +AC_ARG_WITH(opus_libs, [AS_HELP_STRING(--with-opus-libs=dir, + [look for opus libs also in dir])]) if test -n "$with_ogg_headers"; then ogg_cppflags="-I$with_ogg_headers" @@ -585,6 +663,7 @@ AC_CHECK_LIB([ogg], [ogg_stream_init], [], [ have_ogg="no" ]) have_vorbis="yes" have_speex="yes" +have_opus="yes" if test "$have_ogg" = "yes"; then # vorbis if test -n "$with_vorbis_headers"; then @@ -609,14 +688,29 @@ if test "$have_ogg" = "yes"; then fi AC_CHECK_LIB([speex], [speex_decoder_init], [], [ have_speex="no" ]) AC_CHECK_HEADERS([speex/speex.h], [], [ have_speex="no" ]) + + # opus + if test -n "$with_opus_headers"; then + opus_cppflags="-I$with_opus_headers" + CPPFLAGS="$CPPFLAGS $opus_cppflags" + fi + if test -n "$with_opus_libs"; then + speex_libs="-L$with_opus_libs" + LDFLAGS="$LDFLAGS $opus_libs" + fi + AC_CHECK_LIB([opus], [opus_multistream_decode], [], [ have_opus="no" ]) + AC_CHECK_HEADERS([opus/opus.h], [], [ have_opus="no" ]) else - AC_MSG_WARN([vorbis/speex depend on libogg, which was not detected]) + AC_MSG_WARN([vorbis/speex/opus depend on libogg, which was not detected]) have_vorbis="no" have_speex="no" + have_opus="no" fi msg="support in para_server/para_filter/para_afh" -if test "$have_vorbis" = "yes" || test "$have_speex" = "yes"; then +if test "$have_vorbis" = "yes" || \ + test "$have_speex" = "yes" || \ + test "$have_opus" = "yes"; then AC_SUBST(ogg_cppflags) ogg_libs="$ogg_libs -logg" if test "$OSTYPE" = "Darwin"; then @@ -682,6 +776,30 @@ if test "$have_speex" = "yes"; then else AC_MSG_WARN([no ogg/speex $msg]) fi +if test "$have_opus" = "yes"; then + all_errlist_objs="$all_errlist_objs opusdec_filter opus_afh opus_common" + AC_DEFINE(HAVE_OPUS, 1, define to 1 to turn on ogg/opus support) + filters="$filters opusdec" + opus_libs="-lopus" + server_ldflags="$server_ldflags $opus_libs" + filter_ldflags="$filter_ldflags $opus_libs" + audiod_ldflags="$audiod_ldflags $opus_libs" + afh_ldflags="$afh_ldflags $opus_libs" + play_ldflags="$play_ldflags $opus_libs" + recv_ldflags="$recv_ldflags $opus_libs" + + server_errlist_objs="$server_errlist_objs opus_afh opus_common" + filter_errlist_objs="$filter_errlist_objs opusdec_filter opus_common" + audiod_errlist_objs="$audiod_errlist_objs opusdec_filter opus_common" + afh_errlist_objs="$afh_errlist_objs opus_afh opus_common" + play_errlist_objs="$play_errlist_objs opusdec_filter opus_afh opus_common" + recv_errlist_objs="$recv_errlist_objs opus_afh opus_common" + + audiod_audio_formats="$audiod_audio_formats opus" + server_audio_formats="$server_audio_formats opus" +else + AC_MSG_WARN([no ogg/opus $msg]) +fi CPPFLAGS="$OLD_CPPFLAGS" LDFLAGS="$OLD_LDFLAGS" LIBS="$OLD_LIBS" @@ -957,7 +1075,7 @@ if test -n "$mixers"; then extras="$extras fade" executables="$executables fade" all_errlist_objs="$all_errlist_objs fade" - fade_errlist_objs="$fade_errlist_objs fade exec string fd" + fade_errlist_objs="$fade_errlist_objs fade exec string fd version ggo" fade_cmdline_objs="add_cmdline(fade)" fade_objs="$fade_cmdline_objs $fade_errlist_objs" AC_SUBST(fade_objs, add_dot_o($fade_objs)) @@ -1206,7 +1324,7 @@ AC_DEFINE_UNQUOTED(STATUS_ITEM_ARRAY, [$result], AC_DEFINE_UNQUOTED(AUDIO_FORMAT_HANDLERS, "$audio_format_handlers", [formats supported by para_server and para_afh]) -AC_SUBST(executables, add_para($executables)) +AC_SUBST(executables) recv_objs="$recv_cmdline_objs $recv_errlist_objs" filter_objs="$filter_cmdline_objs $filter_errlist_objs" diff --git a/crypt.c b/crypt.c index 5c5333a9..b8577dfc 100644 --- a/crypt.c +++ b/crypt.c @@ -283,40 +283,6 @@ void sc_free(struct stream_cipher *sc) */ #define RC4_ALIGN 8 -int sc_send_bin_buffer(struct stream_cipher_context *scc, char *buf, - size_t len) -{ - int ret; - unsigned char *tmp; - static unsigned char remainder[RC4_ALIGN]; - size_t l1 = ROUND_DOWN(len, RC4_ALIGN), l2 = ROUND_UP(len, RC4_ALIGN); - - assert(len); - tmp = para_malloc(l2); - RC4(&scc->send->key, l1, (const unsigned char *)buf, tmp); - if (len > l1) { - memcpy(remainder, buf + l1, len - l1); - RC4(&scc->send->key, len - l1, remainder, tmp + l1); - } - ret = xwrite(scc->fd, (char *)tmp, len); - free(tmp); - return ret; -} - -int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf, - size_t size) -{ - unsigned char *tmp = para_malloc(ROUND_UP(size, RC4_ALIGN)); - ssize_t ret = recv(scc->fd, tmp, size, 0); - - if (ret > 0) - RC4(&scc->recv->key, ret, tmp, (unsigned char *)buf); - else if (ret < 0) - ret = -ERRNO_TO_PARA_ERROR(errno); - free(tmp); - return ret; -} - void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst) { size_t len = src->iov_len, l1, l2; diff --git a/crypt.h b/crypt.h index 77806af6..1406197d 100644 --- a/crypt.h +++ b/crypt.h @@ -165,75 +165,9 @@ _static_inline_ void sc_trafo(struct iovec *src, struct iovec *dst, */ void sc_free(struct stream_cipher *sc); -/** - * Encrypt and send a buffer. - * - * \param scc The context. - * \param buf The buffer to send. - * \param len The size of \a buf in bytes. - * - * \return The return value of the underyling call to write_all(). - * - * \sa \ref write_all(), RC4(3). - */ -int sc_send_bin_buffer(struct stream_cipher_context *scc, char *buf, - size_t len); - -/** - * Encrypt and send a \p NULL-terminated buffer. - * - * \param scc The context. - * \param buf The buffer to send. - * - * \return The return value of the underyling call to sc_send_bin_buffer(). - */ -int sc_send_buffer(struct stream_cipher_context *scc, char *buf); - -/** - * Format, encrypt and send a buffer. - * - * \param scc The context. - * \param fmt A format string. - * - * \return The return value of the underyling call to sc_send_buffer(). - */ -__printf_2_3 int sc_send_va_buffer(struct stream_cipher_context *scc, - const char *fmt, ...); - -/** - * Receive a buffer and decrypt it. - * - * \param scc The context. - * \param buf The buffer to write the decrypted data to. - * \param size The size of \a buf. - * - * \return The number of bytes received on success, negative on errors, zero if - * the peer has performed an orderly shutdown. - * - * \sa recv(2), RC4(3). - */ -int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf, - size_t size); - -/** - * Receive a buffer, decrypt it and write terminating NULL byte. - * - * \param scc The context. - * \param buf The buffer to write the decrypted data to. - * \param size The size of \a buf. - * - * Read at most \a size - 1 bytes from file descriptor given by \a scc, decrypt - * the received data and write a NULL byte at the end of the decrypted data. - * - * \return The return value of the underlying call to \ref - * sc_recv_bin_buffer(). - */ -int sc_recv_buffer(struct stream_cipher_context *scc, char *buf, size_t size); - /** Size of the hash value in bytes. */ #define HASH_SIZE 20 - /** * Compute the hash of the given input data. * diff --git a/crypt_common.c b/crypt_common.c index cd9500ab..6c71d7e3 100644 --- a/crypt_common.c +++ b/crypt_common.c @@ -321,41 +321,3 @@ int hash_compare(unsigned char *h1, unsigned char *h2) } return 0; } - -int sc_recv_buffer(struct stream_cipher_context *scc, char *buf, size_t size) -{ - int n; - - assert(size); - n = sc_recv_bin_buffer(scc, buf, size - 1); - if (n >= 0) - buf[n] = '\0'; - else - *buf = '\0'; - return n; -} - -int sc_send_buffer(struct stream_cipher_context *scc, char *buf) -{ - size_t len = strlen(buf); - int ret = sc_send_bin_buffer(scc, buf, len); - - if (ret < 0 || ret == len) - return ret; - return -E_SHORT_WRITE; -} - -__printf_2_3 int sc_send_va_buffer(struct stream_cipher_context *scc, - const char *fmt, ...) -{ - char *msg; - int ret; - va_list ap; - - va_start(ap, fmt); - ret = xvasprintf(&msg, fmt, ap); - va_end(ap); - ret = sc_send_bin_buffer(scc, msg, ret); - free(msg); - return ret; -} diff --git a/dccp_recv.c b/dccp_recv.c index 1f9df189..c751f2f7 100644 --- a/dccp_recv.c +++ b/dccp_recv.c @@ -107,11 +107,10 @@ static void *dccp_recv_parse_config(int argc, char **argv) { struct dccp_recv_args_info *tmp = para_calloc(sizeof(*tmp)); - if (!dccp_recv_cmdline_parser(argc, argv, tmp) && - dccp_recv_ccid_support_check(tmp)) - return tmp; - free(tmp); - return NULL; + dccp_recv_cmdline_parser(argc, argv, tmp); + if (!dccp_recv_ccid_support_check(tmp)) + exit(EXIT_FAILURE); + return tmp; } static void dccp_recv_pre_select(struct sched *s, struct task *t) @@ -181,9 +180,6 @@ void dccp_recv_init(struct receiver *r) r->post_select = dccp_recv_post_select; r->parse_config = dccp_recv_parse_config; r->free_config = dccp_recv_free_config; - r->help = (struct ggo_help) { - .short_help = dccp_recv_args_info_help, - .detailed_help = dccp_recv_args_info_detailed_help - }; + r->help = (struct ggo_help)DEFINE_GGO_HELP(dccp_recv); dccp_recv_cmdline_parser_free(&dummy); } diff --git a/error.h b/error.h index ca172cb4..51ace983 100644 --- a/error.h +++ b/error.h @@ -34,12 +34,15 @@ DEFINE_ERRLIST_OBJECT_ENUM; #define STDIN_ERRORS #define WRITE_ERRORS #define CHECK_WAV_ERRORS +#define VERSION_ERRORS +#define SCHED_ERRORS extern const char **para_errlist[]; #define OSS_MIX_ERRORS \ PARA_ERROR(OSS_MIXER_CHANNEL, "invalid mixer channel"), \ + #define ALSA_MIX_ERRORS \ PARA_ERROR(ALSA_MIX_OPEN, "could not open mixer"), \ PARA_ERROR(ALSA_MIX_BAD_ELEM, "invalid/unsupported control element"), \ @@ -52,6 +55,19 @@ extern const char **para_errlist[]; PARA_ERROR(LIBSAMPLERATE, "secret rabbit code error"), \ +#define OPUS_COMMON_ERRORS \ + PARA_ERROR(OPUS_HEADER, "invalid opus header"), \ + + +#define OPUS_AFH_ERRORS \ + PARA_ERROR(OPUS_COMMENT, "invalid or corrupted opus comment"), \ + + +#define OPUSDEC_FILTER_ERRORS \ + PARA_ERROR(CREATE_OPUS_DECODER, "could not create opus decoder"), \ + PARA_ERROR(OPUS_SET_GAIN, "opus: could not set gain"), \ + PARA_ERROR(OPUS_DECODE, "opus decode error"), \ + #define SIDEBAND_ERRORS \ PARA_ERROR(BAD_BAND, "invalid or unexpected band designator"), \ PARA_ERROR(SB_PACKET_SIZE, "invalid sideband packet size or protocol error"), \ @@ -60,7 +76,6 @@ extern const char **para_errlist[]; #define PLAY_ERRORS \ PARA_ERROR(PLAY_SYNTAX, "para_play: syntax error"), \ PARA_ERROR(NO_VALID_FILES, "no valid file found in playlist"), \ - PARA_ERROR(TERM_RQ, "user termination request"), \ PARA_ERROR(BAD_PLAY_CMD, "invalid command"), \ @@ -166,7 +181,6 @@ extern const char **para_errlist[]; #define COMPRESS_FILTER_ERRORS \ - PARA_ERROR(COMPRESS_SYNTAX, "syntax error in compress filter config"), \ PARA_ERROR(COMPRESS_EOF, "compress: end of file"), \ @@ -207,7 +221,6 @@ extern const char **para_errlist[]; #define AFH_ERRORS \ PARA_ERROR(AFH_SYNTAX, "afh syntax error"), \ - PARA_ERROR(AFH_SHORT_WRITE, "afh short write"), \ #define AFH_COMMON_ERRORS \ @@ -277,7 +290,6 @@ extern const char **para_errlist[]; #define AUDIOC_ERRORS \ - PARA_ERROR(AUDIOC_SYNTAX, "audioc syntax error"), \ PARA_ERROR(AUDIOC_EOF, "audioc: end of file"), \ @@ -289,11 +301,7 @@ extern const char **para_errlist[]; PARA_ERROR(SERVER_EOF, "connection closed by para_server"), \ PARA_ERROR(SERVER_CMD_SUCCESS, "command terminated successfully"), \ PARA_ERROR(SERVER_CMD_FAILURE, "command failed"), \ - - -#define SCHED_ERRORS \ - PARA_ERROR(NOT_INITIALIZED, "scheduler not yet initialized"), \ - PARA_ERROR(SCHED_SHUTDOWN, "scheduler was shut down"), \ + PARA_ERROR(INCOMPAT_FEAT, "client/server incompatibility"), \ #define NET_ERRORS \ @@ -307,7 +315,6 @@ extern const char **para_errlist[]; #define UDP_RECV_ERRORS \ - PARA_ERROR(UDP_SYNTAX, "udp_recv syntax error"), \ PARA_ERROR(UDP_OVERRUN, "output buffer overrun"), \ @@ -331,7 +338,6 @@ extern const char **para_errlist[]; PARA_ERROR(UNSUPPORTED_AUDIO_FORMAT, "given audio format not supported"), \ PARA_ERROR(NOT_PLAYING, "not playing"), \ PARA_ERROR(AUDIOD_OFF, "audiod switched off"), \ - PARA_ERROR(STATUS_TIMEOUT, "timeout reading server status"), \ #define AUDIOD_COMMAND_ERRORS \ @@ -367,14 +373,12 @@ extern const char **para_errlist[]; #define MP3DEC_FILTER_ERRORS \ PARA_ERROR(MAD_FRAME_DECODE, "mad frame decode error"), \ - PARA_ERROR(MP3DEC_SYNTAX, "syntax error in mp3dec config"), \ PARA_ERROR(MP3DEC_EOF, "mp3dec: end of file"), \ PARA_ERROR(MP3DEC_CORRUPT, "too many corrupt frames"), \ #define FILTER_ERRORS \ PARA_ERROR(NO_FILTERS, "at least one filter must be given"), \ - PARA_ERROR(FILTER_SYNTAX, "syntax error"), \ #define STRING_ERRORS \ @@ -444,8 +448,6 @@ extern const char **para_errlist[]; PARA_ERROR(SEXP_BUILD, "could not build S-expression"), \ PARA_ERROR(SEXP_ENCRYPT, "could not encrypt S-expression"), \ PARA_ERROR(SEXP_DECRYPT, "could not decrypt S-expression"), \ - PARA_ERROR(MD_OPEN, "could not open message digest object"), \ - PARA_ERROR(CIPHER_OPEN, "could not create stream cipher handle"), \ PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \ PARA_ERROR(KEY_MARKER, "invalid/missing key header or footer"), \ PARA_ERROR(ASN1_PARSE, "could not parse ASN.1 key"), \ @@ -463,7 +465,7 @@ extern const char **para_errlist[]; PARA_ERROR(SENDER_CMD, "command not supported by this sender"), \ PARA_ERROR(SERVER_CRASH, "para_server crashed -- can not live without it"), \ PARA_ERROR(BAD_USER, "auth request for invalid user"), \ - PARA_ERROR(BAD_FEATURE, "request for unknown or invalid feature"), \ + PARA_ERROR(BAD_FEATURE, "invalid feature request"), \ PARA_ERROR(BAD_AUTH, "authentication failure"), \ diff --git a/fade.c b/fade.c index fee8f8ca..878b83a1 100644 --- a/fade.c +++ b/fade.c @@ -14,6 +14,7 @@ #include "string.h" #include "mix.h" #include "error.h" +#include "ggo.h" #include "version.h" INIT_FADE_ERRLISTS; @@ -286,15 +287,26 @@ static struct mixer *get_mixer_or_die(void) exit(EXIT_FAILURE); } +__noreturn static void print_help_and_die(void) +{ + struct ggo_help h = DEFINE_GGO_HELP(fade); + bool d = conf.detailed_help_given; + + ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS); + exit(0); +} + int main(int argc, char *argv[]) { int ret; struct mixer *m; struct mixer_handle *h = NULL; - if (fade_cmdline_parser(argc, argv, &conf)) - exit(EXIT_FAILURE); - HANDLE_VERSION_FLAG("fade", conf); + fade_cmdline_parser(argc, argv, &conf); + loglevel = get_loglevel_by_name(conf.loglevel_arg); + version_handle_flag("fade", conf.version_given); + if (conf.help_given || conf.detailed_help_given) + print_help_and_die(); ret = configfile_exists(); if (!ret && conf.config_file_given) { PARA_EMERG_LOG("can not read config file %s\n", @@ -311,8 +323,8 @@ int main(int argc, char *argv[]) }; fade_cmdline_parser_config_file(conf.config_file_arg, &conf, ¶ms); + loglevel = get_loglevel_by_name(conf.loglevel_arg); } - loglevel = get_loglevel_by_name(conf.loglevel_arg); init_mixers(); m = get_mixer_or_die(); ret = m->open(conf.mixer_device_arg, &h); diff --git a/file_write.c b/file_write.c index cea21cb3..f7b2b307 100644 --- a/file_write.c +++ b/file_write.c @@ -157,9 +157,6 @@ void file_write_init(struct writer *w) w->parse_config_or_die = file_write_parse_config_or_die; w->free_config = file_write_free_config; w->close = file_write_close; - w->help = (struct ggo_help) { - .short_help = file_write_args_info_help, - .detailed_help = file_write_args_info_detailed_help - }; + w->help = (struct ggo_help)DEFINE_GGO_HELP(file_write); file_write_cmdline_parser_free(&dummy); } diff --git a/filter.c b/filter.c index 40e3779e..3b688573 100644 --- a/filter.c +++ b/filter.c @@ -47,30 +47,22 @@ INIT_STDERR_LOGGING(loglevel); __noreturn static void print_help_and_die(void) { - int d = conf.detailed_help_given; - const char **p = d? filter_args_info_detailed_help - : filter_args_info_help; - - printf_or_die("%s\n\n", FILTER_CMDLINE_PARSER_PACKAGE "-" - FILTER_CMDLINE_PARSER_VERSION); - printf_or_die("%s\n\n", filter_args_info_usage); - for (; *p; p++) - printf_or_die("%s\n", *p); - print_filter_helps(d); + struct ggo_help h = DEFINE_GGO_HELP(filter); + bool d = conf.detailed_help_given; + + ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS); + print_filter_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS); exit(0); } -static int parse_config(int argc, char *argv[]) +static int parse_config(void) { static char *cf; /* config file */ struct stat statbuf; - if (filter_cmdline_parser(argc, argv, &conf)) - return -E_FILTER_SYNTAX; - HANDLE_VERSION_FLAG("filter", conf); + version_handle_flag("filter", conf.version_given); if (conf.help_given || conf.detailed_help_given) print_help_and_die(); - loglevel = get_loglevel_by_name(conf.loglevel_arg); if (!cf) { char *home = para_homedir(); cf = make_message("%s/.paraslash/filter.conf", home); @@ -84,8 +76,8 @@ static int parse_config(int argc, char *argv[]) .check_ambiguity = 0, .print_errors = 1 }; - if (filter_cmdline_parser_config_file(cf, &conf, ¶ms)) - return -E_FILTER_SYNTAX; + filter_cmdline_parser_config_file(cf, &conf, ¶ms); + loglevel = get_loglevel_by_name(conf.loglevel_arg); } if (!conf.filter_given) return -E_NO_FILTERS; @@ -112,8 +104,10 @@ int main(int argc, char *argv[]) struct btr_node *parent; struct filter_node **fns; + filter_cmdline_parser(argc, argv, &conf); /* aborts on errors */ + loglevel = get_loglevel_by_name(conf.loglevel_arg); filter_init(); - ret = parse_config(argc, argv); + ret = parse_config(); if (ret < 0) goto out; sit->btrn = btr_new_node(&(struct btr_node_description) diff --git a/filter.h b/filter.h index 9c57904e..80bc896b 100644 --- a/filter.h +++ b/filter.h @@ -106,8 +106,7 @@ struct filter { * Convert (filter) the given data. * * Pointer to the converting function of the filter. On errors, the - * post_select function is supposed to set t->error to a (negative) - * error code. + * post_select function is supposed to return a negative error code. */ int (*post_select)(struct sched *s, struct task *t); /** @@ -121,7 +120,7 @@ struct filter { void filter_init(void); int check_filter_arg(char *filter_arg, void **conf); -void print_filter_helps(int detailed); +void print_filter_helps(unsigned flags); void generic_filter_pre_select(struct sched *s, struct task *t); int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels, char **result); diff --git a/filter_common.c b/filter_common.c index 907912fd..2616c9bd 100644 --- a/filter_common.c +++ b/filter_common.c @@ -101,24 +101,30 @@ int check_filter_arg(char *fa, void **conf) /** * Print help text of each filter to stdout. * - * \param detailed If non-zero, print detailed help. + * \param flags Passed to \ref ggo_print_help(). */ -void print_filter_helps(int detailed) +void print_filter_helps(unsigned flags) { - int i; + int i, num = 0; - printf_or_die("\nAvailable filters: \n\t"); - FOR_EACH_SUPPORTED_FILTER(i) - printf_or_die("%s%s", i? " " : "", filters[i].name); - printf_or_die("\n\n"); + printf_or_die("\nAvailable filters: "); + FOR_EACH_SUPPORTED_FILTER(i) { + if (num > 50) { + printf_or_die("\n "); + num = 0; + } + num += printf_or_die("%s%s", i? " " : "", filters[i].name); + } + printf_or_die("\n"); FOR_EACH_SUPPORTED_FILTER(i) { struct filter *f = filters + i; if (!f->help.short_help) continue; - printf_or_die("Options for %s:\n", f->name); - ggo_print_help(&f->help, detailed); + printf_or_die("\nOptions for %s (%s):", f->name, + f->help.purpose); + ggo_print_help(&f->help, flags); } } diff --git a/gcrypt.c b/gcrypt.c index 115529cb..2736a6c7 100644 --- a/gcrypt.c +++ b/gcrypt.c @@ -758,13 +758,13 @@ static int decode_rsa(gcry_sexp_t sexp, int key_size, unsigned char *outbuf, PARA_DEBUG_LOG("decrypted buffer before unpad (%d bytes):\n", key_size); - dump_buffer("non-unpadded decrypted buffer", oaep_buf, key_size);; + dump_buffer("non-unpadded decrypted buffer", oaep_buf, key_size); ret = unpad_oaep(oaep_buf, key_size, outbuf, nbytes); if (ret < 0) goto out_mpi_release; PARA_DEBUG_LOG("decrypted buffer after unpad (%zu bytes):\n", *nbytes); - dump_buffer("unpadded decrypted buffer", outbuf, *nbytes);; + dump_buffer("unpadded decrypted buffer", outbuf, *nbytes); ret = 1; out_mpi_release: gcry_mpi_release(out_mpi); @@ -937,39 +937,6 @@ void sc_free(struct stream_cipher *sc) free(sc); } -int sc_send_bin_buffer(struct stream_cipher_context *scc, char *buf, - size_t size) -{ - gcry_error_t gret; - int ret; - unsigned char *tmp = para_malloc(size); - - assert(size); - gret = gcry_cipher_encrypt(scc->send->handle, tmp, size, - (unsigned char *)buf, size); - assert(gret == 0); - ret = xwrite(scc->fd, (char *)tmp, size); - free(tmp); - return ret; -} - -int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf, - size_t size) -{ - gcry_error_t gret; - ssize_t ret = recv(scc->fd, buf, size, 0); - - if (ret < 0) - ret = -ERRNO_TO_PARA_ERROR(errno); - if (ret <= 0) - return ret; - /* perform in-place encryption */ - gret = gcry_cipher_encrypt(scc->recv->handle, (unsigned char *)buf, ret, - NULL, 0); - assert(gret == 0); - return ret; -} - void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst) { gcry_cipher_hd_t handle = sc->handle; diff --git a/ggo.c b/ggo.c index b2e079ab..99171c73 100644 --- a/ggo.c +++ b/ggo.c @@ -9,13 +9,17 @@ #include "para.h" #include "ggo.h" +#include "version.h" /** * Wrapper for printf() that exits on errors. * * \param fmt Usual format string. + * + * \return The return value of the underlying (successful) call to vprintf(3), + * i.e. the number of characters printed, excluding the terminating null byte. */ -__printf_1_2 void printf_or_die(const char *fmt, ...) +__printf_1_2 int printf_or_die(const char *fmt, ...) { va_list argp; int ret; @@ -24,7 +28,7 @@ __printf_1_2 void printf_or_die(const char *fmt, ...) ret = vprintf(fmt, argp); va_end(argp); if (ret >= 0) - return; + return ret; exit(EXIT_FAILURE); } @@ -32,21 +36,25 @@ __printf_1_2 void printf_or_die(const char *fmt, ...) * Print one of the two given help texts. * * \param help contains the help texts. - * \param detailed_help Whether to print the detailed help text. + * \param flags What to print, see \ref ggo_print_help_flags. */ -void ggo_print_help(struct ggo_help *help, int detailed_help) +void ggo_print_help(struct ggo_help *help, unsigned flags) { const char **p; - if (!help) - return; - if (detailed_help) + if (help->purpose && (flags & GPH_PRINT_NAME_PURPOSE)) + printf_or_die("para_%s - %s\n", help->prefix, help->purpose); + if (help->usage && (flags & GPH_PRINT_USAGE)) + printf_or_die("\n%s\n", help->usage); + if (help->description && (flags & GPH_PRINT_DESCRIPTION)) + printf_or_die("\n%s\n", help->description); + printf_or_die("\n"); + if (flags & GPH_DETAILED) p = help->detailed_help; else p = help->short_help; if (!p) return; - p += 3; /* skip -h and -V */ for (; *p; p++) - printf_or_die("\t%s\n", *p); + printf_or_die("%s\n", *p); } diff --git a/ggo.h b/ggo.h index e81b10c6..81565e7c 100644 --- a/ggo.h +++ b/ggo.h @@ -7,14 +7,64 @@ /** \file ggo.h Functions and structures for help text handling. */ /** - * Used by executables that can not use gengetopt's generated help function. + * Information extracted from the .cmdline.h header files. */ struct ggo_help { - /** The lines of the short help text. */ + /** Program or module (receiver, filter, writer) name. */ + const char *prefix; + /** Generated by gengetopt from the options in the .ggo file. */ const char **short_help; - /** The lines of the detailed help text. */ + /** Like \a short_help, plus the \a details section. */ const char **detailed_help; + /** The purpose text as specified in the ggo file. */ + const char *purpose; + /** Generated by gengetopt and exported via the *.cmdline.h file. */ + const char *usage; + /** The description text given in the .ggo file. */ + const char *description; }; -void ggo_print_help(struct ggo_help *help, int detailed_help); -__printf_1_2 void printf_or_die(const char *fmt, ...); +/** + * Control the output of \ref ggo_print_help(). + * + * Any combination of these flags may be passed to ggo_print_help(). + * Note that the list of supported options is always printed. + */ +enum ggo_print_help_flags { + /** Whether to print the short help or the detailed help. */ + GPH_DETAILED = 1 << 0, + /** Print the program or module name and the purpose text. */ + GPH_PRINT_NAME_PURPOSE = 1 << 1, + /** Print the synopsis. */ + GPH_PRINT_USAGE = 1 << 2, + /** Print the description text. */ + GPH_PRINT_DESCRIPTION = 1 << 3, +}; + +/** Used to print the normal help of programs (--help) */ +#define GPH_STANDARD_FLAGS \ + (GPH_PRINT_NAME_PURPOSE | GPH_PRINT_USAGE) + +/** Additional information for --detailed-help. */ +#define GPH_STANDARD_FLAGS_DETAILED \ + (GPH_STANDARD_FLAGS | GPH_DETAILED | GPH_PRINT_DESCRIPTION) + +/** For module help embedded in a program help. */ +#define GPH_MODULE_FLAGS 0 + +/** Modules help with detailed descriptions. */ +#define GPH_MODULE_FLAGS_DETAILED GPH_DETAILED | GPH_PRINT_DESCRIPTION + +/** Make a ggo_help structure using information from the .cmdline.h file. */ +#define DEFINE_GGO_HELP(_prefix) \ + { \ + .prefix = #_prefix, \ + .short_help = _prefix ## _args_info_help, \ + .detailed_help = _prefix ## _args_info_detailed_help, \ + .purpose = _prefix ## _args_info_purpose, \ + .usage = _prefix ## _args_info_usage, \ + .description = _prefix ## _args_info_description, \ + } + +void ggo_print_help(struct ggo_help *help, unsigned flags); +__printf_1_2 int printf_or_die(const char *fmt, ...); diff --git a/gui.c b/gui.c index 631e7cc5..baab0bd9 100644 --- a/gui.c +++ b/gui.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "gui.cmdline.h" #include "para.h" @@ -22,6 +23,7 @@ #include "list.h" #include "sched.h" #include "signal.h" +#include "ggo.h" #include "version.h" /** define the array of error lists needed by para_gui */ @@ -51,7 +53,7 @@ static struct ringbuffer *bot_win_rb; static unsigned scroll_position; -static int cmd_died, curses_active; +static int curses_active; static pid_t cmd_pid; static int command_fds[2]; @@ -288,14 +290,20 @@ static char *configfile_exists(void) return file_exists(tmp)? tmp: NULL; } -/* - * print num spaces to curses window - */ +/* Print given number of spaces to curses window. */ static void add_spaces(WINDOW* win, unsigned int num) { - while (num > 0) { - num--; - waddstr(win, " "); + char space[] = " "; + unsigned sz = sizeof(space) - 1; /* number of spaces */ + + while (num >= sz) { + waddstr(win, space); + num -= sz; + } + if (num > 0) { + assert(num < sz); + space[num] = '\0'; + waddstr(win, space); } } @@ -555,7 +563,8 @@ static void setup_signal_handling(void) para_sigaction(SIGHUP, SIG_IGN); } -__noreturn static void do_exit(int ret) +/* kill every process in the process group and exit */ +__noreturn static void kill_pg_and_die(int ret) { para_sigaction(SIGTERM, SIG_IGN); kill(0, SIGTERM); @@ -574,7 +583,7 @@ static void shutdown_curses(void) __noreturn static void finish(int ret) { shutdown_curses(); - do_exit(ret); + kill_pg_and_die(ret); } /* @@ -589,15 +598,15 @@ __noreturn __printf_2_3 static void msg_n_exit(int ret, const char* fmt, ...) va_start(argp, fmt); vfprintf(outfd, fmt, argp); va_end(argp); - do_exit(ret); + kill_pg_and_die(ret); } static void print_welcome(void) { if (loglevel > LL_NOTICE) return; - outputf(COLOR_WELCOME, "Welcome to para_gui " PACKAGE_VERSION - " \"" CODENAME "\". Theme: %s", theme.name); + outputf(COLOR_WELCOME, "Welcome to %s. Theme: %s", + version_single_line("gui"), theme.name); wclrtoeol(bot.win); } @@ -856,10 +865,8 @@ reap_next_child: ret = para_reap_child(&pid); if (ret <= 0) return; - if (pid == cmd_pid) { + if (pid == cmd_pid) cmd_pid = 0; - cmd_died = 1; - } goto reap_next_child; } @@ -947,33 +954,36 @@ static void handle_signal(int sig) } } -static int open_stat_pipe(void) +static void status_pre_select(fd_set *rfds, int *max_fileno, struct timeval *tv) { - static int init = 1; + static struct timeval next_exec, atm, diff; int ret, fds[3] = {0, 1, 0}; pid_t pid; - if (init) - init = 0; - else - /* - * Sleep a bit to avoid a busy loop. As the call to sleep() may - * be interrupted by SIGCHLD, we simply wait until the call - * succeeds. - */ - while (sleep(2)) - ; /* nothing */ + if (stat_pipe >= 0) + goto success; + /* Avoid busy loop */ + gettimeofday(&atm, NULL); + if (tv_diff(&next_exec, &atm, &diff) > 0) { + if (tv_diff(&diff, tv, NULL) < 0) + *tv = diff; + return; + } + next_exec.tv_sec = atm.tv_sec + 2; ret = para_exec_cmdline_pid(&pid, conf.stat_cmd_arg, fds); if (ret < 0) - return ret; + return; ret = mark_fd_nonblocking(fds[1]); - if (ret >= 0) - return fds[1]; - close(fds[1]); - return ret; + if (ret < 0) { + close(fds[1]); + return; + } + stat_pipe = fds[1]; +success: + para_fd_set(stat_pipe, rfds, max_fileno); } -#define COMMAND_BUF_SIZE 4096 +#define COMMAND_BUF_SIZE 32768 /* * This is the core select loop. Besides the (internal) signal @@ -995,6 +1005,7 @@ static int do_select(int mode) char command_buf[2][COMMAND_BUF_SIZE] = {"", ""}; int cbo[2] = {0, 0}; /* command buf offsets */ struct timeval tv; + unsigned flags[2] = {0, 0}; /* for for_each_line() */ repeat: tv.tv_sec = conf.timeout_arg / 1000; @@ -1002,10 +1013,7 @@ repeat: // ret = refresh_status(); FD_ZERO(&rfds); max_fileno = 0; - if (stat_pipe < 0) - stat_pipe = open_stat_pipe(); - if (stat_pipe >= 0) - para_fd_set(stat_pipe, &rfds, &max_fileno); + status_pre_select(&rfds, &max_fileno, &tv); /* signal pipe */ para_fd_set(signal_pipe, &rfds, &max_fileno); /* command pipe only for COMMAND_MODE */ @@ -1015,6 +1023,8 @@ repeat: if (command_fds[1] >= 0) para_fd_set(command_fds[1], &rfds, &max_fileno); } + if (mode == GETCH_MODE || mode == COMMAND_MODE) + para_fd_set(STDIN_FILENO, &rfds, &max_fileno); ret = para_select(max_fileno + 1, &rfds, NULL, &tv); if (ret <= 0) goto check_return; /* skip fd checks */ @@ -1033,18 +1043,26 @@ repeat: COMMAND_BUF_SIZE - 1 - cbo[i], &rfds, &sz); cbo[i] += sz; sz = cbo[i]; - cbo[i] = for_each_line(command_buf[i], cbo[i], + cbo[i] = for_each_line(flags[i], command_buf[i], cbo[i], add_output_line, &i); - if (sz != cbo[i]) + if (sz != cbo[i]) { /* at least one line found */ wrefresh(bot.win); + flags[i] = 0; + } if (ret < 0) { PARA_NOTICE_LOG("closing command fd %d: %s", i, para_strerror(-ret)); close(command_fds[i]); command_fds[i] = -1; + flags[i] = 0; if (command_fds[!i] < 0) /* both fds closed */ return 0; } + if (cbo[i] == COMMAND_BUF_SIZE - 1) { + PARA_NOTICE_LOG("discarding overlong line"); + cbo[i] = 0; + flags[i] = FELF_DISCARD_FIRST; + } } } ret = read_stat_pipe(&rfds); @@ -1082,10 +1100,8 @@ check_return: return ret; break; case EXTERNAL_MODE: - if (cmd_died) { - cmd_died = 0; + if (cmd_pid == 0) return 0; - } } goto repeat; } @@ -1159,7 +1175,6 @@ static void external_cmd(char *cmd) shutdown_curses(); if (para_exec_cmdline_pid(&cmd_pid, cmd, fds) < 0) return; - cmd_died = 0; do_select(EXTERNAL_MODE); init_curses(); } @@ -1348,10 +1363,7 @@ static void com_reread_conf(void) } PARA_INFO_LOG("rereading command line options and config file"); gui_cmdline_parser_ext(_argc, _argv, &conf, ¶ms); - if (gui_cmdline_parser_config_file(cf, &conf, ¶ms) != 0) { - PARA_EMERG_LOG("errors in config file"); - finish(EXIT_FAILURE); - } + gui_cmdline_parser_config_file(cf, &conf, ¶ms); PARA_NOTICE_LOG("config file reloaded"); if (check_key_map_args() < 0) finish(EXIT_FAILURE); @@ -1420,8 +1432,7 @@ static void com_enlarge_top_win(void) static void com_version(void) { - print_in_bar(COLOR_MSG, "para_gui " PACKAGE_VERSION " \"" - CODENAME "\""); + print_in_bar(COLOR_MSG, "%s", version_single_line("gui")); } __noreturn static void com_quit(void) @@ -1500,6 +1511,15 @@ static void handle_command(int c) km_keyname(c)); } +__noreturn static void print_help_and_die(void) +{ + struct ggo_help h = DEFINE_GGO_HELP(gui); + bool d = conf.detailed_help_given; + + ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS); + exit(0); +} + int main(int argc, char *argv[]) { int ret; @@ -1509,7 +1529,10 @@ int main(int argc, char *argv[]) _argv = argv; gui_cmdline_parser(argc, argv, &conf); /* exits on errors */ - HANDLE_VERSION_FLAG("gui", conf); + loglevel = get_loglevel_by_name(conf.loglevel_arg); + version_handle_flag("gui", conf.version_given); + if (conf.help_given || conf.detailed_help_given) + print_help_and_die(); cf = configfile_exists(); if (!cf && conf.config_file_given) { fprintf(stderr, "can not read config file %s\n", @@ -1524,10 +1547,9 @@ int main(int argc, char *argv[]) .check_ambiguity = 0, .print_errors = 1, }; - if (gui_cmdline_parser_config_file(cf, &conf, ¶ms) != 0) - exit(EXIT_FAILURE); + gui_cmdline_parser_config_file(cf, &conf, ¶ms); + loglevel = get_loglevel_by_name(conf.loglevel_arg); } - loglevel = get_loglevel_by_name(conf.loglevel_arg); if (check_key_map_args() < 0) { fprintf(stderr, "invalid key map\n"); exit(EXIT_FAILURE); diff --git a/http_recv.c b/http_recv.c index f1d85930..7db8ba19 100644 --- a/http_recv.c +++ b/http_recv.c @@ -143,10 +143,8 @@ static void *http_recv_parse_config(int argc, char **argv) { struct http_recv_args_info *tmp = para_calloc(sizeof(*tmp)); - if (!http_recv_cmdline_parser(argc, argv, tmp)) - return tmp; - free(tmp); - return NULL; + http_recv_cmdline_parser(argc, argv, tmp); + return tmp; } static int http_recv_open(struct receiver_node *rn) @@ -195,9 +193,6 @@ void http_recv_init(struct receiver *r) r->post_select = http_recv_post_select; r->parse_config = http_recv_parse_config; r->free_config = http_recv_free_config; - r->help = (struct ggo_help) { - .short_help = http_recv_args_info_help, - .detailed_help = http_recv_args_info_detailed_help - }; + r->help = (struct ggo_help)DEFINE_GGO_HELP(http_recv); http_recv_cmdline_parser_free(&dummy); } diff --git a/interactive.c b/interactive.c index f2819012..43cb99f2 100644 --- a/interactive.c +++ b/interactive.c @@ -272,6 +272,7 @@ static void clear_bottom_line(void) rl_redisplay(); wipe_bottom_line(); /* wipe out the prompt */ rl_insert_text(text); + free(text); rl_point = point; } @@ -290,21 +291,25 @@ static bool input_available(void) static void i9e_line_handler(char *line) { int ret; + struct btr_node *dummy; + if (!line) { + i9ep->input_eof = true; + return; + } + if (!*line) + goto free_line; + rl_set_prompt(""); + dummy = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = "dummy line handler")); + i9e_attach_to_stdout(dummy); ret = i9ep->ici->line_handler(line); if (ret < 0) PARA_WARNING_LOG("%s\n", para_strerror(-ret)); - rl_set_prompt(""); - if (line) { - if (!*line) - rl_set_prompt(i9ep->ici->prompt); - else - add_history(line); - free(line); - } else { - rl_set_prompt(""); - i9ep->input_eof = true; - } + add_history(line); + btr_remove_node(&dummy); +free_line: + free(line); } static int i9e_post_select(__a_unused struct sched *s, __a_unused struct task *t) diff --git a/ipc.c b/ipc.c index 40f6a951..1ec53c30 100644 --- a/ipc.c +++ b/ipc.c @@ -221,7 +221,7 @@ size_t shm_get_shmmax(void) #endif if (shmmax == 0) { PARA_WARNING_LOG("unable to determine shmmax\n"); - shmmax = 65535; /* last ressort */ + shmmax = 65535; /* last resort */ } PARA_INFO_LOG("shmmax: %zu\n", shmmax); return shmmax; diff --git a/m4/gengetopt/afh.m4 b/m4/gengetopt/afh.m4 index beb84696..ebc1d1cf 100644 --- a/m4/gengetopt/afh.m4 +++ b/m4/gengetopt/afh.m4 @@ -1,18 +1,12 @@ -args "--unamed-opts=audio_file --no-handle-version" +args "--unamed-opts=audio_file --no-handle-version --no-handle-help" -include(header.m4) - -text " -para_afh, the audio format handler tool, is a simple program for analyzing -audio files. It prints technical information about the given audio file to -stdout. -" - +purpose "Print information about audio file(s)." +include(header.m4) include(loglevel.m4) -option "chunk_table" c +option "chunk-table" c #~~~~~~~~~~~~~~~~~~~~~ "print also the chunk table" flag off @@ -33,7 +27,7 @@ option "parser-friendly" p flag off details = " Currently this option only affects the format of the chunk table, - so it has no effect if --chunk_table is not given. + so it has no effect if --chunk-table is not given. The human-readable output (the default) consists of one output line per chunk and the output contains also the chunk number, diff --git a/m4/gengetopt/afh_recv.m4 b/m4/gengetopt/afh_recv.m4 index 4995e77f..6fdef59e 100644 --- a/m4/gengetopt/afh_recv.m4 +++ b/m4/gengetopt/afh_recv.m4 @@ -1,6 +1,8 @@ -include(header.m4) - -text " +args "--no-version --no-help" + +purpose "Make an audio stream from a local file." + +description " The afh (audio format handler) receiver can be used to write selected parts of the given audio file without decoding the data. @@ -8,9 +10,10 @@ text " The selected parts of the content of the audio file are passed to the child nodes of the buffer tree. Only complete chunks with respect of the underlying audio format are passed. - " +include(header.m4) + option "filename" f #~~~~~~~~~~~~~~~~~~ "file to open" @@ -28,7 +31,7 @@ details = " num_chunks - 1 inclusively where num_chunks is the total number of chunks which is printed when using the --info option. If chunk_num is negative, the given number of chunks are counted - backwards from the end of the file. For example --begin_chunk + backwards from the end of the file. For example --begin-chunk -100 instructs para_afh to start output at chunk num_chunks - 100. This is mainly useful for cutting off the end of an audio file. @@ -40,7 +43,7 @@ option "end-chunk" e int typestr = "chunk_num" optional details = " - For the chunk_num argument the same rules as for --begin_chunk + For the chunk_num argument the same rules as for --begin-chunk apply. The default is to write up to the last chunk. " diff --git a/m4/gengetopt/alsa_write.m4 b/m4/gengetopt/alsa_write.m4 index d0cd4071..04b4963d 100644 --- a/m4/gengetopt/alsa_write.m4 +++ b/m4/gengetopt/alsa_write.m4 @@ -1,3 +1,7 @@ +args "--no-version --no-help" + +purpose "Native ALSA output plugin." + include(header.m4) diff --git a/m4/gengetopt/amp_filter.m4 b/m4/gengetopt/amp_filter.m4 index 865df3ac..2d4d4ce6 100644 --- a/m4/gengetopt/amp_filter.m4 +++ b/m4/gengetopt/amp_filter.m4 @@ -1,3 +1,7 @@ +args "--no-version --no-help" + +purpose "Amplify the decoded audio stream." + option "amp" a #~~~~~~~~~~~~~ "amplification value" diff --git a/m4/gengetopt/ao_write.m4 b/m4/gengetopt/ao_write.m4 index baccc57f..ccee4ee6 100644 --- a/m4/gengetopt/ao_write.m4 +++ b/m4/gengetopt/ao_write.m4 @@ -1,3 +1,7 @@ +args "--no-version --no-help" + +purpose "Output plugin for libao." + include(header.m4) diff --git a/m4/gengetopt/audioc.m4 b/m4/gengetopt/audioc.m4 index 736d7072..36520bfa 100644 --- a/m4/gengetopt/audioc.m4 +++ b/m4/gengetopt/audioc.m4 @@ -1,4 +1,6 @@ -args "--unamed-opts=command --conf-parser --no-handle-version" +args "--unamed-opts=command --conf-parser --no-handle-version --no-handle-help" + +purpose "Communicate with para_audiod through a local socket." include(header.m4) diff --git a/m4/gengetopt/audiod.m4 b/m4/gengetopt/audiod.m4 index 5522a56d..5fc15893 100644 --- a/m4/gengetopt/audiod.m4 +++ b/m4/gengetopt/audiod.m4 @@ -1,5 +1,7 @@ args "--no-handle-help --no-handle-version --conf-parser" +purpose "Connect to para_server, receive, decode and play audio streams." + include(header.m4) define(CURRENT_PROGRAM,para_audiod) define(DEFAULT_CONFIG_FILE,~/.paraslash/audiod.conf) @@ -78,7 +80,7 @@ details=" instructed to use also \"filename\" for connecting para_audiod. " -option "user_allow" - +option "user-allow" - #~~~~~~~~~~~~~~~~~~~~ "allow this uid" int typestr="uid" @@ -96,7 +98,7 @@ details=" to connect to para_audiod. " -option "clock_diff_count" - +option "clock-diff-count" - #~~~~~~~~~~~~~~~~~~~~~~~~~~ "sync clock on startup" int typestr="count" @@ -193,7 +195,7 @@ details=" " -option "stream_delay" - +option "stream-delay" - #~~~~~~~~~~~~~~~~~~~~~~ "time for client sync" int typestr="milliseconds" diff --git a/m4/gengetopt/client.m4 b/m4/gengetopt/client.m4 index 23f7c1e6..5b4c8ec8 100644 --- a/m4/gengetopt/client.m4 +++ b/m4/gengetopt/client.m4 @@ -1,4 +1,6 @@ -args "--unamed-opts=command --no-handle-error --conf-parser --no-handle-version" +args "--unamed-opts=command --no-handle-error --conf-parser --no-handle-version --no-handle-help" + +purpose "Communicate with para_server through the paraslash control port." include(header.m4) define(CURRENT_PROGRAM,para_client) @@ -7,8 +9,8 @@ define(DEFAULT_HISTORY_FILE,~/.paraslash/client.history) option "hostname" i "ip or host to connect" string typestr="host" default="localhost" optional option "user" u "paraslash username" string typestr="username" default="" optional -option "server_port" p "port to connect" int typestr="port" default="2990" optional -option "key_file" k "(default='~/.paraslash/key.')" string typestr="filename" optional +option "server-port" p "port to connect" int typestr="port" default="2990" optional +option "key-file" k "(default='~/.paraslash/key.')" string typestr="filename" optional include(loglevel.m4) diff --git a/m4/gengetopt/color.m4 b/m4/gengetopt/color.m4 index 63e996f3..eb081786 100644 --- a/m4/gengetopt/color.m4 +++ b/m4/gengetopt/color.m4 @@ -8,7 +8,7 @@ values = "yes","no","auto" default = "auto" optional -option "log_color" - +option "log-color" - #~~~~~~~~~~~~~~~~~~~ "select a color for one type of log message" string typestr="color_spec" @@ -26,9 +26,9 @@ details=" Examples: - --log_color \"debug:green\" - --log_color \"info:yellow bold\" - --log_color \"notice:white red bold\" + --log-color \"debug:green\" + --log-color \"info:yellow bold\" + --log-color \"notice:white red bold\" " diff --git a/m4/gengetopt/compress_filter.m4 b/m4/gengetopt/compress_filter.m4 index 74dcbc03..772bdb2e 100644 --- a/m4/gengetopt/compress_filter.m4 +++ b/m4/gengetopt/compress_filter.m4 @@ -1,3 +1,7 @@ +args "--no-version --no-help" + +purpose "Dynamically adjust the volume of an audio stream." + option "blocksize" b #~~~~~~~~~~~~~~~~~~~ "adjust block size" @@ -22,7 +26,7 @@ option "inertia" i default="6" optional -option "target_level" t +option "target-level" t #~~~~~~~~~~~~~~~~~~~~~~ "target signal level (0-32768)" int typestr="number" diff --git a/m4/gengetopt/config_file.m4 b/m4/gengetopt/config_file.m4 index 318ba7a6..29f66b44 100644 --- a/m4/gengetopt/config_file.m4 +++ b/m4/gengetopt/config_file.m4 @@ -1,5 +1,5 @@ -option "config_file" c +option "config-file" c #~~~~~~~~~~~~~~~~~~~~~ "(default='DEFAULT_CONFIG_FILE')" string typestr="filename" diff --git a/m4/gengetopt/dccp_recv.m4 b/m4/gengetopt/dccp_recv.m4 index 80d31233..f8191fe0 100644 --- a/m4/gengetopt/dccp_recv.m4 +++ b/m4/gengetopt/dccp_recv.m4 @@ -1,3 +1,7 @@ +args "--no-version --no-help" + +purpose "Receive a DCCP audio stream." + option "host" i "ip or host" string default="localhost" diff --git a/m4/gengetopt/fade.m4 b/m4/gengetopt/fade.m4 index da7a27e2..7c731c2b 100644 --- a/m4/gengetopt/fade.m4 +++ b/m4/gengetopt/fade.m4 @@ -1,4 +1,6 @@ -args "--conf-parser --no-handle-version" +args "--conf-parser --no-handle-version --no-handle-help" + +purpose "An alarm clock and volume-fader for OSS and ALSA." include(header.m4) define(CURRENT_PROGRAM,para_fade) diff --git a/m4/gengetopt/file_write.m4 b/m4/gengetopt/file_write.m4 index 56c44f7a..045b8657 100644 --- a/m4/gengetopt/file_write.m4 +++ b/m4/gengetopt/file_write.m4 @@ -1,3 +1,7 @@ +args "--no-version --no-help" + +purpose "Output plugin that writes to a local file." + option "filename" f #~~~~~~~~~~~~~~~~~~ "specify output file name" diff --git a/m4/gengetopt/filter.m4 b/m4/gengetopt/filter.m4 index c7337beb..baf6ecdb 100644 --- a/m4/gengetopt/filter.m4 +++ b/m4/gengetopt/filter.m4 @@ -1,5 +1,7 @@ args "--no-handle-help --no-handle-version --conf-parser" +purpose "Decode or process audio data from STDIN to STDOUT." + include(header.m4) include(loglevel.m4) diff --git a/m4/gengetopt/gui.m4 b/m4/gengetopt/gui.m4 index bafd325b..5b44efa6 100644 --- a/m4/gengetopt/gui.m4 +++ b/m4/gengetopt/gui.m4 @@ -1,4 +1,6 @@ -args "--conf-parser --no-handle-version" +args "--conf-parser --no-handle-version --no-handle-help" + +purpose "Show para_audiod status in a curses window." include(header.m4) define(CURRENT_PROGRAM,para_gui) @@ -32,7 +34,7 @@ details = " available themes is printed and the program terminates. " -option "stat_cmd" s +option "stat-cmd" s #~~~~~~~~~~~~~~~~~~ "command to read status items from" string typestr = "command" @@ -54,7 +56,7 @@ details = " section "Mapping keys to commands" #--------------------------------- -option "key_map" k +option "key-map" k #~~~~~~~~~~~~~~~~~ "Map key k to command c using mode m." diff --git a/m4/gengetopt/history_file.m4 b/m4/gengetopt/history_file.m4 index 932d88bf..73e98a78 100644 --- a/m4/gengetopt/history_file.m4 +++ b/m4/gengetopt/history_file.m4 @@ -1,5 +1,5 @@ -option "history_file" - +option "history-file" - #~~~~~~~~~~~~~~~~~~~~~~ "(default='DEFAULT_HISTORY_FILE')" string typestr = "filename" diff --git a/m4/gengetopt/http_recv.m4 b/m4/gengetopt/http_recv.m4 index 14ae99ab..b8ece39d 100644 --- a/m4/gengetopt/http_recv.m4 +++ b/m4/gengetopt/http_recv.m4 @@ -1,3 +1,7 @@ +args "--no-version --no-help" + +purpose "Receive an HTTP audio stream." + include(header.m4) diff --git a/m4/gengetopt/makefile b/m4/gengetopt/makefile index 05be1379..035f44a5 100644 --- a/m4/gengetopt/makefile +++ b/m4/gengetopt/makefile @@ -1,6 +1,6 @@ define ggo_opts --output-dir=$(cmdline_dir) \ - --set-version="$(PACKAGE_VERSION)" \ + --set-version="$(GIT_VERSION) ($(codename))" \ --arg-struct-name=$(*F)_args_info \ --file-name=$(*F).cmdline \ --func-name=$(*F)_cmdline_parser \ @@ -12,38 +12,15 @@ endef $(cmdline_dir)/%.cmdline.h $(cmdline_dir)/%.cmdline.c: $(ggo_dir)/%.ggo | $(cmdline_dir) @[ -z "$(Q)" ] || echo 'GGO $<' $(Q) $(GENGETOPT) $(ggo_opts) < $< +ifeq ($(ggo_descriptions_declared),no) + echo 'extern const char *$(*F)_args_info_description;' >> $(cmdline_dir)/$(*F).cmdline.h +endif -$(ggo_dir)/server.ggo $(ggo_dir)/audiod.ggo: \ - $(m4_ggo_dir)/loglevel.m4 $(m4_ggo_dir)/color.m4 \ - $(m4_ggo_dir)/config_file.m4 $(m4_ggo_dir)/logfile.m4 \ - $(m4_ggo_dir)/daemon.m4 $(m4_ggo_dir)/user.m4 \ - $(m4_ggo_dir)/group.m4 $(m4_ggo_dir)/log_timing.m4 \ - $(m4_ggo_dir)/config_file.m4 - -$(ggo_dir)/afh.ggo: $(m4_ggo_dir)/loglevel.m4 -$(ggo_dir)/audioc.ggo: \ - $(m4_ggo_dir)/loglevel.m4 \ - $(m4_ggo_dir)/history_file.m4 \ - $(m4_ggo_dir)/complete.m4 -$(ggo_dir)/filter.ggo: $(m4_ggo_dir)/loglevel.m4 -$(ggo_dir)/fsck.ggo: $(m4_ggo_dir)/loglevel.m4 -$(ggo_dir)/gui.ggo: $(m4_ggo_dir)/loglevel.m4 $(m4_ggo_dir)/config_file.m4 -$(ggo_dir)/recv.ggo: $(m4_ggo_dir)/loglevel.m4 -$(ggo_dir)/write.ggo: $(m4_ggo_dir)/loglevel.m4 \ - $(m4_ggo_dir)/channels.m4 \ - $(m4_ggo_dir)/sample_rate.m4 \ - $(m4_ggo_dir)/sample_format.m4 -$(ggo_dir)/client.ggo: \ - $(m4_ggo_dir)/loglevel.m4 \ - $(m4_ggo_dir)/config_file.m4 \ - $(m4_ggo_dir)/history_file.m4 \ - $(m4_ggo_dir)/complete.m4 -$(ggo_dir)/fade.ggo: $(m4_ggo_dir)/loglevel.m4 $(m4_ggo_dir)/config_file.m4 -$(ggo_dir)/resample_filter.ggo: \ - $(m4_ggo_dir)/channels.m4 \ - $(m4_ggo_dir)/sample_rate.m4 \ - $(m4_ggo_dir)/sample_format.m4 -$(ggo_dir)/play.ggo: $(m4_ggo_dir)/loglevel.m4 $(m4_ggo_dir)/config_file.m4 +$(m4depdir)/%.m4d: $(m4_ggo_dir)/%.m4 | $(m4depdir) + @[ -z "$(Q)" ] || echo 'M4D $<' + $(Q) m4 -I $(m4_ggo_dir) --debug=i $< 3>&1 1>/dev/null 2>&3 \ + | awk '{if ($$0 ~ "^m4debug: input read from ") \ + print "$(ggo_dir)/$(*F).ggo: " $$NF}' > $@ $(ggo_dir)/%.ggo: $(m4_ggo_dir)/%.m4 $(m4_ggo_dir)/header.m4 | $(ggo_dir) @[ -z "$(Q)" ] || echo 'M4 $<' diff --git a/m4/gengetopt/mp3dec_filter.m4 b/m4/gengetopt/mp3dec_filter.m4 index c02f1583..a6425b3e 100644 --- a/m4/gengetopt/mp3dec_filter.m4 +++ b/m4/gengetopt/mp3dec_filter.m4 @@ -1,3 +1,7 @@ +args "--no-version --no-help" + +purpose "Decode an mp3 stream." + include(header.m4) diff --git a/m4/gengetopt/oss_write.m4 b/m4/gengetopt/oss_write.m4 index 351561cd..352bea5b 100644 --- a/m4/gengetopt/oss_write.m4 +++ b/m4/gengetopt/oss_write.m4 @@ -1,3 +1,7 @@ +args "--no-version --no-help" + +purpose "Output plugin for the Open Sound System." + option "device" d #~~~~~~~~~~~~~~~~ "set PCM device" diff --git a/m4/gengetopt/osx_write.m4 b/m4/gengetopt/osx_write.m4 index bee16d9e..5add6f2e 100644 --- a/m4/gengetopt/osx_write.m4 +++ b/m4/gengetopt/osx_write.m4 @@ -1,3 +1,7 @@ +args "--no-version --no-help" + +purpose "Output plugin for Mac OS coreaudio." + section "osx options" ##################### diff --git a/m4/gengetopt/play.m4 b/m4/gengetopt/play.m4 index 57f954c4..cb406bec 100644 --- a/m4/gengetopt/play.m4 +++ b/m4/gengetopt/play.m4 @@ -1,4 +1,14 @@ args "--unamed-opts=audio_file --no-handle-version --conf-parser --no-handle-help" + +purpose "Command line audio player." + +description "para_play operates either in command mode or in insert +mode. In insert mode it presents a prompt and allows to enter commands +like stop, play, pause etc. In command mode the current audio file +is shown and the program reads single key strokes from stdin. Keys +may be mapped to commands. Whenever a mapped key is pressed, the +associated command is executed." + include(header.m4) define(CURRENT_PROGRAM,para_play) define(DEFAULT_CONFIG_FILE,~/.paraslash/play.conf) @@ -23,7 +33,7 @@ option "randomize" z "randomize playlist at startup." flag off -option "key_map" k +option "key-map" k #~~~~~~~~~~~~~~~~~ "Map key k to a command." diff --git a/m4/gengetopt/prebuffer_filter.m4 b/m4/gengetopt/prebuffer_filter.m4 index 7553e957..9e84dcfc 100644 --- a/m4/gengetopt/prebuffer_filter.m4 +++ b/m4/gengetopt/prebuffer_filter.m4 @@ -1,3 +1,7 @@ +args "--no-version --no-help" + +purpose "Delay processing of an audio stream." + option "duration" d #~~~~~~~~~~~~~~~~~~ "prebuffer time" diff --git a/m4/gengetopt/recv.m4 b/m4/gengetopt/recv.m4 index c7b89fe4..086c9c0a 100644 --- a/m4/gengetopt/recv.m4 +++ b/m4/gengetopt/recv.m4 @@ -1,5 +1,7 @@ args "--no-handle-help --no-handle-version" +purpose "A command line HTTP/DCCP/UDP stream grabber." + include(header.m4) include(loglevel.m4) diff --git a/m4/gengetopt/resample_filter.m4 b/m4/gengetopt/resample_filter.m4 index a4d081ff..4f4af4b0 100644 --- a/m4/gengetopt/resample_filter.m4 +++ b/m4/gengetopt/resample_filter.m4 @@ -1,3 +1,7 @@ +args "--no-version --no-help" + +purpose "Transform raw audio to a different sample rate." + include(header.m4) option "converter" C diff --git a/m4/gengetopt/server.m4 b/m4/gengetopt/server.m4 index 24d344e9..4c52d2c2 100644 --- a/m4/gengetopt/server.m4 +++ b/m4/gengetopt/server.m4 @@ -1,4 +1,6 @@ -args "--conf-parser --no-handle-version" +args "--conf-parser --no-handle-version --no-handle-help" + +purpose "Manage and stream audio files." include(header.m4) define(CURRENT_PROGRAM,para_server) @@ -40,7 +42,7 @@ include(logfile.m4) include(config_file.m4) -option "user_list" - +option "user-list" - #~~~~~~~~~~~~~~~~~~~ "(default='~/.paraslash/server.users')" @@ -58,7 +60,7 @@ option "autoplay" a "start playing on startup" flag off -option "autoplay_delay" - +option "autoplay-delay" - #~~~~~~~~~~~~~~~~~~~~~~~~ "time to wait before streaming" int typestr="ms" @@ -74,7 +76,7 @@ details=" already up when para_server starts to stream. Of course, this option depends on the autoplay option. " -option "announce_time" A +option "announce-time" A #~~~~~~~~~~~~~~~~~~~~~~~ "grace time for clients" @@ -93,7 +95,7 @@ details=" section "audio file selector" ############################# -option "afs_database_dir" D +option "afs-database-dir" D #~~~~~~~~~~~~~~~~~~~~~~~~~~ "location of the database" string typestr="path" @@ -103,7 +105,7 @@ details=" file selector. The default is '~/.paraslash/afs_database-0.4'. " -option "afs_socket" s +option "afs-socket" s #~~~~~~~~~~~~~~~~~~~~ "Command socket for afs" string typestr="path" @@ -115,7 +117,7 @@ details=" audio file selector via a local socket. This option specifies the location of that socket in the file system. " -option "afs_initial_mode" i +option "afs-initial-mode" i #~~~~~~~~~~~~~~~~~~~~~~~~~~ "Mood or playlist to load on startup." @@ -126,7 +128,7 @@ details=" The argument of this option must be prefixed with either 'p/' or 'm/' to indicate whether a playlist or a mood should be loaded. Example: - --afs_initial_mode p/foo + --afs-initial-mode p/foo loads the playlist named 'foo'. " @@ -135,7 +137,7 @@ section "http sender" ##################### -option "http_port" - +option "http-port" - #~~~~~~~~~~~~~~~~~~~ "tcp port for http streaming" int typestr="portnumber" @@ -147,20 +149,20 @@ details=" http request message such as 'GET / HTTP/'. " -option "http_default_deny" - +option "http-default-deny" - #~~~~~~~~~~~~~~~~~~~~~~~~~~~ "make the http ACL a whitelist" flag off details=" The default is to use blacklists instead, i.e. connections to the http sender are allowed unless the connecting host - matches a pattern given by a http_access option. This allows + matches a pattern given by a http-access option. This allows to use access control the other way round: Connections are denied from hosts which are not explicitly allowed by one or - more http_access options. + more http-access options. " -option "http_access" - +option "http-access" - #~~~~~~~~~~~~~~~~~~~~~ "add an entry to the http ACL" string typestr="a.b.c.d/n" @@ -168,13 +170,13 @@ optional multiple details=" Add given host/network to access control list (whitelist if - http_default_deny was given, blacklist otherwise) before + http-default-deny was given, blacklist otherwise) before opening the tcp port. This option can be given multiple times. Example: '192.168.0.0/24' whitelists/blacklists the 256 hosts 192.168.0.x " -option "http_no_autostart" - +option "http-no-autostart" - #~~~~~~~~~~~~~~~~~~~~~~~~~~~ "do not open tcp port on startup" flag off @@ -184,7 +186,7 @@ details=" later time by using the sender command. " -option "http_max_clients" - +option "http-max-clients" - #~~~~~~~~~~~~~~~~~~~~~~~~~~ "maximal number of connections" int typestr="number" @@ -202,45 +204,45 @@ section "dccp sender" ##################### -option "dccp_port" - +option "dccp-port" - #~~~~~~~~~~~~~~~~~~~ "port for dccp streaming" int typestr="portnumber" default="8000" optional details=" - See http_port for details. + See http-port for details. " -option "dccp_default_deny" - +option "dccp-default-deny" - #~~~~~~~~~~~~~~~~~~~~~~~~~~~ "make the dccp ACL a whitelist" flag off details=" - See http_default_deny for details. + See http-default-deny for details. " -option "dccp_access" - +option "dccp-access" - #~~~~~~~~~~~~~~~~~~~~~ "add an entry to the dccp ACL" string typestr="a.b.c.d/n" optional multiple details=" - See http_access for details. + See http-access for details. " -option "dccp_max_clients" - +option "dccp-max-clients" - #~~~~~~~~~~~~~~~~~~~~~~~~~~ "maximal number of connections" int typestr="number" default="-1" optional details=" - See http_max_clients for details. + See http-max-clients for details. " -option "dccp_max_slice_size" - +option "dccp-max-slice-size" - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "Upper bound for the FEC slice size" int typestr = "size" @@ -259,7 +261,7 @@ details = " the MPS of an incoming connection can not be set. " -option "dccp_data_slices_per_group" - +option "dccp-data-slices-per-group" - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "The number of non-redundant slices per FEC group" int typestr = "num" @@ -268,13 +270,13 @@ default = "3" details = " This determines the number of slices in each FEC group that are necessary to decode the group. The given number must be smaller - than the value of the dccp_slices_per_group option below. + than the value of the dccp-slices-per-group option below. Note that the duration of a FEC group is proportional to the - product dccp_max_slice_size * dccp_data_slices_per_group. + product dccp-max-slice-size * dccp-data-slices-per-group. " -option "dccp_slices_per_group" - +option "dccp-slices-per-group" - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "The total number of slices per FEC group" int typestr = "num" @@ -282,7 +284,7 @@ optional default = "4" details = " This value must be larger than the value given for above - dccp_data_slices_per_group above. The difference being the + dccp-data-slices-per-group above. The difference being the number of redundant slices per group, i.e. the number of data packets that may be lost without causing interruptions of the resulting audio stream. @@ -294,7 +296,7 @@ details = " section "udp sender" #################### -option "udp_target" - +option "udp-target" - #~~~~~~~~~~~~~~~~~~~~ "add udp target with optional port" string typestr="host[:port]" @@ -304,7 +306,7 @@ details=" Add given host/port to the list of targets. The 'host' argument can be either an IPv4/v6 address or hostname (RFC 3986 syntax). The 'port' argument is an optional port number. If the 'port' - part is absent, the 'udp_default_port' value is used. + part is absent, the 'udp-default-port' value is used. The following examples are possible targets: '10.10.1.2:8000' (host:port); '10.10.1.2' (with default port); @@ -315,7 +317,7 @@ details=" This option can be given multiple times, for multiple targets. " -option "udp_no_autostart" - +option "udp-no-autostart" - #~~~~~~~~~~~~~~~~~~~~~~~~~~ "do not start sending" flag off @@ -324,37 +326,38 @@ details=" a later time by using the sender command. " -option "udp_default_port" - +option "udp-default-port" - #~~~~~~~~~~~~~~~~~~~~~~~~~~ "udp port to send to" int typestr="port" default="8000" optional -option "udp_mcast_iface" - +option "udp-mcast-iface" - #~~~~~~~~~~~~~~~~~~~~~~~~~~ "outgoing udp multicast interface" string optional -option "udp_header_interval" H +option "udp-header-interval" H #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "duration for sending header" -int typestr="ms" -default="2000" +int typestr = "ms" +default = "2000" optional -details=" +details = " As the udp sender has no idea about connected clients it sends the audio file header periodically if necessary. This - option is used to specify the duration of the interval between - sending the header. Shorter values decrease the average time - clients have to wait before being able to start playback, - but this also increases the amount network traffic. Note - that this affects only ogg vorbis streams as this is the only - audio format that needs an audio file header. + option specifies the duration between subsequent headers are + sent. Smaller values decrease the average time clients have + to wait before starting playback, larger values decrease + network traffic. + + Note that this affects only ogg/* and wma streams. Other + audio formats, including mp3, don't need an audio file header. " -option "udp_ttl" t +option "udp-ttl" t #~~~~~~~~~~~~~~~~~ "set time to live value" int typestr="num" diff --git a/m4/gengetopt/udp_recv.m4 b/m4/gengetopt/udp_recv.m4 index 4c37eace..48770d97 100644 --- a/m4/gengetopt/udp_recv.m4 +++ b/m4/gengetopt/udp_recv.m4 @@ -1,3 +1,7 @@ +args "--no-version --no-help" + +purpose "Receive an UDP audio stream." + option "host" i "ip or host to receive udp packets from" string default="224.0.1.38" diff --git a/m4/gengetopt/write.m4 b/m4/gengetopt/write.m4 index c022bc8f..8b13f457 100644 --- a/m4/gengetopt/write.m4 +++ b/m4/gengetopt/write.m4 @@ -1,5 +1,7 @@ args "--no-handle-help --no-handle-version" +purpose "Play wav or raw audio." + include(header.m4) include(loglevel.m4) diff --git a/mood.c b/mood.c index 5d2d38b4..e5e84953 100644 --- a/mood.c +++ b/mood.c @@ -386,7 +386,7 @@ static int load_mood(const struct osl_row *mood_row, struct mood **m) if (!*mood_name) return -E_DUMMY_ROW; mlpd.m = alloc_new_mood(mood_name); - ret = for_each_line_ro(mood_def.data, mood_def.size, + ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size, parse_mood_line, &mlpd); osl_close_disk_object(&mood_def); if (ret < 0) { @@ -418,7 +418,7 @@ static int check_mood(struct osl_row *mood_row, void *data) ret = para_printf(pb, "checking mood %s...\n", mood_name); if (ret < 0) goto out; - ret = for_each_line_ro(mood_def.data, mood_def.size, + ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size, parse_mood_line, &mlpd); if (ret < 0) para_printf(pb, "%s line %u: %s\n", mood_name, mlpd.line_num, @@ -918,6 +918,8 @@ static int reload_current_mood(void) * This function performs actions required due to the occurrence of the given * event. Possible actions include reload of the current mood and update of the * score of an audio file. + * + * \return Standard. */ int moods_event_handler(enum afs_events event, __a_unused struct para_buffer *pb, void *data) diff --git a/mp3_afh.c b/mp3_afh.c index ece13e1f..251574a5 100644 --- a/mp3_afh.c +++ b/mp3_afh.c @@ -139,27 +139,27 @@ static void mp3_get_id3(__a_unused unsigned char *map, } for (i = 0; i < id3_t->nframes; i++) { struct id3_frame *fr = id3_t->frames[i]; - if (!strcmp(fr->id, "TIT2")) { + if (!strcmp(fr->id, ID3_FRAME_TITLE)) { if (!tags->title) tags->title = get_strings(fr); continue; } - if (!strcmp(fr->id, "TPE1")) { + if (!strcmp(fr->id, ID3_FRAME_ARTIST)) { if (!tags->artist) tags->artist = get_strings(fr); continue; } - if (!strcmp(fr->id, "TALB")) { + if (!strcmp(fr->id, ID3_FRAME_ALBUM)) { if (!tags->album) tags->album = get_strings(fr); continue; } - if (!strcmp(fr->id, "TDRC")) { + if (!strcmp(fr->id, ID3_FRAME_YEAR)) { if (!tags->year) tags->year = get_strings(fr); continue; } - if (!strcmp(fr->id, "COMM")) { + if (!strcmp(fr->id, ID3_FRAME_COMMENT)) { if (!tags->comment) tags->comment = get_strings(fr); continue; diff --git a/mp3dec_filter.c b/mp3dec_filter.c index 98627504..33c0dfc9 100644 --- a/mp3dec_filter.c +++ b/mp3dec_filter.c @@ -221,8 +221,5 @@ void mp3dec_filter_init(struct filter *f) f->pre_select = generic_filter_pre_select; f->post_select = mp3dec_post_select; f->execute = mp3dec_execute; - f->help = (struct ggo_help) { - .short_help = mp3dec_filter_args_info_help, - .detailed_help = mp3dec_filter_args_info_detailed_help - }; + f->help = (struct ggo_help)DEFINE_GGO_HELP(mp3dec_filter); } diff --git a/net.c b/net.c index 4d52e4a4..2ec3f03e 100644 --- a/net.c +++ b/net.c @@ -264,6 +264,12 @@ struct flowopts { struct list_head sockopts; }; +/** + * Allocate and initialize a flowopt queue. + * + * \return A new structure to be passed to \ref flowopt_add(). It is + * automatically deallocated in \ref makesock(). + */ struct flowopts *flowopt_new(void) { struct flowopts *new = para_malloc(sizeof(*new)); @@ -305,14 +311,6 @@ void flowopt_add(struct flowopts *fo, int lev, int opt, list_add_tail(&new->node, &fo->sockopts); } -void flowopt_add_bool(struct flowopts *fo, int lev, int opt, - const char *optname, bool on_or_off) -{ - int on = on_or_off; /* kernel takes 'int' */ - - flowopt_add(fo, lev, opt, optname, &on, sizeof(on)); -} - /** Set the entire bunch of pre-connection options at once. */ static void flowopt_setopts(int sockfd, struct flowopts *fo) { diff --git a/net.h b/net.h index a2c0d173..ae5e8015 100644 --- a/net.h +++ b/net.h @@ -61,12 +61,8 @@ struct flowopts; extern struct flowopts *flowopt_new(void); extern void flowopt_add(struct flowopts *fo, int level, int opt, const char *name, const void *val, int len); -extern void flowopt_add_bool(struct flowopts *fo, int lev, int opt, - const char *optname, bool on_or_off); /** Flowopt shortcut macros */ #define OPT_ADD(fo, lev, opt, val, len) flowopt_add(fo, lev, opt, #opt, val, len) -#define OPT_ENABLE(fo, lev, opt) flowopt_add_bool(fo, lev, opt, #opt, 1) -#define OPT_DISABLE(fo, lev, opt) flowopt_add_bool(fo, lev, opt, #opt, 0) /** * Functions to parse and validate (parts of) URLs. diff --git a/ogg_afh_common.c b/ogg_afh_common.c index 9cdb8092..ac70eb3f 100644 --- a/ogg_afh_common.c +++ b/ogg_afh_common.c @@ -4,7 +4,7 @@ * Licensed under the GPL v2. For licencing details see COPYING. */ -/** \file ogg_afh_common.c Functions common to ogg/vorbis and ogg/speex. */ +/** \file ogg_afh_common.c Functions common to all ogg/ codecs. */ #include #include diff --git a/opus_afh.c b/opus_afh.c new file mode 100644 index 00000000..b079bcd5 --- /dev/null +++ b/opus_afh.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2012-2013 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file opus_afh.c Audio format handler for ogg/opus files. */ + +#include +#include + +#include "para.h" +#include "afh.h" +#include "error.h" +#include "portable_io.h" +#include "string.h" +#include "opus_common.h" +#include "ogg_afh_common.h" + +static const char* opus_suffixes[] = {"opus", NULL}; + +static bool copy_if_tag_type(const char *tag, int taglen, const char *type, + char **p) +{ + char *q = key_value_copy(tag, taglen, type); + if (!q) + return false; + free(*p); + *p = q; + return true; +} + +static int opus_get_comments(char *comments, int length, + struct taginfo *tags) +{ + char *p = comments, *end = comments + length; + int i; + uint32_t val, ntags; + + /* min size of a opus header is 16 bytes */ + if (length < 16) + return -E_OPUS_COMMENT; + if (memcmp(p, "OpusTags", 8) != 0) + return -E_OPUS_COMMENT; + p += 8; + val = read_u32(p); + p += 4; + if (p + val > end) + return -E_OPUS_COMMENT; + tags->comment = safe_strdup(p, val); + p += val; + ntags = read_u32(p); + p += 4; + if (p + ntags * 4 > end) + return -E_OPUS_COMMENT; + PARA_INFO_LOG("found %d tag(s)\n", ntags); + for (i = 0; i < ntags; i++, p += val) { + char *tag; + + if (p + 4 > end) + return -E_OPUS_COMMENT; + val = read_u32(p); + p += 4; + if (p + val > end) + return -E_OPUS_COMMENT; + if (copy_if_tag_type(p, val, "author", &tags->artist)) + continue; + if (copy_if_tag_type(p, val, "artist", &tags->artist)) + continue; + if (copy_if_tag_type(p, val, "title", &tags->title)) + continue; + if (copy_if_tag_type(p, val, "album", &tags->album)) + continue; + if (copy_if_tag_type(p, val, "year", &tags->year)) + continue; + if (copy_if_tag_type(p, val, "comment", &tags->comment)) + continue; + tag = safe_strdup(p, val); + PARA_NOTICE_LOG("unrecognized tag: %s\n", tag); + free(tag); + } + return 1; +} + +static int opus_packet_callback(ogg_packet *packet, int packet_num, + __a_unused int serial, struct afh_info *afhi, + void *private_data) +{ + int ret; + struct opus_header *oh = private_data; + + if (packet_num == 0) { + ret = opus_parse_header((char *)packet->packet, packet->bytes, oh); + if (ret < 0) + return ret; + afhi->channels = oh->channels; + afhi->techinfo = make_message("header version %d, input sample rate: %dHz", + oh->version, oh->input_sample_rate); + /* + * The input sample rate is irrelevant for afhi->frequency as + * we always decode to 48kHz. + */ + afhi->frequency = 48000; + return 1; + } + if (packet_num == 1) { + ret = opus_get_comments((char *)packet->packet, packet->bytes, + &afhi->tags); + if (ret < 0) + return ret; + return 0; /* header complete */ + } + /* never reached */ + assert(0); +} + +static int opus_get_file_info(char *map, size_t numbytes, __a_unused int fd, + struct afh_info *afhi) +{ + int ret, ms; + struct opus_header oh = {.version = 0}; + + struct ogg_afh_callback_info opus_callback_info = { + .packet_callback = opus_packet_callback, + .private_data = &oh, + }; + ret = ogg_get_file_info(map, numbytes, afhi, &opus_callback_info); + if (ret < 0) + return ret; + ret = (afhi->chunk_table[afhi->chunks_total] - afhi->chunk_table[0]) * 8; /* bits */ + ms = tv2ms(&afhi->chunk_tv) * afhi->chunks_total; + afhi->bitrate = ret / ms; + return 1; +} + +/** + * The init function of the ogg/opus audio format handler. + * + * \param afh Pointer to the struct to initialize. + */ +void opus_afh_init(struct audio_format_handler *afh) +{ + afh->get_file_info = opus_get_file_info, + afh->suffixes = opus_suffixes; +} diff --git a/opus_common.c b/opus_common.c new file mode 100644 index 00000000..927df1f3 --- /dev/null +++ b/opus_common.c @@ -0,0 +1,169 @@ +/* Copyright (C)2012 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file opus_common.c Common functions of the opus decoder and audio format + * handler. + */ + +#include + +#include "para.h" +#include "error.h" +#include "opus_common.h" +#include "portable_io.h" + +struct packet { + const char *data; + int maxlen; + int pos; +}; + +static int read_chars(struct packet *p, unsigned char *str, int nb_chars) +{ + int i; + + if (p->pos > p->maxlen - nb_chars) + return 0; + for (i = 0; i < nb_chars; i++) + str[i] = p->data[p->pos++]; + return 1; +} + +static int read_uint32(struct packet *p, ogg_uint32_t *val) +{ + if (p->pos > p->maxlen - 4) + return 0; + *val = read_u32(p->data + p->pos); + p->pos += 4; + return 1; +} + +static int read_uint16(struct packet *p, ogg_uint16_t *val) +{ + if (p->pos > p->maxlen - 2) + return 0; + *val = read_u16(p->data + p->pos); + p->pos += 2; + return 1; +} + +/** + * Get metadata of an opus stream. + * + * This is called from both the audio format handler (which passes ogg packet + * 0) and from the decoder. + * + * \param packet Start of the packet. + * \param len Number of bytes. + * \param h Result. + * + * \return Standard. + */ +int opus_parse_header(const char *packet, int len, struct opus_header *h) +{ + int i; + char str[9]; + struct packet p; + unsigned char ch, channel_mapping; + ogg_uint16_t shortval; + + p.data = packet; + p.maxlen = len; + p.pos = 0; + str[8] = 0; + if (len < 19) + return -E_OPUS_HEADER; + read_chars(&p, (unsigned char*)str, 8); + if (memcmp(str, "OpusHead", 8) != 0) + return -E_OPUS_HEADER; + + if (!read_chars(&p, &ch, 1)) + return -E_OPUS_HEADER; + h->version = ch; + if((h->version & 240) != 0) /* Only major version 0 supported. */ + return -E_OPUS_HEADER; + + if (!read_chars(&p, &ch, 1)) + return -E_OPUS_HEADER; + h->channels = ch; + if (h->channels == 0) + return -E_OPUS_HEADER; + + if (!read_uint16(&p, &shortval)) + return -E_OPUS_HEADER; + h->preskip = shortval; + + if (!read_uint32(&p, &h->input_sample_rate)) + return -E_OPUS_HEADER; + + if (!read_uint16(&p, &shortval)) + return -E_OPUS_HEADER; + h->gain = (short)shortval; + + if (!read_chars(&p, &ch, 1)) + return -E_OPUS_HEADER; + channel_mapping = ch; + + if (channel_mapping != 0) { + if (!read_chars(&p, &ch, 1)) + return -E_OPUS_HEADER; + + if (ch < 1) + return -E_OPUS_HEADER; + h->nb_streams = ch; + + if (!read_chars(&p, &ch, 1)) + return -E_OPUS_HEADER; + + if (ch > h->nb_streams || (ch + h->nb_streams) > 255) + return -E_OPUS_HEADER; + h->nb_coupled = ch; + + /* Multi-stream support */ + for (i = 0; i < h->channels; i++) { + if (!read_chars(&p, &h->stream_map[i], 1)) + return -E_OPUS_HEADER; + if (h->stream_map[i] > (h->nb_streams + h->nb_coupled) + && h->stream_map[i] != 255) + return -E_OPUS_HEADER; + } + } else { + if (h->channels > 2) + return -E_OPUS_HEADER; + h->nb_streams = 1; + h->nb_coupled = h->channels > 1; + h->stream_map[0] = 0; + h->stream_map[1] = 1; + } + /* + * For version 0/1 we know there won't be any more data so reject any + * that have data past the end. + */ + if ((h->version == 0 || h->version == 1) && p.pos != len) + return -E_OPUS_HEADER; + return 1; +} diff --git a/opus_common.h b/opus_common.h new file mode 100644 index 00000000..cae9a6e9 --- /dev/null +++ b/opus_common.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** + * \file opus_common.h + * Common declarations for the opus decoder and audio format handler. + */ + +/** Various bits stored in the header of an opus stream. */ +struct opus_header { + /** lower 4 bits of the version byte, must be 0. */ + int version; + /** 1..255 */ + int channels; + /** Number of bytes to skip from the beginning. */ + int preskip; + /** Sample rate of the input stream, used by the audio format handler. */ + ogg_uint32_t input_sample_rate; + /** In dB, should be zero whenever possible. */ + int gain; + /** Number of logical streams (usually 1). */ + int nb_streams; + /** Number of streams to decode as 2 channel streams. */ + int nb_coupled; + /** Mapping from coded channels to output channels. */ + unsigned char stream_map[255]; +}; + +int opus_parse_header(const char *packet, int len, struct opus_header *h); diff --git a/opusdec_filter.c b/opusdec_filter.c new file mode 100644 index 00000000..90e65bc3 --- /dev/null +++ b/opusdec_filter.c @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2002-2007 Jean-Marc Valin + * Copyright (c) 2008 CSIRO + * Copyright (c) 2007-2012 Xiph.Org Foundation + * Copyright (C) 2012-2013 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file opusdec_filter.c The ogg/opus decoder. */ + +/* This file is based on opusdec.c, by Jean-Marc Valin, see below. */ + +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +/* Silence gcc warning caused by including opus.h */ +#if !defined(__STDC_VERSION__) +#define __STDC_VERSION__ 0 +#endif + +#include +#include +#include +#include +#include + +#include "para.h" +#include "list.h" +#include "sched.h" +#include "ggo.h" +#include "buffer_tree.h" +#include "filter.h" +#include "error.h" +#include "string.h" +#include "opus_common.h" + +/** 120ms at 48000 */ +#define MAX_FRAME_SIZE (960*6) + +struct opusdec_context { + OpusMSDecoder *st; + opus_int64 packet_count; + int total_links; + bool stream_init; + ogg_sync_state oy; + ogg_stream_state os; + ogg_page ogg_page; + bool eos; + int channels; + int preskip; + bool have_opus_stream; + ogg_int32_t opus_serialno; +}; + +static int opusdec_execute(struct btr_node *btrn, const char *cmd, + char **result) +{ + struct filter_node *fn = btr_context(btrn); + struct opusdec_context *ctx = fn->private_data; + + return decoder_execute(cmd, 48000, ctx->channels, result); +} + +static void opusdec_open(struct filter_node *fn) +{ + struct opusdec_context *ctx = para_calloc(sizeof(*ctx)); + + ogg_sync_init(&ctx->oy); + fn->private_data = ctx; +} + +static void opusdec_close(struct filter_node *fn) +{ + struct opusdec_context *ctx = fn->private_data; + + if (ctx->st) { + opus_multistream_decoder_destroy(ctx->st); + if (ctx->stream_init) + ogg_stream_clear(&ctx->os); + ogg_sync_clear(&ctx->oy); + } + free(ctx); + fn->private_data = NULL; +} + +/* Process an Opus header and setup the opus decoder based on it. */ +static int opusdec_init(ogg_packet *op, struct opusdec_context *ctx) +{ + int ret; + struct opus_header header; + + ctx->st = NULL; + ret = opus_parse_header((char *)op->packet, op->bytes, &header); + if (ret < 0) + return ret; + PARA_INFO_LOG("detected header v%d\n", header.version); + ctx->channels = header.channels; + ctx->preskip = header.preskip; + ctx->st = opus_multistream_decoder_create(48000, header.channels, + header.nb_streams, header.nb_coupled, header.stream_map, &ret); + if (ret != OPUS_OK || !ctx->st) { + PARA_ERROR_LOG("%s\n", opus_strerror(ret)); + return -E_CREATE_OPUS_DECODER; + } + if (header.gain != 0) { + ret = opus_multistream_decoder_ctl(ctx->st, + OPUS_SET_GAIN(header.gain)); + if (ret != OPUS_OK) { + PARA_ERROR_LOG("%s\n", opus_strerror(ret)); + return -E_OPUS_SET_GAIN; + } + PARA_INFO_LOG("playback gain: %fdB\n", header.gain / 256.); + } + PARA_INFO_LOG("%d channel(s), 48KHz\n", ctx->channels); + return 1; +} + +static void opusdec_add_output(short *pcm, int frames_available, + struct btr_node *btrn, struct opusdec_context *ctx) +{ + int tmp_skip, num_frames, bytes; + + tmp_skip = PARA_MIN(ctx->preskip, frames_available); + ctx->preskip -= tmp_skip; + num_frames = frames_available - tmp_skip; + if (num_frames <= 0) + return; + bytes = sizeof(short) * num_frames * ctx->channels; + + if (tmp_skip > 0) { + short *in = pcm + ctx->channels * tmp_skip; + short *out = para_malloc(bytes); + memcpy(out, in, bytes); + free(pcm); + pcm = out; + } + btr_add_output((char *)pcm, bytes, btrn); +} + +/* returns > 1 if packet was decoded, 0 if it was ignored, negative on errors. */ +static int decode_packet(struct opusdec_context *ctx, ogg_packet *op, + struct btr_node *btrn) +{ + int ret; + short *output; + + /* + * OggOpus streams are identified by a magic string in the initial + * stream header. + */ + if (op->b_o_s && op->bytes >= 8 && !memcmp(op->packet, "OpusHead", 8)) { + if (!ctx->have_opus_stream) { + ctx->opus_serialno = ctx->os.serialno; + ctx->have_opus_stream = true; + ctx->packet_count = 0; + ctx->eos = false; + ctx->total_links++; + } else + PARA_NOTICE_LOG("ignoring opus stream %llu\n", + (long long unsigned)ctx->os.serialno); + } + if (!ctx->have_opus_stream || ctx->os.serialno != ctx->opus_serialno) + return 0; + /* If first packet in a logical stream, process the Opus header. */ + if (ctx->packet_count == 0) + return opusdec_init(op, ctx); + if (ctx->packet_count == 1) + return 1; + /* don't care for anything except opus eos */ + if (op->e_o_s && ctx->os.serialno == ctx->opus_serialno) + ctx->eos = true; + output = para_malloc(sizeof(short) * MAX_FRAME_SIZE * ctx->channels); + ret = opus_multistream_decode(ctx->st, (unsigned char *)op->packet, + op->bytes, output, MAX_FRAME_SIZE, 0); + if (ret < 0) { + PARA_ERROR_LOG("%s\n", opus_strerror(ret)); + free(output); + return -E_OPUS_DECODE; + } + opusdec_add_output(output, ret, btrn, ctx); + return 1; +} + +static int opusdec_post_select(__a_unused struct sched *s, struct task *t) +{ + struct filter_node *fn = container_of(t, struct filter_node, task); + struct opusdec_context *ctx = fn->private_data; + struct btr_node *btrn = fn->btrn; + int ret; + char *btr_buf, *data; + size_t nbytes; + ogg_packet op; + + ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL); + if (ret <= 0) + goto out; + btr_merge(btrn, fn->min_iqs); + nbytes = btr_next_buffer(btrn, &btr_buf); + nbytes = PARA_MIN(nbytes, (size_t)32768); + ret = 0; + if (nbytes == 0) + goto out; + data = ogg_sync_buffer(&ctx->oy, nbytes); + memcpy(data, btr_buf, nbytes); + btr_consume(btrn, nbytes); + ogg_sync_wrote(&ctx->oy, nbytes); + for (;;) { /* loop over all ogg pages we got */ + ret = 0; + if (ogg_sync_pageout(&ctx->oy, &ctx->ogg_page) != 1) + goto out; + if (!ctx->stream_init) { + ogg_stream_init(&ctx->os, ogg_page_serialno(&ctx->ogg_page)); + ctx->stream_init = true; + } + if (ogg_page_serialno(&ctx->ogg_page) != ctx->os.serialno) + ogg_stream_reset_serialno(&ctx->os, + ogg_page_serialno(&ctx->ogg_page)); + /* Add page to the bitstream */ + ogg_stream_pagein(&ctx->os, &ctx->ogg_page); + for (;;) { /* loop over all opus packets */ + ret = ogg_stream_packetout(&ctx->os, &op); + if (ret != 1) + break; + ret = decode_packet(ctx, &op, btrn); + if (ret < 0) + goto out; + ctx->packet_count++; + if (ctx->eos) + ctx->have_opus_stream = false; + } + } +out: + if (ret < 0) + btr_remove_node(&fn->btrn); + return ret; +} + +/** + * The init function of the opusdec filter. + * + * \param f Pointer to the filter struct to initialize. + * + * \sa filter::init. + */ +void opusdec_filter_init(struct filter *f) +{ + f->open = opusdec_open; + f->close = opusdec_close; + f->pre_select = generic_filter_pre_select; + f->post_select = opusdec_post_select; + f->execute = opusdec_execute; +} diff --git a/oss_mix.c b/oss_mix.c index 9fe86504..5182ad23 100644 --- a/oss_mix.c +++ b/oss_mix.c @@ -56,8 +56,10 @@ static int oss_mix_open(const char *dev, struct mixer_handle **handle) dev = "/dev/mixer"; PARA_INFO_LOG("opening %s\n", dev); ret = para_open(dev, O_RDWR, 42); - if (ret < 0) + if (ret < 0) { + PARA_ERROR_LOG("could not open %s\n", dev); return ret; + } h = para_malloc(sizeof(*h)); h->fd = ret; *handle = h; diff --git a/oss_write.c b/oss_write.c index cd327243..3c61a445 100644 --- a/oss_write.c +++ b/oss_write.c @@ -235,9 +235,6 @@ void oss_write_init(struct writer *w) w->post_select = oss_post_select; w->parse_config_or_die = oss_parse_config_or_die; w->free_config = oss_free_config; - w->help = (struct ggo_help) { - .short_help = oss_write_args_info_help, - .detailed_help = oss_write_args_info_detailed_help - }; + w->help = (struct ggo_help)DEFINE_GGO_HELP(oss_write); oss_write_cmdline_parser_free(&dummy); } diff --git a/osx_write.c b/osx_write.c index 41b19030..f1e308d8 100644 --- a/osx_write.c +++ b/osx_write.c @@ -366,9 +366,6 @@ void osx_write_init(struct writer *w) w->post_select = osx_write_post_select; w->parse_config_or_die = osx_write_parse_config_or_die; w->free_config = osx_free_config; - w->help = (struct ggo_help) { - .short_help = osx_write_args_info_help, - .detailed_help = osx_write_args_info_detailed_help - }; + w->help = (struct ggo_help)DEFINE_GGO_HELP(osx_write); osx_write_cmdline_parser_free(&dummy); } diff --git a/para.h b/para.h index edab4871..6655d356 100644 --- a/para.h +++ b/para.h @@ -82,30 +82,25 @@ extern __printf_2_3 void (*para_log)(int, const char*, ...); /** Sent by para_client to initiate the authentication procedure. */ #define AUTH_REQUEST_MSG "auth rsa " -/** Sent by para_server for commands that expect a data file. */ -#define AWAITING_DATA_MSG "\nAwaiting Data." -/** Sent by para_server if authentication was successful. */ -#define PROCEED_MSG "Proceed." -/** Length of the \p PROCEED_MSG string. */ -#define PROCEED_MSG_LEN strlen(PROCEED_MSG) -/** Sent by para_client to indicate the end of the command line. */ -#define EOC_MSG "\nEnd of Command." /* exec */ int para_exec_cmdline_pid(pid_t *pid, const char *cmdline, int *fds); /* time */ -int tv_diff(const struct timeval *b, const struct timeval *a, struct timeval *diff); -long unsigned tv2ms(const struct timeval*); -void d2tv(double, struct timeval*); -void tv_add(const struct timeval*, const struct timeval *, struct timeval *); -void tv_scale(const unsigned long, const struct timeval *, struct timeval *); +int tv_diff(const struct timeval *b, const struct timeval *a, + struct timeval *diff); +long unsigned tv2ms(const struct timeval *tv); +void d2tv(double x, struct timeval *tv); +void tv_add(const struct timeval *a, const struct timeval *b, + struct timeval *sum); +void tv_scale(const unsigned long mult, const struct timeval *tv, + struct timeval *result); void tv_divide(const unsigned long divisor, const struct timeval *tv, - struct timeval *result); + struct timeval *result); int tv_convex_combination(const long a, const struct timeval *tv1, const long b, const struct timeval *tv2, struct timeval *result); -void ms2tv(const long unsigned n, struct timeval *tv); +void ms2tv(long unsigned n, struct timeval *tv); void compute_chunk_time(long unsigned chunk_num, struct timeval *chunk_tv, struct timeval *stream_start, struct timeval *result); diff --git a/play.c b/play.c index 2cb0536d..6eed58ab 100644 --- a/play.c +++ b/play.c @@ -137,30 +137,14 @@ static void check_afh_receiver_or_die(void) exit(EXIT_FAILURE); } -/** Description to be included in the --detailed-help output. */ -#define PP_DESC \ -"para_play is a command line audio player.\n" \ -"\n" \ -"It operates either in command mode or in insert mode. In insert mode it\n" \ -"presents a prompt and allows to enter para_play commands like stop, play, pause\n" \ -"etc. In command mode, the current audio file is shown and the program reads\n" \ -"single key strokes from stdin. Keys may be mapped to para_play commands.\n" \ -"Whenever a mapped key is pressed, the associated command is executed.\n" \ - __noreturn static void print_help_and_die(void) { - int d = conf.detailed_help_given; - const char **p = d? play_args_info_detailed_help - : play_args_info_help; - -// printf_or_die("%s\n\n", PLAY_CMDLINE_PARSER_PACKAGE "-" -// PLAY_CMDLINE_PARSER_VERSION); - - printf_or_die("%s\n\n", play_args_info_usage); - if (d) - printf_or_die("%s\n", PP_DESC); - for (; *p; p++) - printf_or_die("%s\n", *p); + struct ggo_help help = DEFINE_GGO_HELP(play); + unsigned flags = conf.detailed_help_given? + GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS; + + ggo_print_help(&help, flags); + printf("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS); exit(0); } @@ -176,12 +160,11 @@ static void parse_config_or_die(int argc, char *argv[]) .print_errors = 1 }; - if (play_cmdline_parser_ext(argc, argv, &conf, ¶ms)) - exit(EXIT_FAILURE); - HANDLE_VERSION_FLAG("play", conf); + play_cmdline_parser_ext(argc, argv, &conf, ¶ms); + loglevel = get_loglevel_by_name(conf.loglevel_arg); + version_handle_flag("play", conf.version_given); if (conf.help_given || conf.detailed_help_given) print_help_and_die(); - loglevel = get_loglevel_by_name(conf.loglevel_arg); if (conf.config_file_given) config_file = para_strdup(conf.config_file_arg); else { @@ -198,6 +181,7 @@ static void parse_config_or_die(int argc, char *argv[]) params.initialize = 0; params.check_required = 1; play_cmdline_parser_config_file(config_file, &conf, ¶ms); + loglevel = get_loglevel_by_name(conf.loglevel_arg); } for (i = 0; i < conf.key_map_given; i++) { char *s = strchr(conf.key_map_arg[i] + 1, ':'); @@ -990,15 +974,7 @@ out: static int play_i9e_line_handler(char *line) { - struct play_task *pt = &play_task; - int ret; - - if (line == NULL || !*line) - return 0; - ret = run_command(line, pt); - if (ret < 0) - return ret; - return 0; + return run_command(line, &play_task); } static int play_i9e_key_handler(int key) @@ -1044,7 +1020,7 @@ static void session_open(__a_unused struct play_task *pt) char *history_file; struct sigaction act; - PARA_NOTICE_LOG("\n%s\n", VERSION_TEXT("play")); + PARA_NOTICE_LOG("\n%s\n", version_text("play")); if (conf.history_file_given) history_file = para_strdup(conf.history_file_arg); else { diff --git a/playlist.c b/playlist.c index 60e27e6e..c822e080 100644 --- a/playlist.c +++ b/playlist.c @@ -70,8 +70,8 @@ static int load_playlist(struct osl_row *row, void *data) if (ret < 0) goto err; playlist->length = 0; - ret = for_each_line_ro(playlist_def.data, playlist_def.size, - add_playlist_entry, playlist); + ret = for_each_line(FELF_READ_ONLY, playlist_def.data, + playlist_def.size, add_playlist_entry, playlist); osl_close_disk_object(&playlist_def); if (ret < 0) goto err; @@ -114,8 +114,8 @@ static int check_playlist(struct osl_row *row, void *data) ret = para_printf(pb, "checking playlist %s...\n", playlist_name); if (ret < 0) return ret; - ret = for_each_line_ro(playlist_def.data, playlist_def.size, - check_playlist_path, pb); + ret = for_each_line(FELF_READ_ONLY, playlist_def.data, + playlist_def.size, check_playlist_path, pb); } osl_close_disk_object(&playlist_def); return ret; @@ -219,8 +219,8 @@ static int handle_audio_file_event(enum afs_events event, void *data) ret = pl_get_def_by_name(current_playlist.name, &playlist_def); if (ret < 0) return ret; - ret = for_each_line_ro(playlist_def.data, playlist_def.size, - search_path, new_path); + ret = for_each_line(FELF_READ_ONLY, playlist_def.data, + playlist_def.size, search_path, new_path); osl_close_disk_object(&playlist_def); is_admissible = (ret < 0); if (was_admissible && is_admissible) diff --git a/prebuffer_filter.c b/prebuffer_filter.c index 0759d678..655c981b 100644 --- a/prebuffer_filter.c +++ b/prebuffer_filter.c @@ -123,8 +123,5 @@ void prebuffer_filter_init(struct filter *f) f->free_config = prebuffer_free_config; f->pre_select = prebuffer_pre_select; f->post_select = prebuffer_post_select; - f->help = (struct ggo_help) { - .short_help = prebuffer_filter_args_info_help, - .detailed_help = prebuffer_filter_args_info_detailed_help - }; + f->help = (struct ggo_help)DEFINE_GGO_HELP(prebuffer_filter); } diff --git a/recv.c b/recv.c index 1fb5e25e..7a5549aa 100644 --- a/recv.c +++ b/recv.c @@ -39,28 +39,12 @@ INIT_RECV_ERRLISTS; __noreturn static void print_help_and_die(void) { - int d = conf.detailed_help_given; - const char **p = d? recv_args_info_detailed_help - : recv_args_info_help; - - printf_or_die("%s\n\n", RECV_CMDLINE_PARSER_PACKAGE "-" - RECV_CMDLINE_PARSER_VERSION); - printf_or_die("%s\n\n", recv_args_info_usage); - for (; *p; p++) - printf_or_die("%s\n", *p); - print_receiver_helps(d); - exit(0); -} + struct ggo_help h = DEFINE_GGO_HELP(recv); + bool d = conf.detailed_help_given; -static void *parse_config(int argc, char *argv[], int *receiver_num) -{ - if (recv_cmdline_parser(argc, argv, &conf)) - return NULL; - HANDLE_VERSION_FLAG("recv", conf); - if (conf.help_given || conf.detailed_help_given) - print_help_and_die(); - loglevel = get_loglevel_by_name(conf.loglevel_arg); - return check_receiver_arg(conf.receiver_arg, receiver_num); + ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS); + print_receiver_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS); + exit(0); } /** @@ -82,16 +66,18 @@ int main(int argc, char *argv[]) struct stdout_task sot; static struct sched s; - s.default_timeout.tv_sec = 1; - s.default_timeout.tv_usec = 0; + recv_cmdline_parser(argc, argv, &conf); + loglevel = get_loglevel_by_name(conf.loglevel_arg); + version_handle_flag("recv", conf.version_given); + recv_init(); + if (conf.help_given || conf.detailed_help_given) + print_help_and_die(); - memset(&sot, 0, sizeof(struct stdout_task)); memset(&rn, 0, sizeof(struct receiver_node)); - recv_init(); - ret = -E_RECV_SYNTAX; - rn.conf = parse_config(argc, argv, &receiver_num); + rn.conf = check_receiver_arg(conf.receiver_arg, &receiver_num); if (!rn.conf) { - PARA_EMERG_LOG("parse failed\n"); + PARA_EMERG_LOG("invalid receiver specifier\n"); + ret = -E_RECV_SYNTAX; goto out; } r = &receivers[receiver_num]; @@ -103,6 +89,7 @@ int main(int argc, char *argv[]) goto out; r_opened = 1; + memset(&sot, 0, sizeof(struct stdout_task)); sot.btrn = btr_new_node(&(struct btr_node_description) EMBRACE(.parent = rn.btrn, .name = "stdout")); stdout_set_defaults(&sot); @@ -113,6 +100,8 @@ int main(int argc, char *argv[]) sprintf(rn.task.status, "%s", r->name); register_task(&s, &rn.task); + s.default_timeout.tv_sec = 1; + s.default_timeout.tv_usec = 0; ret = schedule(&s); out: if (r_opened) diff --git a/recv.h b/recv.h index cc44a602..68222acf 100644 --- a/recv.h +++ b/recv.h @@ -58,11 +58,11 @@ struct receiver { /** * The command line parser of the receiver. * - * It should check whether the command line options given by \a argc and \a - * argv are valid. On success, it should return a pointer to the - * receiver-specific configuration data determined by \a argc and \a argv. - * Note that this might be called more than once with different values of - * \a argc and \a argv. + * It should check whether the command line options given by \a argc + * and \a argv are valid. On success, it should return a pointer to + * the receiver-specific configuration data determined by \a argc and + * \a argv. Note that this might be called more than once with + * different values of \a argc and \a argv. */ void *(*parse_config)(int argc, char **argv); /** @@ -141,7 +141,7 @@ struct receiver { void recv_init(void); void *check_receiver_arg(char *ra, int *receiver_num); -void print_receiver_helps(int detailed); +void print_receiver_helps(unsigned flags); int generic_recv_pre_select(struct sched *s, struct task *t); /** \cond receiver */ diff --git a/recv_common.c b/recv_common.c index f899c301..2ea8a599 100644 --- a/recv_common.c +++ b/recv_common.c @@ -92,22 +92,23 @@ void *check_receiver_arg(char *ra, int *receiver_num) /** * Print out the help texts to all receivers. * - * \param detailed Whether the detailed help should be printed. + * \param flags Passed to \ref ggo_print_help(). */ -void print_receiver_helps(int detailed) +void print_receiver_helps(unsigned flags) { int i; - printf_or_die("\nAvailable receivers: \n\t"); + printf_or_die("\nAvailable receivers: "); FOR_EACH_RECEIVER(i) printf_or_die("%s%s", i? " " : "", receivers[i].name); - printf_or_die("\n\n"); + printf_or_die("\n"); FOR_EACH_RECEIVER(i) { struct receiver *r = receivers + i; if (!r->help.short_help) continue; - printf_or_die("Options for %s:\n", r->name); - ggo_print_help(&r->help, detailed); + printf_or_die("\n%s: %s", r->name, + r->help.purpose); + ggo_print_help(&r->help, flags); } } diff --git a/resample_filter.c b/resample_filter.c index bd8ece91..5ad584de 100644 --- a/resample_filter.c +++ b/resample_filter.c @@ -310,8 +310,5 @@ void resample_filter_init(struct filter *f) f->parse_config = resample_parse_config; f->free_config = resample_free_config; f->execute = resample_execute; - f->help = (struct ggo_help) { - .short_help = resample_filter_args_info_help, - .detailed_help = resample_filter_args_info_detailed_help - }; + f->help = (struct ggo_help)DEFINE_GGO_HELP(resample_filter); } diff --git a/sched.h b/sched.h index 9c5c098e..234a8458 100644 --- a/sched.h +++ b/sched.h @@ -41,10 +41,7 @@ struct sched { * Before registering a task to the scheduler, the task structure must be * filled in properly by the caller. * - * If one of these functions sets \a t->error to a negative value, the - * task gets unregistered automatically. - * - * \sa struct sched. + * \sa \ref sched. */ struct task { /** diff --git a/server.c b/server.c index f3d5237f..f26abef5 100644 --- a/server.c +++ b/server.c @@ -91,6 +91,7 @@ #include "signal.h" #include "user_list.h" #include "color.h" +#include "ggo.h" #include "version.h" __printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log; @@ -236,13 +237,13 @@ void parse_config_or_die(int override) .print_errors = !conf.daemon_given }; server_cmdline_parser_config_file(cf, &conf, ¶ms); + daemon_set_loglevel(conf.loglevel_arg); conf.daemon_given = tmp; } if (conf.logfile_given) { daemon_set_logfile(conf.logfile_arg); daemon_open_log_or_die(); } - daemon_set_loglevel(conf.loglevel_arg); init_colors_or_die(); daemon_set_flag(DF_LOG_PID); daemon_set_flag(DF_LOG_LL); @@ -473,6 +474,15 @@ static int init_afs(int argc, char **argv) return afs_server_socket[0]; } +__noreturn static void print_help_and_die(void) +{ + struct ggo_help h = DEFINE_GGO_HELP(server); + bool d = conf.detailed_help_given; + + ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS); + exit(0); +} + static void server_init(int argc, char **argv) { struct server_cmdline_parser_params params = { @@ -488,7 +498,10 @@ static void server_init(int argc, char **argv) init_random_seed_or_die(); /* parse command line options */ server_cmdline_parser_ext(argc, argv, &conf, ¶ms); - HANDLE_VERSION_FLAG("server", conf); + daemon_set_loglevel(conf.loglevel_arg); + version_handle_flag("server", conf.version_given); + if (conf.help_given || conf.detailed_help_given) + print_help_and_die(); drop_privileges_or_die(conf.user_arg, conf.group_arg); /* parse config file, open log and set defaults */ parse_config_or_die(0); diff --git a/sideband.h b/sideband.h index 50daaa60..20e195b3 100644 --- a/sideband.h +++ b/sideband.h @@ -58,6 +58,8 @@ DESIGNATOR(EXIT__SUCCESS), \ /* Command failed. */ \ DESIGNATOR(EXIT__FAILURE), \ + /* The next chunk of the blob (addblob commands only) */ \ + DESIGNATOR(BLOB_DATA), \ /** Just prefix with \p SBD_. */ #define DESIGNATOR(x) SBD_ ## x diff --git a/skencil/overview.sk b/skencil/overview.sk deleted file mode 100644 index 5ab648c8..00000000 --- a/skencil/overview.sk +++ /dev/null @@ -1,474 +0,0 @@ -##Sketch 1 2 -document() -layout('A4',0) -layer('Layer 1',1,1,0,0,(0,0,0)) -lw(1) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(184.297,654.341,0) -bs(184.297,732.403,0) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('open file',(1.19433e-15,1,-1,1.19433e-15,181.259,668.683)) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('mp3 data',(-1.83691e-16,-1,1,-1.83691e-16,193.585,717.172)) -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(190.547,726.853,0) -bs(190.547,655.774,0) -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(145.824,181.165,0) -bs(228.568,181.165,0) -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(228.568,161.165,0) -bs(145.824,161.165,0) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('connect local socket',(1.19433e-15,1,-1,1.19433e-15,272.988,207.639)) -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(307.4,339.815,0) -bs(307.4,200,0) -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(280,200,0) -bs(280,339.815,0) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('server & audiod status',(-1.83691e-16,-1,1,-1.83691e-16,310.438,332.705)) -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(99.2303,144.289,0) -bs(99.2303,73.1918,0) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('s&a status',(-1.83691e-16,-1,1,-1.83691e-16,102.268,137.713)) -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(61.1648,150.482,0) -bs(61.1647,71.2323,0) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('cmd output',(-1.83691e-16,-1,1,-1.83691e-16,64.2028,142.553)) -lw(0.992126) -ld((0.10000000000000001, 1.0)) -b() -bs(24.7094,792.443,0) -bs(310.724,792.443,0) -bs(310.421,640.011,0) -bs(561.98,639.817,0) -bs(561.98,538.528,0) -bs(24.7094,538.528,0) -bs(24.7094,792.443,0) -bC() -fp((0,0,0)) -le() -lw(1) -Fn('Times-Italic') -txt('server host',(252.845,779.314)) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Italic') -txt('client host',(512.93,400.509)) -G() -lw(1) -r(119.09,0,0,-28.2587,39.0214,375.322) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(24) -txt('para_client',(45.2504,355.601)) -G_() -G() -lw(1) -r(143.312,0,0,-28.2587,408.597,189.57) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(24) -txt('sound_device',(414.265,169.849)) -G_() -G() -lw(1) -r(94.8684,0,0,-28.2587,34.9844,58.6731) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(24) -txt('terminal',(42.4226,38.9517)) -G_() -G() -lw(1) -r(131.201,0,0,-28.2587,237.937,189.4) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(24) -txt('para_audioc',(244.894,169.678)) -G_() -G() -lw(1) -r(94.8684,0,0,-28.2587,34.9844,189.4) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(24) -txt('para_gui',(40.4306,169.678)) -G_() -fp((0.9,0.9,0.9)) -lw(1) -r(177.037,0,0,-89.6831,384.699,788.574) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Bold') -Fs(36) -txt('paraslash',(399.201,755.055)) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Bold') -Fs(36) -txt('0.4.0',(437.217,712.433)) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('fork & exec',(153.764,182.009)) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('s&a status',(158.223,162.009)) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('fork & exec',(1.19433e-15,1,-1,1.19433e-15,70.3615,232.519)) -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(73.3994,201.263,0) -bs(73.3995,330.639,0) -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(103.54,330.639,0) -bs(103.539,201.263,0) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('cmd output',(-1.83691e-16,-1,1,-1.83691e-16,106.577,297.647)) -lw(0.992126) -ld((0.10000000000000001, 1.0)) -b() -bs(29.1803,415.392,0) -bs(570.132,415.357,0) -bs(570.132,26.3198,0) -bs(27.1619,26.3198,0) -bs(27.1619,413.359,0) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt(' server cmd',(2.57235e-15,1,-1,2.57235e-15,359.444,441.487)) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('result',(-1.83691e-16,-1,1,-1.83691e-16,380.89,496.536)) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('connect tcp',(1.19433e-15,1,-1,1.19433e-15,420.523,446.644)) -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(426.278,433.194,0) -bs(426.278,524.243,0) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('server status',(-1.83691e-16,-1,1,-1.83691e-16,438.254,513.515)) -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(431.433,522.224,0) -bs(431.433,434.203,0) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('mp3 data ',(-1.83691e-16,-1,1,-1.83691e-16,513.965,505.064)) -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(506.551,520.71,0) -bs(506.551,433.193,0) -G() -lw(1) -r(301.847,0,0,-28.2587,220.095,375.322) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(24) -txt('para_audiod',(311.703,355.601)) -G_() -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(144.486,419.683,0) -bs(144.486,431.685,0) -bs(368.033,431.685,0) -bs(368.033,531.257,0) -lw(1) -la1(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(151.551,419.179,0) -bs(151.551,424.722,0) -bs(374.601,424.722,0) -bs(374.601,529.224,0) -lw(1) -ld((4, 4)) -r(156.432,0,0,-101.428,50.1221,646.762) -G() -lw(1) -r(41.883,0,0,-28.2587,160.634,643.23) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(24) -txt('afh',(166.229,623.509)) -G_() -fp((0,0,0)) -le() -lw(1) -Fn('Times-Italic') -txt('afs process',(106.135,651.24)) -lw(1) -r(0,0,0,-0.504639,125.815,629.1) -lw(1) -ld((5, 5)) -r(272.999,0,0,-67.6189,281.238,614.467) -lw(1) -r(78.2156,0,0,-28.2587,289.816,609.925) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(24) -txt('vss',(313.588,590.204)) -lw(1) -r(73.674,0,0,-59.5452,470.974,609.925) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(24) -txt('sender',(477.579,574.056)) -lw(1) -r(167.533,0,0,-28.2587,289.816,577.629) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(24) -txt('cmd',(0.886791,0,0,1,355.258,557.908)) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Italic') -txt('server process',(286.284,618.44)) -G() -lw(1) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(216.351,599.242,0) -bs(272.871,599.242,0) -lw(1) -la1(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(216.351,603.697,0) -bs(272.87,603.697,0) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('open fd',(223.502,588.236)) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('request fd',(217.951,607.917)) -G_() -G() -lw(1) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(216.688,555.341,0) -bs(273.208,555.341,0) -lw(1) -la1(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(216.688,559.796,0) -bs(273.207,559.796,0) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('result',(228.885,545.344)) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('afs cmd',(222.83,563.006)) -G_() -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(79.9931,735.431,0) -bs(79.9931,652.573,0) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('osl result',(-1.83691e-16,-1,1,-1.83691e-16,83.0311,719.475)) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('osl query',(3.06152e-16,1,-1,3.06152e-16,68.1098,669.658)) -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(73.6709,653.078,0) -bs(73.6709,735.936,0) -G() -lw(1) -r(119.595,0,0,-46.425,75.8576,784.523) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -txt('file system',(108.732,771.908)) -G() -G() -lw(1) -r(48.4434,0,0,-21.1941,81.9131,764.929) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -txt('osl table',(85.9688,750.758)) -G_() -G() -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -txt('mp3 file',(143.495,750.295)) -lw(1) -r(48.4434,0,0,-21.1941,139.44,764.929) -G_() -G_() -G_() -lw(1) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(379.134,590.245,0) -bs(461.495,590.245,0) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -txt('mp3 data',(397.646,596.3)) -G() -lw(1) -r(120.099,0,0,-53.4896,68.2886,608.411) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(24) -txt('afs',(0.886791,0,0,1,115.93,576.074)) -G_() -G() -lw(1) -r(75.1101,0,0,-28.2587,56.6797,643.23) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(24) -txt('libosl',(67.5587,620.179)) -G_() -G() -lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(466.036,335.949,0) -bs(466.036,203.05,0) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('pcm data',(-1.83691e-16,-1,1,-1.83691e-16,476.076,294.966)) -G_() -guidelayer('Guide Lines',1,0,0,1,(0,0,1)) -guide(-307.905,0) -grid((0,0,20,20),0,(0,0,1),'Grid') diff --git a/spx.h b/spx.h index 6bcf1774..c4999f07 100644 --- a/spx.h +++ b/spx.h @@ -37,4 +37,3 @@ struct spx_header_info { int spx_process_header(unsigned char *packet, long bytes, struct spx_header_info *shi); -int spx_ctl(void *state, int request, void *ptr); diff --git a/spx_common.c b/spx_common.c index 3a4d5dd1..3bd3d48d 100644 --- a/spx_common.c +++ b/spx_common.c @@ -60,7 +60,7 @@ * * \return Standard. */ -int spx_ctl(void *state, int request, void *ptr) +static int spx_ctl(void *state, int request, void *ptr) { int ret = speex_decoder_ctl(state, request, ptr); diff --git a/stdin.c b/stdin.c index b25a0ba0..20b9250e 100644 --- a/stdin.c +++ b/stdin.c @@ -69,6 +69,12 @@ static int stdin_post_select(struct sched *s, struct task *t) sz = btr_pool_get_buffer(sit->btrp, &buf); if (sz == 0) return 0; + if (sit->must_set_nonblock_flag) { + ret = mark_fd_nonblocking(STDIN_FILENO); + if (ret < 0) + goto err; + sit->must_set_nonblock_flag = false; + } /* * Do not use the maximal size to avoid having only a single buffer * reference for the whole pool. This is bad because if that single @@ -82,6 +88,8 @@ static int stdin_post_select(struct sched *s, struct task *t) return 0; err: btr_remove_node(&sit->btrn); + /* Revert to blocking mode if necessary. */ + fcntl(STDIN_FILENO, F_SETFL, sit->fd_flags); //btr_pool_free(sit->btrp); return ret; } @@ -92,8 +100,7 @@ err: * \param sit The stdin task structure. * * This fills in the pre/post select function pointers of the task structure - * given by \a sit. Moreover, the stdin file desctiptor is set to nonblocking - * mode, and a buffer tree is created. + * given by \a sit and creates a buffer tree for I/O. */ void stdin_set_defaults(struct stdin_task *sit) { @@ -103,9 +110,18 @@ void stdin_set_defaults(struct stdin_task *sit) sit->task.post_select = stdin_post_select; sit->btrp = btr_pool_new("stdin", 128 * 1024); sprintf(sit->task.status, "stdin reader"); - ret = mark_fd_nonblocking(STDIN_FILENO); - if (ret >= 0) - return; - PARA_EMERG_LOG("%s\n", para_strerror(-ret)); - exit(EXIT_FAILURE); + /* + * Both STDIN_FILENO and STDOUT_FILENO may refer to the same open file + * description (the terminal), and thus share the same file status + * flags. In order to not interfere with the stdout task, we only get + * the file status flags for STDIN here and save a copy. The nonblock + * flag is set later on the first read. + */ + ret = fcntl(STDIN_FILENO, F_GETFL); + if (ret < 0) { + PARA_EMERG_LOG("F_GETFL: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + sit->fd_flags = ret; + sit->must_set_nonblock_flag = (sit->fd_flags & O_NONBLOCK) == 0; } diff --git a/stdin.h b/stdin.h index 54710ae5..cdf0c67f 100644 --- a/stdin.h +++ b/stdin.h @@ -14,6 +14,10 @@ struct stdin_task { struct btr_node *btrn; /** Use a buffer pool to minimize memcpy due to alignment problems. */ struct btr_pool *btrp; + /** The descriptor flags of STDIN at startup. */ + int fd_flags; + /** Whether we have to set STDIN to nonblocking mode. */ + bool must_set_nonblock_flag; }; void stdin_set_defaults(struct stdin_task *sit); diff --git a/stdout.c b/stdout.c index abf3d06f..cf33bf6d 100644 --- a/stdout.c +++ b/stdout.c @@ -62,6 +62,12 @@ static int stdout_post_select(struct sched *s, struct task *t) if (!FD_ISSET(STDOUT_FILENO, &s->wfds)) return 0; + if (sot->must_set_nonblock_flag) { + ret = mark_fd_nonblocking(STDOUT_FILENO); + if (ret < 0) + goto out; + sot->must_set_nonblock_flag = false; + } for (;;) { sz = btr_next_buffer(btrn, &buf); if (sz == 0) @@ -72,8 +78,11 @@ static int stdout_post_select(struct sched *s, struct task *t) btr_consume(btrn, ret); } out: - if (ret < 0) + if (ret < 0) { btr_remove_node(&sot->btrn); + /* Revert to blocking mode if necessary. */ + fcntl(STDOUT_FILENO, F_SETFL, sot->fd_flags); + } return ret; } /** @@ -82,7 +91,7 @@ out: * \param sot The stdout task structure. * * This fills in the pre/post select function pointers of the task structure - * given by \a sot and sets the stdout file descriptor to nonblocking mode. + * given by \a sot. */ void stdout_set_defaults(struct stdout_task *sot) { @@ -91,9 +100,13 @@ void stdout_set_defaults(struct stdout_task *sot) sot->task.pre_select = stdout_pre_select; sot->task.post_select = stdout_post_select; sprintf(sot->task.status, "stdout"); - ret = mark_fd_nonblocking(STDOUT_FILENO); - if (ret >= 0) - return; - PARA_EMERG_LOG("%s\n", para_strerror(-ret)); - exit(EXIT_FAILURE); + + /* See stdin.c for details. */ + ret = fcntl(STDOUT_FILENO, F_GETFL); + if (ret < 0) { + PARA_EMERG_LOG("F_GETFL: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + sot->fd_flags = ret; + sot->must_set_nonblock_flag = (sot->fd_flags & O_NONBLOCK) == 0; } diff --git a/stdout.h b/stdout.h index cc1f2626..a21b7ced 100644 --- a/stdout.h +++ b/stdout.h @@ -16,6 +16,10 @@ struct stdout_task { struct task task; /** Stdout is always a leaf node in the buffer tree. */ struct btr_node *btrn; + /** The descriptor flags of STDOUT at startup. */ + int fd_flags; + /** Whether we have to set STDOUT to nonblocking mode. */ + bool must_set_nonblock_flag; }; void stdout_set_defaults(struct stdout_task *sot); diff --git a/string.c b/string.c index dfcfa2cd..38e25b09 100644 --- a/string.c +++ b/string.c @@ -142,15 +142,18 @@ __must_check __malloc char *para_strdup(const char *s) __printf_2_0 unsigned xvasprintf(char **result, const char *fmt, va_list ap) { int ret; - size_t size; + size_t size = 150; va_list aq; + *result = para_malloc(size + 1); va_copy(aq, ap); - ret = vsnprintf(NULL, 0, fmt, aq); + ret = vsnprintf(*result, size, fmt, aq); va_end(aq); assert(ret >= 0); + if (ret < size) /* OK */ + return ret; size = ret + 1; - *result = para_malloc(size); + *result = para_realloc(*result, size); va_copy(aq, ap); ret = vsnprintf(*result, size, fmt, aq); va_end(aq); @@ -356,19 +359,35 @@ __malloc char *para_hostname(void) } /** - * Used to distinguish between read-only and read-write mode. + * Call a custom function for each complete line. + * + * \param flags Any combination of flags defined in \ref for_each_line_flags. + * \param buf The buffer containing data separated by newlines. + * \param size The number of bytes in \a buf. + * \param line_handler The custom function. + * \param private_data Pointer passed to \a line_handler. + * + * For each complete line in \p buf, \p line_handler is called. The first + * argument to \p line_handler is (a copy of) the current line, and \p + * private_data is passed as the second argument. If the \p FELF_READ_ONLY + * flag is unset, a pointer into \a buf is passed to the line handler, + * otherwise a pointer to a copy of the current line is passed instead. This + * copy is freed immediately after the line handler returns. + * + * The function returns if \p line_handler returns a negative value or no more + * lines are in the buffer. The rest of the buffer (last chunk containing an + * incomplete line) is moved to the beginning of the buffer if FELF_READ_ONLY is + * unset. * - * \sa for_each_line(), for_each_line_ro(). + * \return On success this function returns the number of bytes not handled to + * \p line_handler. The only possible error is a negative return value from the + * line handler. In this case processing stops and the return value of the line + * handler is returned to indicate failure. + * + * \sa \ref for_each_line_flags. */ -enum for_each_line_modes{ - /** Activate read-only mode. */ - LINE_MODE_RO, - /** Activate read-write mode. */ - LINE_MODE_RW -}; - -static int for_each_complete_line(enum for_each_line_modes mode, char *buf, - size_t size, line_handler_t *line_handler, void *private_data) +int for_each_line(unsigned flags, char *buf, size_t size, + line_handler_t *line_handler, void *private_data) { char *start = buf, *end; int ret, i, num_lines = 0; @@ -389,85 +408,29 @@ static int for_each_complete_line(enum for_each_line_modes mode, char *buf, } else end = next_cr; num_lines++; - if (!line_handler) { - start = ++end; - continue; - } - if (mode == LINE_MODE_RO) { - size_t s = end - start; - char *b = para_malloc(s + 1); - memcpy(b, start, s); - b[s] = '\0'; -// PARA_NOTICE_LOG("b: %s, start: %s\n", b, start); - ret = line_handler(b, private_data); - free(b); - } else { - *end = '\0'; - ret = line_handler(start, private_data); + if (!(flags & FELF_DISCARD_FIRST) || start != buf) { + if (flags & FELF_READ_ONLY) { + size_t s = end - start; + char *b = para_malloc(s + 1); + memcpy(b, start, s); + b[s] = '\0'; + ret = line_handler(b, private_data); + free(b); + } else { + *end = '\0'; + ret = line_handler(start, private_data); + } + if (ret < 0) + return ret; } - if (ret < 0) - return ret; start = ++end; } - if (!line_handler || mode == LINE_MODE_RO) - return num_lines; i = buf + size - start; - if (i && i != size) + if (i && i != size && !(flags & FELF_READ_ONLY)) memmove(buf, start, i); return i; } -/** - * Call a custom function for each complete line. - * - * \param buf The buffer containing data separated by newlines. - * \param size The number of bytes in \a buf. - * \param line_handler The custom function. - * \param private_data Pointer passed to \a line_handler. - * - * If \p line_handler is \p NULL, the function returns the number of complete - * lines in \p buf. Otherwise, \p line_handler is called for each complete - * line in \p buf. The first argument to \p line_handler is the current line, - * and \p private_data is passed as the second argument. The function returns - * if \p line_handler returns a negative value or no more lines are in the - * buffer. The rest of the buffer (last chunk containing an incomplete line) - * is moved to the beginning of the buffer. - * - * \return If \p line_handler is not \p NULL, this function returns the number - * of bytes not handled to \p line_handler on success, or the negative return - * value of the \p line_handler on errors. - * - * \sa for_each_line_ro(). - */ -int for_each_line(char *buf, size_t size, line_handler_t *line_handler, - void *private_data) -{ - return for_each_complete_line(LINE_MODE_RW, buf, size, line_handler, - private_data); -} - -/** - * Call a custom function for each complete line. - * - * \param buf Same meaning as in \p for_each_line(). - * \param size Same meaning as in \p for_each_line(). - * \param line_handler Same meaning as in \p for_each_line(). - * \param private_data Same meaning as in \p for_each_line(). - * - * This function behaves like \p for_each_line(), but \a buf is left unchanged. - * - * \return On success, the function returns the number of complete lines in \p - * buf, otherwise the (negative) return value of \p line_handler is returned. - * - * \sa for_each_line(). - */ -int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler, - void *private_data) -{ - return for_each_complete_line(LINE_MODE_RO, buf, size, line_handler, - private_data); -} - /** Return the hex characters of the lower 4 bits. */ #define hex(a) (hexchar[(a) & 15]) diff --git a/string.h b/string.h index d45c9199..935c7d45 100644 --- a/string.h +++ b/string.h @@ -34,6 +34,23 @@ struct para_buffer { void *private_data; }; +/** + * Controls the behavior of for_each_line(). + * + * \sa for_each_line(). + */ +enum for_each_line_flags { + /** Activate read-only mode. */ + FELF_READ_ONLY = 1 << 0, + /** Don't call line handler for the first input line. */ + FELF_DISCARD_FIRST = 1 << 1, +}; + +/** Used for \ref for_each_line(). */ +typedef int line_handler_t(char *, void *); +int for_each_line(unsigned flags, char *buf, size_t size, + line_handler_t *line_handler, void *private_data); + /** * Write the contents of a status item to a para_buffer. * @@ -72,12 +89,6 @@ __must_check __malloc char *para_logname(void); __must_check __malloc char *para_homedir(void); __malloc char *para_hostname(void); __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...); -/** Used for for_each_line() and for_each_line_ro(). */ -typedef int line_handler_t(char *, void *); -int for_each_line(char *buf, size_t size, line_handler_t *line_handler, - void *private_data); -int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler, - void *private_data); int para_atoi64(const char *str, int64_t *result); int para_atoi32(const char *str, int32_t *value); int get_loglevel_by_name(const char *txt); diff --git a/t/t0004-server.sh b/t/t0004-server.sh index 3c878eb7..04d6cd60 100755 --- a/t/t0004-server.sh +++ b/t/t0004-server.sh @@ -74,15 +74,15 @@ EOF $PARA_SERVER \ --logfile "$serverlog" \ - --config_file /dev/null \ + --config-file /dev/null \ --daemon \ --loglevel $loglevel \ --port $port \ - --afs_database_dir "$db" \ - --afs_socket "$sock" \ - --user_list "$user_list" \ - --http_port "$stream_port" \ - --dccp_port "$stream_port" + --afs-database-dir "$db" \ + --afs-socket "$sock" \ + --user-list "$user_list" \ + --http-port "$stream_port" \ + --dccp-port "$stream_port" fi for ((i=0; i < ${#commands[@]}; i++)); do @@ -114,9 +114,9 @@ for ((i=0; i < ${#commands[@]}; i++)); do test_expect_success "$command" " $PARA_CLIENT \ --loglevel $loglevel \ - --server_port $port \ - --key_file $privkey \ - --config_file /dev/null \ + --server-port $port \ + --key-file $privkey \ + --config-file /dev/null \ -- \ ${cmdline[$i]} > $command.out && { [[ -z \"${good[$i]}\" ]] || grep \"${good[$i]}\"; } < $command.out && diff --git a/time.c b/time.c index 6f6dd49e..fe431208 100644 --- a/time.c +++ b/time.c @@ -55,7 +55,8 @@ void d2tv(double x, struct timeval *tv) * * \return If \a b < \a a, this function returns -1, otherwise it returns 1. */ -int tv_diff(const struct timeval *b, const struct timeval *a, struct timeval *diff) +int tv_diff(const struct timeval *b, const struct timeval *a, + struct timeval *diff) { int ret = 1; @@ -85,7 +86,7 @@ int tv_diff(const struct timeval *b, const struct timeval *a, struct timeval *di * \param sum Contains the sum \a a + \a b on return. */ void tv_add(const struct timeval *a, const struct timeval *b, - struct timeval *sum) + struct timeval *sum) { sum->tv_sec = a->tv_sec + b->tv_sec; if (a->tv_usec + b->tv_usec >= 1000 * 1000) { @@ -104,7 +105,7 @@ void tv_add(const struct timeval *a, const struct timeval *b, * \param result Contains \a mult * \a tv on return. */ void tv_scale(const unsigned long mult, const struct timeval *tv, - struct timeval *result) + struct timeval *result) { uint64_t x = ((uint64_t)tv->tv_sec * 1000 * 1000 + tv->tv_usec) * mult; @@ -120,7 +121,7 @@ void tv_scale(const unsigned long mult, const struct timeval *tv, * \param result Contains (1 / mult) * tv on return. */ void tv_divide(const unsigned long divisor, const struct timeval *tv, - struct timeval *result) + struct timeval *result) { uint64_t x = ((uint64_t)tv->tv_sec * 1000 * 1000 + tv->tv_usec) / divisor; diff --git a/udp_recv.c b/udp_recv.c index f9c70ec8..436b298c 100644 --- a/udp_recv.c +++ b/udp_recv.c @@ -100,15 +100,9 @@ static void udp_recv_close(struct receiver_node *rn) static void *udp_recv_parse_config(int argc, char **argv) { - int ret; - struct udp_recv_args_info *tmp = - para_calloc(sizeof(struct udp_recv_args_info)); - - ret = udp_recv_cmdline_parser(argc, argv, tmp)? -E_UDP_SYNTAX : 1; - if (ret >= 0) - return tmp; - free(tmp); - return NULL; + struct udp_recv_args_info *tmp = para_calloc(sizeof(*tmp)); + udp_recv_cmdline_parser(argc, argv, tmp); + return tmp; } /* @@ -226,9 +220,6 @@ void udp_recv_init(struct receiver *r) r->post_select = udp_recv_post_select; r->parse_config = udp_recv_parse_config; r->free_config = udp_recv_free_config; - r->help = (struct ggo_help) { - .short_help = udp_recv_args_info_help, - .detailed_help = udp_recv_args_info_detailed_help - }; + r->help = (struct ggo_help)DEFINE_GGO_HELP(udp_recv); udp_recv_cmdline_parser_free(&dummy); } diff --git a/version.c b/version.c new file mode 100644 index 00000000..f8f7e6a3 --- /dev/null +++ b/version.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2013 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file version.c Some helpers for printing version and copyright strings. */ + +#include "para.h" + +/** \file version.h Macros for printing the version string. */ + +#include "git-version.h" + +/** + * Get the raw git version string + * + * \return The string generated by the GIT-VERSION-GEN script. It is passed + * as a preprocessor define during compilation. + */ +__a_const const char *version_git(void) +{ + return GIT_VERSION; +} + +/** + * Get the version string for an executable. + * + * \param pfx The program name (without the leading "para_"). + * + * \return A statically allocated string which contains the program name, the + * git version and the codename. It must not be freed by the caller. + */ +const char *version_single_line(const char *pfx) +{ + static char buf[100]; + snprintf(buf, sizeof(buf) - 1, + "para_%s " GIT_VERSION, pfx); + return buf; +} + +/** + * Get the full version text. + * + * \param pfx See \ref version_single_line(). + * + * \return A string containing the same text as returned by \ref + * version_single_line(), augmented by additional build information, a + * copyright text and the email address of the author. + * + * Like \ref version_single_line(), this string is stored in a statically + * allocated buffer and must not be freed. + */ +const char *version_text(const char *pfx) +{ + static char buf[512]; + + snprintf(buf, sizeof(buf) - 1, "%s\n" + "Copyright (C) 2013 Andre Noll\n" + "This is free software with ABSOLUTELY NO WARRANTY." + " See COPYING for details.\n" + "Report bugs to .\n" + "build date: " BUILD_DATE ",\n" + "build system: " UNAME_RS ",\n" + "compiler: " CC_VERSION ".\n", + version_single_line(pfx) + ); + return buf; +} + +/** + * Print the version text and exit successfully. + * + * \param pfx See \ref version_single_line(). + * \param flag Whether --version was given. + * + * If \a flag is false, this function does nothing. Otherwise it prints the + * full version text as returned by \ref version_text() and exits successfully. + */ +void version_handle_flag(const char *pfx, bool flag) +{ + if (!flag) + return; + printf("%s", version_text(pfx)); + exit(EXIT_SUCCESS); +} diff --git a/version.h b/version.h index 3d865211..3dd5ba21 100644 --- a/version.h +++ b/version.h @@ -1,19 +1,6 @@ -/** \file version.h Macros for printing the version string. */ - -#include "git-version.h" - -/** Version text printed by all executables if -V was given. */ -#define VERSION_TEXT(prefix) "para_" prefix " " PACKAGE_VERSION \ - " (" GIT_VERSION ": " CODENAME ")" "\n" \ - "Copyright (C) 2013 Andre Noll\n" \ - "This is free software with ABSOLUTELY NO WARRANTY." \ - " See COPYING for details.\n" \ - "Report bugs to .\n" - -/** Print out \p VERSION_TEXT and exit if version flag was given. */ -#define HANDLE_VERSION_FLAG(_prefix, _args_info_struct) \ - if (_args_info_struct.version_given) { \ - printf("%s", VERSION_TEXT(_prefix)); \ - exit(EXIT_SUCCESS); \ - } +/** \file version.h Functions for printing the version string. */ +const char *version_git(void); +const char *version_single_line(const char *pfx); +const char *version_text(const char *pfx); +void version_handle_flag(const char *pfx, bool flag); diff --git a/web/dia/overview.dia b/web/dia/overview.dia new file mode 100644 index 00000000..f9e01586 --- /dev/null +++ b/web/dia/overview.dia @@ -0,0 +1,3817 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Overview# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #para_server# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Incoming connections arrive via TCP at the dispatcher which creates a +command handler process for each connection. + +After the connecting client has been authenticated, the command +handler propagates the incoming request either to the audio file +selector (afs) or to the virtual streaming system (vss). Results are sent +back to the client. + +afs maintans the audio file database and is responsible for selecting +and loading audio files while vss controls the paraslash senders. When +vss needs to stream an audio file it requests an open file descriptor from +afs and feeds small chunks of data (e.g. mp3 frames) to the senders +which send the chunks to all connected clients.# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #dispatcher# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #senders# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #vss# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #command +handler# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #afs# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #para_server# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #para_audiod# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #para_audioc# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #para_gui# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #para_client# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #The two main applications of the paraslash suite (shaded green) are +para_server and para_audiod. Both run in the background usually. +para_server maintains the audio file database and acts as the streaming +source, while para_audiod is the streaming client. + +The two client programs, para_client and para_audioc communicate +with para_server and para_audiod, respectively. + +para_gui controls para_server/audiod by executing paraslash commands. +Command output is shown in a curses window. para_gui automatically +executes para_audioc to obtain the state of para_audiod and para_server +and the metadata of the current audio file. + +Network connections are shaded grey, local connections black.# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #para_audiod# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #The purpose of para_audiod is to download, decode and play an audio +stream received from para_server. It fetches the para_server status and +starts a suitable buffer tree (shaded blue) if an audio stream is available. + +The buffer tree usually consists of a receiver, any number of filters and +a writer. The receiver downloads the audio stream from para_server and +the filters decode or modify the received data. The writer plays the +decoded stream. + +The dispatcher acts on (local) requests from para_audioc, for example to +dump information about the current audio file.# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #The audio file selector (afs) accepts two different kinds of incoming +connections: A bidirectional pipe shared with para_server is used for +passing the file descriptor of the current audio file to the server +process. The local socket is used by command handlers which query +or update the database. + +To add a new file to the database, afs opens the file and locates an +audio format handler (afh) that recognizes the file. A new database +entry with metadata obtained from the afh is then added to the +database.# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #The audio file selector# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #audio files# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #para_server# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #afh# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #osl db# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #command handler# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #afs# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #The OSL database# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Metadata about all known audio files is stored in serveral tables of a +database which is driven by libosl, the object storage layer library. + +The "audio files" table is the main table of the database. It contains +path, hash and metadata of each known file. + +The "attributes" table maps each of the 64 possible attributes to a +string. The attribute value of the file's metadata is translated through +this table. + +The tables shown shaded are blob tables which support add, rm, mv, +cat, ls commands. All of these are optional. + +The "score" table describes the subset of admissible files for the +current playlist or mood. This table is created on demand, resides +only in memory and is discarded on exit. + +When the next audio file is to be streamed, the audio file selector gets +the entry with the highest score from the "score" table, obtains path, +hash, and metadata for this entry from the "audio files" table, opens +the path and verifies the hash.# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #audio files# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #playlists# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #images# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #attributes# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #lyrics# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #moods# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #score# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #dispatcher# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #status fetcher# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #para_server# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #receiver# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #filter1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #filter 2# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #writer# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #para_audioc# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/download.in.html b/web/download.in.html index 563b61af..393fce93 100644 --- a/web/download.in.html +++ b/web/download.in.html @@ -1,13 +1,73 @@

Download


-

Clone the git repository by executing

+Paraslash is only available as source code, no binary packages are +provided at this point. There are several ways to download the source: -

git clone git://paraslash.systemlinux.org/git paraslash

+
    +
  • git. -

    Or grab the tarball -of the current master branch, or download the latest version from the -download directory. All regular releases are -cryptographically signed. Since development -takes place in separate topic branches the master branch is expected -to be more stable than any of the released versions.

    + Clone the git repository by executing + +

      git clone git://paraslash.systemlinux.org/git paraslash  

    + +

    The repository contains the full history of the + project since 2006, all work in progress and the source + code for the web pages. Choosing this option allows to + check out any of the four integration branches maint, + master, next, pu (see the + + git_branches + + section of the manual). All previous releases + correspond to tagged commits and may be checked out + as well. Since development takes place in separate + topic branches the master branch is expected to be + more stable than any of the released versions.

    + +

    Compiling from git requires additional tools, + notably the autoconf package must be installed.

    + +
  • + +
  • regular releases. + + All released versions can be downloaded as compressed + tarballs from the + + download directory. + + These tarballs are + + cryptographically signed + + and contain a pre-generated configure script. They + can be compiled and installed without autoconf. + +
  • + +
  • master tarballs. + + Whenever significant changes are incorporated a + + tarball + + of the current master branch is created. All changes in + this tarball will be included in the next release. Like + for regular releases, a configure script is provided + for convenience. + +
  • + +
  • gitweb snapshots. + + The + + gitweb + + page contains a snapshot link for each revision. This + allows to get a specific revision without downloading + the full history. + +
  • +
diff --git a/web/index.in.html b/web/index.in.html index 13146a37..c9a82a87 100644 --- a/web/index.in.html +++ b/web/index.in.html @@ -1,6 +1,10 @@

Events


    +
  • 2013-07-29: paraslash-0.4.13 + (sig) + "spectral gravity" +
  • 2012-12-20: paraslash-0.4.12 (sig) "volatile relativity" diff --git a/web/manual.m4 b/web/manual.m4 index 3f5f148f..55bb49a6 100644 --- a/web/manual.m4 +++ b/web/manual.m4 @@ -406,7 +406,7 @@ the directory /var/paraslash that has been created during installation: sudo chown $LOGNAME /var/paraslash -Alternatively, use the --afs_socket Option to specify a different +Alternatively, use the --afs-socket Option to specify a different location for the AFS command socket. For this first try, we'll use the info loglevel to make the output @@ -592,7 +592,7 @@ The user_list file At startup para_server reads the user list file which contains one line per user. The default location of the user list file may be -changed with the --user_list option. +changed with the --user-list option. There should be at least one user in this file. Each user must have an RSA key pair. The public part of the key is needed by para_server @@ -639,7 +639,7 @@ restricted on platforms that support UNIX socket credentials which allow para_audiod to obtain the Unix credentials of the connecting process. -Use para_audiod's --user_allow option to allow connections only for +Use para_audiod's --user-allow option to allow connections only for a limited set of users. ----------------------- @@ -1770,7 +1770,7 @@ a curses window. By default the command para_audioc -- stat -p -is executed, but this can be customized via the --stat_cmd option. In +is executed, but this can be customized via the --stat-cmd option. In particular it possible to use para_client -- stat -p diff --git a/write.c b/write.c index 866ea433..6799db41 100644 --- a/write.c +++ b/write.c @@ -35,16 +35,11 @@ INIT_STDERR_LOGGING(loglevel) __noreturn static void print_help_and_die(void) { - int d = conf.detailed_help_given; - const char **p = d? write_args_info_detailed_help - : write_args_info_help; - - printf_or_die("%s\n\n", WRITE_CMDLINE_PARSER_PACKAGE "-" - WRITE_CMDLINE_PARSER_VERSION); - printf_or_die("%s\n\n", write_args_info_usage); - for (; *p; p++) - printf_or_die("%s\n", *p); - print_writer_helps(d); + struct ggo_help h = DEFINE_GGO_HELP(write); + bool d = conf.detailed_help_given; + + ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS); + print_writer_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS); exit(0); } @@ -107,7 +102,6 @@ static int setup_and_schedule(void) }, }; - loglevel = get_loglevel_by_name(conf.loglevel_arg); sit.btrn = btr_new_node(&(struct btr_node_description) EMBRACE(.name = "stdin")); stdin_set_defaults(&sit); @@ -173,9 +167,10 @@ int main(int argc, char *argv[]) { int ret; - writer_init(); write_cmdline_parser(argc, argv, &conf); - HANDLE_VERSION_FLAG("write", conf); + loglevel = get_loglevel_by_name(conf.loglevel_arg); + writer_init(); + version_handle_flag("write", conf.version_given); if (conf.help_given || conf.detailed_help_given) print_help_and_die(); diff --git a/write_common.c b/write_common.c index 44ccf88a..33ef8be6 100644 --- a/write_common.c +++ b/write_common.c @@ -121,23 +121,24 @@ void register_writer_node(struct writer_node *wn, struct btr_node *parent, /** * Print the help text of all writers to stdout. * - * \param detailed Whether to print the detailed help text. + * \param flags Passed to \ref ggo_print_help(). */ -void print_writer_helps(int detailed) +void print_writer_helps(unsigned flags) { int i; - printf_or_die("\nAvailable writers: \n\t"); + printf_or_die("\nAvailable writers: "); FOR_EACH_WRITER(i) printf_or_die("%s%s", i? " " : "", writer_names[i]); - printf_or_die("\n\n"); + printf_or_die("\n"); FOR_EACH_WRITER(i) { struct writer *w = writers + i; if (!w->help.short_help) continue; - printf_or_die("Options for %s:\n", writer_names[i]); - ggo_print_help(&w->help, detailed); + printf_or_die("\n%s: %s", writer_names[i], + w->help.purpose); + ggo_print_help(&w->help, flags); } } diff --git a/write_common.h b/write_common.h index 8788a4cf..ecad2d18 100644 --- a/write_common.h +++ b/write_common.h @@ -8,7 +8,7 @@ void writer_init(void); void *check_writer_arg_or_die(const char *wa, int *writer_num); -void print_writer_helps(int detailed); +void print_writer_helps(unsigned flags); void register_writer_node(struct writer_node *wn, struct btr_node *parent, struct sched *s); void get_btr_sample_rate(struct btr_node *btrn, int32_t *result);