The new logo in .png and .ico format.
Cooking for three weeks.
* refs/heads/t/logo-0.6:
Reduce size of paraslash.ico.
The new logo for v0.6.x.
Makefile
TODO
paraslash-*.tar.bz2
+paraslash-*.tar.xz
web/dia/overview.pdf
*.swp
*.rej
Any knowledge of how to work with mouse and icons is not required.
+Installing lopsub
+~~~~~~~~~~~~~~~~~
+ git clone git://git.tuebingen.mpg.de/lopsub
+ cd lopsub && make && sudo make install
+ (see http://people.tuebingen.mpg.de/maan/lopsub/)
+
Installing osl
~~~~~~~~~~~~~~
git clone git://git.tuebingen.mpg.de/osl
Example for cross-compiling
~~~~~~~~~~~~~~~~~~~~~~~~~~~
- export CROSS_COMPILE='armv6j-hardfloat-linux-gnueabi-'
+ export CC='armv6j-hardfloat-linux-gnueabi-gcc'
export PATH="/usr/cross/arm/bin:$PATH"
- export CC=${CROSS_COMPILE}gcc
-
export LDFLAGS='
-L/usr/sysroot/arm/lib
-L/usr/sysroot/arm/usr/lib
autoconf
autoheader
./configure --host=arm-linux-gnueabi --prefix /usr/sysroot/arm/usr/local
- make CROSS_COMPILE=$CROSS_COMPILE
+ make
For details see the user manual:
PACKAGE_TARNAME := @PACKAGE_TARNAME@
PACKAGE_VERSION := @PACKAGE_VERSION@
-INSTALL := @INSTALL@
M4 := @M4@
-GENGETOPT := @GENGETOPT@
-HELP2MAN := @HELP2MAN@
-
-ggo_descriptions_declared := @ggo_descriptions_declared@
executables := @executables@
-receivers := @receivers@
-filters := @filters@
-writers := @writers@
recv_objs := @recv_objs@
filter_objs := @filter_objs@
gui_objs := @gui_objs@
audiod_objs := @audiod_objs@
audioc_objs := @audioc_objs@
-fade_objs := @fade_objs@
+mixer_objs := @mixer_objs@
server_objs := @server_objs@
write_objs := @write_objs@
afh_objs := @afh_objs@
id3tag_cppflags := @id3tag_cppflags@
openssl_cppflags := @openssl_cppflags@
gcrypt_cppflags := @gcrypt_cppflags@
+lopsub_cppflags := @lopsub_cppflags@
ogg_cppflags := @ogg_cppflags@
mad_cppflags := @mad_cppflags@
faad_cppflags := @faad_cppflags@
readline_cppflags := @readline_cppflags@
alsa_cppflags := @alsa_cppflags@
oss_cppflags := @oss_cppflags@
-mp4v2_cppflags := @mp4v2_cppflags@
-clock_gettime_ldflags := @clock_gettime_ldflags@
id3tag_ldflags := @id3tag_ldflags@
ogg_ldflags := @ogg_ldflags@
vorbis_ldflags := @vorbis_ldflags@
faad_ldflags := @faad_ldflags@
mad_ldflags := @mad_ldflags@
flac_ldflags := @flac_ldflags@
+lopsub_ldflags := @lopsub_ldflags@
oss_ldflags := @oss_ldflags@
alsa_ldflags := @alsa_ldflags@
pthread_ldflags := @pthread_ldflags@
samplerate_ldflags := @samplerate_ldflags@
osl_ldflags := @osl_ldflags@
curses_ldflags := @curses_ldflags@
-core_audio_ldflags := @core_audio_ldflags@
crypto_ldflags := @crypto_ldflags@
iconv_ldflags := @iconv_ldflags@
-mp4v2_ldflags := @mp4v2_ldflags@
include Makefile.real
CC := cc
endif
+LOGLEVELS := LL_DEBUG,LL_INFO,LL_NOTICE,LL_WARNING,LL_ERROR,LL_CRIT,LL_EMERG
vardir := /var/paraslash
mandir := $(datarootdir)/man/man1
-STRIP := $(CROSS_COMPILE)strip
MKDIR_P := mkdir -p
prefixed_executables := $(addprefix para_, $(executables))
else
build_dir := build
endif
-ggo_dir := $(build_dir)/ggo
object_dir := $(build_dir)/objects
dep_dir := $(build_dir)/deps
man_dir := $(build_dir)/man/man1
-cmdline_dir := $(build_dir)/cmdline
-cmdlist_dir := $(build_dir)/cmdlist
m4depdir := $(build_dir)/m4deps
-help2man_dir := $(build_dir)/help2man
-m4_ggo_dir := m4/gengetopt
+lls_suite_dir := $(build_dir)/lls
+lls_m4_dir := m4/lls
test_dir := t
# sort removes duplicate words, which is all we need here
all_objs := $(sort $(recv_objs) $(filter_objs) $(client_objs) $(gui_objs) \
- $(audiod_objs) $(audioc_objs) $(fade_objs) $(server_objs) \
+ $(audiod_objs) $(audioc_objs) $(mixer_objs) $(server_objs) \
$(write_objs) $(afh_objs) $(play_objs))
-deps := $(addprefix $(dep_dir)/, $(filter-out %.cmdline.d, $(all_objs:.o=.d)))
-m4_deps := $(addprefix $(m4depdir)/, $(addsuffix .m4d, $(executables)))
+deps := $(addprefix $(dep_dir)/, $(all_objs:.o=.d))
+
+afh_objs += afh.lsg.o
+audioc_objs += audioc.lsg.o
+audiod_objs += $(addsuffix _cmd.lsg.o, recv filter audiod write) \
+ client.lsg.o audiod.lsg.o
+client_objs += client.lsg.o
+mixer_objs += mixer.lsg.o
+filter_objs += filter_cmd.lsg.o filter.lsg.o
+gui_objs += gui.lsg.o
+play_objs += $(addsuffix _cmd.lsg.o, recv filter play write) play.lsg.o
+recv_objs += recv_cmd.lsg.o recv.lsg.o
+server_objs += server_cmd.lsg.o server.lsg.o
+write_objs += write_cmd.lsg.o write.lsg.o
+
+cmd_suites := $(addsuffix _cmd, audiod server play recv filter write)
+suites := $(addprefix $(lls_suite_dir)/, $(cmd_suites) $(executables))
+m4_lls_deps := $(addsuffix .m4d, $(suites))
+lsg_h := $(addsuffix .lsg.h, $(suites))
# now prefix all objects with object dir
recv_objs := $(addprefix $(object_dir)/, $(recv_objs))
gui_objs := $(addprefix $(object_dir)/, $(gui_objs))
audiod_objs := $(addprefix $(object_dir)/, $(audiod_objs))
audioc_objs := $(addprefix $(object_dir)/, $(audioc_objs))
-fade_objs := $(addprefix $(object_dir)/, $(fade_objs))
+mixer_objs := $(addprefix $(object_dir)/, $(mixer_objs))
server_objs := $(addprefix $(object_dir)/, $(server_objs))
write_objs := $(addprefix $(object_dir)/, $(write_objs))
afh_objs := $(addprefix $(object_dir)/, $(afh_objs))
autocrap := config.h.in configure
tarball_pfx := $(PACKAGE_TARNAME)-$(GIT_VERSION)
tarball_delete := $(addprefix $(tarball_pfx)/, web .gitignore)
-tarball := $(tarball_pfx).tar.bz2
+tarball := $(tarball_pfx).tar.xz
-.PHONY: all clean clean2 distclean maintainer-clean install man tarball
all: $(prefixed_executables) $(man_pages)
+.PHONY: all mostlyclean clean distclean maintainer-clean install \
+ install-strip man dist tarball
+
man: $(man_pages)
-tarball: $(tarball)
-include $(m4_ggo_dir)/makefile
+include $(lls_m4_dir)/makefile
include $(test_dir)/makefile.test
ifeq ($(findstring clean, $(MAKECMDGOALS)),)
-include $(deps)
--include $(m4_deps)
+-include $(m4_lls_deps)
endif
-$(object_dir) $(man_dir) $(ggo_dir) $(cmdline_dir) $(dep_dir) $(m4depdir) \
- $(help2man_dir) $(cmdlist_dir):
+$(object_dir) $(man_dir) $(dep_dir) $(m4depdir) $(lls_suite_dir):
$(Q) $(MKDIR_P) $@
-# When in doubt, use brute force (Ken Thompson)
-TOUPPER = \
-$(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,\
-$(subst f,F,$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,\
-$(subst k,K,$(subst l,L,$(subst m,M,$(subst n,N,$(subst o,O,\
-$(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,\
-$(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X,$(subst y,Y,\
-$(subst z,Z,$1))))))))))))))))))))))))))
-
CPPFLAGS += -DBINDIR='"$(bindir)"'
CPPFLAGS += -DCOPYRIGHT_YEAR='"$(COPYRIGHT_YEAR)"'
CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
+CPPFLAGS += -DLOGLEVELS='$(LOGLEVELS)'
CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
CPPFLAGS += -I/usr/local/include
-CPPFLAGS += -I$(cmdline_dir)
-CPPFLAGS += -I$(cmdlist_dir)
-
-CFLAGS += -Os
-CFLAGS += -Wuninitialized
-CFLAGS += -Wchar-subscripts
-CFLAGS += -Werror-implicit-function-declaration
-CFLAGS += -Wmissing-noreturn
-CFLAGS += -Wbad-function-cast
-CFLAGS += -fno-strict-aliasing
-
-STRICT_CFLAGS = $(CFLAGS)
-STRICT_CFLAGS += -g -Wundef -W
+CPPFLAGS += -I$(lls_suite_dir)
+CPPFLAGS += $(lopsub_cppflags)
+
+STRICT_CFLAGS += -fno-strict-aliasing
+STRICT_CFLAGS += -g
+STRICT_CFLAGS += -Os
+STRICT_CFLAGS += -Wundef -W -Wuninitialized
+STRICT_CFLAGS += -Wchar-subscripts
+STRICT_CFLAGS += -Werror-implicit-function-declaration
+STRICT_CFLAGS += -Wmissing-noreturn
+STRICT_CFLAGS += -Wbad-function-cast
STRICT_CFLAGS += -Wredundant-decls
STRICT_CFLAGS += -Wno-sign-compare -Wno-unknown-pragmas
-STRICT_CFLAGS += -Wformat -Wformat-security
-STRICT_CFLAGS += -Wmissing-format-attribute
STRICT_CFLAGS += -Wdeclaration-after-statement
-
-LDFLAGS += $(clock_gettime_ldflags)
+STRICT_CFLAGS += -Wformat -Wformat-security -Wmissing-format-attribute
ifeq ($(uname_s),Linux)
# these cause warnings on *BSD
Q := @
endif
-$(cmdlist_dir)/%.command_list.h: %.cmd %.c | $(cmdlist_dir)
- @[ -z "$(Q)" ] || echo 'GEN $@'
- $(Q) ./command_util.bash h < $< >$@
-$(cmdlist_dir)/%.command_list.man: %.cmd %.c | $(cmdlist_dir)
- @[ -z "$(Q)" ] || echo 'GEN $@'
- $(Q) ./command_util.bash man < $< > $@
-$(cmdlist_dir)/%.completion.h: %.cmd | $(cmdlist_dir)
- @[ -z "$(Q)" ] || echo 'GEN $@'
- $(Q) ./command_util.bash compl $(strip $(call TOUPPER,$(*F)))_COMPLETERS \
- $(strip $(call TOUPPER,$(*F)))_COMMANDS < $< > $@
-
-$(cmdlist_dir)/server.command_list.h \
-$(cmdlist_dir)/server.command_list.man \
-$(cmdlist_dir)/server.completion.h \
-: command.c
-
-$(cmdlist_dir)/afs.command_list.h \
-$(cmdlist_dir)/afs.command_list.man \
-$(cmdlist_dir)/afs.completion.h \
-: afs.c aft.c attribute.c
-
-$(cmdlist_dir)/audiod.command_list.h \
-$(cmdlist_dir)/audiod.command_list.man \
-$(cmdlist_dir)/audiod.completion.h \
-: audiod_command.c
-
-server_command_lists := $(cmdlist_dir)/server.command_list.man \
- $(cmdlist_dir)/afs.command_list.man
-audiod_command_lists := $(cmdlist_dir)/audiod.command_list.man
-play_command_lists := $(cmdlist_dir)/play.command_list.man
-
-$(man_dir)/para_server.1: $(server_command_lists)
-$(man_dir)/para_audiod.1: $(audiod_command_lists)
-$(man_dir)/para_play.1: $(play_command_lists)
-
-$(man_dir)/para_server.1: man_util_command_lists := $(server_command_lists)
-$(man_dir)/para_audiod.1: man_util_command_lists := $(audiod_command_lists)
-$(man_dir)/para_play.1: man_util_command_lists := $(play_command_lists)
-
-$(man_dir)/para_%.1: $(ggo_dir)/%.ggo man_util.bash \
- git-version.h | $(man_dir) $(help2man_dir)
- @[ -z "$(Q)" ] || echo 'MAN $<'
- $(Q) \
- COMMAND_LISTS="$(man_util_command_lists)" \
- FILTERS="$(filters)" \
- GENGETOPT=$(GENGETOPT) \
- GGO_DIR=$(ggo_dir) \
- HELP2MAN=$(HELP2MAN) \
- HELP2MAN_DIR=$(help2man_dir) \
- RECEIVERS="$(receivers)" \
- VERSION="$(GIT_VERSION)" \
- WRITERS="$(writers)" \
- ./man_util.bash $@
+audiod_commands := $(addprefix $(lls_suite_dir)/, \
+ $(addsuffix _cmd.lsg.man, audiod recv filter write))
+filter_commands := $(lls_suite_dir)/filter_cmd.lsg.man
+play_commands := $(lls_suite_dir)/play_cmd.lsg.man
+recv_commands := $(lls_suite_dir)/recv_cmd.lsg.man
+server_commands := $(lls_suite_dir)/server_cmd.lsg.man
+write_commands := $(lls_suite_dir)/write_cmd.lsg.man
+
+$(man_dir)/para_audiod.1: $(audiod_commands)
+$(man_dir)/para_filter.1: $(filter_commands)
+$(man_dir)/para_play.1: $(play_commands)
+$(man_dir)/para_recv.1: $(recv_commands)
+$(man_dir)/para_server.1: $(server_commands)
+$(man_dir)/para_write.1: $(write_commands)
+
+$(man_dir)/para_audiod.1: all_commands := $(audiod_commands)
+$(man_dir)/para_filter.1: all_commands := $(filter_commands)
+$(man_dir)/para_play.1: all_commands := $(play_commands)
+$(man_dir)/para_recv.1: all_commands := $(recv_commands)
+$(man_dir)/para_server.1: all_commands := $(server_commands)
+$(man_dir)/para_write.1: all_commands := $(write_commands)
+
+$(man_dir)/para_%.1: $(lls_suite_dir)/%.lsg.man \
+ $(lls_m4_dir)/copyright.m4 | $(man_dir)
+ @[ -z "$(Q)" ] || echo 'LLSMAN $<'
+ $(Q) cat $< $(all_commands) > $@
+ $(Q) $(M4) -D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) $(lls_m4_dir)/copyright.m4 >> $@
$(object_dir)/%.o: %.c | $(object_dir)
-$(object_dir)/opus%.o $(dep_dir)/opus%.d: CPPFLAGS += $(opus_cppflags)
-$(object_dir)/gui.o $(object_dir)/gui%.o $(dep_dir)/gui%.d \
+$(object_dir)/opus%.o: CPPFLAGS += $(opus_cppflags)
+$(object_dir)/gui.o $(object_dir)/gui%.o \
: CPPFLAGS += $(curses_cppflags)
-$(object_dir)/spx%.o $(dep_dir)/spx%.d: CPPFLAGS += $(speex_cppflags)
-$(object_dir)/flac%.o $(dep_dir)/flac%.d: CPPFLAGS += $(flac_cppflags)
+$(object_dir)/spx%.o: CPPFLAGS += $(speex_cppflags)
+$(object_dir)/flac%.o: CPPFLAGS += $(flac_cppflags)
-$(object_dir)/mp3_afh.o $(dep_dir)/mp3_afh.d: CPPFLAGS += $(id3tag_cppflags)
-$(object_dir)/crypt.o $(dep_dir)/crypt.d: CPPFLAGS += $(openssl_cppflags)
-$(object_dir)/gcrypt.o $(dep_dir)/gcrypt.d: CPPFLAGS += $(gcrypt_cppflags)
-$(object_dir)/ao_write.o $(dep_dir)/ao_write.d: CPPFLAGS += $(ao_cppflags)
-$(object_dir)/aac_afh.o $(dep_dir)/aac_afh.d: CPPFLAGS += $(mp4v2_cppflags)
-$(object_dir)/alsa%.o $(dep_dir)/alsa%.d: CPPFLAGS += $(alsa_cppflags)
+$(object_dir)/mp3_afh.o: CPPFLAGS += $(id3tag_cppflags)
+$(object_dir)/crypt.o: CPPFLAGS += $(openssl_cppflags)
+$(object_dir)/gcrypt.o: CPPFLAGS += $(gcrypt_cppflags)
+$(object_dir)/ao_write.o: CPPFLAGS += $(ao_cppflags)
+$(object_dir)/alsa%.o: CPPFLAGS += $(alsa_cppflags)
-$(object_dir)/interactive.o $(dep_dir)/interactive.d \
+$(object_dir)/interactive.o \
: CPPFLAGS += $(readline_cppflags)
-$(object_dir)/resample_filter.o $(dep_dir)/resample_filter.d \
+$(object_dir)/resample_filter.o \
: CPPFLAGS += $(samplerate_cppflags)
-$(object_dir)/oss_write.o $(dep_dir)/oss_write.d \
+$(object_dir)/oss_write.o \
: CPPFLAGS += $(oss_cppflags)
-$(object_dir)/ao_write.o $(dep_dir)/ao_write.d \
+$(object_dir)/ao_write.o \
: CPPFLAGS += $(ao_cppflags) $(pthread_cppflags)
-$(object_dir)/mp3dec_filter.o $(dep_dir)/mp3dec_filter.d \
+$(object_dir)/mp3dec_filter.o \
: CPPFLAGS += $(mad_cppflags)
-$(object_dir)/aacdec_filter.o $(dep_dir)/aacdec_filter.d \
-$(object_dir)/aac_common.o $(dep_dir)/aac_common.d \
-$(object_dir)/aac_afh.o $(dep_dir)/aac_afh.d \
+$(object_dir)/aacdec_filter.o \
+$(object_dir)/aac_afh.o \
: CPPFLAGS += $(faad_cppflags)
-$(object_dir)/ogg_afh.o $(dep_dir)/ogg_afh.d \
-$(object_dir)/oggdec_filter.o $(dep_dir)/oggdec_filter.d \
+$(object_dir)/ogg_afh.o \
+$(object_dir)/oggdec_filter.o \
: CPPFLAGS += $(vorbis_cppflags)
-$(object_dir)/spx_common.o $(dep_dir)/spx_common.d \
-$(object_dir)/spxdec_filter.o $(dep_dir)/spxdec_filter.d \
-$(object_dir)/spx_afh.o $(dep_dir)/spx_afh.d \
-$(object_dir)/oggdec_filter.o $(dep_dir)/oggdec_filter.d \
-$(object_dir)/ogg_afh.o $(dep_dir)/ogg_afh.d \
-$(object_dir)/ogg_afh_common.o $(dep_dir)/ogg_afh_common.d \
-$(object_dir)/opus%.o $(dep_dir)/opus%.d \
+$(object_dir)/spx_common.o \
+$(object_dir)/spxdec_filter.o \
+$(object_dir)/spx_afh.o \
+$(object_dir)/oggdec_filter.o \
+$(object_dir)/ogg_afh.o \
+$(object_dir)/ogg_afh_common.o \
+$(object_dir)/opus%.o \
: CPPFLAGS += $(ogg_cppflags)
-$(object_dir)/afs.o $(dep_dir)/afs.d \
-$(object_dir)/aft.o $(dep_dir)/aft.d \
-$(object_dir)/attribute.o $(dep_dir)/attribute.d \
-$(object_dir)/blob.o $(dep_dir)/blob.d \
-$(object_dir)/mood.o $(dep_dir)/mood.d \
-$(object_dir)/playlist.o $(dep_dir)/playlist.d \
-$(object_dir)/score.o $(dep_dir)/score.d \
-$(object_dir)/server.o $(dep_dir)/server.d \
-$(object_dir)/vss.o $(dep_dir)/vss.d \
-$(object_dir)/command.o $(dep_dir)/command.d \
-$(object_dir)/http_send.o $(dep_dir)/http_send.d \
-$(object_dir)/dccp_send.o $(dep_dir)/dccp_send.d \
-$(object_dir)/udp_send.o $(dep_dir)/udp_send.d \
-$(object_dir)/send_common.o $(dep_dir)/send_common.d \
-$(object_dir)/mm.o $(dep_dir)/mm.d \
+$(object_dir)/afs.o \
+$(object_dir)/aft.o \
+$(object_dir)/attribute.o \
+$(object_dir)/blob.o \
+$(object_dir)/mood.o \
+$(object_dir)/playlist.o \
+$(object_dir)/score.o \
+$(object_dir)/server.o \
+$(object_dir)/vss.o \
+$(object_dir)/command.o \
+$(object_dir)/http_send.o \
+$(object_dir)/dccp_send.o \
+$(object_dir)/udp_send.o \
+$(object_dir)/send_common.o \
+$(object_dir)/mm.o \
: CPPFLAGS += $(osl_cppflags)
-$(object_dir)/%.cmdline.o: CFLAGS += -Wno-unused-function
$(object_dir)/compress_filter.o: CFLAGS += -O3
-$(object_dir)/%.o: %.c | $(object_dir)
- @[ -z "$(Q)" ] || echo 'CC $<'
- $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(STRICT_CFLAGS) $<
-
-$(object_dir)/%.cmdline.o: $(cmdline_dir)/%.cmdline.c $(cmdline_dir)/%.cmdline.h | $(object_dir)
+$(object_dir)/%.o: %.c | $(object_dir) $(dep_dir) $(lsg_h)
@[ -z "$(Q)" ] || echo 'CC $<'
- $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $<
-
-# The compiler outputs dependencies either as foo.h or as some_directory/foo.h,
-# depending on whether the latter file exists. Since make needs the directory
-# part we prefix the dependency as appropriate.
-$(dep_dir)/%.d: %.c | $(dep_dir)
- @[ -z "$(Q)" ] || echo 'DEP $<'
- $(Q) $(CC) $(CPPFLAGS) -MM -MG -MP -MT $@ -MT $(object_dir)/$(*F).o $< \
- | sed -e "s@ \([a-zA-Z0-9_]\{1,\}\.cmdline.h\)@ $(cmdline_dir)/\1@g" \
- -e "s@ \([a-zA-Z0-9_]\{1,\}\.command_list.h\)@ $(cmdlist_dir)/\1@g" \
- -e "s@ \([a-zA-Z0-9_]\{1,\}\.completion.h\)@ $(cmdlist_dir)/\1@g" > $@
+ $(Q) $(CC) -c -o $@ -MMD -MF $(dep_dir)/$(*F).d -MT $@ $(CPPFLAGS) \
+ $(STRICT_CFLAGS) $(CFLAGS) $<
para_recv para_afh para_play para_server: LDFLAGS += $(id3tag_ldflags)
para_write para_play para_audiod \
-: LDFLAGS += $(ao_ldflags) $(pthread_ldflags) $(core_audio_ldflags)
+: LDFLAGS += $(ao_ldflags) $(pthread_ldflags)
para_client para_audioc para_play : LDFLAGS += $(readline_ldflags)
para_server: LDFLAGS += $(osl_ldflags)
para_gui: LDFLAGS += $(curses_ldflags)
$(samplerate_ldflags) \
-lm
+para_mixer: LDFLAGS += -lm
+
para_write \
para_play \
para_audiod \
-para_fade \
+para_mixer \
: LDFLAGS += \
$(oss_ldflags) \
$(alsa_ldflags)
+para_afh \
+para_audioc \
+para_audiod \
+para_client \
+para_mixer \
+para_filter \
+para_gui \
+para_play \
+para_recv \
+para_server \
+para_write \
+: LDFLAGS += $(lopsub_ldflags)
+
para_server \
para_filter \
para_audiod \
$(faad_ldflags) \
$(flac_ldflags)
-para_server \
-para_play \
-para_afh \
-para_recv \
-: LDFLAGS += \
- $(mp4v2_ldflags)
-
para_afh para_recv para_server para_play: LDFLAGS += $(iconv_ldflags)
$(foreach exe,$(executables),$(eval para_$(exe): $$($(exe)_objs)))
@[ -z "$(Q)" ] || echo 'LD $@'
$(Q) $(CC) $^ -o $@ $(LDFLAGS)
-clean:
- @[ -z "$(Q)" ] || echo 'CLEAN'
+mostlyclean:
+ @[ -z "$(Q)" ] || echo 'MOSTLYCLEAN'
$(Q) rm -f para_*
$(Q) rm -rf $(object_dir)
-
-clean2: clean
- @[ -z "$(Q)" ] || echo 'CLEAN2'
+clean: mostlyclean
+ @[ -z "$(Q)" ] || echo 'CLEAN'
$(Q) rm -rf $(build_dir)
-distclean: clean2 test-clean
+distclean: clean
@[ -z "$(Q)" ] || echo 'DISTCLEAN'
$(Q) rm -f Makefile autoscan.log config.status config.log
- $(Q) rm -f GPATH GRTAGS GSYMS GTAGS
-
+ $(Q) rm -f config.h configure config.h.in
maintainer-clean: distclean
@[ -z "$(Q)" ] || echo 'MAINTAINER-CLEAN'
- $(Q) rm -f *.tar.bz2 config.h configure config.h.in
+ $(Q) rm -f *.tar.bz2 *.tar.xz
+ $(Q) rm -f GPATH GRTAGS GSYMS GTAGS
+
+INSTALL ?= install
+INSTALL_PROGRAM ?= $(INSTALL)
+INSTALL_DATA ?= $(INSTALL) -m 644
+ifneq ($(findstring strip, $(MAKECMDGOALS)),)
+ strip_option := -s
+endif
-install: all man
- $(MKDIR_P) $(bindir) $(mandir)
- $(INSTALL) -s --strip-program $(STRIP) -m 755 \
- $(prefixed_executables) $(bindir)
- $(INSTALL) -m 644 $(man_pages) $(mandir)
- $(MKDIR_P) $(vardir) >/dev/null 2>&1 || true # not fatal, so don't complain
+install install-strip: all man
+ $(MKDIR_P) $(DESTDIR)$(bindir) $(DESTDIR)$(mandir)
+ $(INSTALL) $(strip_option) $(prefixed_executables) $(DESTDIR)$(bindir)
+ $(INSTALL_DATA) $(man_pages) $(DESTDIR)$(mandir)
+ $(MKDIR_P) $(DESTDIR)$(vardir) >/dev/null 2>&1 || true # not fatal, so don't complain
-$(tarball):
+$(tarball) dist tarball:
$(Q) rm -rf $(tarball) $(tarball_pfx)
$(Q) git archive --format=tar --prefix=$(tarball_pfx)/ HEAD \
| tar --delete $(tarball_delete) > $(tarball_pfx).tar
$(Q) ./GIT-VERSION-GEN > $(tarball_pfx)/VERSION
$(Q) cp $(autocrap) $(tarball_pfx)
$(Q) tar rf $(tarball_pfx).tar $(tarball_pfx)/*
- $(Q) bzip2 -9 $(tarball_pfx).tar
+ $(Q) xz -9 $(tarball_pfx).tar
$(Q) ls -l $(tarball)
- $(Q) ln -sf $(tarball) paraslash-git.tar.bz2
+ $(Q) ln -sf $(tarball) paraslash-git.tar.xz
$(Q) rm -rf $(tarball_pfx)
NEWS
====
+------------------------------------
+0.6.0 (to be announced) "fuzzy flux"
+------------------------------------
+- Support for Mac OS X has been removed.
+- On Linux systems, glibc-2.17 or newer is required to build the
+ source tree.
+- Support for RSA public keys in ASN format (as generated by openssl
+ genrsa) has been removed. These keys have been deprecated since
+ 2011, so users should have long switched to keys generated with
+ ssh-keygen(1).
+- If libgcrypt is used as the crypto library, we now require version
+ 1.5.0 (released in 2011) or later.
+- The insecure RC4 stream cipher has been removed. It was superseded
+ by aes_ctr128 three years ago but the RC4 code had been kept for
+ backwards compatibility.
+- On Linux, abstract unix domain sockets are used unconditionally.
+- The "install" target no longer strips executables, the new
+ install-strip target can be used to get the old behaviour.
+- The clean targets have been renamed: clean2 is gone, and the new
+ mostlyclean removes only the executables and object files.
+- New target: check (identical to test).
+- The DESTDIR make variable is honored to prepend a path to the
+ installation directory. This feature is orthogonal to the --prefix
+ option to configure.
+- Minor WMA cleanups.
+- The aac audio format handler has been rewritten to use the mp4ff library.
+ See the manual for how to install the library on your system.
+- New status item: max_chunk_size. The value is stored in a previously
+ unused field of the afhi object of the aft table. Although backwards
+ compatible, users are encouraged to re-add m4a files to populate
+ the new field.
+- No more chunk tables for aac. Chunk boundaries are determined
+ dynamically at stream time.
+- Release and master branch tarballs are now compressed with xz rather
+ than bzip2.
+- The lopsub package is required to install the paraslash package.
+ Gengetopt is no longer needed.
+- make dep is gone. Dependencies have been created automatically for
+ a long time and it was never necessary to run make dep manually.
+- para_gui lost its --timeout option.
+- Most manual pages have been extended to include an overall
+ description of the command.
+- The --stream-delay option of para_audiod has been removed. It had
+ been a no-op for many years.
+- The deprecated --path option of the server ls command has been
+ removed. The command now prints full paths by default, making
+ --full-path a no-op. Hence --full-path has been depreacted and is
+ scheduled for removal in v0.6.1.
+- It is now possible to use 'CFLAGS' to override the default compiler
+ options.
+- para_fade has been renamed to para_mixer. The four modes of operation
+ (set, fade, snooze, sleep) are implemented as subcommands of the
+ new program.
+- The sleep subcommand of para_mixer (former sleep mode of para_fade)
+ lost its --wake-hour and --wake-min options in favor of the new
+ --wake-time option which also accepts relative wakeup times like
+ "+9:30".
+- The new --fade-exponent option of para_mixer allows for non-linear
+ channel fading.
+- The new logo.
+
+Downloads:
+[tarball](./releases/paraslash-git.tar.bz2),
+
-------------------------------------
0.5.7 (2016-12-31) "semantic density"
-------------------------------------
+++ /dev/null
-/*
- * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file aac.h Exported symbols from aac_common.c. */
-
-#include <neaacdec.h>
-
-NeAACDecHandle aac_open(void);
-int aac_find_esds(char *buf, size_t buflen, size_t *skip,
- unsigned long *decoder_length);
-ssize_t aac_find_entry_point(char *buf, size_t buflen, size_t *skip);
/** \file aac_afh.c para_server's aac audio format handler. */
#include <regex.h>
-#include <mp4v2/mp4v2.h>
+#include <neaacdec.h>
#include "para.h"
+
+/* To get the mp4ff_tag_t and mp4ff_metadata_t typedefs. */
+#define USE_TAGGING
+#include <mp4ff.h>
+
#include "error.h"
#include "portable_io.h"
#include "afh.h"
#include "string.h"
-#include "aac.h"
#include "fd.h"
-static int aac_find_stsz(char *buf, size_t buflen, off_t *skip)
+
+struct aac_afh_context {
+ const void *map;
+ size_t mapsize;
+ size_t fpos;
+ int32_t track;
+ mp4ff_t *mp4ff;
+ mp4AudioSpecificConfig masc;
+ mp4ff_callback_t cb;
+};
+
+static uint32_t aac_afh_read_cb(void *user_data, void *dest, uint32_t want)
{
- int i;
-
- for (i = 0; i + 16 < buflen; i++) {
- char *p = buf + i;
- unsigned sample_count, sample_size;
-
- if (p[0] != 's' || p[1] != 't' || p[2] != 's' || p[3] != 'z')
- continue;
- PARA_DEBUG_LOG("found stsz@%d\n", i);
- i += 8;
- sample_size = read_u32_be(buf + i);
- PARA_DEBUG_LOG("sample size: %u\n", sample_size);
- i += 4;
- sample_count = read_u32_be(buf + i);
- i += 4;
- PARA_DEBUG_LOG("sample count: %u\n", sample_count);
- *skip = i;
- return sample_count;
+ struct aac_afh_context *c = user_data;
+ uint32_t have, rv;
+
+ if (want == 0 || c->fpos >= c->mapsize) {
+ PARA_INFO_LOG("failed attempt to read %u bytes @%zu\n", want,
+ c->fpos);
+ errno = EAGAIN;
+ return -1;
}
- return -E_STSZ;
+ have = c->mapsize - c->fpos;
+ rv = PARA_MIN(have, want);
+ PARA_DEBUG_LOG("reading %u bytes @%zu\n", rv, c->fpos);
+ memcpy(dest, c->map + c->fpos, rv);
+ c->fpos += rv;
+ return rv;
}
-static int atom_cmp(const char *buf1, const char *buf2)
+static uint32_t aac_afh_seek_cb(void *user_data, uint64_t pos)
{
- return memcmp(buf1, buf2, 4)? 1 : 0;
+ struct aac_afh_context *c = user_data;
+ c->fpos = pos;
+ return 0;
}
-static int read_atom_header(char *buf, uint64_t *subsize, char type[5])
+static int32_t aac_afh_get_track(mp4ff_t *mp4ff, mp4AudioSpecificConfig *masc)
{
- uint64_t size = read_u32_be(buf);
-
- memcpy(type, buf + 4, 4);
- type[4] = '\0';
-
- PARA_DEBUG_LOG("size: %llu, type: %s\n", (long long unsigned)size, type);
- if (size != 1) {
- *subsize = size;
- return 8;
+ int32_t i, rc, num_tracks = mp4ff_total_tracks(mp4ff);
+
+ assert(num_tracks >= 0);
+ for (i = 0; i < num_tracks; i++) {
+ unsigned char *buf = NULL;
+ unsigned buf_size = 0;
+
+ mp4ff_get_decoder_config(mp4ff, i, &buf, &buf_size);
+ if (buf) {
+ rc = NeAACDecAudioSpecificConfig(buf, buf_size, masc);
+ free(buf);
+ if (rc < 0)
+ continue;
+ return i;
+ }
}
- buf += 4;
- size = 0;
- size = read_u64_be(buf);
- *subsize = size;
- return 16;
+ return -1; /* no audio track */
}
-static char *get_tag(char *p, int size)
+static int aac_afh_open(const void *map, size_t mapsize, void **afh_context)
{
- char *buf;
-
- assert(size > 0);
- buf = para_malloc(size + 1);
-
- memcpy(buf, p, size);
- buf[size] = '\0';
- PARA_DEBUG_LOG("size: %d: %s\n", size, buf);
- return buf;
+ int ret;
+ struct aac_afh_context *c = para_malloc(sizeof(*c));
+
+ c->map = map;
+ c->mapsize = mapsize;
+ c->fpos = 0;
+ c->cb.read = aac_afh_read_cb;
+ c->cb.seek = aac_afh_seek_cb;
+ c->cb.user_data = c;
+
+ ret = -E_MP4FF_OPEN;
+ c->mp4ff = mp4ff_open_read(&c->cb);
+ if (!c->mp4ff)
+ goto free_ctx;
+ c->track = aac_afh_get_track(c->mp4ff, &c->masc);
+ ret = -E_MP4FF_TRACK;
+ if (c->track < 0)
+ goto close_mp4ff;
+ *afh_context = c;
+ return 0;
+close_mp4ff:
+ mp4ff_close(c->mp4ff);
+free_ctx:
+ free(c);
+ *afh_context = NULL;
+ return ret;
}
-static void read_tags(char *buf, size_t buflen, struct afh_info *afhi)
+static void aac_afh_close(void *afh_context)
{
- char *p = buf;
+ struct aac_afh_context *c = afh_context;
+ mp4ff_close(c->mp4ff);
+ free(c);
+}
- while (p + 32 < buf + buflen) {
- char *q, type1[5], type2[5];
- uint64_t size1, size2;
- int ret, ret2;
+/**
+ * Libmp4ff function to reposition the file to the given sample.
+ *
+ * \param f The opaque handle returned by mp4ff_open_read().
+ * \param track The number of the (audio) track.
+ * \param sample Destination.
+ *
+ * We need this function to obtain the offset of the sample within the audio
+ * file. Unfortunately, it is not exposed in the mp4ff header.
+ *
+ * \return This function always returns 0.
+ */
+int32_t mp4ff_set_sample_position(mp4ff_t *f, const int32_t track, const int32_t sample);
- ret = read_atom_header(p, &size1, type1);
- ret2 = read_atom_header(p + ret, &size2, type2);
+static int aac_afh_get_chunk(long unsigned chunk_num, void *afh_context,
+ const char **buf, size_t *len)
+{
+ struct aac_afh_context *c = afh_context;
+ int32_t ss;
+ size_t offset;
+
+ assert(chunk_num <= INT_MAX);
+ /* this function always returns zero */
+ mp4ff_set_sample_position(c->mp4ff, c->track, chunk_num);
+ offset = c->fpos;
+ ss = mp4ff_read_sample_getsize(c->mp4ff, c->track, chunk_num);
+ if (ss <= 0)
+ return -E_MP4FF_BAD_SAMPLE;
+ assert(ss + offset <= c->mapsize);
+ *buf = c->map + offset;
+ *len = ss;
+ return 1;
+}
- if (size2 <= 16 || atom_cmp(type2, "data")) {
- p += size1;
- continue;
- }
- size2 -= 16;
- q = p + ret + ret2 + 8;
- if (q + size2 > buf + buflen)
- break;
- if (!atom_cmp(type1, "\xa9" "ART"))
- afhi->tags.artist = get_tag(q, size2);
- else if (!atom_cmp(type1, "\xa9" "alb"))
- afhi->tags.album = get_tag(q, size2);
- else if (!atom_cmp(type1, "\xa9" "nam"))
- afhi->tags.title = get_tag(q, size2);
- else if (!atom_cmp(type1, "\xa9" "cmt"))
- afhi->tags.comment = get_tag(q, size2);
- else if (!atom_cmp(type1, "\xa9" "day"))
- afhi->tags.year = get_tag(q, size2);
- p += size1;
- }
+static void _aac_afh_get_taginfo(const mp4ff_t *mp4ff, struct taginfo *tags)
+{
+ mp4ff_meta_get_artist(mp4ff, &tags->artist);
+ mp4ff_meta_get_title(mp4ff, &tags->title);
+ mp4ff_meta_get_date(mp4ff, &tags->year);
+ mp4ff_meta_get_album(mp4ff, &tags->album);
+ mp4ff_meta_get_comment(mp4ff, &tags->comment);
}
-static void read_meta(char *buf, size_t buflen, struct afh_info *afhi)
+/*
+ * Init m4a file and write some tech data to given pointers.
+ */
+static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
+ struct afh_info *afhi)
{
- char *p = buf;
+ int ret;
+ int32_t rv;
+ struct aac_afh_context *c;
+ int64_t tmp;
+ const char *buf;
+ size_t sz;
+ uint32_t n;
+
+ ret = aac_afh_open(map, numbytes, (void **)&c);
+ if (ret < 0)
+ return ret;
- while (p + 4 < buf + buflen) {
+ ret = -E_MP4FF_BAD_SAMPLERATE;
+ rv = mp4ff_get_sample_rate(c->mp4ff, c->track);
+ if (rv <= 0)
+ goto close;
+ afhi->frequency = rv;
- if (p[0] != 'i' || p[1] != 'l' || p[2] != 's' || p[3] != 't') {
- p++;
- continue;
- }
- p += 4;
- return read_tags(p, buflen - (p - buf), afhi);
+ ret = -E_MP4FF_BAD_CHANNEL_COUNT;
+ rv = mp4ff_get_channel_count(c->mp4ff, c->track);
+ if (rv <= 0)
+ goto close;
+ afhi->channels = rv;
+
+ ret = -E_MP4FF_BAD_SAMPLE_COUNT;
+ rv = mp4ff_num_samples(c->mp4ff, c->track);
+ if (rv <= 0)
+ goto close;
+ afhi->chunks_total = rv;
+ afhi->max_chunk_size = 0;
+ for (n = 0; n < afhi->chunks_total; n++) {
+ if (aac_afh_get_chunk(n, c, &buf, &sz) < 0)
+ break;
+ afhi->max_chunk_size = PARA_MAX((size_t)afhi->max_chunk_size, sz);
}
+
+ tmp = c->masc.sbr_present_flag == 1? 2048 : 1024;
+ afhi->seconds_total = tmp * afhi->chunks_total / afhi->frequency;
+ ms2tv(1000 * tmp / afhi->frequency, &afhi->chunk_tv);
+
+ if (aac_afh_get_chunk(0, c, &buf, &sz) >= 0)
+ numbytes -= buf - map;
+ afhi->bitrate = 8 * numbytes / afhi->seconds_total / 1000;
+ _aac_afh_get_taginfo(c->mp4ff, &afhi->tags);
+ ret = 1;
+close:
+ aac_afh_close(c);
+ return ret;
}
-static void aac_get_taginfo(char *buf, size_t buflen, struct afh_info *afhi)
+static uint32_t aac_afh_meta_read_cb(void *user_data, void *dest, uint32_t want)
{
- int i;
- uint64_t subsize;
- char type[5];
-
- for (i = 0; i + 24 < buflen; i++) {
- char *p = buf + i;
- if (p[0] != 'm' || p[1] != 'e' || p[2] != 't' || p[3] != 'a')
- continue;
- PARA_INFO_LOG("found metadata at offset %d\n", i);
- i += 8;
- p = buf + i;
- i += read_atom_header(p, &subsize, type);
- p = buf + i;
- return read_meta(p, buflen - i, afhi);
- }
- PARA_INFO_LOG("no meta data\n");
+ int fd = *(int *)user_data;
+ return read(fd, dest, want);
}
-static ssize_t aac_compute_chunk_table(struct afh_info *afhi,
- char *map, size_t numbytes)
+static uint32_t aac_afh_meta_seek_cb(void *user_data, uint64_t pos)
{
- int ret, i;
- size_t sum = 0;
- off_t skip;
+ int fd = *(int *)user_data;
+ return lseek(fd, pos, SEEK_SET);
+}
- ret = aac_find_stsz(map, numbytes, &skip);
- if (ret < 0)
- return ret;
- afhi->chunks_total = ret;
- PARA_DEBUG_LOG("sz table has %" PRIu32 " entries\n", afhi->chunks_total);
- afhi->chunk_table = para_malloc((afhi->chunks_total + 1) * sizeof(size_t));
- for (i = 1; i <= afhi->chunks_total; i++) {
- if (skip + 4 > numbytes)
- break;
- sum += read_u32_be(map + skip);
- afhi->chunk_table[i] = sum;
- skip += 4;
-// if (i < 10 || i + 10 > afhi->chunks_total)
-// PARA_DEBUG_LOG("offset #%d: %zu\n", i, afhi->chunk_table[i]);
- }
- return skip;
+static uint32_t aac_afh_meta_write_cb(void *user_data, void *dest, uint32_t want)
+{
+ int fd = *(int *)user_data;
+ return write(fd, dest, want);
}
-static int aac_set_chunk_tv(struct afh_info *afhi,
- mp4AudioSpecificConfig *mp4ASC, uint32_t *seconds)
+static uint32_t aac_afh_meta_truncate_cb(void *user_data)
{
- float tmp = mp4ASC->sbr_present_flag == 1? 2047 : 1023;
- struct timeval total;
- long unsigned ms;
-
- if (!mp4ASC->samplingFrequency)
- return -E_MP4ASC;
- ms = 1000.0 * afhi->chunks_total * tmp / mp4ASC->samplingFrequency;
- ms2tv(ms, &total);
- tv_divide(afhi->chunks_total, &total, &afhi->chunk_tv);
- PARA_INFO_LOG("%luHz, %lus (%" PRIu32 " x %lums)\n",
- mp4ASC->samplingFrequency, ms / 1000,
- afhi->chunks_total, tv2ms(&afhi->chunk_tv));
- if (ms < 1000)
- return -E_MP4ASC;
- *seconds = ms / 1000;
- return 1;
+ int fd = *(int *)user_data;
+ off_t offset = lseek(fd, 0, SEEK_CUR);
+ return ftruncate(fd, offset);
}
-/*
- * Init m4a file and write some tech data to given pointers.
- */
-static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
- struct afh_info *afhi)
+static void replace_tag(mp4ff_tag_t *tag, const char *new_val, bool *found)
{
- int i;
- size_t skip;
- ssize_t ret;
- unsigned long rate = 0, decoder_len;
- unsigned char channels = 0;
- mp4AudioSpecificConfig mp4ASC;
- NeAACDecHandle handle = NULL;
-
- ret = aac_find_esds(map, numbytes, &skip, &decoder_len);
- if (ret < 0)
- goto out;
- aac_get_taginfo(map, numbytes, afhi);
- handle = aac_open();
- ret = -E_AAC_AFH_INIT;
- if (NeAACDecInit(handle, (unsigned char *)map + skip, decoder_len,
- &rate, &channels))
- goto out;
- if (!channels)
- goto out;
- PARA_DEBUG_LOG("rate: %lu, channels: %d\n", rate, channels);
- ret = -E_MP4ASC;
- if (NeAACDecAudioSpecificConfig((unsigned char *)map + skip,
- numbytes - skip, &mp4ASC))
- goto out;
- if (!mp4ASC.samplingFrequency)
- goto out;
- ret = aac_compute_chunk_table(afhi, map, numbytes);
- if (ret < 0)
- goto out;
- skip = ret;
- ret = aac_set_chunk_tv(afhi, &mp4ASC, &afhi->seconds_total);
- if (ret < 0)
- goto out;
- ret = aac_find_entry_point(map + skip, numbytes - skip, &skip);
- if (ret < 0)
- goto out;
- afhi->chunk_table[0] = ret;
- for (i = 1; i<= afhi->chunks_total; i++)
- afhi->chunk_table[i] += ret;
- afhi->channels = channels;
- afhi->frequency = rate;
- ret = (afhi->chunk_table[afhi->chunks_total] - afhi->chunk_table[0]) * 8; /* bits */
- ret += (channels * afhi->seconds_total * 500); /* avoid rounding error */
- afhi->bitrate = ret / (channels * afhi->seconds_total * 1000);
- ret = 1;
-out:
- if (handle)
- NeAACDecClose(handle);
- return ret;
+ free(tag->value);
+ tag->value = para_strdup(new_val);
+ *found = true;
}
-static int aac_rewrite_tags(const char *map, size_t mapsize,
- struct taginfo *tags, int fd, const char *filename)
+static void add_tag(mp4ff_metadata_t *md, const char *item, const char *value)
{
- MP4FileHandle h;
- const MP4Tags *mdata;
- int ret = write_all(fd, map, mapsize);
+ md->tags[md->count].item = para_strdup(item);
+ md->tags[md->count].value = para_strdup(value);
+ md->count++;
+}
+static int aac_afh_rewrite_tags(const char *map, size_t mapsize,
+ struct taginfo *tags, int fd, __a_unused const char *filename)
+{
+ int ret, i;
+ int32_t rv;
+ mp4ff_metadata_t metadata;
+ mp4ff_t *mp4ff;
+ mp4ff_callback_t cb = {
+ .read = aac_afh_meta_read_cb,
+ .seek = aac_afh_meta_seek_cb,
+ .write = aac_afh_meta_write_cb,
+ .truncate = aac_afh_meta_truncate_cb,
+ .user_data = &fd
+ };
+ bool found_artist = false, found_title = false, found_album = false,
+ found_year = false, found_comment = false;
+
+ ret = write_all(fd, map, mapsize);
if (ret < 0)
return ret;
lseek(fd, 0, SEEK_SET);
- h = MP4Modify(filename, 0);
- if (!h) {
- PARA_ERROR_LOG("MP4Modify() failed, fd = %d\n", fd);
- return -E_MP4V2;
- }
- mdata = MP4TagsAlloc();
- assert(mdata);
- if (!MP4TagsFetch(mdata, h)) {
- PARA_ERROR_LOG("MP4Tags_Fetch() failed\n");
- ret = -E_MP4V2;
- goto close;
- }
- if (!MP4TagsSetAlbum(mdata, tags->album)) {
- PARA_ERROR_LOG("Could not set album\n");
- ret = -E_MP4V2;
- goto tags_free;
- }
- if (!MP4TagsSetArtist(mdata, tags->artist)) {
- PARA_ERROR_LOG("Could not set album\n");
- ret = -E_MP4V2;
- goto tags_free;
- }
- if (!MP4TagsSetComments(mdata, tags->comment)) {
- PARA_ERROR_LOG("Could not set comment\n");
- ret = -E_MP4V2;
- goto tags_free;
- }
- if (!MP4TagsSetName(mdata, tags->title)) {
- PARA_ERROR_LOG("Could not set title\n");
- ret = -E_MP4V2;
- goto tags_free;
- }
- if (!MP4TagsSetReleaseDate(mdata, tags->year)) {
- PARA_ERROR_LOG("Could not set release date\n");
- ret = -E_MP4V2;
- goto tags_free;
- }
+ mp4ff = mp4ff_open_read_metaonly(&cb);
+ if (!mp4ff)
+ return -E_MP4FF_OPEN;
- if (!MP4TagsStore(mdata, h)) {
- PARA_ERROR_LOG("Could not store tags\n");
- ret = -E_MP4V2;
- goto tags_free;
+ ret = -E_MP4FF_META_READ;
+ rv = mp4ff_meta_get_num_items(mp4ff);
+ if (rv < 0)
+ goto close;
+ metadata.count = rv;
+ PARA_NOTICE_LOG("%d metadata item(s) found\n", rv);
+
+ metadata.tags = para_malloc((metadata.count + 5) * sizeof(mp4ff_tag_t));
+ for (i = 0; i < metadata.count; i++) {
+ mp4ff_tag_t *tag = metadata.tags + i;
+
+ ret = -E_MP4FF_META_READ;
+ if (mp4ff_meta_get_by_index(mp4ff, i,
+ &tag->item, &tag->value) < 0)
+ goto free_tags;
+ PARA_INFO_LOG("found: %s: %s\n", tag->item, tag->value);
+ if (!strcmp(tag->item, "artist"))
+ replace_tag(tag, tags->artist, &found_artist);
+ else if (!strcmp(tag->item, "title"))
+ replace_tag(tag, tags->title, &found_title);
+ else if (!strcmp(tag->item, "album"))
+ replace_tag(tag, tags->album, &found_album);
+ else if (!strcmp(tag->item, "date"))
+ replace_tag(tag, tags->year, &found_year);
+ else if (!strcmp(tag->item, "comment"))
+ replace_tag(tag, tags->comment, &found_comment);
}
+ if (!found_artist)
+ add_tag(&metadata, "artist", tags->artist);
+ if (!found_title)
+ add_tag(&metadata, "title", tags->title);
+ if (!found_album)
+ add_tag(&metadata, "album", tags->album);
+ if (!found_year)
+ add_tag(&metadata, "date", tags->year);
+ if (!found_comment)
+ add_tag(&metadata, "comment", tags->comment);
+ ret = -E_MP4FF_META_WRITE;
+ if (mp4ff_meta_update(&cb, &metadata) < 0)
+ goto free_tags;
ret = 1;
-tags_free:
- MP4TagsFree(mdata);
+free_tags:
+ for (; i > 0; i--) {
+ free(metadata.tags[i - 1].item);
+ free(metadata.tags[i - 1].value);
+ }
+ free(metadata.tags);
close:
- MP4Close(h, 0);
+ mp4ff_close(mp4ff);
return ret;
}
{
afh->get_file_info = aac_get_file_info,
afh->suffixes = aac_suffixes;
- afh->rewrite_tags = aac_rewrite_tags;
+ afh->rewrite_tags = aac_afh_rewrite_tags;
+ afh->open = aac_afh_open;
+ afh->get_chunk = aac_afh_get_chunk;
+ afh->close = aac_afh_close;
}
+++ /dev/null
-/*
- * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-/*
- * based in parts on libfaad, Copyright (C) 2003-2005 M. Bakker,
- * Ahead Software AG
- */
-
-/** \file aac_common.c Common functions of aac_afh and aadcec. */
-
-#include "para.h"
-#include "aac.h"
-#include "error.h"
-#include "portable_io.h"
-
-/**
- * Get a new libfaad decoder handle.
- *
- * \return The handle returned by NeAACDecOpen().
- */
-NeAACDecHandle aac_open(void)
-{
- NeAACDecHandle h = NeAACDecOpen();
- NeAACDecConfigurationPtr c = NeAACDecGetCurrentConfiguration(h);
-
- c->defObjectType = LC;
- c->outputFormat = FAAD_FMT_16BIT;
- c->downMatrix = 0;
- NeAACDecSetConfiguration(h, c);
- return h;
-}
-
-static unsigned long aac_read_decoder_length(char *buf, int *description_len)
-{
- uint8_t b;
- uint8_t numBytes = 0;
- unsigned long length = 0;
-
- do {
- b = buf[numBytes];
- numBytes++;
- length = (length << 7) | (b & 0x7F);
- } while
- ((b & 0x80) && numBytes < 4);
- *description_len = numBytes;
- return length;
-}
-
-/**
- * search for the position and the length of the decoder configuration
- *
- * \param buf buffer to seach
- * \param buflen length of \a buf
- * \param skip Upon succesful return, this contains the offset in \a buf where
- * the decoder config starts.
- * \param decoder_length result pointer that is filled in with the length of
- * the decoder configuration on success.
- *
- * \return positive on success, negative on errors
- */
-int aac_find_esds(char *buf, size_t buflen, size_t *skip,
- unsigned long *decoder_length)
-{
- size_t i;
-
- for (i = 0; i + 4 < buflen; i++) {
- char *p = buf + i;
- int description_len;
-
- if (p[0] != 'e' || p[1] != 's' || p[2] != 'd' || p[3] != 's')
- continue;
- i += 8;
- p = buf + i;
- PARA_INFO_LOG("found esds@%zu, next: %x\n", i, (unsigned)*p);
- if (*p == 3)
- i += 8;
- else
- i += 6;
- p = buf + i;
- PARA_INFO_LOG("next: %x\n", (unsigned)*p);
- if (*p != 4)
- continue;
- i += 18;
- p = buf + i;
- PARA_INFO_LOG("next: %x\n", (unsigned)*p);
- if (*p != 5)
- continue;
- i++;
- p = buf + i;
- *decoder_length = aac_read_decoder_length(p, &description_len);
- PARA_INFO_LOG("decoder length: %lu\n", *decoder_length);
- i += description_len;
- *skip = i;
- return 1;
- }
- return -E_ESDS;
-}
-
-/**
- * search for the first entry in the stco table
- *
- * \param buf buffer to seach
- * \param buflen length of \a buf
- * \param skip Upon succesful return, this contains the number
- * of bytes to skip from the input buffer.
- *
- * \return the position of the first entry in the table on success,
- * -E_STCO on errors.
- */
-ssize_t aac_find_entry_point(char *buf, size_t buflen, size_t *skip)
-{
- ssize_t ret;
- size_t i;
-
- for (i = 0; i + 20 < buflen; i++) {
- char *p = buf + i;
-
- if (p[0] != 's' || p[1] != 't' || p[2] != 'c' || p[3] != 'o')
- continue;
- PARA_INFO_LOG("found stco@%zu\n", i);
- i += 12;
- ret = read_u32_be(buf + i); /* first offset */
- i += 4;
- PARA_INFO_LOG("entry point: %zd\n", ret);
- *skip = i;
- return ret;
- }
- PARA_WARNING_LOG("stco not found, buflen: %zu\n", buflen);
- return -E_STCO;
-}
/** \file aacdec_filter.c paraslash's aac (m4a) decoder. */
#include <regex.h>
+#include <neaacdec.h>
#include "para.h"
+#include "portable_io.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "error.h"
#include "string.h"
-#include "aac.h"
/** Give up decoding after that many errors. */
#define MAX_ERRORS 20
struct private_aacdec_data {
/** the return value of aac_open */
NeAACDecHandle handle;
- /** info about the currently decoded frame */
- NeAACDecFrameInfo frame_info;
/** whether this instance of the aac decoder is already initialized */
- int initialized;
- /**
- * return value of aac_find_esds(). Used to call the right aacdec
- * init function
- */
- unsigned long decoder_length;
+ bool initialized;
/** number of times the decoder returned an error */
unsigned error_count;
/** number of bytes already consumed from the imput stream */
size_t consumed_total;
- /** return value of aac_find_entry_point */
- size_t entry;
/** The number of channels of the current stream. */
unsigned int channels;
/** Current sample rate in Hz. */
static void aacdec_open(struct filter_node *fn)
{
+ NeAACDecConfigurationPtr c;
struct private_aacdec_data *padd = para_calloc(sizeof(*padd));
+ padd->handle = NeAACDecOpen();
+ c = NeAACDecGetCurrentConfiguration(padd->handle);
+ c->defObjectType = LC;
+ c->outputFormat = FAAD_FMT_16BIT;
+ c->downMatrix = 0;
+ NeAACDecSetConfiguration(padd->handle, c);
+
fn->private_data = padd;
fn->min_iqs = 2048;
- padd->handle = aac_open();
}
static void aacdec_close(struct filter_node *fn)
struct btr_node *btrn = fn->btrn;
struct private_aacdec_data *padd = fn->private_data;
int i, ret;
- char *p, *inbuf, *outbuffer;
- char *btr_buf;
- size_t len, skip, consumed, loaded;
+ char *inbuf, *outbuf, *btrbuf;
+ size_t len, consumed, loaded = 0;
+ NeAACDecFrameInfo frame_info;
next_buffer:
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (!padd->initialized) {
unsigned long rate = 0;
unsigned char channels = 0;
- ret = aac_find_esds(inbuf, len, &skip, &padd->decoder_length);
+ ret = NeAACDecInit(padd->handle, (unsigned char *)inbuf,
+ len, &rate, &channels);
+ PARA_INFO_LOG("decoder init: %d\n", ret);
if (ret < 0) {
- PARA_INFO_LOG("%s\n", para_strerror(-ret));
- ret = NeAACDecInit(padd->handle, (unsigned char *)inbuf,
- len, &rate, &channels);
- PARA_INFO_LOG("decoder init: %d\n", ret);
- if (ret < 0) {
- ret = -E_AACDEC_INIT;
- goto out;
- }
- consumed = ret;
- } else {
- PARA_INFO_LOG("decoder len: %lu\n",
- padd->decoder_length);
- consumed += skip;
- p = inbuf + consumed;
ret = -E_AACDEC_INIT;
- if (NeAACDecInit2(padd->handle, (unsigned char *)p,
- padd->decoder_length, &rate,
- &channels) != 0)
- goto out;
+ goto err;
}
+ consumed = ret;
padd->sample_rate = rate;
padd->channels = channels;
PARA_INFO_LOG("rate: %u, channels: %u\n",
padd->sample_rate, padd->channels);
- padd->initialized = 1;
+ padd->initialized = true;
}
- if (padd->decoder_length > 0) {
- consumed = 0;
- if (!padd->entry) {
- ret = aac_find_entry_point(inbuf + consumed,
- len - consumed, &skip);
- if (ret < 0) {
- ret = len;
- goto out;
- }
- consumed += skip;
- padd->entry = ret;
- PARA_INFO_LOG("entry: %zu\n", padd->entry);
- }
- ret = len;
- if (padd->consumed_total + len < padd->entry)
- goto out;
- if (padd->consumed_total < padd->entry)
- consumed = padd->entry - padd->consumed_total;
- }
- for (; consumed < len; consumed++)
- if ((inbuf[consumed] & 0xfe) == 0x20)
- break;
if (consumed >= len)
goto success;
- p = inbuf + consumed;
//PARA_CRIT_LOG("consumed: %zu (%zu + %zu), have: %zu\n", padd->consumed_total + consumed,
// padd->consumed_total, consumed, len - consumed);
- outbuffer = NeAACDecDecode(padd->handle, &padd->frame_info,
- (unsigned char *)p, len - consumed);
- if (padd->frame_info.error) {
- int err = padd->frame_info.error;
+ outbuf = NeAACDecDecode(padd->handle, &frame_info,
+ (unsigned char *)inbuf + consumed, len - consumed);
+ if (frame_info.error) {
+ int err = frame_info.error;
ret = -E_AAC_DECODE;
if (padd->error_count++ > MAX_ERRORS)
goto err;
- /* Suppress non-fatal bitstream error message at BOF/EOF */
- if (len < fn->min_iqs || padd->consumed_total == 0) {
- consumed = len;
- goto success;
- }
- PARA_ERROR_LOG("%s\n", NeAACDecGetErrorMessage(err));
- PARA_ERROR_LOG("consumed: %zu + %zu + %lu\n",
+ PARA_NOTICE_LOG("error #%u: (%s)\n", padd->error_count,
+ NeAACDecGetErrorMessage(err));
+ PARA_NOTICE_LOG("consumed (total, buffer, frame): "
+ "%zu, %zu, %lu\n",
padd->consumed_total, consumed,
- padd->frame_info.bytesconsumed);
- if (consumed < len)
- consumed++; /* catch 21 */
+ frame_info.bytesconsumed);
+ consumed++; /* just eat one byte and hope for the best */
goto success;
}
padd->error_count = 0;
- //PARA_CRIT_LOG("decoder ate %lu\n", padd->frame_info.bytesconsumed);
- consumed += padd->frame_info.bytesconsumed;
- ret = consumed;
- if (!padd->frame_info.samples)
- goto out;
- btr_buf = para_malloc(2 * padd->frame_info.samples);
- loaded = 0;
- for (i = 0; i < padd->frame_info.samples; i++) {
- short sh = ((short *)outbuffer)[i];
- write_int16_host_endian(btr_buf + loaded, sh);
+ //PARA_CRIT_LOG("decoder ate %lu\n", frame_info.bytesconsumed);
+ consumed += frame_info.bytesconsumed;
+ if (!frame_info.samples)
+ goto success;
+ btrbuf = para_malloc(2 * frame_info.samples);
+ for (i = 0; i < frame_info.samples; i++) {
+ short sh = ((short *)outbuf)[i];
+ write_int16_host_endian(btrbuf + loaded, sh);
loaded += 2;
}
- btr_add_output(btr_buf, loaded, btrn);
+ btr_add_output(btrbuf, loaded, btrn);
success:
- ret = consumed;
-out:
- if (ret >= 0) {
- padd->consumed_total += ret;
- btr_consume(btrn, ret);
+ btr_consume(btrn, consumed);
+ padd->consumed_total += consumed;
+ if (loaded == 0)
goto next_buffer;
- }
+ return 1;
err:
assert(ret < 0);
btr_remove_node(&fn->btrn);
return ret;
}
-/**
- * The init function of the aacdec filter.
- *
- * \param f Pointer to the filter struct to initialize.
- *
- * \sa filter::init
- */
-void aacdec_filter_init(struct filter *f)
-{
- f->open = aacdec_open;
- f->close = aacdec_close;
- f->pre_select = generic_filter_pre_select;
- f->post_select = aacdec_post_select;
- f->execute = aacdec_execute;
-}
+const struct filter lsg_filter_cmd_com_aacdec_user_data = {
+ .open = aacdec_open,
+ .close = aacdec_close,
+ .pre_select = generic_filter_pre_select,
+ .post_select = aacdec_post_select,
+ .execute = aacdec_execute
+};
* \param addr The address to add.
* \param netmask The netmask to use for this entry.
*/
-static void acl_add_entry(struct list_head *acl, char *addr, int netmask)
+void acl_add_entry(struct list_head *acl, char *addr, int netmask)
{
struct access_info *ai = para_malloc(sizeof(struct access_info));
return ret;
}
-/**
- * Initialize an access control list.
- *
- * \param acl The list to initialize.
- * \param acl_info An array of strings of the form ip/netmask.
- * \param num The number of strings in \a acl_info.
- */
-void acl_init(struct list_head *acl, char * const *acl_info, int num)
-{
- char addr[16];
- int mask, i;
-
- INIT_LIST_HEAD(acl);
- for (i = 0; i < num; i++)
- if (parse_cidr(acl_info[i], addr, sizeof(addr), &mask) == NULL)
- PARA_CRIT_LOG("ACL syntax error: %s, ignoring\n",
- acl_info[i]);
- else
- acl_add_entry(acl, addr, mask);
-}
-
/**
* Check whether the peer name of a given fd is allowed by an acl.
*
/** \file acl.h Exported functions of acl.c. */
-void acl_init(struct list_head *acl, char * const *acl_info, int num);
+void acl_add_entry(struct list_head *acl, char *addr, int netmask);
char *acl_get_contents(struct list_head *acl);
int acl_check_access(int fd, struct list_head *acl, int default_deny);
void acl_allow(char *addr, int mask, struct list_head *acl, int default_deny);
/** \file afh.c Paraslash's standalone audio format handler tool. */
#include <regex.h>
+#include <lopsub.h>
+#include "afh.lsg.h"
#include "para.h"
#include "string.h"
-#include "afh.cmdline.h"
#include "fd.h"
#include "afh.h"
#include "error.h"
#include "version.h"
-#include "ggo.h"
/** Array of error strings. */
DEFINE_PARA_ERRLIST;
-static struct afh_args_info conf;
+struct lls_parse_result *lpr;
+
+#define CMD_PTR (lls_cmd(0, afh_suite))
+#define OPT_RESULT(_name) (lls_opt_result(LSG_AFH_PARA_AFH_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
static int loglevel;
INIT_STDERR_LOGGING(loglevel)
struct taginfo *tags = &afhi->tags;
bool modified = false;
char *tmp_name;
+ const char *arg;
int output_fd = -1, ret;
struct stat sb;
- if (tag_needs_update(conf.year_given, tags->year, conf.year_arg)) {
+ arg = OPT_STRING_VAL(YEAR);
+ if (tag_needs_update(OPT_GIVEN(YEAR), tags->year, arg)) {
free(tags->year);
- tags->year = para_strdup(conf.year_arg);
+ tags->year = para_strdup(arg);
modified = true;
}
- if (tag_needs_update(conf.title_given, tags->title, conf.title_arg)) {
+ arg = OPT_STRING_VAL(TITLE);
+ if (tag_needs_update(OPT_GIVEN(TITLE), tags->title, arg)) {
free(tags->title);
- tags->title = para_strdup(conf.title_arg);
+ tags->title = para_strdup(arg);
modified = true;
}
- if (tag_needs_update(conf.artist_given, tags->artist,
- conf.artist_arg)) {
+ arg = OPT_STRING_VAL(ARTIST);
+ if (tag_needs_update(OPT_GIVEN(ARTIST), tags->artist, arg)) {
free(tags->artist);
- tags->artist = para_strdup(conf.artist_arg);
+ tags->artist = para_strdup(arg);
modified = true;
}
- if (tag_needs_update(conf.album_given, tags->album, conf.album_arg)) {
+ arg = OPT_STRING_VAL(ALBUM);
+ if (tag_needs_update(OPT_GIVEN(ALBUM), tags->album, arg)) {
free(tags->album);
- tags->album = para_strdup(conf.album_arg);
+ tags->album = para_strdup(arg);
modified = true;
}
- if (tag_needs_update(conf.comment_given, tags->comment,
- conf.comment_arg)) {
+ arg = OPT_STRING_VAL(COMMENT);
+ if (tag_needs_update(OPT_GIVEN(COMMENT), tags->comment, arg)) {
free(tags->comment);
- tags->comment = para_strdup(conf.comment_arg);
+ tags->comment = para_strdup(arg);
modified = true;
}
if (!modified) {
tmp_name);
if (ret < 0)
goto out;
- if (conf.backup_given) {
+ if (OPT_GIVEN(BACKUP)) {
char *backup_name = make_message("%s~", name);
ret = xrename(name, backup_name);
free(backup_name);
free(msg);
}
-static void print_chunk_table(struct afh_info *afhi)
+static void print_chunk_table(struct afh_info *afhi, int audio_format_id,
+ const void *map, size_t mapsize)
{
- int i;
+ int i, ret;
+ void *ctx = NULL;
- if (conf.parser_friendly_given) {
- printf("chunk_table: ");
- for (i = 0; i <= afhi->chunks_total; i++)
- printf("%u ", afhi->chunk_table[i]);
- printf("\n");
- return;
- }
- for (i = 1; i <= afhi->chunks_total; i++) {
+ for (i = 0; i < afhi->chunks_total; i++) {
struct timeval tv;
long unsigned from, to;
- tv_scale(i - 1, &afhi->chunk_tv, &tv);
- from = tv2ms(&tv);
+ const char *buf;
+ size_t len;
tv_scale(i, &afhi->chunk_tv, &tv);
+ from = tv2ms(&tv);
+ tv_scale(i + 1, &afhi->chunk_tv, &tv);
to = tv2ms(&tv);
- printf("%d [%lu.%03lu - %lu.%03lu] %u - %u (%u)\n", i - 1,
- from / 1000, from % 1000, to / 1000, to % 1000,
- afhi->chunk_table[i - 1], afhi->chunk_table[i],
- afhi->chunk_table[i] - afhi->chunk_table[i - 1]);
+ ret = afh_get_chunk(i, afhi, audio_format_id, map, mapsize,
+ &buf, &len, &ctx);
+ if (ret < 0) {
+ PARA_ERROR_LOG("fatal: chunk %d: %s\n", i,
+ para_strerror(-ret));
+ return;
+ }
+ if (!OPT_GIVEN(PARSER_FRIENDLY))
+ printf("%d [%lu.%03lu - %lu.%03lu] ", i, from / 1000,
+ from % 1000, to / 1000, to % 1000);
+ printf("%td - %td", buf - (const char *)map,
+ buf + len - (const char *)map);
+ if (!OPT_GIVEN(PARSER_FRIENDLY))
+ printf(" (%zu)", len);
+ printf("\n");
}
+ afh_close(ctx, audio_format_id);
}
-__noreturn static void print_help_and_die(void)
+static void handle_help_flags(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;
+ char *help;
- ggo_print_help(&h, flags);
- printf("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
+ if (OPT_GIVEN(DETAILED_HELP))
+ help = lls_long_help(CMD_PTR);
+ else if (OPT_GIVEN(HELP) || lls_num_inputs(lpr) == 0)
+ help = lls_short_help(CMD_PTR);
+ else
+ return;
+ printf("%s", help);
+ free(help);
+ printf("Supported audio formats\n %s\n", AUDIO_FORMAT_HANDLERS);
exit(EXIT_SUCCESS);
}
void *audio_file_data;
size_t audio_file_size;
struct afh_info afhi;
+ char *errctx;
- afh_cmdline_parser(argc, argv, &conf);
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
- version_handle_flag("afh", conf.version_given);
- if (conf.help_given || conf.detailed_help_given || conf.inputs_num == 0)
- print_help_and_die();
+ ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
+ if (ret < 0)
+ goto out;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
+ version_handle_flag("afh", OPT_GIVEN(VERSION));
+ handle_help_flags();
afh_init();
- for (i = 0; i < conf.inputs_num; i++) {
+ for (i = 0; i < lls_num_inputs(lpr); i++) {
int ret2;
- ret = mmap_full_file(conf.inputs[i], O_RDONLY, &audio_file_data,
+ const char *path = lls_input(i, lpr);
+ ret = mmap_full_file(path, O_RDONLY, &audio_file_data,
&audio_file_size, &fd);
if (ret < 0) {
- PARA_ERROR_LOG("failed to mmap \"%s\"\n", conf.inputs[i]);
+ PARA_ERROR_LOG("failed to mmap \"%s\"\n", path);
goto out;
}
- ret = compute_afhi(conf.inputs[i], audio_file_data, audio_file_size,
+ ret = compute_afhi(path, audio_file_data, audio_file_size,
fd, &afhi);
if (ret >= 0) {
audio_format_num = ret;
- if (conf.modify_given) {
- ret = rewrite_tags(conf.inputs[i], fd, audio_file_data,
+ if (OPT_GIVEN(MODIFY)) {
+ ret = rewrite_tags(path, fd, audio_file_data,
audio_file_size, audio_format_num, &afhi);
} else {
- printf("File %d: %s\n", i + 1, conf.inputs[i]);
+ printf("File %d: %s\n", i + 1, path);
print_info(audio_format_num, &afhi);
- if (conf.chunk_table_given)
- print_chunk_table(&afhi);
- printf("\n");
+ if (OPT_GIVEN(CHUNK_TABLE))
+ print_chunk_table(&afhi, audio_format_num,
+ audio_file_data, audio_file_size);
}
clear_afhi(&afhi);
}
break;
}
out:
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
if (ret < 0)
- PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
}
* the current audio file.
*/
uint32_t *chunk_table;
+ /** Size of the largest chunk, introduced in v0.6.0. */
+ uint32_t max_chunk_size;
/** Period of time between sending data chunks. */
struct timeval chunk_tv;
/**
int fd;
/** Vss needs this for streaming. */
struct afh_info afhi;
- /** Size of the largest chunk. */
+ /**
+ * Size of the largest chunk. Superseded by afhi->max_chunk_size. May
+ * be removed after v0.6.1.
+ */
uint32_t max_chunk_size;
/** Needed to get the audio file header. */
uint8_t audio_format_id;
struct afh_info *afhi);
/** Optional, used for header-rewriting. See \ref afh_get_header(). */
void (*get_header)(void *map, size_t mapsize, char **buf, size_t *len);
+ /**
+ * An audio format handler may signify support for dynamic chunks by
+ * defining ->get_chunk below. In this case the vss calls ->open() at
+ * BOS, ->get_chunk() for each chunk while streaming, and ->close() at
+ * EOS. The chunk table is not accessed at all.
+ *
+ * The function may return its (opaque) context through the last
+ * argument. The returned pointer is passed to subsequent calls to
+ * ->get_chunk() and ->close().
+ */
+ int (*open)(const void *map, size_t mapsize, void **afh_context);
+ /**
+ * Return a reference to one chunk. The returned pointer points to a
+ * portion of the memory mapped audio file. The caller must not call
+ * free() on it.
+ */
+ int (*get_chunk)(long unsigned chunk_num, void *afh_context,
+ const char **buf, size_t *len);
+ /** Deallocate the resources occupied by ->open(). */
+ void (*close)(void *afh_context);
/**
* Write audio file with altered tags, optional.
*
int compute_afhi(const char *path, char *data, size_t size,
int fd, struct afh_info *afhi);
const char *audio_format_name(int);
-void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
- void *map, const char **buf, size_t *len);
+__must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
+ uint8_t audio_format_id, const void *map, size_t mapsize,
+ const char **buf, size_t *len, void **afh_context);
+void afh_close(void *afh_context, uint8_t audio_format_id);
int32_t afh_get_start_chunk(int32_t approx_chunk_num,
- const struct afh_info *afhi);
+ const struct afh_info *afhi, uint8_t audio_format_id);
void afh_get_header(struct afh_info *afhi, uint8_t audio_format_id,
void *map, size_t mapsize, char **buf, size_t *len);
void afh_free_header(char *header_buf, uint8_t audio_format_id);
unsigned afh_get_afhi_txt(int audio_format_num, struct afh_info *afhi, char **result);
int afh_rewrite_tags(int audio_format_id, void *map, size_t mapsize,
struct taginfo *tags, int output_fd, const char *filename);
+void set_max_chunk_size(struct afh_info *afhi);
+bool afh_supports_dynamic_chunks(int audio_format_id);
},
{
.name = "aac",
-#if defined(HAVE_MP4V2)
+#if defined(HAVE_FAAD)
.init = aac_afh_init,
#endif
},
if (afl[format].init)
return format;
}
-
}
/** Iterate over each supported audio format. */
}
}
+/**
+ * Tell whether an audio format handler provides chunk tables.
+ *
+ * Each audio format handler either provides a chunk table or supports dynamic
+ * chunks.
+ *
+ * \param audio_format_id Offset in the afl array.
+ *
+ * \return True if dynamic chunks are supported, false if the audio format
+ * handler provides chunk tables.
+ */
+bool afh_supports_dynamic_chunks(int audio_format_id)
+{
+ return afl[audio_format_id].get_chunk;
+}
+
/**
* Guess the audio format judging from filename.
*
/**
* Get one chunk of audio data.
*
+ * This implicitly calls the ->open method of the audio format handler at the
+ * first call.
+ *
* \param chunk_num The number of the chunk to get.
* \param afhi Describes the audio file.
+ * \param audio_format_id Determines the afh.
* \param map The memory mapped audio file.
+ * \param mapsize Passed to the afh's ->open() method.
* \param buf Result pointer.
* \param len The length of the chunk in bytes.
+ * \param afh_context Value/result, determines whether ->open() is called.
*
* Upon return, \a buf will point so memory inside \a map. The returned buffer
* must therefore not be freed by the caller.
+ *
+ * \return Standard.
+ */
+__must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
+ uint8_t audio_format_id, const void *map, size_t mapsize,
+ const char **buf, size_t *len, void **afh_context)
+{
+ struct audio_format_handler *afh = afl + audio_format_id;
+
+ if (afh_supports_dynamic_chunks(audio_format_id)) {
+ int ret;
+
+ if (!*afh_context) {
+ ret = afh->open(map, mapsize, afh_context);
+ if (ret < 0)
+ return ret;
+ }
+ ret = afl[audio_format_id].get_chunk(chunk_num, *afh_context,
+ buf, len);
+ if (ret < 0) {
+ afh->close(*afh_context);
+ *afh_context = NULL;
+ }
+ return ret;
+ } else {
+ size_t pos = afhi->chunk_table[chunk_num];
+ *buf = map + pos;
+ *len = get_chunk_len(chunk_num, afhi);
+ return 0;
+ }
+}
+
+/**
+ * Deallocate resources allocated due to dynamic chunk handling.
+ *
+ * This function should be called if afh_get_chunk() was called at least once.
+ * It is OK to call it even for audio formats which do not support dynamic
+ * chunks, in which case the function does nothing.
+ *
+ * \param afh_context As returned from the ->open method of the afh.
+ * \param audio_format_id Determines the afh.
*/
-void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
- void *map, const char **buf, size_t *len)
+void afh_close(void *afh_context, uint8_t audio_format_id)
{
- size_t pos = afhi->chunk_table[chunk_num];
- *buf = map + pos;
- *len = get_chunk_len(chunk_num, afhi);
+ struct audio_format_handler *afh = afl + audio_format_id;
+
+ if (!afh_supports_dynamic_chunks(audio_format_id))
+ return;
+ if (!afh->close)
+ return;
+ if (!afh_context)
+ return;
+ afh->close(afh_context);
}
/**
*
* \param approx_chunk_num Upper bound for the chunk number to return.
* \param afhi Needed for the chunk table.
+ * \param audio_format_id Determines the afh.
*
- * \return The first non-empty chunk <= \a approx_chunk_num.
+ * \return For audio format handlers which support dynamic chunks, the function
+ * returns the given chunk number. Otherwise it returns the first non-empty
+ * chunk <= \a approx_chunk_num.
*
* \sa \ref afh_get_chunk().
*/
int32_t afh_get_start_chunk(int32_t approx_chunk_num,
- const struct afh_info *afhi)
+ const struct afh_info *afhi, uint8_t audio_format_id)
{
int32_t k;
+ if (afh_supports_dynamic_chunks(audio_format_id))
+ return approx_chunk_num;
+
for (k = PARA_MAX(0, approx_chunk_num); k >= 0; k--)
if (get_chunk_len(k, afhi) > 0)
return k;
"%s: %" PRIu32 "\n" /* seconds total */
"%s: %lu: %lu\n" /* chunk time */
"%s: %" PRIu32 "\n" /* num chunks */
+ "%s: %" PRIu32 "\n" /* max chunk size */
"%s: %s\n" /* techinfo */
"%s: %s\n" /* artist */
"%s: %s\n" /* title */
status_item_list[SI_CHUNK_TIME], (long unsigned)afhi->chunk_tv.tv_sec,
(long unsigned)afhi->chunk_tv.tv_usec,
status_item_list[SI_NUM_CHUNKS], afhi->chunks_total,
+ status_item_list[SI_MAX_CHUNK_SIZE], afhi->max_chunk_size,
status_item_list[SI_TECHINFO], afhi->techinfo? afhi->techinfo : "",
status_item_list[SI_ARTIST], afhi->tags.artist? afhi->tags.artist : "",
status_item_list[SI_TITLE], afhi->tags.title? afhi->tags.title : "",
);
}
+/**
+ * Determine the maximal chunk size by investigating the chunk table.
+ *
+ * \param afhi Value/result.
+ *
+ * This function iterates over the chunk table and sets ->max_chunk_size
+ * accordingly. The function exists only for backward compatibility since as of
+ * version 0.6.0, para_server stores the maximal chunk size in its database.
+ * This function is only called if the database value is zero, indicating that
+ * the file was added by an older server version.
+ */
+void set_max_chunk_size(struct afh_info *afhi)
+{
+ uint32_t n, max = 0, old = 0;
+
+ for (n = 0; n <= afhi->chunks_total; n++) {
+ uint32_t val = afhi->chunk_table[n];
+ /*
+ * If the first chunk is the header, do not consider it for the
+ * calculation of the largest chunk size.
+ */
+ if (n == 0 || (n == 1 && afhi->header_len > 0)) {
+ old = val;
+ continue;
+ }
+ max = PARA_MAX(max, val - old);
+ old = val;
+ }
+ afhi->max_chunk_size = max;
+}
+
/**
* Create a copy of the given file with altered meta tags.
*
#include <regex.h>
#include <sys/types.h>
+#include <lopsub.h>
+#include "recv_cmd.lsg.h"
#include "para.h"
#include "error.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "recv.h"
-#include "afh_recv.cmdline.h"
#include "string.h"
#include "fd.h"
#include "afh.h"
long unsigned last_chunk;
struct timeval stream_start;
uint32_t current_chunk;
+ void *afh_context;
};
static int afh_execute(struct btr_node *btrn, const char *cmd, char **result)
return ret;
if (x >= pard->afhi.chunks_total)
return -ERRNO_TO_PARA_ERROR(EINVAL);
- pard->first_chunk = afh_get_start_chunk(x, &pard->afhi);
+ pard->first_chunk = afh_get_start_chunk(x, &pard->afhi,
+ pard->audio_format_num);
pard->current_chunk = pard->first_chunk;
return 1;
}
return -E_BTR_NAVAIL;
}
-static void *afh_recv_parse_config(int argc, char **argv)
-{
- struct afh_recv_args_info *tmp = para_calloc(sizeof(*tmp));
-
- afh_recv_cmdline_parser(argc, argv, tmp);
- return tmp;
-}
-
-static void afh_recv_free_config(void *conf)
-{
- if (!conf)
- return;
- afh_recv_cmdline_parser_free(conf);
- free(conf);
-}
-
static int afh_recv_open(struct receiver_node *rn)
{
- struct afh_recv_args_info *conf = rn->conf;
+ struct lls_parse_result *lpr = rn->lpr;
struct private_afh_recv_data *pard;
struct afh_info *afhi;
- char *filename = conf->filename_arg;
-
+ const char *fn = RECV_CMD_OPT_STRING_VAL(AFH, FILENAME, lpr);
+ int32_t bc = RECV_CMD_OPT_INT32_VAL(AFH, BEGIN_CHUNK, lpr);
+ const struct lls_opt_result *r_e = RECV_CMD_OPT_RESULT(AFH, END_CHUNK, lpr);
int ret;
- if (!filename || *filename == '\0')
+ if (!fn || *fn == '\0')
return -E_AFH_RECV_BAD_FILENAME;
rn->private_data = pard = para_calloc(sizeof(*pard));
afhi = &pard->afhi;
- ret = mmap_full_file(filename, O_RDONLY, &pard->map,
+ ret = mmap_full_file(fn, O_RDONLY, &pard->map,
&pard->map_size, &pard->fd);
if (ret < 0)
goto out;
- ret = compute_afhi(filename, pard->map, pard->map_size,
+ ret = compute_afhi(fn, pard->map, pard->map_size,
pard->fd, afhi);
if (ret < 0)
goto out_unmap;
ret = -ERRNO_TO_PARA_ERROR(EINVAL);
if (afhi->chunks_total == 0)
goto out_clear_afhi;
- if (PARA_ABS(conf->begin_chunk_arg) >= afhi->chunks_total)
+ if (PARA_ABS(bc) >= afhi->chunks_total)
goto out_clear_afhi;
- if (conf->begin_chunk_arg >= 0)
- pard->first_chunk = afh_get_start_chunk(
- conf->begin_chunk_arg, &pard->afhi);
+ if (bc >= 0)
+ pard->first_chunk = afh_get_start_chunk(bc, &pard->afhi,
+ pard->audio_format_num);
else
- pard->first_chunk = afh_get_start_chunk(
- afhi->chunks_total + conf->begin_chunk_arg,
- &pard->afhi);
- if (conf->end_chunk_given) {
+ pard->first_chunk = afh_get_start_chunk(afhi->chunks_total + bc,
+ &pard->afhi, pard->audio_format_num);
+ if (lls_opt_given(r_e)) {
+ int32_t ec = lls_int32_val(0, r_e);
ret = -ERRNO_TO_PARA_ERROR(EINVAL);
- if (PARA_ABS(conf->end_chunk_arg) > afhi->chunks_total)
+ if (PARA_ABS(ec) > afhi->chunks_total)
goto out_clear_afhi;
- if (conf->end_chunk_arg >= 0)
- pard->last_chunk = conf->end_chunk_arg;
+ if (ec >= 0)
+ pard->last_chunk = ec;
else
- pard->last_chunk = afhi->chunks_total + conf->end_chunk_arg;
+ pard->last_chunk = afhi->chunks_total + ec;
} else
pard->last_chunk = afhi->chunks_total - 1;
ret = -ERRNO_TO_PARA_ERROR(EINVAL);
clear_afhi(&pard->afhi);
para_munmap(pard->map, pard->map_size);
close(pard->fd);
+ afh_close(pard->afh_context, pard->audio_format_num);
freep(&rn->private_data);
}
struct receiver_node *rn = context;
struct private_afh_recv_data *pard = rn->private_data;
struct afh_info *afhi = &pard->afhi;
- struct afh_recv_args_info *conf = rn->conf;
+ struct lls_parse_result *lpr = rn->lpr;
struct timeval chunk_time;
int state = generic_recv_pre_select(s, rn);
+ unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
if (state <= 0)
return;
- if (!conf->just_in_time_given) {
+ if (!j_given) {
sched_min_delay(s);
return;
}
static int afh_recv_post_select(__a_unused struct sched *s, void *context)
{
struct receiver_node *rn = context;
- struct afh_recv_args_info *conf = rn->conf;
+ struct lls_parse_result *lpr = rn->lpr;
struct private_afh_recv_data *pard = rn->private_data;
struct btr_node *btrn = rn->btrn;
struct afh_info *afhi = &pard->afhi;
int ret;
char *buf;
- const char *start, *end;
+ const char *start;
size_t size;
struct timeval chunk_time;
+ unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
+ unsigned H_given = RECV_CMD_OPT_GIVEN(AFH, NO_HEADER, lpr);
ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
if (ret <= 0)
goto out;
- if (pard->first_chunk > 0 && !conf->no_header_given) {
+ if (pard->first_chunk > 0 && !H_given) {
char *header;
afh_get_header(afhi, pard->audio_format_num, pard->map,
pard->map_size, &header, &size);
afh_free_header(header, pard->audio_format_num);
}
}
- if (!conf->just_in_time_given) {
- afh_get_chunk(pard->first_chunk, afhi, pard->map, &start, &size);
- afh_get_chunk(pard->last_chunk, afhi, pard->map, &end, &size);
- end += size;
- PARA_INFO_LOG("adding %td bytes\n", end - start);
- btr_add_output_dont_free(start, end - start, btrn);
+ if (!j_given) {
+ long unsigned n;
+ for (n = pard->first_chunk; n < pard->last_chunk; n++) {
+ ret = afh_get_chunk(n, afhi, pard->audio_format_num,
+ pard->map, pard->map_size, &start, &size,
+ &pard->afh_context);
+ if (ret < 0)
+ goto out;
+ PARA_INFO_LOG("adding %zu bytes\n", size);
+ btr_add_output_dont_free(start, size, btrn);
+ }
ret = -E_RECV_EOF;
goto out;
}
if (ret > 0)
goto out;
}
- afh_get_chunk(pard->current_chunk, afhi, pard->map, &start, &size);
+ ret = afh_get_chunk(pard->current_chunk, afhi,
+ pard->audio_format_num, pard->map,
+ pard->map_size, &start, &size,
+ &pard->afh_context);
+ if (ret < 0)
+ goto out;
PARA_DEBUG_LOG("adding chunk %u\n", pard->current_chunk);
btr_add_output_dont_free(start, size, btrn);
if (pard->current_chunk >= pard->last_chunk) {
return ret;
}
-/**
- * The init function of the afh receiver.
- *
- * \param r Pointer to the receiver struct to initialize.
- *
- * This initializes all function pointers of \a r.
- */
-void afh_recv_init(struct receiver *r)
-{
- struct afh_recv_args_info dummy;
-
- afh_init();
- afh_recv_cmdline_parser_init(&dummy);
- r->open = afh_recv_open;
- r->close = afh_recv_close;
- r->pre_select = afh_recv_pre_select;
- r->post_select = afh_recv_post_select;
- r->parse_config = afh_recv_parse_config;
- r->free_config = afh_recv_free_config;
- r->execute = afh_execute;
- r->help = (struct ggo_help)DEFINE_GGO_HELP(afh_recv);
- afh_recv_cmdline_parser_free(&dummy);
-}
+const struct receiver lsg_recv_cmd_com_afh_user_data = {
+ .init = afh_init,
+ .open = afh_recv_open,
+ .close = afh_recv_close,
+ .pre_select = afh_recv_pre_select,
+ .post_select = afh_recv_post_select,
+ .execute = afh_execute,
+};
#include <signal.h>
#include <fnmatch.h>
#include <osl.h>
+#include <lopsub.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
-#include "server.cmdline.h"
+#include "server.lsg.h"
+#include "server_cmd.lsg.h"
#include "para.h"
#include "error.h"
#include "crypt.h"
*(uint32_t *)buf = afs_socket_cookie;
*(int *)(buf + sizeof(afs_socket_cookie)) = query_shmid;
- ret = connect_local_socket(conf.afs_socket_arg);
+ ret = connect_local_socket(OPT_STRING_VAL(AFS_SOCKET));
if (ret < 0)
goto out;
fd = ret;
}
/**
- * Send a callback request passing an options structure and an argument vector.
+ * Wrapper for send_callback_request() which passes a lopsub parse result.
*
- * \param options pointer to an arbitrary data structure.
- * \param argc Argument count.
- * \param argv Standard argument vector.
* \param f The callback function.
- * \param result_handler See \ref send_callback_request.
- * \param private_result_data See \ref send_callback_request.
+ * \param cmd Needed for (de-)serialization.
+ * \param lpr Must match cmd.
+ * \param private_result_data Passed to send_callback_request().
*
- * Some command handlers pass command-specific options to a callback, together
- * with a list of further arguments (often a list of audio files). This
- * function allows to pass an arbitrary structure (given as an osl object) and
- * a usual argument vector to the specified callback.
+ * This function serializes the parse result given by the lpr pointer into a
+ * buffer. The buffer is sent as the query to the afs process with the callback
+ * mechanism.
*
- * \return The return value of the underlying call to \ref
- * send_callback_request().
- *
- * \sa send_standard_callback_request(), send_callback_request().
+ * \return The return value of the underlying call to send_callback_request().
*/
-int send_option_arg_callback_request(struct osl_object *options,
- int argc, char * const * const argv, afs_callback *f,
- callback_result_handler *result_handler,
- void *private_result_data)
+int send_lls_callback_request(afs_callback *f,
+ const struct lls_command * const cmd,
+ struct lls_parse_result *lpr, void *private_result_data)
{
- char *p;
- int i, ret;
- struct osl_object query = {.size = options? options->size : 0};
-
- for (i = 0; i < argc; i++)
- query.size += strlen(argv[i]) + 1;
- query.data = para_malloc(query.size);
- p = query.data;
- if (options) {
- memcpy(query.data, options->data, options->size);
- p += options->size;
- }
- for (i = 0; i < argc; i++) {
- strcpy(p, argv[i]); /* OK */
- p += strlen(argv[i]) + 1;
- }
- ret = send_callback_request(f, &query, result_handler,
- private_result_data);
- free(query.data);
- return ret;
-}
+ struct osl_object query;
+ char *buf = NULL;
+ int ret = lls_serialize_parse_result(lpr, cmd, &buf, &query.size);
-/**
- * Send a callback request with an argument vector only.
- *
- * \param argc The same meaning as in send_option_arg_callback_request().
- * \param argv The same meaning as in send_option_arg_callback_request().
- * \param f The same meaning as in send_option_arg_callback_request().
- * \param result_handler See \ref send_callback_request.
- * \param private_result_data See \ref send_callback_request.
- *
- * This is similar to send_option_arg_callback_request(), but no options buffer
- * is passed to the parent process.
- *
- * \return The return value of the underlying call to
- * send_option_arg_callback_request().
- */
-int send_standard_callback_request(int argc, char * const * const argv,
- afs_callback *f, callback_result_handler *result_handler,
- void *private_result_data)
-{
- return send_option_arg_callback_request(NULL, argc, argv, f, result_handler,
+ assert(ret >= 0);
+ query.data = buf;
+ ret = send_callback_request(f, &query, afs_cb_result_handler,
private_result_data);
+ free(buf);
+ return ret;
}
static int action_if_pattern_matches(struct osl_row *row, void *data)
struct pattern_match_data *pmd = data;
struct osl_object name_obj;
const char *p, *name;
- int ret = osl(osl_get_object(pmd->table, row, pmd->match_col_num, &name_obj));
- const char *pattern_txt = (const char *)pmd->patterns.data;
+ int i, ret;
+ ret = osl(osl_get_object(pmd->table, row, pmd->match_col_num,
+ &name_obj));
if (ret < 0)
return ret;
name = (char *)name_obj.data;
if ((!name || !*name) && (pmd->pm_flags & PM_SKIP_EMPTY_NAME))
return 1;
- if (pmd->patterns.size == 0 &&
- (pmd->pm_flags & PM_NO_PATTERN_MATCHES_EVERYTHING)) {
- pmd->num_matches++;
- return pmd->action(pmd->table, row, name, pmd->data);
+ if (lls_num_inputs(pmd->lpr) == 0) {
+ if (pmd->pm_flags & PM_NO_PATTERN_MATCHES_EVERYTHING) {
+ pmd->num_matches++;
+ return pmd->action(pmd->table, row, name, pmd->data);
+ }
}
- for (p = pattern_txt; p < pattern_txt + pmd->patterns.size;
- p += strlen(p) + 1) {
+ i = pmd->input_skip;
+ for (;;) {
+ if (i >= lls_num_inputs(pmd->lpr))
+ break;
+ p = lls_input(i, pmd->lpr);
ret = fnmatch(p, name, pmd->fnmatch_flags);
- if (ret == FNM_NOMATCH)
- continue;
- if (ret)
- return -E_FNMATCH;
- ret = pmd->action(pmd->table, row, name, pmd->data);
- if (ret >= 0)
- pmd->num_matches++;
- return ret;
+ if (ret != FNM_NOMATCH) {
+ if (ret != 0)
+ return -E_FNMATCH;
+ ret = pmd->action(pmd->table, row, name, pmd->data);
+ if (ret >= 0)
+ pmd->num_matches++;
+ return ret;
+
+ }
+ i++;
}
return 1;
}
static int com_select_callback(struct afs_callback_arg *aca)
{
- const char *arg = aca->query.data;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
+ const char *arg;
int num_admissible, ret;
+ ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+ assert(ret >= 0);
+ arg = lls_input(0, aca->lpr);
ret = clear_score_table();
if (ret < 0) {
- para_printf(&aca->pbout, "could not clear score table: %s\n",
- para_strerror(-ret));
- return ret;
+ para_printf(&aca->pbout, "could not clear score table\n");
+ goto free_lpr;
}
if (current_play_mode == PLAY_MODE_MOOD)
close_current_mood();
out:
para_printf(&aca->pbout, "activated %s (%d admissible files)\n",
current_mop? current_mop : "dummy mood", num_admissible);
+free_lpr:
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-int com_select(struct command_context *cc)
+static int com_select(struct command_context *cc, struct lls_parse_result *lpr)
{
- struct osl_object query;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
+ char *errctx;
+ int ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
- if (cc->argc != 2)
- return -E_AFS_SYNTAX;
- query.data = cc->argv[1];
- query.size = strlen(cc->argv[1]) + 1;
- return send_callback_request(com_select_callback, &query,
- &afs_cb_result_handler, cc);
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ return send_lls_callback_request(com_select_callback, cmd, lpr, cc);
}
+EXPORT_SERVER_CMD_HANDLER(select);
-static void init_admissible_files(char *arg)
+static void init_admissible_files(const char *arg)
{
if (activate_mood_or_playlist(arg, NULL) < 0)
activate_mood_or_playlist(NULL, NULL); /* always successful */
static int setup_command_socket_or_die(void)
{
int ret, socket_fd;
- char *socket_name = conf.afs_socket_arg;
+ const char *socket_name = OPT_STRING_VAL(AFS_SOCKET);
unlink(socket_name);
- ret = create_local_socket(socket_name, 0);
+ ret = create_local_socket(socket_name);
if (ret < 0) {
- ret = create_local_socket(socket_name,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IROTH);
- if (ret < 0) {
- PARA_EMERG_LOG("%s: %s\n", para_strerror(-ret),
- socket_name);
- exit(EXIT_FAILURE);
- }
+ PARA_EMERG_LOG("%s: %s\n", para_strerror(-ret), socket_name);
+ exit(EXIT_FAILURE);
}
socket_fd = ret;
PARA_INFO_LOG("listening on socket %s (fd %d)\n", socket_name,
static void get_database_dir(void)
{
if (!database_dir) {
- if (conf.afs_database_dir_given)
- database_dir = para_strdup(conf.afs_database_dir_arg);
+ if (OPT_GIVEN(AFS_DATABASE_DIR))
+ database_dir = para_strdup(OPT_STRING_VAL(AFS_DATABASE_DIR));
else {
char *home = para_homedir();
database_dir = make_message(
return ret;
buf[n] = '\0';
if (strcmp(buf, "new"))
- return -E_BAD_CMD;
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
return open_next_audio_file();
}
goto out_close;
PARA_INFO_LOG("server_socket: %d, afs_socket_cookie: %u\n",
server_socket, (unsigned) cookie);
- init_admissible_files(conf.afs_initial_mode_arg);
+ init_admissible_files(OPT_STRING_VAL(AFS_INITIAL_MODE));
register_command_task(cookie, &s);
s.default_timeout.tv_sec = 0;
s.default_timeout.tv_usec = 999 * 1000;
return ret;
}
-int com_init(struct command_context *cc)
+static int com_init(struct command_context *cc, struct lls_parse_result *lpr)
{
int i, j, ret;
uint32_t table_mask = (1 << (NUM_AFS_TABLES + 1)) - 1;
struct osl_object query = {.data = &table_mask,
.size = sizeof(table_mask)};
+ unsigned num_inputs = lls_num_inputs(lpr);
ret = make_database_dir();
if (ret < 0)
return ret;
- if (cc->argc != 1) {
+ if (num_inputs > 0) {
table_mask = 0;
- for (i = 1; i < cc->argc; i++) {
+ for (i = 0; i < num_inputs; i++) {
for (j = 0; j < NUM_AFS_TABLES; j++) {
struct afs_table *t = &afs_tables[j];
- if (strcmp(cc->argv[i], t->name))
+ if (strcmp(lls_input(i, lpr), t->name))
continue;
table_mask |= (1 << j);
break;
return send_callback_request(com_init_callback, &query,
afs_cb_result_handler, cc);
}
+EXPORT_SERVER_CMD_HANDLER(init);
-/**
- * Flags for the check command.
- *
- * \sa com_check().
- */
-enum com_check_flags {
- /** Check the audio file table. */
- CHECK_AFT = 1,
- /** Check the mood table. */
- CHECK_MOODS = 2,
- /** Check the playlist table. */
- CHECK_PLAYLISTS = 4,
- /** Check the attribute table against the audio file table. */
- CHECK_ATTS = 8
-};
-
-int com_check(struct command_context *cc)
+static int com_check(struct command_context *cc, struct lls_parse_result *lpr)
{
- unsigned flags = 0;
- int i, ret;
+ const struct lls_opt_result *r_a = SERVER_CMD_OPT_RESULT(CHECK, AFT, lpr);
+ const struct lls_opt_result *r_A = SERVER_CMD_OPT_RESULT(CHECK, ATTRIBUTE, lpr);
+ const struct lls_opt_result *r_m = SERVER_CMD_OPT_RESULT(CHECK, MOOD, lpr);
+ const struct lls_opt_result *r_p = SERVER_CMD_OPT_RESULT(CHECK, PLAYLIST, lpr);
+ bool noopt = !lls_opt_given(r_a) && !lls_opt_given(r_A)
+ && !lls_opt_given(r_m) && !lls_opt_given(r_p);
+ int ret;
- for (i = 1; i < cc->argc; i++) {
- const char *arg = cc->argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strcmp(arg, "-a")) {
- flags |= CHECK_AFT;
- continue;
- }
- if (!strcmp(arg, "-A")) {
- flags |= CHECK_ATTS;
- continue;
- }
- if (!strcmp(arg, "-p")) {
- flags |= CHECK_PLAYLISTS;
- continue;
- }
- if (!strcmp(arg, "-m")) {
- flags |= CHECK_MOODS;
- continue;
- }
- return -E_AFS_SYNTAX;
- }
- if (i < cc->argc)
- return -E_AFS_SYNTAX;
- if (!flags)
- flags = ~0U;
- if (flags & CHECK_AFT) {
+ if (noopt || lls_opt_given(r_a)) {
ret = send_callback_request(aft_check_callback, NULL,
afs_cb_result_handler, cc);
if (ret < 0)
return ret;
}
- if (flags & CHECK_ATTS) {
+ if (noopt || lls_opt_given(r_A)) {
ret = send_callback_request(attribute_check_callback, NULL,
afs_cb_result_handler, cc);
if (ret < 0)
return ret;
}
- if (flags & CHECK_PLAYLISTS) {
+ if (noopt || lls_opt_given(r_p)) {
ret = send_callback_request(playlist_check_callback,
NULL, afs_cb_result_handler, cc);
if (ret < 0)
return ret;
}
- if (flags & CHECK_MOODS) {
+ if (noopt || lls_opt_given(r_m)) {
ret = send_callback_request(mood_check_callback, NULL,
afs_cb_result_handler, cc);
if (ret < 0)
}
return 1;
}
+EXPORT_SERVER_CMD_HANDLER(check);
/**
* The afs event dispatcher.
+++ /dev/null
-BN: afs
-SF: afs.c aft.c attribute.c
-SN: list of afs commands
-TM: mood lyr img pl
----
-N: add
-P: AFS_READ | AFS_WRITE
-D: Add or update audio files.
-U: add [-a] [-l] [-f] [-v] path...
-H: Each path must be absolute and refer to either an audio file, or a
-H: directory. In case of a directory, all audio files in that directory
-H: are added recursively. Only absolute paths are accepted.
-H:
-H: Options:
-H:
-H: -a Add all files. The default is to add only files ending in a
-H: known suffix for a supported audio format.
-H:
-H: -l Add files lazily. If the path already exists in the
-H: database, skip this file. This operation is really cheap. Useful
-H: to update large directories after some files have been added or
-H: deleted.
-H:
-H: -f Force adding/updating. Recompute the audio format handler data
-H: even if a file with the same path and the same hash value exists.
-H:
-H: -v Verbose mode. Print what is being done.
----
-N: init
-P: AFS_READ | AFS_WRITE
-D: Initialize the osl tables for the audio file selector.
-U: init [table_name ...]
-H: When invoked without arguments, this command creates all tables. Otherwise
-H: only the tables given by table_name... are created.
----
-N: ls
-P: AFS_READ
-D: List audio files.
-U: ls [-l=mode] [-p] [-a] [-r] [-d] [-s=order] [pattern...]
-H: Print a list of all audio files matching pattern.
-H:
-H: Options:
-H:
-H: -l=mode Change listing mode. Defaults to short listing if not given.
-H:
-H: Available modes:
-H: s: short listing mode
-H: l: long listing mode (equivalent to -l)
-H: v: verbose listing mode
-H: p: parser-friendly mode
-H: m: mbox listing mode
-H: c: chunk-table listing mode
-H:
-H: -F List full paths. If this option is not specified, only the basename
-H: of each file is printed.
-H: -p Synonym for -F. Deprecated.
-H:
-H: -b Print only the basename of each matching file. This is the default, so
-H: the option is currently a no-op. It is recommended to specify this option,
-H: though, as the default might change in a future release.
-H:
-H: -a List only files that are admissible with respect to the current mood or
-H: playlist.
-H:
-H: -r Reverse sort order.
-H:
-H: -d Print dates as seconds after the epoch.
-H:
-H: -s=order
-H: Change sort order. Defaults to alphabetical path sort if not given.
-H:
-H: Possible values for order:
-H: p: by path
-H: l: by last played time
-H: s: by score (implies -a)
-H: n: by num played count
-H: f: by frequency
-H: c: by number of channels
-H: i: by image id
-H: y: by lyrics id
-H: b: by bit rate
-H: d: by duration
-H: a: by audio format
----
-N: lsatt
-P: AFS_READ
-D: List attributes.
-U: lsatt [-i] [-l] [-r] [pattern]
-H: Print the list of all defined attributes which match the given
-H: pattern. If no pattern is given, the full list is printed.
-H:
-H: Options:
-H:
-H: -i Sort attributes by id. The default is to sort alphabetically by name.
-H:
-H: -l Print a long listing containing both identifier and attribute name. The
-H: default is to print only the name.
-H:
-H: -r Reverse sort order.
----
-N: setatt
-P: AFS_READ | AFS_WRITE
-D: Set attribute(s) for all files matching a pattern.
-U: setatt attribute{+|-}... pattern
-H: Set ('+') or unset ('-') the given attributes for all audio files matching
-H: pattern. Example:
-H:
-H: setatt rock+ punk+ pop- '*foo.mp3'
-H:
-H: sets the 'rock' and the 'punk' attribute and unsets the 'pop'
-H: attribute of all files ending with 'foo.mp3'.
----
-N: addatt
-P: AFS_READ | AFS_WRITE
-D: Add new attribute(s).
-U: addatt attribute1...
-H: This adds new attributes to the attribute table. At most 64
-H: attributes may be defined.
----
-N: mvatt
-P: AFS_READ | AFS_WRITE
-D: Rename an attribute.
-U: mvatt old new
-H: Rename attribute old to new.
----
-N: check
-P: AFS_READ
-D: Run integrity checks against osl tables.
-U: check [-a] [-A] [-m] [-p]
-H: Check the audio file table, the attribute table, the mood definitions
-H: and all defined playlists. Report any inconsistencies.
-H:
-H: Options:
-H:
-H: -a Run audio file table checks. Checks for entries in the audio file
-H: table which are not present in the file system. Moreover, it checks
-H: whether the lyrics id and all entries in the audio file table are
-H: valid.
-H:
-H: -A Check the attribute table against the afs attribute bitmask of
-H: each audio file in the audio file table. Reports audio files
-H: whose attribute bitmask is invalid, i.e., has a bit set which
-H: does not correspond to any attribute of the attribute table.
-H:
-H: -m Run syntax checks on all defined moods in the mood table.
-H:
-H: -p Check all playlists for lines that correspond to files not contained
-H: in the audio file table.
-H:
-H: If called without arguments, all checks are run.
----
-N: rmatt
-P: AFS_READ | AFS_WRITE
-D: Remove attribute(s).
-U: rmatt pattern...
-H: Remove all attributes matching any given pattern. All information
-H: about this attribute in the audio file table is lost.
----
-N: rm
-P: AFS_READ | AFS_WRITE
-D: Remove entries from the audio file table.
-U: rm [-v] [-f] [-p] pattern...
-H: Delete all entries in the audio file table that match any given pattern. Note
-H: that this affects the table entries only; the command won't touch your audio
-H: files on disk.
-H:
-H: Options:
-H:
-H: -v Verbose mode. Explain what is being done.
-H:
-H: -f Force mode. Ignore nonexistent files. Don't complain if nothing
-H: was removed.
-H:
-H: -p Pathname match. Match a slash in the path only with a slash
-H: in pattern and not by an asterisk (*) or a question mark
-H: (?) metacharacter, nor by a bracket expression ([]) containing
-H: a slash (see fnmatch(3)).
----
-N: touch
-P: AFS_READ | AFS_WRITE
-D: Manipulate the afs entry of audio files.
-U: touch [-n=numplayed] [-l=lastplayed] [-y=lyrics_id] [-i=image_id] [-a=amp] [-v] [-p] pattern
-H: If no option is given, the lastplayed field is set to the current time
-H: and the value of the numplayed field is increased by one. Otherwise,
-H: only the given options are taken into account.
-H:
-H: Options:
-H:
-H: -n Set the numplayed count, i.e. the number of times this audio
-H: file was selected for streaming so far.
-H:
-H: -l Set the lastplayed time, i.e. the last time this audio file was
-H: selected for streaming. The argument must be a number of seconds
-H: since the epoch. Example:
-H:
-H: touch -l=$(date +%s) file
-H:
-H: sets the lastplayed time of 'file' to the current time.
-H:
-H: -y Set the lyrics ID which specifies the lyrics data file associated
-H: with the audio file.
-H:
-H: -i Like -y, but sets the image ID.
-H:
-H: -a Set the amplification value (0-255). This determines a scaling
-H: factor by which the amplitude should be multiplied in order to
-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 amp filter.
-H:
-H: -v Verbose mode. Explain what is being done.
-H:
-H: -p Pathname match. Match a slash in the path only with a slash
-H: in pattern and not by an asterisk (*) or a question mark
-H: (?) metacharacter, nor by a bracket expression ([]) containing
-H: a slash (see fnmatch(3)).
----
-N: cpsi
-P: AFS_READ | AFS_WRITE
-D: Copy audio file selector info.
-U: cpsi [-a] [-y] [-i] [-l] [-n] [-v] source pattern...
-H: If no option, or only the -v option is given, all fields of the
-H: audio file selector info are copied to all files matching pattern.
-H: Otherwise, only the given options are taken into account.
-H:
-H: Options:
-H:
-H: -a Copy attributes.
-H:
-H: -y Copy the lyrics id.
-H:
-H: -i Copy the image id.
-H:
-H: -l Copy the lastplayed time.
-H:
-H: -n Copy the numplayed count.
-H:
-H: -v Verbose mode.
----
-N: select
-P: AFS_READ | AFS_WRITE
-D: Activate a mood or a playlist.
-U: select specifier/name
-H: The specifier is either 'm' or 'p' to indicate whether a playlist or
-H: a mood should be activated. Example:
-H:
-H: select m/foo
-H:
-H: loads the mood named 'foo'.
----
-T: add
-N: add@member@
-O: int com_add@member@(struct command_context *cc);
-P: AFS_READ | AFS_WRITE
-D: Add stdin as a blob to the @member@ table.
-U: add@member@ @member@_name
-H: Read from stdin and ask the audio file selector to create a blob in the
-H: corresponding osl table. If the named blob already exists, it gets replaced
-H: with the new data.
----
-T: cat
-N: cat@member@
-O: int com_cat@member@(struct command_context *cc);
-P: AFS_READ
-D: Dump the contents of a blob of type @member@ to stdout.
-U: cat@member@ @member@_name
-H: Retrieve the named blob and write it to stdout.
----
-T: ls
-N: ls@member@
-O: int com_ls@member@(struct command_context *cc);
-P: AFS_READ
-D: List blobs of type @member@ which match a pattern.
-U: ls@member@ [-i] [-l] [-r] [pattern]
-H: Print the list of all blobs which match the given pattern. If no
-H: pattern is given, the full list is printed.
-H:
-H: Options:
-H:
-H: -i Sort by identifier. The default is to sort alphabetically by name.
-H:
-H: -l Print identifier and name. The default is to print only the name.
-H:
-H: -r Reverse sort order.
----
-T: rm
-N: rm@member@
-O: int com_rm@member@(struct command_context *cc);
-P: AFS_READ | AFS_WRITE
-D: Remove blob(s) of type @member@ from the @member@ table.
-U: rm@member@ pattern...
-H: Remove all blobs whose name matches any of the given patterns.
----
-T: mv
-N: mv@member@
-O: int com_mv@member@(struct command_context *cc);
-P: AFS_READ | AFS_WRITE
-D: Rename a blob of type @member@.
-U: mv@member@ source_@member@_name dest_@member@_name
-H: Rename the blob identified by the source blob name to the destination blob
-H: name. The command fails if the source does not exist, or if the destination
-H: already exists.
unsigned pm_flags;
/** This value is passed verbatim to fnmatch(). */
int fnmatch_flags;
- /** Null-terminated array of patterns. */
- struct osl_object patterns;
+ /** Obtained by deserializing the query buffer in the callback. */
+ struct lls_parse_result *lpr;
+ /** Do not try to match the first inputs of lpr */
+ unsigned input_skip;
/** Data pointer passed to the action function. */
void *data;
/** Gets increased by one for each match. */
struct osl_object query;
/** Will be written on band SBD_OUTPUT, fully buffered. */
struct para_buffer pbout;
+ struct lls_parse_result *lpr;
};
/**
int send_callback_request(afs_callback *f, struct osl_object *query,
callback_result_handler *result_handler,
void *private_result_data);
-int send_option_arg_callback_request(struct osl_object *options,
- int argc, char * const * const argv, afs_callback *f,
- callback_result_handler *result_handler,
- void *private_result_data);
-int send_standard_callback_request(int argc, char * const * const argv,
- afs_callback *f, callback_result_handler *result_handler,
- void *private_result_data);
+int send_lls_callback_request(afs_callback *f,
+ const struct lls_command * const cmd,
+ struct lls_parse_result *lpr, void *private_result_data);
int string_compare(const struct osl_object *obj1, const struct osl_object *obj2);
int for_each_matching_row(struct pattern_match_data *pmd);
#include <fnmatch.h>
#include <sys/shm.h>
#include <osl.h>
+#include <lopsub.h>
+#include "server_cmd.lsg.h"
#include "para.h"
#include "error.h"
#include "crypt.h"
unsigned char *hash;
};
-/** The flags accepted by the ls command. */
-enum ls_flags {
- /** -p */
- LS_FLAG_FULL_PATH = 1,
- /** -a */
- LS_FLAG_ADMISSIBLE_ONLY = 2,
- /** -r */
- LS_FLAG_REVERSE = 4,
- /** -d */
- LS_FLAG_UNIXDATE = 8,
-};
-
/**
* The size of the individual output fields of the ls command.
*
/** Data passed from the ls command handler to its callback function. */
struct ls_options {
- /** The given command line flags. */
- unsigned flags;
- /** The sorting method given at the command line. */
+ struct lls_parse_result *lpr;
+ /* Derived from lpr */
enum ls_sorting_method sorting;
- /** The given listing mode (short, long, verbose, mbox). */
+ /* Derived from lpr */
enum ls_listing_mode mode;
- /** The arguments passed to the ls command. */
- char **patterns;
- /** Number of non-option arguments. */
- int num_patterns;
/** Used for long listing mode to align the output fields. */
struct ls_widths widths;
/** Size of the \a data array. */
CHUNKS_TOTAL_OFFSET = 20,
/** The length of the audio file header (4 bytes). */
HEADER_LEN_OFFSET = 24,
- /** Was: The start of the audio file header (4 bytes). */
- AFHI_UNUSED2_OFFSET = 28,
+ /** Size of the largest chunk in bytes. (4 bytes). */
+ AFHI_MAX_CHUNK_SIZE_OFFSET = 28,
/** The seconds part of the chunk time (4 bytes). */
CHUNK_TV_TV_SEC_OFFSET = 32,
/** The microseconds part of the chunk time (4 bytes). */
write_u8(buf + AFHI_CHANNELS_OFFSET, afhi->channels);
write_u32(buf + CHUNKS_TOTAL_OFFSET, afhi->chunks_total);
write_u32(buf + HEADER_LEN_OFFSET, afhi->header_len);
- write_u32(buf + AFHI_UNUSED2_OFFSET, 0);
+ write_u32(buf + AFHI_MAX_CHUNK_SIZE_OFFSET, afhi->max_chunk_size);
write_u32(buf + CHUNK_TV_TV_SEC_OFFSET, afhi->chunk_tv.tv_sec);
write_u32(buf + CHUNK_TV_TV_USEC_OFFSET, afhi->chunk_tv.tv_usec);
p = buf + AFHI_INFO_STRING_OFFSET;
afhi->channels = read_u8(buf + AFHI_CHANNELS_OFFSET);
afhi->chunks_total = read_u32(buf + CHUNKS_TOTAL_OFFSET);
afhi->header_len = read_u32(buf + HEADER_LEN_OFFSET);
+ afhi->max_chunk_size = read_u32(buf + AFHI_MAX_CHUNK_SIZE_OFFSET);
afhi->chunk_tv.tv_sec = read_u32(buf + CHUNK_TV_TV_SEC_OFFSET);
afhi->chunk_tv.tv_usec = read_u32(buf + CHUNK_TV_TV_USEC_OFFSET);
afhi->techinfo = (char *)buf + AFHI_INFO_STRING_OFFSET;
afhi->tags.comment = afhi->tags.album + strlen(afhi->tags.album) + 1;
}
+/* Only used for saving the chunk table, but not for loading. */
static unsigned sizeof_chunk_table(struct afh_info *afhi)
{
- if (!afhi)
+ if (!afhi || !afhi->chunk_table)
return 0;
return 4 * (afhi->chunks_total + 1);
}
-static uint32_t save_chunk_table(struct afh_info *afhi, char *buf)
+static void save_chunk_table(struct afh_info *afhi, char *buf)
{
- int i;
- uint32_t max = 0, old = 0;
-
- for (i = 0; i <= afhi->chunks_total; i++) {
- uint32_t val = afhi->chunk_table[i];
- write_u32(buf + 4 * i, val);
- /*
- * If the first chunk is the header, do not consider it for the
- * calculation of the largest chunk size.
- */
- if (i == 0 || (i == 1 && afhi->header_len > 0)) {
- old = val;
- continue;
- }
- max = PARA_MAX(max, val - old);
- old = val;
- }
- return max;
+ uint32_t n;
+
+ if (!afhi->chunk_table)
+ return;
+ for (n = 0; n <= afhi->chunks_total; n++)
+ write_u32(buf + 4 * n, afhi->chunk_table[n]);
}
-static void load_chunk_table(struct afh_info *afhi, char *buf)
+static void load_chunk_table(struct afh_info *afhi, const struct osl_object *ct)
{
int i;
+ size_t sz;
- afhi->chunk_table = para_malloc(sizeof_chunk_table(afhi));
- for (i = 0; i <= afhi->chunks_total; i++)
- afhi->chunk_table[i] = read_u32(buf + 4 * i);
+ if (!ct->data || ct->size < 4) {
+ afhi->chunk_table = NULL;
+ return;
+ }
+ sz = PARA_MIN(((size_t)afhi->chunks_total + 1) * 4, ct->size) + 1;
+ afhi->chunk_table = para_malloc(sz);
+ for (i = 0; i <= afhi->chunks_total && i * 4 + 3 < ct->size; i++)
+ afhi->chunk_table[i] = read_u32(ct->data + 4 * i);
}
/**
goto err;
buf = shm_afd;
buf += sizeof(*afd);
- afd->max_chunk_size = save_chunk_table(&afd->afhi, buf);
+ save_chunk_table(&afd->afhi, buf);
+ if (afd->afhi.max_chunk_size == 0) { /* v0.5.x on-disk afhi */
+ set_max_chunk_size(&afd->afhi);
+ PARA_NOTICE_LOG("max chunk size unset, re-add required\n");
+ } else
+ PARA_INFO_LOG("using max chunk size from afhi\n");
+ afd->max_chunk_size = afd->afhi.max_chunk_size;
*(struct audio_file_data *)shm_afd = *afd;
shm_detach(shm_afd);
return shmid;
{
void *shm_afd;
int ret;
+ struct osl_object obj;
ret = shm_attach(shmid, ATTACH_RO, &shm_afd);
if (ret < 0)
return ret;
+ ret = shm_size(shmid, &obj.size);
+ if (ret < 0)
+ goto detach;
*afd = *(struct audio_file_data *)shm_afd;
- load_chunk_table(&afd->afhi, shm_afd + sizeof(*afd));
+ obj.data = shm_afd + sizeof(*afd);
+ obj.size -= sizeof(*afd);
+ load_chunk_table(&afd->afhi, &obj);
+ ret = 1;
+detach:
shm_detach(shm_afd);
- return 1;
+ return ret;
}
static int get_local_time(uint64_t *seconds, char *buf, size_t size,
}
static void write_filename_items(struct para_buffer *b, const char *path,
- unsigned flags)
+ bool basename)
{
char *val;
- if (!(flags & LS_FLAG_FULL_PATH)) {
+ if (basename) {
WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", path);
return;
}
(long unsigned) d->afhi.chunk_tv.tv_usec
);
buf = chunk_table_obj.data;
- for (i = 0; i <= d->afhi.chunks_total; i++)
+ for (
+ i = 0;
+ i <= d->afhi.chunks_total && 4 * i + 3 < chunk_table_obj.size;
+ i++
+ )
para_printf(b, "%u ", (unsigned) read_u32(buf + 4 * i));
para_printf(b, "\n");
ret = 1;
return ret;
}
-static void write_score(struct para_buffer *b, struct ls_data *d,
- struct ls_options *opts)
-{
- if (!(opts->flags & LS_FLAG_ADMISSIBLE_ONLY)) /* no score*/
- return;
- WRITE_STATUS_ITEM(b, SI_SCORE, "%li\n", d->score);
-}
-
static int print_list_item(struct ls_data *d, struct ls_options *opts,
struct para_buffer *b, time_t current_time)
{
+ const struct lls_opt_result *r_a = SERVER_CMD_OPT_RESULT(LS, ADMISSIBLE, opts->lpr);
+ const struct lls_opt_result *r_b = SERVER_CMD_OPT_RESULT(LS, BASENAME, opts->lpr);
+ const struct lls_opt_result *r_d = SERVER_CMD_OPT_RESULT(LS, UNIX_DATE, opts->lpr);
int ret;
char att_buf[65];
char last_played_time[30];
goto out;
}
get_attribute_bitmap(&afsi->attributes, att_buf);
- if (opts->flags & LS_FLAG_UNIXDATE)
+ if (lls_opt_given(r_d))
sprintf(last_played_time, "%llu",
(long long unsigned)afsi->last_played);
else {
get_duration_buf(afhi->seconds_total, duration_buf, opts);
if (opts->mode == LS_MODE_LONG) {
struct ls_widths *w = &opts->widths;
- if (opts->flags & LS_FLAG_ADMISSIBLE_ONLY) {
+ if (lls_opt_given(r_a))
para_printf(b, "%*li ", opts->widths.score_width,
d->score);
- }
para_printf(b,
"%s " /* attributes */
"%*u " /* amp */
last_played_time,
bn? bn : "?");
}
- write_filename_items(b, d->path, opts->flags);
- write_score(b, d, opts);
+ write_filename_items(b, d->path, lls_opt_given(r_b));
+ if (lls_opt_given(r_a))
+ WRITE_STATUS_ITEM(b, SI_SCORE, "%li\n", d->score);
ret = write_attribute_items(b, att_buf, afsi);
if (ret < 0)
goto out;
WRITE_STATUS_ITEM(b, SI_CHUNK_TIME, "%lu\n", tv2ms(&afhi->chunk_tv));
WRITE_STATUS_ITEM(b, SI_NUM_CHUNKS, "%" PRIu32 "\n",
afhi->chunks_total);
+ WRITE_STATUS_ITEM(b, SI_MAX_CHUNK_SIZE, "%" PRIu32 "\n",
+ afhi->max_chunk_size);
WRITE_STATUS_ITEM(b, SI_TECHINFO, "%s\n", afhi->techinfo);
WRITE_STATUS_ITEM(b, SI_ARTIST, "%s\n", afhi->tags.artist);
WRITE_STATUS_ITEM(b, SI_TITLE, "%s\n", afhi->tags.title);
static int make_status_items(void)
{
- struct ls_options opts = {
- .flags = LS_FLAG_FULL_PATH | LS_FLAG_ADMISSIBLE_ONLY,
- .mode = LS_MODE_VERBOSE,
- };
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
+ char *argv[] = {"ls", "--admissible", "--listing-mode=verbose"};
+ struct ls_options opts = {.mode = LS_MODE_VERBOSE};
struct para_buffer pb = {.max_size = shm_get_shmmax() - 1};
time_t current_time;
int ret;
+ ret = lls_parse(ARRAY_SIZE(argv), argv, cmd, &opts.lpr, NULL);
+ assert(ret >= 0);
time(¤t_time);
ret = print_list_item(&status_item_ls_data, &opts, &pb, current_time);
if (ret < 0)
- return ret;
+ goto out;
make_inode_status_items(&pb);
free(status_items);
status_items = pb.buf;
+
memset(&pb, 0, sizeof(pb));
pb.max_size = shm_get_shmmax() - 1;
pb.flags = PBF_SIZE_PREFIX;
make_inode_status_items(&pb);
free(parser_friendly_status_items);
parser_friendly_status_items = pb.buf;
- return 1;
+ ret = 1;
+out:
+ lls_free_parse_result(opts.lpr, cmd);
+ return ret;
}
/**
d->afhi.chunk_table = afd->afhi.chunk_table = NULL;
ret = osl(osl_open_disk_object(audio_file_table, current_aft_row,
AFTCOL_CHUNKS, &chunk_table_obj));
- if (ret < 0)
- return ret;
+ if (ret < 0) {
+ if (!afh_supports_dynamic_chunks(d->afsi.audio_format_id))
+ return ret;
+ PARA_INFO_LOG("no chunk table for %s\n", d->path);
+ chunk_table_obj.data = NULL;
+ chunk_table_obj.size = 0;
+ } else {
+ PARA_INFO_LOG("chunk table: %zu bytes\n", chunk_table_obj.size);
+ }
ret = mmap_full_file(d->path, O_RDONLY, &map.data, &map.size, &afd->fd);
if (ret < 0)
goto out;
save_afsi(&new_afsi, &afsi_obj); /* in-place update */
afd->audio_format_id = d->afsi.audio_format_id;
- load_chunk_table(&afd->afhi, chunk_table_obj.data);
+ load_chunk_table(&afd->afhi, &chunk_table_obj);
aced.aft_row = current_aft_row;
aced.old_afsi = &d->afsi;
/*
ret = save_afd(afd);
out:
free(afd->afhi.chunk_table);
- osl_close_disk_object(&chunk_table_obj);
+ if (chunk_table_obj.data)
+ osl_close_disk_object(&chunk_table_obj);
if (ret < 0) {
PARA_ERROR_LOG("%s: %s\n", d->path, para_strerror(-ret));
ret = score_delete(current_aft_row);
return strcmp(d1->path, d2->path);
}
+static inline bool admissible_only(struct ls_options *opts)
+{
+ return SERVER_CMD_OPT_GIVEN(LS, ADMISSIBLE, opts->lpr)
+ || opts->sorting == LS_SORT_BY_SCORE;
+}
+
static int sort_matching_paths(struct ls_options *options)
{
+ const struct lls_opt_result *r_b = SERVER_CMD_OPT_RESULT(LS, BASENAME,
+ options->lpr);
size_t nmemb = options->num_matching_paths;
size_t size = sizeof(*options->data_ptr);
int (*compar)(const void *, const void *);
options->data_ptr[i] = options->data + i;
/* In these cases the array is already sorted */
- if (options->sorting == LS_SORT_BY_PATH
- && !(options->flags & LS_FLAG_ADMISSIBLE_ONLY)
- && (options->flags & LS_FLAG_FULL_PATH))
- return 1;
- if (options->sorting == LS_SORT_BY_SCORE &&
- options->flags & LS_FLAG_ADMISSIBLE_ONLY)
- return 1;
+ if (admissible_only(options)) {
+ if (options->sorting == LS_SORT_BY_SCORE)
+ return 1;
+ } else {
+ if (options->sorting == LS_SORT_BY_PATH && !lls_opt_given(r_b))
+ return 1;
+ }
switch (options->sorting) {
case LS_SORT_BY_PATH:
{
int ret, i;
struct ls_options *options = ls_opts;
+ bool basename_given = SERVER_CMD_OPT_GIVEN(LS, BASENAME, options->lpr);
struct ls_data *d;
struct ls_widths *w;
unsigned short num_digits;
- unsigned tmp;
+ unsigned tmp, num_inputs;
struct osl_row *aft_row;
long score;
char *path;
- if (options->flags & LS_FLAG_ADMISSIBLE_ONLY) {
+ if (admissible_only(options)) {
ret = get_score_and_aft_row(row, &score, &aft_row);
if (ret < 0)
return ret;
ret = get_audio_file_path_of_row(aft_row, &path);
if (ret < 0)
return ret;
- if (!(options->flags & LS_FLAG_FULL_PATH)) {
+ if (basename_given) {
char *p = strrchr(path, '/');
if (p)
path = p + 1;
}
- if (options->num_patterns) {
- for (i = 0; i < options->num_patterns; i++) {
- ret = fnmatch(options->patterns[i], path, 0);
+ num_inputs = lls_num_inputs(options->lpr);
+ if (num_inputs > 0) {
+ for (i = 0; i < num_inputs; i++) {
+ ret = fnmatch(lls_input(i, options->lpr), path, 0);
if (!ret)
break;
if (ret == FNM_NOMATCH)
continue;
return -E_FNMATCH;
}
- if (i >= options->num_patterns) /* no match */
+ if (i >= num_inputs) /* no match */
return 1;
}
tmp = options->num_matching_paths++;
w->amp_width = PARA_MAX(w->amp_width, num_digits);
num_digits = strlen(audio_format_name(d->afsi.audio_format_id));
w->audio_format_width = PARA_MAX(w->audio_format_width, num_digits);
- if (options->flags & LS_FLAG_ADMISSIBLE_ONLY) {
+ if (admissible_only(options)) {
GET_NUM_DIGITS(score, &num_digits);
num_digits++; /* add one for the sign (space or "-") */
w->score_width = PARA_MAX(w->score_width, num_digits);
static int com_ls_callback(struct afs_callback_arg *aca)
{
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
struct ls_options *opts = aca->query.data;
- char *p, *pattern_start = (char *)aca->query.data + sizeof(*opts);
int i = 0, ret;
time_t current_time;
+ const struct lls_opt_result *r_r;
+
+ ret = lls_deserialize_parse_result(
+ (char *)aca->query.data + sizeof(*opts), cmd, &opts->lpr);
+ assert(ret >= 0);
+ r_r = SERVER_CMD_OPT_RESULT(LS, REVERSE, opts->lpr);
aca->pbout.flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0;
- if (opts->num_patterns) {
- opts->patterns = para_malloc(opts->num_patterns * sizeof(char *));
- for (i = 0, p = pattern_start; i < opts->num_patterns; i++) {
- opts->patterns[i] = p;
- p += strlen(p) + 1;
- }
- } else
- opts->patterns = NULL;
- if (opts->flags & LS_FLAG_ADMISSIBLE_ONLY)
+ if (admissible_only(opts))
ret = admissible_file_loop(opts, prepare_ls_row);
else
ret = osl(osl_rbtree_loop(audio_file_table, AFTCOL_PATH, opts,
if (ret < 0)
goto out;
if (opts->num_matching_paths == 0) {
- ret = opts->num_patterns > 0? -E_NO_MATCH : 0;
+ ret = lls_num_inputs(opts->lpr) > 0? -E_NO_MATCH : 0;
goto out;
}
ret = sort_matching_paths(opts);
if (ret < 0)
goto out;
time(¤t_time);
- if (opts->flags & LS_FLAG_REVERSE)
+ if (lls_opt_given(r_r))
for (i = opts->num_matching_paths - 1; i >= 0; i--) {
ret = print_list_item(opts->data_ptr[i], opts,
&aca->pbout, current_time);
goto out;
}
out:
+ lls_free_parse_result(opts->lpr, cmd);
free(opts->data);
free(opts->data_ptr);
- free(opts->patterns);
return ret;
}
-/*
- * TODO: flags -h (sort by hash)
- */
-int com_ls(struct command_context *cc)
+/* TODO: flags -h (sort by hash) */
+static int com_ls(struct command_context *cc, struct lls_parse_result *lpr)
{
- int i;
- unsigned flags = 0;
- enum ls_sorting_method sort = LS_SORT_BY_PATH;
- enum ls_listing_mode mode = LS_MODE_SHORT;
- struct ls_options opts = {.patterns = NULL};
- struct osl_object query = {.data = &opts, .size = sizeof(opts)};
-
- for (i = 1; i < cc->argc; i++) {
- const char *arg = cc->argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- /*
- * Compatibility: Prior to 0.5.5 it was necessary to specify
- * the listing mode without the '=' character as in -lv, for
- * example. Now the variant with '=' is preferred and
- * documented but we still accept the old way to specify the
- * listing mode.
- *
- * Support for the legacy syntax can be dropped at 0.6.0
- * or later.
- */
- if (!strncmp(arg, "-l", 2)) {
- arg += 2;
- if (*arg == '=')
- arg++;
- switch (*arg) {
- case 's':
- mode = LS_MODE_SHORT;
- continue;
- case 'l':
- case '\0':
- mode = LS_MODE_LONG;
- continue;
- case 'v':
- mode = LS_MODE_VERBOSE;
- continue;
- case 'm':
- mode = LS_MODE_MBOX;
- continue;
- case 'c':
- mode = LS_MODE_CHUNKS;
- continue;
- case 'p':
- mode = LS_MODE_PARSER;
- continue;
- default:
- return -E_AFT_SYNTAX;
- }
- }
- if (!strcmp(arg, "-p") || !strcmp(arg, "-F")) {
- flags |= LS_FLAG_FULL_PATH;
- continue;
- }
- if (!strcmp(arg, "-b")) {
- flags &= ~LS_FLAG_FULL_PATH;
- continue;
- }
- if (!strcmp(arg, "-a")) {
- flags |= LS_FLAG_ADMISSIBLE_ONLY;
- continue;
- }
- if (!strcmp(arg, "-r")) {
- flags |= LS_FLAG_REVERSE;
- continue;
- }
- if (!strcmp(arg, "-d")) {
- flags |= LS_FLAG_UNIXDATE;
- continue;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
+ struct ls_options *opts;
+ struct osl_object query;
+ const struct lls_opt_result *r_l = SERVER_CMD_OPT_RESULT(LS, LISTING_MODE,
+ lpr);
+ const struct lls_opt_result *r_s = SERVER_CMD_OPT_RESULT(LS, SORT, lpr);
+ int ret;
+ char *slpr;
+
+ ret = lls_serialize_parse_result(lpr, cmd, NULL, &query.size);
+ assert(ret >= 0);
+ query.size += sizeof(*opts);
+ query.data = para_malloc(query.size);
+ opts = query.data;
+ memset(opts, 0, sizeof(*opts));
+ slpr = query.data + sizeof(*opts);
+ ret = lls_serialize_parse_result(lpr, cmd, &slpr, NULL);
+ assert(ret >= 0);
+ opts->mode = LS_MODE_SHORT;
+ opts->sorting = LS_SORT_BY_PATH;
+ if (lls_opt_given(r_l)) {
+ const char *val = lls_string_val(0, r_l);
+ if (!strcmp(val, "l") || !strcmp(val, "long"))
+ opts->mode = LS_MODE_LONG;
+ else if (!strcmp(val, "s") || !strcmp(val, "short"))
+ opts->mode = LS_MODE_SHORT;
+ else if (!strcmp(val, "v") || !strcmp(val, "verbose"))
+ opts->mode = LS_MODE_VERBOSE;
+ else if (!strcmp(val, "m") || !strcmp(val, "mbox"))
+ opts->mode = LS_MODE_MBOX;
+ else if (!strcmp(val, "c") || !strcmp(val, "chunk-table"))
+ opts->mode = LS_MODE_MBOX;
+ else if (!strcmp(val, "p") || !strcmp(val, "parser-friendly"))
+ opts->mode = LS_MODE_PARSER;
+ else {
+ ret = -E_AFT_SYNTAX;
+ goto out;
}
- /* The compatibility remark above applies also to -s. */
- if (!strncmp(arg, "-s", 2)) {
- arg += 2;
- if (*arg == '=')
- arg++;
- switch (*arg) {
- case 'p':
- sort = LS_SORT_BY_PATH;
- continue;
- case 's': /* -ss implies -a */
- sort = LS_SORT_BY_SCORE;
- flags |= LS_FLAG_ADMISSIBLE_ONLY;
- continue;
- case 'l':
- sort = LS_SORT_BY_LAST_PLAYED;
- continue;
- case 'n':
- sort = LS_SORT_BY_NUM_PLAYED;
- continue;
- case 'f':
- sort = LS_SORT_BY_FREQUENCY;
- continue;
- case 'c':
- sort = LS_SORT_BY_CHANNELS;
- continue;
- case 'i':
- sort = LS_SORT_BY_IMAGE_ID;
- continue;
- case 'y':
- sort = LS_SORT_BY_LYRICS_ID;
- continue;
- case 'b':
- sort = LS_SORT_BY_BITRATE;
- continue;
- case 'd':
- sort = LS_SORT_BY_DURATION;
- continue;
- case 'a':
- sort = LS_SORT_BY_AUDIO_FORMAT;
- continue;
- default:
- return -E_AFT_SYNTAX;
- }
+ }
+ if (lls_opt_given(r_s)) {
+ const char *val = lls_string_val(0, r_s);
+ if (!strcmp(val, "p") || !strcmp(val, "path"))
+ opts->sorting = LS_SORT_BY_PATH;
+ else if (!strcmp(val, "s") || !strcmp(val, "score"))
+ opts->sorting = LS_SORT_BY_SCORE;
+ else if (!strcmp(val, "l") || !strcmp(val, "lastplayed"))
+ opts->sorting = LS_SORT_BY_LAST_PLAYED;
+ else if (!strcmp(val, "n") || !strcmp(val, "numplayed"))
+ opts->sorting = LS_SORT_BY_NUM_PLAYED;
+ else if (!strcmp(val, "f") || !strcmp(val, "frquency"))
+ opts->sorting = LS_SORT_BY_FREQUENCY;
+ else if (!strcmp(val, "c") || !strcmp(val, "channels"))
+ opts->sorting = LS_SORT_BY_CHANNELS;
+ else if (!strcmp(val, "i") || !strcmp(val, "image-id"))
+ opts->sorting = LS_SORT_BY_IMAGE_ID;
+ else if (!strcmp(val, "y") || !strcmp(val, "lyrics-id"))
+ opts->sorting = LS_SORT_BY_LYRICS_ID;
+ else if (!strcmp(val, "b") || !strcmp(val, "bitrate"))
+ opts->sorting = LS_SORT_BY_BITRATE;
+ else if (!strcmp(val, "d") || !strcmp(val, "duration"))
+ opts->sorting = LS_SORT_BY_DURATION;
+ else if (!strcmp(val, "a") || !strcmp(val, "audio-format"))
+ opts->sorting = LS_SORT_BY_AUDIO_FORMAT;
+ else {
+ ret = -E_AFT_SYNTAX;
+ goto out;
}
- return -E_AFT_SYNTAX;
}
- opts.flags = flags;
- opts.sorting = sort;
- opts.mode = mode;
- opts.num_patterns = cc->argc - i;
- return send_option_arg_callback_request(&query, opts.num_patterns,
- cc->argv + i, com_ls_callback, afs_cb_result_handler, cc);
+ ret = send_callback_request(com_ls_callback, &query,
+ afs_cb_result_handler, cc);
+out:
+ free(query.data);
+ return ret;
}
+EXPORT_SERVER_CMD_HANDLER(ls);
/**
* Call the given function for each file in the audio file table.
CAB_AFHI_OFFSET_POS = 0,
/** Start of the chunk table (if present). */
CAB_CHUNKS_OFFSET_POS = 4,
- /** Flags given to the add command. */
- CAB_FLAGS_OFFSET = 8,
+ /** Start of the (serialized) lopsub parse result. */
+ CAB_LPR_OFFSET = 8,
/** Audio format id. */
CAB_AUDIO_FORMAT_ID_OFFSET = 12,
/** The hash of the audio file being added. */
* handler info won't be stored in the buffer.
*/
static void save_add_callback_buffer(unsigned char *hash, const char *path,
- struct afh_info *afhi, uint32_t flags,
+ struct afh_info *afhi, const char *slpr, size_t slpr_size,
uint8_t audio_format_num, struct osl_object *obj)
{
size_t path_len = strlen(path) + 1;
size_t afhi_size = sizeof_afhi_buf(afhi);
size_t size = CAB_PATH_OFFSET + path_len + afhi_size
- + sizeof_chunk_table(afhi);
+ + sizeof_chunk_table(afhi) + slpr_size;
char *buf = para_malloc(size);
uint32_t pos;
+ assert(size <= ~(uint32_t)0);
+ write_u8(buf + CAB_AUDIO_FORMAT_ID_OFFSET, audio_format_num);
+ memcpy(buf + CAB_HASH_OFFSET, hash, HASH_SIZE);
+ strcpy(buf + CAB_PATH_OFFSET, path);
pos = CAB_PATH_OFFSET + path_len;
write_u32(buf + CAB_AFHI_OFFSET_POS, pos);
save_afhi(afhi, buf + pos);
pos += afhi_size;
-
write_u32(buf + CAB_CHUNKS_OFFSET_POS, pos);
- if (afhi)
+ if (afhi) {
save_chunk_table(afhi, buf + pos);
-
- write_u32(buf + CAB_FLAGS_OFFSET, flags);
- write_u8(buf + CAB_AUDIO_FORMAT_ID_OFFSET, audio_format_num);
-
- memcpy(buf + CAB_HASH_OFFSET, hash, HASH_SIZE);
- strcpy(buf + CAB_PATH_OFFSET, path);
-
+ pos += sizeof_chunk_table(afhi);
+ }
+ write_u32(buf + CAB_LPR_OFFSET, pos);
+ memcpy(buf + pos, slpr, slpr_size);
+ assert(pos + slpr_size == size);
obj->data = buf;
obj->size = size;
}
*/
-/** Flags passed to the add command. */
-enum com_add_flags {
- /** Skip paths that exist already. */
- ADD_FLAG_LAZY = 1,
- /** Force adding. */
- ADD_FLAG_FORCE = 2,
- /** Print what is being done. */
- ADD_FLAG_VERBOSE = 4,
- /** Try to add files with unknown suffixes. */
- ADD_FLAG_ALL = 8,
-};
-
static int com_add_callback(struct afs_callback_arg *aca)
{
char *buf = aca->query.data, *path;
char asc[2 * HASH_SIZE + 1];
int ret;
char afsi_buf[AFSI_SIZE];
- uint32_t flags = read_u32(buf + CAB_FLAGS_OFFSET);
+ char *slpr = buf + read_u32(buf + CAB_LPR_OFFSET);
struct afs_info default_afsi = {.last_played = 0};
uint16_t afhi_offset, chunks_offset;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADD);
+ const struct lls_opt_result *r_f, *r_v;
+
+ ret = lls_deserialize_parse_result(slpr, cmd, &aca->lpr);
+ assert(ret >= 0);
+ r_f = SERVER_CMD_OPT_RESULT(ADD, FORCE, aca->lpr);
+ r_v = SERVER_CMD_OPT_RESULT(ADD, VERBOSE, aca->lpr);
hash = (unsigned char *)buf + CAB_HASH_OFFSET;
hash_to_asc(hash, asc);
ret = find_path_brother(path, &pb);
if (ret < 0)
goto out;
- if (hs && pb && hs == pb && !(flags & ADD_FLAG_FORCE)) {
- if (flags & ADD_FLAG_VERBOSE)
+ if (hs && pb && hs == pb && !lls_opt_given(r_f)) {
+ if (lls_opt_given(r_v))
para_printf(&aca->pbout, "ignoring duplicate\n");
ret = 1;
goto out;
if (hs && hs != pb) {
struct osl_object obj;
if (pb) { /* hs trumps pb, remove pb */
- if (flags & ADD_FLAG_VERBOSE)
+ if (lls_opt_given(r_v))
para_printf(&aca->pbout, "removing %s\n", path);
ret = afs_event(AUDIO_FILE_REMOVE, &aca->pbout, pb);
if (ret < 0)
pb = NULL;
}
/* file rename, update hs' path */
- if (flags & ADD_FLAG_VERBOSE) {
+ if (lls_opt_given(r_v)) {
ret = osl(osl_get_object(audio_file_table, hs,
AFTCOL_PATH, &obj));
if (ret < 0)
ret = afs_event(AUDIO_FILE_RENAME, &aca->pbout, hs);
if (ret < 0)
goto out;
- if (!(flags & ADD_FLAG_FORCE))
+ if (!lls_opt_given(r_f))
goto out;
}
/* no hs or force mode, child must have sent afhi */
if (ret < 0)
goto out;
hash_to_asc(old_hash, old_asc);
- if (flags & ADD_FLAG_VERBOSE)
+ if (lls_opt_given(r_v))
para_printf(&aca->pbout, "file change: %s -> %s\n",
old_asc, asc);
- ret = osl_update_object(audio_file_table, pb, AFTCOL_HASH,
- &objs[AFTCOL_HASH]);
+ ret = osl(osl_update_object(audio_file_table, pb, AFTCOL_HASH,
+ &objs[AFTCOL_HASH]));
if (ret < 0)
goto out;
}
if (hs || pb) { /* (hs != NULL and pb != NULL) implies hs == pb */
struct osl_row *row = pb? pb : hs;
/* update afhi and chunk_table */
- if (flags & ADD_FLAG_VERBOSE)
+ if (lls_opt_given(r_v))
para_printf(&aca->pbout,
"updating afhi and chunk table\n");
ret = osl(osl_update_object(audio_file_table, row, AFTCOL_AFHI,
&objs[AFTCOL_AFHI]));
if (ret < 0)
goto out;
+ /* truncate the file to size zero if there is no chunk table */
ret = osl(osl_update_object(audio_file_table, row, AFTCOL_CHUNKS,
&objs[AFTCOL_CHUNKS]));
if (ret < 0)
goto out;
ret = afs_event(AFHI_CHANGE, &aca->pbout, row);
- if (ret < 0)
- goto out;
goto out;
}
/* new entry, use default afsi */
- if (flags & ADD_FLAG_VERBOSE)
+ if (lls_opt_given(r_v))
para_printf(&aca->pbout, "new file\n");
default_afsi.last_played = time(NULL) - 365 * 24 * 60 * 60;
default_afsi.audio_format_id = read_u8(buf + CAB_AUDIO_FORMAT_ID_OFFSET);
out:
if (ret < 0)
para_printf(&aca->pbout, "could not add %s\n", path);
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-/** Used by com_add(). */
+/* Used by com_add(). */
struct private_add_data {
- /** The pointer passed to the original command handler. */
+ /* The pointer passed to the original command handler. */
struct command_context *cc;
- /** The given add flags. */
- uint32_t flags;
+ /* Contains the flags given at the command line. */
+ struct lls_parse_result *lpr;
+ /* Serialized lopsub parse result. */
+ char *slpr;
+ /* Number of bytes. */
+ size_t slpr_size;
};
static int path_brother_callback(struct afs_callback_arg *aca)
struct osl_row *pb = NULL, *hs = NULL; /* path brother/hash sister */
struct osl_object map, obj = {.data = NULL}, query;
unsigned char hash[HASH_SIZE];
+ bool a_given = SERVER_CMD_OPT_GIVEN(ADD, ALL, pad->lpr);
+ bool f_given = SERVER_CMD_OPT_GIVEN(ADD, FORCE, pad->lpr);
+ bool l_given = SERVER_CMD_OPT_GIVEN(ADD, LAZY, pad->lpr);
+ bool v_given = SERVER_CMD_OPT_GIVEN(ADD, VERBOSE, pad->lpr);
ret = guess_audio_format(path);
- if (ret < 0 && !(pad->flags & ADD_FLAG_ALL)) {
+ if (ret < 0 && !a_given) {
ret = 0;
goto out_free;
}
if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
goto out_free;
ret = 1;
- if (pb && (pad->flags & ADD_FLAG_LAZY)) { /* lazy is really cheap */
- if (pad->flags & ADD_FLAG_VERBOSE)
+ if (pb && l_given) { /* lazy is really cheap */
+ if (v_given)
send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
"lazy-ignore: %s\n", path);
goto out_free;
goto out_unmap;
/* Return success if we already know this file. */
ret = 1;
- if (pb && hs && hs == pb && !(pad->flags & ADD_FLAG_FORCE)) {
- if (pad->flags & ADD_FLAG_VERBOSE)
+ if (pb && hs && hs == pb && !f_given) {
+ if (v_given)
send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
"%s exists, not forcing update\n", path);
goto out_unmap;
* We won't recalculate the audio format info and the chunk table if
* there is a hash sister and FORCE was not given.
*/
- if (!hs || (pad->flags & ADD_FLAG_FORCE)) {
+ if (!hs || f_given) {
ret = compute_afhi(path, map.data, map.size, fd, &afhi);
if (ret < 0)
goto out_unmap;
}
munmap(map.data, map.size);
close(fd);
- if (pad->flags & ADD_FLAG_VERBOSE) {
+ if (v_given) {
send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
"adding %s\n", path);
if (send_ret < 0)
goto out_free;
}
- save_add_callback_buffer(hash, path, afhi_ptr, pad->flags, format_num, &obj);
+ save_add_callback_buffer(hash, path, afhi_ptr, pad->slpr,
+ pad->slpr_size, format_num, &obj);
/* Ask afs to consider this entry for adding. */
ret = send_callback_request(com_add_callback, &obj,
afs_cb_result_handler, pad->cc);
return send_ret;
}
-int com_add(struct command_context *cc)
+static int com_add(struct command_context *cc, struct lls_parse_result *lpr)
{
int i, ret;
- struct private_add_data pad = {.cc = cc, .flags = 0};
-
- for (i = 1; i < cc->argc; i++) {
- const char *arg = cc->argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strcmp(arg, "-a")) {
- pad.flags |= ADD_FLAG_ALL;
- continue;
- }
- if (!strcmp(arg, "-l")) {
- pad.flags |= ADD_FLAG_LAZY;
- continue;
- }
- if (!strcmp(arg, "-f")) {
- pad.flags |= ADD_FLAG_FORCE;
- continue;
- }
- if (!strcmp(arg, "-v")) {
- pad.flags |= ADD_FLAG_VERBOSE;
- continue;
- }
+ struct private_add_data pad = {.cc = cc, .lpr = lpr};
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADD);
+ unsigned num_inputs;
+ char *errctx;
+
+ ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
}
- if (cc->argc <= i)
- return -E_AFT_SYNTAX;
- for (; i < cc->argc; i++) {
+ ret = lls_serialize_parse_result(lpr, cmd, &pad.slpr, &pad.slpr_size);
+ assert(ret >= 0);
+ num_inputs = lls_num_inputs(lpr);
+ for (i = 0; i < num_inputs; i++) {
char *path;
- ret = verify_path(cc->argv[i], &path);
+ ret = verify_path(lls_input(i, lpr), &path);
if (ret < 0) {
ret = send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s: %s\n",
- cc->argv[i], para_strerror(-ret));
+ lls_input(i, lpr), para_strerror(-ret));
if (ret < 0)
- return ret;
+ goto out;
continue;
}
if (ret == 1) /* directory */
}
free(path);
}
- return 1;
+ ret = 1;
+out:
+ free(pad.slpr);
+ return ret;
}
+EXPORT_SERVER_CMD_HANDLER(add);
/**
* Flags used by the touch command.
TOUCH_FLAG_VERBOSE = 2
};
-/** Options used by com_touch(). */
-struct com_touch_options {
- /** New num_played value. */
- int32_t num_played;
- /** New last played count. */
- int64_t last_played;
- /** New lyrics id. */
- int32_t lyrics_id;
- /** New image id. */
- int32_t image_id;
- /** New amplification value. */
- int32_t amp;
- /** Command line flags (see \ref touch_flags). */
- unsigned flags;
-};
-
static int touch_audio_file(__a_unused struct osl_table *table,
struct osl_row *row, const char *name, void *data)
{
struct afs_callback_arg *aca = data;
- struct com_touch_options *cto = aca->query.data;
+ bool v_given = SERVER_CMD_OPT_GIVEN(TOUCH, VERBOSE, aca->lpr);
+ const struct lls_opt_result *r_n, *r_l, *r_i, *r_y, *r_a;
+ int ret;
struct osl_object obj;
struct afs_info old_afsi, new_afsi;
- int ret, no_options = cto->num_played < 0 && cto->last_played < 0 &&
- cto->lyrics_id < 0 && cto->image_id < 0 && cto->amp < 0;
+ bool no_options;
struct afsi_change_event_data aced;
+ r_n = SERVER_CMD_OPT_RESULT(TOUCH, NUMPLAYED, aca->lpr);
+ r_l = SERVER_CMD_OPT_RESULT(TOUCH, LASTPLAYED, aca->lpr);
+ r_i = SERVER_CMD_OPT_RESULT(TOUCH, IMAGE_ID, aca->lpr);
+ r_y = SERVER_CMD_OPT_RESULT(TOUCH, LYRICS_ID, aca->lpr);
+ r_a = SERVER_CMD_OPT_RESULT(TOUCH, AMP, aca->lpr);
+ no_options = !lls_opt_given(r_n) && !lls_opt_given(r_l) && !lls_opt_given(r_i)
+ && !lls_opt_given(r_y) && !lls_opt_given(r_a);
+
ret = get_afsi_object_of_row(row, &obj);
if (ret < 0) {
para_printf(&aca->pbout, "cannot touch %s\n", name);
if (no_options) {
new_afsi.num_played++;
new_afsi.last_played = time(NULL);
- if (cto->flags & TOUCH_FLAG_VERBOSE)
+ if (v_given)
para_printf(&aca->pbout, "%s: num_played = %u, "
"last_played = now()\n", name,
new_afsi.num_played);
} else {
- if (cto->flags & TOUCH_FLAG_VERBOSE)
+ if (lls_opt_given(r_l))
+ new_afsi.last_played = lls_uint64_val(0, r_l);
+ if (lls_opt_given(r_n))
+ new_afsi.num_played = lls_uint32_val(0, r_n);
+ if (lls_opt_given(r_i))
+ new_afsi.image_id = lls_uint32_val(0, r_i);
+ if (lls_opt_given(r_y))
+ new_afsi.lyrics_id = lls_uint32_val(0, r_y);
+ if (lls_opt_given(r_a))
+ new_afsi.amp = lls_uint32_val(0, r_a);
+ if (v_given)
para_printf(&aca->pbout, "touching %s\n", name);
- if (cto->lyrics_id >= 0)
- new_afsi.lyrics_id = cto->lyrics_id;
- if (cto->image_id >= 0)
- new_afsi.image_id = cto->image_id;
- if (cto->num_played >= 0)
- new_afsi.num_played = cto->num_played;
- if (cto->last_played >= 0)
- new_afsi.last_played = cto->last_played;
- if (cto->amp >= 0)
- new_afsi.amp = cto->amp;
}
save_afsi(&new_afsi, &obj); /* in-place update */
aced.aft_row = row;
static int com_touch_callback(struct afs_callback_arg *aca)
{
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(TOUCH);
+ bool p_given;
+ const struct lls_opt_result *r_i, *r_y;
int ret;
- struct com_touch_options *cto = aca->query.data;
struct pattern_match_data pmd = {
.table = audio_file_table,
.loop_col_num = AFTCOL_HASH,
.match_col_num = AFTCOL_PATH,
- .patterns = {
- .data = (char *)aca->query.data
- + sizeof(struct com_touch_options),
- .size = aca->query.size
- - sizeof(struct com_touch_options)
- },
.data = aca,
.action = touch_audio_file
};
- if (cto->image_id >= 0) {
- ret = img_get_name_by_id(cto->image_id, NULL);
+
+ ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+ assert(ret >= 0);
+ pmd.lpr = aca->lpr;
+
+ r_i = SERVER_CMD_OPT_RESULT(TOUCH, IMAGE_ID, aca->lpr);
+ if (lls_opt_given(r_i)) {
+ uint32_t id = lls_uint32_val(0, r_i);
+ ret = img_get_name_by_id(id, NULL);
if (ret < 0) {
- para_printf(&aca->pbout, "invalid image ID: %d\n",
- cto->image_id);
+ para_printf(&aca->pbout, "invalid image ID: %u\n", id);
return ret;
}
}
- if (cto->lyrics_id >= 0) {
- ret = lyr_get_name_by_id(cto->lyrics_id, NULL);
+ r_y = SERVER_CMD_OPT_RESULT(TOUCH, LYRICS_ID, aca->lpr);
+ if (lls_opt_given(r_y)) {
+ uint32_t id = lls_uint32_val(0, r_y);
+ ret = lyr_get_name_by_id(id, NULL);
if (ret < 0) {
- para_printf(&aca->pbout, "invalid lyrics ID: %d\n",
- cto->lyrics_id);
+ para_printf(&aca->pbout, "invalid lyrics ID: %u\n", id);
return ret;
}
}
- if (cto->flags & TOUCH_FLAG_FNM_PATHNAME)
+ p_given = SERVER_CMD_OPT_GIVEN(TOUCH, PATHNAME_MATCH, aca->lpr);
+ if (p_given)
pmd.fnmatch_flags |= FNM_PATHNAME;
ret = for_each_matching_row(&pmd);
if (ret >= 0 && pmd.num_matches == 0)
ret = -E_NO_MATCH;
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-int com_touch(struct command_context *cc)
+static int com_touch(struct command_context *cc, struct lls_parse_result *lpr)
{
- struct com_touch_options cto = {
- .num_played = -1,
- .last_played = -1,
- .lyrics_id = -1,
- .image_id = -1,
- .amp = -1,
- };
- struct osl_object query = {.data = &cto, .size = sizeof(cto)};
- int i, ret;
-
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(TOUCH);
+ int ret;
+ char *errctx;
- for (i = 1; i < cc->argc; i++) {
- const char *arg = cc->argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strncmp(arg, "-n=", 3)) {
- ret = para_atoi32(arg + 3, &cto.num_played);
- if (ret < 0)
- return ret;
- continue;
- }
- if (!strncmp(arg, "-l=", 3)) {
- ret = para_atoi64(arg + 3, &cto.last_played);
- if (ret < 0)
- return ret;
- continue;
- }
- if (!strncmp(arg, "-y=", 3)) {
- ret = para_atoi32(arg + 3, &cto.lyrics_id);
- if (ret < 0)
- return ret;
- continue;
- }
- if (!strncmp(arg, "-i=", 3)) {
- ret = para_atoi32(arg + 3, &cto.image_id);
- if (ret < 0)
- return ret;
- continue;
- }
- if (!strncmp(arg, "-a=", 3)) {
- int32_t val;
- ret = para_atoi32(arg + 3, &val);
- if (ret < 0)
- return ret;
- if (val < 0 || val > 255)
- return -ERRNO_TO_PARA_ERROR(EINVAL);
- cto.amp = val;
- continue;
- }
- if (!strcmp(arg, "-p")) {
- cto.flags |= TOUCH_FLAG_FNM_PATHNAME;
- continue;
- }
- if (!strcmp(arg, "-v")) {
- cto.flags |= TOUCH_FLAG_VERBOSE;
- continue;
- }
- break; /* non-option starting with dash */
+ ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
}
- if (i >= cc->argc)
- return -E_AFT_SYNTAX;
- return send_option_arg_callback_request(&query, cc->argc - i,
- cc->argv + i, com_touch_callback, afs_cb_result_handler, cc);
-}
-
-/** Flags for com_rm(). */
-enum rm_flags {
- /** -v */
- RM_FLAG_VERBOSE = 1,
- /** -f */
- RM_FLAG_FORCE = 2,
- /** -p */
- RM_FLAG_FNM_PATHNAME = 4
-};
+ return send_lls_callback_request(com_touch_callback, cmd, lpr, cc);
+}
+EXPORT_SERVER_CMD_HANDLER(touch);
static int remove_audio_file(__a_unused struct osl_table *table,
struct osl_row *row, const char *name, void *data)
{
struct afs_callback_arg *aca = data;
- uint32_t flags =*(uint32_t *)aca->query.data;
+ bool v_given = SERVER_CMD_OPT_GIVEN(RM, VERBOSE, aca->lpr);
int ret;
- if (flags & RM_FLAG_VERBOSE)
+ if (v_given)
para_printf(&aca->pbout, "removing %s\n", name);
ret = afs_event(AUDIO_FILE_REMOVE, &aca->pbout, row);
if (ret < 0)
static int com_rm_callback(struct afs_callback_arg *aca)
{
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(RM);
int ret;
- uint32_t flags = *(uint32_t *)aca->query.data;
struct pattern_match_data pmd = {
.table = audio_file_table,
.loop_col_num = AFTCOL_HASH,
.match_col_num = AFTCOL_PATH,
- .patterns = {.data = (char *)aca->query.data + sizeof(uint32_t),
- .size = aca->query.size - sizeof(uint32_t)},
.data = aca,
.action = remove_audio_file
};
- if (flags & RM_FLAG_FNM_PATHNAME)
+ bool v_given, p_given, f_given;
+
+ ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+ assert(ret >= 0);
+ pmd.lpr = aca->lpr;
+ v_given = SERVER_CMD_OPT_GIVEN(RM, VERBOSE, aca->lpr);
+ p_given = SERVER_CMD_OPT_GIVEN(RM, PATHNAME_MATCH, aca->lpr);
+ f_given = SERVER_CMD_OPT_GIVEN(RM, FORCE, aca->lpr);
+
+ if (p_given)
pmd.fnmatch_flags |= FNM_PATHNAME;
ret = for_each_matching_row(&pmd);
if (ret < 0)
goto out;
if (pmd.num_matches == 0) {
- if (!(flags & RM_FLAG_FORCE))
+ if (!f_given)
ret = -E_NO_MATCH;
- } else if (flags & RM_FLAG_VERBOSE)
+ } else if (v_given)
para_printf(&aca->pbout, "removed %u file(s)\n",
pmd.num_matches);
out:
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
/* TODO options: -r (recursive) */
-int com_rm(struct command_context *cc)
+static int com_rm(struct command_context *cc, struct lls_parse_result *lpr)
{
- uint32_t flags = 0;
- struct osl_object query = {.data = &flags, .size = sizeof(flags)};
- int i;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(RM);
+ char *errctx;
+ int ret;
- for (i = 1; i < cc->argc; i++) {
- const char *arg = cc->argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strcmp(arg, "-f")) {
- flags |= RM_FLAG_FORCE;
- continue;
- }
- if (!strcmp(arg, "-p")) {
- flags |= RM_FLAG_FNM_PATHNAME;
- continue;
- }
- if (!strcmp(arg, "-v")) {
- flags |= RM_FLAG_VERBOSE;
- continue;
- }
- break;
+ ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
}
- if (i >= cc->argc)
- return -E_AFT_SYNTAX;
- return send_option_arg_callback_request(&query, cc->argc - i,
- cc->argv + i, com_rm_callback, afs_cb_result_handler, cc);
+ return send_lls_callback_request(com_rm_callback, cmd, lpr, cc);
}
-
-/**
- * Flags used by the cpsi command.
- *
- * \sa com_cpsi().
- */
-enum cpsi_flags {
- /** Whether the lyrics id should be copied. */
- CPSI_FLAG_COPY_LYRICS_ID = 1,
- /** Whether the image id should be copied. */
- CPSI_FLAG_COPY_IMAGE_ID = 2,
- /** Whether the lastplayed time should be copied. */
- CPSI_FLAG_COPY_LASTPLAYED = 4,
- /** Whether the numplayed count should be copied. */
- CPSI_FLAG_COPY_NUMPLAYED = 8,
- /** Whether the attributes should be copied. */
- CPSI_FLAG_COPY_ATTRIBUTES = 16,
- /** Activates verbose mode. */
- CPSI_FLAG_VERBOSE = 32,
-};
+EXPORT_SERVER_CMD_HANDLER(rm);
/** Data passed to the action handler of com_cpsi(). */
struct cpsi_action_data {
- /** command line flags (see \ref cpsi_flags). */
- unsigned flags;
/** Values are copied from here. */
struct afs_info source_afsi;
/** What was passed to com_cpsi_callback(). */
struct afs_callback_arg *aca;
+ bool copy_all;
};
static int copy_selector_info(__a_unused struct osl_table *table,
int ret;
struct afs_info old_afsi, target_afsi;
struct afsi_change_event_data aced;
+ bool a_given, y_given, i_given, l_given, n_given, v_given;
+
+ a_given = SERVER_CMD_OPT_GIVEN(CPSI, ATTRIBUTE_BITMAP, cad->aca->lpr);
+ y_given = SERVER_CMD_OPT_GIVEN(CPSI, LYRICS_ID, cad->aca->lpr);
+ i_given = SERVER_CMD_OPT_GIVEN(CPSI, IMAGE_ID, cad->aca->lpr);
+ l_given = SERVER_CMD_OPT_GIVEN(CPSI, LASTPLAYED, cad->aca->lpr);
+ n_given = SERVER_CMD_OPT_GIVEN(CPSI, NUMPLAYED, cad->aca->lpr);
+ v_given = SERVER_CMD_OPT_GIVEN(CPSI, VERBOSE, cad->aca->lpr);
ret = get_afsi_object_of_row(row, &target_afsi_obj);
if (ret < 0)
return ret;
- load_afsi(&target_afsi, &target_afsi_obj);
+ ret = load_afsi(&target_afsi, &target_afsi_obj);
+ if (ret < 0)
+ return ret;
old_afsi = target_afsi;
- if (cad->flags & CPSI_FLAG_COPY_LYRICS_ID)
+ if (cad->copy_all || y_given)
target_afsi.lyrics_id = cad->source_afsi.lyrics_id;
- if (cad->flags & CPSI_FLAG_COPY_IMAGE_ID)
+ if (cad->copy_all || i_given)
target_afsi.image_id = cad->source_afsi.image_id;
- if (cad->flags & CPSI_FLAG_COPY_LASTPLAYED)
+ if (cad->copy_all || l_given)
target_afsi.last_played = cad->source_afsi.last_played;
- if (cad->flags & CPSI_FLAG_COPY_NUMPLAYED)
+ if (cad->copy_all || n_given)
target_afsi.num_played = cad->source_afsi.num_played;
- if (cad->flags & CPSI_FLAG_COPY_ATTRIBUTES)
+ if (cad->copy_all || a_given)
target_afsi.attributes = cad->source_afsi.attributes;
save_afsi(&target_afsi, &target_afsi_obj); /* in-place update */
- if (cad->flags & CPSI_FLAG_VERBOSE)
+ if (v_given)
para_printf(&cad->aca->pbout, "copied afsi to %s\n", name);
aced.aft_row = row;
aced.old_afsi = &old_afsi;
static int com_cpsi_callback(struct afs_callback_arg *aca)
{
- struct cpsi_action_data cad = {
- .flags = *(unsigned *)aca->query.data,
- .aca = aca
- };
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(CPSI);
+ bool a_given, y_given, i_given, l_given, n_given, v_given;
+ struct cpsi_action_data cad = {.aca = aca};
int ret;
- char *source_path = (char *)aca->query.data + sizeof(cad.flags);
struct pattern_match_data pmd = {
.table = audio_file_table,
.loop_col_num = AFTCOL_HASH,
.match_col_num = AFTCOL_PATH,
- .patterns = {.data = source_path + strlen(source_path) + 1,
- .size = aca->query.size - sizeof(cad.flags)
- - strlen(source_path) - 1},
+ .input_skip = 1, /* skip first argument (source file) */
.data = &cad,
.action = copy_selector_info
};
- ret = get_afsi_of_path(source_path, &cad.source_afsi);
+ ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+ assert(ret >= 0);
+ pmd.lpr = aca->lpr;
+
+ a_given = SERVER_CMD_OPT_GIVEN(CPSI, ATTRIBUTE_BITMAP, aca->lpr);
+ y_given = SERVER_CMD_OPT_GIVEN(CPSI, LYRICS_ID, aca->lpr);
+ i_given = SERVER_CMD_OPT_GIVEN(CPSI, IMAGE_ID, aca->lpr);
+ l_given = SERVER_CMD_OPT_GIVEN(CPSI, LASTPLAYED, aca->lpr);
+ n_given = SERVER_CMD_OPT_GIVEN(CPSI, NUMPLAYED, aca->lpr);
+ v_given = SERVER_CMD_OPT_GIVEN(CPSI, VERBOSE, aca->lpr);
+ cad.copy_all = !a_given && !y_given && !i_given && !l_given && !n_given;
+
+ ret = get_afsi_of_path(lls_input(0, aca->lpr), &cad.source_afsi);
if (ret < 0)
goto out;
ret = for_each_matching_row(&pmd);
if (ret < 0)
goto out;
if (pmd.num_matches > 0) {
- if (cad.flags & CPSI_FLAG_VERBOSE)
+ if (v_given)
para_printf(&aca->pbout, "updated afsi of %u file(s)\n",
pmd.num_matches);
} else
ret = -E_NO_MATCH;
out:
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-int com_cpsi(struct command_context *cc)
+static int com_cpsi(struct command_context *cc, struct lls_parse_result *lpr)
{
- unsigned flags = 0;
- int i;
- struct osl_object options = {.data = &flags, .size = sizeof(flags)};
-
- for (i = 1; i < cc->argc; i++) {
- const char *arg = cc->argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strcmp(arg, "-y")) {
- flags |= CPSI_FLAG_COPY_LYRICS_ID;
- continue;
- }
- if (!strcmp(arg, "-i")) {
- flags |= CPSI_FLAG_COPY_IMAGE_ID;
- continue;
- }
- if (!strcmp(arg, "-l")) {
- flags |= CPSI_FLAG_COPY_LASTPLAYED;
- continue;
- }
- if (!strcmp(arg, "-n")) {
- flags |= CPSI_FLAG_COPY_NUMPLAYED;
- continue;
- }
- if (!strcmp(arg, "-a")) {
- flags |= CPSI_FLAG_COPY_ATTRIBUTES;
- continue;
- }
- if (!strcmp(arg, "-v")) {
- flags |= CPSI_FLAG_VERBOSE;
- continue;
- }
- break;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(CPSI);
+ char *errctx;
+ int ret = lls(lls_check_arg_count(lpr, 2, INT_MAX, &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
}
- if (i + 1 >= cc->argc) /* need at least source file and pattern */
- return -E_AFT_SYNTAX;
- if (!(flags & ~CPSI_FLAG_VERBOSE)) /* no copy flags given */
- flags = ~(unsigned)CPSI_FLAG_VERBOSE | flags;
- return send_option_arg_callback_request(&options, cc->argc - i,
- cc->argv + i, com_cpsi_callback, afs_cb_result_handler, cc);
+ return send_lls_callback_request(com_cpsi_callback, cmd, lpr, cc);
}
+EXPORT_SERVER_CMD_HANDLER(cpsi);
struct change_atts_data {
uint64_t add_mask, del_mask;
static int com_setatt_callback(struct afs_callback_arg *aca)
{
- char *p;
- int ret;
- size_t len;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SETATT);
+ int i, ret;
struct change_atts_data cad = {.aca = aca};
struct pattern_match_data pmd = {
.table = audio_file_table,
.data = &cad,
.action = change_atts
};
+ unsigned num_inputs;
- for (
- p = aca->query.data;
- p < (char *)aca->query.data + aca->query.size;
- p += len + 1
- ) {
- char c;
+ ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+ assert(ret >= 0);
+ pmd.lpr = aca->lpr;
+
+ num_inputs = lls_num_inputs(aca->lpr);
+ for (i = 0; i < num_inputs; i++) {
unsigned char bitnum;
uint64_t one = 1;
+ const char *arg = lls_input(i, aca->lpr);
+ char c, *p;
+ size_t len = strlen(arg);
- len = strlen(p);
ret = -E_ATTR_SYNTAX;
if (len == 0)
goto out;
- c = p[len - 1];
- if (c != '+' && c != '-')
- break;
+ c = arg[len - 1];
+ if (c != '+' && c != '-') {
+ if (cad.add_mask == 0 && cad.del_mask == 0)
+ goto out; /* no attribute modifier given */
+ goto set_atts;
+ }
+ p = para_malloc(len);
+ memcpy(p, arg, len - 1);
p[len - 1] = '\0';
ret = get_attribute_bitnum_by_name(p, &bitnum);
+ free(p);
if (ret < 0) {
- para_printf(&aca->pbout, "attribute not found: %s\n", p);
+ para_printf(&aca->pbout, "invalid argument: %s\n", arg);
goto out;
}
if (c == '+')
else
cad.del_mask |= (one << bitnum);
}
+ /* no pattern given */
ret = -E_ATTR_SYNTAX;
- if (!cad.add_mask && !cad.del_mask)
- goto out;
- pmd.patterns.data = p;
- if (p >= (char *)aca->query.data + aca->query.size)
- goto out;
- pmd.patterns.size = (char *)aca->query.data + aca->query.size - p;
+ goto out;
+set_atts:
+ pmd.input_skip = i;
ret = for_each_matching_row(&pmd);
- if (ret < 0)
- goto out;
- if (pmd.num_matches == 0)
+ if (ret >= 0 && pmd.num_matches == 0)
ret = -E_NO_MATCH;
out:
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-int com_setatt(struct command_context *cc)
+static int com_setatt(struct command_context *cc, struct lls_parse_result *lpr)
{
- 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);
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SETATT);
+ char *errctx;
+ int ret = lls(lls_check_arg_count(lpr, 2, INT_MAX, &errctx));
+
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ return send_lls_callback_request(com_setatt_callback, cmd, lpr, cc);
}
+EXPORT_SERVER_CMD_HANDLER(setatt);
static int afs_stat_callback(struct afs_callback_arg *aca)
{
return 1;
}
-/**
- * The init function of the ALSA mixer.
- *
- * \param self The structure to initialize.
- *
- * \sa struct \ref mixer, \ref oss_mix_init().
- */
-void alsa_mix_init(struct mixer *self)
-{
- self->open = alsa_mix_open;
- self->get_channels = alsa_mix_get_channels;
- self->set_channel = alsa_mix_set_channel;
- self->close = alsa_mix_close;
- self->get = alsa_mix_get;
- self->set = alsa_mix_set;
-}
+/** The mixer operations for the ALSA mixer. */
+const struct mixer alsa_mixer = {
+ .name = "alsa",
+ .open = alsa_mix_open,
+ .get_channels = alsa_mix_get_channels,
+ .set_channel = alsa_mix_set_channel,
+ .close = alsa_mix_close,
+ .get = alsa_mix_get,
+ .set = alsa_mix_set
+};
#include <regex.h>
#include <sys/types.h>
#include <alsa/asoundlib.h>
+#include <lopsub.h>
+#include "write_cmd.lsg.h"
#include "para.h"
#include "fd.h"
#include "string.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "write.h"
-#include "write_common.h"
-#include "alsa_write.cmdline.h"
#include "error.h"
/** Data specific to the alsa writer. */
}
/* Install PCM software and hardware configuration. */
-static int alsa_init(struct private_alsa_write_data *pad,
- struct alsa_write_args_info *conf)
+static int alsa_init(struct writer_node *wn)
{
+ struct private_alsa_write_data *pad = wn->private_data;
snd_pcm_hw_params_t *hwparams = NULL;
snd_pcm_sw_params_t *swparams = NULL;
snd_pcm_uframes_t start_threshold, stop_threshold;
snd_pcm_uframes_t buffer_size, period_size;
snd_output_t *output_log;
int ret;
- const char *msg;
+ const char *msg, *dev = WRITE_CMD_OPT_STRING_VAL(ALSA, DEVICE, wn->lpr);
unsigned period_time;
- PARA_INFO_LOG("opening %s\n", conf->device_arg);
+ PARA_INFO_LOG("opening %s\n", dev);
msg = "unable to open pcm";
- ret = snd_pcm_open(&pad->handle, conf->device_arg,
- SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ ret = snd_pcm_open(&pad->handle, dev, SND_PCM_STREAM_PLAYBACK,
+ SND_PCM_NONBLOCK);
if (ret < 0)
goto fail;
ret = snd_pcm_hw_params_malloc(&hwparams);
if (ret < 0)
goto fail;
/* alsa wants microseconds */
- pad->buffer_time = conf->buffer_time_arg * 1000;
+ pad->buffer_time = 1000U * WRITE_CMD_OPT_UINT32_VAL(ALSA, BUFFER_TIME,
+ wn->lpr);
msg = "could not set buffer time";
ret = snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams,
&pad->buffer_time, NULL);
if (bytes == 0) /* no data available */
return 0;
- pad = para_calloc(sizeof(*pad));
+ pad = wn->private_data = para_calloc(sizeof(*pad));
get_btr_sample_rate(btrn, &val);
pad->sample_rate = val;
get_btr_channels(btrn, &val);
PARA_INFO_LOG("%u channel(s), %uHz\n", pad->channels,
pad->sample_rate);
- ret = alsa_init(pad, wn->conf);
+ ret = alsa_init(wn);
if (ret < 0) {
- free(pad);
+ free(wn->private_data);
+ wn->private_data = NULL;
goto err;
}
- wn->private_data = pad;
wn->min_iqs = pad->bytes_per_frame;
goto again;
}
return ret;
}
-__malloc static void *alsa_parse_config_or_die(int argc, char **argv)
-{
- struct alsa_write_args_info *conf = para_calloc(sizeof(*conf));
-
- /* exits on errors */
- alsa_write_cmdline_parser(argc, argv, conf);
- return conf;
-}
-
-static void alsa_free_config(void *conf)
-{
- alsa_write_cmdline_parser_free(conf);
-}
-
-/**
- * The init function of the alsa writer.
- *
- * \param w Pointer to the writer to initialize.
- *
- * \sa struct \ref writer.
- */
-void alsa_write_init(struct writer *w)
-{
- struct alsa_write_args_info dummy;
+struct writer lsg_write_cmd_com_alsa_user_data = {
- alsa_write_cmdline_parser_init(&dummy);
- w->close = alsa_close;
- w->pre_select = alsa_write_pre_select;
- 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)DEFINE_GGO_HELP(alsa_write);
- alsa_write_cmdline_parser_free(&dummy);
-}
+ .pre_select = alsa_write_pre_select,
+ .post_select = alsa_write_post_select,
+ .close = alsa_close,
+};
/** \file amp_filter.c Paraslash's amplify filter. */
#include <regex.h>
+#include <lopsub.h>
+#include "filter_cmd.lsg.h"
#include "para.h"
-#include "amp_filter.cmdline.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
free(fn->private_data);
}
-static int amp_parse_config(int argc, char **argv, void **config)
-{
- struct amp_filter_args_info *conf = para_calloc(sizeof(*conf));
- int ret;
-
- amp_filter_cmdline_parser(argc, argv, conf);
- ret = -ERRNO_TO_PARA_ERROR(EINVAL);
- if (conf->amp_arg < 0)
- goto err;
- *config = conf;
- return 1;
-err:
- free(conf);
- return ret;
-}
-
static void amp_open(struct filter_node *fn)
{
struct private_amp_data *pad = para_calloc(sizeof(*pad));
- struct amp_filter_args_info *conf = fn->conf;
+ unsigned given = FILTER_CMD_OPT_GIVEN(AMP, AMP, fn->lpr);
+ uint32_t amp_arg = FILTER_CMD_OPT_UINT32_VAL(AMP, AMP, fn->lpr);
fn->private_data = pad;
fn->min_iqs = 2;
- if (!conf->amp_given && stat_item_values[SI_AMPLIFICATION])
+ if (!given && stat_item_values[SI_AMPLIFICATION])
sscanf(stat_item_values[SI_AMPLIFICATION], "%u", &pad->amp);
else
- pad->amp = conf->amp_arg;
+ pad->amp = amp_arg;
PARA_INFO_LOG("amplification: %u (scaling factor: %1.2f)\n",
pad->amp, pad->amp / 64.0 + 1.0);
}
return ret;
}
-static void amp_free_config(void *conf)
-{
- amp_filter_cmdline_parser_free(conf);
-}
-
-/**
- * The init function of the amplify filter.
- *
- * \param f Pointer to the struct to initialize.
- */
-void amp_filter_init(struct filter *f)
-{
- struct amp_filter_args_info dummy;
-
- amp_filter_cmdline_parser_init(&dummy);
- f->open = amp_open;
- f->close = amp_close;
- f->pre_select = generic_filter_pre_select;
- f->post_select = amp_post_select;
- f->parse_config = amp_parse_config;
- f->free_config = amp_free_config;
- f->help = (struct ggo_help)DEFINE_GGO_HELP(amp_filter);
-}
+const struct filter lsg_filter_cmd_com_amp_user_data = {
+ .open = amp_open,
+ .close = amp_close,
+ .pre_select = generic_filter_pre_select,
+ .post_select = amp_post_select,
+};
#include <pthread.h>
#include <ao/ao.h>
#include <regex.h>
+#include <lopsub.h>
+#include "write_cmd.lsg.h"
#include "para.h"
#include "fd.h"
#include "string.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "write.h"
-#include "write_common.h"
-#include "ao_write.cmdline.h"
#include "error.h"
struct private_aow_data {
ao_close(pawd->dev);
free(pawd);
wn->private_data = NULL;
+ ao_shutdown();
}
static void aow_pre_select(struct sched *s, void *context)
return -E_AO_OPEN_LIVE;
}
+static void aow_show_drivers(void)
+{
+ int i, j, num_drivers;
+ ao_info **driver_list;
+
+ PARA_DEBUG_LOG("libao drivers available on this host:\n");
+ PARA_DEBUG_LOG("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
+
+ driver_list = ao_driver_info_list(&num_drivers);
+
+ for (i = 0; i < num_drivers; i++) {
+ ao_info *info = driver_list[i];
+ char *keys = NULL, *tmp = NULL;
+
+ if (info->type == AO_TYPE_FILE)
+ continue;
+ PARA_DEBUG_LOG("%s: %s", info->short_name, info->name);
+ PARA_DEBUG_LOG("priority: %d", info->priority);
+ for (j = 0; j < info->option_count; j++) {
+ tmp = make_message("%s%s%s", keys? keys : "",
+ keys? ", " : "",
+ info->options[j]);
+ free(keys);
+ keys = tmp;
+ }
+ PARA_DEBUG_LOG("keys: %s", keys? keys : "[none]");
+ free(keys);
+ PARA_DEBUG_LOG("comment: %s", info->comment?
+ info->comment : "[none]");
+ }
+}
+
static int aow_init(struct writer_node *wn, unsigned sample_rate,
unsigned channels, int sample_format)
{
ao_option *aoo = NULL;
ao_sample_format asf;
ao_info *info;
+ const struct lls_opt_result *r;
+ unsigned n;
struct private_aow_data *pawd = para_malloc(sizeof(*pawd));
- struct ao_write_args_info *conf = wn->conf;
- if (conf->driver_given) {
+ ao_initialize();
+ aow_show_drivers();
+ if (WRITE_CMD_OPT_GIVEN(AO, DRIVER, wn->lpr)) {
ret = -E_AO_BAD_DRIVER;
- id = ao_driver_id(conf->driver_arg);
+ id = ao_driver_id(WRITE_CMD_OPT_STRING_VAL(AO, DRIVER, wn->lpr));
} else {
ret = -E_AO_DEFAULT_DRIVER;
id = ao_default_driver_id();
goto fail;
}
PARA_INFO_LOG("using %s driver\n", info->short_name);
- for (i = 0; i < conf->ao_option_given; i++) {
- char *o = para_strdup(conf->ao_option_arg[i]), *value;
+ r = WRITE_CMD_OPT_RESULT(AO, AO_OPTION, wn->lpr);
+ n = lls_opt_given(r);
+ for (i = 0; i < n; i++) {
+ char *o = para_strdup(lls_string_val(i, r));
+ char *value;
ret = -E_AO_BAD_OPTION;
value = strchr(o, ':');
return ret;
}
-__malloc static void *aow_parse_config_or_die(int argc, char **argv)
-{
- struct ao_write_args_info *conf = para_calloc(sizeof(*conf));
-
- /* exits on errors */
- ao_write_cmdline_parser(argc, argv, conf);
- return conf;
-}
-
-static void aow_free_config(void *conf)
-{
- ao_write_cmdline_parser_free(conf);
-}
-
-/**
- * The init function of the ao writer.
- *
- * \param w Pointer to the writer to initialize.
- *
- * \sa struct writer.
- */
-void ao_write_init(struct writer *w)
-{
- struct ao_write_args_info dummy;
- int i, j, num_drivers, num_lines;
- ao_info **driver_list;
- char **dh; /* detailed help */
-
- ao_write_cmdline_parser_init(&dummy);
- w->close = aow_close;
- w->pre_select = aow_pre_select;
- 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)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 */
- num_lines = i;
- dh = para_malloc((num_lines + 3) * sizeof(char *));
- for (i = 0; i < num_lines; i++)
- dh[i] = para_strdup(ao_write_args_info_detailed_help[i]);
- dh[num_lines++] = para_strdup("libao drivers available on this host:");
- dh[num_lines++] = para_strdup("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
-
- ao_initialize();
- driver_list = ao_driver_info_list(&num_drivers);
-
- for (i = 0; i < num_drivers; i++) {
- ao_info *info = driver_list[i];
- char *keys = NULL, *tmp = NULL;
-
- if (info->type == AO_TYPE_FILE)
- continue;
- for (j = 0; j < info->option_count; j++) {
- tmp = make_message("%s%s%s", keys? keys : "",
- keys? ", " : "",
- info->options[j]);
- free(keys);
- keys = tmp;
- }
- dh = para_realloc(dh, (num_lines + 6) * sizeof(char *));
- dh[num_lines++] = make_message("%s: %s", info->short_name, info->name);
- dh[num_lines++] = make_message("priority: %d", info->priority);
- dh[num_lines++] = make_message("keys: %s", keys? keys : "[none]");
- dh[num_lines++] = make_message("comment: %s", info->comment?
- info->comment : "[none]");
- dh[num_lines++] = para_strdup(NULL);
- free(keys);
- }
- dh[num_lines] = NULL;
- w->help.detailed_help = (const char **)dh;
- ao_write_cmdline_parser_free(&dummy);
-}
+struct writer lsg_write_cmd_com_ao_user_data = {
+ .close = aow_close,
+ .pre_select = aow_pre_select,
+ .post_select = aow_post_select,
+};
#include <regex.h>
#include <osl.h>
+#include <lopsub.h>
+#include "server_cmd.lsg.h"
#include "para.h"
#include "error.h"
#include "crypt.h"
return 1;
}
-/**
- * Flags used by the lsatt command.
- *
- * \param \sa com_lsatt().
- */
-enum lsatt_flags {
- /** Whether "-a" was given for the lsatt command. */
- LSATT_FLAG_SORT_BY_ID = 1,
- /** Whether "-l" was given for the lsatt command. */
- LSATT_FLAG_LONG = 2,
- /** Reverse sort order. */
- LSATT_FLAG_REVERSE = 4
-};
-
/** Data passed to the action function of lsatt */
static int print_attribute(struct osl_table *table, struct osl_row *row,
const char *name, void *data)
{
struct afs_callback_arg *aca = data;
- unsigned flags = *(unsigned *)aca->query.data;
+ bool l_given = SERVER_CMD_OPT_GIVEN(LSATT, LONG, aca->lpr);
struct osl_object bitnum_obj;
int ret;
- if (!(flags & LSATT_FLAG_LONG)) {
+ if (!l_given) {
para_printf(&aca->pbout, "%s\n", name);
return 1;
}
static int com_lsatt_callback(struct afs_callback_arg *aca)
{
- unsigned flags = *(unsigned *)aca->query.data;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LSATT);
+ bool i_given, r_given;
int ret;
struct pattern_match_data pmd = {
.table = attribute_table,
.loop_col_num = ATTCOL_NAME,
.match_col_num = ATTCOL_NAME,
- .patterns = {.data = (char *)aca->query.data + sizeof(flags),
- .size = aca->query.size - sizeof(flags)},
.pm_flags = PM_NO_PATTERN_MATCHES_EVERYTHING,
.data = aca,
.action = print_attribute
};
- if (flags & LSATT_FLAG_SORT_BY_ID)
+
+ ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr));
+ assert(ret >= 0);
+ pmd.lpr = aca->lpr;
+ i_given = SERVER_CMD_OPT_GIVEN(LSATT, ID_SORT, aca->lpr);
+ r_given = SERVER_CMD_OPT_GIVEN(LSATT, REVERSE, aca->lpr);
+
+ if (i_given)
pmd.loop_col_num = ATTCOL_BITNUM;
- if (flags & LSATT_FLAG_REVERSE)
+ if (r_given)
pmd.pm_flags |= PM_REVERSE_LOOP;
ret = for_each_matching_row(&pmd);
if (ret < 0)
if (pmd.num_matches == 0)
ret = -E_NO_MATCH;
out:
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-int com_lsatt(struct command_context *cc)
+static int com_lsatt(struct command_context *cc, struct lls_parse_result *lpr)
{
- unsigned flags = 0;
- struct osl_object options = {.data = &flags, .size = sizeof(flags)};
- int i;
-
- for (i = 1; i < cc->argc; i++) {
- const char *arg = cc->argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strcmp(arg, "-i")) {
- flags |= LSATT_FLAG_SORT_BY_ID;
- continue;
- }
- if (!strcmp(arg, "-l")) {
- flags |= LSATT_FLAG_LONG;
- continue;
- }
- if (!strcmp(arg, "-r")) {
- flags |= LSATT_FLAG_REVERSE;
- continue;
- }
- }
- return send_option_arg_callback_request(&options, cc->argc - i, cc->argv + i,
- com_lsatt_callback, afs_cb_result_handler, cc);
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LSATT);
+ return send_lls_callback_request(com_lsatt_callback, cmd, lpr, cc);
}
+EXPORT_SERVER_CMD_HANDLER(lsatt);
struct addatt_event_data {
const char *name;
unsigned char bitnum;
};
-
static int com_addatt_callback(struct afs_callback_arg *aca)
{
- char *p;
- int ret = 1;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADDATT);
+ int i, ret = 1;
size_t len;
+ unsigned num_inputs;
- for (
- p = aca->query.data;
- p < (char *)aca->query.data + aca->query.size;
- p += len + 1
- ) {
+ ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr));
+ assert(ret >= 0);
+ num_inputs = lls_num_inputs(aca->lpr);
+ for (i = 0; i < num_inputs; i++) {
+ const char *name = lls_input(i, aca->lpr);
struct osl_object objs[NUM_ATT_COLUMNS];
struct osl_row *row;
unsigned char bitnum;
struct addatt_event_data aed;
- len = strlen(p);
- if (!len || p[len - 1] == '-' || p[len - 1] == '+') {
- para_printf(&aca->pbout, "invalid attribute name: %s\n", p);
+ len = strlen(name);
+ if (len == 0 || name[len - 1] == '-' || name[len - 1] == '+') {
+ para_printf(&aca->pbout,
+ "invalid attribute name: %s\n", name);
continue;
}
- ret = get_attribute_bitnum_by_name(p, &bitnum);
+ ret = get_attribute_bitnum_by_name(name, &bitnum);
if (ret >= 0) {
- para_printf(&aca->pbout, "attribute \"%s\" already exists\n", p);
+ para_printf(&aca->pbout,
+ "attribute \"%s\" already exists\n", name);
continue;
}
if (ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) /* error */
ret = -E_ATT_TABLE_FULL;
goto out;
}
- objs[ATTCOL_NAME].data = p;
+ objs[ATTCOL_NAME].data = (char *)name;
objs[ATTCOL_NAME].size = len + 1;
ret = osl(osl_add_row(attribute_table, objs));
if (ret < 0)
goto out;
- aed.name = p;
+ aed.name = name;
aed.bitnum = bitnum;
ret = afs_event(ATTRIBUTE_ADD, &aca->pbout, &aed);
if (ret < 0)
}
out:
if (ret < 0)
- para_printf(&aca->pbout, "%s: %s\n", p, para_strerror(-ret));
+ para_printf(&aca->pbout, "error while adding %s\n",
+ lls_input(i, aca->lpr));
return ret;
}
-int com_addatt(struct command_context *cc)
+static int com_addatt(struct command_context *cc, struct lls_parse_result *lpr)
{
- int ret;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADDATT);
+ char *errctx;
+ int ret = lls(lls_check_arg_count(lpr, 1, 64, &errctx));
- if (cc->argc < 2)
- return -E_ATTR_SYNTAX;
- ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
- com_addatt_callback, afs_cb_result_handler, cc);
- if (ret < 0)
- send_strerror(cc, -ret);
- return ret;
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ return send_lls_callback_request(com_addatt_callback, cmd, lpr, cc);
}
+EXPORT_SERVER_CMD_HANDLER(addatt);
static int com_mvatt_callback(struct afs_callback_arg *aca)
{
- char *old = aca->query.data;
- size_t size = strlen(old) + 1;
- char *new = old + size;
- struct osl_object obj = {.data = old, .size = size};
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(MVATT);
+ const char *old, *new;
+ struct osl_object obj;
struct osl_row *row;
int ret;
+ ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr));
+ assert(ret >= 0);
+ old = lls_input(0, aca->lpr);
+ new = lls_input(1, aca->lpr);
+ obj.data = (char *)old;
+ obj.size = strlen(old) + 1;
ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row));
if (ret < 0)
goto out;
- obj.data = new;
+ obj.data = (char *)new;
obj.size = strlen(new) + 1;
+ /* The update fails if the destination attribute exists. */
ret = osl(osl_update_object(attribute_table, row, ATTCOL_NAME, &obj));
out:
if (ret < 0)
para_printf(&aca->pbout, "cannot rename %s to %s\n", old, new);
else
ret = afs_event(ATTRIBUTE_RENAME, &aca->pbout, NULL);
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-int com_mvatt(struct command_context *cc)
+static int com_mvatt(struct command_context *cc, struct lls_parse_result *lpr)
{
- if (cc->argc != 3)
- return -E_ATTR_SYNTAX;
- return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
- com_mvatt_callback, afs_cb_result_handler, cc);
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(MVATT);
+ char *errctx;
+ int ret = lls(lls_check_arg_count(lpr, 2, 2, &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ return send_lls_callback_request(com_mvatt_callback, cmd, lpr, cc);
}
+EXPORT_SERVER_CMD_HANDLER(mvatt);
static int remove_attribute(struct osl_table *table, struct osl_row *row,
const char *name, void *data)
static int com_rmatt_callback(struct afs_callback_arg *aca)
{
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(RMATT);
int ret;
struct pattern_match_data pmd = {
.table = attribute_table,
- .patterns = aca->query,
.loop_col_num = ATTCOL_BITNUM,
.match_col_num = ATTCOL_NAME,
.data = aca,
.action = remove_attribute
};
+ ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr));
+ assert(ret >= 0);
+ pmd.lpr = aca->lpr;
ret = for_each_matching_row(&pmd);
if (ret < 0)
goto out;
if (pmd.num_matches == 0)
ret = -E_NO_MATCH;
out:
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-int com_rmatt(struct command_context *cc)
+static int com_rmatt(struct command_context *cc, struct lls_parse_result *lpr)
{
- if (cc->argc < 2)
- return -E_ATTR_SYNTAX;
- return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
- com_rmatt_callback, afs_cb_result_handler, cc);
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(RMATT);
+ char *errctx;
+ int ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ return send_lls_callback_request(com_rmatt_callback, cmd, lpr, cc);
}
+EXPORT_SERVER_CMD_HANDLER(rmatt);
/**
* Return a binary representation of the given attribute value.
#include <sys/un.h>
#include <netdb.h>
#include <signal.h>
+#include <lopsub.h>
+
+#include "audiod_cmd.lsg.h"
+#include "audioc.lsg.h"
-#include "audioc.cmdline.h"
#include "para.h"
#include "error.h"
#include "net.h"
#include "string.h"
#include "fd.h"
-#include "ggo.h"
#include "version.h"
/** Array of error strings. */
DEFINE_PARA_ERRLIST;
-/** The gengetopt structure containing command line args. */
-static struct audioc_args_info conf;
static char *socket_name;
+static struct lls_parse_result *lpr;
+#define CMD_PTR (lls_cmd(0, audioc_suite))
+#define OPT_RESULT(_name) \
+ (lls_opt_result(LSG_AUDIOC_PARA_AUDIOC_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
static int loglevel;
INIT_STDERR_LOGGING(loglevel);
char *buf = NULL;
for (i = 0; i < argc; i++) {
- buf = para_strcat(buf, argv[i]);
+ const char *arg = argv? argv[i] : lls_input(i, lpr);
+ buf = para_strcat(buf, arg);
if (i != argc - 1)
buf = para_strcat(buf, "\n");
}
#include "sched.h"
#include "buffer_tree.h"
#include "interactive.h"
-#include "audiod.completion.h"
static struct sched sched;
static void version_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
- char *opts[] = {"-v", NULL};
+ char *opts[] = {LSG_AUDIOD_CMD_VERSION_OPTS, NULL};
if (ci->word_num <= 2 && ci->word && ci->word[0] == '-')
i9e_complete_option(opts, ci, cr);
struct i9e_completion_result *cr)
{
char *sia[] = {STATUS_ITEM_ARRAY NULL};
- char *opts[] = {"-p", NULL};
+ char *opts[] = {LSG_AUDIOD_CMD_STAT_OPTS, NULL};
if (ci->word_num <= 2 && ci->word && ci->word[0] == '-')
i9e_complete_option(opts, ci, cr);
static void grab_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
- char *opts[] = {"-ms", "-ms", "-ma", "-p=", "-n=", "-o", NULL};
+ char *opts[] = {LSG_AUDIOD_CMD_GRAB_OPTS, NULL};
i9e_complete_option(opts, ci, cr);
}
+I9E_DUMMY_COMPLETER(SUPERCOMMAND_UNAVAILABLE);
static struct i9e_completer audiod_completers[] = {
- AUDIOD_COMPLETERS
+#define LSG_AUDIOD_CMD_CMD(_name) {.name = #_name, \
+ .completer = _name ## _completer}
+ LSG_AUDIOD_CMD_SUBCOMMANDS
+#undef LSG_AUDIOD_CMD_CMD
{.name = NULL}
};
char *buf = NULL;
struct audioc_task *at = context;
int ret = btr_node_status(at->btrn, 0, BTR_NT_ROOT);
+ size_t bufsize;
if (ret < 0)
goto out;
if (!FD_ISSET(at->fd, &s->rfds))
return 0;
- buf = para_malloc(conf.bufsize_arg);
- ret = recv_bin_buffer(at->fd, buf, conf.bufsize_arg);
+ bufsize = PARA_MAX(1024U, OPT_UINT32_VAL(BUFSIZE));
+ buf = para_malloc(bufsize);
+ ret = recv_bin_buffer(at->fd, buf, bufsize);
PARA_DEBUG_LOG("recv: %d\n", ret);
if (ret == 0)
ret = -E_AUDIOC_EOF;
static int audioc_i9e_line_handler(char *line)
{
- char *args = NULL;
- int ret;
+ int argc, ret;
+ char *args, **argv;
PARA_DEBUG_LOG("line: %s\n", line);
- ret = create_argv(line, " ", &conf.inputs);
+ ret = create_argv(line, " ", &argv);
if (ret < 0)
return ret;
- conf.inputs_num = ret;
- args = concat_args(conf.inputs_num, conf.inputs);
- free_argv(conf.inputs);
+ argc = ret;
+ args = concat_args(argc, argv);
+ free_argv(argv);
if (!args)
return 0;
- conf.inputs_num = 0; /* required for audioc_cmdline_parser_free() */
ret = connect_audiod(socket_name, args);
+ free(args);
if (ret < 0)
- goto out;
+ return ret;
at->fd = ret;
ret = mark_fd_nonblocking(at->fd);
if (ret < 0)
goto close;
- free(args);
- args = NULL;
at->btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.name = "audioc line handler"));
at->task = task_register(&(struct task_info) {
return 1;
close:
close(at->fd);
-out:
- free(args);
return ret;
}
.loglevel = loglevel,
.completers = audiod_completers,
};
+
PARA_NOTICE_LOG("\n%s\n", version_text("audioc"));
- if (conf.history_file_given)
- history_file = para_strdup(conf.history_file_arg);
+ if (OPT_GIVEN(HISTORY_FILE))
+ history_file = para_strdup(OPT_STRING_VAL(HISTORY_FILE));
else {
char *home = para_homedir();
history_file = make_message("%s/.paraslash/audioc.history",
para_log = stderr_log;
out:
free(history_file);
- audioc_cmdline_parser_free(&conf);
+ free(socket_name);
if (ret < 0)
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
return NULL;
}
-__noreturn static void print_help_and_die(void)
+static void handle_help_flag(void)
{
- struct ggo_help h = DEFINE_GGO_HELP(audioc);
- bool d = conf.detailed_help_given;
+ char *help;
- ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
- exit(0);
+ if (OPT_GIVEN(DETAILED_HELP))
+ help = lls_long_help(CMD_PTR);
+ else if (OPT_GIVEN(HELP))
+ help = lls_short_help(CMD_PTR);
+ else
+ return;
+ printf("%s\n", help);
+ free(help);
+ exit(EXIT_SUCCESS);
}
/**
*/
int main(int argc, char *argv[])
{
+ const struct lls_command *cmd = CMD_PTR;
int ret, fd;
- char *cf, *buf = NULL, *args = NULL;
+ char *cf = NULL, *buf, *args, *errctx = NULL;
size_t bufsize;
+ struct lls_parse_result *lpr1, *lpr2, *lpr3;
+ unsigned num_inputs;
- 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();
+ ret = lls(lls_parse(argc, argv, cmd, &lpr1, &errctx));
+ if (ret < 0)
+ goto fail;
+ lpr = lpr1;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
+ version_handle_flag("audioc", OPT_GIVEN(VERSION));
+ handle_help_flag();
cf = configfile_exists();
if (cf) {
- struct audioc_cmdline_parser_params params = {
- .override = 0,
- .initialize = 0,
- .check_required = 0,
- .check_ambiguity = 0,
- .print_errors = 1,
-
- };
- audioc_cmdline_parser_config_file(cf, &conf, ¶ms);
- free(cf);
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ void *map;
+ size_t sz;
+ int cf_argc;
+ char **cf_argv;
+ ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+ if (ret != -E_EMPTY) {
+ if (ret < 0)
+ goto out;
+ ret = lls(lls_convert_config(map, sz, NULL, &cf_argv,
+ &errctx));
+ para_munmap(map, sz);
+ if (ret < 0) {
+ PARA_ERROR_LOG("syntax error in %s\n", cf);
+ goto out;
+ }
+ cf_argc = ret;
+ ret = lls(lls_parse(cf_argc, cf_argv, cmd, &lpr2,
+ &errctx));
+ lls_free_argv(cf_argv);
+ if (ret < 0) {
+ PARA_ERROR_LOG("parse error in %s\n", cf);
+ goto out;
+ }
+ ret = lls(lls_merge(lpr1, lpr2, cmd, &lpr3, &errctx));
+ lls_free_parse_result(lpr2, cmd);
+ if (ret < 0)
+ goto out;
+ lls_free_parse_result(lpr1, cmd);
+ lpr = lpr3;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
+ }
}
- if (conf.socket_given)
- socket_name = para_strdup(conf.socket_arg);
+ if (OPT_GIVEN(COMPLETE))
+ print_completions();
+ if (OPT_GIVEN(SOCKET))
+ socket_name = para_strdup(OPT_STRING_VAL(SOCKET));
else {
char *hn = para_hostname();
socket_name = make_message("/var/paraslash/audiod_socket.%s",
hn);
free(hn);
}
-
- if (conf.complete_given)
- print_completions();
-
- if (conf.inputs_num == 0)
+ num_inputs = lls_num_inputs(lpr);
+ if (num_inputs == 0)
interactive_session();
- args = concat_args(conf.inputs_num, conf.inputs);
+ args = concat_args(num_inputs, NULL);
ret = connect_audiod(socket_name, args);
free(socket_name);
+ free(args);
if (ret < 0)
goto out;
fd = ret;
ret = mark_fd_blocking(STDOUT_FILENO);
if (ret < 0)
goto out;
- bufsize = conf.bufsize_arg;
+ bufsize = PARA_MAX(1024U, OPT_UINT32_VAL(BUFSIZE));
buf = para_malloc(bufsize);
do {
size_t n = ret = recv_bin_buffer(fd, buf, bufsize);
break;
ret = write_all(STDOUT_FILENO, buf, n);
} while (ret >= 0);
-out:
free(buf);
- free(args);
+out:
+ lls_free_parse_result(lpr, cmd);
+ free(cf);
+fail:
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
if (ret < 0)
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
#include <netdb.h>
#include <signal.h>
#include <pwd.h>
+#include <lopsub.h>
+#include "audiod.lsg.h"
+#include "recv_cmd.lsg.h"
#include "para.h"
#include "error.h"
#include "crypt.h"
-#include "audiod.cmdline.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "recv.h"
#include "filter.h"
#include "grab_client.h"
-#include "client.cmdline.h"
#include "client.h"
#include "audiod.h"
#include "net.h"
#include "string.h"
#include "fd.h"
#include "write.h"
-#include "write_common.h"
#include "signal.h"
#include "version.h"
/** Array of error strings. */
DEFINE_PARA_ERRLIST;
+static struct lls_parse_result *lpr;
+#define CMD_PTR (lls_cmd(0, audiod_suite))
+#define OPT_RESULT(_name) (lls_opt_result(LSG_AUDIOD_PARA_AUDIOD_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
+#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \
+ lls_opt(LSG_AUDIOD_PARA_AUDIOD_OPT_ ## _name, CMD_PTR)))
+
__printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
/** define the array containing all supported audio formats */
const char *audio_formats[] = {AUDIOD_AUDIO_FORMAT_ARRAY NULL};
-DEFINE_RECEIVER_ARRAY;
-
/** Defines how audiod handles one supported audio format. */
struct audio_format_info {
- /** pointer to the receiver for this audio format */
- struct receiver *receiver;
- /** the receiver configuration */
- void *receiver_conf;
+ /** the receiver for this audio format */
+ int receiver_num;
+ /** Parsed receiver command line. */
+ struct lls_parse_result *receiver_lpr;
/** the number of filters that should be activated for this audio format */
unsigned int num_filters;
/** Array of filter numbers to be activated. */
unsigned *filter_nums;
/** Pointer to the array of filter configurations. */
void **filter_conf;
+ /** Parsed filter command line, one parse result per filter. */
+ struct lls_parse_result **filter_lpr;
/** the number of filters that should be activated for this audio format */
unsigned int num_writers;
- /** Array of writer numbers to be activated. */
- int *writer_nums;
- /** pointer to the array of writer configurations */
- void **writer_conf;
+ /** Array of writer IDs to be activated. */
+ int *wids;
+ /** Parsed writer command line(s) */
+ struct lls_parse_result **writer_lpr;
/** do not start receiver/filters/writer before this time */
struct timeval restart_barrier;
};
struct writer_node *wns;
};
+#define RECEIVER_CMD(_a) lls_cmd((_a)->receiver_num, recv_cmd_suite)
+#define RECEIVER(_a) ((const struct receiver *)lls_user_data(RECEIVER_CMD(_a)))
+
/** Maximal number of simultaneous instances. */
#define MAX_STREAM_SLOTS 5
*/
int audiod_status = AUDIOD_ON;
-/**
- * the gengetopt args_info struct that holds information on all command line
- * arguments
- */
-static struct audiod_args_info conf;
-
static char *socket_name;
static struct audio_format_info afi[NUM_AUDIO_FORMATS];
-
static struct signal_task *signal_task;
-
static struct status_task status_task_struct;
-
static uid_t *uid_whitelist;
/**
*/
static struct status_task *stat_task = &status_task_struct;
-/*
- * The task for handling audiod commands.
- *
- * We need two listening sockets for backward compability: on Linux systems
- * fd[0] is an abstract socket (more precisely, a socket bound to an address in
- * the abstract namespace), and fd[1] is the usual pathname socket. On other
- * systems, fd[0] is negative, and only the pathname socket is used.
- *
- * For 0.5.x we accept connections on both sockets to make sure that old
- * para_audioc versions can still connect. New versions use only the abstract
- * socket. Hence after v0.6.0 we can go back to a single socket, either an
- * abstract one (Linux) or a pathname socket (all other systems).
- */
struct command_task {
- /** The local listening sockets. */
- int fd[2];
+ /** The local listening socket. */
+ int fd;
/** the associated task structure */
struct task *task;
};
rskip; /* receiver start - sss */
int slot_num = get_play_time_slot_num();
struct slot_info *s = slot_num < 0? NULL : &slot[slot_num];
+ bool writer_active = s && s->wns && s->wns[0].btrn;
char *msg;
if (audiod_status == AUDIOD_OFF)
}
/*
* Valid status items and playing, set length and tmp to the stream
- * start. We use the slot info and fall back to the info from current
- * status items if no slot info is available.
+ * start. We use the writer start time from the slot info and fall back
+ * to the info from current status items if no writer is active yet.
*/
tmp = &stat_task->server_stream_start;
- if (s && s->wns && s->wns[0].btrn) { /* writer active in this slot */
+ if (writer_active) {
btr_get_node_start(s->wns[0].btrn, &wstime);
if (wstime.tv_sec != 0) { /* writer wrote something */
if (s->server_stream_start.tv_sec == 0) {
tv_diff(tmp, &stat_task->sa_time_diff, &sss);
else
tv_add(tmp, &stat_task->sa_time_diff, &sss);
- if (!s || !s->wns || !s->wns[0].btrn || wstime.tv_sec == 0) {
+ if (!writer_active) {
struct timeval diff;
tv_diff(now, &sss, &diff);
seconds = diff.tv_sec + stat_task->offset_seconds;
static void parse_config_or_die(void)
{
- int ret, i;
- char *config_file;
- struct audiod_cmdline_parser_params params = {
- .override = 0,
- .initialize = 0,
- .check_required = 1,
- .check_ambiguity = 0,
- .print_errors = 1
- };
-
- if (conf.config_file_given)
- config_file = para_strdup(conf.config_file_arg);
+ int ret;
+ char *cf, *errctx = NULL;
+ void *map;
+ size_t sz;
+
+ if (OPT_GIVEN(CONFIG_FILE))
+ cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
else {
char *home = para_homedir();
- config_file = make_message("%s/.paraslash/audiod.conf", home);
+ cf = make_message("%s/.paraslash/audiod.conf", home);
free(home);
}
- ret = file_exists(config_file);
- if (conf.config_file_given && !ret) {
- PARA_EMERG_LOG("can not read config file %s\n", config_file);
- free(config_file);
- goto err;
- }
- if (ret) {
- audiod_cmdline_parser_config_file(config_file, &conf, ¶ms);
- daemon_set_loglevel(conf.loglevel_arg);
+ ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+ if (ret < 0) {
+ if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+ goto free_cf;
+ if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE))
+ goto free_cf;
+ } else {
+ int cf_argc;
+ char **cf_argv;
+ struct lls_parse_result *cf_lpr, *merged_lpr;
+ ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx));
+ para_munmap(map, sz);
+ if (ret < 0)
+ goto free_cf;
+ cf_argc = ret;
+ ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr, &errctx));
+ lls_free_argv(cf_argv);
+ if (ret < 0)
+ goto free_cf;
+ ret = lls(lls_merge(lpr, cf_lpr, CMD_PTR, &merged_lpr,
+ &errctx));
+ lls_free_parse_result(cf_lpr, CMD_PTR);
+ if (ret < 0)
+ goto free_cf;
+ lls_free_parse_result(lpr, CMD_PTR);
+ lpr = merged_lpr;
}
- free(config_file);
- if (conf.user_allow_given > 0) {
- uid_whitelist = para_malloc(conf.user_allow_given
- * sizeof(uid_t));
- for (i = 0; i < conf.user_allow_given; i++) {
+ daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+ if (OPT_GIVEN(USER_ALLOW)) {
+ uint32_t n = OPT_GIVEN(USER_ALLOW);
+ int i;
+
+ uid_whitelist = para_malloc(n * sizeof(uid_t));
+ for (i = 0; i < n; i++) {
+ const char *arg = lls_string_val(i,
+ OPT_RESULT(USER_ALLOW));
int32_t val;
struct passwd *pw;
- ret = para_atoi32(conf.user_allow_arg[i], &val);
+ ret = para_atoi32(arg, &val);
if (ret >= 0) {
uid_whitelist[i] = val;
continue;
}
errno = 0; /* see getpwnam(3) */
- pw = getpwnam(conf.user_allow_arg[i]);
+ pw = getpwnam(arg);
if (!pw) {
- PARA_EMERG_LOG("invalid username: %s\n",
- conf.user_allow_arg[i]);
- goto err;
+ PARA_EMERG_LOG("invalid username: %s\n", arg);
+ free(uid_whitelist);
+ goto free_cf;
}
uid_whitelist[i] = pw->pw_uid;
}
}
- return;
-err:
- exit(EXIT_FAILURE);
+ ret = 0;
+free_cf:
+ free(cf);
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ lls_free_parse_result(lpr, CMD_PTR);
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+ exit(EXIT_FAILURE);
+ }
}
static void setup_signal_handling(void)
a = &afi[s->format];
PARA_NOTICE_LOG("closing %s receiver in slot %d\n",
audio_formats[s->format], slot_num);
- a->receiver->close(s->receiver_node);
+ RECEIVER(a)->close(s->receiver_node);
btr_remove_node(&s->receiver_node->btrn);
task_reap(&s->receiver_node->task);
free(s->receiver_node);
static void writer_cleanup(struct writer_node *wn)
{
- struct writer *w;
-
if (!wn)
return;
- w = writers + wn->writer_num;
- PARA_INFO_LOG("closing %s\n", writer_names[wn->writer_num]);
- w->close(wn);
+ PARA_INFO_LOG("closing %s\n", writer_name(wn->wid));
+ writer_get(wn->wid)->close(wn);
btr_remove_node(&wn->btrn);
task_reap(&wn->task);
}
parent = s->receiver_node->btrn;
for (i = 0; i < nf; i++) {
char buf[20];
+ const char *name;
const struct filter *f = filter_get(a->filter_nums[i]);
fn = s->fns + i;
fn->filter_num = a->filter_nums[i];
fn->conf = a->filter_conf[i];
+ fn->lpr = a->filter_lpr[i];
+ name = filter_name(fn->filter_num);
fn->btrn = btr_new_node(&(struct btr_node_description)
- EMBRACE(.name = f->name, .parent = parent,
+ EMBRACE(.name = name, .parent = parent,
.handler = f->execute, .context = fn));
if (f->open)
f->open(fn);
- sprintf(buf, "%s (slot %d)", f->name, (int)(s - slot));
+ sprintf(buf, "%s (slot %d)", name, (int)(s - slot));
fn->task = task_register(&(struct task_info) {
.name = buf,
.pre_select = f->pre_select,
}, &sched);
parent = fn->btrn;
PARA_NOTICE_LOG("%s filter %d/%d (%s) started in slot %d\n",
- audio_formats[s->format], i, nf, f->name, (int)(s - slot));
+ audio_formats[s->format], i, nf, name, (int)(s - slot));
}
}
* sizeof(struct writer_node));
for (i = 0; i < a->num_writers; i++) {
wn = s->wns + i;
- wn->conf = a->writer_conf[i];
- wn->writer_num = a->writer_nums[i];
+ wn->wid = a->wids[i];
+ wn->lpr = a->writer_lpr[i];
register_writer_node(wn, parent, &sched);
PARA_NOTICE_LOG("%s writer started in slot %d\n",
- writer_names[a->writer_nums[i]], (int)(s - slot));
+ writer_name(a->wids[i]), (int)(s - slot));
}
}
struct audio_format_info *a = &afi[format];
struct slot_info *s;
int ret, slot_num;
- struct receiver *r = a->receiver;
+ const struct receiver *r = RECEIVER(a);
+ const char *name = lls_command_name(RECEIVER_CMD(a));
struct receiver_node *rn;
tv_add(now, &(struct timeval)EMBRACE(2, 0), &a->restart_barrier);
slot_num = ret;
rn = para_calloc(sizeof(*rn));
rn->receiver = r;
- rn->conf = a->receiver_conf;
+ rn->lpr = a->receiver_lpr;
rn->btrn = btr_new_node(&(struct btr_node_description)
- EMBRACE(.name = r->name, .context = rn));
+ EMBRACE(.name = name, .context = rn));
ret = r->open(rn);
if (ret < 0) {
btr_remove_node(&rn->btrn);
s->format = format;
s->receiver_node = rn;
PARA_NOTICE_LOG("started %s: %s receiver in slot %d\n",
- audio_formats[format], r->name, slot_num);
+ audio_formats[format], name, slot_num);
rn->task = task_register(&(struct task_info) {
- .name = r->name,
+ .name = name,
.pre_select = r->pre_select,
.post_select = r->post_select,
.context = rn,
return 1;
}
-static int parse_stream_command(const char *txt, char **cmd)
+static int parse_stream_command(const char *txt, const char **cmd)
{
int ret, len;
char *re, *p = strchr(txt, ':');
return ret;
}
-static int add_filter(int format, char *cmdline)
+static int add_filter(int format, const char *cmdline)
{
struct audio_format_info *a = &afi[format];
int filter_num, nf = a->num_filters;
void *cfg;
+ struct lls_parse_result *flpr;
- filter_num = check_filter_arg(cmdline, &cfg);
- if (filter_num < 0)
- return filter_num;
+ filter_num = filter_setup(cmdline, &cfg, &flpr);
+ a->filter_lpr = para_realloc(a->filter_lpr,
+ (nf + 1) * sizeof(flpr));
a->filter_conf = para_realloc(a->filter_conf,
(nf + 1) * sizeof(void *));
a->filter_nums = para_realloc(a->filter_nums,
(nf + 1) * sizeof(unsigned));
+
a->filter_nums[nf] = filter_num;
a->filter_conf[nf] = cfg;
+ a->filter_lpr[nf] = flpr;
a->num_filters++;
PARA_INFO_LOG("%s filter %d: %s\n", audio_formats[format], nf,
- filter_get(filter_num)->name);
+ filter_name(filter_num));
return filter_num;
}
static int parse_writer_args(void)
{
int i, ret;
- char *cmd;
+ const char *cmd;
struct audio_format_info *a;
- for (i = 0; i < conf.writer_given; i++) {
- void *wconf;
- int j, nw, writer_num, af_mask;
+ for (i = 0; i < OPT_GIVEN(WRITER); i++) {
+ int j, nw, af_mask;
- ret = parse_stream_command(conf.writer_arg[i], &cmd);
+ ret = parse_stream_command(lls_string_val(i,
+ OPT_RESULT(WRITER)), &cmd);
if (ret < 0)
return ret;
af_mask = ret;
a = afi + j;
if ((af_mask & (1 << j)) == 0) /* no match */
continue;
- wconf = check_writer_arg_or_die(cmd, &writer_num);
nw = a->num_writers;
- a->writer_nums = para_realloc(a->writer_nums, (nw + 1) * sizeof(int));
- a->writer_conf = para_realloc(a->writer_conf, (nw + 1) * sizeof(void *));
- a->writer_nums[nw] = writer_num;
- a->writer_conf[nw] = wconf;
+ a->wids = para_realloc(a->wids, (nw + 1) * sizeof(int));
+ a->writer_lpr = para_realloc(a->writer_lpr,
+ (nw + 1) * sizeof(struct lls_parse_result *));
+ a->wids[nw] = check_writer_arg_or_die(cmd,
+ a->writer_lpr + nw);
PARA_INFO_LOG("%s writer #%d: %s\n", audio_formats[j],
- nw, writer_names[writer_num]);
+ nw, writer_name(a->wids[nw]));
a->num_writers++;
}
}
/* Use default writer for audio formats which are not yet set up. */
FOR_EACH_AUDIO_FORMAT(i) {
- void *writer_conf;
- int writer_num;
a = afi + i;
if (a->num_writers > 0)
continue; /* already set up */
- writer_conf = check_writer_arg_or_die(NULL, &writer_num);
- a->writer_nums = para_malloc(sizeof(int));
- a->writer_nums[0] = writer_num;
- a->writer_conf = para_malloc(sizeof(void *));
- a->writer_conf[0] = writer_conf;
a->num_writers = 1;
+ a->wids = para_malloc(sizeof(int));
+ a->writer_lpr = para_malloc(sizeof(struct lls_parse_result *));
+ a->wids[0] = check_writer_arg_or_die(NULL, a->writer_lpr);
PARA_INFO_LOG("%s writer: %s (default)\n", audio_formats[i],
- writer_names[writer_num]);
+ writer_name(a->wids[0]));
}
return 1;
}
static int parse_receiver_args(void)
{
- int i, ret, receiver_num;
- char *cmd = NULL;
+ int i, ret;
+ const char *arg;
struct audio_format_info *a;
- for (i = conf.receiver_given - 1; i >= 0; i--) {
- char *arg;
+ FOR_EACH_AUDIO_FORMAT(i)
+ afi[i].receiver_num = -1;
+ for (i = OPT_GIVEN(RECEIVER) - 1; i >= 0; i--) {
int j, af_mask;
- ret = parse_stream_command(conf.receiver_arg[i], &arg);
+ ret = parse_stream_command(lls_string_val(i,
+ OPT_RESULT(RECEIVER)), &arg);
if (ret < 0)
goto out;
af_mask = ret;
* config here. Since we are iterating backwards, the winning
* receiver arg is in fact the first one given.
*/
- if (a->receiver_conf)
- a->receiver->free_config(a->receiver_conf);
- a->receiver_conf = check_receiver_arg(arg, &receiver_num);
- ret = -E_RECV_SYNTAX;
- if (!a->receiver_conf)
- goto out;
- a->receiver = receivers + receiver_num;
+ lls_free_parse_result(a->receiver_lpr, RECEIVER_CMD(a));
+ a->receiver_num = check_receiver_arg(arg, &a->receiver_lpr);
}
}
/*
- * Use the first available receiver with no arguments for those audio
- * formats for which no receiver was specified.
+ * Use the default receiver for those audio formats for which no
+ * receiver was specified.
*/
- cmd = para_strdup(receivers[0].name);
FOR_EACH_AUDIO_FORMAT(i) {
- a = &afi[i];
- if (a->receiver_conf)
+ a = afi + i;
+ if (a->receiver_num >= 0)
continue;
- a->receiver_conf = check_receiver_arg(cmd, &receiver_num);
- if (!a->receiver_conf)
- return -E_RECV_SYNTAX;
- a->receiver = &receivers[receiver_num];
+ a->receiver_num = check_receiver_arg(NULL, &a->receiver_lpr);
}
FOR_EACH_AUDIO_FORMAT(i) {
a = afi + i;
PARA_INFO_LOG("receiving %s streams via %s receiver\n",
- audio_formats[i], a->receiver->name);
+ audio_formats[i], lls_command_name(RECEIVER_CMD(a)));
}
ret = 1;
out:
- free(cmd);
return ret;
}
FOR_EACH_AUDIO_FORMAT(i) {
struct audio_format_info *a = &afi[i];
+ const char *name = lls_command_name(RECEIVER_CMD(a));
char *tmp;
int j;
* udp and dccp streams are fec-encoded, so add fecdec as the
* first filter.
*/
- if (strcmp(afi[i].receiver->name, "udp") == 0 ||
- strcmp(afi[i].receiver->name, "dccp") == 0) {
+ if (strcmp(name, "udp") == 0 || strcmp(name, "dccp") == 0) {
tmp = para_strdup("fecdec");
add_filter(i, tmp);
free(tmp);
/* add "dec" to audio format name */
tmp = make_message("%sdec", audio_formats[i]);
for (j = 0; filter_get(j); j++)
- if (!strcmp(tmp, filter_get(j)->name))
+ if (!strcmp(tmp, filter_name(j)))
break;
free(tmp);
ret = -E_UNSUPPORTED_FILTER;
if (!filter_get(j))
goto out;
- tmp = para_strdup(filter_get(j)->name);
+ tmp = para_strdup(filter_name(j));
ret = add_filter(i, tmp);
free(tmp);
if (ret < 0)
goto out;
PARA_INFO_LOG("%s -> default filter: %s\n", audio_formats[i],
- filter_get(j)->name);
+ filter_name(j));
}
out:
return ret;
{
int i, j, ret, af_mask, num_matches;
- for (i = 0; i < conf.filter_given; i++) {
- char *arg;
- ret = parse_stream_command(conf.filter_arg[i], &arg);
+ for (i = 0; i < OPT_GIVEN(FILTER); i++) {
+ const char *arg;
+ ret = parse_stream_command(lls_string_val(i,
+ OPT_RESULT(FILTER)), &arg);
if (ret < 0)
goto out;
af_mask = ret;
}
if (num_matches == 0)
PARA_WARNING_LOG("ignoring filter spec: %s\n",
- conf.filter_arg[i]);
+ lls_string_val(i, OPT_RESULT(FILTER)));
}
ret = init_default_filters(); /* use default values for the rest */
out:
}
/* does not unlink socket on errors */
-static void init_local_sockets(struct command_task *ct)
+static void init_local_socket(struct command_task *ct)
{
- if (conf.socket_given)
- socket_name = para_strdup(conf.socket_arg);
+ if (OPT_GIVEN(SOCKET))
+ socket_name = para_strdup(OPT_STRING_VAL(SOCKET));
else {
char *hn = para_hostname();
socket_name = make_message("/var/paraslash/audiod_socket.%s",
free(hn);
}
PARA_NOTICE_LOG("local socket: %s\n", socket_name);
- if (conf.force_given)
+ if (OPT_GIVEN(FORCE))
unlink(socket_name);
- ct->fd[0] = create_local_socket(socket_name, 0);
- ct->fd[1] = create_local_socket(socket_name,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
- if (ct->fd[0] >= 0 || ct->fd[1] >= 0)
+ ct->fd = create_local_socket(socket_name);
+ if (ct->fd >= 0)
return;
- PARA_EMERG_LOG("%s\n", para_strerror(-ct->fd[1]));
+ PARA_EMERG_LOG("%s\n", para_strerror(-ct->fd));
exit(EXIT_FAILURE);
}
static void command_pre_select(struct sched *s, void *context)
{
struct command_task *ct = context;
- int i;
-
- for (i = 0; i < 2; i++)
- if (ct->fd[i] >= 0)
- para_fd_set(ct->fd[i], &s->rfds, &s->max_fileno);
+ para_fd_set(ct->fd, &s->rfds, &s->max_fileno);
}
static int command_post_select(struct sched *s, void *context)
{
- int ret, i;
+ int ret;
struct command_task *ct = context;
static struct timeval last_status_dump;
struct timeval tmp, delay;
ret = task_get_notification(ct->task);
if (ret < 0)
return ret;
- for (i = 0; i < 2; i++) {
- if (ct->fd[i] < 0)
- continue;
- ret = handle_connect(ct->fd[i], &s->rfds);
- if (ret < 0) {
- PARA_ERROR_LOG("%s\n", para_strerror(-ret));
- if (ret == -E_AUDIOD_TERM) {
- task_notify_all(s, -ret);
- return ret;
- }
- } else if (ret > 0)
- force = true;
- }
+ ret = handle_connect(ct->fd, &s->rfds);
+ if (ret < 0) {
+ PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ if (ret == -E_AUDIOD_TERM) {
+ task_notify_all(s, -ret);
+ return ret;
+ }
+ } else if (ret > 0)
+ force = true;
if (force == true)
goto dump;
static void init_command_task(struct command_task *ct)
{
- init_local_sockets(ct); /* doesn't return on errors */
+ init_local_socket(ct); /* doesn't return on errors */
ct->task = task_register(&(struct task_info) {
.name = "command",
unlink(socket_name);
close_stat_pipe();
close_unused_slots();
- audiod_cmdline_parser_free(&conf);
close_stat_clients();
free(uid_whitelist);
}
goto out;
}
close_stat_pipe();
- st->clock_diff_count = conf.clock_diff_count_arg;
+ st->clock_diff_count = OPT_UINT32_VAL(CLOCK_DIFF_COUNT);
goto out;
}
if (st->ct) {
{
memset(st, 0, sizeof(struct status_task));
st->sa_time_diff_sign = 1;
- st->clock_diff_count = conf.clock_diff_count_arg;
+ st->clock_diff_count = OPT_UINT32_VAL(CLOCK_DIFF_COUNT);
st->current_audio_format_num = -1;
st->btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.name = "stat"));
static void set_initial_status(void)
{
audiod_status = AUDIOD_ON;
- if (!conf.mode_given)
+ if (!OPT_GIVEN(MODE))
return;
- if (!strcmp(conf.mode_arg, "sb")) {
+ if (!strcmp(OPT_STRING_VAL(MODE), "sb")) {
audiod_status = AUDIOD_STANDBY;
return;
}
- if (!strcmp(conf.mode_arg, "off")) {
+ if (!strcmp(OPT_STRING_VAL(MODE), "off")) {
audiod_status = AUDIOD_OFF;
return;
}
- if (strcmp(conf.mode_arg, "on"))
+ if (strcmp(OPT_STRING_VAL(MODE), "on"))
PARA_WARNING_LOG("invalid mode\n");
}
-__noreturn static void print_help_and_die(void)
-{
- 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);
-}
-
/**
* Lookup the given UID in the whitelist.
*
{
int i;
- if (!conf.user_allow_given)
+ if (!OPT_GIVEN(USER_ALLOW))
return true;
- for (i = 0; i < conf.user_allow_given; i++)
+ for (i = 0; i < OPT_GIVEN(USER_ALLOW); i++)
if (uid == uid_whitelist[i])
return true;
return false;
}
+static void handle_help_flags(void)
+{
+ char *help;
+ bool d = OPT_GIVEN(DETAILED_HELP);
+
+ if (d)
+ help = lls_long_help(CMD_PTR);
+ else if (OPT_GIVEN(HELP))
+ help = lls_short_help(CMD_PTR);
+ else
+ return;
+ printf("%s\n", help);
+ free(help);
+ print_receiver_helps(d);
+ print_filter_helps(d);
+ print_writer_helps(d);
+ exit(EXIT_SUCCESS);
+}
+
/**
* the main function of para_audiod
*
{
int ret, i;
struct command_task command_task_struct, *cmd_task = &command_task_struct;
- struct audiod_cmdline_parser_params params = {
- .override = 0,
- .initialize = 1,
- .check_required = 0,
- .check_ambiguity = 0,
- .print_errors = 1
- };
+ char *errctx;
valid_fd_012();
- 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();
- writer_init();
- if (conf.help_given || conf.detailed_help_given)
- print_help_and_die();
- daemon_set_priority(conf.priority_arg);
- daemon_drop_privileges_or_die(conf.user_arg, conf.group_arg);
+ ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
+ if (ret < 0)
+ goto out;
+ daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+ daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
+ OPT_STRING_VAL(GROUP));
+ version_handle_flag("audiod", OPT_GIVEN(VERSION));
+ handle_help_flags();
parse_config_or_die();
- if (daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no,
- conf.logfile_given)) {
- for (i = 0; i < conf.log_color_given; i++)
- daemon_set_log_color_or_die(conf.log_color_arg[i]);
- }
init_random_seed_or_die();
+ daemon_set_priority(OPT_UINT32_VAL(PRIORITY));
+ recv_init();
+ if (daemon_init_colors_or_die(OPT_UINT32_VAL(COLOR), COLOR_AUTO,
+ COLOR_NO, OPT_GIVEN(LOGFILE))) {
+ for (i = 0; i < OPT_GIVEN(LOG_COLOR); i++)
+ daemon_set_log_color_or_die(lls_string_val(i,
+ OPT_RESULT(LOG_COLOR)));
+ }
daemon_set_flag(DF_LOG_TIME);
daemon_set_flag(DF_LOG_HOSTNAME);
daemon_set_flag(DF_LOG_LL);
- if (conf.log_timing_given)
+ if (OPT_GIVEN(LOG_TIMING))
daemon_set_flag(DF_LOG_TIMING);
- if (conf.logfile_given) {
- daemon_set_logfile(conf.logfile_arg);
+ if (OPT_GIVEN(LOGFILE)) {
+ daemon_set_logfile(OPT_STRING_VAL(LOGFILE));
daemon_open_log_or_die();
}
ret = parse_stream_args();
init_status_task(stat_task);
init_command_task(cmd_task);
- if (conf.daemon_given)
+ if (OPT_GIVEN(DAEMON))
daemonize(false /* parent exits immediately */);
signal_task->task = task_register(&(struct task_info) {
sched_shutdown(&sched);
signal_shutdown(signal_task);
+out:
+ lls_free_parse_result(lpr, CMD_PTR);
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
if (ret < 0)
PARA_EMERG_LOG("%s\n", para_strerror(-ret));
return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
+++ /dev/null
-BN: audiod
-SF: audiod_command.c
-SN: list of audiod commands
----
-N: cycle
-D: switch to next mode
-U: cycle
-H: on -> standby -> off -> on
----
-N: grab
-D: grab the audio stream
-L:
-U: -- grab [-m[{s|p|a}]] [-p=PARENT] [-n=NAME] [-o]
-H:
-H: grab ('splice') the audio stream at any position in the buffer
-H: tree and send that data back to the client.
-H:
-H: Options:
-H:
-H: -m Change grab mode. Defaults to sloppy grab if not given.
-H:
-H: -ms: sloppy grab
-H: -mp: pedantic grab
-H: -ma: aggressive grab
-H:
-H: The various grab modes only differ in what happens if an attempt to
-H: write the grabbed audio data would block. Sloppy mode ignores the
-H: write, pedantic mode aborts and aggressive mode tries to write anyway.
-H:
-H: -p Grab output of node PARENT of the buffer tree.
-H:
-H: -n Name of the new buffer tree node. Defaults to 'grab'.
-H:
-H: -o One-shot mode: Stop grabbing if audio file changes.
----
-N: help
-D: display command list or help for given command
-U: help [command]
-H: When I was younger, so much younger than today, I never needed anybody's help
-H: in any way. But now these days are gone, I'm not so self assured. Now I find
-H: I've changed my mind and opened up the doors.
-H: -- Beatles: Help
----
-N: off
-D: deactivate para_audiod
-U: off
-H: Close connection to para_server and stop all decoders.
----
-N: on
-D: activate para_audiod
-U: on
-H: Establish connection to para_server, retrieve para_server's current status. If
-H: playing, start corresponding decoder. Otherwise stop all decoders.
----
-N: sb
-D: enter standby mode
-U: sb
-H: Stop all decoders but leave connection to para_server open.
----
-N: stat
-D: print status information
-U: stat [-p] [item1 ...]
-H: Dump given status items (all if none given) to stdout. If -p is given, use
-H: parser-friendly mode.
----
-N: tasks
-D: list current tasks
-U: tasks
-H: Print the list of task ids together with the status of each task.
----
-N: term
-D: terminate audiod
-U: term
-H: Stop all decoders, shut down connection to para_server and exit.
----
-N: version
-D: print the version of para_audiod
-U: version [-v]
-H: If the -v option is given, a more detailed version text is printed.
/** array of audio format names supported by para_audiod */
extern const char *audio_formats[];
-/**
- * the possible modes of operation
- *
- * - off: disconnect from para_server
- * - on: receive status information from para_server and play the audio stream
- * - sb: only receive status information but not the audio stream
- */
-enum audiod_status_info {AUDIOD_OFF, AUDIOD_ON, AUDIOD_STANDBY};
-
extern int audiod_status;
/* defined in audiod.c */
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
+#include "audiod.lsg.h"
#include "para.h"
-#include "audiod.cmdline.h"
-#include "audiod.command_list.h"
+#include "audiod_cmd.lsg.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "grab_client.h"
extern struct sched sched;
extern char *stat_item_values[NUM_STAT_ITEMS];
-typedef int audiod_command_handler_t(int, int, char **);
-static audiod_command_handler_t AUDIOD_COMMAND_HANDLERS;
-
-/* Defines one command of para_audiod. */
-struct audiod_command {
- const char *name;
- /* Pointer to the function that handles the command. */
- /*
- * Command handlers must never never close their file descriptor. A
- * positive return value tells audiod that the status items have
- * changed. In this case audiod will send an updated version of all
- * status items to to each connected stat client.
- */
- audiod_command_handler_t *handler;
- /* One-line description. */
- const char *description;
- /* Summary of the command line options. */
- const char *usage;
- /* The long help text. */
- const char *help;
-};
+/** The maximal number of simultaneous connections. */
+#define MAX_STAT_CLIENTS 50
-static struct audiod_command audiod_cmds[] = {DEFINE_AUDIOD_CMD_ARRAY};
+/** Pointer to a command handler function. */
+typedef int (*audiod_cmd_handler_t)(int, struct lls_parse_result *);
-/** Iterate over the array of all audiod commands. */
-#define FOR_EACH_COMMAND(c) for (c = 0; audiod_cmds[c].name; c++)
+/** The lopsub user_data pointer. Only the command handler at the moment. */
+struct audiod_command_info {
+ audiod_cmd_handler_t handler; /**< Implementation of the command. */
+};
-/** The maximal number of simultaneous connections. */
-#define MAX_STAT_CLIENTS 50
+/** Define the user_data pointer as expected by lopsub. */
+#define EXPORT_AUDIOD_CMD_HANDLER(_cmd) \
+ /** Implementation of _cmd. */ \
+ const struct audiod_command_info lsg_audiod_cmd_com_ ## _cmd ## _user_data = { \
+ .handler = com_ ## _cmd \
+ };
/** Flags used for the stat command of para_audiod. */
enum stat_client_flags {
char *buf = para_strdup(""), *tmp = NULL;
int i;
ssize_t ret;
+ const struct lls_command *cmd;
- FOR_EACH_COMMAND(i) {
- tmp = make_message("%s%s\t%s\n", buf, audiod_cmds[i].name,
- audiod_cmds[i].description);
+ for (i = 1; (cmd = lls_cmd(i, audiod_cmd_suite)); i++) {
+ tmp = make_message("%s%s\t%s\n", buf, lls_command_name(cmd),
+ lls_purpose(cmd));
free(buf);
buf = tmp;
}
return ret;
}
-static int com_help(int fd, int argc, char **argv)
+static int com_help(int fd, struct lls_parse_result *lpr)
{
- int i, ret;
- char *buf;
-
- if (argc < 2) {
- ret = dump_commands(fd);
- goto out;
+ int ret;
+ char *buf, *errctx;
+ const struct lls_command *cmd;
+
+ ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+ if (ret < 0) {
+ if (errctx) {
+ buf = make_message("%s\n", errctx);
+ client_write(fd, buf);
+ free(buf);
+ free(errctx);
+ }
+ return ret;
}
- FOR_EACH_COMMAND(i) {
- if (strcmp(audiod_cmds[i].name, argv[1]))
- continue;
- buf = make_message(
- "NAME\n\t%s -- %s\n"
- "SYNOPSIS\n\tpara_audioc %s\n"
- "DESCRIPTION\n%s\n",
- argv[1],
- audiod_cmds[i].description,
- audiod_cmds[i].usage,
- audiod_cmds[i].help
- );
- ret = client_write(fd, buf);
+ if (lls_num_inputs(lpr) == 0)
+ return dump_commands(fd);
+ ret = lls(lls_lookup_subcmd(lls_input(0, lpr), audiod_cmd_suite,
+ &errctx));
+ if (ret < 0) {
+ buf = make_message("%s: %s\nAvailable commands:\n", errctx?
+ errctx : lls_input(0, lpr), para_strerror(-ret));
+ if (client_write(fd, buf) >= 0)
+ dump_commands(fd);
+ free(errctx);
free(buf);
goto out;
}
- ret = client_write(fd, "No such command. Available commands:\n");
- if (ret > 0)
- ret = dump_commands(fd);
+ cmd = lls_cmd(ret, audiod_cmd_suite);
+ buf = lls_long_help(cmd);
+ assert(buf);
+ ret = client_write(fd, buf);
+ free(buf);
out:
return ret < 0? ret : 0;
}
+EXPORT_AUDIOD_CMD_HANDLER(help)
-static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_tasks(int fd, __a_unused struct lls_parse_result *lpr)
{
+ int ret;
char *tl = get_task_list(&sched);
- int ret = 1;
- if (tl)
- ret = client_write(fd, tl);
+ if (!tl) /* no tasks registered yet */
+ return 0;
+ ret = client_write(fd, tl);
free(tl);
- return ret < 0? ret : 0;
+ return ret;
}
+EXPORT_AUDIOD_CMD_HANDLER(tasks)
-static int com_stat(int fd, int argc, char **argv)
+static int com_stat(int fd, struct lls_parse_result *lpr)
{
int i, ret, parser_friendly = 0;
uint64_t mask = 0;
const uint64_t one = 1;
struct para_buffer b = {.flags = 0};
+ const struct lls_opt_result *r;
+ unsigned num_inputs;
ret = mark_fd_nonblocking(fd);
if (ret < 0)
return ret;
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strncmp(arg, "-p", 2)) {
- parser_friendly = 1;
- b.flags = PBF_SIZE_PREFIX;
- }
+ r = lls_opt_result(LSG_AUDIOD_CMD_STAT_OPT_PARSER_FRIENDLY, lpr);
+ if (lls_opt_given(r) > 0) {
+ parser_friendly = 1;
+ b.flags = PBF_SIZE_PREFIX;
}
- if (i >= argc)
+ num_inputs = lls_num_inputs(lpr);
+ if (num_inputs == 0)
mask--; /* set all bits */
- for (; i < argc; i++) {
- ret = stat_item_valid(argv[i]);
+ for (i = 0; i < num_inputs; i++) {
+ ret = stat_item_valid(lls_input(i, lpr));
if (ret < 0)
return ret;
mask |= (one << ret);
free(b.buf);
return ret < 0? ret : 0;
}
+EXPORT_AUDIOD_CMD_HANDLER(stat)
-static int com_grab(int fd, int argc, char **argv)
+static int com_grab(int fd, struct lls_parse_result *lpr)
{
- int ret = grab_client_new(fd, argc, argv, &sched);
+ int ret = grab_client_new(fd, lpr, &sched);
return ret < 0? ret : 0;
}
+EXPORT_AUDIOD_CMD_HANDLER(grab)
-static int com_term(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_term(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
{
return -E_AUDIOD_TERM;
}
+EXPORT_AUDIOD_CMD_HANDLER(term)
-static int com_on(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_on(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
{
audiod_status = AUDIOD_ON;
return 1;
}
+EXPORT_AUDIOD_CMD_HANDLER(on)
-static int com_off(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_off(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
{
audiod_status = AUDIOD_OFF;
return 1;
}
+EXPORT_AUDIOD_CMD_HANDLER(off)
-static int com_sb(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_sb(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
{
audiod_status = AUDIOD_STANDBY;
return 1;
}
+EXPORT_AUDIOD_CMD_HANDLER(sb)
-static int com_cycle(__a_unused int fd, int argc, char **argv)
+static int com_cycle(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
{
switch (audiod_status) {
- case AUDIOD_ON:
- return com_sb(fd, argc, argv);
- break;
- case AUDIOD_OFF:
- return com_on(fd, argc, argv);
- break;
- case AUDIOD_STANDBY:
- return com_off(fd, argc, argv);
- break;
+ case AUDIOD_ON: audiod_status = AUDIOD_STANDBY; break;
+ case AUDIOD_OFF: audiod_status = AUDIOD_ON; break;
+ case AUDIOD_STANDBY: audiod_status = AUDIOD_OFF; break;
}
return 1;
}
+EXPORT_AUDIOD_CMD_HANDLER(cycle)
-static int com_version(int fd, int argc, char **argv)
+static int com_version(int fd, struct lls_parse_result *lpr)
{
int ret;
char *msg;
+ const struct lls_opt_result *r_v;
- if (argc > 1 && strcmp(argv[1], "-v") == 0)
+ r_v = lls_opt_result(LSG_AUDIOD_CMD_VERSION_OPT_VERBOSE, lpr);
+ if (lls_opt_given(r_v))
msg = make_message("%s", version_text("audiod"));
else
msg = make_message("%s\n", version_single_line("audiod"));
free(msg);
return ret < 0? ret : 0;
}
+EXPORT_AUDIOD_CMD_HANDLER(version)
/**
* Handle arriving connections on the local socket.
* */
int handle_connect(int accept_fd, fd_set *rfds)
{
- int i, argc, ret, clifd;
+ int argc, ret, clifd;
char buf[MAXLINE], **argv = NULL;
struct sockaddr_un unix_addr;
uid_t uid;
+ const struct lls_command *cmd;
+ struct lls_parse_result *lpr;
+ char *errctx = NULL;
+ const struct audiod_command_info *aci;
ret = para_accept(accept_fd, rfds, &unix_addr, sizeof(struct sockaddr_un), &clifd);
if (ret <= 0)
if (ret <= 0)
goto out;
argc = ret;
- //PARA_INFO_LOG("argv[0]: %s, argc = %d\n", argv[0], argc);
- FOR_EACH_COMMAND(i) {
- if (strcmp(audiod_cmds[i].name, argv[0]))
- continue;
- ret = audiod_cmds[i].handler(clifd, argc, argv);
+ ret = lls(lls_lookup_subcmd(argv[0], audiod_cmd_suite, &errctx));
+ if (ret < 0)
goto out;
- }
- ret = -E_INVALID_AUDIOD_CMD;
+ cmd = lls_cmd(ret, audiod_cmd_suite);
+ ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+ if (ret < 0)
+ goto out;
+ aci = lls_user_data(cmd);
+ ret = aci->handler(clifd, lpr);
+ lls_free_parse_result(lpr, cmd);
out:
free_argv(argv);
if (ret < 0 && ret != -E_CLIENT_WRITE) {
- char *tmp = make_message("%s\n", para_strerror(-ret));
+ char *tmp;
+ if (errctx) {
+ tmp = make_message("%s\n", errctx);
+ free(errctx);
+ client_write(clifd, tmp);
+ free(tmp);
+ }
+ tmp = make_message("%s\n", para_strerror(-ret));
client_write(clifd, tmp);
free(tmp);
}
echo configuring...
./configure $@ > /dev/null
echo compiling...
-make clean2 > /dev/null 2>&1
+make clean > /dev/null 2>&1
make -j $n > /dev/null
#include "error.h"
#include "string.h"
#include "wma.h"
+#include "portable_io.h"
#include "bitstream.h"
static inline uint32_t get_data(const void *table, int i, int size)
static inline uint32_t show_bits(struct getbit_context *gbc, int num)
{
int idx = gbc->index;
- const uint8_t *p = gbc->buffer + (idx >> 3);
- uint32_t x = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ const char *p = (const char *)gbc->buffer + (idx >> 3);
+ uint32_t x = read_u32_be(p);
return (x << (idx & 7)) >> (32 - num);
}
#include <regex.h>
#include <fnmatch.h>
#include <osl.h>
+#include <lopsub.h>
+#include "server_cmd.lsg.h"
#include "para.h"
#include "error.h"
#include "crypt.h"
INIT_BLOB_TABLE(playlists);
/** \endcond blob_table */
-/** Flags that may be passed to the \p ls functions of each blob type. */
-enum blob_ls_flags {
- /** List both id and name. */
- BLOB_LS_FLAG_LONG = 1,
- /** Reverse sort order. */
- BLOB_LS_FLAG_REVERSE = 2,
- /** Sort by id instead of name. */
- BLOB_LS_FLAG_SORT_BY_ID = 4,
-};
-
static int print_blob(struct osl_table *table, struct osl_row *row,
const char *name, void *data)
{
struct afs_callback_arg *aca = data;
- uint32_t flags = *(uint32_t *)aca->query.data;
+ bool l_given = SERVER_CMD_OPT_GIVEN(LSMOOD, LONG, aca->lpr);
struct osl_object obj;
uint32_t id;
int ret;
- if (!(flags & BLOB_LS_FLAG_LONG)) {
+ if (!l_given) {
para_printf(&aca->pbout, "%s\n", name);
return 0;
}
return 1;
}
-static int com_lsblob_callback(struct osl_table *table,
- struct afs_callback_arg *aca)
+static int com_lsblob_callback(const struct lls_command * const cmd,
+ struct osl_table *table, struct afs_callback_arg *aca)
{
- uint32_t flags = *(uint32_t *)aca->query.data;
+ bool i_given, r_given;
struct pattern_match_data pmd = {
.table = table,
- .patterns = {.data = (char *)aca->query.data + sizeof(uint32_t),
- .size = aca->query.size - sizeof(uint32_t)},
.pm_flags = PM_NO_PATTERN_MATCHES_EVERYTHING | PM_SKIP_EMPTY_NAME,
.match_col_num = BLOBCOL_NAME,
.data = aca,
.action = print_blob,
};
int ret;
+ ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+ pmd.lpr = aca->lpr;
+ assert(ret >= 0);
+ i_given = SERVER_CMD_OPT_GIVEN(LSMOOD, ID_SORT, aca->lpr);
+ r_given = SERVER_CMD_OPT_GIVEN(LSMOOD, REVERSE, aca->lpr);
- if (flags & BLOB_LS_FLAG_REVERSE)
+ if (r_given)
pmd.pm_flags |= PM_REVERSE_LOOP;
- if (!(flags & BLOB_LS_FLAG_SORT_BY_ID))
- pmd.loop_col_num = BLOBCOL_NAME;
- else
+ if (i_given)
pmd.loop_col_num = BLOBCOL_ID;
+ else
+ pmd.loop_col_num = BLOBCOL_NAME;
ret = for_each_matching_row(&pmd);
if (ret < 0)
goto out;
- if (pmd.num_matches == 0 && pmd.patterns.size > 0)
+ if (pmd.num_matches == 0 && lls_num_inputs(aca->lpr) > 0)
ret = -E_NO_MATCH;
out:
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-static int com_lsblob(afs_callback *f, struct command_context *cc)
+static int com_lsblob(afs_callback *f, const struct lls_command * const cmd,
+ struct command_context *cc, struct lls_parse_result *lpr)
{
- uint32_t flags = 0;
- struct osl_object options = {.data = &flags, .size = sizeof(flags)};
- int i;
-
- for (i = 1; i < cc->argc; i++) {
- const char *arg = cc->argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strcmp(arg, "-l")) {
- flags |= BLOB_LS_FLAG_LONG;
- continue;
- }
- if (!strcmp(arg, "-i")) {
- flags |= BLOB_LS_FLAG_SORT_BY_ID;
- continue;
- }
- if (!strcmp(arg, "-r")) {
- flags |= BLOB_LS_FLAG_REVERSE;
- continue;
- }
- break;
- }
-// if (argc > i)
-// return -E_BLOB_SYNTAX;
- return send_option_arg_callback_request(&options, cc->argc - i,
- cc->argv + i, f, afs_cb_result_handler, cc);
+ return send_lls_callback_request(f, cmd, lpr, cc);
}
static int cat_blob(struct osl_table *table, struct osl_row *row,
return (ret < 0)? ret : ret2;
}
-static int com_catblob_callback(struct osl_table *table,
- struct afs_callback_arg *aca)
+static int com_catblob_callback(const struct lls_command * const cmd,
+ struct osl_table *table, struct afs_callback_arg *aca)
{
int ret;
struct pattern_match_data pmd = {
.table = table,
- .patterns = aca->query,
.loop_col_num = BLOBCOL_NAME,
.match_col_num = BLOBCOL_NAME,
.pm_flags = PM_SKIP_EMPTY_NAME,
.data = &aca->fd,
.action = cat_blob
};
+ ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+ assert(ret >= 0);
+ pmd.lpr = aca->lpr;
ret = for_each_matching_row(&pmd);
if (ret < 0)
- return ret;
+ goto out;
if (pmd.num_matches == 0)
ret = -E_NO_MATCH;
+out:
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-static int com_catblob(afs_callback *f, struct command_context *cc)
+static int com_catblob(afs_callback *f, const struct lls_command * const cmd,
+ struct command_context *cc, struct lls_parse_result *lpr)
{
- if (cc->argc < 2)
- return -E_BLOB_SYNTAX;
- return send_standard_callback_request(cc->argc - 1, cc->argv + 1, f,
- afs_cb_result_handler, cc);
+ char *errctx;
+ int ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ return send_lls_callback_request(f, cmd, lpr, cc);
}
static int remove_blob(struct osl_table *table, struct osl_row *row,
return 1;
}
-static int com_rmblob_callback(struct osl_table *table,
- struct afs_callback_arg *aca)
+static int com_rmblob_callback(const struct lls_command * const cmd,
+ struct osl_table *table, struct afs_callback_arg *aca)
{
int ret;
struct pattern_match_data pmd = {
.table = table,
- .patterns = aca->query,
.loop_col_num = BLOBCOL_NAME,
.match_col_num = BLOBCOL_NAME,
.pm_flags = PM_SKIP_EMPTY_NAME,
.data = aca,
.action = remove_blob
};
+ ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+ assert(ret >= 0);
+ pmd.lpr = aca->lpr;
ret = for_each_matching_row(&pmd);
if (ret < 0)
goto out;
ret = afs_event(BLOB_REMOVE, NULL, table);
}
out:
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-static int com_rmblob(afs_callback *f, struct command_context *cc)
+static int com_rmblob(afs_callback *f, const struct lls_command * const cmd,
+ struct command_context *cc, struct lls_parse_result *lpr)
{
- if (cc->argc < 2)
- return -E_MOOD_SYNTAX;
- return send_option_arg_callback_request(NULL, cc->argc - 1, cc->argv + 1, f,
- afs_cb_result_handler, cc);
+ char *errctx;
+ int ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ return send_lls_callback_request(f, cmd, lpr, cc);
}
-static int com_addblob_callback(struct osl_table *table,
- struct afs_callback_arg *aca)
+static int com_addblob_callback(__a_unused const struct lls_command * const cmd,
+ struct osl_table *table, struct afs_callback_arg *aca)
{
struct osl_object objs[NUM_BLOB_COLUMNS];
char *name = aca->query.data;
* the name of the blob to create. The combined buffer is made available to the
* afs process via the callback method.
*/
-static int stdin_command(struct command_context *cc, struct osl_object *arg_obj,
- afs_callback *f)
+static int stdin_command(struct command_context *cc,
+ struct lls_parse_result *lpr, afs_callback *f)
{
struct osl_object query, stdin_obj;
int ret;
+ size_t len = strlen(lls_input(0, lpr));
ret = send_sb(&cc->scc, NULL, 0, SBD_AWAITING_DATA, false);
if (ret < 0)
ret = fd2buf(&cc->scc, &stdin_obj);
if (ret < 0)
return ret;
- query.size = arg_obj->size + stdin_obj.size;
+ query.size = len + 1 + stdin_obj.size;
query.data = para_malloc(query.size);
- memcpy(query.data, arg_obj->data, arg_obj->size);
+ memcpy(query.data, lls_input(0, lpr), len + 1);
if (stdin_obj.size > 0)
- memcpy((char *)query.data + arg_obj->size, stdin_obj.data,
+ memcpy((char *)query.data + len + 1, stdin_obj.data,
stdin_obj.size);
free(stdin_obj.data);
ret = send_callback_request(f, &query, afs_cb_result_handler, cc);
return ret;
}
-static int com_addblob(afs_callback *f, struct command_context *cc)
+static int com_addblob(afs_callback *f, __a_unused const struct lls_command * const cmd,
+ struct command_context *cc, struct lls_parse_result *lpr)
{
- struct osl_object arg_obj;
+ char *errctx;
+ int ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
- if (cc->argc != 2)
- return -E_BLOB_SYNTAX;
- if (!*cc->argv[1]) /* empty name is reserved for the dummy row */
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ if (!lls_input(0, lpr)[0]) /* empty name is reserved for the dummy row */
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);
+ return stdin_command(cc, lpr, f);
}
-static int com_mvblob_callback(struct osl_table *table,
- struct afs_callback_arg *aca)
+static int com_mvblob_callback(const struct lls_command * const cmd,
+ struct osl_table *table, struct afs_callback_arg *aca)
{
- char *src = (char *)aca->query.data;
- struct osl_object obj = {.data = src, .size = strlen(src) + 1};
- char *dest = src + obj.size;
+ const char *src, *dest;
+ struct osl_object obj;
struct osl_row *row;
- int ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
+ int ret;
+
+ ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+ assert(ret >= 0);
+ src = lls_input(0, aca->lpr);
+ dest = lls_input(1, aca->lpr);
+ obj.data = (char *)src;
+ obj.size = strlen(src) + 1;
+ ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
if (ret < 0) {
para_printf(&aca->pbout, "cannot find source blob %s\n", src);
goto out;
}
- obj.data = dest;
+ obj.data = (char *)dest;
obj.size = strlen(dest) + 1;
ret = osl(osl_update_object(table, row, BLOBCOL_NAME, &obj));
if (ret < 0) {
}
ret = afs_event(BLOB_RENAME, NULL, table);
out:
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-static int com_mvblob(afs_callback *f, struct command_context *cc)
+static int com_mvblob(afs_callback *f, const struct lls_command * const cmd,
+ struct command_context *cc, struct lls_parse_result *lpr)
{
- if (cc->argc != 3)
- return -E_MOOD_SYNTAX;
- return send_option_arg_callback_request(NULL, cc->argc - 1,
- cc->argv + 1, f, afs_cb_result_handler, cc);
+ char *errctx;
+ int ret = lls(lls_check_arg_count(lpr, 2, 2, &errctx));
+
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ return send_lls_callback_request(f, cmd, lpr, cc);
}
-#define DEFINE_BLOB_COMMAND(cmd_name, table_name, cmd_prefix) \
- static int com_ ## cmd_name ## cmd_prefix ## _callback(struct afs_callback_arg *aca) \
+#define DEFINE_BLOB_COMMAND(cmd_name, c_cmd_name, table_name, short_name, c_short_name) \
+ static int com_ ## cmd_name ## short_name ## _callback(struct afs_callback_arg *aca) \
{ \
- return com_ ## cmd_name ## blob_callback(table_name ## _table, aca); \
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(c_cmd_name ## c_short_name); \
+ return com_ ## cmd_name ## blob_callback(cmd, table_name ## _table, aca); \
} \
- int com_ ## cmd_name ## cmd_prefix(struct command_context *cc) \
+ static int com_ ## cmd_name ## short_name(struct command_context *cc, struct lls_parse_result *lpr) \
{ \
- return com_ ## cmd_name ## blob(com_ ## cmd_name ## cmd_prefix ## _callback, cc); \
- }
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(c_cmd_name ## c_short_name); \
+ return com_ ## cmd_name ## blob(com_ ## cmd_name ## short_name ## _callback, cmd, cc, lpr); \
+ } \
+ EXPORT_SERVER_CMD_HANDLER(cmd_name ## short_name);
static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
char **name)
/** Define all functions for this blob type. */
-#define DEFINE_BLOB_FUNCTIONS(table_name, cmd_prefix) \
+#define DEFINE_BLOB_FUNCTIONS(table_name, short_name, c_short_name) \
DEFINE_BLOB_OPEN(table_name) \
DEFINE_BLOB_CLOSE(table_name) \
DEFINE_BLOB_CREATE(table_name) \
DEFINE_BLOB_INIT(table_name) \
- DEFINE_BLOB_COMMAND(ls, table_name, cmd_prefix) \
- DEFINE_BLOB_COMMAND(cat, table_name, cmd_prefix) \
- DEFINE_BLOB_COMMAND(add, table_name, cmd_prefix) \
- DEFINE_BLOB_COMMAND(rm, table_name, cmd_prefix) \
- DEFINE_BLOB_COMMAND(mv, table_name, cmd_prefix) \
- DEFINE_GET_NAME_BY_ID(table_name, cmd_prefix); \
- DEFINE_GET_DEF_BY_ID(table_name, cmd_prefix); \
- DEFINE_GET_DEF_BY_NAME(table_name, cmd_prefix); \
- DEFINE_GET_NAME_AND_DEF_BY_ROW(table_name, cmd_prefix); \
+ DEFINE_BLOB_COMMAND(ls, LS, table_name, short_name, c_short_name) \
+ DEFINE_BLOB_COMMAND(cat, CAT, table_name, short_name, c_short_name) \
+ DEFINE_BLOB_COMMAND(add, ADD, table_name, short_name, c_short_name) \
+ DEFINE_BLOB_COMMAND(rm, RM, table_name, short_name, c_short_name) \
+ DEFINE_BLOB_COMMAND(mv, MV, table_name, short_name, c_short_name) \
+ DEFINE_GET_NAME_BY_ID(table_name, short_name); \
+ DEFINE_GET_DEF_BY_ID(table_name, short_name); \
+ DEFINE_GET_DEF_BY_NAME(table_name, short_name); \
+ DEFINE_GET_NAME_AND_DEF_BY_ROW(table_name, short_name); \
/* doxygen isn't smart enough to recognize these */
/** \cond blob_function */
-DEFINE_BLOB_FUNCTIONS(lyrics, lyr);
-DEFINE_BLOB_FUNCTIONS(images, img);
-DEFINE_BLOB_FUNCTIONS(moods, mood);
-DEFINE_BLOB_FUNCTIONS(playlists, pl);
+DEFINE_BLOB_FUNCTIONS(lyrics, lyr, LYR);
+DEFINE_BLOB_FUNCTIONS(images, img, IMG);
+DEFINE_BLOB_FUNCTIONS(moods, mood, MOOD);
+DEFINE_BLOB_FUNCTIONS(playlists, pl, PL);
/** \endcond blob_function */
int sample_format_given;
};
-/**
- * Copy the wav parameters.
- *
- * \param dst Usually a pointer to struct wav_params.
- * \param src Usually a pointer to some args_info struct.
- *
- * This can not be implemented as a function since the type of the structure
- * pointed to by \a src depends on the application.
- */
-#define COPY_WAV_PARMS(dst, src) \
- (dst)->channels_arg = (src)->channels_arg; \
- (dst)->channels_given = (src)->channels_given; \
- (dst)->sample_rate_arg = (src)->sample_rate_arg; \
- (dst)->sample_rate_given = (src)->sample_rate_given; \
- (dst)->sample_format_arg = (src)->sample_format_arg; \
- (dst)->sample_format_given = (src)->sample_format_given;
+#define LLS_COPY_WAV_PARMS(_dst, _pfx, _lpr) \
+ (_dst)->channels_given = lls_opt_given(lls_opt_result( \
+ _pfx ## _OPT_CHANNELS, (_lpr))); \
+ (_dst)->sample_rate_given = lls_opt_given(lls_opt_result( \
+ _pfx ## _OPT_SAMPLE_RATE, (_lpr))); \
+ (_dst)->sample_format_given = lls_opt_given(lls_opt_result( \
+ _pfx ## _OPT_SAMPLE_FORMAT, (_lpr))); \
+ (_dst)->channels_arg = lls_uint32_val(0, lls_opt_result( \
+ _pfx ## _OPT_CHANNELS, (_lpr))); \
+ (_dst)->sample_rate_arg = lls_uint32_val(0, lls_opt_result( \
+ _pfx ## _OPT_SAMPLE_RATE, (_lpr))); \
+ (_dst)->sample_format_arg = lls_uint32_val(0, lls_opt_result( \
+ _pfx ## _OPT_SAMPLE_FORMAT, (_lpr)));
struct check_wav_context *check_wav_init(struct btr_node *parent,
struct btr_node *child, struct wav_params *params,
#include <regex.h>
#include <signal.h>
+#include <lopsub.h>
+#include "client.lsg.h"
#include "para.h"
#include "list.h"
#include "sched.h"
#include "crypt.h"
-#include "client.cmdline.h"
#include "string.h"
#include "stdin.h"
#include "stdout.h"
#ifdef HAVE_READLINE
#include "interactive.h"
-#include "server.completion.h"
-#include "afs.completion.h"
+#include "server_cmd.lsg.h"
struct exec_task {
struct task *task;
return 0;
}
-static int make_client_argv(const char *line)
+/* Called from the line handler and the completers. This overwrites ct->lpr. */
+static int create_merged_lpr(const char *line)
{
- int ret;
+ const struct lls_command *cmd = CLIENT_CMD_PTR;
+ int argc, ret;
+ char *cmdline, **argv, *errctx;
+ struct lls_parse_result *argv_lpr;
+ static struct lls_parse_result *orig_lpr;
- free_argv(ct->conf.inputs);
- ret = create_argv(line, " ", &ct->conf.inputs);
- if (ret >= 0)
- ct->conf.inputs_num = ret;
+ if (!orig_lpr)
+ orig_lpr = ct->lpr;
+ ct->lpr = NULL;
+ cmdline = make_message("-- %s", line);
+ ret = create_shifted_argv(cmdline, " ", &argv);
+ free(cmdline);
+ if (ret < 0)
+ return ret;
+ argc = ret;
+ if (argc == 2) { /* no words (only blanks in line) */
+ free_argv(argv);
+ return 0;
+ }
+ argv[0] = para_strdup("--");
+ /*
+ * The original lpr for the interactive session has no non-option
+ * arguments. We create a fresh lpr from the words in "line" and merge
+ * it with the orignal lpr.
+ */
+ ret = lls(lls_parse(argc, argv, cmd, &argv_lpr, &errctx));
+ free_argv(argv);
+ if (ret < 0)
+ goto fail;
+ ret = lls(lls_merge(orig_lpr, argv_lpr, cmd, &ct->lpr, &errctx));
+ lls_free_parse_result(argv_lpr, cmd);
+ if (ret < 0)
+ goto fail;
+ return 1;
+fail:
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ free(errctx);
+ assert(ret < 0);
return ret;
}
+/* called from completers */
static int execute_client_command(const char *cmd, char **result)
{
int ret;
.result_buf = para_strdup(""),
.result_size = 1,
};
+ struct lls_parse_result *old_lpr = ct->lpr;
+
*result = NULL;
- ret = make_client_argv(cmd);
- if (ret < 0)
+ ret = create_merged_lpr(cmd);
+ if (ret <= 0)
goto out;
exec_task.btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.name = "exec_collect"));
goto out;
schedule(&command_sched);
sched_shutdown(&command_sched);
+ lls_free_parse_result(ct->lpr, CLIENT_CMD_PTR);
+ ct->lpr = old_lpr;
*result = exec_task.result_buf;
btr_remove_node(&exec_task.btrn);
ret = 1;
struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
- char *opts[] = {"-i", "-l", "-r", NULL};
+ char *opts[] = {LSG_SERVER_CMD_LSIMG_OPTS, NULL};
if (ci->word[0] == '-')
return i9e_complete_option(opts, ci, cr);
result->matches = i9e_complete_commands(ci->word, completers);
}
-static void version_completer(struct i9e_completion_info *ci,
+static void stat_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
- char *opts[] = {"-v", NULL};
+ char *opts[] = {LSG_SERVER_CMD_STAT_OPTS, NULL};
i9e_complete_option(opts, ci, cr);
}
-static void stat_completer(struct i9e_completion_info *ci,
+static void version_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
- char *opts[] = {"-n=", "-p", NULL};
- //PARA_CRIT_LOG("word: %s\n", ci->word);
+ char *opts[] = {LSG_SERVER_CMD_VERSION_OPTS, NULL};
i9e_complete_option(opts, ci, cr);
}
static void add_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
- char *opts[] = {"-a", "-l", "-f", "-v", "--", NULL};
+ char *opts[] = {LSG_SERVER_CMD_ADD_OPTS, NULL};
if (ci->word[0] == '-')
i9e_complete_option(opts, ci, cr);
static void ls_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
- char *opts[] = {
- "--", "-l", "-l=s", "-l=l", "-l=v", "-l=p", "-l=m", "-l=c",
- "-p", "-a", "-r", "-d", "-s=p", "-s=l", "-s=s", "-s=n", "-s=f",
- "-s=c", "-s=i", "-s=y", "-s=b", "-s=d", "-s=a", "-F", "-b", NULL
- };
+ char *opts[] = {LSG_SERVER_CMD_LS_OPTS, NULL};
if (ci->word[0] == '-')
i9e_complete_option(opts, ci, cr);
cr->filename_completion_desired = true;
static void lsatt_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
- char *opts[] = {"-i", "-l", "-r", NULL};
+ char *opts[] = {LSG_SERVER_CMD_LSATT_OPTS, NULL};
i9e_complete_option(opts, ci, cr);
}
static void check_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
- char *opts[] = {"-a", "-m", "-p", NULL};
+ char *opts[] = {LSG_SERVER_CMD_CHECK_OPTS, NULL};
i9e_complete_option(opts, ci, cr);
}
static void rm_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
- char *opts[] = {"-v", "-f", "-p", NULL};
+ char *opts[] = {LSG_SERVER_CMD_RM_OPTS, NULL};
if (ci->word[0] == '-') {
i9e_complete_option(opts, ci, cr);
static void touch_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
- char *opts[] = {"-n=", "-l=", "-y=", "-i=", "-a=", "-v", "-p", NULL};
+ char *opts[] = {LSG_SERVER_CMD_TOUCH_OPTS, NULL};
if (ci->word[0] == '-')
i9e_complete_option(opts, ci, cr);
static void cpsi_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
- char *opts[] = {"-a", "-y", "-i", "-l", "-n", "-v", NULL};
+ char *opts[] = {LSG_SERVER_CMD_CPSI_OPTS, NULL};
if (ci->word[0] == '-')
i9e_complete_option(opts, ci, cr);
static int client_i9e_line_handler(char *line)
{
int ret;
+ static bool first = true;
- PARA_DEBUG_LOG("line: %s\n", line);
- ret = make_client_argv(line);
+ if (first)
+ first = false;
+ else
+ lls_free_parse_result(ct->lpr, CLIENT_CMD_PTR);
+ ret = create_merged_lpr(line);
if (ret <= 0)
return ret;
ret = client_connect(ct, &sched, NULL, NULL);
return 1;
}
+I9E_DUMMY_COMPLETER(SUPERCOMMAND_UNAVAILABLE);
static struct i9e_completer completers[] = {
- SERVER_COMPLETERS
- AFS_COMPLETERS
+#define LSG_SERVER_CMD_CMD(_name) {.name = #_name, \
+ .completer = _name ## _completer}
+ LSG_SERVER_CMD_SUBCOMMANDS
+#undef LSG_SERVER_CMD_CMD
{.name = NULL}
};
__noreturn static void interactive_session(void)
{
int ret;
- char *history_file;
struct sigaction act;
struct i9e_client_info ici = {
.fds = {0, 1, 2},
};
PARA_NOTICE_LOG("\n%s\n", version_text("client"));
- if (ct->conf.history_file_given)
- history_file = para_strdup(ct->conf.history_file_arg);
+ if (CLIENT_OPT_GIVEN(HISTORY_FILE, ct->lpr))
+ ici.history_file = para_strdup(CLIENT_OPT_STRING_VAL(
+ HISTORY_FILE, ct->lpr));
else {
char *home = para_homedir();
- history_file = make_message("%s/.paraslash/client.history",
+ ici.history_file = make_message("%s/.paraslash/client.history",
home);
free(home);
}
- ici.history_file = history_file;
-
act.sa_handler = i9e_signal_dispatch;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
__noreturn static void print_completions(void)
{
- int ret = i9e_print_completions(completers);
+ int ret;
+
+ ret = i9e_print_completions(completers);
exit(ret <= 0? EXIT_FAILURE : EXIT_SUCCESS);
}
ret = client_parse_config(argc, argv, &ct, &client_loglevel);
if (ret < 0)
goto out;
- if (ct->conf.complete_given)
+ if (CLIENT_OPT_GIVEN(COMPLETE, ct->lpr))
print_completions();
if (ret == 0)
interactive_session(); /* does not return */
struct btr_node *btrn[2];
/** The hash value of the decrypted challenge. */
unsigned char *challenge_hash;
- /** The configuration (including the command). */
- struct client_args_info conf;
+ /** The parsed command line (including the command). */
+ struct lls_parse_result *lpr;
/** The config file for client options. */
char *config_file;
/** The RSA private key. */
char **features;
};
+#define CLIENT_CMD_PTR (lls_cmd(0, client_suite))
+#define CLIENT_OPT_RESULT(_name, _lpr) \
+ (lls_opt_result(LSG_CLIENT_PARA_CLIENT_OPT_ ## _name, _lpr))
+#define CLIENT_OPT_GIVEN(_name, _lpr) \
+ (lls_opt_given(CLIENT_OPT_RESULT(_name, _lpr)))
+#define CLIENT_OPT_UINT32_VAL(_name, _lpr) \
+ (lls_uint32_val(0, CLIENT_OPT_RESULT(_name, _lpr)))
+#define CLIENT_OPT_STRING_VAL(_name, _lpr) \
+ (lls_string_val(0, CLIENT_OPT_RESULT(_name, _lpr)))
+
void client_close(struct client_task *ct);
int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
int *loglevel);
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
+#include "client.lsg.h"
#include "para.h"
#include "error.h"
#include "list.h"
#include "fd.h"
#include "sideband.h"
#include "string.h"
-#include "client.cmdline.h"
#include "client.h"
#include "buffer_tree.h"
#include "version.h"
-#include "ggo.h"
/** The size of the receiving buffer. */
#define CLIENT_BUFSIZE 4000
free(ct->user);
free(ct->config_file);
free(ct->key_file);
- client_cmdline_parser_free(&ct->conf);
+ lls_free_parse_result(ct->lpr, CLIENT_CMD_PTR);
free(ct->challenge_hash);
sb_free(ct->sbc[0]);
sb_free(ct->sbc[1]);
return ret;
}
-static bool has_feature(const char *feature, struct client_task *ct)
-{
- return find_arg(feature, ct->features) >= 0? true : false;
-}
-
static int send_sb_command(struct client_task *ct)
{
int i;
char *command, *p;
size_t len = 0;
+ unsigned num_inputs = lls_num_inputs(ct->lpr);
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;
+ for (i = 0; i < num_inputs; i++)
+ len += strlen(lls_input(i, ct->lpr)) + 1;
p = command = para_malloc(len);
- for (i = 0; i < ct->conf.inputs_num; i++) {
- strcpy(p, ct->conf.inputs[i]);
- p += strlen(ct->conf.inputs[i]) + 1;
+ for (i = 0; i < num_inputs; i++) {
+ const char *str = lls_input(i, ct->lpr);
+ strcpy(p, str);
+ p += strlen(str) + 1;
}
PARA_DEBUG_LOG("--> %s\n", command);
return send_sb(ct, 0, command, len, SBD_COMMAND, false);
case CL_RECEIVED_WELCOME: /* send auth command */
if (!FD_ISSET(ct->scc.fd, &s->wfds))
return 0;
- sprintf(buf, AUTH_REQUEST_MSG "%s sideband%s", ct->user,
- has_feature("aes_ctr128", ct)? ",aes_ctr128" : "");
+ sprintf(buf, AUTH_REQUEST_MSG "%s sideband,aes_ctr128",
+ ct->user);
PARA_INFO_LOG("--> %s\n", buf);
ret = write_buffer(ct->scc.fd, buf);
if (ret < 0)
/* decrypted challenge/session key buffer */
unsigned char crypt_buf[1024];
struct sb_buffer sbb;
- bool use_aes;
ret = recv_sb(ct, &s->rfds, &sbb);
if (ret <= 0)
goto out;
ct->challenge_hash = para_malloc(HASH_SIZE);
hash_function((char *)crypt_buf, CHALLENGE_SIZE, ct->challenge_hash);
- use_aes = has_feature("aes_ctr128", ct);
- ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN, use_aes);
+ ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
ct->scc.recv = sc_new(crypt_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
- SESSION_KEY_LEN, use_aes);
+ SESSION_KEY_LEN);
hash_to_asc(ct->challenge_hash, buf);
PARA_INFO_LOG("--> %s\n", buf);
ct->status = CL_RECEIVED_CHALLENGE;
struct btr_node *parent, struct btr_node *child)
{
int ret;
+ const char *host = CLIENT_OPT_STRING_VAL(HOSTNAME, ct->lpr);
+ uint32_t port = CLIENT_OPT_UINT32_VAL(SERVER_PORT, ct->lpr);
- PARA_NOTICE_LOG("connecting %s:%d\n", ct->conf.hostname_arg,
- ct->conf.server_port_arg);
+ PARA_NOTICE_LOG("connecting %s:%u\n", host, port);
ct->scc.fd = -1;
- ret = para_connect_simple(IPPROTO_TCP, ct->conf.hostname_arg,
- ct->conf.server_port_arg);
+ ret = para_connect_simple(IPPROTO_TCP, host, port);
if (ret < 0)
return ret;
ct->scc.fd = ret;
return ret;
}
-__noreturn static void print_help_and_die(struct client_task *ct)
+static void handle_help_flag(struct lls_parse_result *lpr)
{
- struct ggo_help h = DEFINE_GGO_HELP(client);
- bool d = ct->conf.detailed_help_given;
+ char *help;
- ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
- exit(0);
+ if (CLIENT_OPT_GIVEN(DETAILED_HELP, lpr))
+ help = lls_long_help(CLIENT_CMD_PTR);
+ else if (CLIENT_OPT_GIVEN(HELP, lpr))
+ help = lls_short_help(CLIENT_CMD_PTR);
+ else
+ return;
+ printf("%s\n", help);
+ free(help);
+ exit(EXIT_SUCCESS);
}
/**
int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
int *loglevel)
{
- char *home = para_homedir();
- int ret;
- struct client_task *ct = para_calloc(sizeof(struct client_task));
-
- *ct_ptr = ct;
- ct->scc.fd = -1;
- ret = -E_CLIENT_SYNTAX;
- if (client_cmdline_parser(argc, argv, &ct->conf))
- goto out;
- 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) :
- make_message("%s/.paraslash/client.conf", home);
- ret = file_exists(ct->config_file);
- if (!ret && ct->conf.config_file_given) {
- ret = -E_NO_CONFIG;
+ const struct lls_command *cmd = CLIENT_CMD_PTR;
+ void *map;
+ size_t sz;
+ struct lls_parse_result *lpr;
+ int ret, ll;
+ struct client_task *ct;
+ char *cf = NULL, *kf = NULL, *user, *errctx, *home = para_homedir();
+
+ ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+ if (ret < 0)
goto out;
- }
- if (ret) {
- struct client_cmdline_parser_params params = {
- .override = 0,
- .initialize = 0,
- .check_required = 0,
- .check_ambiguity = 0,
- .print_errors = 0
- };
- ret = -E_BAD_CONFIG;
- if (client_cmdline_parser_config_file(ct->config_file,
- &ct->conf, ¶ms))
+ ll = CLIENT_OPT_UINT32_VAL(LOGLEVEL, lpr);
+ version_handle_flag("client", CLIENT_OPT_GIVEN(VERSION, lpr));
+ handle_help_flag(lpr);
+
+ if (CLIENT_OPT_GIVEN(CONFIG_FILE, lpr))
+ cf = para_strdup(CLIENT_OPT_STRING_VAL(CONFIG_FILE, lpr));
+ else
+ cf = make_message("%s/.paraslash/client.conf", home);
+ ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+ if (ret < 0) {
+ if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+ goto out;
+ if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) &&
+ CLIENT_OPT_GIVEN(CONFIG_FILE, lpr))
+ goto out;
+ } else {
+ int cf_argc;
+ char **cf_argv;
+ struct lls_parse_result *cf_lpr, *merged_lpr;
+ ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx));
+ para_munmap(map, sz);
+ if (ret < 0)
+ goto out;
+ cf_argc = ret;
+ ret = lls(lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx));
+ lls_free_argv(cf_argv);
+ if (ret < 0)
goto out;
+ ret = lls(lls_merge(lpr, cf_lpr, cmd, &merged_lpr,
+ &errctx));
+ lls_free_parse_result(cf_lpr, cmd);
+ if (ret < 0)
+ goto out;
+ lls_free_parse_result(lpr, cmd);
+ lpr = merged_lpr;
}
- ct->user = ct->conf.user_given?
- para_strdup(ct->conf.user_arg) : para_logname();
+ /* success */
+ user = CLIENT_OPT_GIVEN(USER, lpr)?
+ para_strdup(CLIENT_OPT_STRING_VAL(USER, lpr)) : para_logname();
- if (ct->conf.key_file_given)
- ct->key_file = para_strdup(ct->conf.key_file_arg);
+ if (CLIENT_OPT_GIVEN(KEY_FILE, lpr))
+ kf = para_strdup(CLIENT_OPT_STRING_VAL(KEY_FILE, lpr));
else {
- ct->key_file = make_message("%s/.paraslash/key.%s",
- home, ct->user);
- if (!file_exists(ct->key_file)) {
- free(ct->key_file);
- ct->key_file = make_message("%s/.ssh/id_rsa", home);
+ kf = make_message("%s/.paraslash/key.%s", home, user);
+ if (!file_exists(kf)) {
+ free(kf);
+ kf = make_message("%s/.ssh/id_rsa", home);
}
}
-
+ PARA_INFO_LOG("user: %s\n", user);
+ PARA_INFO_LOG("config file: %s\n", cf);
+ PARA_INFO_LOG("key file: %s\n", kf);
+ PARA_INFO_LOG("loglevel: %d\n", ll);
+ ct = para_calloc(sizeof(*ct));
+ ct->scc.fd = -1;
+ ct->lpr = lpr;
+ ct->key_file = kf;
+ ct->config_file = cf;
+ ct->user = user;
+ *ct_ptr = ct;
if (loglevel)
- *loglevel = get_loglevel_by_name(ct->conf.loglevel_arg);
- PARA_INFO_LOG("loglevel: %s\n", ct->conf.loglevel_arg);
- PARA_INFO_LOG("config_file: %s\n", ct->config_file);
- PARA_INFO_LOG("key_file: %s\n", ct->key_file);
- ret = ct->conf.inputs_num;
+ *loglevel = ll;
+ ret = lls_num_inputs(lpr);
out:
free(home);
if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
- client_close(ct);
+ lls_free_parse_result(lpr, cmd);
+ free(cf);
+ free(kf);
*ct_ptr = NULL;
}
return ret;
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
+#include "server.lsg.h"
#include "para.h"
#include "error.h"
#include "crypt.h"
#include "sideband.h"
#include "command.h"
-#include "server.cmdline.h"
#include "string.h"
#include "afh.h"
#include "afs.h"
#include "daemon.h"
#include "fd.h"
#include "ipc.h"
+#include "server_cmd.lsg.h"
#include "user_list.h"
-#include "server.command_list.h"
-#include "afs.command_list.h"
#include "signal.h"
#include "version.h"
-typedef int server_command_handler_t(struct command_context *);
-static server_command_handler_t SERVER_COMMAND_HANDLERS;
-server_command_handler_t AFS_COMMAND_HANDLERS;
-
-/* Defines one command of para_server. */
-struct server_command {
- /* The name of the command. */
- const char *name;
- /* Pointer to the function that handles the command. */
- server_command_handler_t *handler;
- /* The privileges a user must have to execute this command. */
- unsigned int perms;
- /* One-line description of the command. */
- const char *description;
- /* Summary of the command line options. */
- const char *usage;
- /* The long help text. */
- const char *help;
-};
-
-static struct server_command afs_cmds[] = {DEFINE_AFS_CMD_ARRAY};
-static struct server_command server_cmds[] = {DEFINE_SERVER_CMD_ARRAY};
+#define SERVER_CMD_AUX_INFO(_arg) _arg,
+static const unsigned server_command_perms[] = {LSG_SERVER_CMD_AUX_INFOS};
+#undef SERVER_CMD_AUX_INFO
+#define SERVER_CMD_AUX_INFO(_arg) #_arg,
+static const char * const server_command_perms_txt[] = {LSG_SERVER_CMD_AUX_INFOS};
+#undef SERVER_CMD_AUX_INFO
/** Commands including options must be shorter than this. */
#define MAX_COMMAND_LEN 32768
return para_strdup("paused");
}
-/*
- * return human readable permission string. Never returns NULL.
- */
-static char *cmd_perms_itohuman(unsigned int perms)
-{
- char *msg = para_malloc(5 * sizeof(char));
-
- msg[0] = perms & AFS_READ? 'a' : '-';
- msg[1] = perms & AFS_WRITE? 'A' : '-';
- msg[2] = perms & VSS_READ? 'v' : '-';
- msg[3] = perms & VSS_WRITE? 'V' : '-';
- msg[4] = '\0';
- return msg;
-}
-
/*
* Never returns NULL.
*/
return msg;
}
-static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly,
+static unsigned get_status(struct misc_meta_data *nmmd, bool parser_friendly,
char **result)
{
char *status, *flags; /* vss status info */
return b.offset;
}
-static int check_sender_args(int argc, char * const * argv, struct sender_command_data *scd)
-{
- int i;
-
- const char *subcmds[] = {SENDER_SUBCOMMANDS NULL};
- scd->sender_num = -1;
- if (argc < 3)
- return -E_COMMAND_SYNTAX;
- for (i = 0; senders[i].name; i++)
- if (!strcmp(senders[i].name, argv[1]))
- break;
- PARA_DEBUG_LOG("%d:%s\n", argc, argv[1]);
- if (!senders[i].name)
- return -E_COMMAND_SYNTAX;
- scd->sender_num = i;
- for (i = 0; subcmds[i]; i++)
- if (!strcmp(subcmds[i], argv[2]))
- break;
- if (!subcmds[i])
- return -E_COMMAND_SYNTAX;
- scd->cmd_num = i;
- if (!senders[scd->sender_num].client_cmds[scd->cmd_num])
- return -E_SENDER_CMD;
- switch (scd->cmd_num) {
- case SENDER_on:
- case SENDER_off:
- if (argc != 3)
- return -E_COMMAND_SYNTAX;
- break;
- case SENDER_deny:
- case SENDER_allow:
- if (argc != 4 || parse_cidr(argv[3], scd->host,
- sizeof(scd->host), &scd->netmask) == NULL)
- return -E_COMMAND_SYNTAX;
- break;
- case SENDER_add:
- case SENDER_delete:
- if (argc != 4)
- return -E_COMMAND_SYNTAX;
- return parse_fec_url(argv[3], scd);
- default:
- return -E_COMMAND_SYNTAX;
- }
- return 1;
-}
-
/**
* Send a sideband packet through a blocking file descriptor.
*
return send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", para_strerror(err));
}
+/**
+ * Send an error context to a client,
+ *
+ * \param cc Client info.
+ * \param errctx The error context string.
+ *
+ * \return The return value of the underlying call to send_sb_va().
+ *
+ * This function frees the error context string after it was sent.
+ */
+int send_errctx(struct command_context *cc, char *errctx)
+{
+ int ret;
+
+ if (!errctx)
+ return 0;
+ ret = send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", errctx);
+ free(errctx);
+ return ret;
+}
+
+static int check_sender_args(struct command_context *cc,
+ struct lls_parse_result *lpr, struct sender_command_data *scd)
+{
+ int i, ret;
+ const char *subcmds[] = {SENDER_SUBCOMMANDS};
+ const char *arg;
+ char *errctx;
+ unsigned num_inputs = lls_num_inputs(lpr);
+
+ scd->sender_num = -1;
+ ret = lls(lls_check_arg_count(lpr, 2, INT_MAX, &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ arg = lls_input(0, lpr);
+ for (i = 0; senders[i].name; i++)
+ if (!strcmp(senders[i].name, arg))
+ break;
+ if (!senders[i].name)
+ return -E_COMMAND_SYNTAX;
+ scd->sender_num = i;
+ arg = lls_input(1, lpr);
+ for (i = 0; subcmds[i]; i++)
+ if (!strcmp(subcmds[i], arg))
+ break;
+ if (!subcmds[i])
+ return -E_COMMAND_SYNTAX;
+ scd->cmd_num = i;
+ if (!senders[scd->sender_num].client_cmds[scd->cmd_num])
+ return -E_SENDER_CMD;
+ switch (scd->cmd_num) {
+ case SENDER_on:
+ case SENDER_off:
+ if (num_inputs != 2)
+ return -E_COMMAND_SYNTAX;
+ break;
+ case SENDER_deny:
+ case SENDER_allow:
+ if (num_inputs != 3 || parse_cidr(lls_input(2, lpr), scd->host,
+ sizeof(scd->host), &scd->netmask) == NULL)
+ return -E_COMMAND_SYNTAX;
+ break;
+ case SENDER_add:
+ case SENDER_delete:
+ if (num_inputs != 3)
+ return -E_COMMAND_SYNTAX;
+ return parse_fec_url(lls_input(2, lpr), scd);
+ default:
+ return -E_COMMAND_SYNTAX;
+ }
+ return 1;
+}
+
/**
* Send a sideband packet through a blocking file descriptor.
*
return ret;
}
-static int com_sender(struct command_context *cc)
+static int com_sender(struct command_context *cc, struct lls_parse_result *lpr)
{
int i, ret = 0;
char *msg = NULL;
struct sender_command_data scd;
- if (cc->argc < 2) {
+ if (lls_num_inputs(lpr) == 0) {
for (i = 0; senders[i].name; i++) {
char *tmp;
ret = xasprintf(&tmp, "%s%s\n", msg? msg : "",
}
return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
}
- ret = check_sender_args(cc->argc, cc->argv, &scd);
+ ret = check_sender_args(cc, lpr, &scd);
if (ret < 0) {
if (scd.sender_num < 0)
return ret;
- if (strcmp(cc->argv[2], "status") == 0)
+ if (strcmp(lls_input(1, lpr), "status") == 0)
msg = senders[scd.sender_num].status();
else
msg = senders[scd.sender_num].help();
case SENDER_add:
case SENDER_delete:
assert(senders[scd.sender_num].resolve_target);
- ret = senders[scd.sender_num].resolve_target(cc->argv[3], &scd);
+ ret = senders[scd.sender_num].resolve_target(lls_input(2, lpr),
+ &scd);
if (ret < 0)
return ret;
}
}
return (i < 10)? 1 : -E_LOCK;
}
+EXPORT_SERVER_CMD_HANDLER(sender);
-/* server info */
-static int com_si(struct command_context *cc)
+static int com_si(struct command_context *cc,
+ __a_unused struct lls_parse_result *lpr)
{
- int ret;
char *msg, *ut;
+ int ret;
- if (cc->argc != 1)
- return -E_COMMAND_SYNTAX;
- mutex_lock(mmd_mutex);
ut = daemon_get_uptime_str(now);
+ mutex_lock(mmd_mutex);
ret = xasprintf(&msg,
"up: %s\nplayed: %u\n"
"server_pid: %d\n"
mmd->active_connections,
mmd->num_commands,
mmd->num_connects,
- conf.loglevel_arg,
+ ENUM_STRING_VAL(LOGLEVEL),
AUDIO_FORMAT_HANDLERS
);
mutex_unlock(mmd_mutex);
free(ut);
return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
}
+EXPORT_SERVER_CMD_HANDLER(si);
-/* version */
-static int com_version(struct command_context *cc)
+static int com_version(struct command_context *cc, struct lls_parse_result *lpr)
{
char *msg;
size_t len;
- if (cc->argc > 1 && strcmp(cc->argv[1], "-v") == 0)
+ if (SERVER_CMD_OPT_GIVEN(VERSION, VERBOSE, lpr))
len = xasprintf(&msg, "%s", version_text("server"));
else
len = xasprintf(&msg, "%s\n", version_single_line("server"));
return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false);
}
+EXPORT_SERVER_CMD_HANDLER(version);
/** These status items are cleared if no audio file is currently open. */
#define EMPTY_STATUS_ITEMS \
*
* This is used by vss when currently no audio file is open.
*/
-static unsigned empty_status_items(int parser_friendly, char **result)
+static unsigned empty_status_items(bool parser_friendly, char **result)
{
char *esi;
unsigned len;
}
#undef EMPTY_STATUS_ITEMS
-/* stat */
-static int com_stat(struct command_context *cc)
+static int com_stat(struct command_context *cc, struct lls_parse_result *lpr)
{
- int i, ret;
+ int ret;
struct misc_meta_data tmp, *nmmd = &tmp;
char *s;
- int32_t num = 0;
- int parser_friendly = 0;
+ bool parser_friendly = SERVER_CMD_OPT_GIVEN(STAT, PARSER_FRIENDLY,
+ lpr) > 0;
+ uint32_t num = SERVER_CMD_UINT32_VAL(STAT, NUM, lpr);
para_sigaction(SIGUSR1, dummy);
-
- for (i = 1; i < cc->argc; i++) {
- const char *arg = cc->argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strncmp(arg, "-n=", 3)) {
- ret = para_atoi32(arg + 3, &num);
- if (ret < 0)
- return ret;
- continue;
- }
- if (!strcmp(arg, "-p")) {
- parser_friendly = 1;
- continue;
- }
- return -E_COMMAND_SYNTAX;
- }
- if (i != cc->argc)
- return -E_COMMAND_SYNTAX;
for (;;) {
/*
* Copy the mmd structure to minimize the time we hold the mmd
out:
return ret;
}
+EXPORT_SERVER_CMD_HANDLER(stat);
-static int send_list_of_commands(struct command_context *cc, struct server_command *cmd,
- const char *handler)
+/* fixed-length, human readable permission string */
+const char *server_cmd_perms_str(unsigned int perms)
{
- char *msg = NULL;
+ static char result[5];
- for (; cmd->name; cmd++) {
- char *tmp, *perms = cmd_perms_itohuman(cmd->perms);
- tmp = make_message("%s\t%s\t%s\t%s\n", cmd->name, handler,
- perms, cmd->description);
- free(perms);
- msg = para_strcat(msg, tmp);
- free(tmp);
- }
- assert(msg);
- return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
+ result[0] = perms & AFS_READ? 'a' : '-';
+ result[1] = perms & AFS_WRITE? 'A' : '-';
+ result[2] = perms & VSS_READ? 'v' : '-';
+ result[3] = perms & VSS_WRITE? 'V' : '-';
+ result[4] = '\0';
+ return result;
}
-/* returns string that must be freed by the caller */
-static struct server_command *get_cmd_ptr(const char *name, char **handler)
+static int send_list_of_commands(struct command_context *cc)
{
- struct server_command *cmd;
-
- for (cmd = server_cmds; cmd->name; cmd++)
- if (!strcmp(cmd->name, name)) {
- if (handler)
- *handler = para_strdup("server"); /* server commands */
- return cmd;
- }
- /* not found, look for commands supported by afs */
- for (cmd = afs_cmds; cmd->name; cmd++)
- if (!strcmp(cmd->name, name)) {
- if (handler)
- *handler = para_strdup("afs");
- return cmd;
- }
- return NULL;
+ int i;
+ const struct lls_command *cmd;
+ char *msg = para_strdup("");
+
+ for (i = 1; (cmd = lls_cmd(i, server_cmd_suite)); i++) {
+ const char *perms = server_cmd_perms_str(server_command_perms[i]);
+ char *tmp = make_message("%s%s\t%s\t%s\n", msg,
+ lls_command_name(cmd), perms, lls_purpose(cmd));
+ free(msg);
+ msg = tmp;
+ }
+ return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
}
-/* help */
-static int com_help(struct command_context *cc)
+static int com_help(struct command_context *cc, struct lls_parse_result *lpr)
{
- struct server_command *cmd;
- char *perms, *handler, *buf;
+ const char *perms;
+ char *long_help, *buf, *errctx;
int ret;
+ const struct lls_command *cmd;
- if (cc->argc < 2) {
- /* no argument given, print list of commands */
- if ((ret = send_list_of_commands(cc, server_cmds, "server")) < 0)
- return ret;
- return send_list_of_commands(cc, afs_cmds, "afs");
+ ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
}
+ if (lls_num_inputs(lpr) == 0)
+ return send_list_of_commands(cc);
/* argument given for help */
- cmd = get_cmd_ptr(cc->argv[1], &handler);
- if (!cmd)
- return -E_BAD_CMD;
- perms = cmd_perms_itohuman(cmd->perms);
- ret = xasprintf(&buf, "%s - %s\n\n"
- "handler: %s\n"
- "permissions: %s\n"
- "usage: %s\n\n"
- "%s\n",
- cc->argv[1],
- cmd->description,
- handler,
- perms,
- cmd->usage,
- cmd->help
- );
- free(perms);
- free(handler);
+ ret = lls(lls_lookup_subcmd(lls_input(0, lpr), server_cmd_suite,
+ &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ cmd = lls_cmd(ret, server_cmd_suite);
+ perms = server_command_perms_txt[ret];
+ long_help = lls_long_help(cmd);
+ assert(long_help);
+ ret = xasprintf(&buf, "%spermissions: %s\n", long_help, perms);
+ free(long_help);
return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false);
}
+EXPORT_SERVER_CMD_HANDLER(help);
-/* hup */
-static int com_hup(struct command_context *cc)
+static int com_hup(__a_unused struct command_context *cc,
+ __a_unused struct lls_parse_result *lpr)
{
- if (cc->argc != 1)
- return -E_COMMAND_SYNTAX;
kill(getppid(), SIGHUP);
return 1;
}
+EXPORT_SERVER_CMD_HANDLER(hup);
-/* term */
-static int com_term(struct command_context *cc)
+static int com_term(__a_unused struct command_context *cc,
+ __a_unused struct lls_parse_result *lpr)
{
- if (cc->argc != 1)
- return -E_COMMAND_SYNTAX;
kill(getppid(), SIGTERM);
return 1;
}
+EXPORT_SERVER_CMD_HANDLER(term);
-static int com_play(struct command_context *cc)
+static int com_play(__a_unused struct command_context *cc,
+ __a_unused struct lls_parse_result *lpr)
{
- if (cc->argc != 1)
- return -E_COMMAND_SYNTAX;
mutex_lock(mmd_mutex);
mmd->new_vss_status_flags |= VSS_PLAYING;
mmd->new_vss_status_flags &= ~VSS_NOMORE;
mutex_unlock(mmd_mutex);
return 1;
}
+EXPORT_SERVER_CMD_HANDLER(play);
-/* stop */
-static int com_stop(struct command_context *cc)
+static int com_stop(__a_unused struct command_context *cc,
+ __a_unused struct lls_parse_result *lpr)
{
- if (cc->argc != 1)
- return -E_COMMAND_SYNTAX;
mutex_lock(mmd_mutex);
mmd->new_vss_status_flags &= ~VSS_PLAYING;
mmd->new_vss_status_flags &= ~VSS_REPOS;
mutex_unlock(mmd_mutex);
return 1;
}
+EXPORT_SERVER_CMD_HANDLER(stop);
-/* pause */
-static int com_pause(struct command_context *cc)
+static int com_pause(__a_unused struct command_context *cc,
+ __a_unused struct lls_parse_result *lpr)
{
- if (cc->argc != 1)
- return -E_COMMAND_SYNTAX;
mutex_lock(mmd_mutex);
if (!vss_paused() && !vss_stopped()) {
mmd->events++;
mutex_unlock(mmd_mutex);
return 1;
}
+EXPORT_SERVER_CMD_HANDLER(pause);
-/* next */
-static int com_next(struct command_context *cc)
+static int com_next(__a_unused struct command_context *cc,
+ __a_unused struct lls_parse_result *lpr)
{
- if (cc->argc != 1)
- return -E_COMMAND_SYNTAX;
mutex_lock(mmd_mutex);
mmd->events++;
mmd->new_vss_status_flags |= VSS_NEXT;
mutex_unlock(mmd_mutex);
return 1;
}
+EXPORT_SERVER_CMD_HANDLER(next);
-/* nomore */
-static int com_nomore(struct command_context *cc)
+static int com_nomore(__a_unused struct command_context *cc,
+ __a_unused struct lls_parse_result *lpr)
{
- if (cc->argc != 1)
- return -E_COMMAND_SYNTAX;
mutex_lock(mmd_mutex);
if (vss_playing() || vss_paused())
mmd->new_vss_status_flags |= VSS_NOMORE;
mutex_unlock(mmd_mutex);
return 1;
}
+EXPORT_SERVER_CMD_HANDLER(nomore);
-/* ff */
-static int com_ff(struct command_context *cc)
+static int com_ff(__a_unused struct command_context *cc,
+ struct lls_parse_result *lpr)
{
long promille;
int ret, backwards = 0;
unsigned i;
- char c;
+ char c, *errctx;
- if (cc->argc != 2)
- return -E_COMMAND_SYNTAX;
- if (!(ret = sscanf(cc->argv[1], "%u%c", &i, &c)))
+ ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ if (!(ret = sscanf(lls_input(0, lpr), "%u%c", &i, &c)))
return -E_COMMAND_SYNTAX;
if (ret > 1 && c == '-')
backwards = 1; /* jmp backwards */
mutex_unlock(mmd_mutex);
return ret;
}
+EXPORT_SERVER_CMD_HANDLER(ff);
-/* jmp */
-static int com_jmp(struct command_context *cc)
+static int com_jmp(__a_unused struct command_context *cc,
+ struct lls_parse_result *lpr)
{
long unsigned int i;
int ret;
+ char *errctx;
- if (cc->argc != 2)
- return -E_COMMAND_SYNTAX;
- if (sscanf(cc->argv[1], "%lu", &i) <= 0)
- return -E_COMMAND_SYNTAX;
+ ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ if (sscanf(lls_input(0, lpr), "%lu", &i) <= 0)
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
mutex_lock(mmd_mutex);
ret = -E_NO_AUDIO_FILE;
if (!mmd->afd.afhi.chunks_total)
mutex_unlock(mmd_mutex);
return ret;
}
+EXPORT_SERVER_CMD_HANDLER(jmp);
-static int com_tasks(struct command_context *cc)
+static int com_tasks(struct command_context *cc,
+ __a_unused struct lls_parse_result *lpr)
{
char *tl = server_get_tasks();
- int ret = 1;
-
- if (tl)
- ret = send_sb(&cc->scc, tl, strlen(tl), SBD_OUTPUT, false);
- return ret;
-}
-
-/*
- * check if perms are sufficient to exec a command having perms cmd_perms.
- * Returns 0 if perms are sufficient, -E_PERM otherwise.
- */
-static int check_perms(unsigned int perms, const struct server_command *cmd_ptr)
-{
- PARA_DEBUG_LOG("checking permissions\n");
- return (cmd_ptr->perms & perms) < cmd_ptr->perms ? -E_PERM : 0;
+ assert(tl);
+ return send_sb(&cc->scc, tl, strlen(tl), SBD_OUTPUT, false);
}
+EXPORT_SERVER_CMD_HANDLER(tasks);
static void reset_signals(void)
{
}
struct connection_features {
- bool aes_ctr128_requested;
+ int dummy; /* none at the moment */
};
static int parse_auth_request(char *buf, int len, struct user **u,
if (strcmp(features[i], "sideband") == 0)
continue;
if (strcmp(features[i], "aes_ctr128") == 0)
- cf->aes_ctr128_requested = true;
+ continue;
else {
ret = -E_BAD_FEATURE;
goto out;
static int run_command(struct command_context *cc, struct iovec *iov,
const char *peername)
{
- int ret, i;
- char *p, *end;
- struct server_command *cmd;
+ int ret, i, argc;
+ char *p, *end, **argv;
+ const struct lls_command *lcmd = NULL;
+ unsigned perms;
+ struct lls_parse_result *lpr;
+ char *errctx;
if (iov->iov_base == NULL || iov->iov_len == 0)
- return -E_BAD_CMD;
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
p = iov->iov_base;
p[iov->iov_len - 1] = '\0'; /* just to be sure */
- cmd = get_cmd_ptr(p, NULL);
- if (!cmd)
- return -E_BAD_CMD;
- ret = check_perms(cc->u->perms, cmd);
- if (ret < 0)
+
+ ret = lls(lls_lookup_subcmd(p, server_cmd_suite, &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
return ret;
+ }
+ perms = server_command_perms[ret];
+ if ((perms & cc->u->perms) != perms)
+ return -E_PERM;
+ lcmd = lls_cmd(ret, server_cmd_suite);
end = iov->iov_base + iov->iov_len;
for (i = 0; p < end; i++)
p += strlen(p) + 1;
- cc->argc = i;
- cc->argv = para_malloc((cc->argc + 1) * sizeof(char *));
+ argc = i;
+ argv = para_malloc((argc + 1) * sizeof(char *));
for (i = 0, p = iov->iov_base; p < end; i++) {
- cc->argv[i] = para_strdup(p);
+ argv[i] = para_strdup(p);
p += strlen(p) + 1;
}
- cc->argv[cc->argc] = NULL;
- PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cmd->name,
+ argv[argc] = NULL;
+ PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", lls_command_name(lcmd),
cc->u->name, peername);
- ret = cmd->handler(cc);
- free_argv(cc->argv);
+ ret = lls(lls_parse(argc, argv, lcmd, &lpr, &errctx));
+ if (ret >= 0) {
+ const struct server_cmd_user_data *ud = lls_user_data(lcmd);
+ ret = ud->handler(cc, lpr);
+ lls_free_parse_result(lpr, lcmd);
+ } else
+ send_errctx(cc, errctx);
+ free_argv(argv);
mutex_lock(mmd_mutex);
mmd->num_commands++;
- if (ret >= 0 && (cmd->perms & AFS_WRITE))
+ if (ret >= 0 && (perms & AFS_WRITE))
mmd->events++;
mutex_unlock(mmd_mutex);
return ret;
alarm(0);
PARA_INFO_LOG("good auth for %s\n", cc->u->name);
/* init stream cipher keys with the second part of the random buffer */
- cc->scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN,
- cf.aes_ctr128_requested);
+ 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, cf.aes_ctr128_requested);
+ SESSION_KEY_LEN);
ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false);
if (ret < 0)
goto net_err;
const char *peer;
/** The paraslash user that executes this command. */
struct user *u;
- /** Argument count. */
- int argc;
- /** Argument vector. */
- char **argv;
/** File descriptor and crypto keys. */
struct stream_cipher_context scc;
};
+/** Prototype of a server command handler. */
+typedef int (*server_cmd_handler_t)(struct command_context *,
+ struct lls_parse_result *);
+
+/**
+ * The lopsub user data structure for server commands.
+ *
+ * One such structure exists for each server command. Lopsub maintains
+ * references to the user data structures and provides lls_user_data() for
+ * applications to fetch the user data pointer of a given command. This
+ * mechanism is used by the dispatcher of command.c to run the command handler.
+ */
+struct server_cmd_user_data {
+ /** Pointer to the command handler. */
+ server_cmd_handler_t handler;
+};
+
+/** Define the user data structure for one command. */
+#define EXPORT_SERVER_CMD_HANDLER(_cmd) \
+ const struct server_cmd_user_data lsg_server_cmd_com_ ## _cmd ## _user_data = { \
+ .handler = com_ ## _cmd \
+ };
+
+/** Get the lopsub command pointer by command name. */
+#define SERVER_CMD_CMD_PTR(_cmd) \
+ (lls_cmd(LSG_SERVER_CMD_CMD_ ## _cmd, server_cmd_suite))
+
+/** Get the lopsub parse result of an option. */
+#define SERVER_CMD_OPT_RESULT(_cmd, _opt, _lpr) \
+ (lls_opt_result(LSG_SERVER_CMD_ ## _cmd ## _OPT_ ## _opt, _lpr))
+
+/** How many times an option was given. */
+#define SERVER_CMD_OPT_GIVEN(_cmd, _opt, _lpr) \
+ (lls_opt_given(SERVER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+
+/** Fetch the (first) argument given to an option of type uint32. */
+#define SERVER_CMD_UINT32_VAL(_cmd, _opt, _lpr) \
+ (lls_uint32_val(0, SERVER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+
int send_sb(struct stream_cipher_context *scc, void *buf, size_t numbytes,
int band, bool dont_free);
__printf_3_4 int send_sb_va(struct stream_cipher_context *scc, int band,
const char *fmt, ...);
int send_strerror(struct command_context *cc, int err);
+int send_errctx(struct command_context *cc, char *errctx);
int recv_sb(struct stream_cipher_context *scc,
enum sb_designator expected_band,
size_t max_size, struct iovec *result);
+++ /dev/null
-#!/usr/bin/env bash
-
-read_header()
-{
- local key value i
-
- while read key value; do
- case "$key" in
- ---)
- break
- ;;
- BN:)
- base_name="$value"
- ;;
- SF:)
- source_files="$value"
- ;;
- SN:)
- section_name="$value"
- ;;
- TM:)
- template_members="$value"
- esac
- done
-}
-
-read_one_command()
-{
- local line
-
- name_txt=""
- desc_txt=""
- usage_txt=""
- help_txt=""
- perms_txt=""
- template=0
- template_name=""
- template_prototype=""
- while read key value; do
- case "$key" in
- ---)
- break
- ;;
- N:)
- name_txt="$value"
- ;;
- T:)
- template_name="$value"
- template=1
- ;;
- O:)
- template_prototype="$value"
- template=1
- ;;
- P:)
- perms_txt="$value"
- ;;
- D:)
- desc_txt="$value"
- ;;
- U:)
- usage_txt="$value"
- ;;
- H:)
- help_txt="${value}"
- while read line; do
- if test "$line" = "---"; then
- break;
- fi
- line=${line#H:}
- help_txt="$help_txt
-${line# }"
- done
- break
- ;;
- esac
- done
- if test $template -eq 0; then
- if test -n "$name_txt" -a -n "$desc_txt" -a -n "$usage_txt" \
- -a -n "$help_txt"; then
- ret=1
- return
- fi
- else
- if test -n "$template_name" -a -n "$template_prototype" \
- -a -n "$name_txt " -a -n "$template_members" \
- -a -n "$desc_txt" -a -n "$usage_txt" \
- -a -n "$help_txt"; then
- ret=1
- return
- fi
- fi
- if test -z "$name_txt" -a -z "$desc_txt" -a -z "$usage_txt" \
- -a -z "$help_txt"; then
- ret=0
- return
- fi
- ret=-1
- #return
- echo "!ERROR!"
- echo "N: $name_txt"
- echo "D: $desc_txt"
- echo "S: $usage_txt"
- echo "P: $perms_txt"
- echo "H: $help_txt"
- echo "O: $template_prototype"
-}
-
-dump_man()
-{
- if test $template -eq 0; then
- echo ".SS \"$name_txt\""
- echo "$desc_txt"
- echo
- echo "\\fBUsage: \\fP$usage_txt"
- else
- for member in $template_members; do
- local sed_cmd="sed -e s/@member@/$member/g"
- local t_name_txt=$(echo $name_txt | $sed_cmd)
- echo ".SS \"$t_name_txt\""
- done
- echo "$desc_txt" | sed -e "s/@member@/{$(echo $template_members | sed -e 's/ / | /g')}/g"
- echo
- echo "\\fBUsage: \\fP"
- echo
- echo ".nf"
- for member in $template_members; do
- local sed_cmd="sed -e s/@member@/$member/g"
- local t_usage_txt=$(echo $usage_txt | $sed_cmd)
- printf "\t$t_usage_txt\n"
- done
- echo ".fi"
- fi
- echo
- echo "$help_txt" | sed -e 's/^ //'
- echo
- if test -n "$perms_txt"; then
- echo -n "\\fBpermissions:\\fP "
- if test "$perms_txt" = "0"; then
- echo "(none)"
- else
- echo "$perms_txt"
- fi
- fi
- echo
-}
-
-
-com_man()
-{
- echo "[$section_name]"
- echo
- while : ; do
- read_one_command
- if test $ret -lt 0; then
- exit 1
- fi
- if test $ret -eq 0; then
- break
- fi
- dump_man
- done
-}
-
-cmd_handler_name()
-{
- result="com_$name_txt,"
-}
-
-make_array_member()
-{
- local TAB=' ' CR='
-'
- local tmp
-
- result="{.name = \"$name_txt\", .handler = com_$name_txt, "
- if test -n "$perms_txt"; then
- result="$result .perms = $perms_txt,"
- fi
- result="$result.description = \"$desc_txt\", .usage = \"$usage_txt\", \\$CR .help = "
- tmp="$(printf "%s\n" "$help_txt" | sed -e 's/^/\"/g' -e 's/$/\\n\"/g' \
- -e "s/$TAB/\\\t/g" -e's/$/\\/g')"
- result="$result$tmp$CR}, \\$CR"
-}
-
-make_completion()
-{
- local CR='
-'
- result=" {.name = \"$name_txt\", .completer = ${name_txt}_completer}, \\$CR"
-}
-
-template_loop()
-{
- local loop_result=
-
- local t_name="$name_txt"
- local t_perms="$perms_txt"
- local t_desc="$desc_txt"
- local t_usage="$usage_txt"
- local t_help="$help_txt"
- local t_source_files="$source_files"
- local member
- for member in $template_members; do
- name_txt="${t_name//@member@/$member}"
- perms_txt="${t_perms//@member@/$member}"
- desc_txt="${t_desc//@member@/$member}"
- usage_txt="${t_usage//@member@/$member}"
- help_txt="${t_help//@member@/$member}"
- prototype="${template_prototype//@member@/$member}"
- result=
- $1
- loop_result="$loop_result$result"
- done
- result="$loop_result"
- # reset global variables
- name_txt="$t_name"
- perms_txt="$t_perms"
- desc_txt="$t_desc"
- usage_txt="$t_usage"
- help_txt="$t_help"
- source_files="$t_source_files"
-}
-
-com_header()
-{
- local array_members handlers= CR='
-'
-
- while : ; do
- read_one_command
- if test $ret -lt 0; then
- exit 1
- fi
- if test $ret -eq 0; then
- break
- fi
- if test $template -eq 0; then
- cmd_handler_name
- handlers+="$result"
- make_array_member
- array_members="$array_members$result"
- continue
- fi
- template_loop cmd_handler_name
- handlers+="$result"
- template_loop make_array_member
- array_members="$array_members$result"
- done
- array_members="$array_members{.name = NULL} \\$CR"
- echo "#define DEFINE_${base_name^^}_CMD_ARRAY $array_members"
- echo "#define ${base_name^^}_COMMAND_HANDLERS ${handlers%,}"
-}
-
-com_completion()
-{
-
- echo "#define $1 \\"
- while : ; do
- read_one_command
- if test $ret -lt 0; then
- exit 1
- fi
- if test $ret -eq 0; then
- break
- fi
- if test $template -eq 0; then
- make_completion
- printf "%s" "$result"
- continue
- fi
- template_loop make_completion
- printf "%s" "$result"
- done
- echo
-}
-
-read_header
-arg="$1"
-shift
-case "$arg" in
- "h")
- com_header
- ;;
- "man")
- com_man $*
- ;;
- "compl")
- com_completion $*
- ;;
-esac
*/
#include <regex.h>
+#include <lopsub.h>
+#include "filter_cmd.lsg.h"
#include "para.h"
-#include "compress_filter.cmdline.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
#include "error.h"
+#define U32_OPTVAL(_opt, _lpr) (FILTER_CMD_OPT_UINT32_VAL(COMPRESS, _opt, _lpr))
+
/** Data specific to the compress filter. */
struct private_compress_data {
/** The current multiplier. */
unsigned current_gain;
- /** Points to the configuration data for this instance of the compress filter. */
- struct compress_filter_args_info *conf;
/** Maximal admissible gain. */
unsigned max_gain;
/** Number of samples already seen. */
char *inbuf;
size_t length, i;
int16_t *ip, *op;
- unsigned gain_shift = pcd->conf->inertia_arg + pcd->conf->damp_arg,
- mask = (1 << pcd->conf->blocksize_arg) - 1;
-
+ uint32_t inertia = U32_OPTVAL(INERTIA, fn->lpr);
+ unsigned gain_shift = inertia + U32_OPTVAL(DAMP, fn->lpr),
+ mask = (1 << U32_OPTVAL(BLOCKSIZE, fn->lpr)) - 1;
//inplace = false;
next_buffer:
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (sample > 32767) { /* clip */
sample = 32767;
pcd->current_gain = (3 * pcd->current_gain +
- (1 << pcd->conf->inertia_arg)) / 4;
+ (1 << inertia)) / 4;
pcd->peak = 0;
} else if (sample > pcd->peak)
pcd->peak = sample;
continue;
// PARA_DEBUG_LOG("gain: %u, peak: %u\n", pcd->current_gain,
// pcd->peak);
- if (pcd->peak < pcd->conf->target_level_arg) {
+ if (pcd->peak < U32_OPTVAL(TARGET_LEVEL, fn->lpr)) {
if (pcd->current_gain < pcd->max_gain)
pcd->current_gain++;
} else
pcd->current_gain = PARA_MAX(pcd->current_gain - 2,
- 1U << pcd->conf->inertia_arg);
+ 1U << inertia);
pcd->peak = 0;
}
if (inplace)
return ret;
}
-/** TODO: Add sanity checks */
-static int compress_parse_config(int argc, char **argv, void **config)
-{
- struct compress_filter_args_info *conf = para_calloc(sizeof(*conf));
-
- compress_filter_cmdline_parser(argc, argv, conf);
- *config = conf;
- return 1;
-}
-
static void compress_open(struct filter_node *fn)
{
- struct private_compress_data *pcd = para_calloc(
- sizeof(struct private_compress_data));
- pcd->conf = fn->conf;
+ struct private_compress_data *pcd = para_calloc(sizeof(*pcd));
+ uint32_t inertia = U32_OPTVAL(INERTIA, fn->lpr);
+ uint32_t aggressiveness = U32_OPTVAL(AGGRESSIVENESS, fn->lpr);
+
fn->private_data = pcd;
fn->min_iqs = 2; /* 16 bit audio */
- pcd->current_gain = 1 << pcd->conf->inertia_arg;
- pcd->max_gain = 1 << (pcd->conf->inertia_arg + pcd->conf->aggressiveness_arg);
-}
-
-static void compress_free_config(void *conf)
-{
- compress_filter_cmdline_parser_free(conf);
+ pcd->current_gain = 1U << inertia;
+ pcd->max_gain = 1U << (inertia + aggressiveness);
}
-/**
- * The init function of the compress filter.
- *
- * \param f Pointer to the struct to initialize.
- */
-void compress_filter_init(struct filter *f)
-{
- struct compress_filter_args_info dummy;
-
- compress_filter_cmdline_parser_init(&dummy);
- f->open = compress_open;
- f->close = compress_close;
- f->pre_select = generic_filter_pre_select;
- f->post_select = compress_post_select;
- f->parse_config = compress_parse_config;
- f->free_config = compress_free_config;
- f->help = (struct ggo_help)DEFINE_GGO_HELP(compress_filter);
-}
+const struct filter lsg_filter_cmd_com_compress_user_data = {
+ .open = compress_open,
+ .close = compress_close,
+ .pre_select = generic_filter_pre_select,
+ .post_select = compress_post_select,
+};
AC_USE_SYSTEM_EXTENSIONS
AC_C_BIGENDIAN()
-AC_PATH_PROG([GENGETOPT], [gengetopt])
-test -z "$GENGETOPT" && AC_MSG_ERROR(
- [gengetopt is required to build this package])
-
AC_PATH_PROG([M4], [m4])
test -z "$M4" && AC_MSG_ERROR(
[The m4 macro processor is required to build this package])
-AC_PATH_PROG([HELP2MAN], [help2man])
-test -z "$HELP2MAN" && AC_MSG_ERROR(
- [help2man is required to build this package])
-
-AC_PATH_PROG([INSTALL], [install])
-test -z "$INSTALL" && AC_MSG_ERROR(
- [The install program is required to build this package])
+AC_PATH_PROG([lopsubgen], [lopsubgen])
+test -z "$lopsubgen" && AC_MSG_ERROR(
+ [lopsubgen is required to build this package])
AC_PROG_CC
AC_PROG_CPP
executables="recv filter audioc write afh play"
-################################################################## clock_gettime
-clock_gettime_lib=
-AC_CHECK_LIB([c], [clock_gettime], [clock_gettime_lib=c], [
- AC_CHECK_LIB([rt], [clock_gettime], [clock_gettime_lib=rt], [], [])
-])
-if test -n "$clock_gettime_lib"; then
- AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [
- define to 1 if clock_gettime() is supported])
-fi
-if test "$clock_gettime_lib" = "rt"; then
- AC_SUBST(clock_gettime_ldflags, -lrt)
-fi
-
########################################################################### osl
STASH_FLAGS
LIB_ARG_WITH([osl], [-losl])
AC_CHECK_LIB([osl], [osl_open_table], [], [HAVE_OSL=no])
LIB_SUBST_FLAGS(osl)
UNSTASH_FLAGS
+######################################################################## lopsub
+STASH_FLAGS
+LIB_ARG_WITH([lopsub], [-llopsub])
+HAVE_LOPSUB=yes
+AC_CHECK_HEADER(lopsub.h, [], [HAVE_LOPSUB=no])
+AC_CHECK_LIB([lopsub], [lls_merge], [], [HAVE_LOPSUB=yes])
+if test $HAVE_LOPSUB = no; then AC_MSG_ERROR([
+ The lopsub library is required to build this software, but
+ the above checks indicate it is not installed on your system.
+ Run the following command to download a copy.
+ git clone git://git.tuebingen.mpg.de/lopsub.git
+ Install the library, then run this configure script again.
+])
+fi
+LIB_SUBST_FLAGS([lopsub])
+UNSTASH_FLAGS
######################################################################## openssl
STASH_FLAGS
HAVE_OPENSSL=yes
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
STASH_FLAGS
LIB_ARG_WITH([curses], [])
if test ${have_ip_mreqn} = yes; then
AC_DEFINE(HAVE_IP_MREQN, 1, define to 1 you have struct ip_mreqn)
fi
-########################################################################### osx
-
-AC_MSG_CHECKING(for CoreAudio (MacOs))
-AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- #include <CoreAudio/CoreAudio.h>
-]], [[
- AudioDeviceID id;
-]])],[have_core_audio=yes],[have_core_audio=no])
-AC_MSG_RESULT($have_core_audio)
-if test ${have_core_audio} = yes; then
- f1="-framework CoreAudio"
- f2="-framework AudioToolbox"
- f3="-framework AudioUnit"
- f4="-framework CoreServices"
- core_audio_ldflags="$f1 $f2 $f3 $f4"
- AC_SUBST(core_audio_ldflags)
- AC_DEFINE(HAVE_CORE_AUDIO, 1, define to 1 on Mac Os X)
-fi
########################################################################### ogg
STASH_FLAGS
LIB_ARG_WITH([ogg], [-logg])
}])
########################################################################### faad
STASH_FLAGS
-LIB_ARG_WITH([faad], [-lfaad])
+LIB_ARG_WITH([faad], [-lfaad -lmp4ff])
HAVE_FAAD=yes
AC_CHECK_HEADER(neaacdec.h, [], HAVE_FAAD=no)
+AC_CHECK_HEADER(mp4ff.h, [], HAVE_FAAD=no)
AC_CHECK_LIB([faad], [NeAACDecOpen], [], HAVE_FAAD=no)
+AC_CHECK_LIB([mp4ff], [mp4ff_meta_get_artist], [], HAVE_FAAD=no)
LIB_SUBST_FLAGS(faad)
UNSTASH_FLAGS
########################################################################### mad
AC_CHECK_LIB([samplerate], [src_process], [], HAVE_SAMPLERATE=no)
LIB_SUBST_FLAGS(samplerate)
UNSTASH_FLAGS
-########################################################################## mp4v2
-STASH_FLAGS
-LIB_ARG_WITH([mp4v2], [-lmp4v2])
-HAVE_MP4V2=yes
-AC_CHECK_HEADER([mp4v2/mp4v2.h], [], [HAVE_MP4V2=no])
-AC_CHECK_LIB([mp4v2], [MP4Read], [], [HAVE_MP4V2=no])
-LIB_SUBST_FLAGS(mp4v2)
-UNSTASH_FLAGS
######################################################################### server
if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes; then
build_server="yes"
executables="$executables server"
- server_cmdline_objs="server"
server_errlist_objs="
server
afh_common
wma_common
sideband
version
- ggo
"
if test "$CRYPTOLIB" = openssl; then
server_errlist_objs="$server_errlist_objs crypt"
NEED_SPEEX_OBJECTS() && server_errlist_objs="$server_errlist_objs spx_afh spx_common"
NEED_OPUS_OBJECTS() && server_errlist_objs="$server_errlist_objs opus_afh opus_common"
NEED_FLAC_OBJECTS && server_errlist_objs="$server_errlist_objs flac_afh"
- if test $HAVE_FAAD = yes && test $HAVE_MP4V2 = yes; then
- server_errlist_objs="$server_errlist_objs aac_afh aac_common"
+ if test $HAVE_FAAD = yes; then
+ server_errlist_objs="$server_errlist_objs aac_afh"
fi
- server_objs="add_cmdline($server_cmdline_objs) $server_errlist_objs"
+ server_objs="$server_errlist_objs"
AC_SUBST(server_objs, add_dot_o($server_objs))
else
build_server="no"
if test -n "$CRYPTOLIB"; then
build_client="yes"
executables="$executables client"
- client_cmdline_objs="client"
client_errlist_objs="
client
net
crypt_common
base64
version
- ggo
"
if test "$CRYPTOLIB" = openssl; then
client_errlist_objs="$client_errlist_objs crypt"
if test $HAVE_READLINE = yes; then
client_errlist_objs="$client_errlist_objs interactive"
fi
- client_objs="add_cmdline($client_cmdline_objs) $client_errlist_objs"
- AC_SUBST(client_objs, add_dot_o($client_objs))
+ client_objs="$client_errlist_objs"
+ AC_SUBST(client_objs, add_dot_o($client_errlist_objs))
else
build_client="no"
fi
build_audiod="yes"
executables="$executables audiod"
audiod_audio_formats="wma"
- audiod_cmdline_objs="$audiod_cmdline_objs
- audiod
- compress_filter
- http_recv
- dccp_recv
- file_write
- client
- amp_filter
- udp_recv
- prebuffer_filter
- sync_filter
- "
audiod_errlist_objs="$audiod_errlist_objs
audiod
signal
audiod_command
fecdec_filter
client_common
- ggo
udp_recv
color
fec
else
audiod_errlist_objs="$audiod_errlist_objs gcrypt"
fi
- if test "$have_core_audio" = "yes"; then
- audiod_errlist_objs="$audiod_errlist_objs osx_write ipc"
- audiod_cmdline_objs="$audiod_cmdline_objs osx_write"
- fi
NEED_VORBIS_OBJECTS && {
audiod_errlist_objs="$audiod_errlist_objs oggdec_filter"
audiod_audio_formats="$audiod_audio_formats ogg"
audiod_audio_formats="$audiod_audio_formats flac"
}
if test $HAVE_FAAD = yes; then
- audiod_errlist_objs="$audiod_errlist_objs aacdec_filter aac_common"
+ audiod_errlist_objs="$audiod_errlist_objs aacdec_filter"
audiod_audio_formats="$audiod_audio_formats aac"
fi
if test $HAVE_MAD = yes; then
audiod_audio_formats="$audiod_audio_formats mp3"
- audiod_cmdline_objs="$audiod_cmdline_objs mp3dec_filter"
audiod_errlist_objs="$audiod_errlist_objs mp3dec_filter"
fi
if test $HAVE_OSS = yes; then
audiod_errlist_objs="$audiod_errlist_objs oss_write"
- audiod_cmdline_objs="$audiod_cmdline_objs oss_write"
fi
if test $HAVE_ALSA = yes; then
audiod_errlist_objs="$audiod_errlist_objs alsa_write"
- audiod_cmdline_objs="$audiod_cmdline_objs alsa_write"
fi
NEED_AO_OBJECTS && {
audiod_errlist_objs="$audiod_errlist_objs ao_write"
- audiod_cmdline_objs="$audiod_cmdline_objs ao_write"
}
if test $HAVE_SAMPLERATE = yes; then
audiod_errlist_objs="$audiod_errlist_objs resample_filter check_wav"
- audiod_cmdline_objs="$audiod_cmdline_objs resample_filter"
fi
- audiod_objs="add_cmdline($audiod_cmdline_objs) $audiod_errlist_objs"
+ audiod_objs="$audiod_errlist_objs"
AC_SUBST(audiod_objs, add_dot_o($audiod_objs))
enum="$(for i in $audiod_audio_formats; do printf "AUDIO_FORMAT_${i}, " | tr '[a-z]' '[A-Z]'; done)"
else
build_audiod="no"
fi
-########################################################################### fade
+########################################################################### mixer
if test $HAVE_OSS = yes -o $HAVE_ALSA = yes; then
- build_fade="yes"
- executables="$executables fade"
- fade_cmdline_objs="fade"
- fade_errlist_objs="fade exec string fd version ggo"
+ build_mixer="yes"
+ executables="$executables mixer"
+ mixer_errlist_objs="mixer exec string fd version"
if test $HAVE_OSS = yes; then
- fade_errlist_objs="$fade_errlist_objs oss_mix"
- mixers="${mixers}oss "
- default_mixer="OSS_MIX"
+ mixer_errlist_objs="$mixer_errlist_objs oss_mix"
fi
if test $HAVE_ALSA = yes; then
- fade_errlist_objs="$fade_errlist_objs alsa_mix"
- mixers="${mixers}alsa "
- default_mixer="ALSA_MIX"
+ mixer_errlist_objs="$mixer_errlist_objs alsa_mix"
fi
- fade_objs="add_cmdline($fade_cmdline_objs) $fade_errlist_objs"
- AC_SUBST(fade_objs, add_dot_o($fade_objs))
- enum="$(
- for i in $mixers; do
- printf "${i}_MIX, " | tr '[a-z]' '[A-Z]'
- done
- )"
- AC_DEFINE_UNQUOTED(MIXER_ENUM, $enum NUM_SUPPORTED_MIXERS,
- enum of supported mixers)
- AC_DEFINE_UNQUOTED(DEFAULT_MIXER, $default_mixer,
- use this mixer if none was specified)
- names="$(for i in $mixers; do printf \"$i\",' ' ; done)"
- AC_DEFINE_UNQUOTED(MIXER_NAMES, $names, supported mixer names)
- inits="$(
- for i in $mixers; do
- printf 'extern void '$i'_mix_init(struct mixer *); '
- done
- )"
- AC_DEFINE_UNQUOTED(DECLARE_MIXER_INITS, $inits,
- init functions of the supported mixers)
- array="$(for i in $mixers; do printf '{.init = '$i'_mix_init},'; done)"
- AC_DEFINE_UNQUOTED(MIXER_ARRAY, $array, array of supported mixers)
+ mixer_objs="$mixer_errlist_objs"
+ AC_SUBST(mixer_objs, add_dot_o($mixer_objs))
else
- build_fade="no"
+ build_mixer="no"
AC_MSG_WARN([no mixer support])
fi
########################################################################### gui
if test $HAVE_CURSES = yes; then
build_gui="yes"
executables="$executables gui"
- gui_cmdline_objs="gui"
gui_errlist_objs="
exec
signal
time
sched
version
- ggo
"
- gui_objs="add_cmdline($gui_cmdline_objs) $gui_errlist_objs"
+ gui_objs="$gui_errlist_objs"
AC_SUBST(gui_objs, add_dot_o($gui_objs))
else
build_gui="no"
AC_MSG_WARN([no curses lib, cannot build para_gui])
fi
######################################################################## filter
-filters="
- compress
- wav
- amp
- fecdec
- wmadec
- prebuffer
- sync
-"
filter_errlist_objs="
filter_common
wav_filter
sched
fd
amp_filter
- ggo
fecdec_filter
fec
version
net
sync_filter
"
-filter_cmdline_objs="
- filter
- compress_filter
- amp_filter
- prebuffer_filter
- sync_filter
-"
-NEED_VORBIS_OBJECTS && {
- filters="$filters oggdec"
- filter_errlist_objs="$filter_errlist_objs oggdec_filter"
-}
-NEED_SPEEX_OBJECTS && {
- filters="$filters spxdec"
- filter_errlist_objs="$filter_errlist_objs spxdec_filter spx_common"
-}
-NEED_OPUS_OBJECTS && {
- filters="$filters opusdec"
- filter_errlist_objs="$filter_errlist_objs opusdec_filter opus_common"
-}
-NEED_FLAC_OBJECTS && {
- filter_errlist_objs="$filter_errlist_objs flacdec_filter"
- filters="$filters flacdec"
-}
+NEED_VORBIS_OBJECTS && filter_errlist_objs="$filter_errlist_objs oggdec_filter"
+NEED_SPEEX_OBJECTS && filter_errlist_objs="$filter_errlist_objs spxdec_filter spx_common"
+NEED_OPUS_OBJECTS && filter_errlist_objs="$filter_errlist_objs opusdec_filter opus_common"
+NEED_FLAC_OBJECTS && filter_errlist_objs="$filter_errlist_objs flacdec_filter"
if test $HAVE_FAAD = yes; then
- filter_errlist_objs="$filter_errlist_objs aacdec_filter aac_common"
- filters="$filters aacdec"
+ filter_errlist_objs="$filter_errlist_objs aacdec_filter"
fi
if test $HAVE_MAD = yes; then
- filter_cmdline_objs="$filter_cmdline_objs mp3dec_filter"
filter_errlist_objs="$filter_errlist_objs mp3dec_filter"
- filters="$filters mp3dec"
fi
if test $HAVE_SAMPLERATE = yes; then
filter_errlist_objs="$filter_errlist_objs resample_filter check_wav"
- filter_cmdline_objs="$filter_cmdline_objs resample_filter"
- filters="$filters resample"
fi
-filters="$(echo $filters)"
-AC_SUBST(filters)
-filter_objs="add_cmdline($filter_cmdline_objs) $filter_errlist_objs"
+filter_objs="$filter_errlist_objs"
AC_SUBST(filter_objs, add_dot_o($filter_objs))
-
-enum="$(for i in $filters; do printf "${i}_FILTER, " | tr '[a-z]' '[A-Z]'; done)"
-AC_DEFINE_UNQUOTED(FILTER_ENUM, $enum NUM_SUPPORTED_FILTERS,
- enum of supported filters)
-inits="$(for i in $filters; do printf 'extern void '$i'_filter_init(struct filter *f); '; done)"
-AC_DEFINE_UNQUOTED(DECLARE_FILTER_INITS, $inits, init functions of the supported filters)
-array="$(for i in $filters; do printf '{.name = "'$i'", .init = '$i'_filter_init},'; done)"
-AC_DEFINE_UNQUOTED(FILTER_ARRAY, $array, array of supported filters)
########################################################################## recv
-recv_cmdline_objs="
- recv
- http_recv
- dccp_recv
- udp_recv
- afh_recv
-"
-
recv_errlist_objs="
http_recv
recv_common
fd
sched
stdout
- ggo
udp_recv
buffer_tree
afh_recv
NEED_OPUS_OBJECTS && recv_errlist_objs="$recv_errlist_objs opus_afh opus_common"
NEED_FLAC_OBJECTS && recv_errlist_objs="$recv_errlist_objs flac_afh"
-if test $HAVE_FAAD = yes -a $HAVE_MP4V2 = yes; then
- recv_errlist_objs="$recv_errlist_objs aac_afh aac_common"
+if test $HAVE_FAAD = yes; then
+ recv_errlist_objs="$recv_errlist_objs aac_afh"
fi
-recv_objs="add_cmdline($recv_cmdline_objs) $recv_errlist_objs"
-AC_SUBST(receivers, "http dccp udp afh")
+recv_objs="$recv_errlist_objs"
AC_SUBST(recv_objs, add_dot_o($recv_objs))
########################################################################### afh
audio_format_handlers="mp3 wma"
-afh_cmdline_objs="afh"
afh_errlist_objs="
afh
string
wma_afh
wma_common
version
- ggo
"
NEED_OGG_OBJECTS && afh_errlist_objs="$afh_errlist_objs ogg_afh_common"
NEED_VORBIS_OBJECTS && {
afh_errlist_objs="$afh_errlist_objs flac_afh"
audio_format_handlers="$audio_format_handlers flac"
}
-if test $HAVE_FAAD = yes -a $HAVE_MP4V2 = yes; then
- afh_errlist_objs="$afh_errlist_objs aac_afh aac_common"
+if test $HAVE_FAAD = yes; then
+ afh_errlist_objs="$afh_errlist_objs aac_afh"
audio_format_handlers="$audio_format_handlers aac"
fi
-afh_objs="add_cmdline($afh_cmdline_objs) $afh_errlist_objs"
+afh_objs="$afh_errlist_objs"
AC_SUBST(afh_objs, add_dot_o($afh_objs))
########################################################################## play
play
fd
sched
- ggo
buffer_tree
time
string
version
sync_filter
"
-play_cmdline_objs="
- http_recv
- dccp_recv
- udp_recv
- afh_recv
- compress_filter
- amp_filter
- prebuffer_filter
- file_write
- play
- sync_filter
-"
-if test "$have_core_audio" = "yes"; then
- play_errlist_objs="$play_errlist_objs osx_write ipc"
- play_cmdline_objs="$play_cmdline_objs osx_write"
-fi
NEED_OGG_OBJECTS && play_errlist_objs="$play_errlist_objs ogg_afh_common"
NEED_VORBIS_OBJECTS && {
play_errlist_objs="$play_errlist_objs oggdec_filter ogg_afh"
play_errlist_objs="$play_errlist_objs flacdec_filter flac_afh"
}
if test $HAVE_FAAD = yes; then
- play_errlist_objs="$play_errlist_objs aacdec_filter"
-fi
-if test $HAVE_MP4V2 = yes; then
- play_errlist_objs="$play_errlist_objs aac_afh"
-fi
-if test $HAVE_MP4V2 = yes || test $HAVE_FAAD = yes; then
- play_errlist_objs="$play_errlist_objs aac_common"
+ play_errlist_objs="$play_errlist_objs aac_afh aacdec_filter"
fi
if test $HAVE_MAD = yes; then
- play_cmdline_objs="$play_cmdline_objs mp3dec_filter"
play_errlist_objs="$play_errlist_objs mp3dec_filter"
fi
if test $HAVE_OSS = yes; then
play_errlist_objs="$play_errlist_objs oss_write"
- play_cmdline_objs="$play_cmdline_objs oss_write"
fi
if test $HAVE_ALSA = yes; then
play_errlist_objs="$play_errlist_objs alsa_write"
- play_cmdline_objs="$play_cmdline_objs alsa_write"
fi
NEED_AO_OBJECTS && {
play_errlist_objs="$play_errlist_objs ao_write"
- play_cmdline_objs="$play_cmdline_objs ao_write"
}
if test $HAVE_READLINE = yes; then
play_errlist_objs="$play_errlist_objs interactive"
fi
if test $HAVE_SAMPLERATE = yes; then
play_errlist_objs="$play_errlist_objs resample_filter check_wav"
- play_cmdline_objs="$play_cmdline_objs resample_filter"
fi
-play_objs="add_cmdline($play_cmdline_objs) $play_errlist_objs"
+play_objs="$play_errlist_objs"
AC_SUBST(play_objs, add_dot_o($play_objs))
######################################################################### write
-write_cmdline_objs="
- write
- file_write
-"
write_errlist_objs="
write
write_common
sched
stdin
buffer_tree
- ggo
check_wav
version
"
-writers="file"
-default_writer="FILE_WRITE"
-if test "$have_core_audio" = "yes"; then
- write_errlist_objs="$write_errlist_objs osx_write ipc"
- write_cmdline_objs="$write_cmdline_objs osx_write"
- writers="$writers osx"
- default_writer="OSX_WRITE"
-fi
NEED_AO_OBJECTS && {
write_errlist_objs="$write_errlist_objs ao_write"
- write_cmdline_objs="$write_cmdline_objs ao_write"
- writers="$writers ao"
- default_writer="AO_WRITE"
}
if test $HAVE_OSS = yes; then
write_errlist_objs="$write_errlist_objs oss_write"
- write_cmdline_objs="$write_cmdline_objs oss_write"
- writers="$writers oss"
- default_writer="OSS_WRITE"
fi
if test $HAVE_ALSA = yes; then
write_errlist_objs="$write_errlist_objs alsa_write"
- write_cmdline_objs="$write_cmdline_objs alsa_write"
- writers="$writers alsa"
- default_writer="ALSA_WRITE"
fi
-AC_SUBST(writers)
-write_objs="add_cmdline($write_cmdline_objs) $write_errlist_objs"
+write_objs="$write_errlist_objs"
AC_SUBST(write_objs, add_dot_o($write_objs))
-enum="$(for i in $writers; do printf "${i}_WRITE, " | tr '[a-z]' '[A-Z]'; done)"
-AC_DEFINE_UNQUOTED(WRITER_ENUM, $enum NUM_SUPPORTED_WRITERS,
- enum of supported writers)
-AC_DEFINE_UNQUOTED(DEFAULT_WRITER, $default_writer, use this writer if none was specified)
-names="$(for i in $writers; do printf \"$i\",' ' ; done)"
-AC_DEFINE_UNQUOTED(WRITER_NAMES, $names, supported writer names)
-inits="$(for i in $writers; do printf 'extern void '$i'_write_init(struct writer *); '; done)"
-AC_DEFINE_UNQUOTED(DECLARE_WRITER_INITS, $inits, init functions of the supported writers)
-array="$(for i in $writers; do printf '{.init = '$i'_write_init},'; done)"
-AC_DEFINE_UNQUOTED(WRITER_ARRAY, $array, array of supported writers)
######################################################################## audioc
-audioc_cmdline_objs="audioc"
audioc_errlist_objs="
audioc
string
net
fd
version
- ggo
"
if test $HAVE_READLINE = yes; then
audioc_errlist_objs="$audioc_errlist_objs
time
"
fi
-audioc_objs="add_cmdline($audioc_cmdline_objs) $audioc_errlist_objs"
+audioc_objs="$audioc_errlist_objs"
AC_SUBST(audioc_objs, add_dot_o($audioc_objs))
################################################################## status items
offset seconds_total stream_start current_time audiod_uptime image_id
lyrics_id duration directory lyrics_name image_name path hash channels
last_played num_chunks chunk_time amplification artist title year album
-comment"
+comment max_chunk_size"
result=
for i in $status_items; do
readline (interactive CLIs): $HAVE_READLINE
id3 version 2 support: $HAVE_ID3TAG
faad: $HAVE_FAAD
-mp4v2: $HAVE_MP4V2
-
audio format handlers: $audio_format_handlers
-filters: $(echo $filters)
-writers: $writers
para_server: $build_server
para_gui: $build_gui
-para_fade: $build_fade
+para_mixer: $build_mixer
para_client: $build_client
para_audiod: $build_audiod
])
#include <sys/socket.h>
#include <openssl/rand.h>
#include <openssl/err.h>
-#include <openssl/rc4.h>
#include <openssl/pem.h>
#include <openssl/sha.h>
#include <openssl/bn.h>
srandom(seed);
}
-static EVP_PKEY *load_key(const char *file, int private)
+static int get_private_key(const char *path, RSA **rsa)
{
- BIO *key;
- EVP_PKEY *pkey = NULL;
- int ret = check_key_file(file, private);
-
- if (ret < 0) {
- PARA_ERROR_LOG("%s\n", para_strerror(-ret));
- return NULL;
- }
- key = BIO_new(BIO_s_file());
- if (!key)
- return NULL;
- if (BIO_read_filename(key, file) > 0) {
- if (private == LOAD_PRIVATE_KEY)
- pkey = PEM_read_bio_PrivateKey(key, NULL, NULL, NULL);
- else
- pkey = PEM_read_bio_PUBKEY(key, NULL, NULL, NULL);
- }
- BIO_free(key);
- return pkey;
-}
-
-static int get_openssl_key(const char *key_file, RSA **rsa, int private)
-{
- EVP_PKEY *key = load_key(key_file, private);
-
- if (!key)
- return (private == LOAD_PRIVATE_KEY)? -E_PRIVATE_KEY
- : -E_PUBLIC_KEY;
- *rsa = EVP_PKEY_get1_RSA(key);
- EVP_PKEY_free(key);
- if (!*rsa)
- return -E_RSA;
- return RSA_size(*rsa);
+ EVP_PKEY *pkey;
+ BIO *bio = BIO_new(BIO_s_file());
+
+ *rsa = NULL;
+ if (!bio)
+ return -E_PRIVATE_KEY;
+ if (BIO_read_filename(bio, path) <= 0)
+ goto bio_free;
+ pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+ if (!pkey)
+ goto bio_free;
+ *rsa = EVP_PKEY_get1_RSA(pkey);
+ EVP_PKEY_free(pkey);
+bio_free:
+ BIO_free(bio);
+ return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY;
}
/*
return ret;
}
-int get_asymmetric_key(const char *key_file, int private,
- struct asymmetric_key **result)
+int get_public_key(const char *key_file, struct asymmetric_key **result)
{
struct asymmetric_key *key = NULL;
void *map = NULL;
char *cp;
key = para_malloc(sizeof(*key));
- if (private) {
- ret = get_openssl_key(key_file, &key->rsa, LOAD_PRIVATE_KEY);
- goto out;
- }
ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
if (ret < 0)
goto out;
ret = is_ssh_rsa_key(map, map_size);
if (!ret) {
- ret = para_munmap(map, map_size);
- map = NULL;
- if (ret < 0)
- goto out;
- ret = get_openssl_key(key_file, &key->rsa, LOAD_PUBLIC_KEY);
- goto out;
+ para_munmap(map, map_size);
+ return -E_SSH_PARSE;
}
cp = map + ret;
encoded_size = map_size - ret;
return ret;
}
-void free_asymmetric_key(struct asymmetric_key *key)
+void free_public_key(struct asymmetric_key *key)
{
if (!key)
return;
struct asymmetric_key *priv;
int ret;
+ ret = check_private_key_file(key_file);
+ if (ret < 0)
+ return ret;
if (inlen < 0)
return -E_RSA;
- ret = get_asymmetric_key(key_file, LOAD_PRIVATE_KEY, &priv);
- if (ret < 0)
+ priv = para_malloc(sizeof(*priv));
+ ret = get_private_key(key_file, &priv->rsa);
+ if (ret < 0) {
+ free(priv);
return ret;
+ }
/*
* RSA is vulnerable to timing attacks. Generate a random blinding
* factor to protect against this kind of attack.
if (ret <= 0)
ret = -E_DECRYPT;
out:
- free_asymmetric_key(priv);
+ RSA_free(priv->rsa);
+ free(priv);
return ret;
}
}
struct stream_cipher {
- bool use_aes;
- union {
- RC4_KEY rc4_key;
- EVP_CIPHER_CTX *aes;
- } context;
+ EVP_CIPHER_CTX *aes;
};
-struct stream_cipher *sc_new(const unsigned char *data, int len,
- bool use_aes)
+struct stream_cipher *sc_new(const unsigned char *data, int len)
{
struct stream_cipher *sc = para_malloc(sizeof(*sc));
- sc->use_aes = use_aes;
- if (!use_aes) {
- RC4_set_key(&sc->context.rc4_key, len, data);
- return sc;
- }
assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
- sc->context.aes = EVP_CIPHER_CTX_new();
- EVP_EncryptInit_ex(sc->context.aes, EVP_aes_128_ctr(), NULL, data,
+ sc->aes = EVP_CIPHER_CTX_new();
+ EVP_EncryptInit_ex(sc->aes, EVP_aes_128_ctr(), NULL, data,
data + AES_CRT128_BLOCK_SIZE);
return sc;
}
{
if (!sc)
return;
- EVP_CIPHER_CTX_free(sc->context.aes);
+ EVP_CIPHER_CTX_free(sc->aes);
free(sc);
}
-/**
- * The RC4() implementation of openssl apparently reads and writes data in
- * blocks of 8 bytes. So we have to make sure our buffer sizes are a multiple
- * of this.
- */
-#define RC4_ALIGN 8
-
-static void rc4_crypt(RC4_KEY *key, struct iovec *src, struct iovec *dst)
-{
- size_t len = src->iov_len, l1, l2;
-
- assert(len > 0);
- assert(len < ((typeof(src->iov_len))-1) / 2);
- l1 = ROUND_DOWN(len, RC4_ALIGN);
- l2 = ROUND_UP(len, RC4_ALIGN);
-
- *dst = (typeof(*dst)) {
- /* Add one for the terminating zero byte. */
- .iov_base = para_malloc(l2 + 1),
- .iov_len = len
- };
- RC4(key, l1, src->iov_base, dst->iov_base);
- if (len > l1) {
- unsigned char remainder[RC4_ALIGN] = "";
- memcpy(remainder, src->iov_base + l1, len - l1);
- RC4(key, len - l1, remainder, dst->iov_base + l1);
- }
- ((char *)dst->iov_base)[len] = '\0';
-}
-
static void aes_ctr128_crypt(EVP_CIPHER_CTX *ctx, struct iovec *src,
struct iovec *dst)
{
void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
{
- if (sc->use_aes)
- return aes_ctr128_crypt(sc->context.aes, src, dst);
- return rc4_crypt(&sc->context.rc4_key, src, dst);
+ return aes_ctr128_crypt(sc->aes, src, dst);
}
void hash_function(const char *data, unsigned long len, unsigned char *hash)
* Read an asymmetric key from a file.
*
* \param key_file The file containing the key.
- * \param private if non-zero, read the private key, otherwise the public key.
* \param result The key structure is returned here.
*
* \return The size of the key on success, negative on errors.
*/
-int get_asymmetric_key(const char *key_file, int private,
- struct asymmetric_key **result);
+int get_public_key(const char *key_file, struct asymmetric_key **result);
/**
- * Deallocate an asymmetric key structure.
+ * Deallocate a public key.
*
* \param key Pointer to the key structure to free.
*
- * This must be called for any key obtained by get_asymmetric_key().
+ * This should be called for keys obtained by get_public_key() if the key is no
+ * longer needed.
*/
-void free_asymmetric_key(struct asymmetric_key *key);
+void free_public_key(struct asymmetric_key *key);
/**
};
/**
- * Allocate and initialize a stream cipher structure.
+ * Allocate and initialize an aes_ctr128 stream cipher structure.
*
* \param data The key.
* \param len The size of the key.
- * \param use_aes True: Use the aes_ctr128 stream cipher, false: Use RC4.
*
* \return A new stream cipher structure.
*/
-struct stream_cipher *sc_new(const unsigned char *data, int len,
- bool use_aes);
+struct stream_cipher *sc_new(const unsigned char *data, int len);
/**
* Encrypt or decrypt a buffer using a stream cipher.
size_t is_ssh_rsa_key(char *data, size_t size);
uint32_t read_ssh_u32(const void *vp);
int check_ssh_key_header(const unsigned char *blob, int blen);
-int check_key_file(const char *file, bool private_key);
+int check_private_key_file(const char *file);
}
/**
- * Check existence and permissions of a key file.
+ * Check existence and permissions of a private key file.
*
* \param file The path of the key file.
- * \param private_key Whether this is a private key.
*
- * This checks whether the file exists. If it is a private key, we additionally
- * check that the permissions are restrictive enough. It is considered an error
- * if we own the file and it is readable for others.
+ * This checks whether the file exists and its permissions are restrictive
+ * enough. It is considered an error if we own the file and it is readable for
+ * others.
*
* \return Standard.
*/
-int check_key_file(const char *file, bool private_key)
+int check_private_key_file(const char *file)
{
struct stat st;
if (stat(file, &st) != 0)
return -ERRNO_TO_PARA_ERROR(errno);
- if (!private_key)
- return 0;
if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0)
return -E_KEY_PERM;
return 1;
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
+#include "recv_cmd.lsg.h"
#include "para.h"
#include "error.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "recv.h"
#include "string.h"
#include "net.h"
#include "fd.h"
-#include "dccp_recv.cmdline.h"
-
static void dccp_recv_close(struct receiver_node *rn)
{
if (rn->fd > 0)
btr_pool_free(rn->btrp);
}
+/* Check whether the host supports the requested 'ccid' arguments. */
+static int dccp_recv_ccid_support_check(const struct lls_parse_result *lpr)
+{
+ uint8_t *ccids;
+ int i, j, ret, nccids;
+ unsigned given = RECV_CMD_OPT_GIVEN(DCCP, CCID, lpr);
+
+ ret = dccp_available_ccids(&ccids);
+ if (ret < 0)
+ return ret;
+ nccids = ret;
+ for (i = 0; i < given; i++) {
+ uint32_t val = lls_uint32_val(i,
+ RECV_CMD_OPT_RESULT(DCCP, CCID, lpr));
+ for (j = 0; j < nccids && ccids[j] != val; j++)
+ ;
+ if (j == nccids) {
+ PARA_ERROR_LOG("'CCID-%u' not supported on this host\n",
+ val);
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ }
+ }
+ return 1;
+}
+
static int dccp_recv_open(struct receiver_node *rn)
{
- struct dccp_recv_args_info *conf = rn->conf;
+ struct lls_parse_result *lpr = rn->lpr;
struct flowopts *fo = NULL;
uint8_t *ccids = NULL;
int fd, ret, i;
+ const struct lls_opt_result *r_c = RECV_CMD_OPT_RESULT(DCCP, CCID, lpr);
+ const char *host = RECV_CMD_OPT_STRING_VAL(DCCP, HOST, lpr);
+ uint32_t port = RECV_CMD_OPT_UINT32_VAL(DCCP, PORT, lpr);
+ unsigned given;
+ ret = dccp_recv_ccid_support_check(lpr);
+ if (ret < 0)
+ return ret;
/* Copy CCID preference list (u8 array required) */
- if (conf->ccid_given) {
- ccids = para_malloc(conf->ccid_given);
- fo = flowopt_new();
-
- for (i = 0; i < conf->ccid_given; i++)
- ccids[i] = conf->ccid_arg[i];
-
+ given = lls_opt_given(r_c);
+ if (given) {
+ ccids = para_malloc(given);
+ fo = flowopt_new();
+ for (i = 0; i < given; i++)
+ ccids[i] = lls_int32_val(i, r_c);
OPT_ADD(fo, SOL_DCCP, DCCP_SOCKOPT_CCID, ccids, i);
}
- fd = makesock(IPPROTO_DCCP, 0, conf->host_arg, conf->port_arg, fo);
+ fd = makesock(IPPROTO_DCCP, 0, host, port, fo);
flowopt_cleanup(fo);
free(ccids);
if (fd < 0)
return ret;
}
-/**
- * Check whether the host supports the requested 'ccid' arguments.
- * \param conf DCCP receiver arguments.
- * \return True if all CCIDs requested in \a conf are supported.
- */
-static bool dccp_recv_ccid_support_check(struct dccp_recv_args_info *conf)
-{
- uint8_t *ccids;
- int i, j, nccids;
-
- nccids = dccp_available_ccids(&ccids);
- if (nccids <= 0)
- return false;
-
- for (i = 0; i < conf->ccid_given; i++) {
- for (j = 0; j < nccids && ccids[j] != conf->ccid_arg[i]; j++)
- ;
- if (j == nccids) {
- PARA_ERROR_LOG("'CCID-%d' not supported on this host.\n",
- conf->ccid_arg[i]);
- return false;
- }
- }
- return true;
-}
-
-static void *dccp_recv_parse_config(int argc, char **argv)
-{
- struct dccp_recv_args_info *tmp = para_calloc(sizeof(*tmp));
-
- 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, void *context)
{
struct receiver_node *rn = context;
return ret;
}
-static void dccp_recv_free_config(void *conf)
-{
- dccp_recv_cmdline_parser_free(conf);
- free(conf);
-}
-
-/**
- * The init function of the dccp receiver.
- *
- * \param r Pointer to the receiver struct to initialize.
- *
- * Initialize all function pointers of \a r.
- */
-void dccp_recv_init(struct receiver *r)
-{
- struct dccp_recv_args_info dummy;
-
- dccp_recv_cmdline_parser_init(&dummy);
- r->open = dccp_recv_open;
- r->close = dccp_recv_close;
- r->pre_select = dccp_recv_pre_select;
- 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)DEFINE_GGO_HELP(dccp_recv);
- dccp_recv_cmdline_parser_free(&dummy);
-}
+const struct receiver lsg_recv_cmd_com_dccp_user_data = {
+ .open = dccp_recv_open,
+ .close = dccp_recv_close,
+ .pre_select = dccp_recv_pre_select,
+ .post_select = dccp_recv_post_select,
+};
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
+#include "server.lsg.h"
#include "para.h"
#include "error.h"
#include "string.h"
#include "fd.h"
#include "close_on_fork.h"
#include "chunk_queue.h"
-#include "server.cmdline.h"
#include "acl.h"
static struct sender_status dccp_sender_status, *dss = &dccp_sender_status;
{
int mps, ret;
socklen_t ml = sizeof(mps);
+ uint32_t mss; /* max slize size */
/* If call fails, return some sensible minimum value */
ret = getsockopt(sc->fd, SOL_DCCP, DCCP_SOCKOPT_GET_CUR_MPS, &mps, &ml);
}
PARA_INFO_LOG("current MPS = %d bytes\n", mps);
assert(mps > 0);
- if (conf.dccp_max_slice_size_arg > 0)
- mps = PARA_MIN(mps, conf.dccp_max_slice_size_arg);
+ mss = OPT_UINT32_VAL(DCCP_MAX_SLICE_SIZE);
+ if (mss > 0 && mss <= INT_MAX)
+ mps = PARA_MIN(mps, (int)mss);
return mps;
}
struct sender_client *sc;
struct dccp_fec_client *dfc;
int tx_ccid;
+ uint32_t k, n;
sc = accept_sender_client(dss, rfds);
if (!sc)
}
dfc = para_calloc(sizeof(*dfc));
sc->private_data = dfc;
- dfc->fcp.data_slices_per_group = conf.dccp_data_slices_per_group_arg;
- dfc->fcp.slices_per_group = conf.dccp_slices_per_group_arg;
+ k = OPT_UINT32_VAL(DCCP_DATA_SLICES_PER_GROUP);
+ n = OPT_UINT32_VAL(DCCP_SLICES_PER_GROUP);
+ if (k == 0 || n == 0 || k >= n) {
+ PARA_WARNING_LOG("invalid FEC parameters, using defaults\n");
+ dfc->fcp.data_slices_per_group = 3;
+ dfc->fcp.slices_per_group = 4;
+ } else {
+ dfc->fcp.data_slices_per_group = k;
+ dfc->fcp.slices_per_group = n;
+ }
dfc->fcp.init_fec = dccp_init_fec;
dfc->fcp.send_fec = dccp_send_fec;
dfc->fcp.need_periodic_header = false;
*/
void dccp_send_init(struct sender *s)
{
- int ret, k, n;
+ int ret;
s->status = dccp_status;
s->send = NULL;
s->client_cmds[SENDER_add] = NULL;
s->client_cmds[SENDER_delete] = NULL;
- k = conf.dccp_data_slices_per_group_arg;
- n = conf.dccp_slices_per_group_arg;
-
- if (k <= 0 || n <= 0 || k >= n) {
- PARA_WARNING_LOG("invalid FEC parameters, using defaults\n");
- conf.dccp_data_slices_per_group_arg = 3;
- conf.dccp_slices_per_group_arg = 4;
- }
-
- init_sender_status(dss, conf.dccp_access_arg, conf.dccp_access_given,
- conf.dccp_port_arg, conf.dccp_max_clients_arg,
- conf.dccp_default_deny_given);
+ init_sender_status(dss, OPT_RESULT(DCCP_ACCESS),
+ OPT_UINT32_VAL(DCCP_PORT), OPT_UINT32_VAL(DCCP_MAX_CLIENTS),
+ OPT_GIVEN(DCCP_DEFAULT_DENY));
ret = generic_com_on(dss, IPPROTO_DCCP);
if (ret < 0)
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
/** Codes and messages. */
#define PARA_ERRORS \
- PARA_ERROR(AAC_AFH_INIT, "failed to init aac decoder"), \
PARA_ERROR(AACDEC_INIT, "failed to init aac decoder"), \
PARA_ERROR(AAC_DECODE, "aac decode error"), \
PARA_ERROR(ACL_PERM, "access denied by acl"), \
- PARA_ERROR(ADD_CALLBACK, "can not add callback"), \
PARA_ERROR(ADDRESS_LOOKUP, "can not resolve requested address"),\
PARA_ERROR(AFH_RECV_BAD_FILENAME, "no file name given"), \
PARA_ERROR(AFS_SHORT_READ, "short read from afs socket"), \
PARA_ERROR(BAD_BAND, "invalid or unexpected band designator"), \
PARA_ERROR(BAD_CHANNEL_COUNT, "channel count not supported"), \
PARA_ERROR(BAD_CHANNEL, "invalid channel"), \
- PARA_ERROR(BAD_CMD, "invalid command"), \
- PARA_ERROR(BAD_CONFIG, "syntax error in config file"), \
PARA_ERROR(BAD_CT, "invalid chunk table or bad FEC configuration"), \
PARA_ERROR(BAD_FEATURE, "invalid feature request"), \
PARA_ERROR(BAD_FEC_HEADER, "invalid fec header"), \
- PARA_ERROR(BAD_FILTER_OPTIONS, "invalid filter option given"), \
PARA_ERROR(BAD_LL, "invalid loglevel"), \
PARA_ERROR(BAD_PATH, "invalid path"), \
- PARA_ERROR(BAD_PLAY_CMD, "invalid command"), \
PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \
PARA_ERROR(BAD_SAMPLE_FORMAT, "sample format not supported"), \
PARA_ERROR(BAD_SAMPLERATE, "sample rate not supported"), \
PARA_ERROR(CREATE_OPUS_DECODER, "could not create opus decoder"), \
PARA_ERROR(DCCP_OVERRUN, "dccp output buffer buffer overrun"), \
PARA_ERROR(DECRYPT, "decrypt error"), \
- PARA_ERROR(DEFAULT_COMP, "can not find default audio output component"), \
PARA_ERROR(DUMMY_ROW, "attempted to access blob dummy object"), \
PARA_ERROR(DUP_PIPE, "exec error: can not create pipe"), \
PARA_ERROR(EMPTY, "file is empty"), \
PARA_ERROR(ENCRYPT, "encrypt error"), \
PARA_ERROR(EOF, "end of file"), \
- PARA_ERROR(ESDS, "did not find esds atom"), \
PARA_ERROR(FEC_BAD_IDX, "invalid index vector"), \
PARA_ERROR(FECDEC_EOF, "received eof packet"), \
PARA_ERROR(FECDEC_OVERRUN, "fecdec output buffer overrun"), \
PARA_ERROR(ID3_SETENCODING, "could not set id3 text encoding field"), \
PARA_ERROR(ID3_SETSTRING, "could not set id3 string field"), \
PARA_ERROR(INCOHERENT_BLOCK_LEN, "incoherent block length"), \
- PARA_ERROR(INVALID_AUDIOD_CMD, "invalid command"), \
PARA_ERROR(KEY_MARKER, "invalid/missing key header or footer"), \
PARA_ERROR(KEY_PERM, "unprotected private key"), \
PARA_ERROR(LIBSAMPLERATE, "secret rabbit code error"), \
PARA_ERROR(MP3DEC_CORRUPT, "too many corrupt frames"), \
PARA_ERROR(MP3DEC_EOF, "mp3dec: end of file"), \
PARA_ERROR(MP3_INFO, "could not read mp3 info"), \
- PARA_ERROR(MP4ASC, "audio spec config error"), \
- PARA_ERROR(MP4V2, "mp4v2 library error"), \
+ PARA_ERROR(MP4FF_BAD_CHANNEL_COUNT, "mp4ff: invalid number of channels"), \
+ PARA_ERROR(MP4FF_BAD_SAMPLE, "mp4ff: invalid sample number"), \
+ PARA_ERROR(MP4FF_BAD_SAMPLERATE, "mp4ff: invalid sample rate"), \
+ PARA_ERROR(MP4FF_BAD_SAMPLE_COUNT, "mp4ff: invalid number of samples"), \
+ PARA_ERROR(MP4FF_META_READ, "mp4ff: could not read mp4 metadata"), \
+ PARA_ERROR(MP4FF_META_WRITE, "mp4ff: could not update mp4 metadata"), \
+ PARA_ERROR(MP4FF_OPEN, "mp4ff: open failed"), \
+ PARA_ERROR(MP4FF_TRACK, "mp4fF: no audio track"), \
PARA_ERROR(MPI_PRINT, "could not convert multi-precision integer"), \
PARA_ERROR(MPI_SCAN, "could not scan multi-precision integer"), \
PARA_ERROR(NAME_TOO_LONG, "name too long for struct sockaddr_un"), \
PARA_ERROR(NO_AFHI, "audio format handler info required"), \
PARA_ERROR(NO_ATTRIBUTES, "no attributes defined yet"), \
PARA_ERROR(NO_AUDIO_FILE, "no audio file"), \
- PARA_ERROR(NO_CONFIG, "config file not found"), \
PARA_ERROR(NOFD, "did not receive open fd from afs"), \
- PARA_ERROR(NO_FILTERS, "at least one filter must be given"), \
PARA_ERROR(NO_MATCH, "no matches"), \
PARA_ERROR(NO_MOOD, "no mood available"), \
PARA_ERROR(NO_MORE_SLOTS, "no more empty slots"), \
PARA_ERROR(NOT_PLAYING, "not playing"), \
PARA_ERROR(NO_VALID_FILES, "no valid file found in playlist"), \
PARA_ERROR(NO_WMA, "asf/wma format not recognized"), \
- PARA_ERROR(OEAP, "error during oeap (un)padding"), \
PARA_ERROR(OGGDEC_BADHEADER, "invalid vorbis bitstream header"), \
PARA_ERROR(OGGDEC_BADLINK, "invalid stream section or requested link corrupt"), \
PARA_ERROR(OGGDEC_FAULT, "bug or heap/stack corruption"), \
PARA_ERROR(OGG_PACKET_IN, "ogg_stream_packetin() failed"), \
PARA_ERROR(OGG_STREAM_FLUSH, "ogg_stream_flush() failed"), \
PARA_ERROR(OGG_SYNC, "internal ogg storage overflow"), \
- PARA_ERROR(OPEN_COMP, "OpenAComponent() error"), \
PARA_ERROR(OPUS_COMMENT, "invalid or corrupted opus comment"), \
PARA_ERROR(OPUS_DECODE, "opus decode error"), \
PARA_ERROR(OPUS_HEADER, "invalid opus header"), \
PARA_ERROR(PERM, "permission denied"), \
PARA_ERROR(PLAYLIST_EMPTY, "attempted to load empty playlist"), \
PARA_ERROR(PLAYLIST_LOADED, ""), /* not really an error */ \
- PARA_ERROR(PLAY_SYNTAX, "para_play: syntax error"), \
PARA_ERROR(PREBUFFER_SUCCESS, "prebuffering complete"), \
PARA_ERROR(PRIVATE_KEY, "can not read private key"), \
- PARA_ERROR(PUBLIC_KEY, "can not read public key"), \
PARA_ERROR(QUEUE, "packet queue overrun"), \
PARA_ERROR(READ_PATTERN, "did not read expected pattern"), \
PARA_ERROR(RECV_EOF, "end of file"), \
PARA_ERROR(RECVMSG, "recvmsg() failed"), \
- PARA_ERROR(RECV_SYNTAX, "recv syntax error"), \
PARA_ERROR(REGEX, "regular expression error"), \
PARA_ERROR(RESAMPLE_EOF, "resample filter: end of file"), \
PARA_ERROR(RSA, "RSA error"), \
+ PARA_ERROR(RSA_DECODE, "RSA decoding error"), \
PARA_ERROR(SB_PACKET_SIZE, "invalid sideband packet size or protocol error"), \
PARA_ERROR(SCM_CREDENTIALS, "did not receive SCM credentials"), \
PARA_ERROR(SENDER_CMD, "command not supported by this sender"), \
PARA_ERROR(SSH_PARSE, "could not parse ssh public key"), \
PARA_ERROR(STAT_ITEM_PARSE, "failed to parse status item"), \
PARA_ERROR(STATUS_TIMEOUT, "status item timeout"), \
- PARA_ERROR(STCO, "did not find stco atom"), \
- PARA_ERROR(STREAM_FORMAT, "could not set stream format"), \
PARA_ERROR(STREAM_PACKETIN, "ogg stream packet-in error"), \
PARA_ERROR(STREAM_PACKETOUT, "ogg stream packet-out error"), \
PARA_ERROR(STREAM_PAGEIN, "ogg stream page-in error"), \
PARA_ERROR(STREAM_PAGEOUT, "ogg stream page-out error"), \
PARA_ERROR(STRFTIME, "strftime() failed"), \
- PARA_ERROR(STSZ, "did not find stcz atom"), \
PARA_ERROR(SYNC_COMPLETE, "all buddies in sync"), \
PARA_ERROR(SYNC_LISTEN_FD, "no fd to listen on"), \
PARA_ERROR(SYNC_PAGEOUT, "ogg sync page-out error (no ogg file?)"), \
PARA_ERROR(TOO_MANY_CLIENTS, "maximal number of stat clients exceeded"), \
PARA_ERROR(UCRED_PERM, "permission denied"), \
PARA_ERROR(UDP_OVERRUN, "output buffer overrun"), \
- PARA_ERROR(UNIT_INIT, "AudioUnitInitialize() error"), \
- PARA_ERROR(UNIT_START, "AudioUnitStart() error"), \
PARA_ERROR(UNKNOWN_STAT_ITEM, "status item not recognized"), \
PARA_ERROR(UNSUPPORTED_AUDIO_FORMAT, "given audio format not supported"), \
PARA_ERROR(UNSUPPORTED_FILTER, "given filter not supported"), \
*/
#define OSL_ERROR_BIT 29
+#define LLS_ERROR_BIT 28
+
/** Check whether the system error bit is set. */
#define IS_SYSTEM_ERROR(num) (!!((num) & (1 << SYSTEM_ERROR_BIT)))
/** Check whether the osl error bit is set. */
#define IS_OSL_ERROR(num) (!!((num) & (1 << OSL_ERROR_BIT)))
+/** Check whether the lopsub error bit is set. */
+#define IS_LLS_ERROR(num) (!!((num) & (1 << LLS_ERROR_BIT)))
+
/** Set the system error bit for the given number. */
#define ERRNO_TO_PARA_ERROR(num) ((num) | (1 << SYSTEM_ERROR_BIT))
/** Set the osl error bit for the given number. */
#define OSL_ERRNO_TO_PARA_ERROR(num) ((num) | (1 << OSL_ERROR_BIT))
+/** Set the lopsub error bit for the error code. */
+#define LLS_ERRNO_TO_PARA_ERROR(num) ((num) | (1 << LLS_ERROR_BIT))
+
static const char *weak_osl_strerror(int) __attribute__ ((weakref("osl_strerror")));
+static const char *weak_lls_strerror(int) __attribute__ ((weakref("lls_strerror")));
/**
* Paraslash's version of strerror(3).
*
assert(weak_osl_strerror);
return weak_osl_strerror(num & ~(1U << OSL_ERROR_BIT));
}
+ if (IS_LLS_ERROR(num)) {
+ assert(weak_lls_strerror);
+ return weak_lls_strerror(num & ~(1U << LLS_ERROR_BIT));
+ }
if (IS_SYSTEM_ERROR(num))
return strerror(num & ~(1U << SYSTEM_ERROR_BIT));
return para_errlist[num];
return ret;
return -OSL_ERRNO_TO_PARA_ERROR(-ret);
}
+
+/**
+ * Wrapper for lopsub library calls.
+ *
+ * \param ret See osl().
+ * \return See osl().
+ */
+_static_inline_ int lls(int ret)
+{
+ if (ret >= 0)
+ return ret;
+ return -LLS_ERRNO_TO_PARA_ERROR(-ret);
+}
+++ /dev/null
-/*
- * Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file fade.c A volume fader and alarm clock for OSS. */
-
-#include <regex.h>
-
-#include "fade.cmdline.h"
-#include "para.h"
-#include "fd.h"
-#include "string.h"
-#include "mix.h"
-#include "error.h"
-#include "ggo.h"
-#include "version.h"
-
-/** Array of error strings. */
-DEFINE_PARA_ERRLIST;
-
-static struct fade_args_info conf;
-
-enum mixer_id {MIXER_ENUM};
-static char *mixer_name[] = {MIXER_NAMES};
-DECLARE_MIXER_INITS;
-static struct mixer supported_mixer[] = {MIXER_ARRAY};
-#define FOR_EACH_MIXER(i) for ((i) = 0; (i) < NUM_SUPPORTED_MIXERS; (i)++)
-
-static int loglevel;
-static __printf_2_3 void date_log(int ll, const char *fmt, ...)
-{
- va_list argp;
- time_t t1;
- struct tm *tm;
-
- if (ll < loglevel)
- return;
- time(&t1);
- tm = localtime(&t1);
- fprintf(stderr, "%d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec);
- va_start(argp, fmt);
- vprintf(fmt, argp);
- va_end(argp);
-}
-__printf_2_3 void (*para_log)(int, const char*, ...) = date_log;
-
-static int set_channel(struct mixer *m, struct mixer_handle *h, const char *channel)
-{
-
- PARA_NOTICE_LOG("using %s mixer channel\n", channel?
- channel : "default");
- return m->set_channel(h, channel);
-}
-
-/* Fade to new volume in fade_time seconds. */
-static int fade(struct mixer *m, struct mixer_handle *h, int new_vol, int fade_time)
-{
- int vol, diff, incr, ret;
- unsigned secs;
- struct timespec ts;
- unsigned long long tmp, tmp2; /* Careful with that axe, Eugene! */
-
- if (fade_time <= 0)
- return m->set(h, new_vol);
- secs = fade_time;
- ret = m->get(h);
- if (ret < 0)
- goto out;
- vol = ret;
- PARA_NOTICE_LOG("fading %s from %d to %d in %u seconds\n",
- conf.mixer_channel_arg, vol, new_vol, secs);
- diff = new_vol - vol;
- if (!diff) {
- sleep(secs);
- goto out;
- }
- incr = diff > 0? 1: -1;
- diff = diff > 0? diff: -diff;
- tmp = secs * 1000 / diff;
- tmp2 = tmp % 1000;
- while ((new_vol - vol) * incr > 0) {
- ts.tv_nsec = tmp2 * 1000000; /* really nec ?*/
- ts.tv_sec = tmp / 1000; /* really nec ?*/
- //printf("ts.tv_sec: %i\n", ts.tv_nsec);
- vol += incr;
- ret = m->set(h, vol);
- if (ret < 0)
- goto out;
- //printf("vol = %i\n", vol);
- nanosleep(&ts, NULL);
- }
-out:
- return ret;
-}
-
-static void client_cmd(const char *cmd)
-{
- int ret, status, fds[3] = {0, 0, 0};
- pid_t pid;
- char *cmdline = make_message(BINDIR "/para_client %s", cmd);
-
- PARA_NOTICE_LOG("%s\n", cmdline);
- ret = para_exec_cmdline_pid(&pid, cmdline, fds);
- free(cmdline);
- if (ret < 0) {
- PARA_ERROR_LOG("%s\n", para_strerror(-ret));
- goto fail;
- }
- do
- pid = waitpid(pid, &status, 0);
- while (pid == -1 && errno == EINTR);
- if (pid < 0) {
- PARA_ERROR_LOG("%s\n", strerror(errno));
- goto fail;
- }
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
- goto fail;
- return;
-fail:
- PARA_EMERG_LOG("command \"%s\" failed\n", cmd);
- exit(EXIT_FAILURE);
-}
-
-static void change_afs_mode(char *afs_mode)
-{
- char *cmd;
-
- client_cmd("stop");
- if (!afs_mode)
- return;
- cmd = make_message("select %s", afs_mode);
- client_cmd(cmd);
- free(cmd);
-}
-
-static int set_initial_volume(struct mixer *m, struct mixer_handle *h)
-{
- int i, ret;
-
- for (i = 0; i < conf.ivol_given; i++) {
- char *p, *ch, *arg = para_strdup(conf.ivol_arg[i]);
- int32_t iv;
- p = strchr(arg, ':');
- if (p) {
- *p = '\0';
- p++;
- ch = arg;
- } else {
- p = arg;
- ch = NULL;
- }
- ret = para_atoi32(p, &iv);
- if (ret < 0) {
- free(arg);
- return ret;
- }
- ret = set_channel(m, h, ch);
- if (!ch)
- ch = "default";
- if (ret < 0) {
- PARA_WARNING_LOG("ignoring channel %s\n", ch);
- ret = 0;
- } else {
- PARA_INFO_LOG("initial volume %s: %d\n", ch, iv);
- ret = m->set(h, iv);
- }
- free(arg);
- if (ret < 0)
- return ret;
- }
- return 1;
-}
-
-static int sweet_dreams(struct mixer *m, struct mixer_handle *h)
-{
- time_t t1, wake_time_epoch;
- unsigned int delay;
- struct tm *tm;
- int ret, min = conf.wake_min_arg;
- char *fo_mood = conf.fo_mood_arg;
- char *fi_mood = conf.fi_mood_arg;
- char *sleep_mood = conf.sleep_mood_arg;
- int fit = conf.fi_time_arg;
- int fot = conf.fo_time_arg;
- int fiv = conf.fi_vol_arg;
- int fov = conf.fo_vol_arg;
-
- /* calculate wake time */
- time(&t1);
- if (conf.wake_hour_given) {
- int hour = conf.wake_hour_arg;
- tm = localtime(&t1);
- if (tm->tm_hour > hour || (tm->tm_hour == hour && tm->tm_min> min)) {
- t1 += 86400; /* wake time is tomorrow */
- tm = localtime(&t1);
- }
- tm->tm_hour = hour;
- tm->tm_min = min;
- tm->tm_sec = 0;
- } else {
- t1 += 9 * 60 * 60; /* nine hours from now */
- PARA_INFO_LOG("default wake time: %lu\n", (long unsigned)t1);
- tm = localtime(&t1);
- }
- wake_time_epoch = mktime(tm);
- PARA_INFO_LOG("waketime: %d:%02d\n", tm->tm_hour, tm->tm_min);
- client_cmd("stop");
- sleep(1);
- if (fot) {
- ret = set_initial_volume(m, h);
- if (ret < 0)
- return ret;
- change_afs_mode(fo_mood);
- client_cmd("play");
- ret = set_channel(m, h, conf.mixer_channel_arg);
- if (ret < 0)
- return ret;
- ret = fade(m, h, fov, fot);
- if (ret < 0)
- return ret;
- } else {
- ret = m->set(h, fov);
- if (ret < 0)
- return ret;
- }
- if (conf.sleep_mood_given) {
- change_afs_mode(sleep_mood);
- client_cmd("play");
- } else
- client_cmd("stop");
- if (!fit)
- return 1;
- change_afs_mode(fi_mood);
- for (;;) {
- time(&t1);
- if (wake_time_epoch <= t1 + fit)
- break;
- delay = wake_time_epoch - t1 - fit;
- PARA_INFO_LOG("sleeping %u seconds (%u:%02u)\n",
- delay, delay / 3600,
- (delay % 3600) / 60);
- sleep(delay);
- }
- client_cmd("play");
- ret = fade(m, h, fiv, fit);
- PARA_INFO_LOG("fade complete, returning\n");
- return ret;
-}
-
-static int snooze(struct mixer *m, struct mixer_handle *h)
-{
- int ret, val;
-
- if (conf.so_time_arg <= 0)
- return 1;
- ret = m->get(h);
- if (ret < 0)
- return ret;
- val = ret;
- if (val < conf.so_vol_arg)
- ret = m->set(h, conf.so_vol_arg);
- else
- ret = fade(m, h, conf.so_vol_arg, conf.so_time_arg);
- if (ret < 0)
- return ret;
- client_cmd("pause");
- PARA_NOTICE_LOG("%d seconds snooze time...\n", conf.snooze_time_arg);
- sleep(conf.snooze_time_arg);
- client_cmd("play");
- return fade(m, h, conf.si_vol_arg, conf.si_time_arg);
-}
-
-static int configfile_exists(void)
-{
- static char *config_file;
-
- if (!conf.config_file_given) {
- char *home = para_homedir();
- free(config_file);
- config_file = make_message("%s/.paraslash/fade.conf", home);
- free(home);
- conf.config_file_arg = config_file;
- }
- return file_exists(conf.config_file_arg);
-}
-
-static void init_mixers(void)
-{
- int i;
-
- FOR_EACH_MIXER(i) {
- struct mixer *m = &supported_mixer[i];
- PARA_DEBUG_LOG("initializing mixer API #%d (%s)\n",
- i, mixer_name[i]);
- m->init(m);
- }
-}
-
-static int set_val(struct mixer *m, struct mixer_handle *h)
-{
- return m->set(h, conf.val_arg);
-}
-
-static struct mixer *get_mixer_or_die(void)
-{
- int i;
-
- if (!conf.mixer_api_given)
- i = DEFAULT_MIXER;
- else
- FOR_EACH_MIXER(i)
- if (!strcmp(mixer_name[i], conf.mixer_api_arg))
- break;
- if (i < NUM_SUPPORTED_MIXERS) {
- PARA_NOTICE_LOG("using %s mixer API\n", mixer_name[i]);
- return supported_mixer + i;
- }
- printf("available mixer APIs: ");
- FOR_EACH_MIXER(i) {
- int d = (i == DEFAULT_MIXER);
- printf("%s%s%s ", d? "[" : "", mixer_name[i], d? "]" : "");
- }
- printf("\n");
- 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);
-}
-
-/**
- * The main function of para_fade.
- *
- * The executable is linked with the alsa or the oss mixer API, or both. It has
- * a custom log function which prefixes log messages with the current date.
- *
- * \param argc Argument counter.
- * \param argv Argument vector.
- *
- * \return EXIT_SUCCESS or EXIT_FAILURE.
- */
-int main(int argc, char *argv[])
-{
- int ret;
- struct mixer *m;
- struct mixer_handle *h = NULL;
-
- 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",
- conf.config_file_arg);
- exit(EXIT_FAILURE);
- }
- if (ret) {
- struct fade_cmdline_parser_params params = {
- .override = 0,
- .initialize = 0,
- .check_required = 0,
- .check_ambiguity = 0,
- .print_errors = 1
- };
- fade_cmdline_parser_config_file(conf.config_file_arg,
- &conf, ¶ms);
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
- }
- init_mixers();
- m = get_mixer_or_die();
- ret = m->open(conf.mixer_device_arg, &h);
- if (ret < 0)
- goto out;
- ret = set_channel(m, h, conf.mixer_channel_arg);
- if (ret == -E_BAD_CHANNEL) {
- char *channels = m->get_channels(h);
- printf("Available channels: %s\n", channels);
- free(channels);
- }
- if (ret < 0)
- goto out;
- switch (conf.mode_arg) {
- case mode_arg_fade:
- ret = fade(m, h, conf.fade_vol_arg, conf.fade_time_arg);
- break;
- case mode_arg_snooze:
- ret = snooze(m, h);
- break;
- case mode_arg_set:
- ret = set_val(m, h);
- break;
- default: /* sleep mode */
- ret = sweet_dreams(m, h);
- break;
- }
-out:
- m->close(&h);
- if (ret < 0)
- PARA_EMERG_LOG("%s\n", para_strerror(-ret));
- return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
-}
#include "error.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
fn->min_iqs = FEC_HEADER_SIZE;
}
-/**
- * The init function of the fecdec filter.
- *
- * \param f Struct to initialize.
- */
-void fecdec_filter_init(struct filter *f)
-{
- f->close = fecdec_close;
- f->open = fecdec_open;
- f->pre_select = generic_filter_pre_select;
- f->post_select = fecdec_post_select;
-}
+const struct filter lsg_filter_cmd_com_fecdec_user_data = {
+ .open = fecdec_open,
+ .pre_select = generic_filter_pre_select,
+ .post_select = fecdec_post_select,
+ .close = fecdec_close,
+};
#include <regex.h>
#include <sys/types.h>
+#include <lopsub.h>
+#include "write_cmd.lsg.h"
#include "para.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "write.h"
-#include "write_common.h"
#include "string.h"
#include "fd.h"
-#include "file_write.cmdline.h"
#include "error.h"
/** Data specific to the file writer. */
static int prepare_output_file(struct writer_node *wn)
{
- struct file_write_args_info *conf = wn->conf;
- char *filename;
- int ret;
- struct private_file_write_data *pfwd = para_calloc(sizeof(*pfwd));
-
- if (conf->filename_given)
- filename = conf->filename_arg;
- else
- filename = random_filename();
- ret = para_open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
- if (!conf->filename_given)
- free(filename);
- if (ret < 0)
- goto out;
- pfwd->fd = ret;
- ret = mark_fd_blocking(pfwd->fd);
+ const unsigned flags = O_WRONLY | O_CREAT, mode = S_IRUSR | S_IWUSR;
+ int ret, fd;
+ struct private_file_write_data *pfwd;
+
+
+ if (WRITE_CMD_OPT_GIVEN(FILE, FILENAME, wn->lpr)) {
+ const char *path = WRITE_CMD_OPT_STRING_VAL(FILE, FILENAME,
+ wn->lpr);
+ ret = para_open(path, flags, mode);
+ } else {
+ char *path = random_filename();
+ ret = para_open(path, flags, mode);
+ free(path);
+ }
if (ret < 0)
- goto out_close;
- wn->private_data = pfwd;
+ return ret;
+ fd = ret;
+ ret = mark_fd_blocking(fd);
+ if (ret < 0) {
+ close(fd);
+ return ret;
+ }
+ pfwd = wn->private_data = para_calloc(sizeof(*pfwd));
+ pfwd->fd = fd;
return 1;
-out_close:
- close(pfwd->fd);
-out:
- free(pfwd);
- return ret;
}
static void file_write_pre_select(struct sched *s, void *context)
return ret;
}
-__malloc static void *file_write_parse_config_or_die(int argc, char **argv)
-{
- struct file_write_args_info *conf = para_calloc(sizeof(*conf));
-
- /* exits on errors */
- file_write_cmdline_parser(argc, argv, conf);
- return conf;
-}
-
-static void file_write_free_config(void *conf)
-{
- file_write_cmdline_parser_free(conf);
-}
-
/** the init function of the file writer */
-void file_write_init(struct writer *w)
-{
- struct file_write_args_info dummy;
-
- file_write_cmdline_parser_init(&dummy);
- w->pre_select = file_write_pre_select;
- w->post_select = file_write_post_select;
- 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)DEFINE_GGO_HELP(file_write);
- file_write_cmdline_parser_free(&dummy);
-}
+struct writer lsg_write_cmd_com_file_user_data = {
+ .pre_select = file_write_pre_select,
+ .post_select = file_write_post_select,
+ .close = file_write_close,
+};
/** \file filter.c The stand-alone filter program. */
#include <regex.h>
+#include <lopsub.h>
+#include "filter.lsg.h"
+#include "filter_cmd.lsg.h"
#include "para.h"
-#include "filter.cmdline.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
#include "stdin.h"
#include "stdout.h"
#include "error.h"
+#include "fd.h"
#include "version.h"
/** Array of error strings. */
DEFINE_PARA_ERRLIST;
+static struct lls_parse_result *lpr; /* command line options */
+
+#define CMD_PTR (lls_cmd(0, filter_suite))
+#define OPT_RESULT(_name) \
+ (lls_opt_result(LSG_FILTER_PARA_FILTER_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
/** The list of all status items used by para_{server,audiod,gui}. */
const char *status_item_list[] = {STATUS_ITEM_ARRAY};
/** Pointer to the stdout task. */
static struct stdout_task *sot = &stdout_task_struct;
-/** Gengetopt struct that holds the command line args. */
-static struct filter_args_info conf;
-
static int loglevel;
INIT_STDERR_LOGGING(loglevel);
-__noreturn static void print_help_and_die(void)
+static void handle_help_flag(void)
{
- 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);
+ char *help;
+
+ if (OPT_GIVEN(DETAILED_HELP))
+ help = lls_long_help(CMD_PTR);
+ else if (OPT_GIVEN(HELP))
+ help = lls_short_help(CMD_PTR);
+ else
+ return;
+ printf("%s\n", help);
+ free(help);
+ print_filter_helps(OPT_GIVEN(DETAILED_HELP));
exit(EXIT_SUCCESS);
}
static int parse_config(void)
{
- static char *cf; /* config file */
+ char *home, *cf; /* config file */
struct stat statbuf;
+ int ret;
- version_handle_flag("filter", conf.version_given);
- if (conf.help_given || conf.detailed_help_given)
- print_help_and_die();
- if (!cf) {
- char *home = para_homedir();
- cf = make_message("%s/.paraslash/filter.conf", home);
- free(home);
- }
+ version_handle_flag("filter", OPT_GIVEN(VERSION));
+ handle_help_flag();
+ home = para_homedir();
+ cf = make_message("%s/.paraslash/filter.conf", home);
+ free(home);
if (!stat(cf, &statbuf)) {
- struct filter_cmdline_parser_params params = {
- .override = 0,
- .initialize = 0,
- .check_required = 0,
- .check_ambiguity = 0,
- .print_errors = 1
- };
- filter_cmdline_parser_config_file(cf, &conf, ¶ms);
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ void *map;
+ size_t sz;
+ int cf_argc;
+ char **cf_argv, *errctx;
+ struct lls_parse_result *cf_lpr, *merged_lpr;
+
+ ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+ if (ret != -E_EMPTY) {
+ if (ret < 0)
+ return ret;
+ ret = lls(lls_convert_config(map, sz, NULL, &cf_argv,
+ &errctx));
+ para_munmap(map, sz);
+ if (ret < 0) {
+ PARA_ERROR_LOG("syntax error in %s\n", cf);
+ return ret;
+ }
+ cf_argc = ret;
+ ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr,
+ &errctx));
+ lls_free_argv(cf_argv);
+ if (ret < 0) {
+ PARA_ERROR_LOG("parse error in %s\n", cf);
+ return ret;
+ }
+ ret = lls(lls_merge(lpr, cf_lpr, CMD_PTR, &merged_lpr, &errctx));
+ lls_free_parse_result(cf_lpr, CMD_PTR);
+ if (ret < 0)
+ return ret;
+ lls_free_parse_result(lpr, CMD_PTR);
+ lpr = merged_lpr;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
+ }
+ }
+ if (!OPT_GIVEN(FILTER)) {
+ print_filter_list();
+ exit(EXIT_SUCCESS);
}
- if (!conf.filter_given)
- return -E_NO_FILTERS;
return 1;
}
const struct filter *f;
struct btr_node *parent;
struct filter_node **fns;
+ struct lls_parse_result *filter_lpr; /* per-filter options */
+ char *errctx;
- filter_cmdline_parser(argc, argv, &conf); /* aborts on errors */
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
- filter_init();
- ret = parse_config();
+ ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
if (ret < 0)
goto out;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
+ ret = parse_config();
+ if (ret < 0)
+ goto free_lpr;
sit->btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.name = "stdin"));
stdin_task_register(sit, &s);
- fns = para_malloc(conf.filter_given * sizeof(*fns));
- for (i = 0, parent = sit->btrn; i < conf.filter_given; i++) {
- char *fa = conf.filter_arg[i];
+ fns = para_malloc(OPT_GIVEN(FILTER) * sizeof(*fns));
+ for (i = 0, parent = sit->btrn; i < OPT_GIVEN(FILTER); i++) {
+ const char *fa = lls_string_val(i, OPT_RESULT(FILTER));
+ const char *name;
struct filter_node *fn;
struct task_info ti;
fn = fns[i] = para_calloc(sizeof(*fn));
- ret = check_filter_arg(fa, &fn->conf);
- if (ret < 0) {
- free(fn);
- goto out_cleanup;
- }
- fn->filter_num = ret;
+ fn->filter_num = filter_setup(fa, &fn->conf, &filter_lpr);
+ name = filter_name(fn->filter_num);
+ fn->lpr = filter_lpr;
+ PARA_DEBUG_LOG("filter #%d: %s\n", i, name);
f = filter_get(fn->filter_num);
- PARA_DEBUG_LOG("filter #%d: %s\n", i, f->name);
fn->btrn = btr_new_node(&(struct btr_node_description)
- EMBRACE(.name = f->name, .parent = parent,
+ EMBRACE(.name = name, .parent = parent,
.handler = f->execute, .context = fn));
- ti.name = f->name;
+ ti.name = name;
ti.pre_select = f->pre_select;
ti.post_select = f->post_select;
ti.context = fn;
btr_log_tree(sit->btrn, LL_INFO);
ret = schedule(&s);
sched_shutdown(&s);
-out_cleanup:
for (i--; i >= 0; i--) {
struct filter_node *fn = fns[i];
if (f->close)
f->close(fn);
btr_remove_node(&fn->btrn);
- if (f->free_config)
- f->free_config(fn->conf);
+ if (f->teardown)
+ f->teardown(fn->lpr, fn->conf);
free(fn);
}
free(fns);
btr_remove_node(&sit->btrn);
btr_remove_node(&sot->btrn);
+free_lpr:
+ lls_free_parse_result(lpr, CMD_PTR);
out:
- if (ret < 0)
- PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ }
exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
}
/** \file filter.h Filter-related structures and exported symbols from filter_common.c. */
-/** The list of supported filters. */
-enum filter_enum {FILTER_ENUM};
-
/**
* Describes one running instance of a filter.
*/
struct list_head callbacks;
/** A pointer to the configuration of this instance. */
void *conf;
+ /** The parsed command line, merged with options given in the config file. */
+ struct lls_parse_result *lpr;
/** The buffer tree node. */
struct btr_node *btrn;
/** The task corresponding to this filter node. */
/**
* The structure associated with a paraslash filter.
*
- * Paraslash filters are "modules" which are used to transform an audio stream.
- * struct filter contains pointers to functions that must be supplied by the
- * filter code in order to be used by the driving application (currently
- * para_audiod and para_filter).
+ * Paraslash filters are "modules" which transform an audio stream. struct
+ * filter contains methods which are implemented by each filter.
*
* Note: As several instances of the same filter may be running at the same
* time, all these filter functions must be reentrant; no static non-constant
* variables may be used.
+ *
* \sa mp3dec_filter.c, oggdec_filter.c, wav_filter.c, compress_filter.c, filter_node
*/
struct filter {
- /** The name of the filter. */
- const char *name;
- /**
- * Pointer to the filter init routine.
- *
- * This function is only called once at startup. It must initialize the
- * other non-optional function pointers of this structure.
- */
- void (*init)(struct filter *f);
/**
* Open one instance of this filter.
*
*/
void (*close)(struct filter_node *fn);
/**
- * A pointer to the filter's command line parser.
+ * Prepare the filter according to command line options.
*
- * If this optional function pointer is not NULL, any filter options
- * are passed from the main program to this command line parser once at
- * application startup. The command line parser should check its
- * command line options given by \a argc and \a argv and abort on
- * errors. Success must be indicated by a non-negative return value. In
- * this case the function should return a pointer to the
- * filter-specific configuration data determined by \a argc and \a
- * argv. On failure, a negative paraslash error code must be returned.
+ * In addition to the syntactic checks which are automatically performed
+ * by the lopsub functions, some filters like to also check the command
+ * line arguments semantically. Moreover, since applications may open
+ * the filter many times with the same options, filters need a method
+ * which allows them to precompute once those parts of the setup which
+ * depend only on the command line options.
+ *
+ * If this function pointer is not NULL, the function is called once at
+ * startup. The returned pointer value is made available to the ->open
+ * method via the ->conf pointer of struct filter_node.
+ *
+ * Filters are supposed to abort if the setup fails. If the function
+ * returns, it is assumed to have succeeded.
*/
- int (*parse_config)(int argc, char **argv, void **config);
+ void *(*setup)(const struct lls_parse_result *lpr);
/**
- * Deallocate the memory for the configuration.
+ * Deallocate precomputed resources.
*
- * This is called to free whatever ->parse_config() has allocated.
+ * This should free whatever ->setup() has allocated.
*/
- void (*free_config)(void *conf);
-
- /** The help texts for this filter. */
- struct ggo_help help;
+ void (*teardown)(const struct lls_parse_result *lpr, void *conf);
/**
* Set scheduler timeout and add file descriptors to fd sets.
*
btr_command_handler execute;
};
-void filter_init(void);
-int check_filter_arg(const char *fa, void **conf);
-void print_filter_helps(unsigned flags);
+void print_filter_helps(bool detailed);
+void print_filter_list(void);
+int filter_setup(const char *fa, void **conf, struct lls_parse_result **lprp);
+#define FILTER_CMD(_num) (lls_cmd(_num, filter_cmd_suite))
+#define FILTER_CMD_OPT(_cmd, _opt) (lls_opt( \
+ LSG_FILTER_CMD_ ## _cmd ## _OPT_ ## _opt, \
+ FILTER_CMD(LSG_FILTER_CMD_CMD_ ## _cmd)))
+#define FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr) \
+ (lls_opt_result(LSG_FILTER_CMD_ ## _cmd ## _OPT_ ## _opt, _lpr))
+#define FILTER_CMD_OPT_GIVEN(_cmd, _opt, _lpr) \
+ (lls_opt_given(FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+#define FILTER_CMD_OPT_UINT32_VAL(_cmd, _opt, _lpr) \
+ (lls_uint32_val(0, FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+#define FILTER_CMD_OPT_STRING_VAL(_cmd, _opt, _lpr) \
+ (lls_string_val(0, FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+
void generic_filter_pre_select(struct sched *s, void *context);
int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels,
char **result);
#endif
}
-DECLARE_FILTER_INITS
-
+/** Make a filter pointer from the filter number. */
const struct filter *filter_get(int filter_num);
+const char *filter_name(int filter_num);
#include <regex.h>
#include <sys/types.h>
+#include <lopsub.h>
+#include "filter_cmd.lsg.h"
#include "para.h"
#include "list.h"
#include "sched.h"
#include "fd.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "error.h"
#include "string.h"
-/** Iterate over the array of supported filters. */
-#define FOR_EACH_SUPPORTED_FILTER(j) for (j = 0; j < NUM_SUPPORTED_FILTERS; j++)
-
-/** The array of supported filters. */
-static struct filter filters[NUM_SUPPORTED_FILTERS] = {FILTER_ARRAY};
+/** Iterate over all filters. */
+#define FOR_EACH_FILTER(j) for (j = 1; lls_cmd(j, filter_cmd_suite); j++)
/**
* Obtain a reference to a filter structure.
*/
const struct filter *filter_get(int filter_num)
{
- assert(filter_num >= 0);
- assert(filter_num < NUM_SUPPORTED_FILTERS);
- return filters + filter_num;
+ assert(filter_num >= 1);
+ assert(filter_num <= LSG_NUM_FILTER_CMD_SUBCOMMANDS);
+ return lls_user_data(FILTER_CMD(filter_num));
}
-/**
- * Call the init function of each supported filter.
- * \sa filter::init
- */
-void filter_init(void)
+static inline bool filter_supported(int filter_num)
{
- int i;
-
- FOR_EACH_SUPPORTED_FILTER(i)
- filter_get(i)->init((struct filter *)filter_get(i));
+ return lls_user_data(FILTER_CMD(filter_num));
}
-/*
- * If the filter has a command line parser and options is not NULL, run it.
- * Returns filter_num on success, negative on errors
- */
-static int parse_filter_args(int filter_num, const char *options, void **conf)
+const char *filter_name(int filter_num)
{
- const struct filter *f = filter_get(filter_num);
- int ret, argc;
- char **argv;
-
- if (!f->parse_config)
- return strlen(options)? -E_BAD_FILTER_OPTIONS : filter_num;
- argc = create_shifted_argv(options, " \t", &argv);
- if (argc < 0)
- return -E_BAD_FILTER_OPTIONS;
- argv[0] = para_strdup(f->name);
- ret = f->parse_config(argc, argv, conf);
- free_argv(argv);
- return ret < 0? ret : filter_num;
+ return lls_command_name(FILTER_CMD(filter_num));
}
/**
- * Check the filter command line options.
+ * Parse a filter command line and call the corresponding ->setup method.
*
* \param fa The filter argument.
- * \param conf Points to the filter configuration upon successful return.
+ * \param conf Points to filter-specific setup upon successful return.
+ * \param lprp Parsed command line options are returned here.
*
* Check if the given filter argument starts with the name of a supported
* filter, optionally followed by options for this filter. If yes, call the
- * command line parser of that filter.
- *
- * \return On success, the number of the filter is returned and \a conf
- * is initialized to point to the filter configuration determined by \a fa.
- * On errors, a negative value is returned.
- *
- * Note: If \a fa specifies a filter that has no command line parser success is
- * returned, and \a conf is initialized to \p NULL.
+ * command line parser of that filter and its ->setup method.
*
- * \sa filter::parse_config
+ * \return This function either succeeds or does not return. On success, the
+ * number of the filter is returned and conf is initialized to point to the
+ * filter configuration as returned by the filter's ->setup() method, if any.
+ * Moreover, *lprp is initialized to contain the parsed command line options.
+ * On errors, the function calls exit(EXIT_FAILURE).
*/
-int check_filter_arg(const char *fa, void **conf)
+int filter_setup(const char *fa, void **conf, struct lls_parse_result **lprp)
{
- int j;
-
- *conf = NULL;
-// PARA_DEBUG_LOG("arg: %s\n", fa);
- FOR_EACH_SUPPORTED_FILTER(j) {
- const char *name = filter_get(j)->name;
- size_t len = strlen(name);
- char c;
- if (strlen(fa) < len)
- continue;
- if (strncmp(name, fa, len))
- continue;
- c = fa[len];
- if (c && c != ' ')
- continue;
- if (c && !filter_get(j)->parse_config)
- return -E_BAD_FILTER_OPTIONS;
- return parse_filter_args(j, c? fa + len + 1 :
- fa + strlen(fa), conf);
+ int ret, filter_num, argc;
+ char *errctx = NULL, **argv;
+ const struct lls_command *cmd;
+ const struct filter *f;
+
+ ret = create_argv(fa, " \t\n", &argv);
+ if (ret < 0)
+ goto fail;
+ argc = ret;
+ ret = lls(lls_lookup_subcmd(argv[0], filter_cmd_suite, &errctx));
+ if (ret < 0)
+ goto free_argv;
+ filter_num = ret;
+ cmd = FILTER_CMD(filter_num);
+ if (!filter_supported(filter_num)) {
+ ret = -E_UNSUPPORTED_FILTER;
+ errctx = make_message("bad filter name: %s",
+ lls_command_name(cmd));
+ goto free_argv;
}
- return -E_UNSUPPORTED_FILTER;
+ ret = lls(lls_parse(argc, argv, cmd, lprp, &errctx));
+ if (ret < 0)
+ goto free_argv;
+ f = filter_get(filter_num);
+ *conf = f->setup? f->setup(*lprp) : NULL;
+ ret = filter_num;
+free_argv:
+ free_argv(argv);
+ if (ret >= 0)
+ return ret;
+fail:
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+ exit(EXIT_FAILURE);
}
/**
* Print help text of each filter to stdout.
*
- * \param flags Passed to \ref ggo_print_help().
+ * \param detailed Whether to print short or long help.
*/
-void print_filter_helps(unsigned flags)
+void print_filter_helps(bool detailed)
{
int i, num = 0;
- printf_or_die("\nAvailable filters: ");
- FOR_EACH_SUPPORTED_FILTER(i) {
+ printf("\nAvailable filters: ");
+ FOR_EACH_FILTER(i) {
+ if (!filter_supported(i))
+ continue;
if (num > 50) {
- printf_or_die("\n ");
+ printf("\n ");
num = 0;
}
- num += printf_or_die("%s%s", i? " " : "", filter_get(i)->name);
+ num += printf("%s%s", i? " " : "", filter_name(i));
}
- printf_or_die("\n");
+ printf("\n");
- FOR_EACH_SUPPORTED_FILTER(i) {
- struct filter *f = (struct filter *)filter_get(i);
+ FOR_EACH_FILTER(i) {
+ const struct lls_command *cmd = FILTER_CMD(i);
+ char *help;
+
+ if (!filter_supported(i))
+ continue;
+ help = detailed? lls_long_help(cmd) : lls_short_help(cmd);
+ if (!help)
+ continue;
+ printf("%s\n", help);
+ free(help);
+ }
+}
+
+/**
+ * Print a short summary of all available filters to stdout.
+ *
+ * For each supported filter, the filter name and the purpose text is printed
+ * in a single line. Since no options are shown, the filter list is more
+ * concise than the text obtained from print_filter_helps().
+ */
+void print_filter_list(void)
+{
+ int i;
- if (!f->help.short_help)
+ printf("Available filters:\n");
+ FOR_EACH_FILTER(i) {
+ const struct lls_command *cmd = FILTER_CMD(i);
+ if (!filter_supported(i))
continue;
- printf_or_die("\nOptions for %s (%s):", f->name,
- f->help.purpose);
- ggo_print_help(&f->help, flags);
+ printf("%-9s %s\n", filter_name(i), lls_purpose(cmd));
}
}
break;
}
afhi->chunks_total = c;
+ set_max_chunk_size(afhi);
ret = 1;
free_decoder:
FLAC__stream_decoder_finish(decoder);
#include "para.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "error.h"
fn->min_iqs = 0;
}
-/**
- * The init function of the flacdec filter.
- *
- * \param f Pointer to the filter struct to initialize.
- *
- * \sa filter::init.
- */
-void flacdec_filter_init(struct filter *f)
-{
- f->open = flacdec_open;
- f->close = flacdec_close;
- f->pre_select = flacdec_pre_select;
- f->post_select = flacdec_post_select;
- f->execute = flacdec_execute;
-}
+const struct filter lsg_filter_cmd_com_flacdec_user_data = {
+ .open = flacdec_open,
+ .close = flacdec_close,
+ .pre_select = flacdec_pre_select,
+ .post_select = flacdec_post_select,
+ .execute = flacdec_execute,
+};
//#define GCRYPT_DEBUG 1
-static bool libgcrypt_has_oaep;
-static const char *rsa_decrypt_sexp;
-
#ifdef GCRYPT_DEBUG
static void dump_buffer(const char *msg, unsigned char *buf, int len)
{
* don't have to initialize any random seed here, but we must initialize the
* gcrypt library. This task is performed by gcry_check_version() which can
* also check that the gcrypt library version is at least the minimal required
- * version. This function also tells us whether we have to use our own OAEP
- * padding code.
+ * version.
*/
void init_random_seed_or_die(void)
{
- const char *ver, *req_ver;
-
- ver = gcry_check_version(NULL);
- req_ver = "1.4.0";
- if (!gcry_check_version(req_ver)) {
- PARA_EMERG_LOG("fatal: need at least libgcrypt-%s, have: %s\n",
- req_ver, ver);
- exit(EXIT_FAILURE);
- }
- req_ver = "1.5.0";
- if (gcry_check_version(req_ver)) {
- libgcrypt_has_oaep = true;
- rsa_decrypt_sexp = "(enc-val(flags oaep)(rsa(a %m)))";
- } else {
- libgcrypt_has_oaep = false;
- rsa_decrypt_sexp = "(enc-val(rsa(a %m)))";
- }
+ const char *req_ver = "1.5.0";
+
+ if (gcry_check_version(req_ver))
+ return;
+ PARA_EMERG_LOG("fatal: need at least libgcrypt-%s, have: %s\n",
+ req_ver, gcry_check_version(NULL));
+ exit(EXIT_FAILURE);
}
/** S-expression for the public part of an RSA key. */
#define RSA_PUBKEY_SEXP "(public-key (rsa (n %m) (e %m)))"
/** S-expression for a private RSA key. */
#define RSA_PRIVKEY_SEXP "(private-key (rsa (n %m) (e %m) (d %m) (p %m) (q %m) (u %m)))"
-
-/* rfc 3447, appendix B.2 */
-static void mgf1(unsigned char *seed, size_t seed_len, unsigned result_len,
- unsigned char *result)
-{
- gcry_error_t gret;
- gcry_md_hd_t handle;
- size_t n;
- unsigned char *md;
- unsigned char octet_string[4], *rp = result, *end = rp + result_len;
-
- assert(result_len / HASH_SIZE < 1ULL << 31);
- gret = gcry_md_open(&handle, GCRY_MD_SHA1, 0);
- assert(gret == 0);
- for (n = 0; rp < end; n++) {
- gcry_md_write(handle, seed, seed_len);
- octet_string[0] = (unsigned char)((n >> 24) & 255);
- octet_string[1] = (unsigned char)((n >> 16) & 255);
- octet_string[2] = (unsigned char)((n >> 8)) & 255;
- octet_string[3] = (unsigned char)(n & 255);
- gcry_md_write(handle, octet_string, 4);
- gcry_md_final(handle);
- md = gcry_md_read(handle, GCRY_MD_SHA1);
- memcpy(rp, md, PARA_MIN(HASH_SIZE, (int)(end - rp)));
- rp += HASH_SIZE;
- gcry_md_reset(handle);
- }
- gcry_md_close(handle);
-}
-
-/** The sha1 hash of an empty file. */
-static const unsigned char empty_hash[HASH_SIZE] =
- "\xda" "\x39" "\xa3" "\xee" "\x5e"
- "\x6b" "\x4b" "\x0d" "\x32" "\x55"
- "\xbf" "\xef" "\x95" "\x60" "\x18"
- "\x90" "\xaf" "\xd8" "\x07" "\x09";
-
-/* rfc3447, section 7.1.1 */
-static void pad_oaep(unsigned char *in, size_t in_len, unsigned char *out,
- size_t out_len)
-{
- size_t ps_len = out_len - in_len - 2 * HASH_SIZE - 2;
- size_t n, mask_len = out_len - HASH_SIZE - 1;
- unsigned char *seed = out + 1, *db = seed + HASH_SIZE,
- *ps = db + HASH_SIZE, *one = ps + ps_len;
- unsigned char *db_mask, seed_mask[HASH_SIZE];
-
- assert(in_len <= out_len - 2 - 2 * HASH_SIZE);
- assert(out_len > 2 * HASH_SIZE + 2);
- PARA_DEBUG_LOG("padding %zu byte input -> %zu byte output\n",
- in_len, out_len);
- dump_buffer("unpadded buffer", in, in_len);
-
- out[0] = '\0';
- get_random_bytes_or_die(seed, HASH_SIZE);
- memcpy(db, empty_hash, HASH_SIZE);
- memset(ps, 0, ps_len);
- *one = 0x01;
- memcpy(one + 1, in, in_len);
- db_mask = para_malloc(mask_len);
- mgf1(seed, HASH_SIZE, mask_len, db_mask);
- for (n = 0; n < mask_len; n++)
- db[n] ^= db_mask[n];
- mgf1(db, mask_len, HASH_SIZE, seed_mask);
- for (n = 0; n < HASH_SIZE; n++)
- seed[n] ^= seed_mask[n];
- free(db_mask);
- dump_buffer("padded buffer", out, out_len);
-}
-
-/* rfc 3447, section 7.1.2 */
-static int unpad_oaep(unsigned char *in, size_t in_len, unsigned char *out,
- size_t *out_len)
-{
- unsigned char *masked_seed = in + 1;
- unsigned char *db = in + 1 + HASH_SIZE;
- unsigned char seed[HASH_SIZE], seed_mask[HASH_SIZE];
- unsigned char *db_mask, *p;
- size_t n, mask_len = in_len - HASH_SIZE - 1;
-
- mgf1(db, mask_len, HASH_SIZE, seed_mask);
- for (n = 0; n < HASH_SIZE; n++)
- seed[n] = masked_seed[n] ^ seed_mask[n];
- db_mask = para_malloc(mask_len);
- mgf1(seed, HASH_SIZE, mask_len, db_mask);
- for (n = 0; n < mask_len; n++)
- db[n] ^= db_mask[n];
- free(db_mask);
- if (memcmp(db, empty_hash, HASH_SIZE))
- return -E_OEAP;
- for (p = db + HASH_SIZE; p < in + in_len - 1; p++)
- if (*p != '\0')
- break;
- if (p >= in + in_len - 1)
- return -E_OEAP;
- p++;
- *out_len = in + in_len - p;
- memcpy(out, p, *out_len);
- return 1;
-}
+/** S-expression for decryption. */
+#define RSA_DECRYPT_SEXP "(enc-val(flags oaep)(rsa(a %m)))"
struct asymmetric_key {
gcry_sexp_t sexp;
return c & 0x7f;
}
-static int find_pubkey_bignum_offset(const unsigned char *data, int len)
-{
- const unsigned char *p = data, *end = data + len;
-
- /* the whole thing starts with one sequence */
- if (*p != ASN1_TYPE_SEQUENCE)
- return -E_ASN1_PARSE;
- p++;
- if (p >= end)
- return -E_ASN1_PARSE;
- if (is_short_form(*p))
- p++;
- else
- p += 1 + get_long_form_num_length_bytes(*p);
- if (p >= end)
- return -E_ASN1_PARSE;
- /* another sequence containing the object id, skip it */
- if (*p != ASN1_TYPE_SEQUENCE)
- return -E_ASN1_PARSE;
- p++;
- if (p >= end)
- return -E_ASN1_PARSE;
- if (!is_short_form(*p))
- return -E_ASN1_PARSE;
- p += 1 + get_short_form_length(*p);
- if (p >= end)
- return -E_ASN1_PARSE;
- /* all numbers are wrapped in a bit string object that follows */
- if (*p != ASN1_TYPE_BIT_STRING)
- return -E_ASN1_PARSE;
- p++;
- if (p >= end)
- return -E_ASN1_PARSE;
- if (is_short_form(*p))
- p++;
- else
- p += 1 + get_long_form_num_length_bytes(*p);
- p++; /* skip number of unused bits in the bit string */
- if (p >= end)
- return -E_ASN1_PARSE;
-
- /* next, we have a sequence of two integers (n and e) */
- if (*p != ASN1_TYPE_SEQUENCE)
- return -E_ASN1_PARSE;
- p++;
- if (p >= end)
- return -E_ASN1_PARSE;
- if (is_short_form(*p))
- p++;
- else
- p += 1 + get_long_form_num_length_bytes(*p);
- if (p >= end)
- return -E_ASN1_PARSE;
- if (*p != ASN1_TYPE_INTEGER)
- return -E_ASN1_PARSE;
- return p - data;
-}
-
/*
* Returns: Number of bytes scanned. This may differ from the value returned via
* bn_bytes because the latter does not include the ASN.1 prefix and a leading
gcry_sexp_t sexp;
struct asymmetric_key *key;
+ *result = NULL;
ret = decode_key(key_file, PRIVATE_KEY_HEADER, PRIVATE_KEY_FOOTER,
&blob);
if (ret < 0)
return ret;
}
-/** Public keys start with this header. */
-#define PUBLIC_KEY_HEADER "-----BEGIN PUBLIC KEY-----"
-/** Public keys end with this footer. */
-#define PUBLIC_KEY_FOOTER "-----END PUBLIC KEY-----"
-
-static int get_asn_public_key(const char *key_file, struct asymmetric_key **result)
-{
- gcry_mpi_t n = NULL, e = NULL;
- unsigned char *blob, *cp, *end;
- int blob_size, ret, n_size;
- gcry_error_t gret;
- size_t erroff;
- gcry_sexp_t sexp;
- struct asymmetric_key *key;
-
- ret = decode_key(key_file, PUBLIC_KEY_HEADER, PUBLIC_KEY_FOOTER,
- &blob);
- if (ret < 0)
- return ret;
- blob_size = ret;
- end = blob + blob_size;
- ret = find_pubkey_bignum_offset(blob, blob_size);
- if (ret < 0)
- goto free_blob;
- PARA_DEBUG_LOG("decoding public RSA params at offset %d\n", ret);
- cp = blob + ret;
-
- ret = read_bignum(cp, end, &n, &n_size);
- if (ret < 0)
- goto free_blob;
- cp += ret;
-
- ret = read_bignum(cp, end, &e, NULL);
- if (ret < 0)
- goto release_n;
-
- gret = gcry_sexp_build(&sexp, &erroff, RSA_PUBKEY_SEXP, n, e);
- if (gret) {
- PARA_ERROR_LOG("offset %zu: %s\n", erroff,
- gcry_strerror(gcry_err_code(gret)));
- ret = -E_SEXP_BUILD;
- goto release_e;
- }
- key = para_malloc(sizeof(*key));
- key->sexp = sexp;
- key->num_bytes = n_size;
- *result = key;
- ret = n_size;
- PARA_INFO_LOG("successfully read %d bit asn public key\n", n_size * 8);
-
-release_e:
- gcry_mpi_release(e);
-release_n:
- gcry_mpi_release(n);
-free_blob:
- free(blob);
- return ret;
-}
-
static int get_ssh_public_key(unsigned char *data, int size, gcry_sexp_t *result)
{
int ret;
return ret;
}
-int get_asymmetric_key(const char *key_file, int private,
- struct asymmetric_key **result)
+int get_public_key(const char *key_file, struct asymmetric_key **result)
{
int ret, ret2;
void *map;
gcry_sexp_t sexp;
struct asymmetric_key *key;
- if (private)
- return get_private_key(key_file, result);
ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
if (ret < 0)
return ret;
ret = is_ssh_rsa_key(map, map_size);
if (!ret) {
- ret = para_munmap(map, map_size);
- if (ret < 0)
- return ret;
- return get_asn_public_key(key_file, result);
+ para_munmap(map, map_size);
+ return -E_SSH_PARSE;
}
start = map + ret;
end = map + map_size;
return ret;
}
-void free_asymmetric_key(struct asymmetric_key *key)
+void free_public_key(struct asymmetric_key *key)
{
if (!key)
return;
free(key);
}
-static int decode_rsa(gcry_sexp_t sexp, int key_size, unsigned char *outbuf,
- size_t *nbytes)
+static int decode_rsa(gcry_sexp_t sexp, unsigned char *outbuf, size_t *nbytes)
{
- int ret;
- gcry_error_t gret;
- unsigned char oaep_buf[512];
- gcry_mpi_t out_mpi;
-
- if (libgcrypt_has_oaep) {
- const char *p = gcry_sexp_nth_data(sexp, 1, nbytes);
-
- if (!p) {
- PARA_ERROR_LOG("could not get data from list\n");
- return -E_OEAP;
- }
- memcpy(outbuf, p, *nbytes);
- return 1;
- }
- out_mpi = gcry_sexp_nth_mpi(sexp, 0, GCRYMPI_FMT_USG);
- if (!out_mpi)
- return -E_SEXP_FIND;
- gret = gcry_mpi_print(GCRYMPI_FMT_USG, oaep_buf, sizeof(oaep_buf),
- nbytes, out_mpi);
- if (gret) {
- PARA_ERROR_LOG("mpi_print: %s\n", gcrypt_strerror(gret));
- ret = -E_MPI_PRINT;
- goto out_mpi_release;
- }
- /*
- * An oaep-encoded buffer always starts with at least one zero byte.
- * However, leading zeroes in an mpi are omitted in the output of
- * gcry_mpi_print() when using the GCRYMPI_FMT_USG format. The
- * alternative, GCRYMPI_FMT_STD, does not work either because here the
- * leading zero(es) might also be omitted, depending on the value of
- * the second byte.
- *
- * To circumvent this, we shift the oaep buffer to the right. But first
- * we check that the buffer actually started with a zero byte, i.e. that
- * nbytes < key_size. Otherwise a decoding error occurred.
- */
- ret = -E_SEXP_DECRYPT;
- if (*nbytes >= key_size)
- goto out_mpi_release;
- memmove(oaep_buf + key_size - *nbytes, oaep_buf, *nbytes);
- memset(oaep_buf, 0, key_size - *nbytes);
+ const char *p = gcry_sexp_nth_data(sexp, 1, nbytes);
- PARA_DEBUG_LOG("decrypted buffer before unpad (%d bytes):\n",
- 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);
- ret = 1;
-out_mpi_release:
- gcry_mpi_release(out_mpi);
- return ret;
+ if (!p)
+ return -E_RSA_DECODE;
+ memcpy(outbuf, p, *nbytes);
+ return 1;
}
int priv_decrypt(const char *key_file, unsigned char *outbuf,
unsigned char *inbuf, int inlen)
{
gcry_error_t gret;
- int ret, key_size;
+ int ret;
struct asymmetric_key *priv;
gcry_mpi_t in_mpi = NULL;
gcry_sexp_t in, out, priv_key;
size_t nbytes;
- ret = check_key_file(key_file, true);
+ ret = check_private_key_file(key_file);
if (ret < 0)
return ret;
PARA_INFO_LOG("decrypting %d byte input\n", inlen);
ret = get_private_key(key_file, &priv);
if (ret < 0)
return ret;
- key_size = ret / 8;
/* asymmetric key priv -> sexp priv_key */
ret = -E_SEXP_FIND;
goto key_release;
}
/* in_mpi -> in sexp */
- gret = gcry_sexp_build(&in, NULL, rsa_decrypt_sexp, in_mpi);
+ gret = gcry_sexp_build(&in, NULL, RSA_DECRYPT_SEXP, in_mpi);
if (gret) {
PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret));
ret = -E_SEXP_BUILD;
ret = -E_SEXP_DECRYPT;
goto in_release;
}
- ret = decode_rsa(out, key_size, outbuf, &nbytes);
+ ret = decode_rsa(out, outbuf, &nbytes);
if (ret < 0)
goto out_release;
PARA_INFO_LOG("successfully decrypted %zu byte message\n", nbytes);
key_release:
gcry_sexp_release(priv_key);
free_key:
- free_asymmetric_key(priv);
+ gcry_sexp_release(priv->sexp);
+ free(priv);
return ret;
}
pub_key = gcry_sexp_find_token(pub->sexp, "public-key", 0);
if (!pub_key)
return -E_SEXP_FIND;
- if (libgcrypt_has_oaep) {
- gret = gcry_sexp_build(&in, NULL,
- "(data(flags oaep)(value %b))", len, inbuf);
- } else {
- unsigned char padded_input[256];
- const size_t pad_size = 256;
- /* inbuf -> padded inbuf */
- pad_oaep(inbuf, len, padded_input, pad_size);
- /* padded inbuf -> in sexp */
- gret = gcry_sexp_build(&in, NULL,
- "(data(flags raw)(value %b))", pad_size, padded_input);
- }
+ gret = gcry_sexp_build(&in, NULL, "(data(flags oaep)(value %b))", len, inbuf);
if (gret) {
PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret));
ret = -E_SEXP_BUILD;
gcry_cipher_hd_t handle;
};
-struct stream_cipher *sc_new(const unsigned char *data, int len,
- bool use_aes)
+struct stream_cipher *sc_new(const unsigned char *data, int len)
{
gcry_error_t gret;
struct stream_cipher *sc = para_malloc(sizeof(*sc));
- if (use_aes) {
- assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
- gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_AES128,
- GCRY_CIPHER_MODE_CTR, 0);
- assert(gret == 0);
- gret = gcry_cipher_setkey(sc->handle, data,
- AES_CRT128_BLOCK_SIZE);
- assert(gret == 0);
- gret = gcry_cipher_setctr(sc->handle,
- data + AES_CRT128_BLOCK_SIZE, AES_CRT128_BLOCK_SIZE);
- assert(gret == 0);
- return sc;
- }
- gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_ARCFOUR,
- GCRY_CIPHER_MODE_STREAM, 0);
- if (gret) {
- PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret));
- free(sc);
- return NULL;
- }
- gret = gcry_cipher_setkey(sc->handle, data, (size_t)len);
+ assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
+ gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_AES128,
+ GCRY_CIPHER_MODE_CTR, 0);
+ assert(gret == 0);
+ gret = gcry_cipher_setkey(sc->handle, data,
+ AES_CRT128_BLOCK_SIZE);
+ assert(gret == 0);
+ gret = gcry_cipher_setctr(sc->handle,
+ data + AES_CRT128_BLOCK_SIZE, AES_CRT128_BLOCK_SIZE);
assert(gret == 0);
return sc;
}
+++ /dev/null
-/*
- * Copyright (C) 2008 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file ggo.c Function for printing help. */
-
-
-#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 int printf_or_die(const char *fmt, ...)
-{
- va_list argp;
- int ret;
-
- va_start(argp, fmt);
- ret = vprintf(fmt, argp);
- va_end(argp);
- if (ret >= 0)
- return ret;
- exit(EXIT_FAILURE);
-}
-
-/**
- * Print one of the two given help texts.
- *
- * \param help contains the help texts.
- * \param flags What to print, see \ref ggo_print_help_flags.
- */
-void ggo_print_help(struct ggo_help *help, unsigned flags)
-{
- const char **p;
-
- 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;
- for (; *p; p++)
- printf_or_die("%s\n", *p);
-}
+++ /dev/null
-/*
- * Copyright (C) 2008 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file ggo.h Functions and structures for help text handling. */
-
-/**
- * Information extracted from the .cmdline.h header files.
- */
-struct ggo_help {
- /** Program or module (receiver, filter, writer) name. */
- const char *prefix;
- /** Generated by gengetopt from the options in the .ggo file. */
- const char **short_help;
- /** 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;
-};
-
-/**
- * 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, ...);
#include <regex.h>
#include <sys/types.h>
+#include <inttypes.h>
+#include <lopsub.h>
+
+#include "audiod_cmd.lsg.h"
#include "para.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "grab_client.h"
#include "audiod.h"
return ret;
}
-static int gc_check_args(int argc, char **argv, struct grab_client *gc)
+static int gc_check_args(struct lls_parse_result *lpr, struct grab_client *gc)
{
- int i;
+ const struct lls_opt_result *r;
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strncmp(arg, "-m", 2)) {
- if (*(arg + 3))
- return -E_GC_SYNTAX;
- switch(*(arg + 2)) {
- case 's':
- gc->mode = GM_SLOPPY;
- continue;
- case 'a':
- gc->mode = GM_AGGRESSIVE;
- continue;
- case 'p':
- gc->mode = GM_PEDANTIC;
- continue;
- default:
- return -E_GC_SYNTAX;
- }
- }
- if (!strcmp(arg, "-o")) {
- gc->flags |= GF_ONE_SHOT;
- continue;
- }
- if (!strncmp(arg, "-p=", 3)) {
- gc->parent = para_strdup(arg + 3);
- continue;
- }
- if (!strncmp(arg, "-n=", 3)) {
- gc->name = para_strdup(arg + 3);
- continue;
- }
- return -E_GC_SYNTAX;
+ r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_MODE, lpr);
+ if (lls_opt_given(r) > 0) {
+ const char *arg = lls_string_val(0, r);
+ if (strcmp(arg, "s") == 0)
+ gc->mode = GM_SLOPPY;
+ else if (strcmp(arg, "a") == 0)
+ gc->mode = GM_AGGRESSIVE;
+ else if (strcmp(arg, "p") == 0)
+ gc->mode = GM_PEDANTIC;
+ else
+ return -E_GC_SYNTAX;
+ }
+
+ r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_ONE_SHOT, lpr);
+ if (lls_opt_given(r) > 0)
+ gc->flags |= GF_ONE_SHOT;
+
+ r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_PARENT, lpr);
+ if (lls_opt_given(r) > 0) {
+ const char *arg = lls_string_val(0, r);
+ gc->parent = para_strdup(arg);
+ }
+
+ r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_NAME, lpr);
+ if (lls_opt_given(r) > 0) {
+ const char *arg = lls_string_val(0, r);
+ gc->name = para_strdup(arg);
}
- if (i != argc)
- return -E_GC_SYNTAX;
return 1;
}
/**
- * Check the command line options and allocate a grab_client structure.
+ * Create and activate a grab client.
*
* \param fd The file descriptor of the client.
- * \param argc Argument count.
- * \param argv Argument vector.
+ * \param lpr The parsed command line of the grab command.
* \param s The scheduler to register the grab client task to.
*
- * If the command line options given by \a argc and \a argv are valid.
- * allocate a struct grab_client and initialize it with this valid
- * configuration.
- *
- * If the new grab client can be added to an existing buffer tree, activate it.
- * Otherwise, add it to the inactive list for later activation.
+ * This function semantically parses the arguments given as options to the grab
+ * command. On success it allocates a struct grab_client, associates it with
+ * the given file descriptor and activates it. If the new grab client can not
+ * be attached to an existing buffer tree node it is put into the inactive list
+ * for later activation.
*
* \return Standard.
*/
-int grab_client_new(int fd, int argc, char **argv, struct sched *s)
+int grab_client_new(int fd, struct lls_parse_result *lpr, struct sched *s)
{
int ret;
struct grab_client *gc = para_calloc(sizeof(struct grab_client));
- ret = gc_check_args(argc, argv, gc);
+ ret = gc_check_args(lpr, gc);
if (ret < 0)
goto err_out;
ret = dup(fd);
/** \file grab_client.h exported symbols from grab_client.c */
-int grab_client_new(int fd, int argc, char **argv, struct sched *s);
+int grab_client_new(int fd, struct lls_parse_result *lpr, struct sched *s);
void activate_grab_clients(struct sched *s);
#include <curses.h>
#include <locale.h>
#include <sys/time.h>
+#include <lopsub.h>
-#include "gui.cmdline.h"
+#include "gui.lsg.h"
#include "para.h"
#include "gui.h"
#include "string.h"
#include "list.h"
#include "sched.h"
#include "signal.h"
-#include "ggo.h"
#include "version.h"
/** Array of error strings. */
DEFINE_PARA_ERRLIST;
+static struct lls_parse_result *cmdline_lpr, *lpr;
+
+#define CMD_PTR (lls_cmd(0, gui_suite))
+#define OPT_RESULT(_name) (lls_opt_result(LSG_GUI_PARA_GUI_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
+#define FOR_EACH_KEY_MAP(_i) for (_i = 0; _i < OPT_GIVEN(KEY_MAP); _i++)
+
static char *stat_content[NUM_STAT_ITEMS];
static struct gui_window {
static pid_t exec_pid;
static int exec_fds[2] = {-1, -1};
-static struct gui_args_info conf;
static int loglevel;
/** Type of the process currently being executed. */
if (tv_diff(&st->next_exec, now, NULL) > 0)
return 0;
st->next_exec.tv_sec = now->tv_sec + 2;
- ret = para_exec_cmdline_pid(&st->pid, conf.stat_cmd_arg, fds);
+ ret = para_exec_cmdline_pid(&st->pid,
+ OPT_STRING_VAL(STAT_CMD), fds);
if (ret < 0)
return 0;
ret = mark_fd_nonblocking(fds[1]);
{
int i;
char *tmp = NULL;
+ const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
- for (i = 0; i < conf.key_map_given; ++i) {
+ FOR_EACH_KEY_MAP(i) {
char *handler, *arg;
free(tmp);
- tmp = para_strdup(conf.key_map_arg[i]);
+ tmp = para_strdup(lls_string_val(i, lor));
if (!split_key_map(tmp, &handler, &arg))
break;
if (strlen(handler) != 1)
if (find_cmd_byname(arg) < 0)
break;
}
- if (i != conf.key_map_given)
- die(EXIT_FAILURE, "invalid key map: %s\n", conf.key_map_arg[i]);
+ if (i != OPT_GIVEN(KEY_MAP))
+ die(EXIT_FAILURE, "invalid key map: %s\n",
+ lls_string_val(i, lor));
free(tmp);
}
-static void parse_config_file_or_die(bool override)
+static void parse_config_file_or_die(bool reload)
{
- bool err;
- char *config_file;
- struct gui_cmdline_parser_params params = {
- .override = override,
- .initialize = 0,
- .check_required = !override,
- .check_ambiguity = 0,
- .print_errors = 1,
- };
+ int ret;
+ char *cf = NULL, *errctx = NULL;
+ void *map;
+ size_t sz;
+ int cf_argc;
+ char **cf_argv;
+ struct lls_parse_result *cf_lpr, *merged_lpr;
- if (conf.config_file_given)
- config_file = para_strdup(conf.config_file_arg);
+ if (OPT_GIVEN(CONFIG_FILE))
+ cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
else {
char *home = para_homedir();
- config_file = make_message("%s/.paraslash/gui.conf", home);
+ cf = make_message("%s/.paraslash/gui.conf", home);
free(home);
}
- if (!file_exists(config_file)) {
- if (!conf.config_file_given)
- err = false;
- else {
- PARA_EMERG_LOG("config file %s does not exist\n",
- config_file);
- err = true;
- }
- goto out;
- }
- /*
- * When the gengetopt config file parser is called more than once, any
- * key map arguments found in the config file are _appended_ to the old
- * values, even though we turn on ->override. We want the new arguments
- * to replace the old ones, so we must empty the key_map_arg array
- * first. Unfortunately, this also clears any key map arguments given
- * at the command line.
- */
- if (override) {
- int i;
- for (i = 0; i < conf.key_map_given; i++) {
- free(conf.key_map_arg[i]);
- conf.key_map_arg[i] = NULL;
- }
- conf.key_map_given = 0;
+ ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+ if (ret < 0) {
+ if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+ goto free_cf;
+ if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE))
+ goto free_cf;
+ ret = 0;
+ lpr = cmdline_lpr;
+ goto success;
}
-
- gui_cmdline_parser_config_file(config_file, &conf, ¶ms);
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx));
+ para_munmap(map, sz);
+ if (ret < 0)
+ goto free_cf;
+ cf_argc = ret;
+ ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr, &errctx));
+ lls_free_argv(cf_argv);
+ if (ret < 0)
+ goto free_cf;
+ if (reload) /* config file overrides command line */
+ ret = lls(lls_merge(cf_lpr, cmdline_lpr, CMD_PTR, &merged_lpr,
+ &errctx));
+ else /* command line options overrride config file options */
+ ret = lls(lls_merge(cmdline_lpr, cf_lpr, CMD_PTR, &merged_lpr,
+ &errctx));
+ lls_free_parse_result(cf_lpr, CMD_PTR);
+ if (ret < 0)
+ goto free_cf;
+ if (lpr != cmdline_lpr)
+ lls_free_parse_result(lpr, CMD_PTR);
+ lpr = merged_lpr;
+success:
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
check_key_map_args_or_die();
- err = false;
-out:
- free(config_file);
- if (err)
+ theme_init(OPT_STRING_VAL(THEME), &theme);
+free_cf:
+ free(cf);
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
exit(EXIT_FAILURE);
- theme_init(conf.theme_arg, &theme);
+ }
}
/* reread configuration, terminate on errors */
static void reread_conf(void)
{
/*
- * gengetopt might print to stderr and exit on errors. So we have to
- * shutdown curses first.
+ * If the reload of the config file fails, we are about to exit. In
+ * this case we print the error message to stderr rather than to the
+ * curses window. So we have to shutdown curses first.
*/
shutdown_curses();
- parse_config_file_or_die(true /* override */);
+ parse_config_file_or_die(true);
init_curses();
print_in_bar(COLOR_MSG, "config file reloaded\n");
}
static void handle_command(int c)
{
int i;
+ const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
/* first check user-defined key bindings */
- for (i = 0; i < conf.key_map_given; ++i) {
+ FOR_EACH_KEY_MAP(i) {
char *tmp, *handler, *arg;
- tmp = para_strdup(conf.key_map_arg[i]);
+ tmp = para_strdup(lls_string_val(i, lor));
if (!split_key_map(tmp, &handler, &arg)) {
free(tmp);
return;
static void com_help(void)
{
int i;
+ const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
- for (i = 0; i < conf.key_map_given; ++i) {
- char *handler, *arg, *tmp = para_strdup(conf.key_map_arg[i]);
+ FOR_EACH_KEY_MAP(i) {
+ char *handler, *arg, *tmp = para_strdup(lls_string_val(i, lor));
const char *handler_text = "???", *desc = NULL;
if (!split_key_map(tmp, &handler, &arg)) {
com_refresh();
}
-__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);
-}
-
static int setup_tasks_and_schedule(void)
{
int ret;
struct status_task status_task = {.fd = -1};
struct input_task input_task = {.task = NULL};
struct signal_task *signal_task;
- struct sched sched = {
- .default_timeout = {
- .tv_sec = conf.timeout_arg / 1000,
- .tv_usec = (conf.timeout_arg % 1000) * 1000,
- },
- };
+ struct sched sched = {.default_timeout = {.tv_sec = 1}};
exec_task.task = task_register(&(struct task_info) {
.name = "exec",
return ret;
}
+static void handle_help_flags(void)
+{
+ char *help;
+
+ if (OPT_GIVEN(DETAILED_HELP))
+ help = lls_long_help(CMD_PTR);
+ else if (OPT_GIVEN(HELP))
+ help = lls_short_help(CMD_PTR);
+ else
+ return;
+ printf("%s\n", help);
+ free(help);
+ exit(EXIT_SUCCESS);
+}
+
/**
* The main function of para_gui.
*
*/
int main(int argc, char *argv[])
{
- gui_cmdline_parser(argc, argv, &conf); /* exits on errors */
- 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();
- parse_config_file_or_die(false /* override */);
+ int ret;
+ char *errctx;
+
+ ret = lls(lls_parse(argc, argv, CMD_PTR, &cmdline_lpr, &errctx));
+ if (ret < 0)
+ goto out;
+ lpr = cmdline_lpr;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
+ version_handle_flag("gui", OPT_GIVEN(VERSION));
+ handle_help_flags();
+ parse_config_file_or_die(false);
bot_win_rb = ringbuffer_new(RINGBUFFER_SIZE);
setlocale(LC_CTYPE, "");
initscr(); /* needed only once, always successful */
init_curses();
- return setup_tasks_and_schedule() < 0? EXIT_FAILURE : EXIT_SUCCESS;
+ ret = setup_tasks_and_schedule();
+out:
+ lls_free_parse_result(lpr, CMD_PTR);
+ if (lpr != cmdline_lpr)
+ lls_free_parse_result(cmdline_lpr, CMD_PTR);
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+ }
+ return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
}
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
+#include "recv_cmd.lsg.h"
#include "para.h"
#include "error.h"
#include "http.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "recv.h"
-#include "http_recv.cmdline.h"
#include "net.h"
#include "string.h"
#include "fd.h"
free(rn->private_data);
}
-static void *http_recv_parse_config(int argc, char **argv)
-{
- struct http_recv_args_info *tmp = para_calloc(sizeof(*tmp));
-
- http_recv_cmdline_parser(argc, argv, tmp);
- return tmp;
-}
-
static int http_recv_open(struct receiver_node *rn)
{
struct private_http_recv_data *phd;
- struct http_recv_args_info *conf = rn->conf;
- int fd, ret = para_connect_simple(IPPROTO_TCP, conf->host_arg,
- conf->port_arg);
+ struct lls_parse_result *lpr = rn->lpr;
+ const char *r_i = RECV_CMD_OPT_STRING_VAL(HTTP, HOST, lpr);
+ uint32_t r_p = RECV_CMD_OPT_UINT32_VAL(HTTP, PORT, lpr);
+ int fd, ret = para_connect_simple(IPPROTO_TCP, r_i, r_p);
if (ret < 0)
return ret;
return 1;
}
-static void http_recv_free_config(void *conf)
-{
- http_recv_cmdline_parser_free(conf);
- free(conf);
-}
-
-/**
- * The init function of the http receiver.
- *
- * \param r Pointer to the receiver struct to initialize.
- *
- * This initializes all function pointers of \a r.
- */
-void http_recv_init(struct receiver *r)
-{
- struct http_recv_args_info dummy;
-
- http_recv_cmdline_parser_init(&dummy);
- r->open = http_recv_open;
- r->close = http_recv_close;
- r->pre_select = http_recv_pre_select;
- 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)DEFINE_GGO_HELP(http_recv);
- http_recv_cmdline_parser_free(&dummy);
-}
+const struct receiver lsg_recv_cmd_com_http_user_data = {
+ .open = http_recv_open,
+ .close = http_recv_close,
+ .pre_select = http_recv_pre_select,
+ .post_select = http_recv_post_select,
+};
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
+#include "server.lsg.h"
#include "para.h"
#include "error.h"
#include "string.h"
-#include "server.cmdline.h"
#include "afh.h"
#include "server.h"
#include "http.h"
s->client_cmds[SENDER_add] = NULL;
s->client_cmds[SENDER_delete] = NULL;
- init_sender_status(hss, conf.http_access_arg, conf.http_access_given,
- conf.http_port_arg, conf.http_max_clients_arg,
- conf.http_default_deny_given);
- if (conf.http_no_autostart_given)
+ init_sender_status(hss, OPT_RESULT(HTTP_ACCESS),
+ OPT_UINT32_VAL(HTTP_PORT), OPT_UINT32_VAL(HTTP_MAX_CLIENTS),
+ OPT_GIVEN(HTTP_DEFAULT_DENY));
+ if (OPT_GIVEN(HTTP_NO_AUTOSTART))
return;
ret = generic_com_on(hss, IPPROTO_TCP);
if (ret < 0)
}
/* z[0...8n - 1], w[1...2n - 1] */
-#define PASS(name)\
-static void name(struct fft_complex *z, const fftsample_t *wre, unsigned int n)\
-{\
- fftsample_t t1, t2, t3, t4, t5, t6;\
- int o1 = 2 * n;\
- int o2 = 4 * n;\
- int o3 = 6 * n;\
- const fftsample_t *wim = wre + o1;\
- n--;\
-\
- TRANSFORM_ZERO(z[0], z[o1], z[o2], z[o3]);\
- TRANSFORM(z[1], z[o1 + 1], z[o2 + 1], z[o3 + 1], wre[1], wim[-1]);\
- do {\
- z += 2;\
- wre += 2;\
- wim -= 2;\
- TRANSFORM(z[0], z[o1], z[o2], z[o3], wre[0], wim[0]);\
- TRANSFORM(z[1], z[o1 + 1], z[o2 + 1], z[o3 + 1], wre[1], wim[-1]);\
- } while (--n);\
+static void pass(struct fft_complex *z, const fftsample_t *wre, unsigned int n)
+{
+ fftsample_t t1, t2, t3, t4, t5, t6;
+ int o1 = 2 * n;
+ int o2 = 4 * n;
+ int o3 = 6 * n;
+ const fftsample_t *wim = wre + o1;
+
+ n--;
+ TRANSFORM_ZERO(z[0], z[o1], z[o2], z[o3]);
+ TRANSFORM(z[1], z[o1 + 1], z[o2 + 1], z[o3 + 1], wre[1], wim[-1]);
+ do {
+ z += 2;
+ wre += 2;
+ wim -= 2;
+ TRANSFORM(z[0], z[o1], z[o2], z[o3], wre[0], wim[0]);
+ TRANSFORM(z[1], z[o1 + 1], z[o2 + 1], z[o3 + 1], wre[1], wim[-1]);
+ } while (--n);
}
-PASS(pass)
#undef BUTTERFLIES
#define BUTTERFLIES BUTTERFLIES_BIG
static void wipe_bottom_line(void)
{
- char x[] = " ";
- int n = i9ep->num_columns;
-
- /*
- * For reasons beyond my understanding, writing more than 68 characters
- * here causes MacOS to mess up the terminal. Writing a line of spaces
- * in smaller chunks works fine though. Weird.
- */
- fprintf(i9ep->stderr_stream, "\r");
- while (n > 0) {
- if (n >= sizeof(x)) {
- fprintf(i9ep->stderr_stream, "%s", x);
- n -= sizeof(x);
- continue;
- }
- x[n] = '\0';
- fprintf(i9ep->stderr_stream, "%s", x);
- break;
- }
- fprintf(i9ep->stderr_stream, "\r");
+ fprintf(i9ep->stderr_stream, "\r%s\r", i9ep->empty_line);
}
#ifndef RL_FREE_KEYMAP_DECLARED
return *result == (void *) -1? -ERRNO_TO_PARA_ERROR(errno) : 1;
}
+/**
+ * Get the size of a shared memory segment.
+ *
+ * \param id The shared memory segment identifier.
+ * \param result Size in bytes is returned here, zero on errors.
+ *
+ * \return Standard.
+ *
+ * \sa shmctl(2).
+ */
+int shm_size(int id, size_t *result)
+{
+ struct shmid_ds ds; /* data structure */
+
+ *result = 0;
+ if (shmctl(id, IPC_STAT, &ds) < 0)
+ return -ERRNO_TO_PARA_ERROR(errno);
+ *result = ds.shm_segsz;
+ return 1;
+}
+
/**
* Detach a shared memory segment.
*
int shm_attach(int id, enum shm_attach_mode mode, void **result);
int shm_detach(void *addr);
int shm_destroy(int id);
+int shm_size(int id, size_t *result);
size_t shm_get_shmmax(void);
+++ /dev/null
-args "--unamed-opts=audio_file --no-handle-version --no-handle-help"
-
-purpose "Print information about audio file(s)"
-
-include(header.m4)
-include(loglevel.m4)
-
-<qu>
-
-###################################
-section "printing meta information"
-###################################
-
-option "chunk-table" c
-#~~~~~~~~~~~~~~~~~~~~~
-"print also the chunk table"
-flag off
-details = "
- The 'chunk table' of an audio file is an array of offsets
- within the audio file. Each offset corresponds to chunk
- of encoded data. The exact meaning of 'chunk' depends on
- the audio format.
-
- Programs which are unaware of the particular audio format can
- read the chunk table to obtain the timing information needed
- to stream the file.
-"
-
-option "parser-friendly" p
-#~~~~~~~~~~~~~~~~~~~~~~~~~
-"do not use human-readable output format"
-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.
-
- The human-readable output (the default) consists of one output
- line per chunk and the output contains also the chunk number,
- the duration and the size of each chunk. The parser-friendly
- output prints only the offsets, in one line.
-"
-
-#############################
-section "modifying meta tags"
-#############################
-
-option "modify" m
-#~~~~~~~~~~~~~~~~
-"modify (rather than print) tags"
-flag off
-details = "
- When this option is given, para_afh creates the result file
- as a temporary copy of the given file(s), but with meta
- tags changed according to the options below. On errors,
- the temporary file is removed, leaving the original file
- unchanged. On success, if --backup is given, the original
- file is moved away. Finally the temporary file is renamed to
- the name of the original file.
-"
-
-option "backup" b
-"create backup of the original file"
-flag off
-details = "
- The backup suffix is '~', i.e. a single tilde character is appended
- to the given file name.
-"
-
-option "year" y
-#~~~~~~~~~~~~~~
-"set the year tag"
-string typestr="year"
-optional
-
-option "title" t
-#~~~~~~~~~~~~~~~
-"set the title tag"
-string typestr="title"
-optional
-
-option "artist" a
-#~~~~~~~~~~~~~~~~
-"set the artist/author tag"
-string typestr="artist"
-optional
-
-option "album" A
-#~~~~~~~~~~~~~~~
-"set the album tag"
-string typestr="album"
-optional
-
-option "comment" C
-#~~~~~~~~~~~~~~~~~
-"set the comment tag"
-string typestr="comment"
-optional
-
-</qu>
+++ /dev/null
-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.
-
- 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)
-<qu>
-option "filename" f
-#~~~~~~~~~~~~~~~~~~
-"file to open"
-string typestr = "filename"
-required
-
-option "begin-chunk" b
-#~~~~~~~~~~~~~~~~~~~~~
-"skip the beginning of the file"
-int typestr = "chunk_num"
-default = "0"
-optional
-details = "
- The chunk_num argument must be between -num_chunks and
- num_chunks - 1, inclusively, where num_chunks is the total
- number of chunks of the audio file given by the argument to
- --filename. If chunk_num is negative, the given number of
- chunks are counted backwards from the end of the file. For
- example --begin-chunk -100 instructs the afh receiver to
- start output at chunk num_chunks - 100. This is useful for
- selecting the last part of an audio file.
-"
-
-option "end-chunk" e
-#~~~~~~~~~~~~~~~~~~~
-"only write up to chunk chunk_num"
-int typestr = "chunk_num"
-optional
-details = "
- For the chunk_num argument the same rules as for --begin-chunk
- apply. The default is to write up to the last chunk.
-"
-
-option "just-in-time" j
-#~~~~~~~~~~~~~~~~~~~~~~
-"use timed writes"
-flag off
-details = "
- Write the specified chunks of data 'just in time', i.e. the
- write of each chunk is delayed until the time it is needed
- by the decoder/player in order to guarantee an uninterrupted
- audio stream. This may be useful for third-party software
- that is capable of reading from stdin.
-"
-
-option "no-header" H
-#~~~~~~~~~~~~~~~~~~~
-"do not write an audio file header"
-flag off
-details = "
- If an audio format needs information about the audio file
- in a format-specific header in order to be understood by
- the decoding software, a suitable header is automatically
- send. This option changes the default behaviour, i.e. no
- header is written.
-"
-
-</qu>
+++ /dev/null
-args "--no-version --no-help"
-
-purpose "Native ALSA output plugin"
-
-include(header.m4)
-
-<qu>
-option "device" d
-#~~~~~~~~~~~~~~~~
-"set PCM device"
-string typestr = "device"
-default = "default"
-optional
-details = "
- Check for the presence of a /proc/asound/ directory to see if
- ALSA is present in your kernel. The file /proc/asound/devices
- contains all devices ALSA knows about.
-"
-
-option "buffer-time" B
-#~~~~~~~~~~~~~~~~~~~~~
-"duration of the ALSA buffer"
-int typestr = "milliseconds"
-default = "170"
-optional
-details = "
- This is only a hint as ALSA might pick a slightly different
- time, depending on the sound hardware. The chosen value is
- shown in debug output as BUFFER_TIME.
-
- If synchronization between multiple clients is desired,
- the same buffer time should be configured for all clients.
-"
-
-</qu>
-
+++ /dev/null
-args "--no-version --no-help"
-
-purpose "Amplify the decoded audio stream"
-
-option "amp" a
-#~~~~~~~~~~~~~
-"amplification value"
-int typestr="number"
-default="32"
-optional
-details="
- The amplification value determines the scaling factor by
- which the amplitude of the audio stream is multiplied. The
- formula for the scaling factor is
-
- factor = 1 + amp / 64.
-
- For example, an amplifiction value of zero results in a
- scaling factor of one while an amplification value of 64
- means to double the volume.
-"
+++ /dev/null
-args "--no-version --no-help"
-
-purpose "Output plugin for libao"
-
-include(header.m4)
-<qu>
-
-option "driver" d
-#~~~~~~~~~~~~~~~~
-"Select a output driver by name"
-string typestr = "name"
-optional
-details = "
- If this is not given, the driver with the highest priority
- (see below) will be used.
-"
-
-option "ao-option" o
-#~~~~~~~~~~~~~~~~~~~
-"Pass a key-value pair to the libao driver"
-string typestr = "key:value"
-optional
-multiple
-details = "
- For each time this option is given, the supplied key-value
- pair is appended to the list of options for the driver. Invalid
- keys are silently ignored.
-"
-
-</qu>
+++ /dev/null
-args "--unamed-opts=command --conf-parser --no-handle-version --no-handle-help"
-
-purpose "Communicate with para_audiod through a local socket"
-
-include(header.m4)
-<qu>
-option "socket" s
-#~~~~~~~~~~~~~~~~
-"well-known socket (default=/var/paraslash/audiod.socket.$HOSTNAME)"
- string typestr="filename"
- optional
-
-
-option "bufsize" b
-#~~~~~~~~~~~~~~~~~
-"size of internal buffer"
- int typestr="bytes"
- default="8192"
- optional
-</qu>
-
-define(CURRENT_PROGRAM,para_audioc)
-define(DEFAULT_HISTORY_FILE,~/.paraslash/audioc.history)
-include(loglevel.m4)
-include(history_file.m4)
-include(complete.m4)
+++ /dev/null
-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)
-
-<qu>
-#########################
-section "General options"
-#########################
-</qu>
-
-include(loglevel.m4)
-include(color.m4)
-include(config_file.m4)
-include(logfile.m4)
-include(log_timing.m4)
-include(daemon.m4)
-include(user.m4)
-include(group.m4)
-include(priority.m4)
-
-<qu>
-########################
-section "Audiod options"
-########################
-
-option "force" F
-#~~~~~~~~~~~~~~~
-"force startup"
-flag off
-details="
- If this flag is not given, para_audiod refuses to start if the
- well-known socket file (see the --socket option) already exists
- because this usually means that para_audiod is already running
- and listening on that socket. After a crash or if para_audiod
- received a SIGKILL signal, a stale socket file might remain and
- you have to use --force once to force startup of para_audiod.
-"
-
-option "mode" m
-#~~~~~~~~~~~~~~
-"startup mode"
-string typestr="mode"
-default="on"
-optional
-details="
- Para_audiod supports three modes of operation: On, off and
- standby (sb). This option selects the mode that should be
- used on startup. If para_audiod operates in \"on\" mode, it
- will connect to para_server in order to receive its status
- information. If para_server announces the availability of an
- audio stream, para_audiod will automatically download, decode
- and play the audio stream according to the given stream I/O
- options, see below.
-
- In \"standby\" mode, para_audiod will only receive the
- status information from para_server but will not download
- the audio stream.
-
- In \"off\" mode, para_audiod does not connect para_server at
- all, but still listens on the local socket for connections.
-"
-
-option "socket" s
-#~~~~~~~~~~~~~~~~
-"well-known socket"
-string typestr="filename"
-optional
-details="
- Para_audiod uses a \"well-known\" socket to listen
- on for connections from para_audioc. This socket is a
- special file in the file system; its location defaults to
- /var/paraslash/audiod_sock.<host_name>.
-
- para_audioc, the client program used to connect to para_audiod,
- opens this socket in order to talk to para_audiod. If the
- default value for para_audiod is changed, para_audioc must be
- instructed to use also \"filename\" for connecting para_audiod.
-"
-
-option "user-allow" -
-#~~~~~~~~~~~~~~~~~~~~
-"allow this user to connect to audiod"
-string typestr = "username"
-optional
-multiple
-details = "
- Allow the user identified by username (either a string or
- a UID) to connect to para_audiod. This option may be given
- multiple times. If not specified at all, all users are allowed
- to connect.
-
- This feature is based on the ability to send unix
- credentials through local sockets using ancillary data
- (SCM_CREDENTIALS). Currently it only works on Linux. On
- other operating systems the option is silently ignored and
- all local users are allowed to connect.
-"
-
-option "clock-diff-count" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"sync clock on startup"
-int typestr="count"
-default="0"
-optional
-details="
- Check the clock difference between the host running para_server
- and the local host running para_audiod that many times before
- starting any stream I/0. Set this to non-zero for non-local
- setups if the clocks of these two hosts are not synchronized
- by ntp or similar.
-"
-
-#############################
-section "Stream I/O options"
-#############################
-
-option "receiver" r
-#~~~~~~~~~~~~~~~~~~
-"select receiver"
-string typestr="receiver_spec"
-default="http"
-optional
-multiple
-details="
- This option may be given multiple times, for each audio format
- separately. If multiple definitions for an audio format are
- given, the first one is selected.
-
- The \"receiver_spec\" consists of an audio format specifier
- and one or more receiver arguments, separated by a colon.
-
- The audio format specifier is a regular expression which
- specifies the set of audio formats for which this option
- should apply.
-
- If any receiver options are present, the whole receiver
- argument must be quoted:
-
- -r 'mp3:http -i my.host.org -p 8009'
-
- Since a single dot '.' matches the name of any audio format,
- specifying '.' instead of 'mp3' above activates the http
- receiver for all audio formats.
-
-"
-
-option "filter" f
-#~~~~~~~~~~~~~~~~
-"Specify the filter configuration."
-string typestr = "filter_spec"
-optional
-multiple
-details = "
- This option may be given multiple times. The \"filter_spec\"
- consists of an audio format specifier (see above), the name
- of the filter, and any options for that filter. Note that
- order matters.
-
- The compiled-in defaults apply to all audio formats for which
- no --filter option was given. These defaults depend on the
- receiver being used.
-
- For HTTP streams, only the decoder for the current audio
- format is activated. UDP and DCCP streams, on the other
- hand, are sent FEC-encoded by para_server. In order to play
- such streams, the receiver output must be FEC-decoded first,
- i.e. fed to the fecdec filter. Therefore the default for UDP
- and DCCP streams is to activate the fecdec filter, followed
- by the decoding filter for the audio format.
-
- Examples:
-
- --filter 'mp3:mp3dec'
-
- --filter 'mp3|aac:compress --inertia 5 --damp 2'
-
- --filter '.:fecdec'
-
-"
-
-option "writer" w
-#~~~~~~~~~~~~~~~~
-"Specify stream writer."
-string typestr="writer_spec"
-optional
-multiple
-details="
- May be given multiple times, even multiple times for the same
- audio format. Default value is \"alsa\" for all supported
- audio formats. Example:
-
- --writer 'aac|wma:oss'
-
-"
-
-option "stream-delay" -
-#~~~~~~~~~~~~~~~~~~~~~~
-"time for client sync"
-int typestr="milliseconds"
-default="200"
-optional
-details="
- Add the given amount of milliseconds to the stream start time
- announced by para_server and do not send data to the writer
- before that time (modulo clock difference).
-
- This is useful mainly for synchronizing the audio output of
- different clients.
-"
-</qu>
+++ /dev/null
-<qu>
-option "channels" c
-#~~~~~~~~~~~~~~~~~~
-"specify number of channels"
-int typestr = "num"
-default = "2"
-optional
-details = "
- It is only necessary to specify this option for raw audio. If
- it is not given, the channel count is queried from the parent
- buffer tree nodes (e.g. the decoder) or the wav header. Only
- if this fails, the default value applies.
-"
-</qu>
+++ /dev/null
-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)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/client.conf)
-define(DEFAULT_HISTORY_FILE,~/.paraslash/client.history)
-<qu>
-option "hostname" i
-#~~~~~~~~~~~~~~~~~~
-"ip or host to connect"
-string typestr = "host"
-default = "localhost"
-optional
-
-option "user" u
-#~~~~~~~~~~~~~~
-"paraslash username"
-string typestr = "username"
-default = "<current user>"
-optional
-
-option "server-port" p
-#~~~~~~~~~~~~~~~~~~~~~
-"port to connect"
-int typestr = "port"
-default = "2990"
-optional
-
-option "key-file" k
-#~~~~~~~~~~~~~~~~~~
-"path to private key"
-string typestr = "filename"
-optional
-details = "
- If not given, the following files are tried, in order:
- $HOME/.paraslash/key.$LOGNAME, $HOME/.ssh/id_rsa. It is a fatal
- error if the key file can not be opened, or is world-readable.
-"
-</qu>
-
-include(loglevel.m4)
-include(config_file.m4)
-include(history_file.m4)
-include(complete.m4)
+++ /dev/null
-<qu>
-
-option "color" C
-#~~~~~~~~~~~~~~~
-"activate color output"
-enum typestr="when"
-values = "yes","no","auto"
-default = "auto"
-optional
-
-option "log-color" -
-#~~~~~~~~~~~~~~~~~~~
-"select a color for one type of log message"
-string typestr="color_spec"
-multiple
-optional
-details="
- The format of \"color_spec\" is [fg [bg]] [attr].
-
- Valid colors for \"fg\" and \"bg\" are \"normal\", \"black\",
- \"red\", \"green\", \"yellow\", \"blue\", \"magenta\",
- \"cyan\", and \"white\".
-
- The \"attr\" value must be one of \"bold\", \"dim\", \"ul\",
- \"blink\", \"reverse\".
-
- Examples:
-
- --log-color \"debug:green\"
- --log-color \"info:yellow bold\"
- --log-color \"notice:white red bold\"
-"
-
-</qu>
+++ /dev/null
-<qu>
-option "complete" -
-#~~~~~~~~~~~~~~~~~~
-"print possible command line completions"
- flag off
- details = "
- If this flag is given, </qu>CURRENT_PROGRAM<qu> reads the environment
- variables COMP_LINE and COMP_POINT to obtain the current command line
- and the cursor position respectively, prints possible completions
- to stdout and exits.
-"
-</qu>
+++ /dev/null
-args "--no-version --no-help"
-
-purpose "Dynamically adjust the volume of an audio stream"
-
-option "blocksize" b
-#~~~~~~~~~~~~~~~~~~~
-"adjust block size"
-int typestr="number"
-default="15"
-optional
-details = "
- Larger blocksize means fewer volume adjustments per time unit.
-"
-
-option "aggressiveness" a
-#~~~~~~~~~~~~~~~~~~~~~~~~
- "controls the maximum amount to amplify by"
-int typestr="number"
-default="4"
-optional
-
-option "inertia" i
-#~~~~~~~~~~~~~~~~~
- "how much inertia ramping has"
- int typestr="number"
-default="6"
-optional
-
-option "target-level" t
-#~~~~~~~~~~~~~~~~~~~~~~
-"target signal level (0-32768)"
-int typestr="number"
-default="20000"
-optional
-
-option "damp" d
-#~~~~~~~~~~~~~~
-"if non-zero, scale down after normalizing"
-int typestr="number"
-default="0"
-optional
+++ /dev/null
-<qu>
-option "config-file" c
-#~~~~~~~~~~~~~~~~~~~~~
-"(default='</qu>DEFAULT_CONFIG_FILE<qu>')"
-string typestr="filename"
-optional
-details="
- </qu>CURRENT_PROGRAM<qu> reads its config file right after parsing
- the options that were given at the command line. If an
- option is given both at the command line and in the
- config file, the value that was specified at the command line
- takes precedence.
-"
-</qu>
+++ /dev/null
-<qu>
-option "daemon" d
-#~~~~~~~~~~~~~~~~
-"run as background daemon"
-flag off
-details = "
- If this option is given and no logfile was specified, all
- messages go to /dev/null.
-"
-</qu>
+++ /dev/null
-args "--no-version --no-help"
-
-purpose "Receive a DCCP audio stream"
-
-option "host" i
-"ip or host"
-string default="localhost"
-optional
-details="
- Both IPv4 and IPv6 addresses are supported.
-"
-
-option "port" p
-"port to connect to"
-int
-default="8000"
-optional
-
-option "ccid" c
-"CCID preference(s) for this connection"
-int
-# restrict the maximum number of times this option can be passed
-optional multiple(-10)
-# currently known CCIDs:
-# - CCID-2 (RFC 4341),
-# - CCID-3 (RFC 4342),
-# - CCID-4 (RFC 5622),
-# - CCID-248 ... CCID-254 are experimental (RFC 4340, 19.5)
-values="2", "3", "4", "248", "249", "250", "251", "252", "253", "254"
-details="
- When present exactly once, this option mandates the CCID for the
- sender-receiver connection. If it is passed more than once, it sets
- a preference list where the order of appearance signifies descending
- priority. For example, passing 4, 2, 3 creates the preference list
- (CCID-4, CCID-2, CCID-3), assigning CCID-4 highest preference.
-
- The request is reconciled with the CCIDs on the server through the
- 'server-priority' mechanism of RFC 4340 6.3.1/10. The server CCIDs
- can be listed by calling 'para_client si'.
-
-"
+++ /dev/null
-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)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/fade.conf)
-<qu>
-section "General options"
-#########################
-
-</qu>
-include(config_file.m4)
-include(loglevel.m4)
-<qu>
-option "mode" o
-#~~~~~~~~~~~~~~
-"how to fade volume"
- enum typestr = "mode"
- values = "sleep", "fade", "set", "snooze"
- default = "sleep"
- optional
- details="
- para_fade knows the following modes:
-
- sleep mode: Change to the initial volume and select
- the initial afs mood/playlist. Then fade out until
- the fade-out volume is reached. Switch to the
- sleep mood/playlist until wake time minus fade-in
- time. Finally switch to the wake mood/playlist and
- fade to the fade-in volume.
-
- fade: Fade the volume to the given value in the
- given time.
-
- set: Just set the value and exit.
-
- snooze: Fade out, sleep a bit and fade in.
-"
-
-option "mixer-api" a
-#~~~~~~~~~~~~~~~~~~~
-"choose the mixer API"
- string typestr = "api"
- optional
- details = "
- ALSA is preferred over OSS if both APIs are supported
- and this option is not given. To see the supported
- mixer APIs, use this option with an invalid string
- as the mixer API, e.g. --mixer-api help.
- "
-
-option "mixer-device" m
-#~~~~~~~~~~~~~~~~~~~~~~
-"choose mixer device"
- string typestr = "device"
- optional
- details = "
- The default device (used if this option is not given)
- depends on the selected mixer API. For ALSA, the
- default is 'hw:0' which corresponds to the first sound
- device. For OSS, '/dev/mixer' is used as the default.
- "
-
-option "mixer-channel" C
-#~~~~~~~~~~~~~~~~~~~~~~~
-"select the analog mixer channel"
- string typestr = "channel"
- optional
- details = "
- For the ALSA mixer API, the possible values are
- determined at runtime depending on the hardware and
- can be printed by specifying an invalid mixer channel,
- for example --mixer-channel help. The default channel
- is 'Master'.
-
- For OSS the possible values are invariably 'volume',
- 'bass', 'treble', 'synth', 'pcm', 'speaker', 'line',
- 'mic', 'cd', 'imix', 'altpcm', 'reclev', 'igain',
- 'ogain'. However, not all listed channels might be
- supported on any particular hardware. The default
- channel is 'volume'.
- "
-
-section "Options for sleep mode"
-################################
-
-option "ivol" -
-#~~~~~~~~~~~~~~
-"set initial volume"
- string typestr = "[channel:]volume"
- default = "60"
- optional
- multiple
- details = "
- Used as the start volume, before fading out to the
- fade out volume. The channel part may be omitted, in
- which case the default channel is used. This option
- may be given multiple times.
- "
-
-option "fo-mood" -
-#~~~~~~~~~~~~~~~~~
-"afs mood/playlist during fade out"
- string typestr = "mood_spec"
- default = "m/fade"
- optional
- details = "
- Select this mood right after setting the
- volume. Example: --fo-mood m/sleep
-"
-
-option "fo-time" -
-#~~~~~~~~~~~~~~~~~
-"fall asleep fade out time"
- int typestr = "seconds"
- default = "1800"
- optional
- details = "
- No fading if set to 0.
- "
-
-option "fo-vol" -
-#~~~~~~~~~~~~~~~~
-"volume to fade out to"
- int typestr = "volume"
- default = "20"
- optional
-
-option "sleep-mood" -
-#~~~~~~~~~~~~~~~~~~~~
-"sleep time afs mood/playlist"
- string typestr = "mood_spec"
- default = "m/sleep"
- optional
- details = "
- Select the given afs mood/playlist after the fade
- out is complete. If unset, the \"stop\" command is
- sent to para_server.
- "
-
-option "wake-hour" H
-#~~~~~~~~~~~~~~~~~~~
-"(0-23) (default: now + 9 hours)"
- int typestr = "hour"
- optional
-
-option "wake-min" M
-#~~~~~~~~~~~~~~~~~~
-"(0-59)"
- int typestr = "minutes"
- default = "0"
- optional
-
-option "fi-mood" -
-#~~~~~~~~~~~~~~~~~
-"afs mood/playlist during fade in"
- string typestr = "mood_spec"
- default = "m/wake"
- optional
- details = "
- Change to this afs mood/playlist on wake time.
- "
-
-option "fi-time" -
-#~~~~~~~~~~~~~~~~~
-"wake up fade in time"
- int typestr="seconds"
- default="1200"
- optional
- details = "
- No fading in if set to 0.
- "
-
-option "fi-vol" -
-#~~~~~~~~~~~~~~~~
-"volume to fade to at wake time"
- int typestr = "volume"
- default = "80"
- optional
-
-section "Options for snooze mode"
-#################################
-
-option "so-time" -
-#~~~~~~~~~~~~~~~~~
-"snooze-out time"
- int typestr = "seconds"
- default = "30"
- optional
-
-option "so-vol" -
-#~~~~~~~~~~~~~~~~
-"volume to fade to before snooze"
- int typestr = "volume"
- default = "20"
- optional
-
-option "snooze-time" -
-#~~~~~~~~~~~~~~~~~~~~~
-"delay"
- int typestr = "seconds"
- default = "600"
- optional
-
-option "si-time" -
-#~~~~~~~~~~~~~~~~~
-"snooze-in time"
- int typestr = "seconds"
- default = "180"
- optional
-
-option "si-vol" -
-#~~~~~~~~~~~~~~~~
-"volume to fade to after snooze"
- int typestr = "volume"
- default = "80"
- optional
-
-section "Options for fade mode"
-###############################
-
-option "fade-vol" f
-#~~~~~~~~~~~~~~~~~~
-"volume to fade to"
- int typestr = "volume"
- default = "50"
- optional
-
-option "fade-time" t
-#~~~~~~~~~~~~~~~~~~~
-"fading time"
- int typestr = "seconds"
- default = "5"
- optional
-
-section "Options for set mode"
-##############################
-
-option "val" -
-"value to set"
- int typestr = "value"
- default = "0"
- optional
-
-</qu>
+++ /dev/null
-args "--no-version --no-help"
-
-purpose "Output plugin that writes to a local file"
-
-option "filename" f
-#~~~~~~~~~~~~~~~~~~
-"specify output file name"
-string typestr="filename"
-optional
-details="
- Defaults to a random filename in ~/.paraslash.
-"
-
+++ /dev/null
-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)
-<qu>
-option "filter" f
-#~~~~~~~~~~~~~~~~
-"Specify filter."
-string typestr="filter_spec"
-optional
-multiple
-details="
- May be given multiple times to 'pipe' the stream through
- arbitrary many filters (without copying the data). The same
- filter may appear more than once, order matters.
-
- Options for a particular filter may be specified for each
- given '--filter' option separately. You will have to quote
- these options like this:
-
- --filter 'compress --inertia 5 --damp 2'
-"
-</qu>
+++ /dev/null
-option "group" g
-#~~~~~~~~~~~~~~~
-"set group id"
-string typestr="group"
-optional
-details="
- This option sets the group id according to 'group'. This option
- is silently ignored if EUID != 0. Otherwise, real/effective
- GID and the saved set-group ID are all set to the GID given by
- 'group'. Must not be given in the config file.
-"
-
+++ /dev/null
-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)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/gui.conf)
-
-<qu>
-#########################
-section "General options"
-#########################
-</qu>
-
-include(config_file.m4)
-include(loglevel.m4)
-
-<qu>
-option "timeout" t
-#~~~~~~~~~~~~~~~~~
-"set timeout"
-int typestr = "milliseconds"
-default = "30"
-optional
-
-option "theme" T
-#~~~~~~~~~~~~~~~
-"select startup theme"
-string typestr = "name"
-optional
-details = "
- If this option is not given the default theme is used.
- If the given name is not a valid theme name, the list of
- available themes is printed and the program terminates.
-"
-
-option "stat-cmd" s
-#~~~~~~~~~~~~~~~~~~
-"command to read status items from"
-string typestr = "command"
-default = "para_audioc -- stat -p"
-optional
-details = "
- On a host on which the para_audiod service is not is running, the
- default command will fail. An alternative is
-
- para_client -- stat -p
-
- This command connects para_server instead of para_audiod. However,
- no timing information about the current audio file is printed.
-"
-
-#---------------------------------
-section "Mapping keys to commands"
-#---------------------------------
-
-option "key-map" k
-#~~~~~~~~~~~~~~~~~
-"Map key k to command c using mode m."
-
-string typestr = "k:m:c"
-optional
-multiple
-details = "
- Mode may be d, x or p for display, external and paraslash
- commands, respectively. Of course, this option may be given
- multiple times, one for each key mapping.
-"
-</qu>
+++ /dev/null
-changequote(<qu>,</qu>)
+++ /dev/null
-<qu>
-option "history-file" -
-#~~~~~~~~~~~~~~~~~~~~~~
-"(default='</qu>DEFAULT_HISTORY_FILE<qu>')"
-string typestr = "filename"
-optional
-details = "
- If </qu>CURRENT_PROGRAM<qu> runs in interactive mode, it reads the history
- file on startup. Upon exit, the in-memory history is appended
- to the history file.
-"
-</qu>
+++ /dev/null
-args "--no-version --no-help"
-
-purpose "Receive an HTTP audio stream"
-
-include(header.m4)
-
-<qu>
-option "host" i
-#~~~~~~~~~~~~~~
-"ip or host"
-string
-default="localhost"
-optional
-details="
- Both IPv4 and IPv6 addresses are supported.
-"
-
-option "port" p
-#~~~~~~~~~~~~~~
-"tcp port to connect to"
-int default="8000"
-optional
-</qu>
+++ /dev/null
-<qu>
-option "log-timing" T
-#~~~~~~~~~~~~~~~~~~~~
-"show milliseconds in log messages"
-flag off
-details = "
- Selecting this option causes milliseconds to be included in
- the log message output. This allows to measure the interval
- between log messages in milliseconds which is useful for
- identifying timing problems.
-"
-</qu>
+++ /dev/null
-<qu>
-option "logfile" L
-#~~~~~~~~~~~~~~~~~
-"where to write log output"
-string typestr="filename"
-optional
-details="
- If this option is not given, </qu>CURRENT_PROGRAM<qu> writes the log
- messages to stderr.
-"
-</qu>
+++ /dev/null
-<qu>
-option "loglevel" l
-#~~~~~~~~~~~~~~~~~~
-"set loglevel"
-string typestr="level"
-values = "debug","info","notice","warning","error","crit","emerg"
-default="warning"
-optional
-details="
- Log only messages with severity greater or equal the given
- value.
-
- debug: Produces really noisy output.
- info: Still noisy, but won't fill up the disk quickly.
- notice: Indicates normal, but significant event.
- warning: Unexpected events that can be handled.
- error: Unhandled error condition.
- crit: System might be unreliable.
- emerg: Last message before exit.
-"
-
-</qu>
+++ /dev/null
-define ggo_opts
- --output-dir=$(cmdline_dir) \
- --set-version="$(GIT_VERSION)" \
- --arg-struct-name=$(*F)_args_info \
- --file-name=$(*F).cmdline \
- --func-name=$(*F)_cmdline_parser \
- --set-package="para_$(*F)"
-endef
-
-.PRECIOUS: $(cmdline_dir)/%.cmdline.c $(cmdline_dir)/%.cmdline.h $(ggo_dir)/%.ggo
-
-$(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
-
-$(m4depdir)/%.m4d: $(m4_ggo_dir)/%.m4 | $(m4depdir)
- @[ -z "$(Q)" ] || echo 'M4D $<'
- $(Q) $(M4) -I $(m4_ggo_dir) -s $< \
- | awk '{if ($$1 ~ /#line/) {gsub(/"/, "", $$3); if ($$3 != "$<") \
- print "$(ggo_dir)/$(*F).ggo: " $$3}}' | sort | uniq > $@
-
-$(ggo_dir)/%.ggo: $(m4_ggo_dir)/%.m4 $(m4_ggo_dir)/header.m4 | $(ggo_dir)
- @[ -z "$(Q)" ] || echo 'M4 $<'
- $(Q) $(M4) -I $(m4_ggo_dir) $< > $@
+++ /dev/null
-args "--no-version --no-help"
-
-purpose "Decode an mp3 stream"
-
-include(header.m4)
-
-<qu>
-option "ignore-crc" i
-#~~~~~~~~~~~~~~~~~~~~
-"ignore CRC information in the audio stream."
-flag off
-details="
- This causes frames with CRC errors to be decoded and played
- anyway. This option is not recommended, but since some encoders
- have been known to generate bad CRC information, this option
- is a work-around to play streams from such encoders.
-"
-</qu>
+++ /dev/null
-args "--no-version --no-help"
-
-purpose "Output plugin for the Open Sound System"
-
-option "device" d
-#~~~~~~~~~~~~~~~~
-"set PCM device"
-string typestr="device"
-default="/dev/dsp"
-optional
-details = ""
+++ /dev/null
-args "--no-version --no-help"
-
-purpose "Output plugin for Mac OS coreaudio"
-
-section "osx options"
-#####################
-
-option "numbuffers" n
-#~~~~~~~~~~~~~~~~~~~~~
-
-"number of audio buffers to allocate (increase if
-you get buffer underruns)"
-
- int typestr="num"
- default="20"
- optional
- details = ""
+++ /dev/null
-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)
-define(DEFAULT_HISTORY_FILE,~/.paraslash/play.history)
-
-<qu>
-#########################
-section "General options"
-#########################
-</qu>
-include(loglevel.m4)
-include(config_file.m4)
-include(history_file.m4)
-<qu>
-
-###############################
-section "Options for para_play"
-###############################
-
-option "randomize" z
-#~~~~~~~~~~~~~~~~~~~
-"randomize playlist at startup."
-flag off
-
-option "key-map" k
-#~~~~~~~~~~~~~~~~~
-"Map key k to a command."
-
-string typestr = "key:command [args]"
-optional
-multiple
-details = "
- This option may be given multiple times, one for each key
- mapping. Example:
- 5:jmp 50
-"
-</qu>
+++ /dev/null
-args "--no-version --no-help"
-
-purpose "Delay processing of an audio stream"
-
-option "duration" d
-#~~~~~~~~~~~~~~~~~~
-"prebuffer time"
-int typestr="milliseconds"
-default="200"
-optional
-details="
- Wait that many milliseconds before letting data go through.
- The time interval starts when the first data byte is seen by
- this filter.
-"
-
-option "size" s
-#~~~~~~~~~~~~~~
-"amount of data to prebuffer"
-int typestr="bytes"
-default="0"
-optional
-details="
- Wait until that many data bytes are available in the input buffer.
- The default value of zero means to not prebuffer by size at all.
- If both --duration and --size options are given and non-zero, the
- filter waits until both conditions are met.
-"
+++ /dev/null
-option "priority" -
-#~~~~~~~~~~~~~~~~~~
-"adjust scheduling priority"
-int typestr = "prio"
-default = "0"
-optional
-details = "
- The priority (also known as nice value) is a value in the range -20
- to 19. Lower priorities cause more favorable scheduling. Since only
- privileged processes may request a negative priority, specifying
- a negative value works only if the daemon is started with root
- privileges.
-
- Failure to set the given priority value is not considered an error
- but a log message is printed in this case.
-"
+++ /dev/null
-args "--no-handle-help --no-handle-version"
-
-purpose "A command line HTTP/DCCP/UDP stream grabber"
-
-include(header.m4)
-include(loglevel.m4)
-
-<qu>
-option "receiver" r
-"Select receiver"
-string typestr="receiver_spec"
-default="http"
-optional
-details="
- Any options for the selected receiver must
- be quoted. Example:
-
- -r 'http -i www.paraslash.org -p 8009'
-"
-</qu>
+++ /dev/null
-args "--no-version --no-help"
-
-purpose "Transform raw audio to a different sample rate"
-
-include(header.m4)
-
-option "converter" C
-#~~~~~~~~~~~~~~~~~~~
-"choose converter type"
-enum typestr = "type"
-values = "best", "medium", "fastest", "zero_order_hold", "linear"
-default = "medium"
-optional
-
-details = "
- best: This is a bandlimited interpolator derived from the
- mathematical sinc function and this is the highest quality
- sinc based converter, providing a worst case Signal-to-Noise
- Ratio (SNR) of 97 decibels (dB) at a bandwidth of 97%.
-
- medium: This is another bandlimited interpolator much like the
- previous one. It has an SNR of 97dB and a bandwidth of 90%. The
- speed of the conversion is much faster than the previous one.
-
- fastest: This is the fastest bandlimited interpolator and
- has an SNR of 97dB and a bandwidth of 80%.
-
- zero_order_hold: A Zero Order Hold converter (interpolated
- value is equal to the last value). The quality is poor but
- the conversion speed is blindlingly fast.
-
- linear: A linear converter. Again the quality is poor, but
- the conversion speed is blindingly fast.
-"
-
-include(channels.m4)
-include(sample_rate.m4)
-include(sample_format.m4)
-
-option "dest-sample-rate" d
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"sample rate to convert to"
-int typestr = "rate"
-default = "44100"
-optional
+++ /dev/null
-<qu>
-option "sample-format" f
-#~~~~~~~~~~~~~~~~~~~~~~~
-"specify sample format"
-# This must match the enum sample_format of para.h
-values = "S8", "U8", "S16_LE", "S16_BE", "U16_LE", "U16_BE" enum
-default = "S16_LE"
-optional
-details = "
- It is only necessary to specify this for raw audio. See the
- discussion of the --channels option.
-"
-</qu>
+++ /dev/null
-<qu>
-option "sample-rate" s
-#~~~~~~~~~~~~~~~~~~~~~
-"do not guess the input sample rate"
-int typestr = "num"
-default = "44100"
-optional
-details = "
- It is only necessary to specify this for raw audio. See the
- discussion of the --channels option.
-"
-</qu>
+++ /dev/null
-args "--conf-parser --no-handle-version --no-handle-help"
-
-purpose "Manage and stream audio files"
-
-include(header.m4)
-define(CURRENT_PROGRAM,para_server)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/server.conf)
-
-<qu>
-#########################
-section "General options"
-#########################
-</qu>
-
-include(loglevel.m4)
-include(log_timing.m4)
-include(color.m4)
-include(daemon.m4)
-include(user.m4)
-include(group.m4)
-include(priority.m4)
-
-<qu>
-option "port" p
-#~~~~~~~~~~~~~~
-"listening port"
-int typestr="portnumber"
-default="2990"
-optional
-details="
- para_server listens on this tcp port for incoming connections
- from clients such as para_client. If the default port is
- changed, the corresponding option of para_client must be used
- to connect to para_server.
-"
-
-#############################
-section "Configuration files"
-#############################
-</qu>
-
-include(logfile.m4)
-include(config_file.m4)
-
-<qu>
-option "user-list" -
-#~~~~~~~~~~~~~~~~~~~
-"(default='~/.paraslash/server.users')"
-
-string typestr="filename"
-optional
-
-
-##################################
-section "virtual streaming system"
-##################################
-
-
-option "autoplay" a
-#~~~~~~~~~~~~~~~~~~
-"start playing on startup"
-flag off
-
-option "autoplay-delay" -
-#~~~~~~~~~~~~~~~~~~~~~~~~
-"time to wait before streaming"
-int typestr="ms"
-default="0"
-optional
-dependon="autoplay"
-details="
- If para_server is started with the autoplay option, this option
- may be used to set up a delay before para_server streams its
- first audio file. This is useful for example if para_server
- and para_audiod are started during system startup. The delay
- time should be choosen large enough so that para_audiod is
- already up when para_server starts to stream. Of course, this
- option depends on the autoplay option.
-"
-option "announce-time" A
-#~~~~~~~~~~~~~~~~~~~~~~~
-"grace time for clients"
-
-int typestr="ms"
-default="300"
-optional
-details="
- Clients such as para_audiod connect to para_server and execute
- the stat command to find out whether an audio stream is
- currently available. This sets the delay betweeen announcing
- the stream via the output of the stat command and sending
- the first chunk of data.
-"
-
-#############################
-section "audio file selector"
-#############################
-
-option "afs-database-dir" D
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"location of the database"
-string typestr="path"
-optional
-details="
- Where para_server should look for the osl database of the audio
- file selector. The default is '~/.paraslash/afs_database-0.4'.
-"
-
-option "afs-socket" s
-#~~~~~~~~~~~~~~~~~~~~
-"Command socket for afs"
-string typestr="path"
-default="/var/paraslash/afs_command_socket-0.4"
-optional
-details="
- For each server command that is handled by the audio file
- selector, the child process of para_server connects to the
- audio file selector via a local socket. This option specifies
- the location of that socket in the file system.
-"
-option "afs-initial-mode" i
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-"Mood or playlist to load on startup."
-string typestr="<specifier>/<name>"
-optional
-
-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
- loads the playlist named 'foo'.
-"
-
-#####################
-section "http sender"
-#####################
-
-
-option "http-port" -
-#~~~~~~~~~~~~~~~~~~~
-"tcp port for http streaming"
-int typestr="portnumber"
-default="8000"
-optional
-details="
- The http sender of para_server listens on this port for
- incoming connections. Clients are expected to send the usual
- http request message such as 'GET / HTTP/'.
-"
-
-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
- 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.
-"
-
-option "http-access" -
-#~~~~~~~~~~~~~~~~~~~~~
-"add an entry to the http ACL"
-string typestr="a.b.c.d/n"
-optional
-multiple
-details="
- Add given host/network to access control list (whitelist if
- 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" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"do not open tcp port on startup"
-flag off
-details="
- If this option is given, the http sender does not listen on
- its tcp port. It may be instructed to open this port at a
- later time by using the sender command.
-"
-
-option "http-max-clients" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"maximal number of connections"
-int typestr="number"
-default="-1"
-optional
-details="
- The http sender will refuse connections if already that number
- of clients are currently connected. A non-positive value
- (the default) allows an unlimited number of simultaneous
- connections.
-"
-
-#####################
-section "dccp sender"
-#####################
-
-
-option "dccp-port" -
-#~~~~~~~~~~~~~~~~~~~
-"port for dccp streaming"
-int typestr="portnumber"
-default="8000"
-optional
-details="
- See http-port for details.
-"
-
-option "dccp-default-deny" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"make the dccp ACL a whitelist"
-flag off
-details="
- See http-default-deny for details.
-"
-
-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.
-"
-
-option "dccp-max-clients" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"maximal number of connections"
-int typestr="number"
-default="-1"
-optional
-details="
- See http-max-clients for details.
-"
-
-option "dccp-max-slice-size" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"Upper bound for the FEC slice size"
-int typestr = "size"
-optional
-default = "0"
-details = "
- If this value is non-positive (the default) the dccp sender
- uses the maximum packet size (MPS) of the connection as the
- slice size. The MPS is a network parameter and depends on
- the path maximum transmission unit (path MTU) of an incoming
- connection, i.e. on the largest packet size that can be
- transmitted without causing fragmentation.
-
- This option allows to use a value less than the MPS in order
- to fine-tune application performance. Values greater than
- the MPS of an incoming connection can not be set.
-"
-
-option "dccp-data-slices-per-group" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"The number of non-redundant slices per FEC group"
-int typestr = "num"
-optional
-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.
-
- Note that the duration of a FEC group is proportional to the
- product dccp-max-slice-size * dccp-data-slices-per-group.
-"
-
-option "dccp-slices-per-group" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"The total number of slices per FEC group"
-int typestr = "num"
-optional
-default = "4"
-details = "
- This value must be larger than the value of the argument to
- --dccp-data-slices-per-group. The difference of the two values is
- the number of redundant slices, that is, the number of slices which
- may be lost without causing interruptions in the audio stream.
-
- Increase this value if you are on a lossy network.
-"
-
-####################
-section "udp sender"
-####################
-
-option "udp-target" -
-#~~~~~~~~~~~~~~~~~~~~
-"add udp target with optional port"
-string typestr="host[:port]"
-optional
-multiple
-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.
-
- The following examples are possible targets:
- '10.10.1.2:8000' (host:port); '10.10.1.2' (with default port);
- '224.0.1.38:1500' (IPv4 multicast); 'localhost:8001' (hostname
- with port); '[::1]:8001' (IPv6 localhost); '[badc0de::1]' (IPv6
- host with default port); '[FF00::beef]:1500' (IPv6 multicast).
-
- This option can be given multiple times, for multiple targets.
-"
-
-option "udp-no-autostart" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"do not start sending"
-flag off
-details="
- If this option is given, udp streaming may be activated at
- a later time by using the sender command.
-"
-
-option "udp-default-port" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"udp port to send to"
-int typestr="port"
-default="8000"
-optional
-
-option "udp-mcast-iface" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"outgoing udp multicast interface"
-string
-optional
-
-option "udp-header-interval" H
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"duration for sending header"
-int typestr = "ms"
-default = "2000"
-optional
-details = "
- As the udp sender has no idea about connected clients it
- sends the audio file header periodically if necessary. This
- 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
-#~~~~~~~~~~~~~~~~~
-"set time to live value"
-int typestr="num"
-default="-1"
-optional
-details="
- This option applies exclusively to multicast UDPv4/v6 streaming.
-
- For the sending UDPv4 socket it sets the multicast Time-To-Live
- value to \"num\". Traditional TTL scope values are: 0=host,
- 1=network, 32=same site, 64=same region, 128=same continent,
- 255=unrestricted. Please note however that this scoping is not
- a good solution: RFC 2365 e.g. presents a better alternative.
-
- When using UDPv6 multicasting, the option sets the number of
- multicast hops (as described in RFC 3493); a value of -1
- allows the kernel to auto-select the hop value.
-"
-</qu>
+++ /dev/null
-args "--no-version --no-help"
-
-purpose "Synchronize playback between multiple clients."
-
-option "buddy" b
-#~~~~~~~~~~~~~~~
-"host to synchronize with"
-multiple
-string typestr = "url"
-optional
-details = "
- This option may be given multiple times, one per buddy. Each
- value may be given as a host, port pair in either IPv4 or
- IPv6 form, with port being optional. If no port was specified
- the listening port (as specified with --port, see below)
- is used to send the synchronization packet to this buddy.
-"
-
-option "port" p
-#~~~~~~~~~~~~~~
-"UDP port for incoming synchronization packets"
-int typestr = "portnumber"
-default = "29900"
-optional
-details = "
- The sync filter receives incoming synchronization packets on
- this UDP port.
-"
-
-option "timeout" t
-#~~~~~~~~~~~~~~~~~
-"how long to wait for other clients"
-int typestr = "milliseconds"
-default = "2000"
-optional
-details = "
- Once the sync filter receives its first chunk of input, a
- synchronization period of the given number of milliseconds
- begins. Playback is deferred until a synchronization packet
- has been received from each defined buddy, or until the end
- of the period. Buddies which did not send a synchronization
- packet in time are temporarily disabled and are not waited for
- during subsequent synchronization periods. They are re-enabled
- automatically when another synchronization packet arrives.
-"
+++ /dev/null
-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"
-optional
-details="
- The default address resolves to DANTZ.MCAST.NET and activates
- multicast.
-"
-
-option "port" p "udp port"
-int typestr="portnumber"
-default="8000"
-optional
-
-option "iface" I "receiving udp multicast interface"
-string
-optional
+++ /dev/null
-<qu>
-option "user" u
-#~~~~~~~~~~~~~~
-"run as the given user"
-string typestr="name"
-optional
-details="
- </qu>CURRENT_PROGRAM<qu> does not need any special privileges.
-
- If started as root (EUID == 0) this option must
- be given at the command line (not in the configuration
- file) so that </qu>CURRENT_PROGRAM<qu> can drop the root
- privileges right after parsing the command line options,
- but before parsing the configuration file. In this case,
- real/effective/saved UID are all set to the UID of 'name'. As
- the configuration file is read afterwards, those options that
- have a default value depending on the UID (e.g. the directory
- for the configuration file) are computed by using the uid of
- 'name'. This option has no effect if </qu>CURRENT_PROGRAM<qu>
- is started as a non-root user (i.e. EUID != 0).
-" </qu>
+++ /dev/null
-args "--no-handle-help --no-handle-version"
-
-purpose "Play wav or raw audio"
-
-include(header.m4)
-include(loglevel.m4)
-
-option "writer" w
-#~~~~~~~~~~~~~~~~
-"select stream writer"
-string typestr="name"
-optional
-multiple
-details="
- May be given multiple times, and the same writer may be specified more
- than once. If this option is not given, the first supported writer
- is started. The list of supported writers is shown in the help output.
-"
-
-include(channels.m4)
-include(sample_rate.m4)
-include(sample_format.m4)
--- /dev/null
+m4_define(PROGRAM, para_afh)
+[suite afh]
+version-string = GIT_VERSION()
+[supercommand para_afh]
+ purpose = print or modify meta information about audio file(s)
+ [description]
+ By default para_afh prints information about the given audio files,
+ such as format, duration, bitrate, etc., and the content of the most
+ common meta tags (artist, title, album, year, comment). In modify
+ mode it changes the meta tags to the values given at the command line.
+ [/description]
+ non-opts-name = file...
+ m4_include(common-option-section.m4)
+ m4_include(help.m4)
+ m4_include(detailed-help.m4)
+ m4_include(version.m4)
+ m4_include(loglevel.m4)
+ [option modify-section]
+ summary = Printing meta information
+ flag ignored
+ [option chunk-table]
+ short_opt = c
+ summary = print also the chunk table
+ [help]
+ The chunk table of an audio file is an array of offsets where each
+ offset corresponds to chunk of encoded data. The exact meaning of
+ chunk depends on the audio format.
+
+ Programs which are unaware of the particular audio format can read the
+ chunk table to obtain the timing information needed to stream the file.
+ [/help]
+ [option parser-friendly]
+ short_opt = p
+ summary = do not use human-readable output format
+ [help]
+ This option only affects the format of the chunk table, 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, the duration
+ and the size of each chunk. The parser-friendly output prints only
+ the offsets, in one line.
+ [/help]
+ [option modify-section]
+ summary = Modifying meta tags
+ flag ignored
+ [option modify]
+ short_opt = m
+ summary = modify (rather than print) tags
+ [help]
+ If this option is given, para_afh creates a temporary copy of the given
+ file(s), with meta tags changed according to the options below. On
+ errors, the temporary file is removed, leaving the original file
+ unchanged. On success, if --backup is given, the original file is
+ moved away. Finally the temporary file is renamed to the name of the
+ original file.
+ [/help]
+ [option backup]
+ short_opt = b
+ summary = create backup of the original file
+ [help]
+ The backup suffix is '~'. That is, a single tilde character is appended
+ to the given file name.
+ [/help]
+ [option year]
+ short_opt = y
+ summary = set the year tag
+ arg_info = required_arg
+ arg_type = string
+ typestr = string
+ [option title]
+ short_opt = t
+ summary = set the title tag
+ arg_info = required_arg
+ arg_type = string
+ typestr = string
+ [option artist]
+ short_opt = a
+ summary = set the artist tag
+ arg_info = required_arg
+ arg_type = string
+ typestr = string
+ [option album]
+ short_opt = A
+ summary = set the album tag
+ arg_info = required_arg
+ arg_type = string
+ typestr = string
+ [option comment]
+ short_opt = C
+ summary = set the comment tag
+ arg_info = required_arg
+ arg_type = string
+ typestr = string
--- /dev/null
+m4_define(PROGRAM, para_audioc)
+m4_define(DEFAULT_HISTORY_FILE, ~/.paraslash/audioc.history)
+[suite audioc]
+version-string = GIT_VERSION()
+[supercommand para_audioc]
+ purpose = communicate with para_audiod through a local socket
+ non-opts-name = [command [options]]
+ [description]
+ The client program to control para_audiod at runtime. It allows to
+ enable/disable streaming, to receive status info, or to grab the
+ audio stream at any point of the decoding process.
+
+ If no command is given, para_audioc enters interactive mode.
+ [/description]
+ m4_include(common-option-section.m4)
+ m4_include(help.m4)
+ m4_include(detailed-help.m4)
+ m4_include(version.m4)
+ m4_include(loglevel.m4)
+ m4_include(complete.m4)
+ m4_include(history-file.m4)
+ m4_include(per-command-options-section.m4)
+ [option socket]
+ short_opt = s
+ summary = path to well-known socket
+ arg_info = required_arg
+ arg_type = string
+ typestr = path
+ [help]
+ The default path of the socket is
+ /var/paraslash/audiod.socket.$HOSTNAME.
+ [/help]
+ [option bufsize]
+ short_opt = b
+ summary = size of internal buffer
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = bytes
+ default_val = 8192
--- /dev/null
+m4_define(PROGRAM, para_audiod)
+m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/audiod.conf)
+[suite audiod]
+version-string = GIT_VERSION()
+[supercommand para_audiod]
+ purpose = connect to para_server, receive, decode and play audio streams
+ [description]
+ para_audiod runs on a host with an audio device and connects
+ para_server to obtain status information and receive the current audio
+ stream. The stream is transformed through any number of filters and
+ then written to the configured output device.
+
+ Moreover, para_audiod listens on a local socket and sends status
+ information to local clients on request. Access to the local socket
+ can be restricted by means of Unix socket credentials, if available.
+ [/description]
+ m4_include(common-option-section.m4)
+ m4_include(help.m4)
+ m4_include(detailed-help.m4)
+ m4_include(version.m4)
+ m4_include(config-file.m4)
+ m4_include(loglevel.m4)
+ m4_include(logfile.m4)
+ m4_include(color.m4)
+ m4_include(log-timing.m4)
+ m4_include(daemon.m4)
+ m4_include(user.m4)
+ m4_include(group.m4)
+ m4_include(priority.m4)
+ m4_include(per-command-options-section.m4)
+ [option force]
+ summary = force startup
+ short_opt = F
+ [help]
+ If this flag is not given, para_audiod refuses to start if the
+ well-known socket file (see the --socket option) already exists
+ because this usually means that para_audiod is already running and
+ listening on that socket. After a crash or if para_audiod received
+ a SIGKILL signal, a stale socket file might remain and you have to
+ use --force once to force startup of para_audiod.
+ [/help]
+ [option mode]
+ summary = select startup mode
+ arg_info = required_arg
+ arg_type = string
+ typestr = mode
+ values = {AUDIOD_ON = "on", AUDIOD_OFF = "off", AUDIOD_STANDBY = "sb"}
+ default_val = on
+ [help]
+ para_audiod supports three modes of operation: On, off and standby
+ (sb). This option selects the mode that should be used at startup. If
+ mode is "on", para_audiiod connects para_server to receive status
+ information. If the server announces the availability of an audio
+ stream, para_audiod downloads, decodes and plays the audio stream
+ according to the given stream I/O options, see below.
+
+ In "standby" mode, para_audiod only receives the status information
+ from para_server but does not download the audio stream.
+
+ In "off" mode, para_audiod does not connect para_server at all,
+ but still listens on the local socket.
+ [/help]
+ [option socket]
+ short_opt = s
+ summary = path to the well-known socket
+ arg_info = required_arg
+ arg_type = string
+ typestr = path
+ [help]
+ para_audiod listens on a "well-known" socket for connections
+ from para_audioc. This socket is a special file in the file system;
+ its location defaults to /var/paraslash/audiod_sock.$hostname.
+
+ para_audioc, the client program used to connect to para_audiod,
+ opens this socket in order to talk to para_audiod. If the default
+ value for para_audiod is changed, para_audioc must be instructed to
+ use also <path> to connect para_audiod.
+ [/help]
+ [option user-allow]
+ summary = allow this user to connect to audiod
+ arg_info = required_arg
+ arg_type = string
+ typestr = username
+ [help]
+ Allow the user identified by username (either a string or a UID) to
+ connect to para_audiod. This option may be given multiple times. If
+ not specified at all, all users are allowed to connect.
+
+ This feature is based on the ability to send unix credentials through
+ local sockets using ancillary data (SCM_CREDENTIALS). Currently it
+ only works on Linux. On other operating systems the option is silently
+ ignored and all local users are allowed to connect.
+ [/help]
+ [option clock-diff-count]
+ summary = sync clock on startup
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = count
+ [help]
+ Check the clock difference between the host running para_server and
+ the local host running para_audiod this many times before starting
+ any stream I/0. Set this to non-zero for non-local setups if the
+ clocks of these two hosts are not synchronized by ntp or similar.
+ [/help]
+ [option Stream-IO-options]
+ summary = Stream I/O options
+ flag ignored
+ [option receiver]
+ short_opt = r
+ summary = select receiver
+ arg_info = required_arg
+ arg_type = string
+ typestr = receiver_spec
+ default_val = http
+ flag multiple
+ [help]
+ This option may be given multiple times, for each audio format
+ separately. If multiple definitions for an audio format are given,
+ the first one is selected.
+
+ The <receiver_spec> consists of an audio format specifier and one or
+ more receiver arguments, separated by a colon.
+
+ The audio format specifier is a regular expression which specifies
+ the set of audio formats for which this option should apply.
+
+ If any receiver options are present, the whole receiver argument must
+ be quoted:
+
+ -r 'mp3:http -i my.host.org -p 8009'
+
+ Since a single dot '.' matches the name of any audio format, specifying
+ '.' instead of 'mp3' above activates the http receiver for all audio
+ formats.
+ [/help]
+ [option filter]
+ short_opt = f
+ summary = specify the filter configuration
+ arg_info = required_arg
+ arg_type = string
+ typestr = filter_spec
+ flag multiple
+ [help]
+ This option may be given multiple times. The <filter_spec< consists
+ of an audio format specifier (see above), the name of the filter,
+ and any options for that filter. Note that order matters.
+
+ The compiled-in defaults apply to all audio formats for which no
+ --filter option was given. These defaults depend on the receiver
+ being used.
+
+ For HTTP streams, only the decoder for the current audio format
+ is activated. UDP and DCCP streams, on the other hand, are sent
+ FEC-encoded by para_server. In order to play such streams, the
+ receiver output must be FEC-decoded first, i.e. fed to the fecdec
+ filter. Therefore the default for UDP and DCCP streams is to activate
+ the fecdec filter, followed by the decoding filter for the audio
+ format.
+
+ Examples:
+
+ --filter 'mp3:mp3dec'
+
+ --filter 'mp3|aac:compress --inertia 5 --damp 2'
+
+ --filter '.:fecdec'
+ [/help]
+ [option writer]
+ short_opt = w
+ summary = specify one or more stream writers
+ arg_info = required_arg
+ arg_type = string
+ typestr = writer_spec
+ flag multiple
+ [help]
+ May be given multiple times, even multiple times for the same audio
+ format. The default is to use the first supported writer. Example:
+
+ --writer 'aac|wma:oss'
+ [/help]
+ [option stream-delay]
+ summary = specify time interval for client sync
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = count
+ [help]
+ Check the clock difference between the host running para_server and
+ the local host running para_audiod this many times before starting
+ any stream I/0. Set this to non-zero for non-local setups if the
+ clocks of these two hosts are not synchronized by ntp or similar.
+ [/help]
--- /dev/null
+[suite audiod_cmd]
+caption = list of audiod commands
+[subcommand cycle]
+ purpose = switch to next operating mode
+ [description]
+ Cycle through the three operation modes (on, standby, off).
+ [/description]
+
+[subcommand help]
+ purpose = display command list or help for given command
+ non-opts-name = [subcommand]
+ [description]
+ When I was younger, so much younger than today, I never needed
+ anybody's help in any way. But now these days are gone, I'm not so self
+ assured. Now I find I've changed my mind and opened up the doors. --
+ Beatles: Help
+ [/description]
+
+[subcommand grab]
+ purpose = grab the audio stream
+ [description]
+ grab ('splice') the audio stream at any position in the buffer tree
+ and send that data back to the client.
+ [/description]
+ [option mode]
+ short_opt = m
+ summary = change grab mode
+ arg_info = required_arg
+ arg_type = string
+ typestr = mode
+ default_val = s
+ [help]
+ The various grab modes only differ in what happens if an attempt to
+ write the grabbed audio data would block. Sloppy mode ("s") ignores
+ the write, pedantic mode ("p") aborts and aggressive mode ("a")
+ tries to write anyway.
+ [/help]
+ [option parent]
+ short_opt = p
+ summary = Grab output of the given node of the buffer tree
+ arg_info = required_arg
+ arg_type = string
+ typestr = node
+ [option name]
+ short_opt = n
+ summary = Name of the new buffer tree node. Defaults to 'grab'
+ arg_info = optional_arg
+ arg_type = string
+ typestr = string
+ [option one-shot]
+ short_opt = o
+ summary = One-shot mode: Stop grabbing if audio file changes
+
+[subcommand off]
+ purpose = deactivate para_audiod
+ [description]
+ Close connection to para_server and stop all decoders.
+ [/description]
+
+[subcommand on]
+ purpose = activate para_audiod
+ [description]
+ Establish connection to para_server, retrieve para_server's current
+ status. If playing, start corresponding decoder. Otherwise stop
+ all decoders.
+ [/description]
+
+[subcommand sb]
+ purpose = enter standby mode
+ [description]
+ Stop all decoders but leave connection to para_server open.
+ [/description]
+
+[subcommand stat]
+ purpose = print status information
+ non-opts-name = [item...]
+ [description]
+ Dump given status items (all if none given) to stdout.
+ [/description]
+ [option parser-friendly]
+ short_opt = p
+ summary = use parser-friendly output format
+
+[subcommand version]
+ purpose = print the version of para_audiod
+ [option verbose]
+ short_opt = v
+ summary = print detailed (multi-line) version text
+
+[subcommand tasks]
+ purpose = list current tasks
+ [description]
+ Print the list of task ids together with the status of each task.
+ [/description]
+
+[subcommand term]
+ purpose = terminate para_audiod
+ [description]
+ Stop all decoders, shut down connection to para_server and exit.
+ [/description]
--- /dev/null
+m4_define(PROGRAM, para_client)
+m4_define(DEFAULT_HISTORY_FILE, ~/.paraslash/client.history)
+m4_define(DEFAULT_CONFIG_FILE,~/.paraslash/client.conf)
+[suite client]
+version-string = GIT_VERSION()
+[supercommand para_client]
+ purpose = Communicate with para_server through the paraslash control port
+ non-opts-name = [command [options]]
+ [description]
+ para_client is the program used to connect to a running instance of
+ para_server. If a command is provided, this command is sent to the
+ server and any output received from the server is echoed to stdout. If
+ no command is given, para_client enters interactive mode.
+ [/description]
+ m4_include(common-option-section.m4)
+ m4_include(help.m4)
+ m4_include(detailed-help.m4)
+ m4_include(version.m4)
+ m4_include(loglevel.m4)
+ m4_include(config-file.m4)
+ m4_include(history-file.m4)
+ m4_include(per-command-options-section.m4)
+ [option hostname]
+ short_opt = i
+ summary = IP address or name of the host where para_server is running
+ typestr = host
+ arg_info = required_arg
+ arg_type = string
+ default_val = localhost
+ [help]
+ Both IPv4 and IPv6 addresses are supported.
+ [/help]
+ [option user]
+ short_opt = u
+ summary = the paraslash username to authenticate as
+ arg_info = required_arg
+ arg_type = string
+ typestr = name
+ [help]
+ If this is not given, the current username is assumed.
+ [/help]
+ [option server-port]
+ short_opt = p
+ summary = the port on the remote host to connect
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = portnumber
+ default_val = 2990
+ [help]
+ If this is not given, the current username is assumed.
+ [/help]
+ [option key-file]
+ short_opt = k
+ summary = path to private key
+ arg_info = required_arg
+ arg_type = string
+ typestr = filename
+ [help]
+ If not given, the following files are tried, in order:
+ $HOME/.paraslash/key.$LOGNAME, $HOME/.ssh/id_rsa. It is a fatal error
+ if the key file can not be opened, or is world-readable.
+ [/help]
+ m4_include(complete.m4)
--- /dev/null
+.SH COPYRIGHT
+Written by Andre Noll
+.br
+Copyright (C) COPYRIGHT_YEAR() Andre Noll
+.br
+License: GNU GPL version 2
+.br
+This is free software: you are free to change and redistribute it.
+.br
+There is NO WARRANTY, to the extent permitted by law.
+.br
+Report bugs to
+.MT <maan@tuebingen.mpg.de>
+Andre Noll
+.ME
--- /dev/null
+m4_define(PROGRAM, para_filter)
+[suite filter]
+version-string = GIT_VERSION()
+[supercommand para_filter]
+ purpose = decode or process audio data from STDIN to STDOUT
+ [description]
+ This program allows to specify a chain of filters which transform the
+ audio stream read from STDIN. A common mode of operation is to decode
+ an mp3 file with the mp3dec filter, but many other filters exist which
+ transform the audio stream in different ways.
+ [/description]
+ m4_include(common-option-section.m4)
+ m4_include(help.m4)
+ m4_include(detailed-help.m4)
+ m4_include(version.m4)
+ m4_include(loglevel.m4)
+ m4_include(per-command-options-section.m4)
+ [option filter]
+ short_opt = f
+ summary = add one filter to the filter chain
+ arg_info = required_arg
+ arg_type = string
+ flag multiple
+ typestr = filter_spec
+ [help]
+ A filter specifier begins with the name of a supported filter,
+ optionally followed by zero or more options for the named filter.
+ Filter name and options must be separated by whitespace. If the
+ there is at least one option, the filter specifier needs to be quoted
+ like this:
+
+ --filter 'compress --inertia 5 --damp 2'
+
+ This option may be specified multiple times to 'pipe' the stream
+ through all given filters (in a single thread without copying the
+ data). The same filter may appear more than once, and order matters.
+ [/help]
--- /dev/null
+[suite filter_cmd]
+caption = filters
+[subcommand aacdec]
+ purpose = decode an aac stream
+[subcommand amp]
+ purpose = amplify (scale) a raw audio stream
+ [option amp]
+ short_opt = a
+ summary = amplification value
+ typestr = number
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 32
+ [help]
+ The amplification value determines the scaling factor by which the
+ amplitude of the audio stream is multiplied. The formula for the
+ scaling factor is
+
+ factor = 1 + amp / 64.
+
+ For example, an amplification value of zero results in a scaling factor
+ of one while an amplification value of 64 means to double the volume.
+ [/help]
+[subcommand compress]
+ purpose = dynamically adjust the volume of an audio stream
+ [option blocksize]
+ short_opt = b
+ summary = use blocks of size 2**bits
+ typestr = bits
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 15
+ [help]
+ Larger blocksize means fewer volume adjustments per time unit.
+ [/help]
+ [option aggressiveness]
+ short_opt = a
+ summary = controls the maximum amount to amplify by
+ typestr = bits
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 4
+ [option inertia]
+ short_opt = i
+ summary = how much inertia ramping has
+ typestr = bits
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 6
+ [option target-level]
+ short_opt = t
+ summary = target signal level (0-32768)
+ typestr = level
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 20000
+ [option damp]
+ short_opt = d
+ summary = if non-zero, scale down after normalizing
+ typestr = bits
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 0
+[subcommand fecdec]
+ purpose = decode a (lossy) input stream using forward error correction
+[subcommand flacdec]
+ purpose = decode a flac stream
+[subcommand mp3dec]
+ purpose = decode an mp3 stream
+ [option ignore-crc]
+ short_opt = i
+ summary = ignore CRC information in the audio stream
+ [help]
+ This causes frames with CRC errors to be decoded and played
+ anyway. This option is not recommended, but since some encoders
+ have been known to generate bad CRC information, this option is a
+ work-around to play streams from such encoders.
+ [/help]
+[subcommand oggdec]
+ purpose = decode an ogg/vorbis stream
+[subcommand opusdec]
+ purpose = decode an ogg/opus stream
+[subcommand prebuffer]
+ purpose = delay processing of an audio stream
+ [option duration]
+ short_opt = d
+ summary = length of the prebuffer period
+ typestr = milliseconds
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 200
+ [help]
+ Wait this many milliseconds before letting data go through. The time
+ interval starts when the first data byte is seen in the input queue.
+ [/help]
+ [option size]
+ short_opt = s
+ summary = amount of data to prebuffer
+ typestr = bytes
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 0
+ [help]
+ Wait until this many data bytes are available in the input queue. The
+ default value of zero means to not prebuffer by size. If both
+ --duration and --size are given and non-zero, the prebuffer filter
+ waits until both conditions are met.
+ [/help]
+[subcommand resample]
+ purpose = transform raw audio to a different sample rate
+ [option converter]
+ short_opt = C
+ summary = set conversion algorithm
+ typestr = type
+ arg_info = required_arg
+ arg_type = string
+ values = {
+ # RCT: resample conversion type
+ RCT_BEST = "best",
+ RCT_MEDIUM = "medium",
+ RCT_FASTEST = "fastest",
+ RCT_ZERO_ORDER_HOLD = "zero_order_hold",
+ RCT_LINEAR = "linear"
+ }
+ default_val = medium
+ [help]
+ best: This is a bandlimited interpolator derived from the mathematical
+ sinc function and this is the highest quality sinc based converter,
+ providing a worst case Signal-to-Noise Ratio (SNR) of 97 decibels
+ (dB) at a bandwidth of 97%.
+
+ medium: This is another bandlimited interpolator much like the previous
+ one. It has an SNR of 97dB and a bandwidth of 90%. The speed of the
+ conversion is much faster than the previous one.
+
+ fastest: This is the fastest bandlimited interpolator and has an SNR
+ of 97dB and a bandwidth of 80%.
+
+ zero_order_hold: A Zero Order Hold converter (interpolated value
+ is equal to the last value). The quality is poor but the conversion
+ speed is blindlingly fast.
+
+ linear: A linear converter. Again the quality is poor, but the
+ conversion speed is blindingly fast.
+ [/help]
+ [option dest-sample-rate]
+ short_opt = d
+ summary = sample rate to convert to
+ typestr = rate
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 44100
+ m4_include(channels.m4)
+ m4_include(sample-rate.m4)
+ m4_include(sample-format.m4)
+[subcommand spxdec]
+ purpose = decode an ogg/speex stream
+[subcommand sync]
+ purpose = synchronize playback between multiple clients
+ [option buddy]
+ short_opt = b
+ summary = client to synchronize with
+ typestr = url
+ arg_info = required_arg
+ arg_type = string
+ flag multiple
+ [help]
+ This option may be given multiple times, one per buddy. Each value
+ may be given as a host, port pair in either IPv4 or IPv6 form, with
+ port being optional. If no port was specified the listening port (as
+ specified with --port, see below) is used to send the synchronization
+ packet to this buddy.
+ [/help]
+ [option port]
+ short_opt = p
+ summary = UDP port for incoming synchronization packets
+ typestr = portnumber
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 29900
+ [help]
+ The sync filter expects incoming synchronization packets on this
+ UDP port.
+ [/help]
+ [option timeout]
+ short_opt = t
+ summary = how long to wait for other clients
+ typestr = milliseconds
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 2000
+ [help]
+ Once the sync filter receives its first chunk of input,
+ a synchronization period of the given number of milliseconds
+ begins. Playback is deferred until a synchronization packet has
+ been received from each defined buddy, or until the end of the
+ period. Buddies which did not send a synchronization packet in time
+ are temporarily disabled and are not waited for during subsequent
+ synchronization periods. They are re-enabled automatically when
+ another synchronization packet arrives.
+ [/help]
+[subcommand wav]
+ purpose = insert a Microsoft wave header into a raw audio stream
+[subcommand wmadec]
+ purpose = decode a wma stream
--- /dev/null
+m4_define(PROGRAM, para_gui)
+m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/gui.conf)
+[suite gui]
+version-string = GIT_VERSION()
+[supercommand para_gui]
+ purpose = show para_audiod status in a curses window
+ [description]
+ para_gui is a curses program which displays status information
+ about para_server and para_audiod in a terminal. Keys may be
+ mapped to user-defined commands which are executed when the key is
+ pressed. Command output is shown in a simple pager.
+ [/description]
+ m4_include(common-option-section.m4)
+ m4_include(help.m4)
+ m4_include(detailed-help.m4)
+ m4_include(version.m4)
+ m4_include(config-file.m4)
+ m4_include(loglevel.m4)
+ m4_include(per-command-options-section.m4)
+ [option theme]
+ short_opt = T
+ summary = select startup theme
+ arg_info = required_arg
+ arg_type = string
+ typestr = name
+ [help]
+ If this option is not given the default theme is used. If the given
+ name is not a valid theme name, the list of available themes is
+ printed and the program terminates.
+ [/help]
+ [option stat-cmd]
+ short_opt = s
+ summary = command to read status items from
+ arg_info = required_arg
+ arg_type = string
+ typestr = command
+ default_val = para_audioc -- stat -p
+ [help]
+ On a host on which the para_audiod service is not is running, the
+ default command will fail. An alternative is
+
+ para_client -- stat -p
+
+ This command connects para_server instead of para_audiod. However,
+ no timing information about the current audio file is printed.
+ [/help]
+ [option key-map]
+ short_opt = k
+ summary = map a key to a command using one of three possible modes
+ arg_info = required_arg
+ arg_type = string
+ typestr = k:m:c
+ flag multiple
+ [help]
+ Mode may be d, x or p for display, external and paraslash commands,
+ respectively. Of course, this option may be given multiple times,
+ one for each key mapping. See the manual for more information.
+ [/help]
--- /dev/null
+[option channels]
+ short_opt = c
+ summary = specify number of channels
+ typestr = num
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 2
+ [help]
+ It is only necessary to specify this option for raw audio. If it is
+ not given, the channel count is queried from the parent buffer tree
+ nodes (e.g. the decoder) or the wav header. Only if this query fails,
+ the default value applies.
+ [/help]
--- /dev/null
+[option color]
+ short_opt = C
+ summary = activate color output
+ typestr = when
+ arg_info = required_arg
+ arg_type = string
+ values = {COLOR_YES = "yes", COLOR_NO = "no", COLOR_AUTO = "auto"}
+ default_val = auto
+[option log-color]
+ summary = select a color for one type of log message
+ typestr = color_spec
+ arg_info = required_arg
+ arg_type = string
+ flag multiple
+ [help]
+ The format of <color_spec> is [<fg> [<bg>]] [<attr>].
+
+ Valid colors for <fg> and <bg> are "normal", "black", "red", "green",
+ "yellow", "blue", "magenta", "cyan", and "white".
+
+ The <attr> value must be one of "bold", "dim", "ul", "blink",
+ "reverse".
+
+ Examples:
+
+ --log-color "debug:green"
+ --log-color "info:yellow bold"
+ --log-color "notice:white red bold"
+ [/help]
--- /dev/null
+[option common-option-section]
+ summary = Common options
+ flag ignored
+ [help]
+ The following options are implemented generically and are available
+ for many of the commands.
+ [/help]
+
--- /dev/null
+[option complete]
+ summary = print possible command line completions
+ [help]
+ If this flag is given, the environment variables COMP_LINE and
+ COMP_POINT are examined to obtain the current command line and the
+ cursor position respectively. Possible completions are written to
+ stdout and the program exits.
+ [/help]
--- /dev/null
+[option config-file]
+ short_opt = c
+ summary = path to alternative config file
+ arg_info = required_arg
+ arg_type = string
+ typestr = filename
+ [help]
+ default: DEFAULT_CONFIG_FILE()
+
+ PROGRAM() reads its config file right after parsing the options that
+ were given at the command line. If an option is given both at the
+ command line and in the config file, the value that was specified at
+ the command line takes precedence.
+ [/help]
--- /dev/null
+[option daemon]
+ short_opt = d
+ summary = run as background daemon
+ [help]
+ If this option is given and no logfile was specified, all messages
+ go to /dev/null.
+ [/help]
--- /dev/null
+
+[option detailed-help]
+ summary = print help, including all details, and exit
--- /dev/null
+[option group]
+ short_opt = g
+ summary = set group id
+ arg_info = required_arg
+ arg_type = string
+ typestr = groupname
+ [help]
+ This option sets the group id according to <group>. This option is
+ silently ignored if EUID != 0. Otherwise, real/effective GID and the
+ saved set-group ID are all set to the GID given by <group>. Must not
+ be given in the config file.
+ [/help]
--- /dev/null
+[option help]
+ summary = print help and exit
+ short_opt = h
--- /dev/null
+[option history-file]
+arg_info = required_arg
+arg_type = string
+typestr = filename
+summary = location of the file for the command history list
+[help]
+ If PROGRAM() runs in interactive mode, it reads the history file
+ on startup. Upon exit, the in-memory history is appended to the
+ history file.
+
+ If this option is not given, the history file is expected at
+ DEFAULT_HISTORY_FILE().
+[/help]
--- /dev/null
+[option host]
+ short_opt = i
+ summary = IP address or hostname
+ typestr = host
+ arg_info = required_arg
+ arg_type = string
+ default_val = localhost
+ [help]
+ Both IPv4 and IPv6 addresses are supported.
+ [/help]
--- /dev/null
+[option log-timing]
+ short_opt = T
+ summary = show milliseconds in log messages
+ [help]
+ Selecting this option causes milliseconds to be included in
+ the log message output. This allows to measure the interval
+ between log messages in milliseconds which is useful for
+ identifying timing problems.
+ [/help]
--- /dev/null
+[option logfile]
+short_opt = L
+arg_info = required_arg
+arg_type = string
+typestr = filename
+summary = where to write log output
+[help]
+ If this option is not given, PROGRAM() writes the log messages
+ to stderr.
+[/help]
--- /dev/null
+m4_define(`downcase', `m4_translit(`$*', `A-Z', `a-z')')
+m4_define(`SUITE_LOGLEVELS', `m4_patsubst(`$*', `LL_\([A-Z]+\)',
+ `LSGLL_\1 = "downcase(`\1')" ')')
+[option loglevel]
+ summary = control amount of logging
+ short_opt = l
+ arg_info = required_arg
+ arg_type = string
+ typestr = severity
+ values = {SUITE_LOGLEVELS(LOGLEVELS())}
+ default_val = warning
+ [help]
+ Log only messages with severity greater or equal than the given
+ value. Possible values:
+
+ debug: Produces really noisy output.
+ info: Still noisy, but won't fill up the disk quickly.
+ notice: Indicates normal, but significant event.
+ warning: Unexpected events that can be handled.
+ error: Unhandled error condition.
+ crit: System might be unreliable.
+ emerg: Last message before exit.
+ [/help]
--- /dev/null
+[option per-command-option-section]
+ summary = Options for PROGRAM()
+ flag ignored
--- /dev/null
+[option port]
+ short_opt = p
+ summary = TCP port to connect to
+ typestr = portnumber
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 8000
--- /dev/null
+[option priority]
+ summary = adjust scheduling priority
+ arg_info = required_arg
+ arg_type = int32
+ typestr = prio
+ default_val = 0
+ [help]
+ The priority (also known as nice value) is a value in the range -20
+ to 19. Lower priorities cause more favorable scheduling. Since only
+ privileged processes may request a negative priority, specifying
+ a negative value works only if the daemon is started with root
+ privileges.
+ [/help]
--- /dev/null
+[option sample-format]
+ short_opt = f
+ summary = specify sample format
+ typestr = format
+ arg_info = required_arg
+ arg_type = string
+ # TODO: dedup this from enum in para.h.
+ values = {
+ SAMPLE_FORMAT_S8 = "S8",
+ SAMPLE_FORMAT_U8 = "U8",
+ SAMPLE_FORMAT_S16_LE = "S16_LE",
+ SAMPLE_FORMAT_S16_BE = "S16_BE",
+ SAMPLE_FORMAT_U16_LE = "U16_LE",
+ SAMPLE_FORMAT_U16_BE = "U16_BE"
+ }
+ default_val = S16_LE
+ [help]
+ It is only necessary to specify this for raw audio. See the discussion
+ of the --channels option.
+ [/help]
--- /dev/null
+[option sample-rate]
+ short_opt = s
+ summary = do not guess the input sample rate
+ typestr = rate
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 44100
+ [help]
+ It is only necessary to specify this for raw audio. See the discussion
+ of the --channels option.
+ [/help]
--- /dev/null
+[option user]
+ short_opt = u
+ summary = run as the given user
+ arg_info = required_arg
+ arg_type = string
+ typestr = username
+ [help]
+ PROGRAM() does not need any special privileges.
+
+ If started as root (EUID == 0) this option must be given at the
+ command line (not in the configuration file) so that PROGRAM()
+ can drop the root privileges right after parsing the command line
+ options, but before parsing the configuration file. In this case,
+ real/effective/saved UID are all set to the UID of <username>. As
+ the configuration file is read afterwards, those options that have
+ a default value depending on the UID (e.g. the directory for the
+ configuration file) are computed by using the uid of <username>. This
+ option has no effect if PROGRAM() is started as a non-root user (i.e.
+ EUID != 0).
+ [/help]
+
--- /dev/null
+[option version]
+ summary = print version and exit
+ short_opt = V
--- /dev/null
+.PRECIOUS: $(lls_suite_dir)/%.suite $(lsg_h)
+lls_m4_include_dir := $(lls_m4_dir)/include
+
+$(lls_suite_dir)/%.m4d: $(lls_m4_dir)/%.suite.m4 | $(lls_suite_dir)
+ @[ -z "$(Q)" ] || echo 'M4D $<'
+
+ $(Q) $(M4) -Pg -I $(lls_m4_include_dir) -s $< \
+ | awk '{if ($$1 ~ /#line/) {gsub(/"/, "", $$3); if ($$3 != "$<") \
+ print "$(lls_suite_dir)/$(*F).suite: " $$3}}' | sort | uniq > $@
+
+$(lls_suite_dir)/%.suite: $(lls_m4_dir)/%.suite.m4 | $(lls_suite_dir)
+ $(Q) $(M4) -Pg -I $(lls_m4_include_dir) -D GIT_VERSION=$(GIT_VERSION) \
+ -D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) -D LOGLEVELS=$(LOGLEVELS) \
+ $< > $@
+
+$(lls_suite_dir)/%.lsg.c: $(lls_suite_dir)/%.suite
+ @[ -z "$(Q)" ] || echo 'LSGC $<'
+ $(Q) lopsubgen --gen-c --output-dir $(lls_suite_dir) < $<
+
+$(lls_suite_dir)/%.lsg.h: $(lls_suite_dir)/%.suite
+ @[ -z "$(Q)" ] || echo 'LSGH $<'
+ $(Q) lopsubgen --gen-header --output-dir $(lls_suite_dir) < $<
+
+$(lls_suite_dir)/%.lsg.man: $(lls_suite_dir)/%.suite
+ @[ -z "$(Q)" ] || echo 'LSGM $<'
+ $(Q) lopsubgen --gen-man --output-dir $(lls_suite_dir) < $<
+
+$(object_dir)/%.o: $(lls_suite_dir)/%.c | $(object_dir)
+ @[ -z "$(Q)" ] || echo 'CC $<'
+ $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(STRICT_CFLAGS) $<
--- /dev/null
+m4_define(PROGRAM, para_mixer)
+m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/mixer.conf)
+[suite mixer]
+version-string = GIT_VERSION()
+caption = List of subcommands
+[supercommand para_mixer]
+ purpose = alarm clock and volume-fader for OSS and ALSA
+ synopsis = [<options>] -- [<subcommand>] [<subcommand_options>]
+ [description]
+ para_mixer adjusts the settings of an audio mixing device. It can
+ set the level of a mixer channel, or fade the level from one value
+ to another in a given time period. The sleep and snooze subcommands
+ contact para_server to start or stop streaming.
+ [/description]
+ m4_include(common-option-section.m4)
+ m4_include(help.m4)
+ m4_include(detailed-help.m4)
+ m4_include(version.m4)
+ m4_include(config-file.m4)
+ m4_include(loglevel.m4)
+ m4_include(per-command-options-section.m4)
+ [help]
+ These options apply to several subcommands.
+ [/help]
+ [option mixer-api]
+ short_opt = a
+ summary = select alternative mixer API
+ arg_info = required_arg
+ arg_type = string
+ typestr = api
+ [help]
+ ALSA is preferred over OSS if both APIs are supported and this option
+ is not given. To see the supported mixer APIs, use this option with
+ an invalid string as the mixer API, e.g. --mixer-api help.
+ [/help]
+ [option mixer-device]
+ short_opt = m
+ summary = set mixer device
+ arg_info = required_arg
+ arg_type = string
+ typestr = device
+ [help]
+ The default device (used if this option is not given) depends
+ on the selected mixer API. For ALSA, the default is 'hw:0' which
+ corresponds to the first sound device. For OSS, '/dev/mixer' is used
+ as the default.
+ [/help]
+ [option mixer-channel]
+ short_opt = C
+ summary = select a mixer channel
+ arg_info = required_arg
+ arg_type = string
+ typestr = channel
+ [help]
+ For the ALSA mixer API, the possible values are determined at runtime
+ depending on the hardware and can be printed by specifying an invalid
+ mixer channel, for example --mixer-channel help. The default channel is
+ 'Master'.
+
+ For OSS the possible values are invariably 'volume', 'bass', 'treble',
+ 'synth', 'pcm', 'speaker', 'line', 'mic', 'cd', 'imix', 'altpcm',
+ 'reclev', 'igain', 'ogain'. However, not all listed channels might be
+ supported on any particular hardware. The default channel is 'volume'.
+ [/help]
+ [option fade-exponent]
+ summary = set non-linear time scale for fading
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = value
+ default_val = 0
+ [help]
+ This option affects the fade, snooze and sleep subcommands. It is
+ ignored in set mode.
+
+ The argument must be a number between 0 and 100. The default value
+ 0 corresponds to linear scaling. That is, the value of the mixer
+ channel is increased or decreased in fixed time intervals until the
+ destination value is reached. Exponents between 1 and 99 cause low
+ channel values to be increased more quickly than high channel values.
+ Large exponents cause greater differences. The special value 100 sets
+ the destination value immediately. The command then sleeps for the
+ configured fade time.
+ [/help]
+[subcommand help]
+ purpose = print subcommand help
+ non-opts-name = [<subcommand>]
+ [option long]
+ summary = print the long help text
+ short_opt = l
+
+[subcommand set]
+ purpose = set a channel to the given value and exit
+ [option val]
+ summary = mixer channel value to set
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = value
+ default_val = 10
+[subcommand fade]
+ purpose = fade a channel to the given value in the given time
+ [option fade-vol]
+ short_opt = f
+ summary = destination volume for fading
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = volume
+ default_val = 50
+ [option fade-time]
+ short_opt = t
+ summary = duration of fade period
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = seconds
+ default_val = 5
+[subcommand snooze]
+ purpose = fade out, pause, sleep, play, fade in
+ [option so-time]
+ summary = duration of fade-out period
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = seconds
+ default_val = 30
+ [option so-vol]
+ summary = destination volume for fade-out
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = volume
+ default_val = 20
+ [option snooze-time]
+ summary = delay between end of fade-out and begin of fade-in
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = seconds
+ default_val = 600
+ [option si-time]
+ summary = duration of fade-in period
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = seconds
+ default_val = 180
+ [option si-vol]
+ summary = destination volume for fade-in
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = volume
+ default_val = 80
+[subcommand sleep]
+ purpose = stream, fade out, sleep, fade in
+ [description]
+ Change to the initial volume and select the initial mood/playlist.
+ Fade out to the given fade-out volume in the specified time. Switch
+ to the sleep mood/playlist and wait until wake time minus fade-in
+ time. Finally, switch to the wake mood/playlist and fade in to the
+ fade-in volume.
+ [/description]
+ [option ivol]
+ summary = set initial volume
+ arg_info = required_arg
+ arg_type = string
+ default_val = 60
+ flag multiple
+ typestr = [channel:]volume
+ [help]
+ Used as the start volume, before fading out to the fade-out volume. The
+ channel part may be omitted, in which case the default channel is
+ used. This option may be given multiple times.
+ [/help]
+ [option fo-mood]
+ summary = mood or playlist for fade-out
+ arg_info = required_arg
+ arg_type = string
+ typestr = mood_spec
+ default_val = m/fade
+ [help]
+ This mood (or playlist) is selected right after setting the initial
+ volume.
+ [/help]
+ [option fo-time]
+ summary = duration of fade-out period
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = seconds
+ default_val = 1800
+ [help]
+ No fading if this is set to 0.
+ [/help]
+ [option fo-vol]
+ summary = destination volume for fade-out
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = volume
+ default_val = 20
+ [option sleep-mood]
+ summary = mood/playlist between fade-out and fade-in
+ arg_info = required_arg
+ arg_type = string
+ typestr = mood_spec
+ default_val = m/sleep
+ [help]
+ Select the given mood or playlist after the fade-out. If unset,
+ playback is stopped until fade-in starts.
+ [/help]
+ [option wake-time]
+ short_opt = w
+ summary = when to start fade in
+ arg_info = required_arg
+ arg_type = string
+ typestr = [+][HH][:MM]
+ default_val = +9:00
+ [help]
+ If the optional plus character is given, the wake time is computed as
+ now + HH hours + MM minutes. Otherwise the HH:MM argument is considered
+ an absolute time (referring to either the current or the next day).
+ [/help]
+ [option fi-mood]
+ summary = mood or playlist for fade-in
+ arg_info = required_arg
+ arg_type = string
+ typestr = mood_spec
+ default_val = m/wake
+ [help]
+ This mood or playlist is selected right before fade-in begins.
+ [/help]
+ [option fi-time]
+ summary = duration of fade-in period
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = seconds
+ default_val = 1200
+ [help]
+ No fading if this is set to 0.
+ [/help]
+ [option fi-vol]
+ summary = destination volume for fade-in
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = volume
+ default_val = 80
--- /dev/null
+m4_define(PROGRAM, para_play)
+m4_define(DEFAULT_HISTORY_FILE, ~/.paraslash/play.history)
+m4_define(DEFAULT_CONFIG_FILE,~/.paraslash/play.conf)
+[suite play]
+version-string = GIT_VERSION()
+[supercommand para_play]
+ purpose = command line audio player
+ non-opts-name = <audio_file>...
+ [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 and the
+ playback position are shown and the program reads single key strokes
+ from stdin. Keys may be mapped to commands so that the configured
+ command is executed when a mapped key is pressed.
+ [/description]
+ m4_include(common-option-section.m4)
+ m4_include(help.m4)
+ m4_include(detailed-help.m4)
+ m4_include(version.m4)
+ m4_include(loglevel.m4)
+ m4_include(config-file.m4)
+ m4_include(history-file.m4)
+ m4_include(per-command-options-section.m4)
+ [option randomize]
+ short_opt = z
+ summary = randomize playlist at startup
+ [option key-map]
+ short_opt = k
+ summary = map a key to a command
+ arg_info = required_arg
+ arg_type = string
+ flag multiple
+ typestr = key:command [args]
+ [help]
+ This option may be given multiple times, one for each key
+ mapping. Example:
+
+ --key-map 5:jmp 50
+ [/help]
--- /dev/null
+[suite play_cmd]
+caption = list of commands
+[subcommand help]
+ purpose = list commands or keybindings, or print command-specific help
+ non-opts-name = [command]
+ [description]
+
+ This command acts differently depending on whether it is executed in
+ command mode or in insert mode.
+
+ In command mode, the help command prints the list of defined
+ keybindings. In insert mode, if no argument is given, it prints the
+ list of available commands. When called with the name of a command
+ as first argument, it prints the description of this command.
+ [/description]
+
+[subcommand fg]
+ purpose = enter command mode
+ [description]
+ In this mode, file name and play time of the current audio file are
+ displayed. Hit CTRL+C or : to switch to input mode.
+ [/description]
+[subcommand next]
+ purpose = load the next file of the playlist
+
+[subcommand prev]
+ purpose = load the previous file of the playlist
+
+[subcommand bg]
+ purpose = enter insert mode
+ [description]
+ Only useful if called in command mode via a key binding. The default
+ key bindings map the colon key to this command, so pressing : in
+ command mode activates insert mode.
+ [/description]
+
+[subcommand jmp]
+ purpose = change playback position
+ non-opts-name = percent
+ [description]
+ The percent argument must be an integer between 0 and 100, inclusively.
+ [/description]
+
+[subcommand ff]
+ purpose = set playback position relative to the current position
+ non-opts-name = seconds
+ [description]
+ Negative values mean to jump backwards the given amount of seconds.
+ [/description]
+
+[subcommand ls]
+ purpose = list the playlist
+ [description]
+ This prints all paths of the playlist. The currently
+ active file is marked with an asterisk.
+ [/description]
+
+[subcommand info]
+ purpose = print information about the current file
+ [description]
+ The output contains the playlist position, the path
+ and information provided by the audio format handler.
+ [/description]
+
+[subcommand play]
+ purpose = start or resume playback
+ non-opts-name = [num]
+ [description]
+ If no argument is given, playback starts at the current
+ position. Otherwise, the corresponding file is loaded
+ and playback is started at the beginning of the file.
+ [/description]
+
+[subcommand pause]
+ purpose = suspend playback
+ [description]
+ When paused, it is still possible to jump around in
+ the file via the jmp and ff commands.
+ [/description]
+
+[subcommand tasks]
+ purpose = print list of active tasks
+ [description]
+ Mainly useful for debugging.
+ [/description]
+
+[subcommand quit]
+ purpose = exit para_play
+ [description]
+ Pressing CTRL+D causes EOF on stdin which also exits
+ para_play.
+ [/description]
--- /dev/null
+m4_define(PROGRAM, para_recv)
+[suite recv]
+version-string = GIT_VERSION()
+[supercommand para_recv]
+ purpose = a command line HTTP/DCCP/UDP stream grabber
+ m4_include(common-option-section.m4)
+ m4_include(help.m4)
+ m4_include(detailed-help.m4)
+ m4_include(version.m4)
+ m4_include(loglevel.m4)
+ m4_include(per-command-options-section.m4)
+ [option receiver]
+ short_opt = r
+ summary = select the receiver
+ arg_info = required_arg
+ arg_type = string
+ typestr = receiver_spec
+ [help]
+ Any options for the selected receiver must be quoted. Example:
+
+ -r 'http -i www.paraslash.org -p 8009'
+ [/help]
--- /dev/null
+[suite recv_cmd]
+caption = receivers
+[subcommand afh]
+ purpose = make an audio stream from a local file
+ [description]
+ The afh (audio format handler) receiver extracts selected parts of
+ the given audio file without decoding the file. Only complete chunks
+ with respect to the underlying audio format are extracted.
+ [/description]
+ [option filename]
+ short_opt = f
+ summary = file to open
+ typestr = filename
+ arg_info = required_arg
+ arg_type = string
+ [option begin-chunk]
+ short_opt = b
+ summary = skip the beginning of the file
+ typestr = chunk_num
+ arg_info = required_arg
+ arg_type = int32
+ [help]
+ The argument must be an integer between -num_chunks and num_chunks -
+ 1, inclusively, where num_chunks is the total number of chunks. If
+ chunk_num is negative, the given number of chunks are counted backwards
+ from the end of the file. For example --begin-chunk -100 instructs
+ the afh receiver to start at chunk num_chunks - 100. This is useful
+ for cutting off the beginning of an audio file.
+ [/help]
+ [option end-chunk]
+ short_opt = e
+ summary = only write up to chunk chunk_num
+ typestr = chunk_num
+ arg_info = required_arg
+ arg_type = int32
+ [help]
+ For the chunk_num argument the same rules as for --begin-chunk
+ apply. The default is to write up to the last chunk.
+ [/help]
+ [option just-in-time]
+ short_opt = j
+ summary = use timed writes
+ [help]
+ Write the specified data chunks 'just in time', i.e., delay the write
+ until data is needed by the decoder/player for an uninterrupted audio
+ stream. This may be useful for third-party software.
+ [/help]
+ [option no-header]
+ short_opt = h
+ summary = do not write an audio file header
+ [help]
+ Some audio formats store information about the audio file in
+ a format-specific header which is needed to decode any part of
+ the file. For such formats the afh receiver generates a suitable
+ header. This option changes the default behaviour, i.e. no header
+ is written.
+ [/help]
+[subcommand http]
+ purpose = receive an audio stream over HTTP
+ m4_include(host.m4)
+ m4_include(port.m4)
+[subcommand dccp]
+ purpose = receive an audio stream over DCCP
+ m4_include(host.m4)
+ m4_include(port.m4)
+ [option ccid]
+ short_opt = c
+ summary = CCID preference(s) for this connection
+ typestr = id
+ arg_info = required_arg
+ arg_type = uint32
+ flag multiple
+ [help]
+ When present exactly once, this option mandates the CCID for the
+ sender-receiver connection. If it is passed more than once, it sets
+ a preference list where the order of appearance signifies descending
+ priority. For example, passing 4, 2, 3 creates the preference list
+ (CCID-4, CCID-2, CCID-3), assigning CCID-4 highest preference.
+
+ The request is reconciled with the CCIDs on the server through the
+ 'server-priority' mechanism of RFC 4340 6.3.1/10. The server CCIDs
+ can be listed by calling 'para_client si'.
+ [/help]
+[subcommand udp]
+ purpose = receive an audio stream over UDP
+ [option host]
+ short_opt = i
+ summary = IP address or hostname
+ typestr = host
+ arg_info = required_arg
+ arg_type = string
+ default_val = 224.0.1.38
+ [help]
+ The default address resolves to DANTZ.MCAST.NET and activates
+ multicast.
+ [/help]
+ m4_include(port.m4)
+ [option iface]
+ summary = receiving udp multicast interface
+ typestr = iface-name
+ arg_info = required_arg
+ arg_type = string
--- /dev/null
+m4_define(PROGRAM, para_server)
+m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/server.conf)
+[suite server]
+version-string = GIT_VERSION()
+[supercommand para_server]
+ purpose = manage and stream audio files
+ [description]
+ para_server streams audio files over a local or remote network. It
+ is controlled by para_client(1), which connects para_server through
+ the paraslash control service.
+
+ On startup the server spawns a second process, the audio file selector,
+ which maintains the database of all known audio files. This database
+ contains file format, duration and tag information of each known file
+ and statistics such as last-played time, and the number of times each
+ file was streamed. Lyrics and cover art may be added to the database
+ and associated with one or more audio files.
+
+ Besides ordinary playlists the audio file selector supports so-called
+ moods. Moods instruct the server to determine the files to be streamed
+ and their order in terms of properties stored in the database.
+ [/description]
+ m4_include(common-option-section.m4)
+ m4_include(help.m4)
+ m4_include(detailed-help.m4)
+ m4_include(version.m4)
+ m4_include(config-file.m4)
+ m4_include(priority.m4)
+ m4_include(daemon.m4)
+ m4_include(logfile.m4)
+ m4_include(user.m4)
+ m4_include(group.m4)
+ m4_include(loglevel.m4)
+ m4_include(log-timing.m4)
+ m4_include(color.m4)
+ m4_include(per-command-options-section.m4)
+ [option port]
+ short_opt = p
+ summary = listening port of the paraslash control service
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = portnumber
+ default_val = 2990
+ [help]
+ para_server listens on this TCP port for incoming connections
+ from clients such as para_client. If the default port is changed,
+ the corresponding option of para_client must be used to connect
+ to para_server.
+ [/help]
+ [option user-list]
+ summary = file which contains user names and credentials
+ arg_info = required_arg
+ arg_type = string
+ typestr = path
+ [help]
+ This file contains one line per user of the form
+
+ user <username> <key> <perms>
+
+ See the manual for more information.
+ [/help]
+ [option vss]
+ summary = Options for the virtual streaming system
+ flag ignored
+ [option autoplay]
+ summary = start streaming on startup
+ short_opt = a
+ [help]
+ The default is to defer streaming until para_client connects and
+ executes the "play" command.
+ [/help]
+ [option autoplay-delay]
+ summary = time to wait before streaming
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = milliseconds
+ default_val = 0
+ [help]
+ This option is ignored if --autplay is not given. Otherwise, its
+ argument defines for how long streaming is delayed at startup.
+
+ This is useful in init scripts to set the delay large enough to make
+ sure para_audiod is up when para_server starts to stream.
+ [/help]
+ [option announce-time]
+ short_opt = A
+ summary = grace time for data connections
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = milliseconds
+ default_val = 300
+ [help]
+ para_server tells para_audiod through the control service connection
+ whether an audio stream is currently available. This option defines
+ the delay between announcing the stream and sending the first chunk
+ of audio data.
+ [/help]
+ [option afs]
+ summary = Options for the audio file selector
+ flag ignored
+ [option afs-database-dir]
+ summary = location of the afs database
+ short_opt = D
+ arg_info = required_arg
+ arg_type = string
+ typestr = directory
+ [help]
+ The directory which contains the database for the audio file
+ selector. The default is ~/.paraslash/afs_database-0.4.
+
+ If no database was found, the "init" command must be executed to
+ initialize the database. Once initialized, audio files may added with
+ the "add" command.
+ [/help]
+ [option afs-socket]
+ summary = socket for afs connections
+ short_opt = s
+ arg_info = required_arg
+ arg_type = string
+ typestr = path
+ default_val = /var/paraslash/afs_command_socket-0.4
+ [help]
+ Server commands communicate with the audio file selector, via a
+ local socket. This option specifies the location of the socket in
+ the file system.
+ [/help]
+ [option afs-initial-mode]
+ summary = mood or playlist to load on startup
+ short_opt = i
+ arg_info = required_arg
+ arg_type = string
+ typestr = specifier/name
+ [help]
+ The argument of this option consists of a prefix, either 'm/' or
+ 'p/', to indicate whether a mood or a playlist should be loaded,
+ followed by the name of the mood or playlist. Example:
+
+ --afs-initial-mode p/foo
+
+ loads the playlist named "foo".
+
+ If this option is not given, the dummy mood is loaded at startup.
+ [/help]
+ [option http]
+ summary = Options for the http sender
+ flag ignored
+ [option http-port]
+ summary = TCP port for http streaming
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = portnumber
+ default_val = 8000
+ [help]
+ The http sender of para_server listens on this port for incoming
+ connections. Clients are expected to send the usual http request
+ message such as 'GET / HTTP/'.
+ [/help]
+ [option http-default-deny]
+ summary = make the http access control list a whitelist
+ [help]
+ The default is to use blacklists, i.e. connections to the http sender
+ are allowed unless the connecting host 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.
+ [/help]
+ [option http-access]
+ summary = add an entry to the http access control list
+ arg_info = required_arg
+ arg_type = string
+ typestr = a.b.c.d/n
+ flag multiple
+ [help]
+ Add the given host/network to access control list (whitelist if
+ http-default-deny was given, blacklist otherwise) before opening
+ the tcp port. This option can be given multiple times. Example:
+
+ --http-access 192.168.0.0/24
+
+ whitelists/blacklists the 256 hosts 192.168.0.x.
+
+ This option may be given multiple times to blacklist/whitelist any
+ number of hosts or networks.
+ [/help]
+ [option http-no-autostart]
+ summary = do not open TCP port for http streaming on startup
+ [help]
+ If this option is given, the http sender does not listen on its TCP
+ port until the "sender" command is executed to open the port.
+ [/help]
+ [option http-max-clients]
+ summary = maximal number of simultaneous http connections
+ arg_info = required_arg
+ arg_type = int32
+ typestr = number
+ default_val = -1
+ [help]
+ The http sender will refuse connections if already that number of
+ clients are currently connected. A non-positive value (the default)
+ allows for an unlimited number of simultaneous connections.
+ [/help]
+ [option dccp]
+ summary = Options for the dccp sender
+ flag ignored
+ [option dccp-port]
+ summary = port for dccp streaming
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = portnumber
+ default_val = 8000
+ [help]
+ See --http-port for details.
+ [/help]
+ [option dccp-default-deny]
+ summary = make the dccp access control list a whitelist
+ [help]
+ See http-default-deny for details.
+ [/help]
+ [option dccp-access]
+ summary = add an entry to the dccp access control list
+ arg_info = required_arg
+ arg_type = string
+ typestr = a.b.c.d/n
+ flag multiple
+ [help]
+ See --http-access for details.
+ [/help]
+ [option dccp-max-clients]
+ summary = maximal number of simultaneous dccp connections
+ arg_info = required_arg
+ arg_type = int32
+ typestr = number
+ default_val = -1
+ [help]
+ See --http-max-clients for details.
+ [/help]
+ [option dccp-max-slice-size]
+ summary = upper bound for the FEC slice size
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = bytes
+ default_val = 0
+ [help]
+ If this value is zero (the default) the dccp sender uses the maximum
+ packet size (MPS) of the connection as the slice size. The MPS is a
+ network parameter and depends on the path maximum transmission unit
+ (path MTU) of an incoming connection, i.e. on the largest packet size
+ that can be transmitted without causing fragmentation.
+
+ This option allows to use a value less than the MPS in order to
+ fine-tune application performance. Values greater than the MPS of an
+ incoming connection can not be set.
+ [/help]
+ [option dccp-data-slices-per-group]
+ summary = the number of non-redundant slices per FEC group
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = count
+ default_val = 3
+ [help]
+ This determines the number of slices in each FEC group which are
+ necessary to decode the group. The given number must be smaller than
+ the argument to 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.
+ [/help]
+ [option dccp-slices-per-group]
+ summary = the total number of slices per FEC group
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = count
+ default_val = 4
+ [help]
+ This value must be larger than the value of the argument to
+ --dccp-data-slices-per-group. The difference of the two values is
+ the number of redundant slices, that is, the number of slices which
+ may be lost without causing interruptions in the audio stream.
+
+ Increase this value if you are on a lossy network.
+ [/help]
+ [option udp]
+ summary = Options for the udp sender
+ flag ignored
+ [option udp-target]
+ summary = add udp target with optional port
+ arg_info = required_arg
+ arg_type = string
+ typestr = host[:port]
+ flag multiple
+ [help]
+ Add the 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 (see below) is used.
+
+ The following examples are possible targets: "10.10.1.2:8000"
+ (host:port); "10.10.1.2" (with default port); "224.0.1.38:1500"
+ (IPv4 multicast); "localhost:8001" (hostname with port); "[::1]:8001"
+ (IPv6 localhost); "[badc0de::1]" (IPv6 host with default port);
+ "[FF00::beef]:1500" (IPv6 multicast).
+
+ This option can be given multiple times, for multiple targets.
+ [/help]
+ [option udp-default-port]
+ summary = default port for udp targets
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = portnumber
+ default_val = 8000
+ [option udp-no-autostart]
+ summary = do not send the audio stream to UDP targets
+ [help]
+ If this option is given, udp streaming may be activated at a later
+ time by executing the "sender" command.
+ [/help]
+ [option udp-mcast-iface]
+ summary = outgoing udp multicast interface
+ arg_info = required_arg
+ arg_type = string
+ typestr = interface
+ [option udp-header-interval]
+ short_opt = H
+ summary = duration for sending header
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = milliseconds
+ default_val = 2000
+ [help]
+ As the udp sender has no idea about connected clients it sends the
+ audio file header periodically if necessary. This 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.
+ [/help]
+ [option udp-ttl]
+ short_opt = t
+ summary = set time to live value
+ arg_info = required_arg
+ arg_type = int32
+ typestr = num
+ default_val = -1
+ [help]
+ This option applies exclusively to multicast UDPv4/v6 streaming.
+
+ For the sending UDPv4 socket it sets the multicast Time-To-Live value
+ to "num". Traditional TTL scope values are: 0=host, 1=network, 32=same
+ site, 64=same region, 128=same continent, 255=unrestricted. Please
+ note however that this scoping is not a good solution: RFC 2365
+ e.g. presents a better alternative.
+
+ When using UDPv6 multicasting, the option sets the number of multicast
+ hops (as described in RFC 3493); a value of -1 allows the kernel to
+ auto-select the hop value.
+ [/help]
--- /dev/null
+[suite server_cmd]
+caption = list of server commands
+aux_info_prefix = Permissions:
+
+[introduction]
+ The server process listens on a network socket and accepts connections
+ from para_client or para_audiod. For the connection to succeed the
+ connecting peer must authenticate as one of the users stored in the
+ user table of para_server. Each entry of the user table contains the
+ set of permission bits that are granted to the user. Authenticated
+ users may execute one of the commands below if the set of permission
+ bits of the command is a subset of the permission bits that are
+ granted to the user.
+[/introduction]
+
+[subcommand add]
+ purpose = add or update audio files
+ non-opts-name = path...
+ aux_info = AFS_READ | AFS_WRITE
+ [description]
+ Each path must be absolute and refer to either an audio file or a
+ directory. In case of a directory, all audio files in that directory
+ are added recursively. Note that the given paths refer to files or
+ directories on the host on which para_server is running.
+ [/description]
+ [option all]
+ short_opt = a
+ summary = add all files
+ [help]
+ The default is to add only files ending in a known suffix for a
+ supported audio format.
+ [/help]
+ [option lazy]
+ short_opt = l
+ summary = add files lazily
+ [help]
+ If the path already exists in the database, skip this file. This
+ operation is really cheap. Useful to update large directories after
+ some files have been added.
+ [/help]
+ [option force]
+ short_opt = f
+ summary = force adding/updating
+ [help]
+ Recompute the audio format handler data even if a file with the same
+ path and the same hash value exists.
+ [/help]
+ [option verbose]
+ short_opt = v
+ summary = enable verbose mode
+ [help]
+ Print what is being done.
+ [/help]
+
+[subcommand addatt]
+ purpose = add new attribute(s)
+ non-opts-name = attribute...
+ aux_info = AFS_READ | AFS_WRITE
+ [description]
+ This adds new attributes to the attribute table. At most 64 attributes
+ may be defined.
+ [/description]
+
+[subcommand check]
+ purpose = run integrity checks on database tables
+ aux_info = AFS_READ
+ [description]
+ If no options are given, all checks are run.
+ [/description]
+ [option aft]
+ short_opt = a
+ summary = run audio file table checks
+ [help]
+ Report stale paths and invalid image and lyrics ids of the audio
+ file table.
+ [/help]
+ [option attribute]
+ short_opt = A
+ summary = check for invalid attributes
+ [help]
+ Report audio files whose attribute bitmask is invalid, i.e., has a bit
+ set which does not correspond to any attribute of the attribute table.
+ [/help]
+ [option mood]
+ short_opt = m
+ summary = check for invalid mood definitions
+ [help]
+ Run syntax checks on all moods of the mood table.
+ [/help]
+ [option playlist]
+ short_opt = p
+ summary = find invalid paths in playlists
+ [help]
+ Check all playlists for paths not contained in the audio file table.
+ [/help]
+
+[subcommand cpsi]
+ purpose = copy selected parts of the audio file selector info
+ non-opts-name = source pattern...
+ aux_info = AFS_READ | AFS_WRITE
+ [description]
+ If no option, or only --verbose is given, all fields of the audio
+ file selector info structure are copied to each row of the audio file
+ table whose path matches at least one of the given patterns. Otherwise,
+ only those fields which correspond to the given options are copied.
+ [/description]
+ [option attribute-bitmap]
+ short_opt = a
+ summary = copy the attribute bitmap
+ [option image-id]
+ short_opt = i
+ summary = copy the image id
+ [option lyrics-id]
+ short_opt = y
+ summary = copy the lyrics id
+ [option lastplayed]
+ short_opt = l
+ summary = copy the lastplayed timestamp
+ [option numplayed]
+ short_opt = n
+ summary = copy the numplayed counter
+ [option verbose]
+ short_opt = v
+ summary = enable verbose mode
+
+[subcommand ff]
+ purpose = jump N seconds forward or backward
+ synopsis = n[-]
+ aux_info = VSS_READ | VSS_WRITE
+ [description]
+ This sets the 'R' (reposition request) bit of the vss status flags
+ which enqueues a request to jump n seconds forwards or backwards.
+
+ Example:
+
+ para_client ff 30-
+
+ jumps 30 seconds backwards.
+
+ [/description]
+
+[subcommand help]
+ purpose = list available commands or print command-specific help
+ non-opts-name = [command]
+ aux_info = NO_PERMISSION_REQUIRED
+ [description]
+ Without any arguments, help prints a list of available commands. When
+ called with a command name as first argument, it prints the description
+ of this command.
+ [/description]
+
+[subcommand hup]
+ purpose = reload config file, log file and user list
+ aux_info = VSS_WRITE
+ [description]
+ Reread the config file and the user list file, close and reopen the log
+ file, and ask the afs process to do the same. Sending the HUP signal
+ to the server process has the same effect as running this command.
+ [/description]
+
+[subcommand init]
+ purpose = initialize the database tables for the audio file selector
+ synopsis = [table_name...]
+ aux_info = AFS_READ | AFS_WRITE
+ [description]
+ When invoked without arguments, this command creates all
+ tables: audio_files, attributes, scores, moods, lyrics, images,
+ playlists. Otherwise only the given tables are created.
+ [/description]
+
+[subcommand jmp]
+ purpose = reposition the current stream
+ non-opts-name = n
+ aux_info = VSS_READ | VSS_WRITE
+ [description]
+ Set the 'R' (reposition request) bit of the vss status flags and
+ enqueue a request to jump to n% of the current audio file, where 0 <=
+ n <= 100.
+ [/description]
+
+[subcommand ls]
+ purpose = list audio files which match a pattern
+ non-opts-name = [pattern...]
+ aux_info = AFS_READ
+ [description]
+ If no pattern is given, all files are listed. Otherwise, the command
+ lists all files of the audio file table whose path matches at least
+ one of the given patterns.
+ [/description]
+ [option listing-mode]
+ short_opt = l
+ summary = use alternative output format
+ arg_type = string
+ arg_info = optional_arg
+ typestr = mode
+ default_val = long
+ [help]
+ The optional mode argument is either a single character or a word
+ according to the following list.
+
+ short (s). List only the path or basename (last component of the path),
+ depending on whether -p is also given. This listing mode acts as if
+ --listing-mode had not been given.
+
+ long (l). Show detailed information. This is the default if no argument
+ to --listing-mode is supplied.
+
+ verbose (v). Multi-line output, one row per data field stored in the
+ audio file table.
+
+ parser-friendly (p). Like verbose listing mode, but use numerical
+ values for the names of the output fields and prefix each line with
+ a length field.
+
+ mbox (m). Generate output suitable to be viewed with a mail
+ program. One "mail" per matching audio file.
+
+ chunk-table (c). Print path (or basename, depending on whether -p is
+ also given), chunk time and chunk offsets.
+
+ [/help]
+ [option full-path]
+ short_opt = F
+ summary = list full paths, match full paths against patterns
+ [help]
+ This option is the default, so it does nothing. Deprecated as of
+ v0.6.0, scheduled for removal in v0.6.1.
+ [/help]
+ [option basename]
+ short_opt = b
+ summary = list and match basenames only
+ [help]
+ Print only the basename of each matching file and match only the
+ basenames of the paths stored in the audio file table against the
+ given patterns. The default is to print and match the full path.
+ [/help]
+ [option admissible]
+ short_opt = a
+ summary = list only admissible files
+ [help]
+ List only files which are admissible with respect to the current mood
+ or playlist.
+ [/help]
+ [option reverse]
+ short_opt = r
+ summary = reverse sort order
+ [option unix-date]
+ short_opt = d
+ summary = print dates as seconds after the epoch
+ [option sort]
+ short_opt = s
+ summary = change sort order
+ arg_type = string
+ arg_info = required_arg
+ typestr = order
+ default_val = path
+ [help]
+ The sort order must be given as an required argument. Like for
+ --listing-mode, this argument may either be a single character or a
+ word, according to the following list.
+
+ path (p). Sort alphabetically by path or basename (see -p). This is
+ the default if --sort is not given.
+
+ score (s). Iterate over the entries of the score table, rather than
+ the audio file table. This sort order implies --admissible, since
+ the score table contains only admissible files.
+
+ lastplayed (l)
+
+ numplayed (n)
+
+ frequency (f)
+
+ channels (c)
+
+ image-id (i)
+
+ lyrics-id (y)
+
+ bitrate (b)
+
+ duration (d)
+
+ audio-format (a)
+
+ If --sort is not given, path sort is implied.
+ [/help]
+
+[subcommand lsatt]
+ purpose = list attributes
+ aux_info = AFS_READ
+ [description]
+ Print the list of all defined attributes which match the given
+ pattern. If no pattern is given, the full list is printed.
+ [/description]
+
+ [option id-sort]
+ short_opt = i
+ summary = sort attributes by id
+ [help]
+ The default is to sort alphabetically by name.
+
+ Attributes are internally represented as an 64 bit array. The attribute
+ id is the bit number in this array.
+ [/help]
+ [option long]
+ short_opt = l
+ summary = print long listing
+ [help]
+ The long listing prints the attribute id in addition to the name of
+ the attribute. The id is printed as a decimal number and is separated
+ from the name by a tab character.
+ [/help]
+ [option reverse]
+ short_opt = r
+ summary = reverse sort order
+
+[subcommand mvatt]
+ purpose = rename an attribute
+ synopsis = source dest
+ aux_info = AFS_READ | AFS_WRITE
+ [description]
+ Rename the attribute given by the first argument to the destination
+ given by the second argument. It is an error if the destination
+ attribute exists.
+ [/description]
+
+[subcommand next]
+ purpose = close the stream and start to stream the next audio file
+ aux_info = VSS_READ | VSS_WRITE
+ [description]
+ Set the 'N' (next audio file) bit of the vss status flags. This
+ instructs the server to close the current stream, if any. The 'P'
+ (playing) bit is not modified by this command. If it is on, playing
+ continues with the next audio file.
+
+ This command is equivalent to stop if paused, and has no effect
+ if stopped.
+ [/description]
+
+[subcommand nomore]
+ purpose = stop playing after current audio file
+ aux_info = VSS_READ | VSS_WRITE
+ [description]
+ Set the 'O' (no more) bit of the vss status flags which asks
+ para_server to clear the 'P' (playing) bit after the 'N' (next audio
+ file) bit transitions from off to on (because the end of the current
+ audio file is reached). Use this command instead of stop if you don't
+ like sudden endings.
+ [/description]
+
+[subcommand pause]
+ purpose = suspend the current stream
+ aux_info = VSS_READ | VSS_WRITE
+ [description]
+ Clear the 'P' (playing) bit of the vss status flags.
+ [/description]
+
+[subcommand play]
+ purpose = start or resume playback
+ aux_info = VSS_READ | VSS_WRITE
+ [description]
+ Set the 'P' (playing) bit of the vss status flags.
+ [/description]
+
+[subcommand rm]
+ purpose = remove rows from the audio file table
+ non-opts-name = pattern...
+ aux_info = AFS_READ | AFS_WRITE
+ [description]
+ Remove all rows of the audio file table which match any of the given
+ patterns. Note that this affects only the database table; the command
+ won't touch your audio files on disk.
+ [/description]
+ [option verbose]
+ short_opt = v
+ summary = print paths of deleted rows
+ [option force]
+ short_opt = f
+ summary = don't complain if nothing was removed
+ [option pathname-match]
+ short_opt = p
+ summary = modify matching behaviour
+ [help]
+ Match a slash in the path only with a slash in pattern and not by an
+ asterisk (*) or a question mark (?) metacharacter, nor by a bracket
+ expression ([]) containing a slash (see fnmatch(3)).
+ [/help]
+
+[subcommand rmatt]
+ purpose = remove attribute(s)
+ non-opts-name = pattern...
+ aux_info = AFS_READ | AFS_WRITE
+ [description]
+ Remove all attributes which match any given pattern. All information
+ about the removed attributes in the audio file table is lost.
+ [/description]
+
+[subcommand select]
+ purpose = activate a mood or a playlist
+ non-opts-name = specifier/name
+ aux_info = AFS_READ | AFS_WRITE
+ [description]
+ The specifier is either 'm' or 'p' to indicate whether a playlist or
+ a mood should be activated. Example:
+
+ select m/foo
+
+ activates the mood named 'foo'.
+ [/description]
+
+[subcommand sender]
+ purpose = control paraslash senders
+ synopsis = [sender cmd [arguments]]
+ aux_info = VSS_READ | VSS_WRITE
+ [description]
+ Send a command to a specific sender. The following commands are
+ available, but not all senders support every command.
+
+ help, on, off, add, delete, allow, deny, status.
+
+ The help command prints the help text of the given sender. If no
+ command is given the list of available senders is shown.
+
+ Example:
+
+ para_client sender http help
+
+ [/description]
+
+[subcommand setatt]
+ purpose = set or unset attributes
+ synopsis = attribute{+|-}... pattern...
+ aux_info = AFS_READ | AFS_WRITE
+ [description]
+ Set ('+') or unset ('-') the given attributes for all audio files
+ matching the given pattern. Example:
+
+ setatt rock+ punk+ pop- '*foo.mp3'
+
+ sets the 'rock' and the 'punk' attribute and unsets the 'pop' attribute
+ of all files ending with 'foo.mp3'.
+ [/description]
+
+[subcommand si]
+ purpose = print server info
+ aux_info = NO_PERMISSION_REQUIRED
+ [description]
+ Show server and afs PID, number of connections, uptime and more.
+ [/description]
+
+[subcommand stat]
+ purpose = print information about the current audio file
+ aux_info = VSS_READ
+ [option num]
+ short_opt = n
+ summary = number of times to show the status info
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = num
+ [help]
+ Exit after the status information has been shown num times. If this
+ option is not given, the command runs in an endless loop.
+ [/help]
+ [option parser-friendly]
+ short_opt = p
+ summary = enable parser-friendly output
+ [help]
+ Show status item identifiers as numerical values and prefix each
+ status item with its size in bytes.
+ [/help]
+
+[subcommand stop]
+ purpose = stop playback
+ aux_info = VSS_READ | VSS_WRITE
+ [description]
+ Clear the 'P' (playing) bit and set the 'N' (next audio file) bit of
+ the vss status flags, effectively stopping playback.
+ [/description]
+
+[subcommand tasks]
+ purpose = list active server tasks
+ aux_info = NO_PERMISSION_REQUIRED
+ [description]
+ For each task, print ID, status and name. This is mostly useful
+ for debugging.
+ [/description]
+
+[subcommand term]
+ purpose = ask the server to terminate
+ aux_info = VSS_READ | VSS_WRITE
+ [description]
+ Shut down the server. Instead of this command, you can also send
+ SIGINT or SIGTERM to the para_server process. It should never be
+ necessary to send SIGKILL.
+ [/description]
+
+[subcommand touch]
+ purpose = manipulate the afs information of audio files
+ non-opts-name = pattern...
+ aux_info = AFS_READ | AFS_WRITE
+ [description]
+ This command modifies the afs info structure of all rows of the audio
+ file table whose path matches at least one of the given patters.
+
+ If at least one option is given which takes a number as its argument,
+ only those fields of the afs info structure are updated which
+ correspond to the given options while all other fields stay unmodified.
+
+ If no such option is given, the lastplayed field is set to the current
+ time and the value of the numplayed field is increased by one while
+ all other fields are left unchanged. This mimics what happens when
+ the virtual streaming system selects the file for streaming.
+
+ If the file is admissible for the current mood (or contained in the
+ current playlist), its score is recomputed according to the changed
+ values.
+ [/description]
+ [option numplayed]
+ short_opt = n
+ summary = set the numplayed count manually
+ arg_type = uint32
+ arg_info = required_arg
+ typestr = num
+ [help]
+ The numplayed count of an audio file is the number of times the file
+ was selected for streaming. It is one of the inputs to the scoring
+ function which determines the order in which admissible files are
+ streamed.
+
+ The virtual streaming system increases this number automatically each
+ time it opens the file for streaming.
+ [/help]
+ [option lastplayed]
+ short_opt = l
+ summary = set the lastplayed time manually
+ arg_type = uint64
+ arg_info = required_arg
+ typestr = num
+ [help]
+ The lastplayed time of an audio file is the time when the file was
+ last opened for streaming.
+
+ Like the numplayed count, it is an input for the scoring function
+ and is updated automatically by the virtual streaming system.
+
+ The argument must be a number of seconds since the epoch. Example:
+
+ touch -l=$(date +%s) file
+
+ sets the lastplayed time of 'file' to the current time.
+ [/help]
+ [option image-id]
+ short_opt = i
+ summary = set the image id
+ arg_type = uint32
+ arg_info = required_arg
+ typestr = num
+ [help]
+ The afs info structure of each row of the audio file table contains
+ a slot for the image id of the audio file that corresponds to the
+ row. The image id stored in this slot refers to the key in the image
+ table that identifies the blob.
+
+ When a new audio file is added to the audio file table, its image
+ id starts out as zero, indicating that there is no image associated
+ with the file. Setting the image id to a non-zero number associates
+ the file with a particular blob of the image table, for example the
+ cover art of the album in jpg format.
+ [/help]
+ [option lyrics-id]
+ short_opt = y
+ summary = set the lyrics id
+ arg_type = uint32
+ arg_info = required_arg
+ typestr = num
+ [help]
+ This option works just like --image-id, but sets the lyrics ID rather
+ than the image id.
+ [/help]
+ [option amp]
+ short_opt = a
+ summary = set the amplification value (0-255)
+ arg_type = uint32
+ arg_info = required_arg
+ typestr = num
+ [help]
+ The amplification value of an audio file is a number which is stored
+ in the afs info structure.
+
+ The value determines the scaling factor by which the amplitude of
+ the decoded samples should be multiplied in order to normalize the
+ volume. A value of zero means no amplification, 64 means the amplitude
+ should be multiplied by a factor of two, 128 by three and so on.
+
+ The amp filter of para_audiod amplifies the volume according to
+ this value.
+ [/help]
+ [option verbose]
+ short_opt = v
+ summary = explain what is being done
+ [option pathname-match]
+ short_opt = p
+ summary = modify matching behaviour
+ [help]
+ Match a slash in the path only with a slash in pattern and not by an
+ asterisk (*) or a question mark (?) metacharacter, nor by a bracket
+ expression ([]) containing a slash (see fnmatch(3)).
+ [/help]
+
+[subcommand version]
+ purpose = print the git version string of para_server
+ aux_info = NO_PERMISSION_REQUIRED
+ [option verbose]
+ short_opt = v
+ summary = print detailed (multi-line) version text
+
+m4_define(`BLOB_COMMANDS', `
+[subcommand rm`$2']
+ purpose = remove `$1' blob(s)
+ non-opts-name = pattern...
+ aux_info = AFS_READ | AFS_WRITE
+ [description]
+ Remove all `$1' blobs which match any of the given patterns.
+ [/description]
+
+[subcommand mv`$2']
+ purpose = rename `$1' blob(s)
+ non-opts-name = source dest
+ aux_info = AFS_READ | AFS_WRITE
+ [description]
+ Rename `$1' source to dest. The command fails if the source `$1'
+ does not exist or if the destination `$1' already exists.
+ [/description]
+
+[subcommand add`$2']
+ purpose = add a blob to the `$1' table
+ non-opts-name = `$1'_name
+ aux_info = AFS_READ | AFS_WRITE
+ [description]
+ Read from stdin and ask the audio file selector to create a blob in
+ the `$1' table. If the named blob already exists, it gets replaced
+ with the new data.
+ [/description]
+
+[subcommand cat`$2']
+ purpose = dump a `$1' blob to stdout
+ non-opts-name = `$1'_name
+ aux_info = AFS_READ
+
+[subcommand ls`$2']
+ purpose = list blobs of type `$1' which match a pattern
+ non-opts-name = [pattern...]
+ aux_info = AFS_READ
+ [description]
+ Print the list of all blobs which match the given pattern. If no
+ pattern is given, the full list is printed.
+ [/description]
+ [option id-sort]
+ short_opt = i
+ summary = sort by identifier
+ [help]
+ The default is to sort alphabetically by name.
+ [/help]
+ [option long]
+ short_opt = l
+ summary = long listing
+ [help]
+ Print identifier and name. The default is to print only the name.
+ [/help]
+ [option reverse]
+ short_opt = r
+ summary = reverse sort order
+')
+
+BLOB_COMMANDS(`moods', `mood')
+BLOB_COMMANDS(`playlist', `pl')
+BLOB_COMMANDS(`image', `img')
+BLOB_COMMANDS(`lyrics', `lyr')
--- /dev/null
+m4_define(PROGRAM, para_write)
+[suite write]
+version-string = GIT_VERSION()
+[supercommand para_write]
+ purpose = play wav or raw audio
+ [description]
+ para_write reads audio data from stdin and starts one supported writer.
+ [/description]
+ m4_include(common-option-section.m4)
+ m4_include(help.m4)
+ m4_include(detailed-help.m4)
+ m4_include(version.m4)
+ m4_include(loglevel.m4)
+ m4_include(per-command-options-section.m4)
+ [option writer]
+ short_opt = w
+ summary = select writer to start
+ arg_info = required_arg
+ arg_type = string
+ flag multiple
+ typestr = 'name [options]'
+ [help]
+ May be given multiple times, and the same writer may be specified more
+ than once. If this option is not given, the first supported writer
+ is started. The list of supported writers is shown in the help output.
+
+ Options for a particular writer may be specified for each given
+ '--writer' option separately. You will have to quote these options
+ like this:
+
+ --writer 'oss --device /dev/dsp2'
+ [/help]
+ m4_include(channels.m4)
+ m4_include(sample-rate.m4)
+ m4_include(sample-format.m4)
--- /dev/null
+[suite write_cmd]
+caption = writers
+[subcommand alsa]
+ purpose = native ALSA output plugin
+ [option device]
+ short_opt = d
+ summary = set PCM device
+ typestr = device
+ arg_info = required_arg
+ arg_type = string
+ default_val = default
+ [help]
+ Check for the presence of a /proc/asound/ directory to see if ALSA
+ is present in your kernel. The file /proc/asound/devices contains
+ all devices ALSA knows about.
+ [/help]
+ [option buffer-time]
+ short_opt = B
+ summary = duration of the ALSA buffer
+ typestr = milliseconds
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 170
+ [help]
+ This is only a hint as ALSA might pick a slightly different time,
+ depending on the sound hardware. The chosen value is shown in debug
+ output as BUFFER_TIME.
+
+ If synchronization between multiple clients is desired, the same
+ buffer time should be configured for all clients.
+ [/help]
+[subcommand ao]
+ purpose = output plugin for libao
+ [option driver]
+ short_opt = d
+ summary = select a output driver by name
+ typestr = name
+ arg_info = required_arg
+ arg_type = string
+ [help]
+ If this is not given, the driver with the highest priority (see below)
+ will be used.
+ [/help]
+ [option ao-option]
+ short_opt = o
+ summary = pass a key-value pair to the libao driver
+ typestr = key:value
+ arg_info = required_arg
+ arg_type = string
+ flag multiple
+ [help]
+ For each time this option is given, the supplied key-value pair is
+ appended to the list of options for the driver. Invalid keys are
+ silently ignored.
+ [/help]
+[subcommand oss]
+ purpose = output plugin for the Open Sound System
+ [option device]
+ short_opt = d
+ summary = set PCM device
+ typestr = path
+ arg_info = required_arg
+ arg_type = string
+ default_val = /dev/dsp
+[subcommand osx]
+ purpose = output plugin for Mac OS coreaudio
+ [option numbuffers]
+ short_opt = n
+ summary = number of audio buffers to allocate
+ typestr = num
+ arg_info = required_arg
+ arg_type = uint32
+ default_val = 20
+ [help]
+ Increase if you get buffer underruns.
+ [/help]
+[subcommand file]
+ purpose = output plugin that writes to a local file
+ [option filename]
+ short_opt = f
+ summary = specify output file name
+ typestr = path
+ arg_info = required_arg
+ arg_type = string
+ [help]
+ Defaults to a random filename in ~/.paraslash.
+ [/help]
+++ /dev/null
-#!/usr/bin/env bash
-
-# Receivers, filters, writers are called "modules" in this script
-print_modhelp()
-{
- local ggo="$1"
-
- $GENGETOPT --show-detailed-help \
- --set-version "" \
- --set-package "" \
- < "$ggo" | awk 'BEGIN {
- have_purpose=0
- have_usage=0
- } {
- if (!have_purpose) {
- if ($0 ~ /^ *$/)
- next
- printf(" (%s):", $0)
- have_purpose=1
- next
- }
- if (!have_usage) {
- if ($0 ~ /^Usage: /) {
- have_usage=1
- }
- next
- }
- print $0
- }'
-}
-
-make_help()
-{
- local target="$1" module ggo
-
- ggo="$GGO_DIR/$1.ggo"
- $GENGETOPT --show-detailed-help \
- --set-version "$VERSION" \
- --set-package "para_$1" \
- < "$ggo"
-
- if [[ "$target" == 'recv' || "$target" == 'audiod' ]]; then
- for module in $RECEIVERS; do
- ggo="$GGO_DIR/${module}_recv.ggo"
- [[ ! -f "$ggo" ]] && continue
- printf "\nOptions for the $module receiver"
- print_modhelp "$ggo"
- done
- fi
- if [[ "$target" == 'filter' || "$target" == 'audiod' ]]; then
- for module in $FILTERS; do
- ggo="$GGO_DIR/${module}_filter.ggo"
- [[ ! -f "$ggo" ]] && continue
- printf "\nOptions for the $module filter"
- print_modhelp "$ggo"
- done
- fi
- if [[ "$target" == 'write' || "$target" == 'audiod' ]]; then
- for module in $WRITERS; do
- ggo="$GGO_DIR/${module}_write.ggo"
- [[ ! -f "$ggo" ]] && continue
- printf "\nOptions for the $module writer"
- print_modhelp "$ggo"
- done
- fi
-}
-
-set -u
-
-(($# != 1)) && exit 1
-
-# These must be set by the caller (make or help2man)
-export COMMAND_LISTS FILTERS GENGETOPT GGO_DIR HELP2MAN HELP2MAN_DIR \
- RECEIVERS VERSION WRITERS
-
-# If either --version or --help-xxx was given, we are being called by help2man
-if [[ "$1" == "--version" ]]; then
- echo "$VERSION"
- exit $?
-fi
-if [[ "$1" =~ --help- ]]; then
- make_help "${1#--help-}"
- exit $?
-fi
-
-# Called by make, run help2man
-output_file="$1"
-target="${output_file##*/para_}"
-target="${target%.*}" # server, audiod, filter, ...
-link="$HELP2MAN_DIR/para_$target"
-
-cl_opts=
-for cl in $COMMAND_LISTS; do
- cl_opts+=" --include $cl"
-done
-
-# Create a symlink para_$target, pointing to this script. This hack is
-# necessary because help2man always includes the name of the executable in its
-# output.
-ln -sf "$PWD/$0" "$link"
-
-# This will call us again twice, with either --help-$target or --version given.
-$HELP2MAN --no-info --help-option "--help-$target" $cl_opts \
- "$link" > "$output_file"
-if (($? != 0)); then
- rm -f "$output_file"
- exit 1
-fi
* Licensed under the GPL v2. For licencing details see COPYING.
*/
-/** \file mix.h Mixer API (used by para_fade). */
+/** \file mix.h Mixer API for para_mixer. */
/**
* Opaque structure which corresponds to an instance of a mixer.
/**
* Operations provided by each mixer plugin.
+ *
+ * Each mixer plugin must define a non-static instance of this structure, with
+ * all pointers initialized to non-NULL values. No other symbols need to be
+ * exported.
*/
struct mixer {
- /** Called on startup, must fill in all other members. */
- void (*init)(struct mixer *self);
+ /** Used to identify the mixer. */
+ const char * const name;
/** Return a handle that can be passed to other methods. */
int (*open)(const char *dev, struct mixer_handle **handle);
/** Returns a string of all valid mixer channels. */
/** Free all resources associated with the given handle. */
void (*close)(struct mixer_handle **handle);
};
+
+/** Declared even if unsupported because it does not hurt and avoids ifdefs. */
+extern const struct mixer alsa_mixer, oss_mixer;
--- /dev/null
+/*
+ * Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file mixer.c A volume fader and alarm clock for OSS. */
+
+#include <regex.h>
+#include <lopsub.h>
+#include <math.h>
+
+#include "mixer.lsg.h"
+#include "para.h"
+#include "fd.h"
+#include "string.h"
+#include "mix.h"
+#include "error.h"
+#include "version.h"
+
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
+
+/* At least one of the two is defined if this file gets compiled. */
+static const struct mixer *mixers[] = {
+#ifdef HAVE_ALSA
+ &alsa_mixer,
+#endif
+#ifdef HAVE_OSS
+ &oss_mixer,
+#endif
+};
+
+#define NUM_SUPPORTED_MIXERS (ARRAY_SIZE(mixers))
+#define FOR_EACH_MIXER(i) for ((i) = 0; (i) < NUM_SUPPORTED_MIXERS; (i)++)
+
+static struct lls_parse_result *lpr, *sub_lpr;
+
+#define CMD_PTR(_cmd) (lls_cmd(LSG_MIXER_CMD_ ## _cmd, mixer_suite))
+#define OPT_RESULT(_cmd, _opt) (lls_opt_result( \
+ LSG_MIXER_ ## _cmd ## _OPT_ ## _opt, (LSG_MIXER_CMD_ ## _cmd == 0)? lpr : sub_lpr))
+#define OPT_GIVEN(_cmd, _opt) (lls_opt_given(OPT_RESULT(_cmd, _opt)))
+#define OPT_STRING_VAL(_cmd, _opt) (lls_string_val(0, OPT_RESULT(_cmd, _opt)))
+#define OPT_UINT32_VAL(_cmd, _opt) (lls_uint32_val(0, OPT_RESULT(_cmd, _opt)))
+
+typedef int (*mixer_subcommand_handler_t)(const struct mixer *, struct mixer_handle *);
+
+#define EXPORT_CMD(_cmd) const mixer_subcommand_handler_t \
+ lsg_mixer_com_ ## _cmd ## _user_data = &com_ ## _cmd;
+
+static int loglevel;
+static __printf_2_3 void date_log(int ll, const char *fmt, ...)
+{
+ va_list argp;
+ time_t t1;
+ struct tm *tm;
+
+ if (ll < loglevel)
+ return;
+ time(&t1);
+ tm = localtime(&t1);
+ fprintf(stderr, "%d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec);
+ va_start(argp, fmt);
+ vprintf(fmt, argp);
+ va_end(argp);
+}
+__printf_2_3 void (*para_log)(int, const char*, ...) = date_log;
+
+static int set_channel(const struct mixer *m, struct mixer_handle *h,
+ const char *channel)
+{
+
+ PARA_NOTICE_LOG("using %s mixer channel\n", channel?
+ channel : "default");
+ return m->set_channel(h, channel);
+}
+
+static void millisleep(int ms)
+{
+ struct timespec ts;
+
+ PARA_INFO_LOG("sleeping %dms\n", ms);
+ if (ms < 0)
+ return;
+ ts.tv_sec = ms / 1000,
+ ts.tv_nsec = (ms % 1000) * 1000 * 1000;
+ nanosleep(&ts, NULL);
+}
+
+/*
+ * This implements the inverse function of t -> t^alpha, scaled to the time
+ * interval [0,T] and the range given by old_vol and new_vol. It returns the
+ * amount of milliseconds until the given volume is reached.
+ */
+static unsigned volume_time(double vol, double old_vol, double new_vol,
+ double T, double alpha)
+{
+ double c, d, x;
+
+ if (old_vol < new_vol) {
+ c = old_vol;
+ d = new_vol;
+ } else {
+ c = new_vol;
+ d = old_vol;
+ }
+
+ x = T * exp(log(((vol - c) / (d - c))) / alpha);
+ assert(x <= T);
+ if (old_vol < new_vol)
+ return x;
+ else
+ return T - x;
+}
+
+/* Fade to new volume in fade_time seconds. */
+static int fade(const struct mixer *m, struct mixer_handle *h, uint32_t new_vol,
+ uint32_t fade_time)
+{
+ int i, T, old_vol, ret, slept, incr;
+ double ms, alpha;
+ uint32_t fe = OPT_UINT32_VAL(PARA_MIXER, FADE_EXPONENT);
+
+ if (fade_time <= 0 || fe >= 100) {
+ ret = m->set(h, new_vol);
+ if (ret < 0)
+ return ret;
+ goto sleep;
+ }
+ alpha = (100 - fe) / 100.0;
+ ret = m->get(h);
+ if (ret < 0)
+ return ret;
+ old_vol = ret;
+ if (old_vol == new_vol)
+ goto sleep;
+ PARA_NOTICE_LOG("fading %s from %d to %u in %u seconds\n",
+ OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL), old_vol,
+ new_vol, fade_time);
+ incr = old_vol < new_vol? 1 : -1;
+ T = fade_time * 1000;
+ i = old_vol;
+ slept = 0;
+ do {
+ ms = volume_time(i + incr, old_vol, new_vol, T, alpha);
+ millisleep(ms - slept);
+ slept = ms;
+ i += incr;
+ ret = m->set(h, i);
+ if (ret < 0)
+ return ret;
+ } while (i != new_vol);
+ return 1;
+sleep:
+ sleep(fade_time);
+ return ret;
+}
+
+static int com_fade(const struct mixer *m, struct mixer_handle *h)
+{
+ uint32_t new_vol = OPT_UINT32_VAL(FADE, FADE_VOL);
+ uint32_t fade_time = OPT_UINT32_VAL(FADE, FADE_TIME);
+ return fade(m, h, new_vol, fade_time);
+}
+EXPORT_CMD(fade);
+
+static void client_cmd(const char *cmd)
+{
+ int ret, status, fds[3] = {0, 0, 0};
+ pid_t pid;
+ char *cmdline = make_message(BINDIR "/para_client %s", cmd);
+
+ PARA_NOTICE_LOG("%s\n", cmdline);
+ ret = para_exec_cmdline_pid(&pid, cmdline, fds);
+ free(cmdline);
+ if (ret < 0) {
+ PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ goto fail;
+ }
+ do
+ pid = waitpid(pid, &status, 0);
+ while (pid == -1 && errno == EINTR);
+ if (pid < 0) {
+ PARA_ERROR_LOG("%s\n", strerror(errno));
+ goto fail;
+ }
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ goto fail;
+ return;
+fail:
+ PARA_EMERG_LOG("command \"%s\" failed\n", cmd);
+ exit(EXIT_FAILURE);
+}
+
+static void change_afs_mode(const char *afs_mode)
+{
+ char *cmd;
+
+ client_cmd("stop");
+ if (!afs_mode)
+ return;
+ cmd = make_message("select %s", afs_mode);
+ client_cmd(cmd);
+ free(cmd);
+}
+
+static int set_initial_volume(const struct mixer *m, struct mixer_handle *h)
+{
+ int i, ret;
+
+ for (i = 0; i < OPT_GIVEN(SLEEP, IVOL); i++) {
+ const char *val = lls_string_val(i, OPT_RESULT(SLEEP, IVOL));
+ char *p, *ch, *arg = para_strdup(val);
+ int32_t iv;
+ p = strchr(arg, ':');
+ if (p) {
+ *p = '\0';
+ p++;
+ ch = arg;
+ } else {
+ p = arg;
+ ch = NULL;
+ }
+ ret = para_atoi32(p, &iv);
+ if (ret < 0) {
+ free(arg);
+ return ret;
+ }
+ ret = set_channel(m, h, ch);
+ if (!ch)
+ ch = "default";
+ if (ret < 0) {
+ PARA_WARNING_LOG("ignoring channel %s\n", ch);
+ ret = 0;
+ } else {
+ PARA_INFO_LOG("initial volume %s: %d\n", ch, iv);
+ ret = m->set(h, iv);
+ }
+ free(arg);
+ if (ret < 0)
+ return ret;
+ }
+ return 1;
+}
+
+static int com_sleep(const struct mixer *m, struct mixer_handle *h)
+{
+ time_t t1, wake_time_epoch;
+ unsigned int delay;
+ struct tm *tm;
+ int ret;
+ const char *wake_time = OPT_STRING_VAL(SLEEP, WAKE_TIME);
+ const char *fo_mood = OPT_STRING_VAL(SLEEP, FO_MOOD);
+ const char *fi_mood = OPT_STRING_VAL(SLEEP, FI_MOOD);
+ const char *sleep_mood = OPT_STRING_VAL(SLEEP, SLEEP_MOOD);
+ int fit = OPT_UINT32_VAL(SLEEP, FI_TIME);
+ int fot = OPT_UINT32_VAL(SLEEP, FO_TIME);
+ int fiv = OPT_UINT32_VAL(SLEEP, FI_VOL);
+ int fov = OPT_UINT32_VAL(SLEEP, FO_VOL);
+ int32_t hour, min = 0;
+ char *tmp;
+ char *wt = para_strdup(wake_time + (wake_time[0] == '+'));
+
+ /* calculate wake time */
+ time(&t1);
+ tmp = strchr(wt, ':');
+ if (tmp) {
+ *tmp = '\0';
+ tmp++;
+ ret = para_atoi32(tmp, &min);
+ if (ret < 0) {
+ free(wt);
+ return ret;
+ }
+ }
+ ret = para_atoi32(wt, &hour);
+ free(wt);
+ if (ret < 0)
+ return ret;
+ if (wake_time[0] == '+') { /* relative */
+ t1 += hour * 60 * 60 + min * 60;
+ tm = localtime(&t1);
+ } else {
+ tm = localtime(&t1);
+ if (tm->tm_hour > hour || (tm->tm_hour == hour && tm->tm_min> min)) {
+ t1 += 86400; /* wake time is tomorrow */
+ tm = localtime(&t1);
+ }
+ tm->tm_hour = hour;
+ tm->tm_min = min;
+ tm->tm_sec = 0;
+ }
+ wake_time_epoch = mktime(tm);
+ PARA_INFO_LOG("waketime: %d:%02d\n", tm->tm_hour, tm->tm_min);
+ client_cmd("stop");
+ sleep(1);
+ if (fot) {
+ ret = set_initial_volume(m, h);
+ if (ret < 0)
+ return ret;
+ change_afs_mode(fo_mood);
+ client_cmd("play");
+ ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL));
+ if (ret < 0)
+ return ret;
+ ret = fade(m, h, fov, fot);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = m->set(h, fov);
+ if (ret < 0)
+ return ret;
+ }
+ if (OPT_GIVEN(SLEEP, SLEEP_MOOD)) {
+ change_afs_mode(sleep_mood);
+ client_cmd("play");
+ } else
+ client_cmd("stop");
+ if (!fit)
+ return 1;
+ change_afs_mode(fi_mood);
+ for (;;) {
+ time(&t1);
+ if (wake_time_epoch <= t1 + fit)
+ break;
+ delay = wake_time_epoch - t1 - fit;
+ PARA_INFO_LOG("sleeping %u seconds (%u:%02u)\n",
+ delay, delay / 3600,
+ (delay % 3600) / 60);
+ sleep(delay);
+ }
+ client_cmd("play");
+ ret = fade(m, h, fiv, fit);
+ PARA_INFO_LOG("fade complete, returning\n");
+ return ret;
+}
+EXPORT_CMD(sleep);
+
+static int com_snooze(const struct mixer *m, struct mixer_handle *h)
+{
+ int ret, val;
+
+ if (OPT_UINT32_VAL(SNOOZE, SO_TIME) == 0)
+ return 1;
+ ret = m->get(h);
+ if (ret < 0)
+ return ret;
+ val = ret;
+ if (val < OPT_UINT32_VAL(SNOOZE, SO_VOL))
+ ret = m->set(h, OPT_UINT32_VAL(SNOOZE, SO_VOL));
+ else
+ ret = fade(m, h, OPT_UINT32_VAL(SNOOZE, SO_VOL),
+ OPT_UINT32_VAL(SNOOZE, SO_TIME));
+ if (ret < 0)
+ return ret;
+ client_cmd("pause");
+ PARA_NOTICE_LOG("%" PRIu32 " seconds snooze time...\n",
+ OPT_UINT32_VAL(SNOOZE, SNOOZE_TIME));
+ sleep(OPT_UINT32_VAL(SNOOZE, SNOOZE_TIME));
+ client_cmd("play");
+ return fade(m, h, OPT_UINT32_VAL(SNOOZE, SI_VOL),
+ OPT_UINT32_VAL(SNOOZE, SI_TIME));
+}
+EXPORT_CMD(snooze);
+
+static int com_set(const struct mixer *m, struct mixer_handle *h)
+{
+ return m->set(h, OPT_UINT32_VAL(SET, VAL));
+}
+EXPORT_CMD(set);
+
+static const struct mixer *get_mixer_or_die(void)
+{
+ int i;
+
+ if (!OPT_GIVEN(PARA_MIXER, MIXER_API))
+ i = 0; /* default: use first mixer */
+ else
+ FOR_EACH_MIXER(i)
+ if (!strcmp(mixers[i]->name,
+ OPT_STRING_VAL(PARA_MIXER, MIXER_API)))
+ break;
+ if (i < NUM_SUPPORTED_MIXERS) {
+ PARA_NOTICE_LOG("using %s mixer API\n", mixers[i]->name);
+ return mixers[i];
+ }
+ printf("available mixer APIs: ");
+ FOR_EACH_MIXER(i)
+ printf("%s%s%s ", i == 0? "[" : "", mixers[i]->name,
+ i == 0? "]" : "");
+ printf("\n");
+ exit(EXIT_FAILURE);
+}
+
+static void show_subcommands(void)
+{
+ const struct lls_command *cmd;
+ int i;
+ printf("Subcommands:\n");
+ for (i = 1; (cmd = lls_cmd(i, mixer_suite)); i++) {
+ const char *name = lls_command_name(cmd);
+ const char *purpose = lls_purpose(cmd);
+ printf("%-20s%s\n", name, purpose);
+ }
+}
+
+
+static int com_help(__a_unused const struct mixer *m,
+ __a_unused struct mixer_handle *h)
+{
+ const struct lls_command *cmd;
+ const struct lls_opt_result *r_l = OPT_RESULT(HELP, LONG);
+ char *txt, *errctx;
+ const char *name;
+ int ret;
+
+ ret = lls_check_arg_count(sub_lpr, 0, 1, NULL);
+ if (ret < 0)
+ return ret;
+ if (lls_num_inputs(sub_lpr) == 0) {
+ show_subcommands();
+ return 0;
+ }
+ name = lls_input(0, sub_lpr);
+ ret = lls(lls_lookup_subcmd(name, mixer_suite, &errctx));
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ return ret;
+ }
+ cmd = lls_cmd(ret, mixer_suite);
+ if (lls_opt_given(r_l))
+ txt = lls_long_help(cmd);
+ else
+ txt = lls_short_help(cmd);
+ printf("%s", txt);
+ free(txt);
+ return 0;
+}
+EXPORT_CMD(help);
+
+static void handle_help_flags(void)
+{
+ char *help;
+
+ if (OPT_GIVEN(PARA_MIXER, DETAILED_HELP))
+ help = lls_long_help(CMD_PTR(PARA_MIXER));
+ else if (OPT_GIVEN(PARA_MIXER, HELP))
+ help = lls_short_help(CMD_PTR(PARA_MIXER));
+ else
+ return;
+ printf("%s", help);
+ free(help);
+ show_subcommands();
+ exit(EXIT_SUCCESS);
+}
+
+static int parse_and_merge_config_file(const struct lls_command *cmd)
+{
+ int ret;
+ int cf_argc;
+ char **cf_argv;
+ char *cf, *errctx = NULL;
+ struct lls_parse_result **lprp, *cf_lpr, *merged_lpr;
+ void *map;
+ size_t sz;
+ const char *subcmd_name;
+
+ if (cmd == lls_cmd(0, mixer_suite)) {
+ lprp = &lpr;
+ subcmd_name = NULL;
+ } else {
+ lprp = &sub_lpr;
+ subcmd_name = lls_command_name(cmd);
+ }
+ if (OPT_GIVEN(PARA_MIXER, CONFIG_FILE))
+ cf = para_strdup(OPT_STRING_VAL(PARA_MIXER, CONFIG_FILE));
+ else {
+ char *home = para_homedir();
+ cf = make_message("%s/.paraslash/mixer.conf", home);
+ free(home);
+ }
+ ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+ if (ret < 0) {
+ if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+ goto free_cf;
+ if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) &&
+ OPT_GIVEN(PARA_MIXER, CONFIG_FILE))
+ goto free_cf;
+ } else {
+ ret = lls(lls_convert_config(map, sz, subcmd_name, &cf_argv, &errctx));
+ para_munmap(map, sz);
+ if (ret < 0)
+ goto free_cf;
+ cf_argc = ret;
+ ret = lls(lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx));
+ lls_free_argv(cf_argv);
+ if (ret < 0)
+ goto free_cf;
+ ret = lls(lls_merge(*lprp, cf_lpr, cmd, &merged_lpr, &errctx));
+ lls_free_parse_result(cf_lpr, cmd);
+ if (ret < 0)
+ goto free_cf;
+ lls_free_parse_result(*lprp, cmd);
+ *lprp = merged_lpr;
+ loglevel = OPT_UINT32_VAL(PARA_MIXER, LOGLEVEL);
+ }
+ ret = 1;
+free_cf:
+ free(cf);
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ return ret;
+}
+
+/**
+ * The main function of para_mixer.
+ *
+ * The executable is linked with the alsa or the oss mixer API, or both. It has
+ * a custom log function which prefixes log messages with the current date.
+ *
+ * \param argc Argument counter.
+ * \param argv Argument vector.
+ *
+ * \return EXIT_SUCCESS or EXIT_FAILURE.
+ */
+int main(int argc, char *argv[])
+{
+ const struct lls_command *cmd = CMD_PTR(PARA_MIXER);
+ int ret;
+ char *errctx;
+ const char *subcmd;
+ const struct mixer *m;
+ struct mixer_handle *h;
+ unsigned n;
+
+ ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+ if (ret < 0)
+ goto fail;
+ loglevel = OPT_UINT32_VAL(PARA_MIXER, LOGLEVEL);
+ version_handle_flag("mixer", OPT_GIVEN(PARA_MIXER, VERSION));
+ handle_help_flags();
+
+ n = lls_num_inputs(lpr);
+ if (n == 0) {
+ show_subcommands();
+ ret = 0;
+ goto free_lpr;
+ }
+ ret = parse_and_merge_config_file(cmd);
+ if (ret < 0)
+ goto free_lpr;
+ subcmd = lls_input(0, lpr);
+ ret = lls(lls_lookup_subcmd(subcmd, mixer_suite, &errctx));
+ if (ret < 0)
+ goto fail;
+ cmd = lls_cmd(ret, mixer_suite);
+ ret = lls(lls_parse(n, argv + argc - n, cmd, &sub_lpr, &errctx));
+ if (ret < 0)
+ goto free_lpr;
+ ret = parse_and_merge_config_file(cmd);
+ if (ret < 0)
+ goto free_lpr;
+ m = get_mixer_or_die();
+ ret = m->open(OPT_STRING_VAL(PARA_MIXER, MIXER_DEVICE), &h);
+ if (ret < 0)
+ goto free_sub_lpr;
+ ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL));
+ if (ret == -E_BAD_CHANNEL) {
+ char *channels = m->get_channels(h);
+ printf("Available channels: %s\n", channels);
+ free(channels);
+ }
+ if (ret < 0)
+ goto close_mixer;
+ ret = (*(mixer_subcommand_handler_t *)(lls_user_data(cmd)))(m ,h);
+close_mixer:
+ m->close(&h);
+free_sub_lpr:
+ lls_free_parse_result(sub_lpr, cmd);
+free_lpr:
+ lls_free_parse_result(lpr, CMD_PTR(PARA_MIXER));
+ if (ret >= 0)
+ return EXIT_SUCCESS;
+fail:
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+ return EXIT_FAILURE;
+}
#include <regex.h>
#include <fnmatch.h>
#include <osl.h>
+#include <lopsub.h>
#include "para.h"
#include "error.h"
#include <regex.h>
#include <osl.h>
+#include <lopsub.h>
#include "para.h"
#include "error.h"
tv_divide(afhi->chunks_total, &total_time, &afhi->chunk_tv);
PARA_DEBUG_LOG("%" PRIu32 "chunks, each %lums\n", afhi->chunks_total,
tv2ms(&afhi->chunk_tv));
+ set_max_chunk_size(afhi);
ret = mp3_get_id3(map, numbytes, fd, &afhi->tags);
afhi->techinfo = make_message("%cbr, %s, %s tags", vbr? 'v' : 'c',
header_mode(&header), tag_versions[ret]);
#include <mad.h>
#include <regex.h>
+#include <lopsub.h>
+#include "filter_cmd.lsg.h"
#include "para.h"
-#include "mp3dec_filter.cmdline.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "error.h"
static void mp3dec_open(struct filter_node *fn)
{
struct private_mp3dec_data *pmd = para_calloc(sizeof(*pmd));
- struct mp3dec_filter_args_info *mp3_conf = fn->conf;
fn->private_data = pmd;
mad_stream_init(&pmd->stream);
mad_frame_init(&pmd->frame);
mad_synth_init(&pmd->synth);
- if (mp3_conf->ignore_crc_given)
+ if (FILTER_CMD_OPT_GIVEN(MP3DEC, IGNORE_CRC, fn->lpr))
mad_stream_options(&pmd->stream, MAD_OPTION_IGNORECRC);
}
-static int mp3dec_parse_config(int argc, char **argv, void **config)
-{
- struct mp3dec_filter_args_info *conf = para_calloc(sizeof(*conf));
-
- mp3dec_filter_cmdline_parser(argc, argv, conf);
- *config = conf;
- return 1;
-}
-
static int mp3dec_execute(struct btr_node *btrn, const char *cmd, char **result)
{
struct filter_node *fn = btr_context(btrn);
return decoder_execute(cmd, pmd->sample_rate, pmd->channels, result);
}
-static void mp3dec_free_config(void *conf)
-{
- mp3dec_filter_cmdline_parser_free(conf);
-}
-/**
- * The init function of the mp3dec filter.
- *
- * \param f Pointer to the filter struct to initialize.
- *
- * \sa filter::init.
- */
-void mp3dec_filter_init(struct filter *f)
-{
- struct mp3dec_filter_args_info dummy;
-
- mp3dec_filter_cmdline_parser_init(&dummy);
- f->open = mp3dec_open;
- f->close = mp3dec_close;
- f->parse_config = mp3dec_parse_config;
- f->free_config = mp3dec_free_config;
- f->pre_select = generic_filter_pre_select;
- f->post_select = mp3dec_post_select;
- f->execute = mp3dec_execute;
- f->help = (struct ggo_help)DEFINE_GGO_HELP(mp3dec_filter);
-}
+const struct filter lsg_filter_cmd_com_mp3dec_user_data = {
+ .open = mp3dec_open,
+ .close = mp3dec_close,
+ .pre_select = generic_filter_pre_select,
+ .post_select = mp3dec_post_select,
+ .execute = mp3dec_execute,
+};
*/
int generic_max_transport_msg_size(int sockfd)
{
- struct sockaddr_storage ss;
+ struct sockaddr_storage ss = {0};
socklen_t sslen = sizeof(ss);
int af_type = AF_INET;
*/
char *remote_name(int fd)
{
- struct sockaddr_storage ss;
+ struct sockaddr_storage ss = {0};
const struct sockaddr *sa;
socklen_t sslen = sizeof(ss);
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
return nccids;
}
-/**
- * Prepare a structure for \p AF_UNIX socket addresses.
- *
- * \param u Pointer to the struct to be prepared.
- * \param name The socket pathname.
+/*
+ * Prepare a structure for AF_UNIX socket addresses.
*
- * This just copies \a name to the sun_path component of \a u.
+ * This just copies name to the sun_path component of u, prepending a zero byte
+ * if abstract sockets are supported.
*
- * \return Positive on success, \p -E_NAME_TOO_LONG if \a name is longer
- * than \p UNIX_PATH_MAX.
+ * The first call to this function tries to bind a socket to the abstract name
+ * space. The result of this test is stored in a static variable. Subsequent
+ * calls read this variable and create abstract sockets on systems that support
+ * them.
*/
-static int init_unix_addr(struct sockaddr_un *u, const char *name,
- bool abstract)
+static int init_unix_addr(struct sockaddr_un *u, const char *name)
{
- if (strlen(name) + abstract >= UNIX_PATH_MAX)
+ static int use_abstract;
+
+ if (strlen(name) + 1 >= UNIX_PATH_MAX)
return -E_NAME_TOO_LONG;
memset(u->sun_path, 0, UNIX_PATH_MAX);
u->sun_family = PF_UNIX;
- strcpy(u->sun_path + abstract, name);
+ if (use_abstract == 0) { /* executed only once */
+ int fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ memcpy(u->sun_path, "\0x\0", 3);
+ if (bind(fd, (struct sockaddr *)u, sizeof(*u)) == 0)
+ use_abstract = 1; /* yes */
+ else
+ use_abstract = -1; /* no */
+ close(fd);
+ PARA_NOTICE_LOG("%susing abstract socket namespace\n",
+ use_abstract == 1? "" : "not ");
+ }
+ strcpy(u->sun_path + (use_abstract == 1? 1 : 0), name);
return 1;
}
* Create a socket for local communication and listen on it.
*
* \param name The socket pathname.
- * \param mode The desired permissions of the socket.
*
* This function creates a passive local socket for sequenced, reliable,
* two-way, connection-based byte streams. The socket file descriptor is set to
* nonblocking mode and listen(2) is called to prepare the socket for
* accepting incoming connection requests.
*
- * If mode is zero, an abstract socket (a non-portable Linux extension) is
- * created. In this case the socket name has no connection with filesystem
- * pathnames.
- *
* \return The file descriptor on success, negative error code on failure.
*
* \sa socket(2), \sa bind(2), \sa chmod(2), listen(2), unix(7).
*/
-int create_local_socket(const char *name, mode_t mode)
+int create_local_socket(const char *name)
{
struct sockaddr_un unix_addr;
int fd, ret;
- bool abstract = mode == 0;
- ret = init_unix_addr(&unix_addr, name, abstract);
+ ret = init_unix_addr(&unix_addr, name);
if (ret < 0)
return ret;
ret = socket(PF_UNIX, SOCK_STREAM, 0);
ret = -ERRNO_TO_PARA_ERROR(errno);
goto err;
}
- if (!abstract) {
+ if (unix_addr.sun_path[0] != 0) { /* pathname socket */
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
+ | S_IROTH | S_IWOTH;
ret = -E_CHMOD;
if (chmod(name, mode) < 0)
goto err;
fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (fd < 0)
return -ERRNO_TO_PARA_ERROR(errno);
- /* first try (linux-only) abstract socket */
- ret = init_unix_addr(&unix_addr, name, true);
- if (ret < 0)
- goto err;
- if (connect(fd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) != -1)
- return fd;
- /* next try pathname socket */
- ret = init_unix_addr(&unix_addr, name, false);
+ ret = init_unix_addr(&unix_addr, name);
if (ret < 0)
goto err;
if (connect(fd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) != -1)
int recv_buffer(int fd, char *buf, size_t size);
int para_accept(int fd, fd_set *rfds, void *addr, socklen_t size, int *new_fd);
-int create_local_socket(const char *name, mode_t mode);
+int create_local_socket(const char *name);
int connect_local_socket(const char *name);
int recv_cred_buffer(int, char *, size_t);
ssize_t send_cred_buffer(int, char*);
}
}
afhi->chunks_total = j;
+ set_max_chunk_size(afhi);
set_chunk_tv(frames_per_chunk, afhi->frequency, &afhi->chunk_tv);
ret = 0;
out:
#include "para.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "error.h"
return ret;
}
-/**
- * The init function of the ogg vorbis decoder.
- *
- * \param f Its fields are filled in by the function.
- */
-void oggdec_filter_init(struct filter *f)
-{
- f->open = ogg_open;
- f->close = ogg_close;
- f->pre_select = ogg_pre_select;
- f->post_select = ogg_post_select;
- f->execute = oggdec_execute;
-}
+const struct filter lsg_filter_cmd_com_oggdec_user_data = {
+ .open = ogg_open,
+ .close = ogg_close,
+ .pre_select = ogg_pre_select,
+ .post_select = ogg_post_select,
+ .execute = oggdec_execute
+};
#include "para.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "error.h"
return sched_min_delay(s);
}
-/**
- * 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 = opusdec_pre_select;
- f->post_select = opusdec_post_select;
- f->execute = opusdec_execute;
-}
+const struct filter lsg_filter_cmd_com_opusdec_user_data = {
+ .open = opusdec_open,
+ .close = opusdec_close,
+ .pre_select = opusdec_pre_select,
+ .post_select = opusdec_post_select,
+ .execute = opusdec_execute,
+};
*handle = NULL;
}
-/**
- * The init function of the OSS mixer.
- *
- * \param self The structure to initialize.
- *
- * \sa struct \ref mixer, \ref alsa_mix_init().
- */
-void oss_mix_init(struct mixer *self)
-{
- self->open = oss_mix_open;
- self->get_channels = oss_mix_get_channels;
- self->set_channel = oss_mix_set_channel;
- self->get = oss_mix_get;
- self->set = oss_mix_set;
- self->close = oss_mix_close;
-}
+/** The mixer operations for the OSS mixer. */
+const struct mixer oss_mixer = {
+ .name = "oss",
+ .open = oss_mix_open,
+ .get_channels = oss_mix_get_channels,
+ .set_channel = oss_mix_set_channel,
+ .close = oss_mix_close,
+ .get = oss_mix_get,
+ .set = oss_mix_set
+};
#include <regex.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
+#include <lopsub.h>
+#include "write_cmd.lsg.h"
#include "para.h"
#include "fd.h"
#include "string.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "write.h"
-#include "write_common.h"
-#include "oss_write.cmdline.h"
#include "error.h"
/** Data specific to the oss writer. */
{
int ret, format;
unsigned ch, rate;
- struct oss_write_args_info *conf = wn->conf;
struct private_oss_write_data *powd = para_calloc(sizeof(*powd));
+ const char *dev = WRITE_CMD_OPT_STRING_VAL(OSS, DEVICE, wn->lpr);
- PARA_INFO_LOG("opening %s\n", conf->device_arg);
- ret = para_open(conf->device_arg, O_WRONLY, 0);
+ PARA_INFO_LOG("opening %s\n", dev);
+ ret = para_open(dev, O_WRONLY, 0);
if (ret < 0)
goto err_free;
powd->fd = ret;
close(powd->fd);
err_free:
free(powd);
- PARA_ERROR_LOG("failed to init %s: %s\n", conf->device_arg,
- para_strerror(-ret));
+ PARA_ERROR_LOG("failed to init %s: %s\n", dev, para_strerror(-ret));
return ret;
}
return ret;
}
-__malloc static void *oss_parse_config_or_die(int argc, char **argv)
-{
- struct oss_write_args_info *conf = para_calloc(sizeof(*conf));
-
- /* exits on errors */
- oss_write_cmdline_parser(argc, argv, conf);
- return conf;
-}
-
-static void oss_free_config(void *conf)
-{
- oss_write_cmdline_parser_free(conf);
-}
-
-/**
- * The init function of the oss writer.
- *
- * \param w Pointer to the writer to initialize.
- *
- * \sa struct writer.
- */
-void oss_write_init(struct writer *w)
-{
- struct oss_write_args_info dummy;
-
- oss_write_cmdline_parser_init(&dummy);
- w->close = oss_close;
- w->pre_select = oss_pre_select;
- 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)DEFINE_GGO_HELP(oss_write);
- oss_write_cmdline_parser_free(&dummy);
-}
+const struct writer lsg_write_cmd_com_oss_user_data = {
+ .pre_select = oss_pre_select,
+ .post_select = oss_post_select,
+ .close = oss_close,
+};
#include <regex.h>
#include <sys/types.h>
+#include <lopsub.h>
+#include "write_cmd.lsg.h"
#include "para.h"
#include "fd.h"
#include "string.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "write.h"
-#include "write_common.h"
-#include "osx_write.cmdline.h"
#include "ipc.h"
#include "error.h"
return ret;
}
-__malloc static void *osx_write_parse_config_or_die(int argc, char **argv)
-{
- struct osx_write_args_info *conf = para_calloc(sizeof(*conf));
-
- /* exits on errors */
- osx_write_cmdline_parser(argc, argv, conf);
- return conf;
-}
-
-static void osx_free_config(void *conf)
-{
- osx_write_cmdline_parser_free(conf);
-}
-
static void osx_write_close(struct writer_node *wn)
{
struct private_osx_write_data *powd = wn->private_data;
return ret;
}
-/**
- * The init function of the osx writer.
- *
- * \param w Filled in by the function.
- */
-void osx_write_init(struct writer *w)
-{
- struct osx_write_args_info dummy;
-
- osx_write_cmdline_parser_init(&dummy);
- w->close = osx_write_close;
- w->pre_select = osx_write_pre_select;
- 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)DEFINE_GGO_HELP(osx_write);
- osx_write_cmdline_parser_free(&dummy);
-}
+struct writer lsg_write_cmd_com_osx_user_data = {
+ .close = osx_write_close,
+ .pre_select = osx_write_pre_select,
+ .post_select = osx_write_post_select,
+};
#define SAMPLE_FORMAT(a, b) b
/** \endcond sample_format */
-/** Debug loglevel, gets really noisy. */
-#define LL_DEBUG 0
-/** Still noisy, but won't fill your disk. */
-#define LL_INFO 1
-/** Normal, but significant event. */
-#define LL_NOTICE 2
-/** Unexpected event that can be handled. */
-#define LL_WARNING 3
-/** Unhandled error condition. */
-#define LL_ERROR 4
-/** System might be unreliable. */
-#define LL_CRIT 5
-/** Last message before exit. */
-#define LL_EMERG 6
-/** Number of all loglevels. */
-#define NUM_LOGLEVELS 7
-
-/** \cond log */
+/** Debug, Info, etc. */
+enum loglevels {LOGLEVELS, NUM_LOGLEVELS};
+
#define PARA_DEBUG_LOG(f,...) para_log(LL_DEBUG, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
#define PARA_INFO_LOG(f,...) para_log(LL_INFO, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
#define PARA_NOTICE_LOG(f,...) para_log(LL_NOTICE, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
#define PARA_ERROR_LOG(f,...) para_log(LL_ERROR, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
#define PARA_CRIT_LOG(f,...) para_log(LL_CRIT, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
#define PARA_EMERG_LOG(f,...) para_log(LL_EMERG, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
-/** \endcond log */
/** \file play.c Paraslash's standalone player. */
#include <regex.h>
-#include <fnmatch.h>
#include <signal.h>
+#include <inttypes.h>
+#include <lopsub.h>
+#include "recv_cmd.lsg.h"
+#include "play_cmd.lsg.h"
+#include "write_cmd.lsg.h"
+#include "play.lsg.h"
#include "para.h"
#include "list.h"
-#include "play.cmdline.h"
#include "error.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "version.h"
#include "string.h"
#include "afh.h"
#include "recv.h"
#include "write.h"
-#include "write_common.h"
#include "fd.h"
/**
/** Array of error strings. */
DEFINE_PARA_ERRLIST;
+static struct lls_parse_result *play_lpr;
+
+#define CMD_PTR (lls_cmd(0, play_suite))
+#define OPT_RESULT(_name) \
+ (lls_opt_result(LSG_PLAY_PARA_PLAY_OPT_ ## _name, play_lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+
/**
* Describes a request to change the state of para_play.
*
char *afhi_txt;
};
-/* Activate the afh receiver. */
-extern void afh_recv_init(struct receiver *r);
-#undef AFH_RECEIVER
-/** Initialization code for a receiver struct. */
-#define AFH_RECEIVER {.name = "afh", .init = afh_recv_init},
-/** This expands to the array of all receivers. */
-DEFINE_RECEIVER_ARRAY;
+typedef int (*play_cmd_handler_t)(struct play_task *pt,
+ struct lls_parse_result *lpr);
+struct play_command_info {
+ play_cmd_handler_t handler;
+};
+#define EXPORT_PLAY_CMD_HANDLER(_cmd) \
+ const struct play_command_info lsg_play_cmd_com_ ## _cmd ## _user_data = { \
+ .handler = com_ ## _cmd \
+ };
static int loglevel = LL_WARNING;
char *stat_item_values[NUM_STAT_ITEMS] = {NULL};
-/** Iterate over all files in the playlist. */
-#define FOR_EACH_PLAYLIST_FILE(i) for (i = 0; i < conf.inputs_num; i++)
-static struct play_args_info conf;
-
static struct sched sched = {.max_fileno = 0};
static struct play_task play_task;
-static struct receiver *afh_recv;
-static void check_afh_receiver_or_die(void)
-{
- int i;
+#define AFH_RECV_CMD (lls_cmd(LSG_RECV_CMD_CMD_AFH, recv_cmd_suite))
+#define AFH_RECV ((struct receiver *)lls_user_data(AFH_RECV_CMD))
- FOR_EACH_RECEIVER(i) {
- struct receiver *r = receivers + i;
- if (strcmp(r->name, "afh"))
- continue;
- afh_recv = r;
- return;
- }
- PARA_EMERG_LOG("fatal: afh receiver not found\n");
- exit(EXIT_FAILURE);
+static unsigned *shuffle_map;
+
+static const char *get_playlist_file(unsigned idx)
+{
+ return lls_input(shuffle_map[idx], play_lpr);
}
-__noreturn static void print_help_and_die(void)
+static void handle_help_flags(void)
{
- struct ggo_help help = DEFINE_GGO_HELP(play);
- unsigned flags = conf.detailed_help_given?
- GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
+ char *help;
- ggo_print_help(&help, flags);
- printf("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
- exit(0);
+ if (OPT_GIVEN(DETAILED_HELP))
+ help = lls_long_help(CMD_PTR);
+ else if (OPT_GIVEN(HELP))
+ help = lls_short_help(CMD_PTR);
+ else
+ return;
+ printf("%s\n", help);
+ free(help);
+ exit(EXIT_SUCCESS);
}
static void parse_config_or_die(int argc, char *argv[])
{
- int i, ret;
- char *config_file;
- struct play_cmdline_parser_params params = {
- .override = 0,
- .initialize = 1,
- .check_required = 0,
- .check_ambiguity = 0,
- .print_errors = 1
- };
+ const struct lls_command *cmd = CMD_PTR;
+ int i, ret, cf_argc;
+ char *cf, *errctx, **cf_argv;
+ struct lls_parse_result *cf_lpr, *merged_lpr;
+ unsigned num_kmas;
+ void *map;
+ size_t sz;
- 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();
- if (conf.config_file_given)
- config_file = para_strdup(conf.config_file_arg);
+ ret = lls(lls_parse(argc, argv, cmd, &play_lpr, &errctx));
+ if (ret < 0)
+ goto fail;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
+ version_handle_flag("play", OPT_GIVEN(VERSION));
+ handle_help_flags();
+ if (OPT_GIVEN(CONFIG_FILE))
+ cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
else {
char *home = para_homedir();
- config_file = make_message("%s/.paraslash/play.conf", home);
+ cf = make_message("%s/.paraslash/play.conf", home);
free(home);
}
- ret = file_exists(config_file);
- if (conf.config_file_given && !ret) {
- PARA_EMERG_LOG("can not read config file %s\n", config_file);
- goto err;
- }
- if (ret) {
- params.initialize = 0;
- params.check_required = 1;
- play_cmdline_parser_config_file(config_file, &conf, ¶ms);
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+ if (ret < 0) {
+ if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+ goto free_cf;
+ if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE))
+ goto free_cf;
+ ret = 0;
+ goto free_cf;
}
- for (i = 0; i < conf.key_map_given; i++) {
- char *kma = conf.key_map_arg[i];
+ ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx));
+ para_munmap(map, sz);
+ if (ret < 0)
+ goto free_cf;
+ cf_argc = ret;
+ ret = lls(lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx));
+ lls_free_argv(cf_argv);
+ if (ret < 0)
+ goto free_cf;
+ ret = lls(lls_merge(play_lpr, cf_lpr, cmd, &merged_lpr, &errctx));
+ lls_free_parse_result(cf_lpr, cmd);
+ if (ret < 0)
+ goto free_cf;
+ lls_free_parse_result(play_lpr, cmd);
+ play_lpr = merged_lpr;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
+
+ ret = lls(lls_check_arg_count(play_lpr, 1, INT_MAX, &errctx));
+ if (ret < 0)
+ goto free_cf;
+ num_kmas = OPT_GIVEN(KEY_MAP);
+ for (i = 0; i < num_kmas; i++) {
+ const char *kma = lls_string_val(i, OPT_RESULT(KEY_MAP));
if (*kma && strchr(kma + 1, ':'))
continue;
PARA_EMERG_LOG("invalid key map arg: %s\n", kma);
- goto err;
+ goto free_cf;
}
- free(config_file);
- return;
-err:
- free(config_file);
+ ret = 1;
+free_cf:
+ free(cf);
+ if (ret >= 0)
+ return;
+ lls_free_parse_result(play_lpr, cmd);
+fail:
+ if (errctx)
+ PARA_EMERG_LOG("%s\n", errctx);
+ free(errctx);
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
exit(EXIT_FAILURE);
}
return result;
}
+
static void wipe_receiver_node(struct play_task *pt)
{
PARA_NOTICE_LOG("cleaning up receiver node\n");
btr_remove_node(&pt->rn.btrn);
- afh_recv->close(&pt->rn);
- afh_recv->free_config(pt->rn.conf);
+ AFH_RECV->close(&pt->rn);
+ lls_free_parse_result(pt->rn.lpr, AFH_RECV_CMD);
memset(&pt->rn, 0, sizeof(struct receiver_node));
}
static int eof_cleanup(struct play_task *pt)
{
- struct writer *w = writers + DEFAULT_WRITER;
- const struct filter *decoder = filter_get(pt->fn.filter_num);
+ const struct filter *decoder;
+ const struct writer *w = writer_get(-1); /* default writer */
int ret;
ret = get_playback_error(pt);
task_reap(&pt->wn.task);
w->close(&pt->wn);
btr_remove_node(&pt->wn.btrn);
- w->free_config(pt->wn.conf);
+ lls_free_parse_result(pt->wn.lpr, WRITE_CMD(pt->wn.wid));
memset(&pt->wn, 0, sizeof(struct writer_node));
+ decoder = filter_get(pt->fn.filter_num);
task_reap(&pt->fn.task);
if (decoder->close)
decoder->close(&pt->fn);
return para_random(100) - 50;
}
-static void shuffle(char **base, size_t num)
+static void init_shuffle_map(void)
{
+ unsigned n, num_inputs = lls_num_inputs(play_lpr);
+ shuffle_map = para_malloc(num_inputs * sizeof(unsigned));
+ for (n = 0; n < num_inputs; n++)
+ shuffle_map[n] = n;
+ if (!OPT_GIVEN(RANDOMIZE))
+ return;
srandom(time(NULL));
- qsort(base, num, sizeof(char *), shuffle_compare);
+ qsort(shuffle_map, num_inputs, sizeof(unsigned), shuffle_compare);
}
static struct btr_node *new_recv_btrn(struct receiver_node *rn)
{
return btr_new_node(&(struct btr_node_description)
- EMBRACE(.name = afh_recv->name, .context = rn,
- .handler = afh_recv->execute));
+ EMBRACE(.name = lls_command_name(AFH_RECV_CMD), .context = rn,
+ .handler = AFH_RECV->execute));
}
static int open_new_file(struct play_task *pt)
{
int ret;
- char *tmp, *path = conf.inputs[pt->next_file], *afh_recv_conf[] =
- {"play", "-f", path, "-b", "0", NULL};
+ const char *path = get_playlist_file(pt->next_file);
+ char *tmp = para_strdup(path), *errctx;
+ char *argv[] = {"play", "-f", tmp, "-b", "0", NULL};
PARA_NOTICE_LOG("next file: %s\n", path);
wipe_receiver_node(pt);
pt->start_chunk = 0;
pt->rn.btrn = new_recv_btrn(&pt->rn);
- pt->rn.conf = afh_recv->parse_config(ARRAY_SIZE(afh_recv_conf) - 1,
- afh_recv_conf);
- assert(pt->rn.conf);
- pt->rn.receiver = afh_recv;
- ret = afh_recv->open(&pt->rn);
+ ret = lls(lls_parse(ARRAY_SIZE(argv) - 1, argv, AFH_RECV_CMD,
+ &pt->rn.lpr, &errctx));
+ free(tmp);
+ assert(ret >= 0);
+ pt->rn.receiver = AFH_RECV;
+ ret = AFH_RECV->open(&pt->rn);
if (ret < 0) {
PARA_ERROR_LOG("could not open %s\n", path);
goto fail;
char *tmp, buf[20];
int ret;
const struct filter *decoder;
+ static struct lls_parse_result *filter_lpr, *writer_lpr;
btr_remove_node(&pt->rn.btrn);
if (!pt->rn.receiver || pt->next_file != pt->current_file) {
/* set up decoding filter */
af = audio_format_name(pt->audio_format_num);
tmp = make_message("%sdec", af);
- PARA_INFO_LOG("decoder: %s\n", tmp);
- ret = check_filter_arg(tmp, &pt->fn.conf);
+ ret = filter_setup(tmp, &pt->fn.conf, &filter_lpr);
freep(&tmp);
if (ret < 0)
goto fail;
pt->fn.filter_num = ret;
+ pt->fn.lpr = filter_lpr;
decoder = filter_get(ret);
pt->fn.btrn = btr_new_node(&(struct btr_node_description)
- EMBRACE(.name = decoder->name, .parent = pt->rn.btrn,
- .handler = decoder->execute, .context = &pt->fn));
+ EMBRACE(.name = filter_name(pt->fn.filter_num),
+ .parent = pt->rn.btrn, .handler = decoder->execute,
+ .context = &pt->fn));
if (decoder->open)
decoder->open(&pt->fn);
PARA_INFO_LOG("buffer tree:\n");
btr_log_tree(pt->rn.btrn, LL_INFO);
/* setup default writer */
- pt->wn.conf = check_writer_arg_or_die(NULL, &pt->wn.writer_num);
-
+ pt->wn.wid = check_writer_arg_or_die(NULL, &writer_lpr);
+ pt->wn.lpr = writer_lpr;
/* success, register tasks */
pt->rn.task = task_register(
&(struct task_info) {
- .name = afh_recv->name,
- .pre_select = afh_recv->pre_select,
- .post_select = afh_recv->post_select,
+ .name = lls_command_name(AFH_RECV_CMD),
+ .pre_select = AFH_RECV->pre_select,
+ .post_select = AFH_RECV->post_select,
.context = &pt->rn
}, &sched);
sprintf(buf, "%s decoder", af);
static int next_valid_file(struct play_task *pt)
{
int i, j = pt->current_file;
+ unsigned num_inputs = lls_num_inputs(play_lpr);
- FOR_EACH_PLAYLIST_FILE(i) {
- j = (j + 1) % conf.inputs_num;
+ for (i = 0; i < num_inputs; i++) {
+ j = (j + 1) % num_inputs;
if (!pt->invalid[j])
return j;
}
static int previous_valid_file(struct play_task *pt)
{
int i, j = pt->current_file;
+ unsigned num_inputs = lls_num_inputs(play_lpr);
- FOR_EACH_PLAYLIST_FILE(i) {
+ for (i = 0; i < num_inputs; i++) {
j--;
if (j < 0)
- j = conf.inputs_num - 1;
+ j = num_inputs - 1;
if (!pt->invalid[j])
return j;
}
static const char *default_commands[] = {INTERNAL_KEYMAP_ENTRIES};
#undef KEYMAP_ENTRY
#define NUM_INTERNALLY_MAPPED_KEYS ARRAY_SIZE(default_commands)
-#define NUM_MAPPED_KEYS (NUM_INTERNALLY_MAPPED_KEYS + conf.key_map_given)
+#define NUM_MAPPED_KEYS (NUM_INTERNALLY_MAPPED_KEYS + OPT_GIVEN(KEY_MAP))
#define FOR_EACH_MAPPED_KEY(i) for (i = 0; i < NUM_MAPPED_KEYS; i++)
static inline bool is_internal_key(int key)
get_internal_key_map_idx(key) : get_user_key_map_idx(key);
}
-static inline char *get_user_key_map_arg(int key)
+static inline const char *get_user_key_map_arg(int key)
{
- return conf.key_map_arg[get_user_key_map_idx(key)];
+ return lls_string_val(get_user_key_map_idx(key), OPT_RESULT(KEY_MAP));
}
static inline char *get_internal_key_map_seq(int key)
return result;
}
-#include "play.command_list.h"
-
-typedef int play_command_handler_t(struct play_task *, int, char**);
-static play_command_handler_t PLAY_COMMAND_HANDLERS;
-
-/* defines one command of para_play */
-struct pp_command {
- const char *name;
- play_command_handler_t *handler;
- const char *description;
- const char *usage;
- const char *help;
-};
-
-static struct pp_command pp_cmds[] = {DEFINE_PLAY_CMD_ARRAY};
-#define FOR_EACH_COMMAND(c) for (c = 0; pp_cmds[c].name; c++)
-
-#include "play.completion.h"
static struct i9e_completer pp_completers[];
I9E_DUMMY_COMPLETER(jmp);
result->matches = i9e_complete_commands(ci->word, pp_completers);
}
-static struct i9e_completer pp_completers[] = {PLAY_COMPLETERS {.name = NULL}};
+I9E_DUMMY_COMPLETER(SUPERCOMMAND_UNAVAILABLE);
+static struct i9e_completer pp_completers[] = {
+#define LSG_PLAY_CMD_CMD(_name) {.name = #_name, \
+ .completer = _name ## _completer}
+ LSG_PLAY_CMD_SUBCOMMANDS
+#undef LSG_PLAY_CMD_CMD
+ {.name = NULL}
+};
static void attach_stdout(struct play_task *pt, const char *name)
{
btr_remove_node(&pt->btrn);
}
-static int com_quit(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_quit(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
- if (argc != 1)
- return -E_PLAY_SYNTAX;
pt->rq = CRT_TERM_RQ;
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(quit);
-static int com_help(struct play_task *pt, int argc, char **argv)
+static int com_help(struct play_task *pt, struct lls_parse_result *lpr)
{
- int i;
- char *buf;
+ int i, ret;
+ char *buf, *errctx;
size_t sz;
+ const struct lls_command *cmd;
- if (argc > 2)
- return -E_PLAY_SYNTAX;
- if (argc < 2) {
- if (pt->background)
- FOR_EACH_COMMAND(i) {
- sz = xasprintf(&buf, "%s\t%s\n", pp_cmds[i].name,
- pp_cmds[i].description);
- btr_add_output(buf, sz, pt->btrn);
- }
- else {
- FOR_EACH_MAPPED_KEY(i) {
- bool internal = is_internal_key(i);
- int idx = get_key_map_idx(i);
- char *seq = get_key_map_seq_safe(i);
- char *cmd = get_key_map_cmd(i);
- sz = xasprintf(&buf,
- "%s key #%d: %s -> %s\n",
- internal? "internal" : "user-defined",
- idx, seq, cmd);
+ ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ return ret;
+ }
+ if (lls_num_inputs(lpr) == 0) {
+ if (pt->background) {
+ for (i = 1; (cmd = lls_cmd(i, play_cmd_suite)); i++) {
+ sz = xasprintf(&buf, "%s\t%s\n",
+ lls_command_name(cmd), lls_purpose(cmd));
btr_add_output(buf, sz, pt->btrn);
- free(seq);
- free(cmd);
}
+ return 0;
+ }
+ FOR_EACH_MAPPED_KEY(i) {
+ bool internal = is_internal_key(i);
+ int idx = get_key_map_idx(i);
+ char *seq = get_key_map_seq_safe(i);
+ char *kmc = get_key_map_cmd(i);
+ sz = xasprintf(&buf, "%s key #%d: %s -> %s\n",
+ internal? "internal" : "user-defined",
+ idx, seq, kmc);
+ btr_add_output(buf, sz, pt->btrn);
+ free(seq);
+ free(kmc);
}
return 0;
}
- FOR_EACH_COMMAND(i) {
- if (strcmp(pp_cmds[i].name, argv[1]))
- continue;
- sz = xasprintf(&buf,
- "NAME\n\t%s -- %s\n"
- "SYNOPSIS\n\t%s\n"
- "DESCRIPTION\n%s\n",
- argv[1],
- pp_cmds[i].description,
- pp_cmds[i].usage,
- pp_cmds[i].help
- );
- btr_add_output(buf, sz, pt->btrn);
- return 0;
+ ret = lls(lls_lookup_subcmd(lls_input(0, lpr), play_cmd_suite,
+ &errctx));
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ return ret;
}
- return -E_BAD_PLAY_CMD;
+ cmd = lls_cmd(ret, play_cmd_suite);
+ buf = lls_long_help(cmd);
+ assert(buf);
+ btr_add_output(buf, strlen(buf), pt->btrn);
+ return 0;
}
+EXPORT_PLAY_CMD_HANDLER(help);
-static int com_info(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_info(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
char *buf;
size_t sz;
static char dflt[] = "[no information available]";
- if (argc != 1)
- return -E_PLAY_SYNTAX;
sz = xasprintf(&buf, "playlist_pos: %u\npath: %s\n",
- pt->current_file, conf.inputs[pt->current_file]);
+ pt->current_file, get_playlist_file(pt->current_file));
btr_add_output(buf, sz, pt->btrn);
buf = pt->afhi_txt? pt->afhi_txt : dflt;
btr_add_output_dont_free(buf, strlen(buf), pt->btrn);
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(info);
static void list_file(struct play_task *pt, int num)
{
size_t sz;
sz = xasprintf(&buf, "%s %4d %s\n", num == pt->current_file?
- "*" : " ", num, conf.inputs[num]);
+ "*" : " ", num, get_playlist_file(num));
btr_add_output(buf, sz, pt->btrn);
}
-static int com_tasks(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_tasks(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
static char state;
char *buf;
size_t sz;
- if (argc != 1)
- return -E_PLAY_SYNTAX;
-
buf = get_task_list(&sched);
btr_add_output(buf, strlen(buf), pt->btrn);
state = get_playback_state(pt);
btr_add_output(buf, sz, pt->btrn);
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(tasks);
-static int com_ls(struct play_task *pt, int argc, char **argv)
+static int com_ls(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
- int i, j, ret;
+ int i;
+ unsigned num_inputs = lls_num_inputs(play_lpr);
- if (argc == 1) {
- FOR_EACH_PLAYLIST_FILE(i)
- list_file(pt, i);
- return 0;
- }
- for (j = 1; j < argc; j++) {
- FOR_EACH_PLAYLIST_FILE(i) {
- ret = fnmatch(argv[j], conf.inputs[i], 0);
- if (ret == 0) /* match */
- list_file(pt, i);
- }
- }
+ for (i = 0; i < num_inputs; i++)
+ list_file(pt, i);
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(ls);
-static int com_play(struct play_task *pt, int argc, char **argv)
+static int com_play(struct play_task *pt, struct lls_parse_result *lpr)
{
int32_t x;
int ret;
- char state;
+ char state, *errctx;
- if (argc > 2)
- return -E_PLAY_SYNTAX;
+ ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ return ret;
+ }
state = get_playback_state(pt);
- if (argc == 1) {
+ if (lls_num_inputs(lpr) == 0) {
if (state == 'P')
return 0;
pt->next_file = pt->current_file;
pt->playing = true;
return 0;
}
- ret = para_atoi32(argv[1], &x);
+ ret = para_atoi32(lls_input(0, lpr), &x);
if (ret < 0)
return ret;
- if (x < 0 || x >= conf.inputs_num)
+ if (x < 0 || x >= lls_num_inputs(play_lpr))
return -ERRNO_TO_PARA_ERROR(EINVAL);
kill_stream(pt);
pt->next_file = x;
pt->rq = CRT_FILE_CHANGE;
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(play);
-static int com_pause(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_pause(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
char state;
long unsigned seconds, ss;
- if (argc != 1)
- return -E_PLAY_SYNTAX;
state = get_playback_state(pt);
pt->playing = false;
if (state != 'P')
kill_stream(pt);
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(pause);
-static int com_prev(struct play_task *pt, int argc, __a_unused char **argv)
-
+static int com_prev(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
int ret;
- if (argc != 1)
- return -E_PLAY_SYNTAX;
ret = previous_valid_file(pt);
if (ret < 0)
return ret;
pt->start_chunk = 0;
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(prev);
-static int com_next(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_next(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
int ret;
- if (argc != 1)
- return -E_PLAY_SYNTAX;
ret = next_valid_file(pt);
if (ret < 0)
return ret;
pt->start_chunk = 0;
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(next);
-static int com_fg(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_fg(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
- if (argc != 1)
- return -E_PLAY_SYNTAX;
pt->background = false;
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(fg);
-static int com_bg(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_bg(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
- if (argc != 1)
- return -E_PLAY_SYNTAX;
pt->background = true;
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(bg);
-static int com_jmp(struct play_task *pt, int argc, char **argv)
+static int com_jmp(struct play_task *pt, struct lls_parse_result *lpr)
{
int32_t percent;
int ret;
+ char *errctx;
- if (argc != 2)
- return -E_PLAY_SYNTAX;
- ret = para_atoi32(argv[1], &percent);
+ ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ return ret;
+ }
+ ret = para_atoi32(lls_input(0, lpr), &percent);
if (ret < 0)
return ret;
if (percent < 0 || percent > 100)
return -ERRNO_TO_PARA_ERROR(EINVAL);
if (percent == 100)
- return com_next(pt, 1, (char *[]){"next", NULL});
+ return com_next(pt, NULL);
if (pt->playing && !pt->fn.btrn)
return 0;
pt->start_chunk = percent * pt->num_chunks / 100;
kill_stream(pt);
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(jmp);
-static int com_ff(struct play_task *pt, int argc, char **argv)
+static int com_ff(struct play_task *pt, struct lls_parse_result *lpr)
{
int32_t seconds;
+ char *errctx;
int ret;
- if (argc != 2)
- return -E_PLAY_SYNTAX;
- ret = para_atoi32(argv[1], &seconds);
+ ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ return ret;
+ }
+ ret = para_atoi32(lls_input(0, lpr), &seconds);
if (ret < 0)
return ret;
if (pt->playing && !pt->fn.btrn)
kill_stream(pt);
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(ff);
static int run_command(char *line, struct play_task *pt)
{
- int i, ret, argc;
+ int ret, argc;
char **argv = NULL;
+ char *errctx = NULL;
+ const struct play_command_info *pci;
+ struct lls_parse_result *lpr;
+ const struct lls_command *cmd;
attach_stdout(pt, __FUNCTION__);
ret = create_argv(line, " ", &argv);
- if (ret < 0) {
- PARA_ERROR_LOG("parse error: %s\n", para_strerror(-ret));
- return 0;
- }
+ if (ret < 0)
+ goto out;
if (ret == 0)
goto out;
argc = ret;
- FOR_EACH_COMMAND(i) {
- if (strcmp(pp_cmds[i].name, argv[0]))
- continue;
- ret = pp_cmds[i].handler(pt, argc, argv);
- if (ret < 0)
- PARA_WARNING_LOG("%s: %s\n", pt->background?
- "" : argv[0], para_strerror(-ret));
- ret = 1;
+ ret = lls(lls_lookup_subcmd(argv[0], play_cmd_suite, &errctx));
+ if (ret < 0)
goto out;
- }
- PARA_WARNING_LOG("invalid command: %s\n", argv[0]);
- ret = 0;
+ cmd = lls_cmd(ret, play_cmd_suite);
+ ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+ if (ret < 0)
+ goto out;
+ pci = lls_user_data(cmd);
+ ret = pci->handler(pt, lpr);
+ lls_free_parse_result(lpr, cmd);
out:
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
free_argv(argv);
return ret;
}
struct sigaction act;
PARA_NOTICE_LOG("\n%s\n", version_text("play"));
- if (conf.history_file_given)
- history_file = para_strdup(conf.history_file_arg);
+ if (OPT_GIVEN(HISTORY_FILE))
+ history_file = para_strdup(OPT_STRING_VAL(HISTORY_FILE));
else {
char *home = para_homedir();
history_file = make_message("%s/.paraslash/play.history",
length? (seconds * 100 + length / 2) / length : 0,
length / 60,
length % 60,
- conf.inputs[pt->current_file]
+ get_playlist_file(pt->current_file)
);
}
{
int ret;
struct play_task *pt = &play_task;
+ unsigned num_inputs;
/* needed this early to make help work */
recv_init();
- filter_init();
- writer_init();
sched.default_timeout.tv_sec = 5;
-
parse_config_or_die(argc, argv);
- if (conf.inputs_num == 0)
- print_help_and_die();
- check_afh_receiver_or_die();
-
+ AFH_RECV->init();
session_open(pt);
- if (conf.randomize_given)
- shuffle(conf.inputs, conf.inputs_num);
- pt->invalid = para_calloc(sizeof(*pt->invalid) * conf.inputs_num);
+ num_inputs = lls_num_inputs(play_lpr);
+ init_shuffle_map();
+ pt->invalid = para_calloc(sizeof(*pt->invalid) * num_inputs);
pt->rq = CRT_FILE_CHANGE;
- pt->current_file = conf.inputs_num - 1;
+ pt->current_file = num_inputs - 1;
pt->playing = true;
pt->task = task_register(&(struct task_info){
.name = "play",
+++ /dev/null
-BN: play
-SF: play.c
-SN: list of commands
----
-N: help
-D: Display command list or help for given command.
-U: help [command]
-H: This command acts differently depending on whether it is executed in command
-H: mode or in insert mode. In command mode, the list of keybindings is printed.
-H: In insert mode, if no command is given, the list of commands is shown.
-H: Otherwise, the help for the given command is printed.
----
-N: next
-D: Load next file.
-U: next
-H: Closes the current file and loads the next file of the playlist.
----
-N: prev
-D: Load previous file.
-U: prev
-H: Closes the current file and loads the previous file of the playlist.
----
-N: fg
-D: Enter command mode.
-U: fg
-H: In this mode, file name and play time are displayed. Hit CTRL+C to switch to
-H: input mode.
----
-N: bg
-D: Enter input mode.
-U: bg
-H: Only useful if called in command mode via a key binding. The default key
-H: bindings map this command to the colon key, so pressing : in command mode
-H: activates insert mode.
----
-N: jmp
-D: Jump to position in current file.
-U: jmp <percent>
-H: The <percent> argument should be an integer between 0 and 100.
----
-N: ff
-D: Jump forwards or backwards.
-U: ff <seconds>
-H: Negative values mean to jmp backwards the given amount of seconds.
----
-N: ls
-D: List playlist.
-U: ls
-H: This prints all paths of the playlist. The currently active file is
-H: marked with an asterisk.
----
-N: info
-D: Print information about the current file.
-U: info
-H: This is the audio file selector info.
----
-N: play
-D: Start or resume playing.
-U: play [<num>]
-H: Without <num>, starts playing at the current position. Otherwise, the
-H: corresponding file is loaded and playback is started.
----
-N: pause
-D: Stop playing.
-U: pause
-H: When paused, it is still possible to jump around in the file via the jmp and ff
-H: comands.
----
-N: tasks
-D: Print list of active tasks.
-U: tasks
-H: Mainly useful for debugging.
----
-N: quit
-D: Exit para_play.
-U: quit
-H: Pressing CTRL+D causes EOF on stdin which also exits para_play.
#include <regex.h>
#include <osl.h>
+#include <lopsub.h>
#include "para.h"
#include "error.h"
/** \file prebuffer_filter.c Paraslash's prebuffering filter. */
#include <regex.h>
+#include <lopsub.h>
#include "para.h"
-#include "prebuffer_filter.cmdline.h"
+#include "filter_cmd.lsg.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
/** Data specific to the prebuffer filter. */
struct private_prebuffer_data {
- /** The configuration data for this instance of the filter. */
- struct prebuffer_filter_args_info *conf;
/** Number of bytes prebuffered or -1 if no longer prebuffering. */
int prebuffered;
/** End of prebuffering period. */
struct btr_node *btrn = fn->btrn;
size_t iqs = btr_get_input_queue_size(btrn);
struct private_prebuffer_data *ppd = fn->private_data;
- struct prebuffer_filter_args_info *conf = ppd->conf;
struct timeval diff;
if (iqs == 0)
return;
if (ppd->barrier.tv_sec == 0) {
+ uint32_t duration = FILTER_CMD_OPT_UINT32_VAL(PREBUFFER,
+ DURATION, fn->lpr);
struct timeval tv;
- PARA_INFO_LOG("prebuffer period %dms\n",
- conf->duration_arg);
- ms2tv(conf->duration_arg, &tv);
+ PARA_INFO_LOG("prebuffer period %" PRIu32 "ms\n", duration);
+ ms2tv(duration, &tv);
tv_add(&tv, now, &ppd->barrier);
}
if (tv_diff(&ppd->barrier, now, &diff) < 0)
struct btr_node *btrn = fn->btrn;
size_t iqs = btr_get_input_queue_size(btrn);
struct private_prebuffer_data *ppd = fn->private_data;
- struct prebuffer_filter_args_info *conf = ppd->conf;
+ uint32_t size = FILTER_CMD_OPT_UINT32_VAL(PREBUFFER, SIZE, fn->lpr);
if (ppd->barrier.tv_sec == 0)
return 0;
if (tv_diff(now, &ppd->barrier, NULL) < 0)
return 0;
- if (iqs < conf->size_arg)
+ if (iqs < size)
return 0;
btr_splice_out_node(&fn->btrn);
return -E_PREBUFFER_SUCCESS;
}
-static int prebuffer_parse_config(int argc, char **argv, void **config)
-{
- struct prebuffer_filter_args_info *conf = para_calloc(sizeof(*conf));
- int ret;
-
- prebuffer_filter_cmdline_parser(argc, argv, conf);
- ret = -ERRNO_TO_PARA_ERROR(EINVAL);
- if (conf->duration_arg < 0)
- goto err;
- if (conf->size_arg < 0)
- goto err;
- PARA_NOTICE_LOG("prebuffering %ims, %i bytes\n", conf->duration_arg,
- conf->size_arg);
- *config = conf;
- return 1;
-err:
- free(conf);
- return ret;
-}
-
static void prebuffer_open(struct filter_node *fn)
{
struct private_prebuffer_data *ppd = para_calloc(sizeof(*ppd));
-
- ppd->conf = fn->conf;
fn->private_data = ppd;
}
-static void prebuffer_free_config(void *conf)
-{
- prebuffer_filter_cmdline_parser_free(conf);
-}
-
-/**
- * The init function of the prebuffer filter.
- *
- * \param f Pointer to the struct to initialize.
- */
-void prebuffer_filter_init(struct filter *f)
-{
- struct prebuffer_filter_args_info dummy;
-
- prebuffer_filter_cmdline_parser_init(&dummy);
- f->open = prebuffer_open;
- f->close = prebuffer_close;
- f->parse_config = prebuffer_parse_config;
- f->free_config = prebuffer_free_config;
- f->pre_select = prebuffer_pre_select;
- f->post_select = prebuffer_post_select;
- f->help = (struct ggo_help)DEFINE_GGO_HELP(prebuffer_filter);
-}
+const struct filter lsg_filter_cmd_com_prebuffer_user_data = {
+ .open = prebuffer_open,
+ .close = prebuffer_close,
+ .pre_select = prebuffer_pre_select,
+ .post_select = prebuffer_post_select,
+};
#include <regex.h>
#include <sys/types.h>
+#include <inttypes.h>
+#include <lopsub.h>
+#include "recv_cmd.lsg.h"
+#include "recv.lsg.h"
#include "para.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "recv.h"
-#include "recv.cmdline.h"
#include "fd.h"
#include "string.h"
#include "error.h"
/** Array of error strings. */
DEFINE_PARA_ERRLIST;
-extern void afh_recv_init(struct receiver *r);
-#undef AFH_RECEIVER
-#define AFH_RECEIVER {.name = "afh", .init = afh_recv_init},
-DEFINE_RECEIVER_ARRAY;
-
-/** The gengetopt args info struct. */
-static struct recv_args_info conf;
+#define CMD_PTR (lls_cmd(0, recv_suite))
+#define OPT_RESULT(_name, _lpr) \
+ (lls_opt_result(LSG_RECV_PARA_RECV_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name, _lpr) (lls_opt_given(OPT_RESULT(_name, _lpr)))
+#define OPT_UINT32_VAL(_name, _lpr) (lls_uint32_val(0, OPT_RESULT(_name, _lpr)))
+#define OPT_STRING_VAL(_name, _lpr) (lls_string_val(0, OPT_RESULT(_name, _lpr)))
static int loglevel;
/** Always log to stderr. */
INIT_STDERR_LOGGING(loglevel);
-__noreturn static void print_help_and_die(void)
+static void handle_help_flag(struct lls_parse_result *lpr)
{
- struct ggo_help h = DEFINE_GGO_HELP(recv);
- bool d = conf.detailed_help_given;
-
- 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);
+ char *help;
+
+ if (OPT_GIVEN(DETAILED_HELP, lpr))
+ help = lls_long_help(CMD_PTR);
+ else if (OPT_GIVEN(HELP, lpr))
+ help = lls_short_help(CMD_PTR);
+ else
+ return;
+ printf("%s\n", help);
+ free(help);
+ print_receiver_helps(OPT_GIVEN(DETAILED_HELP, lpr));
+ exit(EXIT_SUCCESS);
}
/**
*/
int main(int argc, char *argv[])
{
- int ret, r_opened = 0, receiver_num;
- struct receiver *r = NULL;
+ int ret;
+ const struct receiver *r = NULL;
struct receiver_node rn;
struct stdout_task sot = {.btrn = NULL};
static struct sched s;
struct task_info ti;
+ const struct lls_command *cmd;
+ struct lls_parse_result *lpr; /* command line */
+ struct lls_parse_result *receiver_lpr; /* receiver specific options */
+ char *errctx;
- recv_cmdline_parser(argc, argv, &conf);
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
- version_handle_flag("recv", conf.version_given);
+ ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
+ if (ret < 0)
+ goto out;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL, lpr);
+ version_handle_flag("recv", OPT_GIVEN(VERSION, lpr));
+ handle_help_flag(lpr);
recv_init();
- if (conf.help_given || conf.detailed_help_given)
- print_help_and_die();
-
memset(&rn, 0, sizeof(struct receiver_node));
- rn.conf = check_receiver_arg(conf.receiver_arg, &receiver_num);
- if (!rn.conf) {
- PARA_EMERG_LOG("invalid receiver specifier\n");
- ret = -E_RECV_SYNTAX;
- goto out;
- }
- r = &receivers[receiver_num];
+ ret = check_receiver_arg(OPT_STRING_VAL(RECEIVER, lpr), &receiver_lpr);
+ if (ret < 0)
+ goto free_lpr;
+ cmd = lls_cmd(ret, recv_cmd_suite);
+ r = lls_user_data(cmd);
rn.receiver = r;
+ rn.lpr = receiver_lpr;
rn.btrn = btr_new_node(&(struct btr_node_description)
- EMBRACE(.name = r->name));
+ EMBRACE(.name = lls_command_name(cmd)));
ret = r->open(&rn);
if (ret < 0)
- goto out;
- r_opened = 1;
-
+ goto remove_btrn;
sot.btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.parent = rn.btrn, .name = "stdout"));
stdout_task_register(&sot, &s);
- ti.name = r->name;
+ ti.name = lls_command_name(cmd);
ti.pre_select = r->pre_select;
ti.post_select = r->post_select;
ti.context = &rn;
s.default_timeout.tv_usec = 0;
ret = schedule(&s);
sched_shutdown(&s);
-out:
- if (r_opened)
- r->close(&rn);
- btr_remove_node(&rn.btrn);
+ r->close(&rn);
btr_remove_node(&sot.btrn);
- if (rn.conf)
- r->free_config(rn.conf);
-
- if (ret < 0)
+remove_btrn:
+ btr_remove_node(&rn.btrn);
+ lls_free_parse_result(receiver_lpr, cmd);
+free_lpr:
+ lls_free_parse_result(lpr, CMD_PTR);
+out:
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ }
return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
}
*/
struct receiver_node {
/** Points to the corresponding receiver. */
- struct receiver *receiver;
+ const struct receiver *receiver;
/** Receiver-specific data. */
void *private_data;
- /** Pointer to the configuration data for this instance. */
- void *conf;
+ /** The parsed command line options for this instance. */
+ struct lls_parse_result *lpr;
/** The task associated with this instance. */
struct task *task;
/** The receiver node is always the root of the buffer tree. */
*/
struct receiver {
/**
- * The name of the receiver.
- */
- const char *name;
- /**
- * The receiver init function.
+ * The optional receiver init function.
*
- * It must fill in all other function pointers and is assumed to succeed.
+ * Performs any initialization needed before the receiver can be opened.
*
* \sa http_recv_init udp_recv_init.
*/
- void (*init)(struct receiver *r);
- /**
- * 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.
- */
- void *(*parse_config)(int argc, char **argv);
- /**
- * Deallocate the configuration structure of a receiver node.
- *
- * This calls the receiver-specific cleanup function generated by
- * gengetopt.
- */
- void (*free_config)(void *conf);
+ void (*init)(void);
/**
* Open one instance of the receiver.
*
*/
int (*post_select)(struct sched *s, void *context);
- /** The two help texts of this receiver. */
- struct ggo_help help;
/**
* Answer a buffer tree query.
*
btr_command_handler execute;
};
-/** Define an array of all available receivers. */
-#define DEFINE_RECEIVER_ARRAY struct receiver receivers[] = { \
- HTTP_RECEIVER \
- DCCP_RECEIVER \
- UDP_RECEIVER \
- AFH_RECEIVER \
- {.name = NULL}};
+#define RECV_CMD(_num) (lls_cmd(_num, recv_cmd_suite))
+
+#define RECV_CMD_OPT_RESULT(_recv, _opt, _lpr) \
+ (lls_opt_result(LSG_RECV_CMD_ ## _recv ## _OPT_ ## _opt, _lpr))
+#define RECV_CMD_OPT_GIVEN(_recv, _opt, _lpr) \
+ (lls_opt_given(RECV_CMD_OPT_RESULT(_recv, _opt, _lpr)))
+#define RECV_CMD_OPT_STRING_VAL(_recv, _opt, _lpr) \
+ (lls_string_val(0, RECV_CMD_OPT_RESULT(_recv, _opt, _lpr)))
+#define RECV_CMD_OPT_UINT32_VAL(_recv, _opt, _lpr) \
+ (lls_uint32_val(0, RECV_CMD_OPT_RESULT(_recv, _opt, _lpr)))
+#define RECV_CMD_OPT_INT32_VAL(_recv, _opt, _lpr) \
+ (lls_int32_val(0, RECV_CMD_OPT_RESULT(_recv, _opt, _lpr)))
/** Iterate over all available receivers. */
-#define FOR_EACH_RECEIVER(i) for (i = 0; receivers[i].name; i++)
+#define FOR_EACH_RECEIVER(i) for (i = 1; lls_cmd(i, recv_cmd_suite); i++)
void recv_init(void);
-void *check_receiver_arg(char *ra, int *receiver_num);
-void print_receiver_helps(unsigned flags);
+int check_receiver_arg(const char *ra, struct lls_parse_result **lprp);
+void print_receiver_helps(bool detailed);
int generic_recv_pre_select(struct sched *s, struct receiver_node *rn);
-
-/** \cond receiver */
-extern void http_recv_init(struct receiver *r);
-#define HTTP_RECEIVER {.name = "http", .init = http_recv_init},
-extern void dccp_recv_init(struct receiver *r);
-#define DCCP_RECEIVER {.name = "dccp", .init = dccp_recv_init},
-extern void udp_recv_init(struct receiver *r);
-#define UDP_RECEIVER {.name = "udp", .init = udp_recv_init},
-#define AFH_RECEIVER /* not active by default */
-
-extern struct receiver receivers[];
-/** \endcond receiver */
-
/** \file recv_common.c common functions of para_recv and para_audiod */
#include <regex.h>
+#include <inttypes.h>
+#include <lopsub.h>
+#include "recv_cmd.lsg.h"
#include "para.h"
+#include "error.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "recv.h"
#include "string.h"
{
int i;
- FOR_EACH_RECEIVER(i)
- receivers[i].init(&receivers[i]);
-}
-
-static void *parse_receiver_args(int receiver_num, char *options)
-{
- struct receiver *r = &receivers[receiver_num];
- char **argv;
- int argc;
- void *conf;
-
- if (options) {
- argc = create_shifted_argv(options, " \t", &argv);
- if (argc < 0)
- return NULL;
- } else {
- argc = 1;
- argv = para_malloc(2 * sizeof(char*));
- argv[1] = NULL;
+ FOR_EACH_RECEIVER(i) {
+ const struct lls_command *cmd = RECV_CMD(i);
+ const struct receiver *r = lls_user_data(cmd);
+ if (r && r->init)
+ r->init();
}
- argv[0] = make_message("%s_recv", r->name);
- conf = r->parse_config(argc, argv);
- free_argv(argv);
- return conf;
}
/**
* Check if the given string is a valid receiver specifier.
*
- * \param \ra string of the form receiver_name:options
- * \param receiver_num contains the number of the receiver upon success
+ * \param \ra string of the form receiver_name [options...]
+ * \param lprp Filled in on success, undefined else.
*
* This function checks whether \a ra starts with the name of a receiver,
* optionally followed by options for that receiver. If a valid receiver name
* was found the remaining part of \a ra is passed to the receiver's config
* parser.
*
- * \return On success, a pointer to the receiver-specific gengetopt args info
- * struct is returned and \a receiver_num contains the number of the receiver.
- * On errors, the function returns \p NULL.
+ * If a NULL pointer or an empty string is passed as the first argument, the
+ * hhtp receiver with no options is assumed.
+ *
+ * \return On success the number of the receiver is returned. On errors, the
+ * function calls exit(EXIT_FAILURE).
*/
-void *check_receiver_arg(char *ra, int *receiver_num)
+int check_receiver_arg(const char *ra, struct lls_parse_result **lprp)
{
- int j;
+ int ret, argc, receiver_num;
+ char *errctx = NULL, **argv;
+ const struct lls_command *cmd;
- PARA_DEBUG_LOG("checking %s\n", ra);
- for (j = 0; receivers[j].name; j++) {
- const char *name = receivers[j].name;
- size_t len = strlen(name);
- char c;
- if (strlen(ra) < len)
- continue;
- if (strncmp(name, ra, len))
- continue;
- c = ra[len];
- if (c && c != ' ')
- continue;
- if (c && !receivers[j].parse_config)
- return NULL;
- *receiver_num = j;
- return parse_receiver_args(j, c? ra + len + 1: NULL);
+ *lprp = NULL;
+ if (!ra || !*ra) {
+ argc = 1;
+ argv = para_malloc(2 * sizeof(char*));
+ argv[0] = para_strdup("http");
+ argv[1] = NULL;
+ } else {
+ ret = create_argv(ra, " \t\n", &argv);
+ if (ret < 0) {
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+ exit(EXIT_FAILURE);
+ }
+ argc = ret;
}
- PARA_ERROR_LOG("receiver not found\n");
- return NULL;
+ ret = lls(lls_lookup_subcmd(argv[0], recv_cmd_suite, &errctx));
+ if (ret < 0) {
+ PARA_EMERG_LOG("%s: %s\n", errctx? errctx : argv[0],
+ para_strerror(-ret));
+ exit(EXIT_FAILURE);
+ }
+ receiver_num = ret;
+ cmd = RECV_CMD(receiver_num);
+ ret = lls(lls_parse(argc, argv, cmd, lprp, &errctx));
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+ exit(EXIT_FAILURE);
+ }
+ ret = receiver_num;
+ free_argv(argv);
+ return ret;
}
/**
* Print out the help texts to all receivers.
*
- * \param flags Passed to \ref ggo_print_help().
+ * \param detailed Whether to print the short or the detailed help.
*/
-void print_receiver_helps(unsigned flags)
+void print_receiver_helps(bool detailed)
{
int i;
- printf_or_die("\nAvailable receivers: ");
- FOR_EACH_RECEIVER(i)
- printf_or_die("%s%s", i? " " : "", receivers[i].name);
- printf_or_die("\n");
+ printf("\nAvailable receivers: ");
+ FOR_EACH_RECEIVER(i) {
+ const struct lls_command *cmd = RECV_CMD(i);
+ printf("%s%s", i? " " : "", lls_command_name(cmd));
+ }
+ printf("\n\n");
FOR_EACH_RECEIVER(i) {
- struct receiver *r = receivers + i;
- if (!r->help.short_help)
+ const struct lls_command *cmd = RECV_CMD(i);
+ char *help = detailed? lls_long_help(cmd) : lls_short_help(cmd);
+ if (!help)
continue;
- printf_or_die("\n%s: %s", r->name,
- r->help.purpose);
- ggo_print_help(&r->help, flags);
+ printf("%s\n", help);
+ free(help);
}
}
#include <regex.h>
#include <samplerate.h>
+#include <lopsub.h>
-#include "resample_filter.cmdline.h"
+#include "filter_cmd.lsg.h"
#include "para.h"
#include "error.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
#include "check_wav.h"
+#define U32_OPTVAL(_opt, _lpr) (FILTER_CMD_OPT_UINT32_VAL(RESAMPLE, _opt, _lpr))
+#define OPT_GIVEN(_opt, _lpr) (FILTER_CMD_OPT_GIVEN(RESAMPLE, _opt, _lpr))
+
+/* effective values, may differ from config arg */
struct resample_context {
- int channels;
+ uint32_t channels;
int source_sample_rate;
float ratio;
SRC_STATE *src_state;
{
struct filter_node *fn = btr_context(btrn);
struct resample_context *ctx = fn->private_data;
- struct resample_filter_args_info *conf = fn->conf;
-
- return decoder_execute(cmd, conf->dest_sample_rate_arg, ctx->channels,
- result);
+ uint32_t dsr = U32_OPTVAL(DEST_SAMPLE_RATE, fn->lpr);
+ return decoder_execute(cmd, dsr, ctx->channels, result);
}
static void resample_close(struct filter_node *fn)
static void resample_open(struct filter_node *fn)
{
struct resample_context *ctx = para_calloc(sizeof(*ctx));
- struct resample_filter_args_info *conf = fn->conf;
struct btr_node *btrn = fn->btrn;
struct wav_params wp;
fn->private_data = ctx;
fn->min_iqs = 2;
- COPY_WAV_PARMS(&wp, conf);
+ LLS_COPY_WAV_PARMS(&wp, LSG_FILTER_CMD_RESAMPLE, fn->lpr);
ctx->cwc = check_wav_init(btr_parent(btrn), btrn, &wp, NULL);
btr_log_tree(btr_parent(btr_parent(btrn)), LL_INFO);
}
{
int ret;
struct resample_context *ctx = fn->private_data;
- struct resample_filter_args_info *conf = fn->conf;
struct btr_node *btrn = fn->btrn;
+ struct lls_parse_result *lpr = fn->lpr;
- ctx->channels = conf->channels_arg;
- if (!conf->channels_given) {
+ ctx->channels = U32_OPTVAL(CHANNELS, lpr);
+ if (!OPT_GIVEN(CHANNELS, lpr)) {
ret = get_btr_val("channels", btrn);
if (ret >= 0)
ctx->channels = ret;
}
-
- ctx->source_sample_rate = conf->sample_rate_arg;
- if (!conf->sample_rate_given) {
+ ctx->source_sample_rate = U32_OPTVAL(SAMPLE_RATE, lpr);
+ if (!OPT_GIVEN(SAMPLE_RATE, lpr)) {
ret = get_btr_val("sample_rate", btrn);
if (ret >= 0)
ctx->source_sample_rate = ret;
/* reject all sample formats except 16 bit signed, little endian */
ret = get_btr_val("sample_format", btrn);
if (ret >= 0 && ret != SF_S16_LE) {
- const char *sample_formats[] = {SAMPLE_FORMATS};
+ const char * const sample_formats[] = {SAMPLE_FORMATS};
PARA_ERROR_LOG("unsupported sample format: %s\n",
sample_formats[ret]);
return -ERRNO_TO_PARA_ERROR(EINVAL);
}
- ctx->ratio = (float)conf->dest_sample_rate_arg / ctx->source_sample_rate;
+ ctx->ratio = U32_OPTVAL(DEST_SAMPLE_RATE, lpr)
+ / (float)ctx->source_sample_rate;
return 1;
}
static int resample_init(struct filter_node *fn)
{
- int ret, converter;
+ int ret;
+ const uint32_t trafo[] = {
+ [RCT_BEST] = SRC_SINC_BEST_QUALITY,
+ [RCT_MEDIUM] = SRC_SINC_MEDIUM_QUALITY,
+ [RCT_FASTEST] = SRC_SINC_FASTEST,
+ [RCT_ZERO_ORDER_HOLD] = SRC_ZERO_ORDER_HOLD,
+ [RCT_LINEAR] = SRC_LINEAR
+ };
struct resample_context *ctx = fn->private_data;
- struct resample_filter_args_info *conf = fn->conf;
+ const struct lls_option *o_c = FILTER_CMD_OPT(RESAMPLE, CONVERTER);
+ uint32_t converter = U32_OPTVAL(CONVERTER, fn->lpr);
+ PARA_INFO_LOG("converter type: %s\n",
+ lls_enum_string_val(converter, o_c));
ret = resample_set_params(fn);
if (ret < 0)
return ret;
- switch (conf->converter_arg) {
- case converter_arg_best:
- converter = SRC_SINC_BEST_QUALITY;
- break;
- case converter_arg_medium:
- converter = SRC_SINC_MEDIUM_QUALITY;
- break;
- case converter_arg_fastest:
- converter = SRC_SINC_FASTEST;
- break;
- case converter_arg_zero_order_hold:
- converter = SRC_ZERO_ORDER_HOLD;
- break;
- case converter_arg_linear:
- converter = SRC_LINEAR;
- break;
- default:
- assert(0);
- }
- ctx->src_state = src_new(converter, conf->channels_arg, &ret);
+ ctx->src_state = src_new(trafo[converter],
+ U32_OPTVAL(CHANNELS, fn->lpr), &ret);
if (!ctx->src_state) {
PARA_ERROR_LOG("%s\n", src_strerror(ret));
return -E_LIBSAMPLERATE;
int ret;
struct filter_node *fn = context;
struct resample_context *ctx = fn->private_data;
- struct resample_filter_args_info *conf = fn->conf;
struct btr_node *btrn = fn->btrn;
int16_t *in, *out;
size_t in_bytes, num_frames;
if (ret <= 0)
goto out;
}
- if (ctx->source_sample_rate == conf->dest_sample_rate_arg) {
+ if (ctx->source_sample_rate == U32_OPTVAL(DEST_SAMPLE_RATE, fn->lpr)) {
/*
* No resampling necessary. We do not splice ourselves out
* though, since our children might want to ask us through the
return ret;
}
-static int resample_parse_config(int argc, char **argv, void **config)
+static void *resample_setup(const struct lls_parse_result *lpr)
{
- int ret, val, given;
- struct resample_filter_args_info *conf = para_calloc(sizeof(*conf));
-
- resample_filter_cmdline_parser(argc, argv, conf);
+ int given;
+ uint32_t u32;
/* sanity checks */
- ret = -ERRNO_TO_PARA_ERROR(EINVAL);
- val = conf->channels_arg;
- given = conf->channels_given;
- if (val < 0 || (val == 0 && given))
- goto err;
- val = conf->sample_rate_arg;
- given = conf->sample_rate_given;
- if (val < 0 || (val == 0 && given))
- goto err;
- val = conf->dest_sample_rate_arg;
- given = conf->dest_sample_rate_given;
- if (val < 0 || (val == 0 && given))
- goto err;
- *config = conf;
- return 1;
-err:
- free(conf);
- return ret;
+ u32 = U32_OPTVAL(CHANNELS, lpr);
+ given = OPT_GIVEN(CHANNELS, lpr);
+ if (u32 == 0 && given) {
+ PARA_EMERG_LOG("fatal: zero channels?!\n");
+ exit(EXIT_FAILURE);
+ }
+ u32 = U32_OPTVAL(SAMPLE_RATE, lpr);
+ given = OPT_GIVEN(SAMPLE_RATE, lpr);
+ if (u32 == 0 && given) {
+ PARA_EMERG_LOG("fatal: input sample rate can not be 0\n");
+ exit(EXIT_FAILURE);
+ }
+ u32 = U32_OPTVAL(DEST_SAMPLE_RATE, lpr);
+ given = OPT_GIVEN(DEST_SAMPLE_RATE, lpr);
+ if (u32 == 0 && given) {
+ PARA_EMERG_LOG("fatal: destination sample rate can not be 0\n");
+ exit(EXIT_FAILURE);
+ }
+ return NULL;
}
-static void resample_free_config(void *conf)
+static void resample_teardown(__a_unused const struct lls_parse_result *lpr,
+ void *conf)
{
- if (!conf)
- return;
- resample_filter_cmdline_parser_free(conf);
free(conf);
}
-/**
- * The init function of the resample filter.
- *
- * \param f Structure to initialize.
- */
-void resample_filter_init(struct filter *f)
-{
- struct resample_filter_args_info dummy;
-
- resample_filter_cmdline_parser_init(&dummy);
- f->close = resample_close;
- f->open = resample_open;
- f->pre_select = resample_pre_select;
- f->post_select = resample_post_select;
- f->parse_config = resample_parse_config;
- f->free_config = resample_free_config;
- f->execute = resample_execute;
- f->help = (struct ggo_help)DEFINE_GGO_HELP(resample_filter);
-}
+const struct filter lsg_filter_cmd_com_resample_user_data = {
+ .setup = resample_setup,
+ .open = resample_open,
+ .pre_select = resample_pre_select,
+ .post_select = resample_post_select,
+ .close = resample_close,
+ .teardown = resample_teardown,
+ .execute = resample_execute
+};
/** \file score.c Scoring functions to determine the audio file streaming order. */
#include <regex.h>
#include <osl.h>
+#include <lopsub.h>
#include "para.h"
#include "error.h"
void shutdown_client(struct sender_client *sc, struct sender_status *ss);
void shutdown_clients(struct sender_status *ss);
-void init_sender_status(struct sender_status *ss, char **access_arg, int num_access_args,
- int port, int max_clients, int default_deny);
+void init_sender_status(struct sender_status *ss,
+ const struct lls_opt_result *acl_opt_result, int port,
+ int max_clients, int default_deny);
char *generic_sender_status(struct sender_status *ss, const char *name);
-
void generic_com_allow(struct sender_command_data *scd,
struct sender_status *ss);
void generic_com_deny(struct sender_command_data *scd,
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
#include "para.h"
#include "error.h"
* Initialize a struct sender status.
*
* \param ss The struct to initialize.
- * \param access_arg The array of access arguments given at the command line.
- * \param num_access_args The number of elements in \a access_arg.
+ * \param acl_opt_result Contains array of --{http|dccp}-access arguments.
* \param port The tcp or dccp port to listen on.
* \param max_clients The maximal number of simultaneous connections.
* \param default_deny Whether a blacklist should be used for access control.
*/
-void init_sender_status(struct sender_status *ss, char **access_arg,
- int num_access_args, int port, int max_clients, int default_deny)
+void init_sender_status(struct sender_status *ss,
+ const struct lls_opt_result *acl_opt_result, int port,
+ int max_clients, int default_deny)
{
+ int i;
+
ss->listen_fd = -1;
INIT_LIST_HEAD(&ss->client_list);
ss->port = port;
- acl_init(&ss->acl, access_arg, num_access_args);
+
+ /* Initialize an access control list */
+ INIT_LIST_HEAD(&ss->acl);
+ for (i = 0; i < lls_opt_given(acl_opt_result); i++) {
+ const char *arg = lls_string_val(i, acl_opt_result);
+ char addr[16];
+ int mask;
+ if (!parse_cidr(arg, addr, sizeof(addr), &mask))
+ PARA_WARNING_LOG("ACL syntax error: %s, ignoring\n",
+ arg);
+ else
+ acl_add_entry(&ss->acl, addr, mask);
+ }
ss->num_clients = 0;
ss->max_clients = max_clients;
ss->default_deny = default_deny;
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
+#include "server.lsg.h"
#include "para.h"
#include "error.h"
#include "crypt.h"
-#include "server.cmdline.h"
#include "afh.h"
#include "string.h"
#include "afs.h"
#include "signal.h"
#include "user_list.h"
#include "color.h"
-#include "ggo.h"
#include "version.h"
/** Array of error strings. */
struct misc_meta_data *mmd;
/**
- * The configuration of para_server
+ * The active value for all config options of para_server.
*
- * It also contains the options for the audio file selector, audio format
- * handler and all supported senders.
+ * It is computed by merging the parse result of the command line options with
+ * the parse result of the config file.
*/
-struct server_args_info conf;
+struct lls_parse_result *server_lpr = NULL;
+
+/* Command line options (no config file options). Used in handle_sighup(). */
+static struct lls_parse_result *cmdline_lpr;
/** A random value used in child context for authentication. */
uint32_t afs_socket_cookie;
/** The mutex protecting the shared memory area containing the mmd struct. */
int mmd_mutex;
-/** The file containing user information (public key, permissions). */
-static char *user_list_file = NULL;
-
static struct sched sched;
static struct signal_task *signal_task;
/**
* (Re-)read the server configuration files.
*
- * \param override Passed to gengetopt to activate the override feature.
+ * \param reload Whether config file overrides command line.
*
- * This function also re-opens the logfile and sets the global \a
- * user_list_file variable.
+ * This function also re-opens the logfile and the user list. On SIGHUP it is
+ * called from both server and afs context.
*/
-void parse_config_or_die(int override)
+void parse_config_or_die(bool reload)
{
- char *home = para_homedir();
int ret;
- char *cf;
+ char *cf = NULL, *errctx = NULL, *user_list_file = NULL;
+ void *map;
+ size_t sz;
+ int cf_argc;
+ char **cf_argv;
+ struct lls_parse_result *cf_lpr, *merged_lpr;
+ char *home = para_homedir();
daemon_close_log();
- if (conf.config_file_given)
- cf = para_strdup(conf.config_file_arg);
+ if (OPT_GIVEN(CONFIG_FILE))
+ cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
else
cf = make_message("%s/.paraslash/server.conf", home);
- free(user_list_file);
- if (!conf.user_list_given)
- user_list_file = make_message("%s/.paraslash/server.users", home);
- else
- user_list_file = para_strdup(conf.user_list_arg);
- ret = file_exists(cf);
- if (conf.config_file_given && !ret) {
- ret = -1;
- PARA_EMERG_LOG("can not read config file %s\n", cf);
- goto out;
+ if (!mmd || getpid() != mmd->afs_pid) {
+ if (OPT_GIVEN(USER_LIST))
+ user_list_file = para_strdup(OPT_STRING_VAL(USER_LIST));
+ else
+ user_list_file = make_message("%s/.paraslash/server.users", home);
}
- if (ret) {
- int tmp = conf.daemon_given;
- struct server_cmdline_parser_params params = {
- .override = override,
- .initialize = 0,
- .check_required = 1,
- .check_ambiguity = 0,
- .print_errors = !conf.daemon_given
- };
- server_cmdline_parser_config_file(cf, &conf, ¶ms);
- daemon_set_loglevel(conf.loglevel_arg);
- conf.daemon_given = tmp;
+ free(home);
+ ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+ if (ret < 0) {
+ if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+ goto free_cf;
+ if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE))
+ goto free_cf;
+ ret = 0;
+ server_lpr = cmdline_lpr;
+ goto success;
}
- if (conf.logfile_given) {
- daemon_set_logfile(conf.logfile_arg);
+ ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx));
+ para_munmap(map, sz);
+ if (ret < 0)
+ goto free_cf;
+ cf_argc = ret;
+ ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr, &errctx));
+ lls_free_argv(cf_argv);
+ if (ret < 0)
+ goto free_cf;
+ if (reload) /* config file overrides command line */
+ ret = lls(lls_merge(cf_lpr, cmdline_lpr, CMD_PTR, &merged_lpr,
+ &errctx));
+ else /* command line options overrride config file options */
+ ret = lls(lls_merge(cmdline_lpr, cf_lpr, CMD_PTR, &merged_lpr,
+ &errctx));
+ lls_free_parse_result(cf_lpr, CMD_PTR);
+ if (ret < 0)
+ goto free_cf;
+ if (server_lpr != cmdline_lpr)
+ lls_free_parse_result(server_lpr, CMD_PTR);
+ server_lpr = merged_lpr;
+success:
+ daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+ if (OPT_GIVEN(LOGFILE)) {
+ daemon_set_logfile(OPT_STRING_VAL(LOGFILE));
daemon_open_log_or_die();
}
-
- if (daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no,
- conf.logfile_given)) {
+ if (daemon_init_colors_or_die(OPT_UINT32_VAL(COLOR), COLOR_AUTO,
+ COLOR_NO, OPT_GIVEN(LOGFILE))) {
int i;
- for (i = 0; i < conf.log_color_given; i++)
- daemon_set_log_color_or_die(conf.log_color_arg[i]);
+ for (i = 0; i < OPT_GIVEN(LOG_COLOR); i++)
+ daemon_set_log_color_or_die(lls_string_val(i,
+ OPT_RESULT(LOG_COLOR)));
}
daemon_set_flag(DF_LOG_PID);
daemon_set_flag(DF_LOG_LL);
daemon_set_flag(DF_LOG_TIME);
- if (conf.log_timing_given)
+ if (OPT_GIVEN(LOG_TIMING))
daemon_set_flag(DF_LOG_TIMING);
+ daemon_set_priority(OPT_UINT32_VAL(PRIORITY));
+ if (user_list_file)
+ init_user_list(user_list_file);
ret = 1;
-out:
+free_cf:
free(cf);
- free(home);
- if (ret > 0)
- return;
free(user_list_file);
- user_list_file = NULL;
- exit(EXIT_FAILURE);
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+ exit(EXIT_FAILURE);
+ }
}
/*
*/
static void handle_sighup(void)
{
+
PARA_NOTICE_LOG("SIGHUP\n");
- parse_config_or_die(1); /* reopens log */
- init_user_list(user_list_file); /* reload user list */
+ parse_config_or_die(true);
if (mmd->afs_pid)
kill(mmd->afs_pid, SIGHUP);
}
*/
for (i = sct->argc - 1; i >= 0; i--)
memset(sct->argv[i], 0, strlen(sct->argv[i]));
- sprintf(sct->argv[0], "para_server (serving %s)", peer_name);
+ i = sct->argc - 1 - lls_num_inputs(cmdline_lpr);
+ sprintf(sct->argv[i], "para_server (serving %s)", peer_name);
handle_connect(new_fd, peer_name);
/* never reached*/
out:
PARA_NOTICE_LOG("initializing tcp command socket\n");
sct->argc = argc;
sct->argv = argv;
- ret = para_listen_simple(IPPROTO_TCP, conf.port_arg);
+ ret = para_listen_simple(IPPROTO_TCP, OPT_UINT32_VAL(PORT));
if (ret < 0)
goto err;
sct->listen_fd = ret;
for (i = argc - 1; i >= 0; i--)
memset(argv[i], 0, strlen(argv[i]));
- sprintf(argv[0], "para_server (afs)");
+ i = argc - lls_num_inputs(cmdline_lpr) - 1;
+ sprintf(argv[i], "para_server (afs)");
close(afs_server_socket[0]);
afs_init(afs_socket_cookie, afs_server_socket[1]);
}
return afs_server_socket[0];
}
-__noreturn static void print_help_and_die(void)
+static void handle_help_flags(void)
{
- struct ggo_help h = DEFINE_GGO_HELP(server);
- bool d = conf.detailed_help_given;
+ char *help;
+ bool d = OPT_GIVEN(DETAILED_HELP);
- ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
- exit(0);
+ if (d)
+ help = lls_long_help(CMD_PTR);
+ else if (OPT_GIVEN(HELP))
+ help = lls_short_help(CMD_PTR);
+ else
+ return;
+ printf("%s\n", help);
+ free(help);
+ exit(EXIT_SUCCESS);
}
static void server_init(int argc, char **argv)
{
- struct server_cmdline_parser_params params = {
- .override = 0,
- .initialize = 1,
- .check_required = 0,
- .check_ambiguity = 0,
- .print_errors = 1
- };
- int afs_socket, daemon_pipe = -1;
+ int ret, afs_socket, daemon_pipe = -1;
+ char *errctx;
valid_fd_012();
- init_random_seed_or_die();
/* parse command line options */
- server_cmdline_parser_ext(argc, argv, &conf, ¶ms);
- 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();
- daemon_set_priority(conf.priority_arg);
- daemon_drop_privileges_or_die(conf.user_arg, conf.group_arg);
- /* parse config file, open log and set defaults */
- parse_config_or_die(0);
+ ret = lls(lls_parse(argc, argv, CMD_PTR, &cmdline_lpr, &errctx));
+ if (ret < 0)
+ goto fail;
+ server_lpr = cmdline_lpr;
+ daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+ daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
+ OPT_STRING_VAL(GROUP));
+ version_handle_flag("server", OPT_GIVEN(VERSION));
+ handle_help_flags();
+ parse_config_or_die(false);
+ /* become daemon */
+ if (OPT_GIVEN(DAEMON))
+ daemon_pipe = daemonize(true /* parent waits for SIGTERM */);
+ init_random_seed_or_die();
daemon_log_welcome("server");
init_ipc_or_die(); /* init mmd struct and mmd->lock */
daemon_set_start_time();
- init_user_list(user_list_file);
- /* become daemon */
- if (conf.daemon_given)
- daemon_pipe = daemonize(true /* parent waits for us */);
PARA_NOTICE_LOG("initializing audio format handlers\n");
afh_init();
* Although afs uses its own signal handling we must ignore SIGUSR1
* _before_ the afs child process gets born by init_afs() below. It's
* racy to do this in the child because the parent might send SIGUSR1
- * before the child gets a chance to ignore this signal -- only the
- * good die young.
- */
- para_sigaction(SIGUSR1, SIG_IGN);
- /*
- * We have to block SIGCHLD before the afs process is being forked off.
- * Otherwise, para_server does not notice if afs dies before the
+ * before the child gets a chance to ignore this signal.
+ *
+ * We also have to block SIGCHLD before the afs process is created
+ * because otherwise para_server does not notice if afs dies before the
* SIGCHLD handler has been installed for the parent process by
* init_signal_task() below.
*/
+ para_sigaction(SIGUSR1, SIG_IGN);
para_block_signal(SIGCHLD);
PARA_NOTICE_LOG("initializing the audio file selector\n");
afs_socket = init_afs(argc, argv);
close(daemon_pipe);
}
PARA_NOTICE_LOG("server init complete\n");
+ return;
+fail:
+ assert(ret < 0);
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+ exit(EXIT_FAILURE);
}
static void status_refresh(void)
mutex_lock(mmd_mutex);
ret = schedule(&sched);
sched_shutdown(&sched);
- if (ret < 0) {
+ lls_free_parse_result(server_lpr, CMD_PTR);
+ if (server_lpr != cmdline_lpr)
+ lls_free_parse_result(cmdline_lpr, CMD_PTR);
+ if (ret < 0)
PARA_EMERG_LOG("%s\n", para_strerror(-ret));
- exit(EXIT_FAILURE);
- }
- exit(EXIT_SUCCESS);
+ exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
}
+++ /dev/null
-BN: server
-SF: command.c
-SN: list of server commands
----
-N: ff
-P: VSS_READ | VSS_WRITE
-D: Jump N seconds forward or backward.
-U: ff n[-]
-H: This sets the 'R' (reposition request) bit of the vss status flags
-H: which enqueues a request to jump n seconds forwards or backwards.
-H:
-H: Example:
-H:
-H: para_client ff 30-
-H:
-H: jumps 30 seconds backwards.
----
-N: help
-P: 0
-D: Print online help.
-U: help [command]
-H: Without any arguments, help prints a list of available commands. When
-H: called with a command name as first argument, it prints the description
-H: of that command.
----
-N: hup
-P: VSS_WRITE
-D: Reload config file, log file and user list.
-U: hup
-H: Reread the config file and the user list file, close and reopen the log
-H: file, and ask the afs process to do the same. Sending the HUP signal to
-H: the server process has the same effect.
----
-N: jmp
-P: VSS_READ | VSS_WRITE
-D: Jump to the given position.
-U: jmp n
-H: Set the 'R' (reposition request) bit of the vss status flags and enqueue a
-H: request to jump to n% of the current audio file, where 0 <= n <= 100.
----
-N: next
-P: VSS_READ | VSS_WRITE
-D: Close the current audio file.
-U: next
-H: Set the 'N' (next audio file) bit of the vss status flags which instructs the
-H: server to close its current audio file if necessary. If the 'P' bit (playing)
-H: is on, playing continues with the next audio file. This command is equivalent
-H: to stop if paused, and has no effect if stopped.
----
-N: nomore
-P: VSS_READ | VSS_WRITE
-D: Stop playing after current audio file.
-U: nomore
-H: Set the 'O' (no more) bit of the vss status flags which asks para_server to
-H: clear the 'P' (playing) bit after the 'N' (next audio file) bit transitions
-H: from off to on (because the end of the current audio file is reached). Use this
-H: command instead of stop if you don't like sudden endings.
----
-N: pause
-P: VSS_READ | VSS_WRITE
-D: Pause current audio file.
-U: pause
-H: Clear the 'P' (playing) bit of the vss status flags.
----
-N: play
-P: VSS_READ | VSS_WRITE
-D: Start or resume playing.
-U: play
-H: Set the 'P' (playing) bit of the vss status flags.
----
-N: sender
-P: VSS_READ | VSS_WRITE
-D: Control paraslash senders.
-U: sender [s cmd [arguments]]
-H: Send a command to a specific sender. The following commands are available, but
-H: not all senders support every command.
-H:
-H: help, on, off, add, delete, allow, deny, status.
-H:
-H: The help command prints the help text of the given sender. If no command is
-H: given the list of compiled in senders is shown.
-H:
-H: Example:
-H:
-H: para_client sender http help
----
-N: si
-P: 0
-D: Print server info.
-U: si
-H: Show server and afs PID, number of connections, uptime and more.
----
-N: stat
-P: VSS_READ
-D: Print information about the current audio file.
-U: stat [-n=num] [-p]
-H: If -n is given, exit after the status information has been shown n times.
-H: Otherwise, the command runs in an endless loop.
-H:
-H: The -p option activates parser-friendly output: Each status item is
-H: prefixed with its size in bytes and the status item identifiers are
-H: printed as numerical values.
----
-N: stop
-P: VSS_READ | VSS_WRITE
-D: Stop playing.
-U: stop
-H: Clear the 'P' (playing) bit and set the 'N' (next audio file) bit of the vss
-H: status flags, effectively stopping playback.
----
-N: tasks
-P: 0
-D: List server tasks.
-U: tasks
-H: For each task, print ID, status and name.
----
-N: term
-P: VSS_READ | VSS_WRITE
-D: Ask the server to terminate.
-U: term
-H: Shut down the server. Instead of this command, you can also send SIGINT or
-H: SIGTERM to the para_server process. It should never be necessary to send
-H: SIGKILL.
----
-N: version
-P: 0
-D: Print the git version string of para_server.
-U: version
-H: Show version and other info.
* propagate to the stat command handlers.
*/
struct misc_meta_data {
- /** The size of the current audio file in bytes. */
- size_t size;
/** The "old" status flags -- commands may only read them. */
unsigned int vss_status_flags;
/** The new status flags -- commands may set them. */
struct audio_file_data afd;
};
-/** Command line options for para_server. */
-extern struct server_args_info conf;
+extern struct lls_parse_result *server_lpr;
+
+/**
+ * Get a reference to the supercommand of para_server.
+ *
+ * This is needed for parsing the command line and for the ENUM_STRING_VAL()
+ * macro below. The latter macro is used in command.c, so CMD_PTR() can not
+ * be made local to server.c.
+ */
+#define CMD_PTR (lls_cmd(0, server_suite))
+
+/** Get the parse result of an option to para_server. */
+#define OPT_RESULT(_name) (lls_opt_result( \
+ LSG_SERVER_PARA_SERVER_OPT_ ## _name, server_lpr))
+
+/** How many times a server option was given. */
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+
+/** The (first) argument to a server option of type string. */
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+
+/** The (first) argument to a server option of type uint32. */
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
+
+/** The (first) argument to a server option of type int32. */
+#define OPT_INT32_VAL(_name) (lls_int32_val(0, OPT_RESULT(_name)))
+
+/** Get the string which corresponds to an enum constant. */
+#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \
+ lls_opt(LSG_SERVER_PARA_SERVER_OPT_ ## _name, CMD_PTR)))
__noreturn void handle_connect(int fd, const char *peername);
-void parse_config_or_die(int override);
+void parse_config_or_die(bool reload);
char *server_get_tasks(void);
#include "para.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "error.h"
return ret;
}
-/**
- * The init function of the ogg/speex decoder.
- *
- * \param f Its fields are filled in by the function.
- */
-void spxdec_filter_init(struct filter *f)
-{
- f->open = spxdec_open;
- f->close = speexdec_close;
- f->pre_select = generic_filter_pre_select;
- f->post_select = speexdec_post_select;
- f->execute = speexdec_execute;
-}
+const struct filter lsg_filter_cmd_com_spxdec_user_data = {
+ .open = spxdec_open,
+ .close = speexdec_close,
+ .pre_select = generic_filter_pre_select,
+ .post_select = speexdec_post_select,
+ .execute = speexdec_execute,
+};
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
+#include "filter_cmd.lsg.h"
#include "para.h"
-#include "sync_filter.cmdline.h"
#include "list.h"
#include "net.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
struct list_head node;
};
-/* Allocated in ->open() */
+/* Allocated in ->open(), stored in fn->private_data */
struct sync_filter_context {
int listen_fd;
struct list_head buddies;
bool ping_sent;
};
-/* Allocated and freed in ->parse_config() and ->free_config(). */
-struct sync_filter_config {
- struct sync_filter_args_info *conf;
- struct sync_buddy_info *buddy_info;
-};
-
#define FOR_EACH_BUDDY(_buddy, _list) \
list_for_each_entry(_buddy, _list, node)
#define FOR_EACH_BUDDY_SAFE(_buddy, _tmp_buddy, _list) \
fn->private_data = NULL;
}
-static void sync_free_config(void *conf)
+static void sync_teardown(const struct lls_parse_result *lpr, void *conf)
{
- struct sync_filter_config *sfc = conf;
- int i;
+ struct sync_buddy_info *sbi = conf;
+ int i, num_buddies = FILTER_CMD_OPT_GIVEN(SYNC, BUDDY, lpr);
- for (i = 0; i < sfc->conf->buddy_given; i++) {
- free(sfc->buddy_info[i].host);
- freeaddrinfo(sfc->buddy_info[i].ai);
+ for (i = 0; i < num_buddies; i++) {
+ free(sbi[i].host);
+ freeaddrinfo(sbi[i].ai);
}
- sync_filter_cmdline_parser_free(sfc->conf);
- free(sfc);
+ free(sbi);
}
static void sync_open(struct filter_node *fn)
{
int i, ret;
- struct sync_filter_config *sfc = fn->conf;
struct sync_buddy *buddy;
struct sync_filter_context *ctx;
-
- assert(sfc);
+ struct sync_buddy_info *sbi = fn->conf;
+ uint32_t port = FILTER_CMD_OPT_UINT32_VAL(SYNC, PORT, fn->lpr);
+ unsigned buddy_given;
+ const struct lls_opt_result *r_b;
ctx = fn->private_data = para_calloc(sizeof(*ctx));
INIT_LIST_HEAD(&ctx->buddies);
- ctx->listen_fd = -1;
/* create socket to listen for incoming packets */
ret = makesock(
IPPROTO_UDP,
true /* passive */,
NULL /* no host required */,
- sfc->conf->port_arg,
+ port,
NULL /* no flowopts */
);
if (ret < 0) {
- PARA_ERROR_LOG("could not create UDP listening socket %d\n",
- sfc->conf->port_arg);
+ PARA_ERROR_LOG("could not create UDP listening socket %u\n",
+ port);
return;
}
ctx->listen_fd = ret;
PARA_INFO_LOG("listening on fd %d\n", ctx->listen_fd);
- for (i = 0; i < sfc->conf->buddy_given; i++) {
- struct sync_buddy_info *sbi = sfc->buddy_info + i;
- const char *url = sfc->conf->buddy_arg[i];
+ r_b = FILTER_CMD_OPT_RESULT(SYNC, BUDDY, fn->lpr);
+ buddy_given = lls_opt_given(r_b);
+ for (i = 0; i < buddy_given; i++) {
int fd;
+ const char *url = lls_string_val(i, r_b);
/* make buddy udp socket from address info */
assert(sbi->ai);
ret = makesock_addrinfo(
IPPROTO_UDP,
false /* not passive */,
- sbi->ai,
+ sbi[i].ai,
NULL /* no flowopts */
);
if (ret < 0) {
}
buddy = para_malloc(sizeof(*buddy));
buddy->fd = fd;
- buddy->sbi = sbi;
+ buddy->sbi = sbi + i;
buddy->ping_received = false;
para_list_add(&buddy->node, &ctx->buddies);
}
/*
- * At parse config time, we build an array of struct sync_buddy_info with one
- * entry for each buddy given in the arguments. This array is not affected by
- * sync_close(), so information stored there can be used for multiple instances
- * (para_audiod). We store the resolved url and the ->disabled bit in this
- * array.
+ * Build an array of struct sync_buddy_info with one entry for each buddy given
+ * in the arguments. This array is not affected by sync_close(), so information
+ * stored there can be used for multiple instances (para_audiod). We store the
+ * resolved url and the ->disabled bit in this array.
*/
-static int sync_parse_config(int argc, char **argv, void **result)
+static void *sync_setup(const struct lls_parse_result *lpr)
{
- int i, ret, n;
- struct sync_filter_config *sfc;
- struct sync_filter_args_info *conf = para_malloc(sizeof(*conf));
-
- sync_filter_cmdline_parser(argc, argv, conf); /* exits on error */
- sfc = para_calloc(sizeof(*sfc));
- sfc->conf = conf;
- n = conf->buddy_given;
- sfc->buddy_info = para_malloc((n + 1) * sizeof(*sfc->buddy_info));
- PARA_INFO_LOG("initializing buddy info array of length %d\n", n);
+ int i, ret;
+ unsigned n;
+ struct sync_buddy_info *sbi;
+ const struct lls_opt_result *r_b;
+
+ r_b = FILTER_CMD_OPT_RESULT(SYNC, BUDDY, lpr);
+ n = lls_opt_given(r_b);
+ sbi = para_malloc(n * sizeof(*sbi));
+ PARA_INFO_LOG("initializing buddy info array of length %u\n", n);
for (i = 0; i < n; i++) {
- const char *url = conf->buddy_arg[i];
+ const char *url = lls_string_val(i, r_b);
size_t len = strlen(url);
char *host = para_malloc(len + 1);
int port;
struct addrinfo *ai;
- struct sync_buddy_info *sbi = sfc->buddy_info + i;
if (!parse_url(url, host, len, &port)) {
- free(host);
PARA_ERROR_LOG("could not parse url %s\n", url);
- ret = -ERRNO_TO_PARA_ERROR(EINVAL);
- goto fail;
+ exit(EXIT_FAILURE);
}
if (port < 0)
- port = conf->port_arg;
+ port = FILTER_CMD_OPT_UINT32_VAL(SYNC, PORT, lpr);
ret = lookup_address(IPPROTO_UDP, false /* not passive */,
host, port, &ai);
if (ret < 0) {
- PARA_ERROR_LOG("host lookup failure for %s\n", url);
- free(host);
- goto fail;
+ PARA_ERROR_LOG("host lookup failure for %s: %s\n",
+ url, para_strerror(-ret));
+ exit(EXIT_FAILURE);
}
- sbi->url = url;
- sbi->host = host;
- sbi->port = port;
- sbi->ai = ai;
- sbi->disabled = false;
+ sbi[i].url = url;
+ sbi[i].host = host;
+ sbi[i].port = port;
+ sbi[i].ai = ai;
+ sbi[i].disabled = false;
PARA_DEBUG_LOG("buddy #%d: %s\n", i, url);
}
- *result = sfc;
- return 1;
-fail:
- assert(ret < 0);
- PARA_ERROR_LOG("%s\n", para_strerror(-ret));
- sync_free_config(sfc);
- return ret;
+ return sbi;
}
/*
}
static void sync_set_timeout(struct sync_filter_context *ctx,
- struct sync_filter_config *sfc)
+ struct lls_parse_result *lpr)
{
+ uint32_t ms = FILTER_CMD_OPT_UINT32_VAL(SYNC, TIMEOUT, lpr);
struct timeval to;
- ms2tv(sfc->conf->timeout_arg, &to);
+ ms2tv(ms, &to);
tv_add(now, &to, &ctx->timeout);
}
int ret;
struct filter_node *fn = context;
struct sync_filter_context *ctx = fn->private_data;
- struct sync_filter_config *sfc = fn->conf;
if (list_empty(&ctx->buddies))
return sched_min_delay(s);
if (ret == 0)
return;
if (ctx->timeout.tv_sec == 0) { /* must ping buddies */
- sync_set_timeout(ctx, sfc);
+ sync_set_timeout(ctx, fn->lpr);
return sched_min_delay(s);
}
if (sync_complete(ctx)) /* push down what we have */
int ret;
struct filter_node *fn = context;
struct sync_filter_context *ctx = fn->private_data;
- struct sync_filter_config *sfc = fn->conf;
struct sync_buddy *buddy, *tmp;
if (list_empty(&ctx->buddies))
if (ret == 0)
return 0;
if (ctx->timeout.tv_sec == 0)
- sync_set_timeout(ctx, sfc);
+ sync_set_timeout(ctx, fn->lpr);
else {
if (tv_diff(&ctx->timeout, now, NULL) < 0) {
sync_disable_active_buddies(ctx);
return ret;
}
-/**
- * The synchronization filter.
- *
- * \param f Pointer to the struct to initialize.
- */
-void sync_filter_init(struct filter *f)
-{
- struct sync_filter_args_info dummy;
-
- sync_filter_cmdline_parser_init(&dummy);
- f->open = sync_open;
- f->close = sync_close;
- f->pre_select = sync_pre_select;
- f->post_select = sync_post_select;
- f->parse_config = sync_parse_config;
- f->free_config = sync_free_config;
- f->help = (struct ggo_help)DEFINE_GGO_HELP(sync_filter);
-}
+const struct filter lsg_filter_cmd_com_sync_user_data = {
+ .setup = sync_setup,
+ .open = sync_open,
+ .pre_select = sync_pre_select,
+ .post_select = sync_post_select,
+ .close = sync_close,
+ .teardown = sync_teardown
+};
tests := $(sort $(wildcard $(test_dir)/t[0-9][0-9][0-9][0-9]-*.sh))
+check: $(tests)
test: $(tests)
$(tests): all
$(RM) -r $(results_dir)
$(RM) -r $(trash_dir)
-.PHONY: $(tests) test-help
+.PHONY: $(tests) test-help test-clean test check
i=0
commands[$i]="help"
cmdline[$i]="help"
-good[$i]='help server ----'
+good[$i]='help ----'
let i++
commands[$i]="init"
. ${0%/*}/test-lib.sh
-rfw_regex='Options for .\{100,\}Options for ' # recv/filter/writer
-
grep_man()
{
local regex="$1" exe="$2"
# check that options of all reveivers/filters/writers are contained
# in the man pages
-regex="$rfw_regex"
-test_expect_success 'para_recv: receiver options' "grep_man '$regex' recv"
-test_expect_success 'para_filter: filter options' "grep_man '$regex' filter"
-test_expect_success 'para_write: writer options' "grep_man '$regex' write"
+test_expect_success 'para_recv: receiver options' "grep_man 'RECEIVERS' recv"
+test_expect_success 'para_filter: filter options' "grep_man 'FILTERS' filter"
+test_expect_success 'para_write: writer options' "grep_man 'WRITERS' write"
test_require_objects "audiod"
if [[ -n "$result" ]]; then
test_skip 'para_audiod' "missing object(s): $result"
else
test_expect_success 'para_audiod: receivers' \
- "grep_man 'Options for the http receiver' audiod"
+ "grep_man 'RECEIVERS' audiod"
test_expect_success 'para_audiod: filters' \
- "grep_man 'Options for the compress filter' audiod"
+ "grep_man 'FILTERS' audiod"
test_expect_success 'para_audiod: writers' \
- "grep_man 'Options for the file writer' audiod"
+ "grep_man 'WRITERS' audiod"
fi
# check various command lists
if [[ -n "$missing_objects" ]]; then
test_skip "para_server" "missing object(s): $missing_objects"
else
- regex='LIST OF SERVER COMMANDS.\{100,\}LIST OF AFS COMMANDS'
+ regex='LIST OF SERVER COMMANDS.\{100,\}'
test_expect_success 'para_server: server/afs commands' \
"grep_man '$regex' server"
fi
fixup_dirs
[[ -z "$o_executables" ]] && o_executables="para_afh para_audioc para_audiod
- para_client para_fade para_filter para_gui para_recv para_server
+ para_client para_mixer para_filter para_gui para_recv para_server
para_write"
for exe in $o_executables; do
export $(tr 'a-z' 'A-Z' <<< $exe)="$o_executables_dir/$exe"
struct timeval *clock_get_realtime(struct timeval *tv)
{
static struct timeval user_friendly;
+ struct timespec t;
+ int ret;
if (!tv)
tv = &user_friendly;
-#ifdef HAVE_CLOCK_GETTIME
- {
- struct timespec t;
- int ret;
-
- ret = clock_gettime(CLOCK_REALTIME, &t);
- assert(ret == 0);
- tv->tv_sec = t.tv_sec;
- tv->tv_usec = t.tv_nsec / 1000;
- }
-#else
- #include <sys/time.h>
- gettimeofday(tv, NULL);
-#endif /* HAVE_CLOCK_GETTIME */
+ ret = clock_gettime(CLOCK_REALTIME, &t);
+ assert(ret == 0);
+ tv->tv_sec = t.tv_sec;
+ tv->tv_usec = t.tv_nsec / 1000;
return tv;
}
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
+#include "recv_cmd.lsg.h"
#include "para.h"
#include "error.h"
#include "portable_io.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "recv.h"
-#include "udp_recv.cmdline.h"
#include "string.h"
#include "net.h"
#include "fd.h"
btr_pool_free(rn->btrp);
}
-static void *udp_recv_parse_config(int argc, char **argv)
-{
- struct udp_recv_args_info *tmp = para_calloc(sizeof(*tmp));
- udp_recv_cmdline_parser(argc, argv, tmp);
- return tmp;
-}
-
/*
* Perform AF-independent joining of multicast receive addresses.
*
static int udp_recv_open(struct receiver_node *rn)
{
- struct udp_recv_args_info *c = rn->conf;
- char *iface = c->iface_given ? c->iface_arg : NULL;
+ struct lls_parse_result *lpr = rn->lpr;
+ const char *iface = RECV_CMD_OPT_STRING_VAL(UDP, IFACE, lpr);
+ const char *host = RECV_CMD_OPT_STRING_VAL(UDP, HOST, lpr);
+ uint32_t port = RECV_CMD_OPT_UINT32_VAL(UDP, PORT, lpr);
int ret;
- ret = makesock(IPPROTO_UDP, 1, c->host_arg, c->port_arg, NULL);
+ ret = makesock(IPPROTO_UDP, 1, host, port, NULL);
if (ret < 0)
- goto err;
+ return ret;
rn->fd = ret;
-
ret = mcast_receiver_setup(rn->fd, iface);
- if (ret < 0) {
- close(rn->fd);
+ if (ret < 0)
goto err;
- }
-
ret = mark_fd_nonblocking(rn->fd);
- if (ret < 0) {
- close(rn->fd);
+ if (ret < 0)
goto err;
- }
- PARA_INFO_LOG("receiving from %s:%d, fd=%d\n", c->host_arg,
- c->port_arg, rn->fd);
+ PARA_INFO_LOG("receiving from %s:%u, fd=%d\n", host, port, rn->fd);
rn->btrp = btr_pool_new("udp_recv", 320 * 1024);
return rn->fd;
err:
+ close(rn->fd);
return ret;
}
-static void udp_recv_free_config(void *conf)
-{
- udp_recv_cmdline_parser_free(conf);
- free(conf);
-}
-
-/**
- * The init function of the udp receiver.
- *
- * \param r Pointer to the receiver struct to initialize.
- *
- * Initialize all function pointers of \a r.
- */
-void udp_recv_init(struct receiver *r)
-{
- struct udp_recv_args_info dummy;
-
- udp_recv_cmdline_parser_init(&dummy);
- r->open = udp_recv_open;
- r->close = udp_recv_close;
- r->pre_select = udp_recv_pre_select;
- 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)DEFINE_GGO_HELP(udp_recv);
- udp_recv_cmdline_parser_free(&dummy);
-}
+const struct receiver lsg_recv_cmd_com_udp_user_data = {
+ .open = udp_recv_open,
+ .close = udp_recv_close,
+ .pre_select = udp_recv_pre_select,
+ .post_select = udp_recv_post_select,
+};
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
-#include "server.cmdline.h"
+#include "server.lsg.h"
#include "para.h"
#include "error.h"
#include "string.h"
{
struct sockaddr_storage ss;
socklen_t sslen = sizeof(ss);
- int ttl = conf.udp_ttl_arg, id = 0;
+ int ttl = OPT_INT32_VAL(UDP_TTL), id = 0;
const int on = 1;
- if (conf.udp_mcast_iface_given) {
- char *iface = conf.udp_mcast_iface_arg;
+ if (OPT_GIVEN(UDP_MCAST_IFACE)) {
+ const char *iface = OPT_STRING_VAL(UDP_MCAST_IFACE);
id = if_nametoindex(iface);
if (id == 0)
ret = parse_fec_url(url, scd);
if (ret)
return ret;
- port = scd->port > 0 ? scd->port : conf.udp_default_port_arg;
+ port = scd->port > 0 ? scd->port : OPT_UINT32_VAL(UDP_DEFAULT_PORT);
ret = para_connect_simple(IPPROTO_UDP, scd->host, port);
if (ret < 0)
"port: %s\n"
"targets: %s\n",
(sender_status == SENDER_on)? "on" : "off",
- stringify_port(conf.udp_default_port_arg, "udp"),
+ stringify_port(OPT_UINT32_VAL(UDP_DEFAULT_PORT), "udp"),
tgts? tgts : "(none)"
);
free(tgts);
int i;
INIT_LIST_HEAD(&targets);
- for (i = 0; i < conf.udp_target_given; i++) {
- if (udp_resolve_target(conf.udp_target_arg[i], &scd) < 0)
+ for (i = 0; i < OPT_GIVEN(UDP_TARGET); i++) {
+ const char *arg = lls_string_val(i, OPT_RESULT(UDP_TARGET));
+ if (udp_resolve_target(arg, &scd) < 0)
PARA_CRIT_LOG("not adding requested target '%s'\n",
- conf.udp_target_arg[i]);
+ arg);
else
udp_com_add(&scd);
}
s->client_cmds[SENDER_delete] = udp_com_delete;
sender_status = SENDER_off;
udp_init_target_list();
- if (!conf.udp_no_autostart_given)
+ if (!OPT_GIVEN(UDP_NO_AUTOSTART))
sender_status = SENDER_on;
PARA_DEBUG_LOG("udp sender init complete\n");
}
if (strcmp(w, "user"))
continue;
PARA_DEBUG_LOG("found entry for user %s\n", n);
- ret = get_asymmetric_key(k, LOAD_PUBLIC_KEY, &pubkey);
+ ret = get_public_key(k, &pubkey);
if (ret < 0) {
PARA_NOTICE_LOG("skipping entry for user %s: %s\n", n,
para_strerror(-ret));
if (ret <= CHALLENGE_SIZE + 2 * SESSION_KEY_LEN + 41) {
PARA_WARNING_LOG("public key %s too short (%d)\n",
k, ret);
- free_asymmetric_key(pubkey);
+ free_public_key(pubkey);
continue;
}
u = para_malloc(sizeof(*u));
list_for_each_entry_safe(u, tmp, &user_list, node) {
list_del(&u->node);
free(u->name);
- free_asymmetric_key(u->pubkey);
+ free_public_key(u->pubkey);
free(u);
}
} else
* is read at server startup.
*/
enum server_command_permissions {
+ NO_PERMISSION_REQUIRED = 0, /** None of the below. */
AFS_READ = 1, /** Read-only operation on the AFS database. */
AFS_WRITE = 2, /** Read-write operation on the AFS database. */
VSS_READ = 4, /** Read-only operation on the virtual streaming system. */
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
+#include "server.lsg.h"
#include "para.h"
#include "error.h"
#include "portable_io.h"
#include "afs.h"
#include "server.h"
#include "net.h"
-#include "server.cmdline.h"
#include "list.h"
#include "send.h"
#include "sched.h"
enum afs_socket_status afsss;
/** The memory mapped audio file. */
char *map;
+ /** The size of the memory mapping. */
+ size_t mapsize;
/** Used by the scheduler. */
struct task *task;
/** Pointer to the header of the mapped audio file. */
size_t header_len;
/** Time between audio file headers are sent. */
struct timeval header_interval;
+ /* Only used if afh supports dynamic chunks. */
+ void *afh_context;
};
/**
static void vss_get_chunk(int chunk_num, struct vss_task *vsst,
char **buf, size_t *sz)
{
+ int ret;
+
/*
* Chunk zero is special for header streams: It is the first portion of
* the audio file which consists of the audio file header. It may be
* rather than the unmodified header (chunk zero).
*/
if (chunk_num == 0 && vsst->header_len > 0) {
+ assert(vsst->header_buf);
*buf = vsst->header_buf; /* stripped header */
*sz = vsst->header_len;
return;
}
- afh_get_chunk(chunk_num, &mmd->afd.afhi, vsst->map, (const char **)buf,
- sz);
+ ret = afh_get_chunk(chunk_num, &mmd->afd.afhi,
+ mmd->afd.audio_format_id, vsst->map, vsst->mapsize,
+ (const char **)buf, sz, &vsst->afh_context);
+ if (ret < 0) {
+ PARA_WARNING_LOG("could not get chunk %d: %s\n",
+ chunk_num, para_strerror(-ret));
+ *buf = NULL;
+ *sz = 0;
+ }
}
static void compute_group_size(struct vss_task *vsst, struct fec_group *g,
set_eof_barrier(vsst);
afh_free_header(vsst->header_buf, mmd->afd.audio_format_id);
vsst->header_buf = NULL;
- para_munmap(vsst->map, mmd->size);
+ para_munmap(vsst->map, vsst->mapsize);
vsst->map = NULL;
mmd->chunks_sent = 0;
//mmd->offset = 0;
mmd->afd.afhi.chunk_tv.tv_usec = 0;
free(mmd->afd.afhi.chunk_table);
mmd->afd.afhi.chunk_table = NULL;
- mmd->size = 0;
+ vsst->mapsize = 0;
+ afh_close(vsst->afh_context, mmd->afd.audio_format_id);
+ vsst->afh_context = NULL;
mmd->events++;
}
ret = -ERRNO_TO_PARA_ERROR(errno);
goto err;
}
- mmd->size = statbuf.st_size;
- ret = para_mmap(mmd->size, PROT_READ, MAP_PRIVATE | MAP_POPULATE,
+ ret = para_mmap(statbuf.st_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE,
passed_fd, 0, &vsst->map);
if (ret < 0)
goto err;
+ vsst->mapsize = statbuf.st_size;
close(passed_fd);
mmd->chunks_sent = 0;
mmd->current_chunk = 0;
mmd->num_played++;
mmd->new_vss_status_flags &= (~VSS_NEXT);
afh_get_header(&mmd->afd.afhi, mmd->afd.audio_format_id,
- vsst->map, mmd->size, &vsst->header_buf, &vsst->header_len);
+ vsst->map, vsst->mapsize, &vsst->header_buf, &vsst->header_len);
return;
err:
free(mmd->afd.afhi.chunk_table);
*/
if (mmd->current_chunk > 0) { /* chunk 0 might be on the heap */
buf += len;
- for (i = 0; i < 5 && buf < vsst->map + mmd->size; i++) {
+ for (i = 0; i < 5 && buf < vsst->map + vsst->mapsize; i++) {
__a_unused volatile char x = *buf;
buf += 4096;
}
set_eof_barrier(vsst);
mmd->chunks_sent = 0;
mmd->current_chunk = afh_get_start_chunk(mmd->repos_request,
- &mmd->afd.afhi);
+ &mmd->afd.afhi, mmd->afd.audio_format_id);
mmd->new_vss_status_flags &= ~VSS_REPOS;
set_mmd_offset();
}
static struct vss_task vss_task_struct, *vsst = &vss_task_struct;
int i;
char *hn = para_hostname(), *home = para_homedir();
- long unsigned announce_time = conf.announce_time_arg > 0?
- conf.announce_time_arg : 300,
- autoplay_delay = conf.autoplay_delay_arg > 0?
- conf.autoplay_delay_arg : 0;
+ long unsigned announce_time = OPT_UINT32_VAL(ANNOUNCE_TIME),
+ autoplay_delay = OPT_UINT32_VAL(AUTOPLAY_DELAY);
vsst->header_interval.tv_sec = 5; /* should this be configurable? */
vsst->afs_socket = afs_socket;
ms2tv(announce_time, &vsst->announce_tv);
free(hn);
free(home);
mmd->sender_cmd_data.cmd_num = -1;
- if (conf.autoplay_given) {
+ if (OPT_GIVEN(AUTOPLAY)) {
struct timeval tmp;
mmd->vss_status_flags |= VSS_PLAYING;
mmd->new_vss_status_flags |= VSS_PLAYING;
#include "error.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
return ret;
}
-/**
- * The init function of the wav filter.
- *
- * \param f Structure to initialize.
- */
-void wav_filter_init(struct filter *f)
-{
- f->close = wav_close;
- f->open = wav_open;
- f->pre_select = wav_pre_select;
- f->post_select = wav_post_select;
-}
+const struct filter lsg_filter_cmd_com_wav_user_data = {
+ .close = wav_close,
+ .open = wav_open,
+ .pre_select = wav_pre_select,
+ .post_select = wav_post_select,
+};
AddIcon ../signature.png *.asc
AddDescription "Digital signature" *.asc
- AddIcon ../tar-icon.png *.tgz *.tar.bz2
- AddDescription "current master snapshot" -git.tar.bz2 .g*.tar.bz2 .g*.dirty.tar.bz2
- AddDescription "release tarball" *.tgz *.tar.bz2
+ AddIcon ../tar-icon.png *.tgz *.tar.bz2 *.tar.xz
+ AddDescription "current master snapshot" -git.tar.xz .g*.tar.xz .g*.dirty.tar.xz
+ AddDescription "release tarball" *.tgz *.tar.bz2 *.tar.xz
</ifmodule>
systems. It is written in C and released under the GPLv2.
<ul>
- <li> Runs on Linux, Mac OS, FreeBSD, NetBSD </li>
+ <li> Runs on Linux, FreeBSD, NetBSD </li>
<li> Mp3, ogg/vorbis, ogg/speex, aac (m4a), wma, flac and ogg/opus support </li>
<li> http, dccp and udp network streaming </li>
<li> Stand-alone decoder, player, tagger </li>
[<a href="para_filter.man.html">para_filter</a>]
[<a href="para_write.man.html">para_write</a>]
[<a href="para_gui.man.html">para_gui</a>]
- [<a href="para_fade.man.html">para_fade</a>]
+ [<a href="para_mixer.man.html">para_mixere</a>]
[<a href="para_play.man.html">para_play</a>]
</p>
Clone the git repository by executing
- <p> <pre> <kbd>
+ <pre> <kbd>
git clone git://git.tuebingen.mpg.de/paraslash.git
- </kbd> </pre> </p>
+ </kbd> </pre>
<p> The repository contains the full history of the
project since 2006, all work in progress and the source
Whenever significant changes are incorporated a
- <a href="releases/paraslash-git.tar.bz2">tarball</a>
+ <a href="releases/paraslash-git.tar.xz">tarball</a>
of the current master branch is created. All changes in
this tarball will be included in the next release. Like
### para_write ###
A modular audio stream writer. It supports a simple file writer
-output plug-in and optional WAV/raw players for ALSA (Linux) and for
-coreaudio (Mac OS). para_write can also be used as a stand-alone WAV
-or raw audio player.
+output plug-in and optional WAV/raw players for ALSA (Linux) and OSS.
+para_write can also be used as a stand-alone WAV or raw audio player.
### para_play ###
key-bindings for the most common server commands and new key-bindings
can be added easily.
-### para_fade ###
+### para_mixer ###
An alarm clock and volume-fader for OSS and ALSA.
git clone git://git.tuebingen.mpg.de/osl
cd osl && make && sudo make install && sudo ldconfig
- sudo apt-get install autoconf libssl-dev help2man gengetopt m4 \
+ sudo apt-get install autoconf libssl-dev m4 \
libmad0-dev libid3tag0-dev libasound2-dev libvorbis-dev \
libfaad-dev libspeex-dev libFLAC-dev libsamplerate-dev realpath \
libasound2-dev libao-dev libreadline-dev libncurses-dev \
git clone git://git.tuebingen.mpg.de/osl
+- [lopsub](http://people.tuebingen.mpg.de/maan/lopsub/). The long
+option parser for subcommands generates the command line and config
+file parsers for all paraslash executables. Clone the source code
+repository with
+
+ git clone git://git.tuebingen.mpg.de/lopsub
+
- [gcc](ftp://ftp.gnu.org/pub/gnu/gcc) or
[clang](http://clang.llvm.org). All gcc versions >= 4.2 are currently
supported. Clang version 1.1 or newer should work as well.
during compilation require the _Bourne again shell_. It is most
likely already installed.
-- [gengetopt](ftp://ftp.gnu.org/pub/gnu/gengetopt/) is needed to
-generate the C code for the command line parsers of all paraslash
-executables.
-
-- [help2man](ftp://ftp.gnu.org/pub/gnu/help2man) is used to create
-the man pages.
-
- [m4](ftp://ftp.gnu.org/pub/gnu/m4/). Some source files are generated
from templates by the m4 macro processor.
you need libogg, libvorbis, libvorbisfile. The corresponding Debian
packages are called `libogg-dev` and `libvorbis-dev`.
-- [libfaad](http://www.audiocoding.com/). For aac files (m4a) you
-need libfaad (`libfaad-dev`).
+- [libfaad and mp4ff](http://www.audiocoding.com/). For aac files
+(m4a) you need libfaad and libmp4ff (package: `libfaad-dev`). Note
+that for some distributions, e.g. Ubuntu, mp4ff is not part of the
+libfaad package. Install the faad library from sources (available
+through the above link) to get the mp4ff library and header files.
- [speex](http://www.speex.org/). In order to stream or decode speex
files, libspeex (`libspeex-dev`) is required.
para_server uses a challenge-response mechanism to authenticate
requests from incoming connections, similar to ssh's public key
authentication method. Authenticated connections are encrypted using
-a stream cipher, either RC4 or AES in integer counter mode.
+the AES stream cipher in integer counter mode.
-In this chapter we briefly describe RSA, RC4 and AES, and sketch the
+In this chapter we briefly describe RSA and AES, and sketch the
[authentication handshake](#Client-server.authentication)
between para_client and para_server. User management is discussed
in the section on [the user_list file](#The.user_list.file).
server. Connecting para_audiod is a different matter and is described
in a [separate section](#Connecting.para_audiod).
-RSA, RC4, AES
--------------
+RSA and AES
+-----------
-RSA is an asymmetric block cipher which is used in many applications,
-including ssh and gpg. An RSA key consists in fact of two keys,
+A block cipher is a transformation which operates on fixed-length
+blocks. For symmetric block ciphers the transformation is determined
+by a single key for both encryption and decryption. For asymmetric
+block ciphers, on the other hand, the key consists of two parts,
called the public key and the private key. A message can be encrypted
-with either key and only the counterpart of that key can decrypt
-the message. While RSA can be used for both signing and encrypting
-a message, paraslash uses RSA only for the latter purpose. The
-RSA public key encryption and signatures algorithms are defined in
-detail in RFC 2437.
-
-RC4 is a stream cipher, i.e. the input is XORed with a pseudo-random
-key stream to produce the output. Decryption uses the same function
-calls as encryption. While RC4 supports variable key lengths,
-paraslash uses a fixed length of 256 bits, which is considered a
-strong encryption by today's standards. Since the same key must never
-be used twice, a different, randomly-generated key is used for every
-new connection.
+with either key and only the counterpart of that key can decrypt the
+message. Asymmetric block ciphers can be used for both signing and
+encrypting a message.
+
+RSA is an asymmetric block cipher which is used in many applications,
+including ssh and gpg. The RSA public key encryption and signatures
+algorithms are defined in detail in RFC 2437. Paraslash relies on
+RSA for authentication.
+
+Stream ciphers XOR the input with a pseudo-random key stream to produce
+the output. Decryption uses the same function calls as encryption.
+Any block cipher can be turned into a stream cipher by generating the
+pseudo-random key stream by encrypting successive values of a counter
+(counter mode).
AES, the advanced encryption standard, is a well-known symmetric block
-cipher, i.e. a transformation operating on fixed-length blocks which
-is determined by a single key for both encryption and decryption. Any
-block cipher can be turned into a stream cipher by generating
-a pseudo-random key stream by encrypting successive values of a
-counter. The AES_CTR128 stream cipher used in paraslash is obtained
-in this way from the AES block cipher with a 128 bit block size.
+cipher. Paraslash employs AES in counter mode as described above to
+encrypt communications. Since a stream cipher key must not be used
+twice, a random key is generated for every new connection.
Client-server authentication
----------------------------
the session key known to both peers.
paraslash relies on the quality of the pseudo-random bytes provided
-by the crypto library (openssl or libgcrypt), on the security of the
-implementation of the RSA, RC4 and AES crypto routines and on the
+by the crypto library (openssl or libgcrypt), on the security of
+the implementation of the RSA and AES crypto routines and on the
infeasibility to invert the SHA1 function.
Neither para_server or para_client create RSA keys on their
also limited. For example only one application can open the device
at any time. The OSS writer is activated by default on BSD Systems.
-- *OSX*. Mac OS X has yet another API called CoreAudio. The OSX writer
-for this API is only compiled in on such systems and is of course
-the default there.
-
- *FILE*. The file writer allows to capture the audio stream and
write the PCM data to a file on the file system rather than playing
it through a sound device. It is supported on all platforms and is
sfc++;
}
PARA_INFO_LOG("%d frames, %d superframes\n", fc, sfc);
- if (num_superframes)
- *num_superframes = sfc;
+ *num_superframes = sfc;
return fc;
}
*out++ = in;
return 1;
}
- bytes = (wma_log2(in) + 4) / 5;
+ bytes = DIV_ROUND_UP(wma_log2(in), 5);
shift = (bytes - 1) * 6;
*out++ = (256 - (256 >> bytes)) | (in >> shift);
while (shift >= 6) {
}
}
afhi->chunks_total = j;
+ set_max_chunk_size(afhi);
set_chunk_tv(frames_per_chunk, afhi->frequency, &afhi->chunk_tv);
return 1;
fail:
static int convert_utf8_to_utf16(char *src, char **dst)
{
- /*
- * Without specifying LE (little endian), iconv includes a byte order
- * mark (e.g. 0xFFFE) at the beginning.
- */
- iconv_t cd = iconv_open("UTF-16LE", "UTF-8");
+ iconv_t cd;
size_t sz, inbytes, outbytes, inbytesleft, outbytesleft;
char *inbuf, *outbuf;
int ret;
if (!src || !*src) {
*dst = para_calloc(2);
- ret = 0;
- goto out;
+ return 0;
}
- if (cd == (iconv_t) -1)
+ /*
+ * Without specifying LE (little endian), iconv includes a byte order
+ * mark (e.g. 0xFFFE) at the beginning.
+ */
+ cd = iconv_open("UTF-16LE", "UTF-8");
+ if (cd == (iconv_t)-1) {
+ *dst = NULL;
return -ERRNO_TO_PARA_ERROR(errno);
+ }
inbuf = src;
/* even though src is in UTF-8, strlen() should DTRT */
inbytes = inbytesleft = strlen(src);
sz = iconv(cd, ICONV_CAST &inbuf, &inbytesleft, &outbuf, &outbytesleft);
if (sz == (size_t)-1) {
ret = -ERRNO_TO_PARA_ERROR(errno);
+ free(*dst);
+ *dst = NULL;
goto out;
}
assert(outbytes >= outbytesleft);
*dst = outbuf;
PARA_INFO_LOG("converted %s to %d UTF-16 bytes\n", src, ret);
out:
- if (ret < 0)
- free(*dst);
if (iconv_close(cd) < 0)
PARA_WARNING_LOG("iconv_close: %s\n", strerror(errno));
return ret;
struct asf_object *result)
{
const char *cr, *rating; /* orig data */
- uint16_t orig_title_bytes, orig_artist_bytes, orig_cr_bytes,
- orig_comment_bytes, orig_rating_bytes;
+ uint16_t orig_cr_bytes, orig_rating_bytes;
/* pointers to new UTF-16 tags */
char *artist = NULL, *title = NULL, *comment = NULL;
/* number of bytes in UTF-16 for the new tags */
comment_bytes = ret;
if (cdo) {
+ uint16_t orig_title_bytes, orig_artist_bytes, orig_comment_bytes;
/*
* Sizes of the five fields (stored as 16-bit numbers) are
* located after the header (16 bytes) and the cdo size (8
cr = cdo->ptr + 34 + orig_title_bytes + orig_artist_bytes;
rating = cr + orig_cr_bytes + orig_comment_bytes;
} else {
- orig_title_bytes = 2;
- orig_artist_bytes = 2;
orig_cr_bytes = 2;
- orig_comment_bytes = 2;
orig_rating_bytes = 2;
cr = null;
rating = null;
v >>= 8;
n += 8;
}
- n += log2_tab[v];
-
- return n;
+ return n + log2_tab[v];
}
#include "para.h"
#include "error.h"
#include "list.h"
-#include "ggo.h"
#include "string.h"
#include "sched.h"
#include "buffer_tree.h"
#include "filter.h"
+#include "portable_io.h"
#include "bitstream.h"
#include "imdct.h"
#include "wma.h"
fn->min_iqs = 4096;
}
-/**
- * The init function of the wma decoder.
- *
- * \param f Its fields are filled in by the function.
- */
-void wmadec_filter_init(struct filter *f)
-{
- f->open = wmadec_open;
- f->close = wmadec_close;
- f->execute = wmadec_execute;
- f->pre_select = generic_filter_pre_select;
- f->post_select = wmadec_post_select;
-}
+const struct filter lsg_filter_cmd_com_wmadec_user_data = {
+ .open = wmadec_open,
+ .close = wmadec_close,
+ .execute = wmadec_execute,
+ .pre_select = generic_filter_pre_select,
+ .post_select = wmadec_post_select,
+};
#include <regex.h>
#include <sys/types.h>
+#include <lopsub.h>
+#include "write_cmd.lsg.h"
+#include "write.lsg.h"
#include "para.h"
#include "string.h"
-#include "write.cmdline.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "stdin.h"
#include "buffer_tree.h"
#include "write.h"
-#include "write_common.h"
#include "fd.h"
#include "error.h"
#include "version.h"
/** Array of error strings. */
DEFINE_PARA_ERRLIST;
-static struct write_args_info conf;
+#define CMD_PTR (lls_cmd(0, write_suite))
+#define OPT_RESULT(_name, _lpr) \
+ (lls_opt_result(LSG_WRITE_PARA_WRITE_OPT_ ## _name, _lpr))
+#define OPT_GIVEN(_name, _lpr) (lls_opt_given(OPT_RESULT(_name, _lpr)))
+#define OPT_UINT32_VAL(_name, _lpr) (lls_uint32_val(0, OPT_RESULT(_name, _lpr)))
static struct stdin_task sit;
-
static int loglevel;
INIT_STDERR_LOGGING(loglevel)
-__noreturn static void print_help_and_die(void)
+static void handle_help_flag(struct lls_parse_result *lpr)
{
- 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);
-}
-
-/*
- * Parse config and register a task for a writer node.
- *
- * \param arg Command line arguments.
- * \param parent The new node will be a child of \a parent.
- * \param wn The writer node.
- *
- * If arg is \p NULL, the OS-dependent default writer is used with no
- * arguments. The default writers are alsa for Linux, osx for OS X, oss for
- * *BSD, and the file writer if the default writer is not supported.
- *
- * Once the writer configuration has been retrieved from the ->parse_config
- * callback a writer node is created, its buffer tree node is added to the
- * buffer tree as a child of the given parent.
- *
- * Finally, the new writer node's task structure is initialized and registered
- * to the paraslash scheduler.
- *
- * \return Standard.
- */
-static void setup_writer_node(const char *arg, struct btr_node *parent,
- struct writer_node *wn, struct sched *s)
-{
- wn->conf = check_writer_arg_or_die(arg, &wn->writer_num);
- register_writer_node(wn, parent, s);
+ char *help;
+
+ if (OPT_GIVEN(DETAILED_HELP, lpr))
+ help = lls_long_help(CMD_PTR);
+ else if (OPT_GIVEN(HELP, lpr))
+ help = lls_short_help(CMD_PTR);
+ else
+ return;
+ printf("%s\n", help);
+ free(help);
+ print_writer_helps(OPT_GIVEN(DETAILED_HELP, lpr));
+ exit(EXIT_SUCCESS);
}
struct write_task {
return check_wav_post_select(wt->cwc);
}
-static int setup_and_schedule(void)
+static int setup_and_schedule(struct lls_parse_result *lpr)
{
- int i, ret;
+ int i, n, ret, writer_given = OPT_GIVEN(WRITER, lpr);
struct btr_node *cw_btrn;
struct writer_node *wns;
static struct sched s;
EMBRACE(.name = "stdin"));
stdin_task_register(&sit, &s);
- COPY_WAV_PARMS(&wp, &conf);
+ LLS_COPY_WAV_PARMS(&wp, LSG_WRITE_PARA_WRITE, lpr);
wt.cwc = check_wav_init(sit.btrn, NULL, &wp, &cw_btrn);
wt.task = task_register(&(struct task_info) {
.name = "write",
.post_select = write_post_select,
.context = &wt,
}, &s);
- if (!conf.writer_given) {
- wns = para_calloc(sizeof(*wns));
- setup_writer_node(NULL, cw_btrn, wns, &s);
- i = 1;
- } else {
- wns = para_calloc(conf.writer_given * sizeof(*wns));
- for (i = 0; i < conf.writer_given; i++)
- setup_writer_node(conf.writer_arg[i], cw_btrn,
- wns + i, &s);
- }
+ n = writer_given? writer_given : 1;
+ wns = para_calloc(n * sizeof(*wns));
+ for (i = 0; i < n; i++) {
+ const char *arg = i < writer_given?
+ lls_string_val(i, OPT_RESULT(WRITER, lpr)) : NULL;
+ wns[i].wid = check_writer_arg_or_die(arg, &wns[i].lpr);
+ register_writer_node(wns + i, cw_btrn, &s);
+ }
s.default_timeout.tv_sec = 10;
s.default_timeout.tv_usec = 50000;
ret = schedule(&s);
if (ret >= 0) {
int j, ts;
- for (j = 0; j < i; j++) {
+ for (j = 0; j < n; j++) {
struct writer_node *wn = wns + j;
ts = task_status(wn->task);
assert(ts < 0);
if (ts != -E_WRITE_COMMON_EOF && ts != -E_BTR_EOF) {
- const char *name = writer_names[wn->writer_num];
+ const char *name = writer_name(wn->wid);
PARA_ERROR_LOG("%s: %s\n", name,
para_strerror(-ts));
if (ret >= 0)
}
}
}
- for (i--; i >= 0; i--) {
+ for (i = n - 1; i >= 0; i--) {
struct writer_node *wn = wns + i;
- struct writer *w = writers + wn->writer_num;
-
- w->close(wn);
+ writer_get(wn->wid)->close(wn);
btr_remove_node(&wn->btrn);
- w->free_config(wn->conf);
- free(wn->conf);
+ lls_free_parse_result(wns[i].lpr,
+ lls_cmd(wn->wid, write_cmd_suite));
}
free(wns);
check_wav_shutdown(wt.cwc);
int main(int argc, char *argv[])
{
int ret;
-
- write_cmdline_parser(argc, argv, &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();
-
- ret = setup_and_schedule();
+ struct lls_parse_result *lpr;
+ char *errctx;
+
+ ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
+ if (ret < 0)
+ goto out;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL, lpr);
+ version_handle_flag("write", OPT_GIVEN(VERSION, lpr));
+ handle_help_flag(lpr);
+ ret = setup_and_schedule(lpr);
+ lls_free_parse_result(lpr, CMD_PTR);
+out:
if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
- exit(EXIT_FAILURE);
}
- exit(EXIT_SUCCESS);
+ return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
}
/** \file write.h Writer-related structures. */
-/** The list of supported writers. */
-enum writer_enum {WRITER_ENUM};
-
/**
* Describes one running instance of a writer.
*/
struct writer_node {
- /** The number of this writer. */
- int writer_num;
+ /** The ID of this writer. */
+ int wid;
/** Writer-specific data. */
void *private_data;
- /** The writer-specific configuration of this node. */
- void *conf;
+ /** The parsed command line, merged with options given in the config file. */
+ struct lls_parse_result *lpr;
/** The buffer tree node associated with this writer node. */
struct btr_node *btrn;
/** The task of this writer node. */
/** Describes one supported writer. */
struct writer {
- /**
- * The init function of the writer.
- *
- * It must fill in all other function pointers of the given
- * writer structure.
- */
- void (*init)(struct writer *w);
- /**
- * The command line parser of the writer.
- *
- * It should check whether the command line options given by \a argv
- * and \a argc are valid and return a pointer to the writer-specific
- * configuration data determined by these options. This function must
- * either succeed or call exit(). Note that parse_config_or_die() might
- * be called more than once with different values of \a options. \sa
- * \ref free_config().
- */
- void *(*parse_config_or_die)(int argc, char **argv);
- /**
- * Deallocate all configuration resources.
- *
- * This should free whatever was allocated by \ref parse_config_or_die().
- */
- void (*free_config)(void *config);
/**
* Prepare the fd sets for select.
*
* This function is assumed to succeed.
*/
void (*close)(struct writer_node *);
- /** The short and the log help text of this writer. */
- struct ggo_help help;
/**
* The callback handler.
*
btr_command_handler execute;
};
-/** Loop over each supported writer. */
-#define FOR_EACH_WRITER(i) for (i = 0; i < NUM_SUPPORTED_WRITERS; i++)
-
-/** Declare the init functions of all supported writers. */
-DECLARE_WRITER_INITS;
+#define WRITE_CMD(_num) (lls_cmd(_num, write_cmd_suite))
-/** Array containing the name of each writer. */
-extern const char *writer_names[];
+#define WRITE_CMD_OPT_RESULT(_cmd, _opt, _lpr) \
+ (lls_opt_result(LSG_WRITE_CMD_ ## _cmd ## _OPT_ ## _opt, _lpr))
+#define WRITE_CMD_OPT_GIVEN(_cmd, _opt, _lpr) \
+ (lls_opt_given(WRITE_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+#define WRITE_CMD_OPT_UINT32_VAL(_cmd, _opt, _lpr) \
+ (lls_uint32_val(0, WRITE_CMD_OPT_RESULT(_cmd, _opt, (_lpr))))
+#define WRITE_CMD_OPT_STRING_VAL(_cmd, _opt, _lpr) \
+ (lls_string_val(0, WRITE_CMD_OPT_RESULT(_cmd, _opt, (_lpr))))
-/** The writer structure for each supported writer. */
-extern struct writer writers[NUM_SUPPORTED_WRITERS];
+int check_writer_arg_or_die(const char *wa, struct lls_parse_result **lprp);
+const struct writer *writer_get(int wid);
+const char *writer_name(int wid);
+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);
+void get_btr_channels(struct btr_node *btrn, int32_t *result);
+void get_btr_sample_format(struct btr_node *btrn, int32_t *result);
+void print_writer_helps(bool detailed);
/** \file write_common.c common functions of para_audiod and para_write */
#include <regex.h>
+#include <lopsub.h>
+#include "write_cmd.lsg.h"
#include "para.h"
#include "string.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "write.h"
#include "error.h"
-#include "write_common.h"
-/** the array containing the names of all supported writers */
-const char *writer_names[] ={WRITER_NAMES};
+/** Loop over all writers. */
+#define FOR_EACH_WRITER(i) for (i = 1; lls_cmd(i, write_cmd_suite); i++)
-/** the array of supported writers */
-struct writer writers[NUM_SUPPORTED_WRITERS] = {WRITER_ARRAY};
-/**
- * Call the init function of each supported paraslash writer.
- */
-void writer_init(void)
+static inline bool writer_supported(int wid)
+{
+ return lls_user_data(WRITE_CMD(wid));
+}
+
+/* simply return the first available writer */
+static int default_writer_id(void)
{
int i;
FOR_EACH_WRITER(i)
- writers[i].init(&writers[i]);
+ if (writer_supported(i))
+ return i;
+ assert(0); /* the file writer should always be available */
}
+
/**
- * Check if given string is a valid command line for any writer.
+ * Return the writer structure from a writer ID.
*
- * \param \wa String of the form writer_name:options.
- * \param writer_num Contains the number of the writer upon success.
+ * \param wid If non-positive, a pointer to the default writer is returned.
*
- * This function checks whether \a wa starts with the name of a supported
- * paraslash writer, optionally followed by a colon and any options for that
- * writer. If a valid writer name was found and further are present, the
- * remaining part of \a wa is passed to that writer's config parser.
+ * \return Pointer to a (constant) struct writer.
+ */
+const struct writer *writer_get(int wid)
+{
+ if (wid < 0)
+ wid = default_writer_id();
+ return lls_user_data(WRITE_CMD(wid));
+}
+
+/**
+ * Return name of the writer identified by a writer ID.
+ *
+ * \param wid If non-positive, the name of the default writer is returned.
*
- * \return On success, a pointer to the gengetopt args info struct is returned
- * and \a writer_num contains the number of the writer. Otherwise this function
- * prints an error message and calls exit().
+ * \return The returned buffer must not be freed by the caller.
*/
-void *check_writer_arg_or_die(const char *wa, int *writer_num)
+const char *writer_name(int wid)
{
- int i, ret, argc;
- const char *cmdline;
- char **argv;
- void *conf;
+ if (wid <= 0)
+ wid = default_writer_id();
+ return lls_command_name(WRITE_CMD(wid));
+}
- if (!wa || !*wa) {
- i = DEFAULT_WRITER;
- cmdline = NULL;
- goto check;
- }
- PARA_INFO_LOG("checking %s\n", wa);
- FOR_EACH_WRITER(i) {
- const char *name = writer_names[i];
- size_t len = strlen(name);
- char c;
+/**
+ * Check if the given string is a valid command line for any writer.
+ *
+ * \param wa String of the form writer_name options.
+ * \param lprp Contains the parsed command line on success.
+ *
+ * If wa is \p NULL, the (configuration-dependent) default writer is assumed.
+ * Otherwise, the function checks whether \a wa starts with the name of a
+ * supported writer. If a valid writer name was found, the rest of the command
+ * line is passed to the config parser of this writer.
+ *
+ * \return On success, the positive writer ID is returned. Otherwise the
+ * function prints an error message and calls exit().
+ */
+int check_writer_arg_or_die(const char *wa, struct lls_parse_result **lprp)
+{
+ int ret, writer_num, argc;
+ char **argv = NULL, *errctx = NULL;
+ const struct lls_command *cmd;
- if (strlen(wa) < len)
- continue;
- if (strncmp(name, wa, len))
- continue;
- c = wa[len];
- if (!c || c == ' ') {
- cmdline = c? wa + len + 1 : NULL;
- goto check;
- }
- }
- PARA_EMERG_LOG("invalid writer %s\n", wa);
- exit(EXIT_FAILURE);
-check:
- ret = create_shifted_argv(cmdline, " \t", &argv);
- if (ret < 0) {
- PARA_EMERG_LOG("%s: %s\n", wa, para_strerror(-ret));
- exit(EXIT_FAILURE);
+ if (!wa || !*wa) {
+ writer_num = default_writer_id();
+ cmd = WRITE_CMD(writer_num);
+ argv = para_malloc(2 * sizeof(char *));
+ argc = 1;
+ argv[0] = para_strdup(lls_command_name(cmd));
+ argv[1] = NULL;
+ goto parse;
}
+ ret = create_argv(wa, " \t\n", &argv);
+ if (ret < 0)
+ goto fail;
argc = ret;
- argv[0] = make_message("%s_write", writer_names[i]);
- *writer_num = i;
- conf = writers[i].parse_config_or_die(argc, argv);
+ ret = lls(lls_lookup_subcmd(argv[0], write_cmd_suite, &errctx));
+ if (ret < 0)
+ goto free_argv;
+ writer_num = ret;
+ cmd = WRITE_CMD(writer_num);
+ if (!writer_supported(writer_num)) {
+ ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+ errctx = make_message("%s writer is not supported",
+ lls_command_name(cmd));
+ goto free_argv;
+ }
+parse:
+ ret = lls(lls_parse(argc, argv, cmd, lprp, &errctx));
+ if (ret >= 0)
+ ret = writer_num;
+free_argv:
free_argv(argv);
- return conf;
+ if (ret >= 0)
+ return ret;
+fail:
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+ exit(EXIT_FAILURE);
}
/**
* \param wn The writer node to open.
* \param parent The parent btr node (the source for the writer node).
* \param s The scheduler instance to register the task to.
- *
- * The configuration of the writer node stored in \p wn->conf must be
- * initialized before this function may be called.
*/
void register_writer_node(struct writer_node *wn, struct btr_node *parent,
struct sched *s)
{
- struct writer *w = writers + wn->writer_num;
+ const struct writer *w = writer_get(wn->wid);
wn->btrn = btr_new_node(&(struct btr_node_description)
- EMBRACE(.name = writer_names[wn->writer_num], .parent = parent,
+ EMBRACE(.name = writer_name(wn->wid), .parent = parent,
.handler = w->execute, .context = wn));
wn->task = task_register(&(struct task_info) {
- .name = writer_names[wn->writer_num],
+ .name = writer_name(wn->wid),
.pre_select = w->pre_select,
.post_select = w->post_select,
.context = wn,
/**
* Print the help text of all writers to stdout.
*
- * \param flags Passed to \ref ggo_print_help().
+ * \param detailed Whether to print the short or the detailed help.
*/
-void print_writer_helps(unsigned flags)
+void print_writer_helps(bool detailed)
{
int i;
- printf_or_die("\nAvailable writers: ");
- FOR_EACH_WRITER(i)
- printf_or_die("%s%s", i? " " : "", writer_names[i]);
- printf_or_die("\n");
+ printf("\nAvailable writers: ");
FOR_EACH_WRITER(i) {
- struct writer *w = writers + i;
-
- if (!w->help.short_help)
+ if (!writer_supported(i))
+ continue;
+ printf("%s%s", i? " " : "", writer_name(i));
+ }
+ printf("\n");
+ FOR_EACH_WRITER(i) {
+ const struct lls_command *cmd = WRITE_CMD(i);
+ char *help;
+ if (!writer_supported(i))
+ continue;
+ help = detailed? lls_long_help(cmd) : lls_short_help(cmd);
+ if (!help)
continue;
- printf_or_die("\n%s: %s", writer_names[i],
- w->help.purpose);
- ggo_print_help(&w->help, flags);
+ printf("%s\n", help);
+ free(help);
}
}
+++ /dev/null
-/*
- * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file write_common.h Exported symbols from write_common.c. */
-
-void writer_init(void);
-void *check_writer_arg_or_die(const char *wa, int *writer_num);
-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);
-void get_btr_channels(struct btr_node *btrn, int32_t *result);
-void get_btr_sample_format(struct btr_node *btrn, int32_t *result);