From: Andre Noll Date: Sun, 18 Jan 2009 23:29:44 +0000 (+0100) Subject: Merge commit 'athcx/master' X-Git-Tag: v0.3.4~75 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=002731cd3938f3be6b71651e56c062af1adcdec0;hp=6bab1680d9285d0cfc1a16978a1fcfb21b746bfe Merge commit 'athcx/master' --- diff --git a/.gitignore b/.gitignore index 18c71cf9..a23f234f 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,7 @@ web/sync *.swp error2.h web_sync +confdefs.h +conftest +conftest.c + diff --git a/Doxyfile b/Doxyfile index 45acb188..43209a52 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1060,7 +1060,6 @@ INCLUDE_FILE_PATTERNS = # instead of the = operator. PREDEFINED = HAVE_MAD \ - HAVE_ORTP \ HAVE_FAAD \ HAVE_OGGVORBIS \ __GNUC__=4 \ diff --git a/FEATURES b/FEATURES index 100a49c5..8e540a8e 100644 --- a/FEATURES +++ b/FEATURES @@ -6,7 +6,7 @@ Features * Runs on Linux, Mac OS, FreeBSD, NetBSD, Solaris and probably other Unixes * Mp3, ogg vorbis, aac(m4a) support - * Local or remote http, dccp, and ortp network audio streaming + * Local or remote http, dccp, and udp network audio streaming * IPv6 support * Volume normalizer * Stream grabbing at any point in the filter chain diff --git a/INSTALL b/INSTALL index 9cc36f41..b0bbbadb 100644 --- a/INSTALL +++ b/INSTALL @@ -11,11 +11,10 @@ See << REQUIREMENTS >> -for a list of required software. You don't need everything listed -there. In particular, mp3, ogg vorbis and aac, ortp support is -optional. Autoconf will detect what is installed on your system -and will only try to build those executables that can be built with -your setup. +for a list of required software. You don't need everything listed there. In +particular, mp3, ogg vorbis and aac support are all optional. Autoconf will +detect what is installed on your system and will only try to build those +executables that can be built with your setup. Note that no special library (not even the mp3 decoding library libmad) is needed for para_server if you only want to stream mp3 files. @@ -100,10 +99,10 @@ the directory /var/paraslash. Alternatively, use the --afs_socket Option to specify a different location for the afs command socket. -For this first try, we'll use a debug level of two to make the -output of para_server more verbose. +For this first try, we'll use the info loglevel to make the output +of para_server more verbose. - para_server -l 2 + para_server -l info Now you can use para_client to connect to the server and issue commands. Open a new shell (as "bar" on "client_host" in the above @@ -165,7 +164,7 @@ Paraslash comes with its own receiving and playing software, which will be described next. Try the following on client_host (assuming Linux/ALSA and an mp3 stream): - para_recv -l 2 -r 'http -i server_host' > file.mp3 + para_recv -l info -r 'http -i server_host' > file.mp3 # (interrupt with CTRL+C after a few seconds) ls -l file.mp3 # should not be empty para_filter -f mp3dec -f wav < file.mp3 > file.wav @@ -197,7 +196,7 @@ supposed to play the audio stream. Try for help. Usually you have to specify only server_host as the receiver specifier for each supported audio format, like this: - para_audiod -l 2 -r 'mp3:http -i server_host' + para_audiod -l info -r 'mp3:http -i server_host' The preferred way to use para_audiod is to run it once at system start as an unprivileged user. para_audiod needs to create a "well-known" diff --git a/Makefile.in b/Makefile.in index 4e6ef962..670be971 100644 --- a/Makefile.in +++ b/Makefile.in @@ -5,7 +5,8 @@ BINDIR := @bindir@ VARDIR := /var/paraslash PKGDATADIR := @datarootdir@/@PACKAGE_NAME@ MANDIR := @datarootdir@/man/man1 - +PACKAGE_VERSION := @PACKAGE_VERSION@ +PACKAGE_STRING := @PACKAGE_STRING@ install_sh := @install_sh@ build_date := $(shell date) @@ -59,15 +60,22 @@ BINARIES = para_server para_client para_audioc para_recv \ man_binaries := $(BINARIES) man_pages := $(patsubst %, man/man1/%.1, $(man_binaries)) man_pages_in := $(patsubst %, web/%.man.in.html, $(man_binaries)) -gengetopts := $(wildcard *.ggo) -gengetopts_c := $(gengetopts:.ggo=.cmdline.c) -gengetopts_h := $(gengetopts:.ggo=.cmdline.h) + +ggo_dir := ggo + +m4_ggos := afh audioc audiod client filter fsck gui recv server write +all_ggos := $(m4_ggos) dccp_recv oggdec_filter alsa_write fade http_recv \ + osx_write udp_recv amp_filter compress_filter file_write \ + grab_client mp3dec_filter +ggo_generated := $(addsuffix .cmdline.c, $(all_ggos)) $(addsuffix .cmdline.h, $(all_ggos)) \ + $(addsuffix .ggo, $(addprefix $(ggo_dir)/,$(m4_ggos))) + autocrap := config.h.in configure -tarball_pfx := @PACKAGE_TARNAME@-@PACKAGE_VERSION@ +tarball_pfx := @PACKAGE_TARNAME@-$(PACKAGE_VERSION) tarball_delete = web versions pics .changelog_before_cvs .changelog_cvs .gitignore tarball_delete := $(patsubst %,$(tarball_pfx)/%,$(tarball_delete)) -tarball_add := $(gengetopts_c) $(gengetopts_h) $(autocrap) -tarball := @PACKAGE_TARNAME@-@PACKAGE_VERSION@.tar.bz2 +tarball_add := $(ggo_generated) $(autocrap) +tarball := @PACKAGE_TARNAME@-$(PACKAGE_VERSION).tar.bz2 .PHONY: clean distclean maintainer-clean install man tarball all: $(BINARIES) $(man_pages) @@ -77,58 +85,7 @@ tarball: $(tarball) *.o: para.h config.h gcc-compat.h include Makefile.deps - -module_ggo_opts := --set-version="(@PACKAGE_STRING@, $(codename))" - -grab_client.cmdline.h grab_client.cmdline.c: grab_client.ggo - gengetopt $(module_ggo_opts) \ - -S \ - --set-package=grab \ - --no-handle-help \ - --no-handle-error \ - --no-handle-version \ - --arg-struct-name=grab_client_args_info \ - --file-name=$(subst .ggo,,$<).cmdline \ - --func-name $(subst .ggo,,$<)_cmdline_parser < $< - -%_recv.cmdline.h %_recv.cmdline.c: %_recv.ggo - gengetopt $(module_ggo_opts) \ - --set-package=$(subst .ggo,,$<) \ - --arg-struct-name=$(subst .ggo,,$<)_args_info \ - --file-name=$(subst .ggo,,$<).cmdline \ - --func-name $(subst .ggo,,$<)_cmdline_parser < $< - -%_filter.cmdline.h %_filter.cmdline.c: %_filter.ggo - gengetopt $(module_ggo_opts) \ - --set-package=$(subst .ggo,,$<) \ - --arg-struct-name=$(subst .ggo,,$<)_args_info \ - --file-name=$(subst .ggo,,$<).cmdline \ - --func-name $(subst _filter.ggo,,$<)_cmdline_parser < $< -%_write.cmdline.h %_write.cmdline.c: %_write.ggo - gengetopt -S $(module_ggo_opts) \ - --set-package=$(subst .ggo,,$<) \ - --arg-struct-name=$(subst .ggo,,$<)_args_info \ - --file-name=$(subst .ggo,,$<).cmdline \ - --func-name $(subst _write.ggo,,$<)_cmdline_parser < $< - -%.cmdline.h %.cmdline.c: %.ggo - case $< in client.ggo) O="--unamed-opts=command";; \ - audioc.ggo) O="--unamed-opts=command";; \ - fsck.ggo) O="--unamed-opts=table";; \ - afh.ggo) O="--unamed-opts=audio_file";; \ - recv.ggo) O="--no-handle-help";; \ - filter.ggo) O="--no-handle-help";; \ - write.ggo) O="--no-handle-help";; \ - audiod.ggo) O="--no-handle-help";; \ - esac; \ - if test $< != fsck.ggo; then O="$$O --conf-parser "; fi; \ - gengetopt $$O \ - --no-handle-version \ - --file-name=$(*F).cmdline \ - --func-name $(*F)_cmdline_parser \ - --arg-struct-name=$(*F)_args_info \ - --set-package="para_$(subst .cmdline,,$(*F))" \ - --set-version="@PACKAGE_VERSION@" < $< +include $(ggo_dir)/makefile %_command_list.c %_command_list.h: %.cmd ./command_util.sh c < $< >$@ @@ -147,21 +104,9 @@ man/man1/para_audiod.1: para_audiod audiod_command_list.man mkdir -p man/man1 help2man -h --detailed-help -N -i audiod_command_list.man ./para_audiod > $@ -man/man1/para_filter.1: para_filter - mkdir -p man/man1 - help2man -h --detailed-help -N ./$< > $@ - -man/man1/para_write.1: para_write - mkdir -p man/man1 - help2man -h --detailed-help -N ./$< > $@ - -man/man1/para_recv.1: para_recv - mkdir -p man/man1 - help2man -h --detailed-help -N ./$< > $@ - man/man1/%.1: % mkdir -p man/man1 - help2man -N ./$< > $@ + help2man -h --detailed-help -N ./$< > $@ man/html/%.html: man/man1/%.1 mkdir -p man/html @@ -171,12 +116,6 @@ web/%.man.in.html: man/man1/%.1 man2html $< | sed -e '/^<\/BODY>/,$$d' -e '1,/<\/HEAD>/d' > $@ -ortp_recv.o: ortp_recv.c - $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @ortp_cppflags@ $< - -ortp_send.o: ortp_send.c - $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @ortp_cppflags@ $< - oggdec_filter.o: oggdec_filter.c $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @oggvorbis_cppflags@ $< ogg_afh.o: ogg_afh.c @@ -242,7 +181,7 @@ distclean: clean rm -f GPATH GRTAGS GSYMS GTAGS maintainer-clean: distclean - rm -f $(gengetopts_c) $(gengetopts_h) *.tar.bz2 \ + rm -f $(ggo_generated) *.tar.bz2 \ config.h configure \ config.h.in skencil/*.pdf skencil/*.ps rm -f *_command_list.* *.man man/man1/* @@ -254,7 +193,7 @@ install: all man $(install_sh) -m 644 $(man_pages) $(MANDIR) mkdir -p $(VARDIR) >/dev/null 2>&1 || true # not fatal, so don't complain -@PACKAGE_TARNAME@-@PACKAGE_VERSION@.tar.bz2: $(tarball_add) +@PACKAGE_TARNAME@-$(PACKAGE_VERSION).tar.bz2: $(tarball_add) rm -rf $(tarball_pfx).tar.bz2 $(tarball_pfx) git archive --format=tar --prefix=$(tarball_pfx)/ HEAD \ | tar --delete $(tarball_delete) > $(tarball_pfx).tar diff --git a/NEWS b/NEWS index 8418cd8a..5464bd7a 100644 --- a/NEWS +++ b/NEWS @@ -4,11 +4,28 @@ NEWS ---------------------------------------------- 0.3.4 (to be announced) "elliptic inheritance" ---------------------------------------------- + +The new udp sender, colored logs and various other improvements. + + - The udp sender replaces the ortp sender. The new code is + both smaller and cleaner than the old ortp sender/receiver + code. As the udp sender does not depend on any special + libraries, it is built unconditionally. The default port + for udp streaming now defaults to 8000, like for the http + and the dccp senders/receivers. + - Loglevels are now specified as symbolic names, e.g. + "--loglevel info". + - para_server/para_audiod: Color support for log messages. - new options for mp3dec: --ignore-crc, --bufsize + - new audiod option: --config-file. + - gengetopt cleanups. - Improved help/man pages: The documentation of para_audiod, para_recv, para_filter and para_write now also contains - all options of the available receivers/filters/writers. - - new audiod option: --config-file. + all options of the available receivers/filters/writers. The + man page of para_fade contains a description of the different + modes of operation. + - More source code documentation. + - vss timing fixes. -------------------------------------------- 0.3.3 (2008-12-01) "axiomatic perspectivity" diff --git a/README b/README index 3308d0b3..dd0dc76a 100644 --- a/README +++ b/README @@ -18,7 +18,7 @@ accepts commands such as play, stop, pause, next from authenticated clients. However, there are many more commands. It supports three built-in network streaming methods (senders): http, dccp, -or rtp. +or udp. * The http sender is recommended for public streams that can be played by any player like mpg123, xmms, itunes, winamp... @@ -26,7 +26,7 @@ or rtp. * The dccp sender requires kernel support for the datagram congestion control protocol. - * The ortp sender is recommended for multicast LAN streaming. + * The udp sender is recommended for multicast LAN streaming. It is possible to activate more than one sender simultaneously. @@ -58,7 +58,7 @@ Its features include Despite of all these features, paraslash is lightweight. The stripped binary of para_server with all its features compiled in -mp3/ogg/aac support, http/dccp/ortp support) is about 160K on i386 +mp3/ogg/aac support, http/dccp/udp support) is about 160K on i386 under Linux. para_audiod (see below) is even smaller. ----------- @@ -79,7 +79,7 @@ encrypted with a symmetric rc4 session key. para_recv --------- -A command line http/dccp/ortp stream grabber. The http mode of this +A command line http/dccp/udp stream grabber. The http mode of this tool can be used to receive data from any http streaming source. ----------- diff --git a/README.afs b/README.afs index 9e588a6a..ca01df6a 100644 --- a/README.afs +++ b/README.afs @@ -225,10 +225,10 @@ songs is Troubles? --------- -Use loglevel one (option -l 1 for most commands) to show debugging -info. Almost all paraslash executables have a brief online help which -is displayed by using the -h switch. The --detailed-help option prints -the full help text. +Use the debug loglevel (option -l debug for most commands) to show +debugging info. Almost all paraslash executables have a brief online +help which is displayed by using the -h switch. The --detailed-help +option prints the full help text. para_fsck tries to fix your database. Use --force (even if your name isn't Luke) to clean up after a crash. However, first make sure diff --git a/REQUIREMENTS b/REQUIREMENTS index b03c4c3f..c1743fd2 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -42,11 +42,6 @@ Optional features: http://www.audiocoding.com/. Debian package: libfaad-dev. - - *ortp*: - If you intend to use the optional ortp streamer, you'll - need libortp which is contained in the linphone source code: - http://www.linphone.org/index.php/eng - - On Linux, you'll need to have ALSA's development package installed. The Debian package is called libasound2-dev. @@ -58,3 +53,4 @@ Hacking the source: - grutatxt http://www.triptico.com/software/grutatxt.html - doxygen http://www.stack.nl/~dimitri/doxygen/ - global ftp://ftp.gnu.org/pub/gnu/global + - m4: ftp://ftp.gnu.org/pub/gnu/m4/ diff --git a/aac.h b/aac.h index ddb26e5e..1efe75ac 100644 --- a/aac.h +++ b/aac.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/aac_common.c b/aac_common.c index 290622ed..fda63460 100644 --- a/aac_common.c +++ b/aac_common.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/aacdec_filter.c b/aacdec_filter.c index 8eddc0e7..d9226b0d 100644 --- a/aacdec_filter.c +++ b/aacdec_filter.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/acl.c b/acl.c index a3e161db..72869ee8 100644 --- a/acl.c +++ b/acl.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/acl.h b/acl.h index 0e53ce05..60185937 100644 --- a/acl.h +++ b/acl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Andre Noll + * Copyright (C) 2008-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/afh.c b/afh.c index 24c2bc67..fad93c49 100644 --- a/afh.c +++ b/afh.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Andre Noll + * Copyright (C) 2008-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -21,7 +21,9 @@ static struct afh_args_info conf; const char *status_item_list[] = {STATUS_ITEM_ARRAY}; INIT_AFH_ERRLISTS; -INIT_STDERR_LOGGING(conf.loglevel_arg) + +static int loglevel; +INIT_STDERR_LOGGING(loglevel) static void print_info(int audio_format_num, struct afh_info *afhi) { @@ -139,6 +141,7 @@ int main(int argc, char **argv) afh_cmdline_parser(argc, argv, &conf); HANDLE_VERSION_FLAG("afh", conf); + loglevel = get_loglevel_by_name(conf.loglevel_arg); ret = -E_AFH_SYNTAX; if (conf.inputs_num == 0) goto out; diff --git a/afh.ggo b/afh.ggo deleted file mode 100644 index 056a34da..00000000 --- a/afh.ggo +++ /dev/null @@ -1,122 +0,0 @@ -text " -para_afh, the audio format handler tool, is a stand-alone program -contained in the paraslash package for analyzing and streaming audio -files. It can be used to - - - print tech info about the given audio file to stdout. - In particular, the 'chunk table' of the audio file, an array - of offsets within the audio file, may be printed. This table - can be used by other programs unaware of the particular audio - format to stream the audio file. - - - write selected parts of the given audio file in complete - chunks without decoding. Thus para_afh can be used to 'cut' - an audio file. - - - write selected parts of the given audio files 'just in time' - to sdout. This may be useful for third-party software that - is capable of reading from stdin. -" - -option "loglevel" l -#~~~~~~~~~~~~~~~~~~ -"set loglevel (0-6)" -int typestr="level" -default="4" -optional - -defgroup "mode" -#-------------- -groupdesc=" - There are two modes of operation: Info mode and stream mode, - one of which must be selected by the corresponding option. - See below. -" -required - -groupoption "info" i -#~~~~~~~~~~~~~~~~~~~ -"select info mode" -group="mode" -details=" - In this mode, the program prints technical information about - the given audio file to stdout. -" - -groupoption "stream" s -#~~~~~~~~~~~~~~~~~~~~~ -"select stream mode" -group="mode" -details=" - If this mode is selected, the selected parts of the content - of the audio file are written to stdout. Only complete chunks - with respect of the underlying audio format are written. - For example, only complete frames in case of mp3 files. -" - -section "Options for info mode" -#============================== - -option "chunk_table" c -#~~~~~~~~~~~~~~~~~~~~~ -"print also the chunk table" -flag off -dependon="info" - -section "Options for stream mode" -#================================ - - -option "begin_chunk" b -#~~~~~~~~~~~~~~~~~~~~~ -"skip a number of chunks" -int typestr="chunk_num" -default="0" -dependon="stream" -optional -details=" - The chunk_num argument must be between -num_chunks and - num_chunks - 1 inclusively where num_chunks is the total number - of chunks which is printed when using the --info option. If - chunk_num is negative, the given number of chunks are counted - backwards from the end of the file. For example --begin_chunk - -100 instructs para_afh to start output at chunk num_chunks - - 100. This is mainly useful for cutting off the end of an - audio file. -" - -option "end_chunk" e -#~~~~~~~~~~~~~~~~~~~ -"only write up to chunk chunk_num" -int typestr="chunk_num" -dependon="stream" -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 -dependon="stream" -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 uninterupted - audio stream. -" - -option "no_header" H -#~~~~~~~~~~~~~~~~~~~ -"do not write an audio file header" -flag off -dependon="stream" -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. -" diff --git a/afh.h b/afh.h index e315f787..4e2020bb 100644 --- a/afh.h +++ b/afh.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/afh_common.c b/afh_common.c index bec99bf4..f7066cfc 100644 --- a/afh_common.c +++ b/afh_common.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2008 Andre Noll + * Copyright (C) 1997-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/afs.c b/afs.c index 5f47941e..b8acb53a 100644 --- a/afs.c +++ b/afs.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -766,6 +766,7 @@ static void signal_post_select(struct sched *s, struct task *t) st->signum = para_next_signal(); if (st->signum == SIGHUP) { close_afs_tables(); + parse_config_or_die(1); t->error = open_afs_tables(); if (t->error < 0) return; @@ -1174,6 +1175,16 @@ int com_check(int fd, int argc, char * const * const argv) return 1; } +/** + * The afs event dispatcher. + * + * \param event Type of the event. + * \param pb May be \p NULL. + * \param data Type depends on \a event. + * + * This function calls the table handlers of all tables and passes \a pb and \a + * data verbatim. It's up to the handlers to interpret the \a data pointer. + */ void afs_event(enum afs_events event, struct para_buffer *pb, void *data) { diff --git a/afs.h b/afs.h index a596317f..267cc8d3 100644 --- a/afs.h +++ b/afs.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -61,14 +61,23 @@ enum afs_events { BLOB_REMOVE, }; +/** + * Used as data for \ref afs_event() for events of type \p ATTRIBUTE_ADD. + */ struct rmatt_event_data { + /** The name of the attribute being added. */ const char *name; + /** Its bit number. */ unsigned char bitnum; }; - +/** + * Used as data for \ref afs_event() for events of type \p ATTRIBUTE_AFSI_CHANGE. + */ struct afsi_change_event_data { + /** Pointer to the row that has changed. */ struct osl_row *aft_row; + /** Afs info before the change. */ struct afs_info *old_afsi; }; @@ -87,7 +96,6 @@ struct afs_table { /** Handles afs events. */ int (*event_handler)(enum afs_events event, struct para_buffer *pb, void *data); - /* int *(check)() */ }; enum play_mode {PLAY_MODE_MOOD, PLAY_MODE_PLAYLIST}; diff --git a/aft.c b/aft.c index d5a35556..7bfad504 100644 --- a/aft.c +++ b/aft.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -1008,6 +1008,13 @@ out: return ret; } +/** + * Write a list of audio-file related status items with empty values. + * + * \param buf Result pointer. + * + * This is used by vss when currently no audio file is open. + */ void make_empty_status_items(char *buf) { sprintf(buf, diff --git a/alsa_write.ggo b/alsa_write.ggo deleted file mode 100644 index 84f49d58..00000000 --- a/alsa_write.ggo +++ /dev/null @@ -1,34 +0,0 @@ -option "device" d -#~~~~~~~~~~~~~~~~ -"set PCM device" -string typestr="device" -default="default" -optional -details=" - On systems with dmix, a better choice than the default - value might be to use \"plug:swmix\". -" - -option "channels" c -#~~~~~~~~~~~~~~~~~~ -"specify number of channels" -int typestr="num" -default="2" -optional -details=" - This option is only necessary for playing raw audio with - para_write. In all other cases (plaing wav files with - para_write or using this writer with para_audiod), the number - of channels will be obtained from other resources. -" - -option "samplerate" s -#~~~~~~~~~~~~~~~~~~~~~ -"force given sample rate" -int typestr="num" -default="44100" -optional -details=" - Again, it is only necessary to specify this when playing raw - audio with para_write. -" diff --git a/amp_filter.c b/amp_filter.c index a88719e3..a011c411 100644 --- a/amp_filter.c +++ b/amp_filter.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Andre Noll + * Copyright (C) 2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/amp_filter.ggo b/amp_filter.ggo deleted file mode 100644 index 865df3ac..00000000 --- a/amp_filter.ggo +++ /dev/null @@ -1,17 +0,0 @@ -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. -" diff --git a/attribute.c b/attribute.c index 655c2866..d22511de 100644 --- a/attribute.c +++ b/attribute.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2008 Andre Noll + * Copyright (C) 1997-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -598,7 +598,11 @@ static int attribute_create(const char *dir) return osl_create_table(&attribute_table_desc); } - +/** + * Initialize the attribute table structure. + * + * \param t The table structure to initialize. + */ void attribute_init(struct afs_table *t) { t->name = attribute_table_desc.name; diff --git a/audioc.c b/audioc.c index a8f88e31..666990db 100644 --- a/audioc.c +++ b/audioc.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -21,7 +21,8 @@ INIT_AUDIOC_ERRLISTS; /** the gengetopt structure containing command line args */ struct audioc_args_info conf; -INIT_STDERR_LOGGING(conf.loglevel_arg); +static int loglevel; +INIT_STDERR_LOGGING(loglevel); static char *concat_args(unsigned argc, char * const *argv) { @@ -87,6 +88,7 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } } + loglevel = get_loglevel_by_name(conf.loglevel_arg); args = conf.inputs_num? concat_args(conf.inputs_num, conf.inputs) : para_strdup("stat"); diff --git a/audioc.ggo b/audioc.ggo deleted file mode 100644 index 87ef71e3..00000000 --- a/audioc.ggo +++ /dev/null @@ -1,21 +0,0 @@ -option "loglevel" l -#~~~~~~~~~~~~~~~~~~ -"set loglevel (0-6)" - int typestr="level" - default="4" - optional - - -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 diff --git a/audiod.c b/audiod.c index 63d42cac..a92c4e3b 100644 --- a/audiod.c +++ b/audiod.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -83,7 +83,6 @@ int audiod_status = AUDIOD_ON; struct audiod_args_info conf; static char *socket_name; -static FILE *logfile; static struct audio_format_info afi[NUM_AUDIO_FORMATS]; static struct signal_task signal_task_struct, *sig_task = &signal_task_struct; @@ -181,42 +180,27 @@ out: ); } -/** - * the log function of para_audiod - * - * \param ll loglevel - * \param fmt the format string - */ -__printf_2_3 void para_log(int ll, const char* fmt,...) +static int want_colors(void) { - va_list argp; - FILE *outfd; - struct tm *tm; - time_t t1; - char str[MAXLINE] = ""; - static char *hostname; - - if (ll < conf.loglevel_arg) - return; - if (!logfile && conf.daemon_given) - return; - if (!hostname) - hostname = para_hostname(); - outfd = logfile? logfile : stderr; - time(&t1); - tm = localtime(&t1); - strftime(str, MAXLINE, "%b %d %H:%M:%S", tm); - fprintf(outfd, "%s %s ", str, hostname); - if (conf.loglevel_arg <= INFO) - fprintf(outfd, "%i ", ll); - va_start(argp, fmt); - vfprintf(outfd, fmt, argp); - va_end(argp); + if (conf.color_arg == color_arg_no) + return 0; + if (conf.color_arg == color_arg_yes) + return 1; + if (conf.logfile_given) + return 0; + return isatty(STDERR_FILENO); } static void parse_config_or_die(void) { 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); @@ -229,20 +213,12 @@ static void parse_config_or_die(void) PARA_EMERG_LOG("can not read config file %s\n", config_file); goto err; } - if (config_file) { - struct audiod_cmdline_parser_params params = { - .override = 0, - .initialize = 0, - .check_required = 1, - .check_ambiguity = 0, - .print_errors = 1 - }; - if (audiod_cmdline_parser_config_file(config_file, &conf, ¶ms)) { - PARA_EMERG_LOG("parse error in config file\n"); - goto err; - } + if (audiod_cmdline_parser_config_file(config_file, &conf, ¶ms)) { + PARA_EMERG_LOG("parse error in config file\n"); + goto err; } free(config_file); + daemon_set_loglevel(conf.loglevel_arg); return; err: free(config_file); @@ -1109,6 +1085,20 @@ __noreturn static void print_help_and_die(void) exit(0); } +static void init_colors_or_die(void) +{ + int ret, i; + + if (!want_colors()) + return; + daemon_set_default_log_colors(); + daemon_set_flag(DF_COLOR_LOG); + for (i = 0; i < conf.log_color_given; i++) { + ret = daemon_set_log_color(conf.log_color_arg[i]); + if (ret < 0) + exit(EXIT_FAILURE); + } +} /** * the main function of para_audiod @@ -1136,18 +1126,24 @@ int main(int argc, char *argv[]) valid_fd_012(); audiod_cmdline_parser_ext(argc, argv, &conf, ¶ms); HANDLE_VERSION_FLAG("audiod", conf); - para_drop_privileges(conf.user_arg, conf.group_arg); + drop_privileges_or_die(conf.user_arg, conf.group_arg); parse_config_or_die(); - if (conf.logfile_given) - logfile = open_log(conf.logfile_arg); - i = init_stream_io(); if (conf.help_given || conf.detailed_help_given) print_help_and_die(); + daemon_set_flag(DF_LOG_TIME); + daemon_set_flag(DF_LOG_HOSTNAME); + daemon_set_flag(DF_LOG_LL); + if (conf.logfile_given) { + daemon_set_logfile(conf.logfile_arg); + daemon_open_log_or_die(); + } + init_colors_or_die(); + i = init_stream_io(); if (i < 0) { PARA_EMERG_LOG("init stream io error: %s\n", para_strerror(-i)); exit(EXIT_FAILURE); } - log_welcome("para_audiod", conf.loglevel_arg); + log_welcome("para_audiod"); server_uptime(UPTIME_SET); set_initial_status(); FOR_EACH_SLOT(i) @@ -1160,7 +1156,7 @@ int main(int argc, char *argv[]) init_command_task(cmd_task); if (conf.daemon_given) - daemon_init(); + daemonize(); register_task(&sig_task->task); register_task(&cmd_task->task); diff --git a/audiod.ggo b/audiod.ggo deleted file mode 100644 index 8f960c67..00000000 --- a/audiod.ggo +++ /dev/null @@ -1,221 +0,0 @@ -######################### -section "General options" -######################### - -text " - These options are identical to their counterparts in para_server - and are discussed in detail there. -" - -option "loglevel" l -#~~~~~~~~~~~~~~~~~~ -"set loglevel (1-6)" -int typestr="level" -default="4" -optional - -option "config_file" c -#~~~~~~~~~~~~~~~~~~~~~ -"(default='~/.paraslash/audiod.conf'" -string typestr="filename" -optional - -option "logfile" L -#~~~~~~~~~~~~~~~~~ -"where to write log output" -string typestr="filename" -optional - -option "daemon" d -#~~~~~~~~~~~~~~~~ -"run as background daemon" -flag off -dependon="logfile" - -option "user" u -#~~~~~~~~~~~~~~ -"run as the given user" -string typestr="name" -optional - -option "group" g -#~~~~~~~~~~~~~~~ -"set group id" -string typestr="group" -optional - -######################## -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.. - - 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 uid" -int typestr="uid" -default="-1" -optional -multiple -details=" - Allow the user identified by \"uid\" to connect to para_audiod. - May be specified multiple times. If not specified at all, - all users are allowed to connect. - - This feature requires unix socket credentials and is currently - only supported on Linux systems. On other operating systems, - the option is silently ignored and all local users are allowed - to connect to para_audiod. -" - -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 syncronized - 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, once for each - supported audio format. The \"receiver_spec\" consists of - an audio format and the receiver name, separated by a colon, - and any options for that receiver, seperated by whitespace. - If any receiver options are present, the whole receiver - argument must be quoted. - - Example: - - -r 'mp3:http -i my.host.org -p 8009' -" - - -option "no_default_filters" D -#~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"Configure filters manually" -flag off -details=" - If (and only if) this option is set, the --filter options - (see below) take effect. Otherwise, the compiled-in default - filters mp3dec, oggdec and aacdec are activated for mp3, ogg, - aac streams respectively. -" - -option "filter" f -#~~~~~~~~~~~~~~~~ -"Use non-default filters" -string typestr="filter_spec" -optional -multiple -dependon="no_default_filters" -details=" - This option may be given multiple times. The \"filter_spec\" - consists of an audio format, the name of the filter, and any - options for that filter. Mote that order matters. - - Examples: - - --filter 'mp3:mp3dec' - - --filter 'mp3:compress --inertia 5 --damp 2' - -" - -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:osx' - -" - -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 syncronizing the audio output of - different clients. -" diff --git a/audiod.h b/audiod.h index 60de4caf..5bd41c4f 100644 --- a/audiod.h +++ b/audiod.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/audiod_command.c b/audiod_command.c index 27ef63ca..a6f67762 100644 --- a/audiod_command.c +++ b/audiod_command.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/bash_completion b/bash_completion index aeee64c1..cd647e20 100644 --- a/bash_completion +++ b/bash_completion @@ -2,7 +2,7 @@ # # Licensed under the GPL v2. For licencing details see COPYING. -PC="para_client -l 5 -- " +PC="para_client -l error -- " __para_commandlist= __para_sender_list= diff --git a/blob.c b/blob.c index fd8ddc80..1742ac42 100644 --- a/blob.c +++ b/blob.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/chunk_queue.c b/chunk_queue.c index 571f8d18..fa317185 100644 --- a/chunk_queue.c +++ b/chunk_queue.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/chunk_queue.h b/chunk_queue.h index 9959b74c..2bed999c 100644 --- a/chunk_queue.h +++ b/chunk_queue.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/client.c b/client.c index 4208579b..fdd07b24 100644 --- a/client.c +++ b/client.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2008 Andre Noll + * Copyright (C) 1997-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -54,7 +54,8 @@ static struct task svt = { .status = "supervisor task" }; -INIT_STDERR_LOGGING(ct->conf.loglevel_arg); +static int client_loglevel; /* loglevel */ +INIT_STDERR_LOGGING(client_loglevel); /** @@ -82,6 +83,7 @@ int main(int argc, char *argv[]) ret = client_open(argc, argv, &ct); if (ret < 0) /* can not use PARA_LOG here because ct is NULL */ exit(EXIT_FAILURE); + client_loglevel = get_loglevel_by_name(ct->conf.loglevel_arg); register_task(&svt); ret = schedule(&s); if (ret < 0) diff --git a/client.ggo b/client.ggo deleted file mode 100644 index 1d6b410d..00000000 --- a/client.ggo +++ /dev/null @@ -1,9 +0,0 @@ -# file client.conf -args "--no-handle-error" -option "hostname" i "ip or host to connect" string typestr="host" default="localhost" optional -option "user" u "paraslash username" string typestr="username" default="" optional -option "server_port" p "port to connect" int typestr="port" default="2990" optional -option "key_file" k "(default='~/.paraslash/key.')" string typestr="filename" optional -option "loglevel" l "set loglevel (0-6)" int typestr="number" default="5" optional -option "config_file" c "(default='~/.paraslash/client.conf')" string typestr="filename" optional -option "plain" - "request an uncrypted session" flag off diff --git a/client.h b/client.h index dcec3611..b813f186 100644 --- a/client.h +++ b/client.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2008 Andre Noll + * Copyright (C) 1997-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/client_common.c b/client_common.c index 9886cfe5..e757a3a7 100644 --- a/client_common.c +++ b/client_common.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2008 Andre Noll + * Copyright (C) 1997-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -364,7 +364,7 @@ int client_open(int argc, char *argv[], struct client_task **ct_ptr) goto out; } ret = 1; - PARA_INFO_LOG("loglevel: %d\n", 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); PARA_NOTICE_LOG("connecting %s:%d\n", ct->conf.hostname_arg, diff --git a/close_on_fork.c b/close_on_fork.c index 4f7c2b95..299aab0b 100644 --- a/close_on_fork.c +++ b/close_on_fork.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/color.c b/color.c new file mode 100644 index 00000000..bd09446c --- /dev/null +++ b/color.c @@ -0,0 +1,132 @@ +/** \file color.c Functions for printing colored messages. */ + +/* + * Mostly taken from the git source tree, version 1.6.1.76, January 2008. + */ + +#include "para.h" +#include "color.h" + +static int parse_color(const char *name, int len) +{ + static const char * const color_names[] = { + "normal", "black", "red", "green", "yellow", + "blue", "magenta", "cyan", "white" + }; + char *end; + int i; + for (i = 0; i < ARRAY_SIZE(color_names); i++) { + const char *str = color_names[i]; + if (!strncasecmp(name, str, len) && !str[len]) + return i - 1; + } + i = strtol(name, &end, 10); + if (end - name == len && i >= -1 && i <= 255) + return i; + return -2; +} + +static int parse_attr(const char *name, int len) +{ + static const int attr_values[] = { 1, 2, 4, 5, 7 }; + static const char * const attr_names[] = { + "bold", "dim", "ul", "blink", "reverse" + }; + int i; + for (i = 0; i < ARRAY_SIZE(attr_names); i++) { + const char *str = attr_names[i]; + if (!strncasecmp(name, str, len) && !str[len]) + return attr_values[i]; + } + return -1; +} + +/** + * Create an escape sequence for colored output. + * + * \param value Human-readable color spec. + * \param dst Result pointer for the escape sequence. + * + * \return -1 on errors, 1 on success. + * + * Format of \a value: [fg [bg]] [attr]. + */ +int color_parse(const char *value, char *dst) +{ + const char *ptr = value; + int attr = -1; + int fg = -2; + int bg = -2; + + if (!strcasecmp(value, "reset")) { + strcpy(dst, COLOR_RESET); + return 1; + } + + /* [fg [bg]] [attr] */ + while (*ptr) { + const char *word = ptr; + int val, len = 0; + + while (word[len] && !isspace(word[len])) + len++; + + ptr = word + len; + while (*ptr && isspace(*ptr)) + ptr++; + + val = parse_color(word, len); + if (val >= -1) { + if (fg == -2) { + fg = val; + continue; + } + if (bg == -2) { + bg = val; + continue; + } + goto bad; + } + val = parse_attr(word, len); + if (val < 0 || attr != -1) + goto bad; + attr = val; + } + + if (attr >= 0 || fg >= 0 || bg >= 0) { + int sep = 0; + + *dst++ = '\033'; + *dst++ = '['; + if (attr >= 0) { + *dst++ = '0' + attr; + sep++; + } + if (fg >= 0) { + if (sep++) + *dst++ = ';'; + if (fg < 8) { + *dst++ = '3'; + *dst++ = '0' + fg; + } else { + dst += sprintf(dst, "38;5;%d", fg); + } + } + if (bg >= 0) { + if (sep++) + *dst++ = ';'; + if (bg < 8) { + *dst++ = '4'; + *dst++ = '0' + bg; + } else { + dst += sprintf(dst, "48;5;%d", bg); + } + } + *dst++ = 'm'; + } + *dst = 0; + return 1; +bad: + PARA_ERROR_LOG("bad color value '%s'\n", value); + return -1; +} diff --git a/color.h b/color.h new file mode 100644 index 00000000..12972b3a --- /dev/null +++ b/color.h @@ -0,0 +1,10 @@ +/** \file color.h Exported functions from color.c. */ + + +/** "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ +#define COLOR_MAXLEN 24 + +/** Switch back to normal colors. */ +#define COLOR_RESET "\033[m" + +int color_parse(const char *value, char *dst); diff --git a/command.c b/command.c index 3932ed22..9abe04a3 100644 --- a/command.c +++ b/command.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2008 Andre Noll + * Copyright (C) 1997-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -283,7 +283,7 @@ int com_si(int fd, int argc, __a_unused char * const * argv) "server_pid: %d\n" "afs_pid: %d\n" "connections (active/accepted/total): %u/%u/%u\n" - "current loglevel: %i\n" + "current loglevel: %s\n" "supported audio formats: %s\n" "supported senders: %s\n" "%s", diff --git a/compress_filter.c b/compress_filter.c index c7af3253..f853b633 100644 --- a/compress_filter.c +++ b/compress_filter.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/compress_filter.ggo b/compress_filter.ggo deleted file mode 100644 index 74dcbc03..00000000 --- a/compress_filter.ggo +++ /dev/null @@ -1,37 +0,0 @@ -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 diff --git a/configure.ac b/configure.ac index 664280ac..afb993af 100644 --- a/configure.ac +++ b/configure.ac @@ -83,17 +83,18 @@ daemon stat crypt http_send close_on_fork ipc acl afh fade amp_filter dccp_send fd user_list chunk_queue afs osl aft mood score attribute blob ringbuffer playlist sha1 rbtree sched audiod grab_client filter_common wav_filter compress_filter http_recv dccp_recv recv_common write_common file_write audiod_command -client_common recv stdout filter stdin audioc write client fsck exec send_common ggo" +client_common recv stdout filter stdin audioc write client fsck exec send_common ggo +udp_recv udp_send color" all_executables="server recv filter audioc write client fsck afh" -recv_cmdline_objs="recv.cmdline http_recv.cmdline dccp_recv.cmdline" +recv_cmdline_objs="recv.cmdline http_recv.cmdline dccp_recv.cmdline udp_recv.cmdline" recv_errlist_objs="http_recv recv_common recv time string net dccp_recv - fd sched stdout ggo" + fd sched stdout ggo udp_recv" recv_ldflags="" -receivers=" http dccp" -senders=" http dccp" +receivers=" http dccp udp" +senders=" http dccp udp" filter_cmdline_objs="filter.cmdline compress_filter.cmdline amp_filter.cmdline" filter_errlist_objs="filter_common wav_filter compress_filter filter string stdin stdout sched fd amp_filter ggo" @@ -106,11 +107,11 @@ audioc_ldflags="" audiod_cmdline_objs="audiod.cmdline grab_client.cmdline compress_filter.cmdline http_recv.cmdline dccp_recv.cmdline file_write.cmdline client.cmdline - audiod_command_list amp_filter.cmdline" + audiod_command_list amp_filter.cmdline udp_recv.cmdline" audiod_errlist_objs="audiod signal string daemon stat net time grab_client filter_common wav_filter compress_filter amp_filter http_recv dccp_recv recv_common fd sched write_common file_write audiod_command crypt - client_common ggo" + client_common ggo udp_recv color" audiod_ldflags="" audiod_audio_formats="" @@ -122,7 +123,7 @@ server_cmdline_objs="server.cmdline server_command_list afs_command_list" server_errlist_objs="server afh_common mp3_afh vss command net string signal time daemon stat crypt http_send close_on_fork ipc dccp_send fd user_list chunk_queue afs osl aft mood score attribute - blob playlist sha1 rbtree sched acl send_common" + blob playlist sha1 rbtree sched acl send_common udp_send color" server_ldflags="" server_audio_formats=" mp3" @@ -500,52 +501,6 @@ fi CPPFLAGS="$OLD_CPPFLAGS" LDFLAGS="$OLD_LDFLAGS" LIBS="$OLD_LIBS" -########################################################################### ortp -have_ortp="yes" -OLD_CPPFLAGS="$CPPFLAGS" -OLD_LD_FLAGS="$LDFLAGS" -OLD_LIBS="$LIBS" -AC_ARG_WITH(ortp_headers, [AC_HELP_STRING(--with-ortp-headers=dir, - [look for ortp/ortp.h also in dir])]) -if test -n "$with_ortp_headers"; then - ortp_cppflags="-I$with_ortp_headers" - CPPFLAGS="$CPPFLAGS $ortp_cppflags" -fi -AC_ARG_WITH(ortp_libs, [AC_HELP_STRING(--with-ortp-libs=dir, - [look for libortp also in dir])]) -if test -n "$with_ortp_libs"; then - ortp_libs="-L$with_ortp_libs" - LDFLAGS="$LDFLAGS $ortp_libs" -fi -AC_CHECK_HEADERS([ortp/ortp.h], [], [have_ortp="no"]) -AC_CHECK_LIB([ortp], [ortp_init], [], [have_ortp="no"]) -if test "$have_ortp" = "yes"; then - all_errlist_objs="$all_errlist_objs ortp_recv ortp_send" - - recv_cmdline_objs="$recv_cmdline_objs ortp_recv.cmdline" - recv_errlist_objs="$recv_errlist_objs ortp_recv" - - audiod_cmdline_objs="$audiod_cmdline_objs ortp_recv.cmdline" - audiod_errlist_objs="$audiod_errlist_objs ortp_recv" - - server_errlist_objs="$server_errlist_objs ortp_send" - - recv_ldflags="$recv_ldflags $ortp_libs -lortp" - server_ldflags="$server_ldflags $ortp_libs -lortp" - audiod_ldflags="$audiod_ldflags $ortp_libs -lortp" - - receivers="$receivers ortp" - senders="$senders ortp" - AC_DEFINE(HAVE_ORTP, 1, [define to 1 to turn on ortp support]) - AC_SUBST(ortp_cppflags) - AC_SUBST(ortp_libs) -else - AC_MSG_NOTICE([deactivating ortp sender/receiver]) -fi -CPPFLAGS="$OLD_CPPFLAGS" -LDFLAGS="$OLD_LDFLAGS" -LIBS="$OLD_LIBS" - AC_SUBST(extra_binaries, [$extras]) AC_SUBST(extra_filter_objs, [$extra_filter_objs]) @@ -735,7 +690,7 @@ AC_DEFINE_UNQUOTED(AUDIOD_AUDIO_FORMAT_ARRAY, $names, array of audio formats sup AC_OUTPUT AC_MSG_NOTICE([creating Makefile.deps]) -gcc -MM -MG $faad_cppflags $mad_cppflags $ortp_cppflags $oggvorbis_cppflags *.c > Makefile.deps +gcc -MM -MG $faad_cppflags $mad_cppflags $oggvorbis_cppflags *.c > Makefile.deps AC_MSG_NOTICE([ paraslash configuration: ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/crypt.c b/crypt.c index a19b5ac3..73eebe18 100644 --- a/crypt.c +++ b/crypt.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/crypt.h b/crypt.h index 7ac90677..88af9e89 100644 --- a/crypt.h +++ b/crypt.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/daemon.c b/daemon.c index bb75478a..cae63ea7 100644 --- a/daemon.c +++ b/daemon.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2008 Andre Noll + * Copyright (C) 1997-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -8,12 +8,141 @@ #include "para.h" #include "daemon.h" #include - -/* getgrnam() */ -#include +#include /* getgrnam() */ #include #include "string.h" +#include "color.h" + +/** The internal state of the daemon. */ +struct daemon { + /** See \ref daemon_flags. */ + unsigned flags; + /** Set by \ref daemon_set_logfile(). */ + char *logfile_name; + /** Current loglevel, see \ref daemon_set_loglevel(). */ + int loglevel; + + /** Used by \ref server_uptime() and \ref uptime_str(). */ + time_t startuptime; + /** The file pointer if the logfile is open. */ + FILE *logfile; + /** Used by para_log() if \p DF_LOG_HOSTNAME is set. */ + char *hostname; + /** Used for colored log messages. */ + char log_colors[NUM_LOGLEVELS][COLOR_MAXLEN]; +}; + +static struct daemon the_daemon, *me = &the_daemon; + +/** + * Activate default log colors. + * + * This should be called early if color support is wanted. + */ +void daemon_set_default_log_colors(void) +{ + int i; + static const char *default_log_colors[NUM_LOGLEVELS] = { + [LL_DEBUG] = "normal", + [LL_INFO] = "normal", + [LL_NOTICE] = "normal", + [LL_WARNING] = "yellow", + [LL_ERROR] = "red", + [LL_CRIT] = "magenta bold", + [LL_EMERG] = "red bold", + }; + for (i = 0; i < NUM_LOGLEVELS; i++) { + int ret = color_parse(default_log_colors[i], me->log_colors[i]); + assert(ret >= 0); + } +} + +/** + * Set the color for one loglevel. + * + * \param arg The loglevel/color specifier. + * + * \a arg must be of the form "ll:[fg [bg]] [attr]". + * + * \return 1 On success, -1 on errors. + */ +int daemon_set_log_color(char const *arg) +{ + char *p = strchr(arg, ':'); + int ret, ll; + + if (!p) + goto err; + ret = get_loglevel_by_name(arg); + if (ret < 0) + goto err; + ll = ret; + p++; + ret = color_parse(p, me->log_colors[ll]); + if (ret < 0) + goto err; + return 1; +err: + PARA_ERROR_LOG("%s: color syntax error\n", arg); + return -1; + +} + +/** + * Init or change the name of the log file. + * + * \param logfile_name The full path of the logfile. + */ +void daemon_set_logfile(char *logfile_name) +{ + free(me->logfile_name); + me->logfile_name = NULL; + if (logfile_name) + me->logfile_name = para_strdup(logfile_name); +} + +/** + * Supress log messages with severity lower than the given loglevel. + * + * \param loglevel The smallest level that should be logged. + */ +void daemon_set_loglevel(char *loglevel) +{ + int ret = get_loglevel_by_name(loglevel); + + assert(ret >= 0); + me->loglevel = ret; +} + +/** + * Set one of the daemon config flags. + * + * \param flag The flag to set. + * + * \sa \ref daemon_flags. + */ +void daemon_set_flag(unsigned flag) +{ + me->flags |= flag; +} + +/** + * Clear one of the daemon config flags. + * + * \param flag The flag to clear. + * + * \sa \ref daemon_flags. + */ +void daemon_clear_flag(unsigned flag) +{ + me->flags &= ~flag; +} + +static unsigned daemon_test_flag(unsigned flag) +{ + return me->flags & flag; +} /** * Do the usual stuff to become a daemon. @@ -22,7 +151,7 @@ * * \sa fork(2), setsid(2), dup(2). */ -void daemon_init(void) +void daemonize(void) { pid_t pid; int null; @@ -56,50 +185,43 @@ err: } /** - * fopen() the given file in append mode. - * - * \param logfile_name The name of the file to open. - * - * \return Either calls exit() or returns a valid file handle. + * Close the log file of the daemon. */ -FILE *open_log(const char *logfile_name) +void daemon_close_log(void) { - FILE *logfile; - - assert(logfile_name); - logfile = fopen(logfile_name, "a"); - if (!logfile) { - PARA_EMERG_LOG("can not open %s: %s\n", logfile_name, - strerror(errno)); - exit(EXIT_FAILURE); - } - setlinebuf(logfile); - return logfile; + if (!me->logfile) + return; + PARA_INFO_LOG("closing logfile\n"); + fclose(me->logfile); + me->logfile = NULL; } /** - * Close the log file of the daemon. - * - * \param logfile The log file handle. + * fopen() the logfile in append mode. * - * It's OK to call this with logfile == \p NULL. + * \return Either succeeds or exits. */ -void close_log(FILE* logfile) +void daemon_open_log_or_die(void) { - if (!logfile) + daemon_close_log(); + if (!me->logfile_name) return; - PARA_INFO_LOG("closing logfile\n"); - fclose(logfile); + me->logfile = fopen(me->logfile_name, "a"); + if (!me->logfile) { + PARA_EMERG_LOG("can not open %s: %s\n", me->logfile_name, + strerror(errno)); + exit(EXIT_FAILURE); + } + setlinebuf(me->logfile); } /** * Log the startup message containing the paraslash version. */ -void log_welcome(const char *whoami, int loglevel) +void log_welcome(const char *whoami) { PARA_INFO_LOG("welcome to %s " PACKAGE_VERSION " ("BUILD_DATE")\n", whoami); - PARA_DEBUG_LOG("using loglevel %d\n", loglevel); } /** @@ -116,7 +238,7 @@ void log_welcome(const char *whoami, int loglevel) * * \sa getpwnam(3), getuid(2), setuid(2), getgrnam(2), setgid(2) */ -void para_drop_privileges(const char *username, const char *groupname) +void drop_privileges_or_die(const char *username, const char *groupname) { struct passwd *p; char *tmp; @@ -168,16 +290,15 @@ void para_drop_privileges(const char *username, const char *groupname) */ time_t server_uptime(enum uptime set_or_get) { - static time_t startuptime; time_t now; double diff; if (set_or_get == UPTIME_SET) { - time(&startuptime); + time(&me->startuptime); return 0; } time(&now); - diff = difftime(now, startuptime); + diff = difftime(now, me->startuptime); return (time_t) diff; } @@ -194,3 +315,51 @@ __malloc char *uptime_str(void) return make_message("%li:%02li:%02li", t / 86400, (t / 3600) % 24, (t / 60) % 60); } + +/** + * The log function for para_server and para_audiod. + * + * \param ll The log level. + * \param fmt The format string describing the log message. + */ +__printf_2_3 void para_log(int ll, const char* fmt,...) +{ + va_list argp; + FILE *fp; + struct tm *tm; + time_t t1; + char *color, str[MAXLINE] = ""; + + ll = PARA_MIN(ll, NUM_LOGLEVELS - 1); + ll = PARA_MAX(ll, LL_DEBUG); + if (ll < me->loglevel) + return; + + fp = me->logfile? me->logfile : stderr; + color = daemon_test_flag(DF_COLOR_LOG)? me->log_colors[ll] : NULL; + if (color) + fprintf(fp, "%s", color); + if (daemon_test_flag(DF_LOG_TIME)) { + /* date and time */ + time(&t1); + tm = localtime(&t1); + strftime(str, MAXLINE, "%b %d %H:%M:%S", tm); + fprintf(fp, "%s ", str); + } + if (daemon_test_flag(DF_LOG_HOSTNAME)) { + if (!me->hostname) + me->hostname = para_hostname(); + fprintf(fp, "%s ", me->hostname); + } + if (daemon_test_flag(DF_LOG_LL)) /* log loglevel */ + fprintf(fp, "(%d) ", ll); + if (daemon_test_flag(DF_LOG_PID)) { /* log pid */ + pid_t mypid = getpid(); + fprintf(fp, "(%d) ", (int)mypid); + } + va_start(argp, fmt); + vfprintf(fp, fmt, argp); + va_end(argp); + if (color) + fprintf(fp, "%s", COLOR_RESET); +} diff --git a/daemon.h b/daemon.h index e1b8149a..c8dd997f 100644 --- a/daemon.h +++ b/daemon.h @@ -1,12 +1,32 @@ /** \file daemon.h exported symbols from daemon.c */ -void daemon_init(void); -FILE *open_log(const char *logfile_name); -void close_log(FILE* logfile); -void log_welcome(const char *whoami, int loglevel); -void para_drop_privileges(const char *username, const char *groupname); +void daemonize(void); +void daemon_open_log_or_die(void); +void daemon_close_log(void); +void log_welcome(const char *whoami); +void drop_privileges_or_die(const char *username, const char *groupname); /** used for server_uptime() */ enum uptime {UPTIME_SET, UPTIME_GET}; time_t server_uptime(enum uptime set_or_get); __malloc char *uptime_str(void); +void daemon_set_logfile(char *logfile_name); +void daemon_set_flag(unsigned flag); +void daemon_clear_flag(unsigned flag); +void daemon_set_loglevel(char *loglevel); +void daemon_set_default_log_colors(void); +int daemon_set_log_color(char const *arg); + +/** Daemon log configuration flags. */ +enum daemon_flags { + /** Whether the hostname should be logged. */ + DF_LOG_HOSTNAME = 1, + /** Whether the PID should be logged. */ + DF_LOG_PID = 2, + /** Prepend log message with date and time. */ + DF_LOG_TIME = 4, + /** Also print the loglevel for each message. */ + DF_LOG_LL = 8, + /** Use colored output. */ + DF_COLOR_LOG = 16, +}; diff --git a/dccp_recv.c b/dccp_recv.c index c73088f8..93dea809 100644 --- a/dccp_recv.c +++ b/dccp_recv.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/dccp_recv.ggo b/dccp_recv.ggo deleted file mode 100644 index 9717329d..00000000 --- a/dccp_recv.ggo +++ /dev/null @@ -1,13 +0,0 @@ -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 diff --git a/dccp_send.c b/dccp_send.c index bbef02e9..9b72b7c9 100644 --- a/dccp_send.c +++ b/dccp_send.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/error.h b/error.h index 37ff48a9..0a5fb217 100644 --- a/error.h +++ b/error.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -17,7 +17,7 @@ DEFINE_ERRLIST_OBJECT_ENUM; #define TIME_ERRORS #define CLOSE_ON_FORK_ERRORS #define DAEMON_ERRORS -#define ORTP_SEND_ERRORS +#define UDP_SEND_ERRORS #define GUI_ERRORS #define RINGBUFFER_ERRORS #define SCORE_ERRORS @@ -29,6 +29,7 @@ DEFINE_ERRLIST_OBJECT_ENUM; #define DCCP_SEND_ERRORS #define HTTP_SEND_ERRORS #define GGO_ERRORS +#define COLOR_ERRORS extern const char **para_errlist[]; @@ -207,13 +208,11 @@ extern const char **para_errlist[]; PARA_ERROR(RECV_PATTERN, "did not receive expected pattern"), \ -#define ORTP_RECV_ERRORS \ - PARA_ERROR(MSG_TO_BUF, "failed to extract rtp packet"), \ - PARA_ERROR(ORTP_SYNTAX, "ortp syntax error"), \ - PARA_ERROR(TOO_MANY_BAD_CHUNKS, "too many consecutive bad chunks"), \ - PARA_ERROR(INVALID_HEADER, "invalid header packet"), \ - PARA_ERROR(OVERRUN, "output buffer overrun"), \ - PARA_ERROR(ORTP_RECV_EOF, "ortp_recv: end of file"), \ +#define UDP_RECV_ERRORS \ + PARA_ERROR(UDP_SYNTAX, "udp_recv syntax error"), \ + PARA_ERROR(UDP_BAD_HEADER, "invalid udp audio header"), \ + PARA_ERROR(UDP_OVERRUN, "output buffer overrun"), \ + PARA_ERROR(UDP_BAD_STREAM_TYPE, "invalid stream type"), \ #define HTTP_RECV_ERRORS \ @@ -360,8 +359,6 @@ extern const char **para_errlist[]; #define FD_ERRORS \ PARA_ERROR(FGETS, "fgets error"), \ - PARA_ERROR(EMPTY, "file empty"), \ - PARA_ERROR(MMAP, "mmap error"), \ #define WRITE_ERRORS \ diff --git a/exec.c b/exec.c index bafae240..3c810a3e 100644 --- a/exec.c +++ b/exec.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2008 Andre Noll + * Copyright (C) 2003-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/fade.c b/fade.c index 33f1ccbe..40b4623c 100644 --- a/fade.c +++ b/fade.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1998-2008 Andre Noll + * Copyright (C) 1998-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/fade.ggo b/fade.ggo deleted file mode 100644 index 5965cba9..00000000 --- a/fade.ggo +++ /dev/null @@ -1,155 +0,0 @@ -section "general options" -######################### - -option "mode" o -#~~~~~~~~~~~~~~ - -"{sleep|fade|snooze}" - string default="sleep" - optional - -option "config_file" c -#~~~~~~~~~~~~~~~~~~~~~ - "(default='~/.paraslash/fade.conf')" - string typestr="filename" - optional - -option "mixer_device" m -#~~~~~~~~~~~~~~~~~~~~~~ - "mixer device file" - string typestr="device" - default="/dev/mixer" - optional - -section "sleep options (only relevant in sleep mode)" -##################################################### - -option "sleep_ivol" - -#~~~~~~~~~~~~~~~~~~~~ - "set initial volume before doing anything else" - int typestr="volume" - default="60" - optional - -option "fa_mode" - -#~~~~~~~~~~~~~~~~~~~ - "fall asleep afs mode. Select this mode -right after setting the volume. Example: - --fa_mode m/sleep -" - - string typestr="afs_mode" - default="m/fade" - optional - -option "fa_fade" - -#~~~~~~~~~~~~~~~~~ - "fall asleep fading time, no fading if set -to 0" - int typestr="seconds" - default="1800" - optional - -option "fa_vol" - -#~~~~~~~~~~~~~~~~ - "volume to fade to" - int typestr="volume" - default="20" - optional - -option "sleep_mode" - -#~~~~~~~~~~~~~~~~~~~~~~ - "change to this afs mode after fading, stop -playing if unset." - string typestr="afs_mode" - default="m/sleep" - optional - -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 "wake_mode" - -#~~~~~~~~~~~~~~~~~~~~~ - "change to this afs mode on waketime" - string typestr="afs_mode" - default="m/wake" - optional - -option "wake_fade" - -#~~~~~~~~~~~~~~~~~~~ - "no fading in if set to 0" - int typestr="seconds" - default="1200" - optional - -option "wake_vol" - -#~~~~~~~~~~~~~~~~~~ - "vol to fade to at waketime" - int typestr="volume" - default="80" - optional - -section "snooze options" -######################## - -option "snooze_out_fade" - -#~~~~~~~~~~~~~~~~~~~~~~~~~ - "fade out time" - int typestr="seconds" - default="30" - optional - -option "snooze_out_vol" - -#~~~~~~~~~~~~~~~~~~~~~~~~ - "vol to fade to before snooze" - int typestr="volume" - default="20" - optional - -option "snooze_time" - -#~~~~~~~~~~~~~~~~~~~~~ - "delay" - int typestr="seconds" - default="600" - optional - -option "snooze_in_fade" - -#~~~~~~~~~~~~~~~~~~~~~~~~ - "fade in time" - int typestr="seconds" - default="180" - optional - -option "snooze_in_vol" - -#~~~~~~~~~~~~~~~~~~~~~~~ - "vol to fade to after snooze" - int typestr="volume" - default="80" - optional - -section "fade options" -###################### - -option "fade_vol" f -#~~~~~~~~~~~~~~~~~~ - "volume to fade to" - int typestr="volume" - default="50" - optional - -option "fade_time" t -#~~~~~~~~~~~~~~~~~~~ - "time to fade in" - int typestr="seconds" - default="5" - optional diff --git a/fd.c b/fd.c index d0b5c895..8759d509 100644 --- a/fd.c +++ b/fd.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -40,6 +40,45 @@ int write_all(int fd, const char *buf, size_t *len) return 1; } +/** + * Write a buffer to a non-blocking file descriptor. + * + * \param fd The file descriptor. + * \param buf the buffer to write. + * \param len the number of bytes of \a buf. + * \param max_bytes_per_write Do not write more than that many bytes at once. + * + * If \a max_bytes_per_write is non-zero, do not send more than that many bytes + * per write(). + * + * EAGAIN is not considered an error condition. For example CCID3 has a + * sending wait queue which fills up and is emptied asynchronously. The EAGAIN + * case means that there is currently no space in the wait queue, but this can + * change at any moment. + * + * \return Negative on errors, number of bytes written else. + */ +int write_nonblock(int fd, const char *buf, size_t len, + size_t max_bytes_per_write) +{ + size_t written = 0; + int ret = 0; + + while (written < len) { + size_t num = len - written; + + if (max_bytes_per_write && max_bytes_per_write < num) + num = max_bytes_per_write; + ret = write(fd, buf + written, num); + if (ret < 0 && errno == EAGAIN) + return written; + if (ret < 0) + return -ERRNO_TO_PARA_ERROR(errno); + written += ret; + } + return written; +} + /** * Check whether a file exists. * @@ -135,11 +174,7 @@ __must_check int mark_fd_nonblocking(int fd) */ void para_fd_set(int fd, fd_set *fds, int *max_fileno) { - - if (fd < 0 || fd >= FD_SETSIZE) { - PARA_EMERG_LOG("fatal: tried to add invalid fd %d\n", fd); - exit(EXIT_FAILURE); - } + assert(fd >= 0 && fd < FD_SETSIZE); #if 0 { int flags = fcntl(fd, F_GETFL); @@ -190,19 +225,26 @@ again: * \param flags Exactly one of MAP_SHARED and MAP_PRIVATE. * \param fd The file to mmap from. * \param offset Mmap start. + * \param map Result pointer. + * + * \return Standard. * - * \return This function either returns a valid pointer to the mapped area - * or calls exit() on errors. + * \sa mmap(2). */ -void *para_mmap(size_t length, int prot, int flags, int fd, off_t offset) +int para_mmap(size_t length, int prot, int flags, int fd, off_t offset, + void *map) { - void *ret = mmap(NULL, length, prot, flags, fd, offset); - if (ret != MAP_FAILED) - return ret; - PARA_EMERG_LOG("mmap failed: %s\n", strerror(errno)); - PARA_EMERG_LOG("length: %zu, flags: %d, fd: %d, offset: %zu\n", - length, flags, fd, (size_t)offset); - exit(EXIT_FAILURE); + void **m = map; + + errno = EINVAL; + if (!length) + goto err; + *m = mmap(NULL, length, prot, flags, fd, offset); + if (*m != MAP_FAILED) + return 1; +err: + *m = NULL; + return -ERRNO_TO_PARA_ERROR(errno); } /** @@ -363,17 +405,7 @@ int mmap_full_file(const char *path, int open_mode, void **map, goto out; } *size = file_status.st_size; - ret = -E_EMPTY; - PARA_DEBUG_LOG("%s: size %zu\n", path, *size); - if (!*size) - goto out; - *map = mmap(NULL, *size, mmap_prot, mmap_flags, fd, 0); - if (*map == MAP_FAILED) { - *map = NULL; - ret = -E_MMAP; - goto out; - } - ret = 1; + ret = para_mmap(*size, mmap_prot, mmap_flags, fd, 0, map); out: if (ret < 0 || !fd_ptr) close(fd); @@ -427,3 +459,24 @@ again: goto again; return ret; } + +/** + * Ensure that file descriptors 0, 1, and 2 are valid. + * + * Common approach that opens /dev/null until it gets a file descriptor greater + * than two. + * + * \sa okir's Black Hats Manual. + */ +void valid_fd_012(void) +{ + while (1) { + int fd = open("/dev/null", O_RDWR); + if (fd < 0) + exit(EXIT_FAILURE); + if (fd > 2) { + close(fd); + break; + } + } +} diff --git a/fd.h b/fd.h index 22141be2..7c364120 100644 --- a/fd.h +++ b/fd.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -14,7 +14,8 @@ __must_check int mark_fd_nonblocking(int fd); __must_check int mark_fd_blocking(int fd); void para_fd_set(int fd, fd_set *fds, int *max_fileno); __must_check int para_fgets(char *line, int size, FILE *f); -void *para_mmap(size_t length, int prot, int flags, int fd, off_t offset); +int para_mmap(size_t length, int prot, int flags, int fd, off_t offset, + void *map); int para_open(const char *path, int flags, mode_t mode); int para_opendir(const char *dirname, DIR **dir, int *cwd); int para_mkdir(const char *path, mode_t mode); @@ -24,3 +25,6 @@ int mmap_full_file(const char *filename, int open_mode, void **map, size_t *size, int *fd_ptr); int para_munmap(void *start, size_t length); int write_ok(int fd); +void valid_fd_012(void); +int write_nonblock(int fd, const char *buf, size_t len, + size_t max_bytes_per_write); diff --git a/file_write.c b/file_write.c index 533d3331..016732ae 100644 --- a/file_write.c +++ b/file_write.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/file_write.ggo b/file_write.ggo deleted file mode 100644 index 56c44f7a..00000000 --- a/file_write.ggo +++ /dev/null @@ -1,9 +0,0 @@ -option "filename" f -#~~~~~~~~~~~~~~~~~~ -"specify output file name" -string typestr="filename" -optional -details=" - Defaults to a random filename in ~/.paraslash. -" - diff --git a/filter.c b/filter.c index d67aeae4..96880b7d 100644 --- a/filter.c +++ b/filter.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -44,7 +44,8 @@ static struct stdout_task *sot = &stdout_task_struct; /** Gengetopt struct that holds the command line args. */ static struct filter_args_info conf; -INIT_STDERR_LOGGING(conf.loglevel_arg); +static int loglevel; +INIT_STDERR_LOGGING(loglevel); static void open_filters(void) { @@ -162,6 +163,7 @@ int main(int argc, char *argv[]) ret = parse_config(argc, argv); if (ret < 0) goto out; + loglevel = get_loglevel_by_name(conf.loglevel_arg); ret = init_filter_chain(); if (ret < 0) goto out; diff --git a/filter.ggo b/filter.ggo deleted file mode 100644 index 5376a6f8..00000000 --- a/filter.ggo +++ /dev/null @@ -1,24 +0,0 @@ -option "loglevel" l -#~~~~~~~~~~~~~~~~~~ - "set loglevel (0-6)" -int typestr="level" -default="4" -optional - -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 in an efficient way. The same filter - may appear more than once, order matters. - - Filter options may be specified for each '-f' option - separately. Note that you will have to quote these options - like this: - - -f 'compress --inertia 5 --damp 2' -" diff --git a/filter.h b/filter.h index c2a6310b..2c546c1d 100644 --- a/filter.h +++ b/filter.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -197,6 +197,7 @@ struct filter { */ int (*parse_config)(int argc, char **argv, void **config); + /** The help texts for this filter. */ struct ggo_help help; }; diff --git a/filter_common.c b/filter_common.c index a39eefd6..d83629bd 100644 --- a/filter_common.c +++ b/filter_common.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -155,7 +155,7 @@ again: // fc->eof, *fc->out_loaded, conv, conv_total); if (conv) goto again; - if (!*fc->input_error) + if (*fc->input_error >= 0) return; if (*fc->out_loaded) return; diff --git a/fsck.c b/fsck.c index ee7f4ee2..3b605c2d 100644 --- a/fsck.c +++ b/fsck.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2008 Andre Noll + * Copyright (C) 1997-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -19,7 +19,9 @@ static struct fsck_args_info conf; INIT_FSCK_ERRLISTS; -INIT_STDERR_LOGGING(conf.loglevel_arg); + +static int loglevel; +INIT_STDERR_LOGGING(loglevel); /* taken from git */ signed char hexval_table[256] = { @@ -936,6 +938,14 @@ static int check_all_tables(char *base_dir) return ret; } +/** + * The praslash database check program. + * + * \param argc Usual arg count. + * \param argv Usual arg vector. + * + * \return \p EXIT_SUCCESS or \p EXIT_FAILURE. + */ int main(int argc, char **argv) { int i, ret; @@ -947,6 +957,7 @@ int main(int argc, char **argv) goto out; } HANDLE_VERSION_FLAG("fsck", conf); + loglevel = get_loglevel_by_name(conf.loglevel_arg); if (conf.base_dir_given) base_dir = para_strdup(conf.base_dir_arg); else { @@ -970,9 +981,6 @@ out: base_dir? base_dir : "", para_strerror(-ret) ); - if (conf.loglevel_arg > 1) - PARA_EMERG_LOG("re-run with \"--loglevel %d\" to increase verbosity\n", - conf.loglevel_arg - 1); } else PARA_NOTICE_LOG("success\n"); if (base_dir) diff --git a/fsck.ggo b/fsck.ggo deleted file mode 100644 index b893705c..00000000 --- a/fsck.ggo +++ /dev/null @@ -1,47 +0,0 @@ -option "loglevel" l -#~~~~~~~~~~~~~~~~~~ - -"set loglevel (0-6)" - - int typestr="level" - default="3" - optional - -option "base_dir" b -#~~~~~~~~~~~~~~~~~~ -"Full path to the database directory -containing the table(s) to be checked. -(default='~/.paraslash/afs_database'" - - string typestr="path" - optional - -option "dump_dir" d -#~~~~~~~~~~~~~~~~~~ -"If path is non-empty, para_fsck will write a -dump of all given tables to the specified -path." - - string typestr="path" - optional - default="" - -option "no_fsck" n -#~~~~~~~~~~~~~~~~~ -"Disable fsck mode." - - flag off - -option "force" f -#~~~~~~~~~~~~~~~ -"Force fsck even if the table is dirty. -Ignored if -n is given." - - flag off - -option "dry_run" - -#~~~~~~~~~~~~~~~~~ -"Only report problems, don't try to fix them." - - flag off - diff --git a/ggo.c b/ggo.c index 55f10430..b9852b99 100644 --- a/ggo.c +++ b/ggo.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Andre Noll + * Copyright (C) 2008-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -10,6 +10,11 @@ #include "para.h" #include "ggo.h" +/** + * Wrapper for printf() that exits on errors. + * + * \param fmt Usual format string. + */ __printf_1_2 void printf_or_die(const char *fmt, ...) { va_list argp; @@ -20,9 +25,15 @@ __printf_1_2 void printf_or_die(const char *fmt, ...) va_end(argp); if (ret >= 0) return; - fprintf(stderr, "%s: %s\n", __FUNCTION__, strerror(errno)); + exit(EXIT_FAILURE); } +/** + * Print one of the two given help texts. + * + * \param help contains the help texts. + * \param detailed_help Whether to print the detailed help text. + */ void ggo_print_help(struct ggo_help *help, int detailed_help) { const char **p; diff --git a/ggo.h b/ggo.h index c9111b16..4d452b1d 100644 --- a/ggo.h +++ b/ggo.h @@ -1,7 +1,20 @@ +/* + * Copyright (C) 2008-2009 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file ggo.h Functions and structures for help text handling. */ + +/** + * Used by exexutables that can not use gengetopt's generated help function. + */ struct ggo_help { + /** The lines of the short help text. */ const char **short_help; + /** The lines of the detailed help text. */ const char **detailed_help; }; void ggo_print_help(struct ggo_help *help, int detailed_help); -void printf_or_die(const char *fmt, ...); +__printf_1_2 void printf_or_die(const char *fmt, ...); diff --git a/ggo/afh.m4 b/ggo/afh.m4 new file mode 100644 index 00000000..97b8f288 --- /dev/null +++ b/ggo/afh.m4 @@ -0,0 +1,122 @@ +include(header.m4) + +text " +para_afh, the audio format handler tool, is a stand-alone program +contained in the paraslash package for analyzing and streaming audio +files. It can be used to + + - print tech info about the given audio file to stdout. + In particular, the 'chunk table' of the audio file, an array + of offsets within the audio file, may be printed. This table + can be used by other programs unaware of the particular audio + format to stream the audio file. + + - write selected parts of the given audio file in complete + chunks without decoding. Thus para_afh can be used to 'cut' + an audio file. + + - write selected parts of the given audio files 'just in time' + to sdout. This may be useful for third-party software that + is capable of reading from stdin. +" + + +include(loglevel.m4) + + +defgroup "mode" +#-------------- +groupdesc=" + There are two modes of operation: Info mode and stream mode, + one of which must be selected by the corresponding option. + See below. +" +required + +groupoption "info" i +#~~~~~~~~~~~~~~~~~~~ +"select info mode" +group="mode" +details=" + In this mode, the program prints technical information about + the given audio file to stdout. +" + +groupoption "stream" s +#~~~~~~~~~~~~~~~~~~~~~ +"select stream mode" +group="mode" +details=" + If this mode is selected, the selected parts of the content + of the audio file are written to stdout. Only complete chunks + with respect of the underlying audio format are written. + For example, only complete frames in case of mp3 files. +" + +section "Options for info mode" +#============================== + +option "chunk_table" c +#~~~~~~~~~~~~~~~~~~~~~ +"print also the chunk table" +flag off +dependon="info" + +section "Options for stream mode" +#================================ + + +option "begin_chunk" b +#~~~~~~~~~~~~~~~~~~~~~ +"skip a number of chunks" +int typestr="chunk_num" +default="0" +dependon="stream" +optional +details=" + The chunk_num argument must be between -num_chunks and + num_chunks - 1 inclusively where num_chunks is the total number + of chunks which is printed when using the --info option. If + chunk_num is negative, the given number of chunks are counted + backwards from the end of the file. For example --begin_chunk + -100 instructs para_afh to start output at chunk num_chunks + - 100. This is mainly useful for cutting off the end of an + audio file. +" + +option "end_chunk" e +#~~~~~~~~~~~~~~~~~~~ +"only write up to chunk chunk_num" +int typestr="chunk_num" +dependon="stream" +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 +dependon="stream" +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 uninterupted + audio stream. +" + +option "no_header" H +#~~~~~~~~~~~~~~~~~~~ +"do not write an audio file header" +flag off +dependon="stream" +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. +" + diff --git a/ggo/alsa_write.ggo b/ggo/alsa_write.ggo new file mode 100644 index 00000000..84f49d58 --- /dev/null +++ b/ggo/alsa_write.ggo @@ -0,0 +1,34 @@ +option "device" d +#~~~~~~~~~~~~~~~~ +"set PCM device" +string typestr="device" +default="default" +optional +details=" + On systems with dmix, a better choice than the default + value might be to use \"plug:swmix\". +" + +option "channels" c +#~~~~~~~~~~~~~~~~~~ +"specify number of channels" +int typestr="num" +default="2" +optional +details=" + This option is only necessary for playing raw audio with + para_write. In all other cases (plaing wav files with + para_write or using this writer with para_audiod), the number + of channels will be obtained from other resources. +" + +option "samplerate" s +#~~~~~~~~~~~~~~~~~~~~~ +"force given sample rate" +int typestr="num" +default="44100" +optional +details=" + Again, it is only necessary to specify this when playing raw + audio with para_write. +" diff --git a/ggo/amp_filter.ggo b/ggo/amp_filter.ggo new file mode 100644 index 00000000..865df3ac --- /dev/null +++ b/ggo/amp_filter.ggo @@ -0,0 +1,17 @@ +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. +" diff --git a/ggo/audioc.m4 b/ggo/audioc.m4 new file mode 100644 index 00000000..ad112e27 --- /dev/null +++ b/ggo/audioc.m4 @@ -0,0 +1,15 @@ +include(header.m4) +include(loglevel.m4) +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 diff --git a/ggo/audiod.m4 b/ggo/audiod.m4 new file mode 100644 index 00000000..5390d1b0 --- /dev/null +++ b/ggo/audiod.m4 @@ -0,0 +1,195 @@ +include(header.m4) +define(CURRENT_PROGRAM,para_audiod) +define(DEFAULT_CONFIG_FILE,~/.paraslash/audiod.conf) + + +######################### +section "General options" +######################### + + +include(loglevel.m4) +include(color.m4) +include(config_file.m4) +include(logfile.m4) +include(daemon.m4) +include(user.m4) +include(group.m4) + + +######################## +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.. + + 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 uid" +int typestr="uid" +default="-1" +optional +multiple +details=" + Allow the user identified by \"uid\" to connect to para_audiod. + May be specified multiple times. If not specified at all, + all users are allowed to connect. + + This feature requires unix socket credentials and is currently + only supported on Linux systems. On other operating systems, + the option is silently ignored and all local users are allowed + to connect to para_audiod. +" + +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 syncronized + 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, once for each + supported audio format. The \"receiver_spec\" consists of + an audio format and the receiver name, separated by a colon, + and any options for that receiver, seperated by whitespace. + If any receiver options are present, the whole receiver + argument must be quoted. + + Example: + + -r 'mp3:http -i my.host.org -p 8009' +" + + +option "no_default_filters" D +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"Configure filters manually" +flag off +details=" + If (and only if) this option is set, the --filter options + (see below) take effect. Otherwise, the compiled-in default + filters mp3dec, oggdec and aacdec are activated for mp3, ogg, + aac streams respectively. +" + +option "filter" f +#~~~~~~~~~~~~~~~~ +"Use non-default filters" +string typestr="filter_spec" +optional +multiple +dependon="no_default_filters" +details=" + This option may be given multiple times. The \"filter_spec\" + consists of an audio format, the name of the filter, and any + options for that filter. Mote that order matters. + + Examples: + + --filter 'mp3:mp3dec' + + --filter 'mp3:compress --inertia 5 --damp 2' + +" + +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:osx' + +" + +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 syncronizing the audio output of + different clients. +" + diff --git a/ggo/client.m4 b/ggo/client.m4 new file mode 100644 index 00000000..1ae5a38a --- /dev/null +++ b/ggo/client.m4 @@ -0,0 +1,17 @@ +include(header.m4) +define(CURRENT_PROGRAM,para_client) +define(DEFAULT_CONFIG_FILE,~/.paraslash/client.conf) + +args "--no-handle-error" +option "hostname" i "ip or host to connect" string typestr="host" default="localhost" optional +option "user" u "paraslash username" string typestr="username" default="" optional +option "server_port" p "port to connect" int typestr="port" default="2990" optional +option "key_file" k "(default='~/.paraslash/key.')" string typestr="filename" optional + + +include(loglevel.m4) +include(config_file.m4) + + +option "plain" - "request an uncrypted session" flag off + diff --git a/ggo/color.m4 b/ggo/color.m4 new file mode 100644 index 00000000..63e996f3 --- /dev/null +++ b/ggo/color.m4 @@ -0,0 +1,34 @@ + + +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\" +" + + diff --git a/ggo/compress_filter.ggo b/ggo/compress_filter.ggo new file mode 100644 index 00000000..74dcbc03 --- /dev/null +++ b/ggo/compress_filter.ggo @@ -0,0 +1,37 @@ +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 diff --git a/ggo/config_file.m4 b/ggo/config_file.m4 new file mode 100644 index 00000000..9e2ca407 --- /dev/null +++ b/ggo/config_file.m4 @@ -0,0 +1,14 @@ + +option "config_file" c +#~~~~~~~~~~~~~~~~~~~~~ +"(default='DEFAULT_CONFIG_FILE'" +string typestr="filename" +optional +details=" + CURRENT_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. +" + diff --git a/ggo/daemon.m4 b/ggo/daemon.m4 new file mode 100644 index 00000000..b4e842ac --- /dev/null +++ b/ggo/daemon.m4 @@ -0,0 +1,11 @@ + +option "daemon" d +#~~~~~~~~~~~~~~~~ +"run as background daemon" +flag off +dependon="logfile" +details=" + Note that CURRENT_PROGRAM refuses to start in daemon mode if no + logfile was specified. +" + diff --git a/ggo/dccp_recv.ggo b/ggo/dccp_recv.ggo new file mode 100644 index 00000000..9717329d --- /dev/null +++ b/ggo/dccp_recv.ggo @@ -0,0 +1,13 @@ +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 diff --git a/ggo/fade.ggo b/ggo/fade.ggo new file mode 100644 index 00000000..388642e5 --- /dev/null +++ b/ggo/fade.ggo @@ -0,0 +1,178 @@ +section "general options" +######################### + +option "mode" o +#~~~~~~~~~~~~~~ +"{sleep|fade|snooze}" +string default="sleep" +optional +details=" + para_fade knows these three different modes. + + sleep mode: Change to the initial volume and an initial afs + mode, then fade the volume down until the fade out volume is + reached. Switch to the afs sleep mode until the wake time is + reached. Then switch to the afs wake mode and and fade in to + the wake volume. + + fade: Fade the volume to the given value in the given time. + + snooze: Fade out, sleep a bit and fade in. +" + +option "config_file" c +#~~~~~~~~~~~~~~~~~~~~~ + "(default='~/.paraslash/fade.conf')" + string typestr="filename" + optional + +option "mixer_device" m +#~~~~~~~~~~~~~~~~~~~~~~ + "mixer device file" + string typestr="device" + default="/dev/mixer" + optional + +section "sleep options (only relevant in sleep mode)" +##################################################### + +option "sleep_ivol" - +#~~~~~~~~~~~~~~~~~~~~ +"set initial volume" +int typestr="volume" +default="60" +optional +details=" + Used as the start volume, before fading out to the fade out volume. +" + +option "fa_mode" - +#~~~~~~~~~~~~~~~~~ +"fall asleep afs mode." +string typestr="afs_mode" +default="m/fade" +optional +details=" + Select this mode right after setting the volume. Example: + --fa_mode m/sleep +" + +option "fa_fade" - +#~~~~~~~~~~~~~~~~~ + "fall asleep fading time" +int typestr="seconds" +default="1800" +optional +details=" + No fading if set to 0. +" + +option "fa_vol" - +#~~~~~~~~~~~~~~~~ + "volume to fade to" + int typestr="volume" + default="20" + optional + +option "sleep_mode" - +#~~~~~~~~~~~~~~~~~~~~~~ +"sleep time afs mode" +details = " + Select the given afs mode after the fade out is complete. If + unset, the \"stop\" command is sent to para_server. +" +string typestr="afs_mode" +default="m/sleep" +optional + +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 "wake_mode" - +#~~~~~~~~~~~~~~~~~~~ +"wake time afs mode" +string typestr="afs_mode" +default="m/wake" +optional +details=" + Change to this afs mode on waketime. +" + +option "wake_fade" - +#~~~~~~~~~~~~~~~~~~~ + "no fading in if set to 0" + int typestr="seconds" + default="1200" + optional + +option "wake_vol" - +#~~~~~~~~~~~~~~~~~~ + "vol to fade to at waketime" + int typestr="volume" + default="80" + optional + +section "snooze options" +######################## + +option "snooze_out_fade" - +#~~~~~~~~~~~~~~~~~~~~~~~~~ + "fade out time" + int typestr="seconds" + default="30" + optional + +option "snooze_out_vol" - +#~~~~~~~~~~~~~~~~~~~~~~~~ + "vol to fade to before snooze" + int typestr="volume" + default="20" + optional + +option "snooze_time" - +#~~~~~~~~~~~~~~~~~~~~~ + "delay" + int typestr="seconds" + default="600" + optional + +option "snooze_in_fade" - +#~~~~~~~~~~~~~~~~~~~~~~~~ + "fade in time" + int typestr="seconds" + default="180" + optional + +option "snooze_in_vol" - +#~~~~~~~~~~~~~~~~~~~~~~~ + "vol to fade to after snooze" + int typestr="volume" + default="80" + optional + +section "fade options" +###################### + +option "fade_vol" f +#~~~~~~~~~~~~~~~~~~ + "volume to fade to" + int typestr="volume" + default="50" + optional + +option "fade_time" t +#~~~~~~~~~~~~~~~~~~~ + "time to fade in" + int typestr="seconds" + default="5" + optional diff --git a/ggo/file_write.ggo b/ggo/file_write.ggo new file mode 100644 index 00000000..56c44f7a --- /dev/null +++ b/ggo/file_write.ggo @@ -0,0 +1,9 @@ +option "filename" f +#~~~~~~~~~~~~~~~~~~ +"specify output file name" +string typestr="filename" +optional +details=" + Defaults to a random filename in ~/.paraslash. +" + diff --git a/ggo/filter.m4 b/ggo/filter.m4 new file mode 100644 index 00000000..4e991764 --- /dev/null +++ b/ggo/filter.m4 @@ -0,0 +1,21 @@ +include(header.m4) +include(loglevel.m4) + +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 in an efficient way. The same filter + may appear more than once, order matters. + + Filter options may be specified for each '-f' option + separately. Note that you will have to quote these options + like this: + + -f 'compress --inertia 5 --damp 2' +" + diff --git a/ggo/fsck.m4 b/ggo/fsck.m4 new file mode 100644 index 00000000..d593058e --- /dev/null +++ b/ggo/fsck.m4 @@ -0,0 +1,43 @@ +include(header.m4) +include(loglevel.m4) + + +option "base_dir" b +#~~~~~~~~~~~~~~~~~~ +"Full path to the database directory +containing the table(s) to be checked. +(default='~/.paraslash/afs_database'" + + string typestr="path" + optional + +option "dump_dir" d +#~~~~~~~~~~~~~~~~~~ +"If path is non-empty, para_fsck will write a +dump of all given tables to the specified +path." + + string typestr="path" + optional + default="" + +option "no_fsck" n +#~~~~~~~~~~~~~~~~~ +"Disable fsck mode." + + flag off + +option "force" f +#~~~~~~~~~~~~~~~ +"Force fsck even if the table is dirty. +Ignored if -n is given." + + flag off + +option "dry_run" - +#~~~~~~~~~~~~~~~~~ +"Only report problems, don't try to fix them." + + flag off + + diff --git a/ggo/grab_client.ggo b/ggo/grab_client.ggo new file mode 100644 index 00000000..5527143b --- /dev/null +++ b/ggo/grab_client.ggo @@ -0,0 +1,33 @@ +option "filter_num" f + "point of filter chain to grab" + int typestr="num" + default="0" + optional + +option "slot" s + "only grab this slot; grab any slot if negative" + int typestr="num" + default="-1" + optional + +option "audio_format" a + "only grab this type of input stream; + grab any if empty" + string typestr="name" + default="" + optional + +option "input_grab" i + "grab the filter input instead of its output" + flag off + +option "one_shot" o + "stop grabbing if audio file changes" + flag off + +option "mode" m + "select grab mode" + typestr="grab_mode" + values="sloppy","aggressive","pedantic" + default="sloppy" + optional diff --git a/ggo/group.m4 b/ggo/group.m4 new file mode 100644 index 00000000..2a59ad9a --- /dev/null +++ b/ggo/group.m4 @@ -0,0 +1,12 @@ +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. +" + diff --git a/ggo/gui.m4 b/ggo/gui.m4 new file mode 100644 index 00000000..dd1e2496 --- /dev/null +++ b/ggo/gui.m4 @@ -0,0 +1,47 @@ +include(header.m4) +define(CURRENT_PROGRAM,para_gui) + + +######################### +section "general options" +######################### + + +include(config_file.m4) +include(loglevel.m4) + + +option "timeout" t +#~~~~~~~~~~~~~~~~~ +"set timeout" + + int typestr="milliseconds" + default="30" + optional + +option "stat_cmd" s +#~~~~~~~~~~~~~~~~~~ +"command to read server and audiod status +data from" + + string typestr="command" + default="para_audioc stat" + optional + +#--------------------------------- +section "mapping keys to commands" +#--------------------------------- + +option "key_map" k +#~~~~~~~~~~~~~~~~~ + +"Map key k to command c using mode m. 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." + + string typestr="k:m:c" + optional + multiple + diff --git a/ggo/header.m4 b/ggo/header.m4 new file mode 100644 index 00000000..c4231879 --- /dev/null +++ b/ggo/header.m4 @@ -0,0 +1 @@ +changequote(,) diff --git a/ggo/http_recv.ggo b/ggo/http_recv.ggo new file mode 100644 index 00000000..2f1b4d61 --- /dev/null +++ b/ggo/http_recv.ggo @@ -0,0 +1,15 @@ +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 diff --git a/ggo/logfile.m4 b/ggo/logfile.m4 new file mode 100644 index 00000000..bb102939 --- /dev/null +++ b/ggo/logfile.m4 @@ -0,0 +1,11 @@ + +option "logfile" L +#~~~~~~~~~~~~~~~~~ +"where to write log output" +string typestr="filename" +optional +details=" + If this option is not given, CURRENT_PROGRAM writes the log + messages to to stderr +" + diff --git a/ggo/loglevel.m4 b/ggo/loglevel.m4 new file mode 100644 index 00000000..0badbf4c --- /dev/null +++ b/ggo/loglevel.m4 @@ -0,0 +1,22 @@ + +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 quicky. + 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. +" + + diff --git a/ggo/makefile b/ggo/makefile new file mode 100644 index 00000000..4988d6fe --- /dev/null +++ b/ggo/makefile @@ -0,0 +1,69 @@ +module_ggo_opts := --set-version="($(PACKAGE_STRING), $(codename))" + +grab_client.cmdline.h grab_client.cmdline.c: $(ggo_dir)/grab_client.ggo + gengetopt $(module_ggo_opts) \ + -S \ + --set-package=grab \ + --no-handle-help \ + --no-handle-error \ + --no-handle-version \ + --arg-struct-name=grab_client_args_info \ + --file-name=$(subst .ggo,,$( $@ diff --git a/ggo/mp3dec_filter.ggo b/ggo/mp3dec_filter.ggo new file mode 100644 index 00000000..a34c30ae --- /dev/null +++ b/ggo/mp3dec_filter.ggo @@ -0,0 +1,22 @@ +option "bufsize" b +#~~~~~~~~~~~~~~~~~ +"size of output buffer" +int typestr="kilobyte" +default="128" +optional +details=" + Increase this if you encounter output buffer overrun + errors. Smaller values make the mp3dec filter use less + memory. The minimal size is 32K. +" + +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. +" diff --git a/ggo/oggdec_filter.ggo b/ggo/oggdec_filter.ggo new file mode 100644 index 00000000..e2d653d7 --- /dev/null +++ b/ggo/oggdec_filter.ggo @@ -0,0 +1,21 @@ +option "bufsize" b +#~~~~~~~~~~~~~~~~~ +"size of output buffer" +int typestr="kilobyte" +default="128" +optional +details=" + Increase this if you encounter output buffer overrun errors. Smaller + values make the oggdec filter use less memory. +" + +option "initial_buffer" i +#~~~~~~~~~~~~~~~~~~~~~~~~ +"size of initial input buffer" +int typestr="kilobyte" +default="16" +optional +details=" + On startup, defer decoding until that many kilobytes are + available in the input buffer. +" diff --git a/ggo/osx_write.ggo b/ggo/osx_write.ggo new file mode 100644 index 00000000..ce2792e5 --- /dev/null +++ b/ggo/osx_write.ggo @@ -0,0 +1,31 @@ +section "osx options" +##################### + +option "channels" c +#~~~~~~~~~~~~~~~~~~ +"number of channels (only neccessary for raw +audio)" + + int typestr="num" + default="2" + optional + +option "samplerate" s +#~~~~~~~~~~~~~~~~~~~~~ + +"force given sample rate (only neccessary for +raw audio)" + + int typestr="num" + default="44100" + optional + +option "numbuffers" n +#~~~~~~~~~~~~~~~~~~~~~ + +"number of audio buffers to allocate (increase if +you get buffer underruns)" + + int typestr="num" + default="5" + optional diff --git a/ggo/recv.m4 b/ggo/recv.m4 new file mode 100644 index 00000000..9702e12b --- /dev/null +++ b/ggo/recv.m4 @@ -0,0 +1,16 @@ +include(header.m4) +include(loglevel.m4) + + +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' +" + diff --git a/ggo/server.m4 b/ggo/server.m4 new file mode 100644 index 00000000..30c3b41a --- /dev/null +++ b/ggo/server.m4 @@ -0,0 +1,302 @@ +include(header.m4) +define(CURRENT_PROGRAM,para_server) +define(DEFAULT_CONFIG_FILE,~/.paraslash/server.conf) + + +######################### +section "General options" +######################### + + +include(loglevel.m4) +include(color.m4) +include(daemon.m4) +include(user.m4) +include(group.m4) + + +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" +############################# + + +include(logfile.m4) +include(config_file.m4) + + +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'. +" + +option "afs_socket" s +#~~~~~~~~~~~~~~~~~~~~ +"Command socket for afs" +string typestr="path" +default="/var/paraslash/afs_command_socket" +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="/" +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. +" + +#################### +section "udp sender" +#################### + +option "udp_target" - +#~~~~~~~~~~~~~~~~~~~~ +"add udp target" +string typestr="a.b.c.d:p" +optional +multiple +details=" + Add given host/port to the list of targets. This option + can be given multiple times. Example: '224.0.1.38:1500' + instructs the udp sender to send to udp port 1500 on host + 224.0.1.38 (unassigned ip in the Local Network Control Block + 224.0.0/24). This is useful for multicast streaming. +" + +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_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 is used to specify the duration of the interval between + sending the header. Shorter values decrease the average time + clients have to wait before being able to start playback, + but this also increases the amount network traffic. Note + that this affects only ogg vorbis streams as this is the only + audio format that needs an audio file header. +" + +option "udp_ttl" t +#~~~~~~~~~~~~~~~~~ +"set time to live value" +int typestr="num" +default="10" +optional +details=" + This option instructs the udp sender to set the time to live to + \"num\" for the sending udp socket. Only useful for multicast + udp streaming. +" + diff --git a/ggo/udp_recv.ggo b/ggo/udp_recv.ggo new file mode 100644 index 00000000..6ad09f47 --- /dev/null +++ b/ggo/udp_recv.ggo @@ -0,0 +1,13 @@ +option "host" i +"ip or host to receive udp packets from" +string default="224.0.1.38" +optional +details=" + The default address resoves to DANTZ.MCAST.NET and activates + multicast. +" + +option "port" p "udp port" +int typestr="portnumber" +default="8000" +optional diff --git a/ggo/user.m4 b/ggo/user.m4 new file mode 100644 index 00000000..1bd5c59a --- /dev/null +++ b/ggo/user.m4 @@ -0,0 +1,21 @@ + +option "user" u +#~~~~~~~~~~~~~~ +"run as the given user" +string typestr="name" +optional +details=" + CURRENT_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 CURRENT_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 '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 CURRENT_PROGRAM + is started as a non-root user (i.e. EUID != 0). +" diff --git a/ggo/write.m4 b/ggo/write.m4 new file mode 100644 index 00000000..41e44dd7 --- /dev/null +++ b/ggo/write.m4 @@ -0,0 +1,31 @@ +include(header.m4) +include(loglevel.m4) + +option "bufsize" b +#~~~~~~~~~~~~~~~~~ +"input buffer size" +int typestr="kilobytes" +default="64" +optional + +option "writer" w +#~~~~~~~~~~~~~~~~ +"select stream writer" +string typestr="name" +default="alsa (file if alsa is unsupported)" +optional +multiple +details=" + May be give multiple times. The same writer may be specified + more than once. +" + +option "start_time" t +#~~~~~~~~~~~~~~~~~~~~ +"defer playback" +string typestr="timeval" +optional +details=" + Start playback at given time which must be in a:b format where + a denotes seconds and b denotes microseconds since the epoch. +" diff --git a/grab_client.c b/grab_client.c index b9d70147..e419a723 100644 --- a/grab_client.c +++ b/grab_client.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/grab_client.ggo b/grab_client.ggo deleted file mode 100644 index 5527143b..00000000 --- a/grab_client.ggo +++ /dev/null @@ -1,33 +0,0 @@ -option "filter_num" f - "point of filter chain to grab" - int typestr="num" - default="0" - optional - -option "slot" s - "only grab this slot; grab any slot if negative" - int typestr="num" - default="-1" - optional - -option "audio_format" a - "only grab this type of input stream; - grab any if empty" - string typestr="name" - default="" - optional - -option "input_grab" i - "grab the filter input instead of its output" - flag off - -option "one_shot" o - "stop grabbing if audio file changes" - flag off - -option "mode" m - "select grab mode" - typestr="grab_mode" - values="sloppy","aggressive","pedantic" - default="sloppy" - optional diff --git a/gui.c b/gui.c index 61689029..499845d9 100644 --- a/gui.c +++ b/gui.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1998-2008 Andre Noll + * Copyright (C) 1998-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -489,17 +489,19 @@ static int add_output_line(char *line, __a_unused void *data) return 1; } +static int loglevel; + __printf_2_3 void para_log(int ll, const char *fmt,...) { int color; char *msg; - if (ll < conf.loglevel_arg || !curses_active) + if (ll < loglevel || !curses_active) return; switch (ll) { - case DEBUG: - case INFO: - case NOTICE: + case LL_DEBUG: + case LL_INFO: + case LL_NOTICE: color = COLOR_MSG; break; default: @@ -562,8 +564,7 @@ __noreturn __printf_2_3 static void msg_n_exit(int ret, const char* fmt, ...) static void print_welcome(void) { - int ll = conf.loglevel_arg; - if (ll > NOTICE) + if (loglevel > LL_NOTICE) return; outputf(COLOR_WELCOME, "Welcome to para_gui " PACKAGE_VERSION " \"" CODENAME "\". Theme: %s", theme.name); @@ -1168,24 +1169,24 @@ err_out: static void com_ll_decr(void) { - if (conf.loglevel_arg <= DEBUG) { + if (loglevel <= LL_DEBUG) { print_in_bar(COLOR_ERRMSG, "loglevel already at maximal verbosity\n"); return; } - conf.loglevel_arg--; - print_in_bar(COLOR_MSG, "loglevel set to %d\n", conf.loglevel_arg); + loglevel--; + print_in_bar(COLOR_MSG, "loglevel set to %d\n", loglevel); } static void com_ll_incr(void) { - if (conf.loglevel_arg >= EMERG) { + if (loglevel >= LL_EMERG) { print_in_bar(COLOR_ERRMSG, "loglevel already at miminal verbosity\n"); return; } - conf.loglevel_arg++; - print_in_bar(COLOR_MSG, "loglevel set to %d\n", conf.loglevel_arg); + loglevel++; + print_in_bar(COLOR_MSG, "loglevel set to %d\n", loglevel); } /* @@ -1391,6 +1392,7 @@ int main(int argc, char *argv[]) }; gui_cmdline_parser_config_file(cf, &conf, ¶ms); } + loglevel = get_loglevel_by_name(conf.loglevel_arg); if (check_key_map_args() < 0) { fprintf(stderr, "invalid key map in config file\n"); exit(EXIT_FAILURE); diff --git a/gui.ggo b/gui.ggo deleted file mode 100644 index 40a40563..00000000 --- a/gui.ggo +++ /dev/null @@ -1,52 +0,0 @@ -#------------------------ -section "general options" -#------------------------ - -option "config_file" c -#~~~~~~~~~~~~~~~~~~~~~~ -"(default='~/.paraslash/gui.conf')" - - string typestr="filename" - optional - -option "loglevel" l -#~~~~~~~~~~~~~~~~~~ - "set loglevel (0-6)" - - int typestr="level" - default="4" - optional - -option "timeout" t -#~~~~~~~~~~~~~~~~~ -"set timeout" - - int typestr="milliseconds" - default="30" - optional - -option "stat_cmd" s -#~~~~~~~~~~~~~~~~~~ -"command to read server and audiod status -data from" - - string typestr="command" - default="para_audioc stat" - optional - -#--------------------------------- -section "mapping keys to commands" -#--------------------------------- - -option "key_map" k -#~~~~~~~~~~~~~~~~~ - -"Map key k to command c using mode m. 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." - - string typestr="k:m:c" - optional - multiple diff --git a/gui.h b/gui.h index 26628b1c..abc3191b 100644 --- a/gui.h +++ b/gui.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/gui_theme.c b/gui_theme.c index ce79ca63..9345f0ed 100644 --- a/gui_theme.c +++ b/gui_theme.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/hash.h b/hash.h index 2d6de8ab..8a14e3a7 100644 --- a/hash.h +++ b/hash.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/http_recv.c b/http_recv.c index 7716bb0f..7f130341 100644 --- a/http_recv.c +++ b/http_recv.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/http_recv.ggo b/http_recv.ggo deleted file mode 100644 index 2f1b4d61..00000000 --- a/http_recv.ggo +++ /dev/null @@ -1,15 +0,0 @@ -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 diff --git a/http_send.c b/http_send.c index 8ebbbc9c..ce308ab7 100644 --- a/http_send.c +++ b/http_send.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/ipc.c b/ipc.c index cf8a8989..c64cc89c 100644 --- a/ipc.c +++ b/ipc.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/mood.c b/mood.c index cdc0745a..57f3f00a 100644 --- a/mood.c +++ b/mood.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/mp3_afh.c b/mp3_afh.c index b809e8b3..41874b86 100644 --- a/mp3_afh.c +++ b/mp3_afh.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2008 Andre Noll + * Copyright (C) 2003-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -82,7 +82,7 @@ static char *get_latin1(id3_ucs4_t const *string) return (char *)id3_ucs4_latin1duplicate(string); } -char *get_stringlist(union id3_field *field) +static char *get_stringlist(union id3_field *field) { unsigned int k, nstrings = id3_field_getnstrings(field); char *result = NULL; diff --git a/mp3dec_filter.c b/mp3dec_filter.c index ce6cfbcb..90293f0a 100644 --- a/mp3dec_filter.c +++ b/mp3dec_filter.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/mp3dec_filter.ggo b/mp3dec_filter.ggo deleted file mode 100644 index a34c30ae..00000000 --- a/mp3dec_filter.ggo +++ /dev/null @@ -1,22 +0,0 @@ -option "bufsize" b -#~~~~~~~~~~~~~~~~~ -"size of output buffer" -int typestr="kilobyte" -default="128" -optional -details=" - Increase this if you encounter output buffer overrun - errors. Smaller values make the mp3dec filter use less - memory. The minimal size is 32K. -" - -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. -" diff --git a/net.c b/net.c index 9309ac1f..312a6854 100644 --- a/net.c +++ b/net.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -755,3 +755,142 @@ out: free(buf); return ret; } + +static int resolve(const char *hostname, unsigned short port, + struct sockaddr_in *addr) +{ + struct hostent *host; + + assert(hostname); + host = gethostbyname(hostname); + if (!host) + return -ERRNO_TO_PARA_ERROR(h_errno); + if (addr) { + memcpy(&addr->sin_addr, host->h_addr_list[0], host->h_length); + addr->sin_port = port; + } + return 1; +} + +/* + * Create an UDP socket. + * + * If the given address is a multicast adress, the socket will be set + * to use the multicast TTL ttl and sets the datagrams to loop back. + * + * \return The fd of the socket on success, negative on errors. + */ +static int create_udp_socket(struct sockaddr_in *addr, + unsigned short port, unsigned char ttl) +{ + int ret, fd, yes = 1; + + assert(addr); + ret = socket(PF_INET, SOCK_DGRAM, 0); + if (ret < 0) + return -ERRNO_TO_PARA_ERROR(errno); + fd = ret; + /* reuse addresses */ + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + if (ret < 0) { + ret = -ERRNO_TO_PARA_ERROR(errno); + goto err; + } + addr->sin_family = AF_INET; + addr->sin_port = htons(port); + /* set the TTL and turn on multicast loop */ + if (IN_MULTICAST(htonl(addr->sin_addr.s_addr))) { + unsigned char loop = 1; + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, + sizeof(ttl)); + if (ret < 0) { + ret = -ERRNO_TO_PARA_ERROR(errno); + goto err; + } + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, + sizeof(loop)); + if (ret < 0) { + ret = -ERRNO_TO_PARA_ERROR(errno); + goto err; + } + } + return fd; +err: + close(fd); + return ret; +} + +/** + * Create and connect a sending UDP socket. + * + * \param hostname Where to send to (name or IPv4 address). + * \param port The udp port to use. + * \param ttl Time to live (only relevant for multicast). + * + * \return The fd of the socket on success, negative on error. + */ +int create_udp_send_socket(char *hostname, unsigned short port, + unsigned char ttl) +{ + struct sockaddr_in addr; + int fd, ret = resolve(hostname, port, &addr); + + if (ret < 0) + return ret; + ret = create_udp_socket(&addr, port, ttl); + if (ret < 0) + return ret; + fd = ret; + ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (ret >= 0) + return fd; + ret = -ERRNO_TO_PARA_ERROR(errno); + close(fd); + return ret; +} + +/** + * Create and bind a receiving UDP socket. + * + * Bind the created UDP socket to \a hostname, and add multicast membership if + * hostname is a multicast hostname. + * + * \param hostname Name or IPv4 address to receive from. + * \param port The udp port. + * + * \return The fd of the socket on success, negative on errors. + */ +int create_udp_recv_socket(char *hostname, unsigned short port) +{ + struct sockaddr_in addr; + int fd, ret = resolve(hostname, port, &addr); + + if (ret < 0) + memset(&addr.sin_addr, 0, sizeof(addr.sin_addr)); + ret = create_udp_socket(&addr, port, 1); + if (ret < 0) + return ret; + fd = ret; + ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) { + ret = -ERRNO_TO_PARA_ERROR(errno); + goto err; + } + /* Add multicast membership */ + if (IN_MULTICAST(htonl(addr.sin_addr.s_addr))) { + struct ip_mreq mreq; + + mreq.imr_multiaddr.s_addr = addr.sin_addr.s_addr; + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)); + if (ret < 0) { + ret = -ERRNO_TO_PARA_ERROR(errno); + goto err; + } + } + return fd; +err: + close(fd); + return ret; +} diff --git a/net.h b/net.h index 0cb2767c..7cfee2eb 100644 --- a/net.h +++ b/net.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -61,3 +61,6 @@ int recv_pattern(int fd, const char *pattern, size_t bufsize); void enable_crypt(int fd, crypt_function *recv_f, crypt_function *send_f, void *private_data); void disable_crypt(int fd); +int create_udp_recv_socket(char *hostname, unsigned short port); +int create_udp_send_socket(char *hostname, unsigned short port, + unsigned char ttl); diff --git a/ogg_afh.c b/ogg_afh.c index 2030eada..c633f92f 100644 --- a/ogg_afh.c +++ b/ogg_afh.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2008 Andre Noll + * Copyright (C) 2004-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/oggdec_filter.c b/oggdec_filter.c index 0653f7da..8d1abb99 100644 --- a/oggdec_filter.c +++ b/oggdec_filter.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/oggdec_filter.ggo b/oggdec_filter.ggo deleted file mode 100644 index e2d653d7..00000000 --- a/oggdec_filter.ggo +++ /dev/null @@ -1,21 +0,0 @@ -option "bufsize" b -#~~~~~~~~~~~~~~~~~ -"size of output buffer" -int typestr="kilobyte" -default="128" -optional -details=" - Increase this if you encounter output buffer overrun errors. Smaller - values make the oggdec filter use less memory. -" - -option "initial_buffer" i -#~~~~~~~~~~~~~~~~~~~~~~~~ -"size of initial input buffer" -int typestr="kilobyte" -default="16" -optional -details=" - On startup, defer decoding until that many kilobytes are - available in the input buffer. -" diff --git a/ortp.h b/ortp.h deleted file mode 100644 index a3205b3d..00000000 --- a/ortp.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2006-2008 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ - -/** \file ortp.h some macros used by ortp_send.c and ortp_recv.c*/ - -/** the possible packet types */ -enum ortp_audio_packet_type {ORTP_EOF, ORTP_BOF, ORTP_HEADER, ORTP_DATA}; - -/** number of bytes of the paraslash ortp header */ -#define ORTP_AUDIO_HEADER_LEN 10 - -/** write type of this packet to \a buf */ -#define WRITE_PACKET_TYPE(buf, x) (buf)[0] = (unsigned char)((x)&0xff) -/** get type of this packet */ -#define READ_PACKET_TYPE(buf) (unsigned)(buf)[0] - -/** write the chunk time for this packet to \a buf */ -#define WRITE_CHUNK_TIME(buf, x) (buf)[1] = (unsigned char)((x)&0xff);\ - (buf)[2] = (unsigned char)(((x)>>8)&0xff);\ - (buf)[3] = (unsigned char)(((x)>>16)&0xff);\ - (buf)[4] = (unsigned char)(((x)>>24)&0xff); - -/** get the chunk time of this packet */ -#define READ_CHUNK_TIME(buf) (unsigned char)(buf)[1] + \ - ((unsigned char)(buf)[2] << 8) + \ - ((unsigned char)(buf)[3] << 16) + \ - ((unsigned char)(buf)[4] << 24) - -/** write the chunk timestamp */ -#define WRITE_CHUNK_TS(buf, x) (buf)[5] = (unsigned char)((x) & 0xff); \ - (buf)[6] = (unsigned char)(((x >> 8) & 0xff)); -/** get the chunk timestamp */ -#define READ_CHUNK_TS(buf) (unsigned char)(buf)[5] + \ - ((unsigned char)(buf)[6] << 8) - -/** write the stream type (header or headerless) */ -#define WRITE_STREAM_TYPE(buf, x) (buf)[7] = (unsigned char)((x)&0xff) -/** get the type of the stream (header or headerless) */ -#define READ_STREAM_TYPE(buf) (unsigned)(buf)[7] - -/** write the length of the header (only used for streams with header) */ -#define WRITE_HEADER_LEN(buf, x) (buf)[8] = (unsigned char)((x) & 0xff); \ - (buf)[9] = (unsigned char)(((x >> 8) & 0xff)); - -/** get the length of the header (only used for packets containing a header) */ -#define READ_HEADER_LEN(buf) (unsigned char)(buf)[8] + \ - ((unsigned char)(buf)[9] << 8) diff --git a/ortp_recv.c b/ortp_recv.c deleted file mode 100644 index 102fe9ac..00000000 --- a/ortp_recv.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (C) 2005-2008 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ -/** \file ortp_recv.c paraslash's ortp receiver */ - -#include -#include "para.h" - -#include "ortp.h" -#include "list.h" -#include "sched.h" -#include "ggo.h" -#include "recv.h" -#include "ortp_recv.cmdline.h" - -#include "error.h" -#include "audiod.h" -#include "string.h" - -#define CHUNK_SIZE 128 * 1024 - -/** - * data specific to the ortp receiver - * - * \sa receiver receiver_node - */ -struct private_ortp_recv_data { -/** - * - * - * whether a header was received - * - * A flag indicating whether the ortp receiver already received a packet which - * contains the audio file header. - * - * This flag has no effect if the audio stream indicates that no extra headers - * will be sent (mp3). Otherwise, all data packets are dropped until the - * header is received. - */ -int have_header; -/** the ortp session this instance of the receiver belongs to */ -RtpSession *session; -/** the time the first data or header packet was received */ -struct timeval start; -/** ETA of the next chunk */ -struct timeval next_chunk; -/** number of consecutive bad chunks */ -int c_bad; -/** the current timestamp which is passed to the receiving function of libortp */ -uint32_t timestamp; -/** \a timestamp increases by this amount */ -uint32_t chunk_ts; -}; - - -static int msg_to_buf(mblk_t *mp, char *buffer, int len) -{ - mblk_t *m, *mprev; - size_t mlen, rlen = len; - - m = mp->b_cont; - mprev = mp; - while (m != NULL) { - mlen = m->b_wptr - m->b_rptr; - if (mlen <= rlen) { - mblk_t *consumed = m; - memcpy(buffer, m->b_rptr, mlen); - /* go to next mblk_t */ - mprev->b_cont = m->b_cont; - m = m->b_cont; - consumed->b_cont = NULL; - freeb (consumed); - buffer += mlen; - rlen -= mlen; - } else { - memcpy (buffer, m->b_rptr, rlen); - m->b_rptr += rlen; - return len; - } - } - return len - rlen; -} - - -static void ortp_recv_pre_select(struct sched *s, struct task *t) -{ - struct receiver_node *rn = container_of(t, struct receiver_node, task); - struct private_ortp_recv_data *pord = rn->private_data; - struct timeval tmp; - - if (tv_diff(now, &pord->next_chunk, &tmp) >= 0) { - tmp.tv_sec = 0; - tmp.tv_usec = 1000; - } - if (tv_diff(&s->timeout, &tmp, NULL) > 0) - s->timeout = tmp; -} - -static void compute_next_chunk(unsigned chunk_time, - struct private_ortp_recv_data *pord) -{ - struct timeval chunk_tv = {0, chunk_time}; - struct timeval tmp; - - tv_add(&chunk_tv, &pord->next_chunk, &tmp); - pord->next_chunk = tmp; - pord->timestamp += pord->chunk_ts; -// PARA_DEBUG_LOG("next chunk (ts = %d) due at %lu:%lu\n", -// pord->timestamp, pord->next_chunk.tv_sec, -// pord->next_chunk.tv_usec); -} - -static void ortp_recv_post_select(__a_unused struct sched *s, struct task *t) -{ - struct receiver_node *rn = container_of(t, struct receiver_node, task); - struct private_ortp_recv_data *pord = rn->private_data; - mblk_t *mp; - int packet_type, stream_type; - char tmpbuf[CHUNK_SIZE + 3]; - unsigned chunk_time; - size_t packet_size; - -// PARA_INFO_LOG("rn: %p, pord: %p, session: %p\n", rn, pord, pord->session); - if (rn->output_error && *rn->output_error < 0) { - t->error = *rn->output_error; - return; - } - if (pord->start.tv_sec) - if (tv_diff(now, &pord->next_chunk, NULL) < 0) - return; - mp = rtp_session_recvm_with_ts(pord->session, pord->timestamp); - if (!mp) { - struct timeval min_delay = {0, 100}; -// PARA_INFO_LOG("nope, chunk_ts = %d, loaded: %d, bad: %d\n", -// pord->timestamp, rn->loaded, pord->c_bad); - pord->c_bad++; - if ((pord->c_bad > 5000 && pord->start.tv_sec) || pord->c_bad > 10000) { - t->error = -E_TOO_MANY_BAD_CHUNKS; - return; - } - tv_add(now, &min_delay, &pord->next_chunk); - return; - } - /* okay, we have a chunk of data */ - if (!pord->start.tv_sec) - pord->start = *now; - t->error = msg_to_buf(mp, tmpbuf, CHUNK_SIZE); - if (t->error < ORTP_AUDIO_HEADER_LEN) { - if (t->error < 0) - t->error = -E_MSG_TO_BUF; - else - t->error = -E_ORTP_RECV_EOF; - goto err_out; - } - packet_size = t->error; - packet_type = READ_PACKET_TYPE(tmpbuf); - stream_type = READ_STREAM_TYPE(tmpbuf); - chunk_time = READ_CHUNK_TIME(tmpbuf); - pord->chunk_ts = READ_CHUNK_TS(tmpbuf); -// PARA_INFO_LOG("packet type: %d, stream type: %d, chunk time: %u," -// " chunk_ts: %u, loaded: %u, bad: %d\n", packet_type, -// stream_type, chunk_time, pord->chunk_ts, rn->loaded, pord->c_bad); - switch (packet_type) { - unsigned header_len, payload_len; - case ORTP_EOF: - t->error = -E_RECV_EOF; - goto err_out; - case ORTP_BOF: - PARA_INFO_LOG("bof (%zu)\n", packet_size); - pord->have_header = 1; - /* fall through */ - case ORTP_DATA: - if (!pord->have_header && stream_type) - /* can't use the data, wait for header */ - goto success; - if (packet_size + rn->loaded >= CHUNK_SIZE + ORTP_AUDIO_HEADER_LEN) { - t->error = -E_OVERRUN; - goto err_out; - } - if (packet_size > ORTP_AUDIO_HEADER_LEN) { - memcpy(rn->buf + rn->loaded, tmpbuf + ORTP_AUDIO_HEADER_LEN, - packet_size - ORTP_AUDIO_HEADER_LEN); - rn->loaded += packet_size - ORTP_AUDIO_HEADER_LEN; - } - goto success; - case ORTP_HEADER: - header_len = READ_HEADER_LEN(tmpbuf); - PARA_DEBUG_LOG("header packet (%zu bytes), header len: %d\n", - packet_size, header_len); - if (!pord->have_header) { - pord->have_header = 1; - memcpy(rn->buf, tmpbuf + ORTP_AUDIO_HEADER_LEN, - packet_size - ORTP_AUDIO_HEADER_LEN); - rn->loaded = packet_size - ORTP_AUDIO_HEADER_LEN; - goto success; - } - if (header_len + ORTP_AUDIO_HEADER_LEN > packet_size) { - t->error = -E_INVALID_HEADER; - goto err_out; - } - payload_len = packet_size - ORTP_AUDIO_HEADER_LEN - header_len; - if (rn->loaded + payload_len > CHUNK_SIZE) { - t->error = -E_OVERRUN; - goto err_out; - } - if (payload_len) - memcpy(rn->buf + rn->loaded, tmpbuf - + (packet_size - payload_len), payload_len); - rn->loaded += payload_len; - } -success: - t->error = 0; - freemsg(mp); - if (pord->c_bad) { - pord->c_bad = 0; - pord->next_chunk = *now; - } - compute_next_chunk(chunk_time, pord); - return; -err_out: - freemsg(mp); -} - -static void ortp_shutdown(void) -{ -// ortp_global_stats_display(); - ortp_exit(); -} - -static void ortp_recv_close(struct receiver_node *rn) -{ - struct private_ortp_recv_data *pord = rn->private_data; - - rtp_session_destroy(pord->session); - free(rn->private_data); - free(rn->buf); -} - -static void *ortp_recv_parse_config(int argc, char **argv) -{ - int ret; - - struct ortp_recv_args_info *tmp = - para_calloc(sizeof(struct ortp_recv_args_info)); - - ret = ortp_recv_cmdline_parser(argc, argv, tmp)? -E_ORTP_SYNTAX : 1; - if (ret > 0) - return tmp; - free(tmp); - return NULL; -} - -static int ortp_recv_open(struct receiver_node *rn) -{ - struct private_ortp_recv_data *pord; - struct ortp_recv_args_info *c = rn->conf; - - rn->buf = para_calloc(CHUNK_SIZE); - - rn->private_data = para_calloc(sizeof(struct private_ortp_recv_data)); - pord = rn->private_data; - pord->session = rtp_session_new(RTP_SESSION_RECVONLY); - PARA_NOTICE_LOG("receiving from %s:%d\n", c->host_arg, c->port_arg); - rtp_session_set_local_addr(pord->session, c->host_arg, c->port_arg); - rtp_session_set_remote_addr(pord->session, c->host_arg, c->port_arg); - rtp_session_set_payload_type(pord->session, PAYLOAD_AUDIO_CONTINUOUS); - rtp_session_enable_adaptive_jitter_compensation(pord->session, TRUE); - rtp_session_set_jitter_compensation(pord->session, - c->jitter_compensation_arg); - return 1; -} - -/** - * the init function of the ortp receiver - * - * \param r pointer to the receiver struct to initialize - * - * Initialize all function pointers of \a r and call libortp's ortp_init(). - */ -void ortp_recv_init(struct receiver *r) -{ - struct ortp_recv_args_info dummy; - - ortp_recv_cmdline_parser_init(&dummy); - r->shutdown = ortp_shutdown; - r->open = ortp_recv_open; - r->close = ortp_recv_close; - r->pre_select = ortp_recv_pre_select; - r->post_select = ortp_recv_post_select; - r->parse_config = ortp_recv_parse_config; - r->help = (struct ggo_help) { - .short_help = ortp_recv_args_info_help, - .detailed_help = ortp_recv_args_info_detailed_help - }; - - ortp_init(); -} diff --git a/ortp_recv.ggo b/ortp_recv.ggo deleted file mode 100644 index a706d543..00000000 --- a/ortp_recv.ggo +++ /dev/null @@ -1,19 +0,0 @@ -option "host" i -"ip or host to receive rtp packets from" -string default="224.0.1.38" -optional -details=" - The default address resoves to DANTZ.MCAST.NET and activates - multicast. -" - -option "port" p "udp port" -int typestr="portnumber" -default="1500" -optional - -option "jitter_compensation" j -"set ortp's adaptive jitter compensation" -int typestr="milliseconds" -default="0" -optional diff --git a/ortp_send.c b/ortp_send.c deleted file mode 100644 index 17f14d17..00000000 --- a/ortp_send.c +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright (C) 2005-2008 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ - -/** \file ortp_send.c para_server's ortp sender */ - -#include -#include - -#include "server.cmdline.h" -#include "para.h" -#include "error.h" -#include "string.h" -#include "afh.h" -#include "afs.h" -#include "server.h" -#include "vss.h" -#include "list.h" -#include "send.h" -#include "ortp.h" - -/** Convert in_addr to ascii. */ -#define TARGET_ADDR(oc) inet_ntoa((oc)->addr) - -/** Describes one entry in the list of targets for the ortp sender. */ -struct ortp_target { - /** Address info. */ - struct in_addr addr; - /** Whether the ortp sender is activated. */ - int status; - /** The ortp timestamp increases by this amount. */ - uint32_t chunk_ts; - /** The currently used timestamp for this target. */ - uint32_t last_ts; - /** The position of this target in the list of targets. */ - struct list_head node; - /** The UDP port. */ - int port; - /** Non-zero if at least one chunk has been sent to this target. */ - int streaming; - /** The session pointer from libortp. */ - RtpSession *session; -}; - -static struct list_head targets; -static struct sender *self; -static int sender_status; - -static void ortp_delete_target(struct ortp_target *ot, const char *msg) -{ - PARA_NOTICE_LOG("deleting %s:%d (%s) from list\n", TARGET_ADDR(ot), - ot->port, msg); - if (ot->session) { - rtp_session_destroy(ot->session); - ot->session = NULL; - } - list_del(&ot->node); - free(ot); -} - -static void ortp_send_buf(char *buf, size_t len, long unsigned chunks_sent) -{ - struct ortp_target *ot, *tmp; - int ret, ortp_len = len; /* rtp_session_send_with_ts expects int */ - - if (ortp_len < 0) - return; - list_for_each_entry_safe(ot, tmp, &targets, node) { - uint32_t ts; - if (!ot->session) - continue; - WRITE_CHUNK_TS(buf, ot->chunk_ts); - ts = ot->chunk_ts * chunks_sent; - ret = rtp_session_send_with_ts(ot->session, - (unsigned char*)buf, ortp_len, ts); - ot->last_ts = ts; - if (ret < 0) - ortp_delete_target(ot, "send error"); - if (ret != len + 12) - PARA_NOTICE_LOG("short write %d\n", ret); - } -} - -static int set_multicast(RtpSession *s) -{ - unsigned char loop = 1; - int ret; - - ret = setsockopt(s->rtp.socket, - IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)); - if (ret < 0) { - PARA_ERROR_LOG("IP_MULTICAST_LOOP error %d\n", ret); - - } - return 1; -} - -static void ortp_init_session(struct ortp_target *ot) -{ - RtpSession *s; - int ret; - - PARA_NOTICE_LOG("sending to udp %s:%d\n", TARGET_ADDR(ot), ot->port); - ot->session = rtp_session_new(RTP_SESSION_SENDONLY); - if (!ot->session) - return; - s = ot->session; - if (conf.ortp_jitter_compensation_arg) { - rtp_session_enable_adaptive_jitter_compensation(ot->session, TRUE); - rtp_session_set_jitter_compensation(ot->session, - conf.ortp_jitter_compensation_arg); - } - /* always successful */ - rtp_session_set_send_payload_type(s, PAYLOAD_AUDIO_CONTINUOUS); - ret = rtp_session_set_remote_addr(s, TARGET_ADDR(ot), ot->port); - if (ret < 0) { - rtp_session_destroy(ot->session); - ot->session = NULL; - return; - } - set_multicast(s); -} - -/* called by vss */ -static void ortp_shutdown_targets(void) -{ - unsigned char buf[ORTP_AUDIO_HEADER_LEN]; - struct ortp_target *ot, *tmp; - - WRITE_PACKET_TYPE(buf, ORTP_EOF); - list_for_each_entry_safe(ot, tmp, &targets, node) { - if (!ot->session || !ot->streaming) - continue; - PARA_INFO_LOG("sending eof to ortp target %s:%d, ts = %d\n", - TARGET_ADDR(ot), ot->port, ot->last_ts); - rtp_session_send_with_ts(ot->session, buf, - ORTP_AUDIO_HEADER_LEN, ot->last_ts); - ot->streaming = 0; - ot->chunk_ts = 0; - rtp_session_reset(ot->session); - } -} - -static int need_extra_header(long unsigned current_chunk) -{ - static struct timeval last_header; - struct timeval now, diff; - - if (!current_chunk) - return 0; - gettimeofday(&now, NULL); - tv_diff(&now, &last_header, &diff); - if (tv2ms(&diff) < conf.ortp_header_interval_arg) - return 0; - last_header = now; - return 1; -} - -static void ortp_send(long unsigned current_chunk, long unsigned chunks_sent, - const char *buf, size_t len, const char *header_buf, - size_t header_len) -{ - struct ortp_target *ot, *tmp; - size_t sendbuf_len; - int packet_type = ORTP_DATA; - char *sendbuf; - struct timeval *chunk_tv; - - if (sender_status != SENDER_ON) - return; - -// PARA_NOTICE_LOG("sending %lu\n", current_chunk); - chunk_tv = vss_chunk_time(); - if (!chunk_tv) - return; - list_for_each_entry_safe(ot, tmp, &targets, node) { - if (!ot->session) { - ortp_init_session(ot); - if (!ot->session) - continue; - } - if (!ot->chunk_ts) - ot->chunk_ts = rtp_session_time_to_ts(ot->session, - (int)tv2ms(chunk_tv)); -// PARA_DEBUG_LOG("len: %d, ts: %lu, ts: %d\n", -// len, ot->chunk_ts * chunks_sent, ot->chunk_ts); - ot->streaming = 1; - } - if (list_empty(&targets)) - return; - if (!need_extra_header(current_chunk)) - header_len = 0; - sendbuf_len = ORTP_AUDIO_HEADER_LEN + header_len + len; - sendbuf = para_malloc(sendbuf_len); - if (!current_chunk) - packet_type = ORTP_BOF; - else if (header_len) - packet_type = ORTP_HEADER; - WRITE_PACKET_TYPE(sendbuf, packet_type); - WRITE_CHUNK_TIME(sendbuf, chunk_tv->tv_usec); - WRITE_STREAM_TYPE(sendbuf, header_buf? 1 : 0); - WRITE_HEADER_LEN(sendbuf, header_len); - if (header_len) - memcpy(sendbuf + ORTP_AUDIO_HEADER_LEN, header_buf, - header_len); - memcpy(sendbuf + ORTP_AUDIO_HEADER_LEN + header_len, buf, len); - ortp_send_buf(sendbuf, sendbuf_len, chunks_sent); - free(sendbuf); -} - -static int ortp_com_on(__a_unused struct sender_command_data *scd) -{ - - sender_status = SENDER_ON; - return 1; -} - -static int ortp_com_off(__a_unused struct sender_command_data *scd) -{ - ortp_shutdown_targets(); - sender_status = SENDER_OFF; - return 1; -} - -static int ortp_com_delete(struct sender_command_data *scd) -{ - char *a = para_strdup(inet_ntoa(scd->addr)); - struct ortp_target *ot, *tmp; - list_for_each_entry_safe(ot, tmp, &targets, node) { - if (scd->port != ot->port) - continue; - if (strcmp(TARGET_ADDR(ot), a)) - continue; - ortp_delete_target(ot, "com_delete"); - } - return 1; -} - -static void ortp_add_target(int port, struct in_addr *addr) -{ - struct ortp_target *ot = para_calloc(sizeof(struct ortp_target)); - ot->port = port; - ot->addr = *addr; - PARA_INFO_LOG("adding to target list (%s:%d)\n", - TARGET_ADDR(ot), ot->port); - para_list_add(&ot->node, &targets); -} - -static int ortp_com_add(struct sender_command_data *scd) -{ - int port = (scd->port > 0)? scd->port : conf.ortp_default_port_arg; - ortp_add_target(port, &scd->addr); - return 1; -} - -static char *ortp_info(void) -{ - struct ortp_target *ot; - char *ret, *tgts = NULL; - - list_for_each_entry(ot, &targets, node) { - char *tmp = make_message("%s%s:%d ", tgts? tgts : "", - TARGET_ADDR(ot), ot->port); - free(tgts); - tgts = tmp; - } - ret = make_message( - "ortp sender:\n" - "\tstatus: %s\n" - "\tport: udp %d\n" - "\ttargets: %s\n", - (sender_status == SENDER_ON)? "on" : "off", - conf.ortp_default_port_arg, - tgts? tgts : "(none)" - ); - free(tgts); - return ret; -} - -static void ortp_init_target_list(void) -{ - int i; - - INIT_LIST_HEAD(&targets); - for (i = 0; i < conf.ortp_target_given; i++) { - char *arg = para_strdup(conf.ortp_target_arg[i]); - char *p = strchr(arg, ':'); - int port; - struct in_addr addr; - - if (!p) - goto err; - *p = '\0'; - if (!inet_pton(AF_INET, arg, &addr)) - goto err; - port = atoi(++p); - if (port < 0 || port > 65535) - port = conf.ortp_default_port_arg; - ortp_add_target(port, &addr); - goto success; -err: - PARA_CRIT_LOG("syntax error for ortp_target option " - "#%d, ignoring\n", i); -success: - free(arg); - continue; - } -} - -static char *ortp_help(void) -{ - return make_message( - "usage: {on|off}\n" - "usage: {add|delete} IP port\n" - "example: add 224.0.1.38 1500 (LAN-streaming)\n" - ); -} - -/** - * the init function of para_server's ortp sender - * - * \param s pointer to the http sender struct - * - * It initializes all function pointers of \a s and the list of ortp targets. - */ -void ortp_send_init(struct sender *s) -{ - ortp_init(); - INIT_LIST_HEAD(&targets); - s->info = ortp_info; - s->help = ortp_help; - s->send = ortp_send; - s->pre_select = NULL; - s->post_select = NULL; - s->shutdown_clients = ortp_shutdown_targets; - s->client_cmds[SENDER_ON] = ortp_com_on; - s->client_cmds[SENDER_OFF] = ortp_com_off; - s->client_cmds[SENDER_DENY] = NULL; - s->client_cmds[SENDER_ALLOW] = NULL; - s->client_cmds[SENDER_ADD] = ortp_com_add; - s->client_cmds[SENDER_DELETE] = ortp_com_delete; - self = s; - sender_status = SENDER_OFF; - ortp_init_target_list(); - if (!conf.ortp_no_autostart_given) - sender_status = SENDER_ON; - PARA_DEBUG_LOG("ortp sender init complete\n"); -} diff --git a/osl.c b/osl.c index 2aee5b6a..ff588713 100644 --- a/osl.c +++ b/osl.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/osl.h b/osl.h index 58969a43..3c35fd4e 100644 --- a/osl.h +++ b/osl.h @@ -1,6 +1,6 @@ #include /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/osl_core.h b/osl_core.h index 5e2acaa0..de53cdd1 100644 --- a/osl_core.h +++ b/osl_core.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/osx_write.c b/osx_write.c index 057526e3..f67a63ac 100644 --- a/osx_write.c +++ b/osx_write.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/osx_write.ggo b/osx_write.ggo deleted file mode 100644 index ce2792e5..00000000 --- a/osx_write.ggo +++ /dev/null @@ -1,31 +0,0 @@ -section "osx options" -##################### - -option "channels" c -#~~~~~~~~~~~~~~~~~~ -"number of channels (only neccessary for raw -audio)" - - int typestr="num" - default="2" - optional - -option "samplerate" s -#~~~~~~~~~~~~~~~~~~~~~ - -"force given sample rate (only neccessary for -raw audio)" - - int typestr="num" - default="44100" - optional - -option "numbuffers" n -#~~~~~~~~~~~~~~~~~~~~~ - -"number of audio buffers to allocate (increase if -you get buffer underruns)" - - int typestr="num" - default="5" - optional diff --git a/para.h b/para.h index 218b8147..fcc24746 100644 --- a/para.h +++ b/para.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2008 Andre Noll + * Copyright (C) 1997-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -49,63 +49,65 @@ typeof(x) _x = (x); \ _x > 0? _x : -_x; }) -/** debug loglevel, gets really noisy */ -#define DEBUG 1 -/** still noisy, but won't fill your disk */ -#define INFO 2 -/** normal, but significant event */ -#define NOTICE 3 -/** unexpected event that can be handled */ -#define WARNING 4 -/** unhandled error condition */ -#define ERROR 5 -/** system might be unreliable */ -#define CRIT 6 -/** last message before exit */ -#define EMERG 7 +/** 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 /** Log messages with lower priority than that will not be compiled in. */ #define COMPILE_TIME_LOGLEVEL 0 /** \cond */ -#if DEBUG > COMPILE_TIME_LOGLEVEL -#define PARA_DEBUG_LOG(f,...) para_log(DEBUG, "%s: " f, __FUNCTION__, ## __VA_ARGS__) +#if LL_DEBUG >= COMPILE_TIME_LOGLEVEL +#define PARA_DEBUG_LOG(f,...) para_log(LL_DEBUG, "%s: " f, __FUNCTION__, ## __VA_ARGS__) #else #define PARA_DEBUG_LOG(...) do {;} while (0) #endif -#if INFO > COMPILE_TIME_LOGLEVEL -#define PARA_INFO_LOG(f,...) para_log(INFO, "%s: " f, __FUNCTION__, ## __VA_ARGS__) +#if LL_INFO >= COMPILE_TIME_LOGLEVEL +#define PARA_INFO_LOG(f,...) para_log(LL_INFO, "%s: " f, __FUNCTION__, ## __VA_ARGS__) #else #define PARA_INFO_LOG(...) do {;} while (0) #endif -#if NOTICE > COMPILE_TIME_LOGLEVEL -#define PARA_NOTICE_LOG(f,...) para_log(NOTICE, "%s: " f, __FUNCTION__, ## __VA_ARGS__) +#if LL_NOTICE >= COMPILE_TIME_LOGLEVEL +#define PARA_NOTICE_LOG(f,...) para_log(LL_NOTICE, "%s: " f, __FUNCTION__, ## __VA_ARGS__) #else #define PARA_NOTICE_LOG(...) do {;} while (0) #endif -#if WARNING > COMPILE_TIME_LOGLEVEL -#define PARA_WARNING_LOG(f,...) para_log(WARNING, "%s: " f, __FUNCTION__, ## __VA_ARGS__) +#if LL_WARNING >= COMPILE_TIME_LOGLEVEL +#define PARA_WARNING_LOG(f,...) para_log(LL_WARNING, "%s: " f, __FUNCTION__, ## __VA_ARGS__) #else #define PARA_WARNING_LOG(...) do {;} while (0) #endif -#if ERROR > COMPILE_TIME_LOGLEVEL -#define PARA_ERROR_LOG(f,...) para_log(ERROR, "%s: " f, __FUNCTION__, ## __VA_ARGS__) +#if LL_ERROR >= COMPILE_TIME_LOGLEVEL +#define PARA_ERROR_LOG(f,...) para_log(LL_ERROR, "%s: " f, __FUNCTION__, ## __VA_ARGS__) #else #define PARA_ERROR_LOG(...) do {;} while (0) #endif -#if CRIT > COMPILE_TIME_LOGLEVEL -#define PARA_CRIT_LOG(f,...) para_log(CRIT, "%s: " f, __FUNCTION__, ## __VA_ARGS__) +#if LL_CRIT >= COMPILE_TIME_LOGLEVEL +#define PARA_CRIT_LOG(f,...) para_log(LL_CRIT, "%s: " f, __FUNCTION__, ## __VA_ARGS__) #else #define PARA_CRIT_LOG(...) do {;} while (0) #endif -#if EMERG > COMPILE_TIME_LOGLEVEL -#define PARA_EMERG_LOG(f,...) para_log(EMERG, "%s: " f, __FUNCTION__, ## __VA_ARGS__) +#if LL_EMERG >= COMPILE_TIME_LOGLEVEL +#define PARA_EMERG_LOG(f,...) para_log(LL_EMERG, "%s: " f, __FUNCTION__, ## __VA_ARGS__) #else #define PARA_EMERG_LOG(...) #endif @@ -131,7 +133,7 @@ /** version text used by various commands if -V switch was given */ #define VERSION_TEXT(prefix) "para_" prefix " " PACKAGE_VERSION " (" CODENAME ")" "\n" \ - "Copyright (C) 2008 Andre Noll\n" \ + "Copyright (C) 2009 Andre Noll\n" \ "This is free software with ABSOLUTELY NO WARRANTY." \ " See COPYING for details.\n" \ "Written by Andre Noll.\n" \ diff --git a/playlist.c b/playlist.c index 171ebab5..a9925cd3 100644 --- a/playlist.c +++ b/playlist.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/portable_io.h b/portable_io.h index e296bcb2..c2ed8afc 100644 --- a/portable_io.h +++ b/portable_io.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/recv.c b/recv.c index 52935a88..df8b9ce9 100644 --- a/recv.c +++ b/recv.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -16,14 +16,16 @@ #include "recv.h" #include "recv.cmdline.h" #include "fd.h" +#include "string.h" #include "error.h" #include "stdout.h" /** the gengetopt args info struct */ struct recv_args_info conf; -/** always log to stderr */ -INIT_STDERR_LOGGING(conf.loglevel_arg); +static int loglevel; +/** Always log to stderr. */ +INIT_STDERR_LOGGING(loglevel); /** init array of error codes used by para_recv */ INIT_RECV_ERRLISTS; @@ -85,6 +87,7 @@ int main(int argc, char *argv[]) PARA_EMERG_LOG("parse failed\n"); goto out; } + loglevel = get_loglevel_by_name(conf.loglevel_arg); r = &receivers[receiver_num]; rn.receiver = r; ret = r->open(&rn); diff --git a/recv.ggo b/recv.ggo deleted file mode 100644 index b7003b0e..00000000 --- a/recv.ggo +++ /dev/null @@ -1,19 +0,0 @@ -option "loglevel" l -#~~~~~~~~~~~~~~~~~~ -"set loglevel (0-6)" -int typestr="level" -default="4" -optional - -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' -" - diff --git a/recv.h b/recv.h index 3aed0180..b82e04f5 100644 --- a/recv.h +++ b/recv.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -29,7 +29,7 @@ struct receiver_node { /** * Describes one supported paraslash receiver. * - * \sa http_recv.c, ortp_recv.c + * \sa http_recv.c, udp_recv.c */ struct receiver { /** @@ -41,7 +41,7 @@ struct receiver { * * It must fill in all other function pointers and is assumed to succeed. * - * \sa http_recv_init ortp_recv_init. + * \sa http_recv_init udp_recv_init. */ void (*init)(struct receiver *r); /** @@ -105,6 +105,7 @@ struct receiver { */ void (*post_select)(struct sched *s, struct task *t); + /** The two help texts of this receiver. */ struct ggo_help help; }; @@ -114,13 +115,8 @@ 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}, - -#ifdef HAVE_ORTP -extern void ortp_recv_init(struct receiver *r); -#define ORTP_RECEIVER {.name = "ortp", .init = ortp_recv_init}, -#else -#define ORTP_RECEIVER -#endif +extern void udp_recv_init(struct receiver *r); +#define UDP_RECEIVER {.name = "udp", .init = udp_recv_init}, extern struct receiver receivers[]; /** \endcond */ @@ -129,9 +125,10 @@ extern struct receiver receivers[]; #define DEFINE_RECEIVER_ARRAY struct receiver receivers[] = { \ HTTP_RECEIVER \ DCCP_RECEIVER \ - ORTP_RECEIVER \ + UDP_RECEIVER \ {.name = NULL}}; +/** Iterate over all available receivers. */ #define FOR_EACH_RECEIVER(i) for (i = 0; receivers[i].name; i++) void *check_receiver_arg(char *ra, int *receiver_num); diff --git a/recv_common.c b/recv_common.c index 5f2412b5..2840df00 100644 --- a/recv_common.c +++ b/recv_common.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -85,6 +85,11 @@ void *check_receiver_arg(char *ra, int *receiver_num) return NULL; } +/** + * Print out the help texts to all receivers. + * + * \param detailed Whether the detailed help should be printed. + */ void print_receiver_helps(int detailed) { int i; diff --git a/ringbuffer.c b/ringbuffer.c index efe4fd05..c45ed3fa 100644 --- a/ringbuffer.c +++ b/ringbuffer.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/ringbuffer.h b/ringbuffer.h index 2bea374e..a7488429 100644 --- a/ringbuffer.h +++ b/ringbuffer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/sched.c b/sched.c index 4df32fa1..9e346df2 100644 --- a/sched.c +++ b/sched.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/sched.h b/sched.h index 4d57c75f..26aaabe9 100644 --- a/sched.h +++ b/sched.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/score.c b/score.c index f2a28b75..51ec1e4a 100644 --- a/score.c +++ b/score.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/send.h b/send.h index 2fe1cc56..c1ed00cf 100644 --- a/send.h +++ b/send.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -12,7 +12,7 @@ enum {SENDER_ADD, SENDER_DELETE, SENDER_ALLOW, SENDER_DENY, SENDER_ON, SENDER_OF /** * Describes one supported sender of para_server. * - * \sa http_send.c ortp_send.c, dccp_send.c. + * \sa http_send.c udp_send.c, dccp_send.c. */ struct sender { /** The name of the sender. */ @@ -141,3 +141,5 @@ int generic_com_on(struct sender_status *ss, unsigned protocol); void generic_com_off(struct sender_status *ss); char *generic_sender_help(void); struct sender_client *accept_sender_client(struct sender_status *ss); +int send_queued_chunks(int fd, struct chunk_queue *cq, + size_t max_bytes_per_write); diff --git a/send_common.c b/send_common.c index 3d870088..d1dcaeb1 100644 --- a/send_common.c +++ b/send_common.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -93,45 +93,6 @@ void shutdown_clients(struct sender_status *ss) shutdown_client(sc, ss); } -/** - * Write a buffer to a non-blocking file descriptor. - * - * \param fd The file descriptor. - * \param buf the buffer to write. - * \param len the number of bytes of \a buf. - * \param max_bytes_per_write Do not write more than that many bytes at once. - * - * If \a max_bytes_per_write is non-zero, do not send more than that many bytes - * per write(). - * - * EAGAIN is not considered an error condition. For example CCID3 has a - * sending wait queue which fills up and is emptied asynchronously. The EAGAIN - * case means that there is currently no space in the wait queue, but this can - * change at any moment. - * - * \return Negative on errors, number of bytes written else. - */ -static int write_nonblock(int fd, const char *buf, size_t len, - size_t max_bytes_per_write) -{ - size_t written = 0; - int ret = 0; - - while (written < len) { - size_t num = len - written; - - if (max_bytes_per_write && max_bytes_per_write < num) - num = max_bytes_per_write; - ret = write(fd, buf + written, num); - if (ret < 0 && errno == EAGAIN) - return written; - if (ret < 0) - return -ERRNO_TO_PARA_ERROR(errno); - written += ret; - } - return written; -} - static int queue_chunk_or_shutdown(struct sender_client *sc, struct sender_status *ss, const char *buf, size_t num_bytes) { @@ -141,23 +102,31 @@ static int queue_chunk_or_shutdown(struct sender_client *sc, return ret; } -/* return: negative on errors, zero if not everything was sent, one otherwise */ -static int send_queued_chunks(struct sender_client *sc, +/** + * Try to empty the chunk queue for this fd. + * + * \param fd The file descriptor. + * \param cq The list of queued chunks. + * \param max_bytes_per_write Do not send more than this in one go. + * + * \return Negative on errors, zero if not everything was sent, one otherwise. + */ +int send_queued_chunks(int fd, struct chunk_queue *cq, size_t max_bytes_per_write) { struct queued_chunk *qc; - while ((qc = cq_peek(sc->cq))) { + while ((qc = cq_peek(cq))) { const char *buf; size_t len; int ret; cq_get(qc, &buf, &len); - ret = write_nonblock(sc->fd, buf, len, max_bytes_per_write); + ret = write_nonblock(fd, buf, len, max_bytes_per_write); if (ret < 0) return ret; - cq_update(sc->cq, ret); + cq_update(cq, ret); if (ret != len) return 0; - cq_dequeue(sc->cq); + cq_dequeue(cq); } return 1; } @@ -192,7 +161,7 @@ void send_chunk(struct sender_client *sc, struct sender_status *ss, } sc->header_sent = 1; } - ret = send_queued_chunks(sc, max_bytes_per_write); + ret = send_queued_chunks(sc->fd, sc->cq, max_bytes_per_write); if (ret < 0) { shutdown_client(sc, ss); goto out; diff --git a/server.c b/server.c index 6aa7c1f4..5823cb28 100644 --- a/server.c +++ b/server.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2008 Andre Noll + * Copyright (C) 1997-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -27,7 +27,7 @@ * - Volume normalizer: \ref compress_filter.c, * - Output: \ref alsa_write.c, \ref osx_write.c, * - http: \ref http_recv.c, \ref http_send.c, - * - ortp: \ref ortp_recv.c, \ref ortp_send.c, + * - udp: \ref udp_recv.c, \ref udp_send.c, * - dccp: \ref dccp_recv.c, \ref dccp_send.c, * - Audio file selector: \ref afs.c, \ref aft.c, \ref mood.c, * - Afs structures: \ref afs_table, \ref audio_file_data, @@ -64,6 +64,7 @@ #include #include +#include #include "para.h" #include "error.h" @@ -84,6 +85,7 @@ #include "sched.h" #include "signal.h" #include "user_list.h" +#include "color.h" /** Define the array of error lists needed by para_server. */ INIT_SERVER_ERRLISTS; @@ -111,8 +113,6 @@ uint32_t afs_socket_cookie; /** The mutex protecting the shared memory area containing the mmd struct. */ int mmd_mutex; -/* global variables for server-internal use */ -static FILE *logfile; /** The file containing user information (public key, permissions). */ static char *user_list_file = NULL; static int mmd_shm_id; @@ -130,42 +130,36 @@ struct server_command_task { struct task task; }; -/** - * Para_server's log function. - * - * \param ll The log level. - * \param fmt The format string describing the log message. - */ -__printf_2_3 void para_log(int ll, const char* fmt,...) +static int want_colors(void) +{ + if (conf.color_arg == color_arg_no) + return 0; + if (conf.color_arg == color_arg_yes) + return 1; + if (conf.logfile_given) + return 0; + return isatty(STDERR_FILENO); +} + +static void init_colors_or_die(void) { - va_list argp; - FILE *outfd; - struct tm *tm; - time_t t1; - char str[MAXLINE] = ""; - pid_t mypid; - - if (ll < conf.loglevel_arg) + int ret, i; + + if (!want_colors()) return; - outfd = logfile? logfile : stderr; - time(&t1); - tm = localtime(&t1); - strftime(str, MAXLINE, "%b %d %H:%M:%S", tm); - fprintf(outfd, "%s ", str); - if (conf.loglevel_arg <= INFO) - fprintf(outfd, "%i: ", ll); - mypid = getpid(); - if (conf.loglevel_arg <= INFO) - fprintf(outfd, "(%d) ", (int)mypid); - va_start(argp, fmt); - vfprintf(outfd, fmt, argp); - va_end(argp); + daemon_set_flag(DF_COLOR_LOG); + daemon_set_default_log_colors(); + for (i = 0; i < conf.log_color_given; i++) { + ret = daemon_set_log_color(conf.log_color_arg[i]); + if (ret < 0) + exit(EXIT_FAILURE); + } } /* * setup shared memory area and get mutex for locking */ -static void shm_init(void) +static void init_ipc_or_die(void) { void *shm; int ret = shm_new(sizeof(struct misc_meta_data)); @@ -197,13 +191,21 @@ err_out: exit(EXIT_FAILURE); } -static void parse_config(int override) +/** + * (Re-)read the server configuration files. + * + * \param override Passed to gengetopt to activate the override feature. + * + * This function also re-opens the logfile and sets the global \a + * user_list_file variable. + */ +void parse_config_or_die(int override) { char *home = para_homedir(); - struct stat statbuf; int ret; char *cf; + daemon_close_log(); if (conf.config_file_given) cf = para_strdup(conf.config_file_arg); else @@ -213,13 +215,13 @@ static void parse_config(int override) user_list_file = make_message("%s/.paraslash/server.users", home); else user_list_file = para_strdup(conf.user_list_arg); - ret = stat(cf, &statbuf); - if (ret && conf.config_file_given) { + ret = file_exists(cf); + if (conf.config_file_given && !ret) { ret = -1; - PARA_EMERG_LOG("can not stat config file %s\n", cf); + PARA_EMERG_LOG("can not read config file %s\n", cf); goto out; } - if (!ret) { + if (ret) { int tmp = conf.daemon_given; struct server_cmdline_parser_params params = { .override = override, @@ -231,8 +233,15 @@ static void parse_config(int override) server_cmdline_parser_config_file(cf, &conf, ¶ms); conf.daemon_given = tmp; } - if (conf.logfile_given) - logfile = open_log(conf.logfile_arg); + if (conf.logfile_given) { + daemon_set_logfile(conf.logfile_arg); + daemon_open_log_or_die(); + } + daemon_set_loglevel(conf.loglevel_arg); + init_colors_or_die(); + daemon_set_flag(DF_LOG_PID); + daemon_set_flag(DF_LOG_LL); + daemon_set_flag(DF_LOG_TIME); ret = 1; out: free(cf); @@ -256,9 +265,7 @@ static void signal_pre_select(struct sched *s, struct task *t) static void handle_sighup(void) { PARA_NOTICE_LOG("SIGHUP\n"); - close_log(logfile); /* gets reopened if necessary by parse_config */ - logfile = NULL; - parse_config(1); /* reopens log */ + parse_config_or_die(1); /* reopens log */ init_user_list(user_list_file); /* reload user list */ if (mmd->afs_pid) kill(mmd->afs_pid, SIGHUP); @@ -481,16 +488,18 @@ static void server_init(int argc, char **argv) /* parse command line options */ server_cmdline_parser_ext(argc, argv, &conf, ¶ms); HANDLE_VERSION_FLAG("server", conf); - para_drop_privileges(conf.user_arg, conf.group_arg); + drop_privileges_or_die(conf.user_arg, conf.group_arg); /* parse config file, open log and set defaults */ - parse_config(0); - log_welcome("para_server", conf.loglevel_arg); - shm_init(); /* init mmd struct */ + parse_config_or_die(0); + log_welcome("para_server"); + init_ipc_or_die(); /* init mmd struct and mmd->lock */ + /* make sure, the global now pointer is uptodate */ + gettimeofday(now, NULL); server_uptime(UPTIME_SET); /* reset server uptime */ init_user_list(user_list_file); /* become daemon */ if (conf.daemon_given) - daemon_init(); + daemonize(); PARA_NOTICE_LOG("initializing audio format handlers\n"); afh_init(); PARA_NOTICE_LOG("initializing the audio file selector\n"); diff --git a/server.ggo b/server.ggo deleted file mode 100644 index 9f391203..00000000 --- a/server.ggo +++ /dev/null @@ -1,367 +0,0 @@ -######################### -section "General options" -######################### - -option "loglevel" l -#~~~~~~~~~~~~~~~~~~ -"set loglevel (1-6)" -int typestr="level" -default="4" -optional -details=" - Larger values mean less verbose output. Loglevel 1 (debug) gets - really noisy; a value of 2 (info) produces still noisy output, - but this won't fill up the disk quicky. Messaged logged with - loglevel 3 (notice) indicate normal but significant events - while level 4 (warning) logs unexpected events that can be - handled. Unhandled error conditions are logged with loglevel - 5 (error) and crititcal errors are logged using loglevel 6 - (crit). Finally, loglevel 7 (emerg) is reserved for messages - that cause para_server to terminate immediately. -" - -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. -" - -option "daemon" d -#~~~~~~~~~~~~~~~~ -"run as background daemon" -flag off -dependon="logfile" -details=" - Note that para_server refuses to start in daemon mode if no - logfile was specified. -" - -option "user" u -#~~~~~~~~~~~~~~ -"run as the given user" -string typestr="name" -optional -details=" - para_server 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 para_server - 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 - para_server is started as a non-root user (i.e. EUID != 0) -" - - -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. -" - -############################# -section "Configuration files" -############################# - - -option "logfile" L -#~~~~~~~~~~~~~~~~~ -"where to write log output" -string typestr="filename" -optional -details=" - If this option is not given, para_server writes the log - messages to to stderr -" - -option "config_file" c -#~~~~~~~~~~~~~~~~~~~~~ -"(default='~/.paraslash/server.conf'" -string typestr="filename" -optional -details=" - para_server 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. -" - -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'. -" - -option "afs_socket" s -#~~~~~~~~~~~~~~~~~~~~ -"Command socket for afs" -string typestr="path" -default="/var/paraslash/afs_command_socket" -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="/" -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. -" - -##################### -section "ortp sender" -##################### - -option "ortp_target" - -#~~~~~~~~~~~~~~~~~~~~~ -"add ortp target" -string typestr="a.b.c.d:p" -optional -multiple -details=" - Add given host/port to the list of targets. This option - can be given multiple times. Example: '224.0.1.38:1500' - instructs the ortp sender to send to udp port 1500 on host - 224.0.1.38 (unassigned ip in the Local Network Control Block - 224.0.0/24). This is useful for LAN-streaming. -" - -option "ortp_no_autostart" - -#~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"do not start sending" -flag off -details=" - If this option is given, ortp streaming may be activated at - a later time by using the sender command. -" - -option "ortp_default_port" - -#~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"udp port to send to" -int typestr="port" -default="1500" -optional - -option "ortp_header_interval" H -#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"duration for sending header" -int typestr="ms" -default="2000" -optional -details=" - As the ortp sender has no idea about connected clients it - sends the audio file header periodically if necessary. This - option is used to specify the duration of the interval between - sending the header. Shorter values decrease the average time - clients have to wait before being able to start playback, - but this also increases the amount network traffic. Note - that this affects only ogg vorbis streams as this is the only - audio format that needs an audio file header. -" - -option "ortp_jitter_compensation" j -#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"configure jitter compensation" -int typestr="ms" -default="400" -optional -details=" - ortp's adaptive jitter compensation gets activated whenever - this value is greater than zero. See the ortp documentation - about details on this feature. -" diff --git a/server.h b/server.h index b37c9f13..b133d82e 100644 --- a/server.h +++ b/server.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2008 Andre Noll + * Copyright (C) 1997-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -106,3 +106,4 @@ struct misc_meta_data { extern struct server_args_info conf; __noreturn void handle_connect(int fd, const char *peername); +void parse_config_or_die(int override); diff --git a/sha1.c b/sha1.c index af4dbc65..65524bc5 100644 --- a/sha1.c +++ b/sha1.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/signal.c b/signal.c index 9168e918..acc87802 100644 --- a/signal.c +++ b/signal.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2008 Andre Noll + * Copyright (C) 2004-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/signal.h b/signal.h index 68c0ae1f..962e074a 100644 --- a/signal.h +++ b/signal.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/stat.c b/stat.c index e640e4c6..4ba1b0e8 100644 --- a/stat.c +++ b/stat.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/stdin.c b/stdin.c index 0b84cdc7..fea30dd9 100644 --- a/stdin.c +++ b/stdin.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/stdin.h b/stdin.h index 99233f9f..edb72cf8 100644 --- a/stdin.h +++ b/stdin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/stdout.c b/stdout.c index 87bf711f..ce0d2209 100644 --- a/stdout.c +++ b/stdout.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/stdout.h b/stdout.h index 6a6400ce..4149dad3 100644 --- a/stdout.h +++ b/stdout.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/string.c b/string.c index d23e43c1..3c80d27c 100644 --- a/string.c +++ b/string.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2008 Andre Noll + * Copyright (C) 2004-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -328,27 +328,6 @@ __must_check unsigned split_args(char *args, char *** const argv_ptr, const char return n; } -/** - * Ensure that file descriptors 0, 1, and 2 are valid. - * - * Common approach that opens /dev/null until it gets a file descriptor greater - * than two. - * - * \sa okir's Black Hats Manual. - */ -void valid_fd_012(void) -{ - while (1) { - int fd = open("/dev/null", O_RDWR); - if (fd < 0) - exit(EXIT_FAILURE); - if (fd > 2) { - close(fd); - break; - } - } -} - /** * Get the own hostname. * @@ -607,3 +586,22 @@ int para_atoi32(const char *str, int32_t *value) *value = tmp; return 1; } + +int get_loglevel_by_name(const char *txt) +{ + if (!strcasecmp(txt, "debug")) + return LL_DEBUG; + if (!strcasecmp(txt, "info")) + return LL_INFO; + if (!strcasecmp(txt, "notice")) + return LL_NOTICE; + if (!strcasecmp(txt, "warning")) + return LL_WARNING; + if (!strcasecmp(txt, "error")) + return LL_ERROR; + if (!strcasecmp(txt, "crit")) + return LL_CRIT; + if (!strcasecmp(txt, "emerg")) + return LL_EMERG; + return -1; +} diff --git a/string.h b/string.h index 561ff4e8..77e62e90 100644 --- a/string.h +++ b/string.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -40,7 +40,6 @@ __must_check __malloc char *para_logname(void); __must_check __malloc char *para_homedir(void); __must_check unsigned split_args(char *args, char *** const argv_ptr, const char *delim); __malloc char *para_hostname(void); -void valid_fd_012(void); __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...); /** Used for for_each_line() and for_each_line_ro(). */ typedef int line_handler_t(char *, void *); @@ -50,3 +49,4 @@ int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler, void *private_data); int para_atoi64(const char *str, int64_t *result); int para_atoi32(const char *str, int32_t *value); +int get_loglevel_by_name(const char *txt); diff --git a/time.c b/time.c index 0ebc6ec0..6e1d603d 100644 --- a/time.c +++ b/time.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/udp_header.h b/udp_header.h new file mode 100644 index 00000000..83157e6b --- /dev/null +++ b/udp_header.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2006-2009 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file udp_header.h some macros used by udp_send.c and udp_recv.c. */ + +/** + * Number of bytes of the paraslash udp header. + * + * The udp sender prepends a header at the beginning of each data chunk. Within + * this header, the type of the current audio stream and the * type of this + * data chunk is coded. + */ +#define UDP_AUDIO_HEADER_LEN 16 + +/** The possible stream types. */ +enum udp_stream_type { + /** Used for mp3 and aac streams. */ + UDP_PLAIN_STREAM, + /** Ogg vorbis streams. */ + UDP_HEADER_STREAM, + /** stream type not yet known. */ + UDP_UNKNOWN_STREAM +}; + +/** The possible packet types. */ +enum udp_audio_packet_type { + /** Beginning of file. */ + UDP_BOF_PACKET, + /** End of file. */ + UDP_EOF_PACKET, + /** Combined header/data packet (ogg only). */ + UDP_HEADER_PACKET, + /** Packet contains only audio file data. */ + UDP_DATA_PACKET, + /** Invalid packet type. */ + UDP_UNKNOWN_PACKET +}; + +/** The contents of an udp audio header. */ +struct udp_audio_header { + /** see \ref udp_stream_type. */ + uint8_t stream_type; + /** see \ref udp_audio_packet_type. */ + uint8_t packet_type; + /** Non-zero only for header packets. */ + uint16_t header_len; + /** Length of header plus audio file data. */ + uint16_t payload_len; +}; + +/** + * Write a struct udp_audio_header to a buffer. + * + * \param buf The buffer to write to. + * \param h The audio header to write. + * + * Used by the udp sender. + * + */ +_static_inline_ void write_udp_audio_header(char *buf, struct udp_audio_header *h) +{ + memcpy(buf, "UDPM", 4); + write_u8(buf + 4, h->stream_type); + write_u8(buf + 5, h->packet_type); + write_u16(buf + 6, h->header_len); + write_u16(buf + 8, h->payload_len); + memset(buf + 10, 0, 6); +} + +/** + * Used by the udp receiver to read a struct udp_audio_header from a buffer. + * + * \param buf The buffer to read from. + * \param len The length of \a buf. + * \param h Result pointer. + * + * \return 1 if \a buf contains a valid udp audio header, -1 else. + */ +_static_inline_ int read_udp_audio_header(char *buf, size_t len, + struct udp_audio_header *h) +{ + if (len < 4) + goto err; + if (memcmp(buf, "UDPM", 4)) + goto err; + h->stream_type = read_u8(buf + 4); + h->packet_type = read_u8(buf + 5); + h->header_len = read_u16(buf + 6); + h->payload_len = read_u16(buf + 8); + return 1; +err: + h->stream_type = UDP_UNKNOWN_STREAM; + h->packet_type = UDP_UNKNOWN_PACKET; + h->header_len = h->payload_len = 0; + return -1; +} diff --git a/udp_recv.c b/udp_recv.c new file mode 100644 index 00000000..bdc25411 --- /dev/null +++ b/udp_recv.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2005-2009 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ +/** \file udp_recv.c Paraslash's udp receiver */ + +#include + +#include "para.h" +#include "error.h" +#include "portable_io.h" +#include "udp_header.h" +#include "list.h" +#include "sched.h" +#include "ggo.h" +#include "recv.h" +#include "udp_recv.cmdline.h" +#include "audiod.h" +#include "string.h" +#include "net.h" +#include "fd.h" + +/** The size of the receiver node buffer. */ +#define UDP_RECV_CHUNK_SIZE (128 * 1024) + +/** + * Data specific to the udp receiver. + * + * \sa \ref receiver, \ref receiver_node. + */ +struct private_udp_recv_data { + /** + * Whether a header was received. + * + * A flag indicating whether this receiver already received a packet + * which contains the audio file header. + * + * This flag has no effect if the audio stream indicates that no extra + * headers will be sent (mp3, aac). Otherwise, all data packets are + * dropped until the header is received. + */ + int have_header; + /** The socket file descriptor. */ + int fd; + /** Non-zero on short reads. */ + uint16_t need_more; + /** Copied from the first audio header received. */ + uint16_t stream_type; +}; + +static void udp_recv_pre_select(struct sched *s, struct task *t) +{ + struct receiver_node *rn = container_of(t, struct receiver_node, task); + struct private_udp_recv_data *purd = rn->private_data; + + para_fd_set(purd->fd, &s->rfds, &s->max_fileno); +} + +static int enough_space(size_t nbytes, size_t loaded) +{ + return nbytes + loaded < UDP_RECV_CHUNK_SIZE; +} + +/* + * Perform some sanity checks on an udp audio file header. + * + * return: negative on error, 0: discard data, 1: use data + */ +static int examine_audio_header(struct private_udp_recv_data *purd, + struct udp_audio_header *uah, size_t packet_size) +{ + /* payload_len includes header */ + if (uah->payload_len < uah->header_len) + return -E_UDP_BAD_HEADER; + switch (uah->packet_type) { + case UDP_EOF_PACKET: + return -E_RECV_EOF; + case UDP_BOF_PACKET: + purd->have_header = 1; + /* fall through */ + case UDP_DATA_PACKET: + if (uah->header_len) /* header in no-header packet */ + return -E_UDP_BAD_HEADER; + break; + case UDP_HEADER_PACKET: + if (!uah->header_len) /** no header in header packet */ + return -E_UDP_BAD_HEADER; + break; + default: /* bad packet type */ + return -E_UDP_BAD_HEADER; + } + /* check stream type */ + if (uah->stream_type != UDP_PLAIN_STREAM && + uah->stream_type != UDP_HEADER_STREAM) + return -E_UDP_BAD_STREAM_TYPE; + if (purd->stream_type == UDP_UNKNOWN_STREAM) + purd->stream_type = uah->stream_type; + /* stream type must not change */ + if (uah->stream_type != purd->stream_type) + return -E_UDP_BAD_STREAM_TYPE; + if (!purd->have_header && uah->stream_type == UDP_HEADER_STREAM) + /* can't use the data, wait for header packet */ + return 0; + if (packet_size < uah->payload_len + UDP_AUDIO_HEADER_LEN) + /* we read only a part of the package */ + purd->need_more = uah->payload_len + + UDP_AUDIO_HEADER_LEN - packet_size; + return 1; +} + +static int add_rn_output(struct receiver_node *rn, char *buf, size_t len) +{ + if (!len) + return 1; + if (!enough_space(len, rn->loaded)) + return -E_UDP_OVERRUN; + memcpy(rn->buf + rn->loaded, buf, len); + rn->loaded += len; + return 1; +} + +static void udp_recv_post_select(__a_unused struct sched *s, struct task *t) +{ + struct receiver_node *rn = container_of(t, struct receiver_node, task); + struct private_udp_recv_data *purd = rn->private_data; + int ret; + char tmpbuf[UDP_RECV_CHUNK_SIZE]; + uint16_t data_len; + char *data_buf; + size_t packet_size; + struct udp_audio_header uah; + + if (rn->output_error && *rn->output_error < 0) { + t->error = *rn->output_error; + return; + } + if (!FD_ISSET(purd->fd, &s->rfds)) + return; + ret = recv_bin_buffer(purd->fd, tmpbuf, UDP_RECV_CHUNK_SIZE); + if (ret < 0) { + if (is_errno(ret, EINTR) || is_errno(ret, EAGAIN)) + goto success; + t->error = ret; + return; + } + t->error = -E_RECV_EOF; + if (!ret) + return; + packet_size = ret; + for (;;) { + uint16_t num; + + if (!purd->need_more) { + ret = read_udp_audio_header(tmpbuf, packet_size, &uah); + if (ret >= 0) + break; + goto success; /* drop data */ + } + num = PARA_MIN(purd->need_more, (uint16_t)packet_size); + assert(num > 0); + t->error = add_rn_output(rn, tmpbuf, num); + if (t->error < 0) + return; + purd->need_more -= num; + if (packet_size <= num) + goto success; + packet_size -= num; + memmove(tmpbuf, tmpbuf + num, packet_size); + } + assert(!purd->need_more); + t->error = examine_audio_header(purd, &uah, packet_size); + if (t->error <= 0) + return; + data_len = uah.payload_len; + data_buf = tmpbuf + UDP_AUDIO_HEADER_LEN; + if (uah.packet_type == UDP_HEADER_PACKET) { + if (purd->have_header) { /* skip header */ + data_buf += uah.header_len; + data_len -= uah.header_len; + } else { /* only use the header */ + purd->have_header = 1; + data_len = uah.header_len; + } + } + t->error = add_rn_output(rn, data_buf, data_len); + return; +success: + t->error = 1; +} + +static void udp_shutdown(void) +{ + return; +} + +static void udp_recv_close(struct receiver_node *rn) +{ + struct private_udp_recv_data *purd = rn->private_data; + + if (purd->fd >= 0) + close(purd->fd); + free(rn->private_data); + free(rn->buf); +} + +static void *udp_recv_parse_config(int argc, char **argv) +{ + int ret; + struct udp_recv_args_info *tmp = + para_calloc(sizeof(struct udp_recv_args_info)); + + ret = udp_recv_cmdline_parser(argc, argv, tmp)? -E_UDP_SYNTAX : 1; + if (ret >= 0) + return tmp; + free(tmp); + return NULL; +} + +static int udp_recv_open(struct receiver_node *rn) +{ + struct private_udp_recv_data *purd; + struct udp_recv_args_info *c = rn->conf; + int ret; + + rn->buf = para_calloc(UDP_RECV_CHUNK_SIZE); + rn->private_data = para_calloc(sizeof(struct private_udp_recv_data)); + purd = rn->private_data; + ret = create_udp_recv_socket(c->host_arg, c->port_arg); + if (ret < 0) + goto err; + purd->fd = ret; + ret = mark_fd_nonblocking(purd->fd); + if (ret < 0) + goto err; + purd->stream_type = UDP_UNKNOWN_STREAM; + PARA_NOTICE_LOG("receiving from %s:%d, fd=%d\n", c->host_arg, + c->port_arg, purd->fd); + return purd->fd; +err: + free(rn->private_data); + free(rn->buf); + return ret; +} + +/** + * 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->shutdown = udp_shutdown; + 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->help = (struct ggo_help) { + .short_help = udp_recv_args_info_help, + .detailed_help = udp_recv_args_info_detailed_help + }; +} diff --git a/udp_send.c b/udp_send.c new file mode 100644 index 00000000..3804c39c --- /dev/null +++ b/udp_send.c @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2005-2009 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file udp_send.c Para_server's udp sender. */ + + +#include +#include + +#include "server.cmdline.h" +#include "para.h" +#include "error.h" +#include "string.h" +#include "afh.h" +#include "afs.h" +#include "server.h" +#include "vss.h" +#include "list.h" +#include "send.h" +#include "portable_io.h" +#include "udp_header.h" +#include "net.h" +#include "fd.h" +#include "sched.h" +#include "close_on_fork.h" +#include "chunk_queue.h" + +/** Convert in_addr to ascii. */ +#define TARGET_ADDR(oc) inet_ntoa((oc)->addr) + +/** Describes one entry in the list of targets for the udp sender. */ +struct udp_target { + /** Address info. */ + struct in_addr addr; + /** The position of this target in the list of targets. */ + struct list_head node; + /** The UDP port. */ + int port; + /** The socket fd. */ + int fd; + /** The list of queued chunks for this fd. */ + struct chunk_queue *cq; +}; + +static struct list_head targets; +static int sender_status; + +static void udp_close_target(struct udp_target *ut) +{ + if (ut->fd < 0) + return; + close(ut->fd); + del_close_on_fork_list(ut->fd); + cq_destroy(ut->cq); + ut->cq = NULL; + ut->fd = -1; +} + +static void udp_delete_target(struct udp_target *ut, const char *msg) +{ + PARA_NOTICE_LOG("deleting %s:%d (%s) from list\n", TARGET_ADDR(ut), + ut->port, msg); + udp_close_target(ut); + list_del(&ut->node); + free(ut); +} + +/** The maximal size of the per-target chunk queue. */ +#define UDP_CQ_BYTES 40000 + +static int udp_init_session(struct udp_target *ut) +{ + int ret; + + if (ut->fd >= 0) /* nothing to do */ + return 0; + ret = create_udp_send_socket(TARGET_ADDR(ut), ut->port, + conf.udp_ttl_arg); + if (ret < 0) + return ret; + ut->fd = ret; + ret = mark_fd_nonblocking(ut->fd); + if (ret < 0) { + close(ut->fd); + return ret; + } + add_close_on_fork_list(ut->fd); + ut->cq = cq_new(UDP_CQ_BYTES); + PARA_NOTICE_LOG("sending to udp %s:%d\n", TARGET_ADDR(ut), ut->port); + return 1; +} + +static void udp_send_buf(char *buf, size_t len) +{ + struct udp_target *ut, *tmp; + int ret; + + list_for_each_entry_safe(ut, tmp, &targets, node) { + ret = udp_init_session(ut); + if (ret < 0) { + udp_delete_target(ut, para_strerror(-ret)); + continue; + } + ret = send_queued_chunks(ut->fd, ut->cq, 0); + if (ret < 0) { + udp_delete_target(ut, para_strerror(-ret)); + continue; + } + if (!len) + continue; + if (!ret) { /* still data left in the queue */ + ret = cq_enqueue(ut->cq, buf, len); + if (ret < 0) { + udp_delete_target(ut, para_strerror(-ret)); + continue; + } + } + ret = write_nonblock(ut->fd, buf, len, 0); + if (ret < 0) { + udp_delete_target(ut, para_strerror(-ret)); + continue; + } + if (ret != len) { + ret = cq_enqueue(ut->cq, buf + ret, len - ret); + if (ret < 0) { + udp_delete_target(ut, para_strerror(-ret)); + continue; + } + } + } +} + +static void udp_shutdown_targets(void) +{ + char buf[UDP_AUDIO_HEADER_LEN]; + struct udp_target *ut, *tmp; + struct udp_audio_header uah = { + .stream_type = UDP_UNKNOWN_STREAM, + .packet_type = UDP_EOF_PACKET, + }; + + write_udp_audio_header(buf, &uah); + list_for_each_entry_safe(ut, tmp, &targets, node) { + if (ut->fd < 0) + continue; + write(ut->fd, buf, UDP_AUDIO_HEADER_LEN); + udp_close_target(ut); + } +} + +static int need_extra_header(long unsigned current_chunk) +{ + static struct timeval last_header; + struct timeval diff; + + if (!current_chunk) + return 0; + tv_diff(now, &last_header, &diff); + if (tv2ms(&diff) < conf.udp_header_interval_arg) + return 0; + last_header = *now; + return 1; +} + +static void udp_send(long unsigned current_chunk, __a_unused long unsigned chunks_sent, + const char *buf, size_t len, const char *header_buf, + size_t header_len) +{ + char *sendbuf; + size_t sendbuf_len; + struct timeval *chunk_tv; + struct udp_audio_header uah; + +// PARA_NOTICE_LOG("len: %zd, header_len: %zd\n", len, header_len); + if (sender_status != SENDER_ON) + return; + + /* we might not yet know the chunk time */ + chunk_tv = vss_chunk_time(); + if (!chunk_tv) + return; + if (list_empty(&targets)) + return; + uah.stream_type = header_len? UDP_HEADER_STREAM : UDP_PLAIN_STREAM; + uah.header_len = need_extra_header(current_chunk)? header_len : 0; + if (!current_chunk) + uah.packet_type = UDP_BOF_PACKET; + else if (uah.header_len) + uah.packet_type = UDP_HEADER_PACKET; + else + uah.packet_type = UDP_DATA_PACKET; + uah.payload_len = uah.header_len + len; + sendbuf_len = UDP_AUDIO_HEADER_LEN + uah.payload_len; + sendbuf = para_malloc(sendbuf_len); + write_udp_audio_header(sendbuf, &uah); + if (uah.header_len) + memcpy(sendbuf + UDP_AUDIO_HEADER_LEN, header_buf, + uah.header_len); + memcpy(sendbuf + UDP_AUDIO_HEADER_LEN + uah.header_len, buf, len); + udp_send_buf(sendbuf, sendbuf_len); + free(sendbuf); +} + +static int udp_com_on(__a_unused struct sender_command_data *scd) +{ + sender_status = SENDER_ON; + return 1; +} + +static int udp_com_off(__a_unused struct sender_command_data *scd) +{ + udp_shutdown_targets(); + sender_status = SENDER_OFF; + return 1; +} + +static int udp_com_delete(struct sender_command_data *scd) +{ + char *a = para_strdup(inet_ntoa(scd->addr)); + struct udp_target *ut, *tmp; + list_for_each_entry_safe(ut, tmp, &targets, node) { + if (scd->port != ut->port) + continue; + if (strcmp(TARGET_ADDR(ut), a)) + continue; + udp_delete_target(ut, "com_delete"); + } + return 1; +} + +static void udp_add_target(int port, struct in_addr *addr) +{ + struct udp_target *ut = para_calloc(sizeof(struct udp_target)); + ut->port = port; + ut->addr = *addr; + ut->fd = -1; /* not yet connected */ + PARA_INFO_LOG("adding to target list (%s:%d)\n", + TARGET_ADDR(ut), ut->port); + para_list_add(&ut->node, &targets); +} + +static int udp_com_add(struct sender_command_data *scd) +{ + int port = (scd->port > 0)? scd->port : conf.udp_default_port_arg; + udp_add_target(port, &scd->addr); + return 1; +} + +static char *udp_info(void) +{ + struct udp_target *ut; + char *ret, *tgts = NULL; + + list_for_each_entry(ut, &targets, node) { + char *tmp = make_message("%s%s:%d ", tgts? tgts : "", + TARGET_ADDR(ut), ut->port); + free(tgts); + tgts = tmp; + } + ret = make_message( + "udp sender:\n" + "\tstatus: %s\n" + "\tport: udp %d\n" + "\ttargets: %s\n", + (sender_status == SENDER_ON)? "on" : "off", + conf.udp_default_port_arg, + tgts? tgts : "(none)" + ); + free(tgts); + return ret; +} + +static void udp_init_target_list(void) +{ + int i; + + INIT_LIST_HEAD(&targets); + for (i = 0; i < conf.udp_target_given; i++) { + char *arg = para_strdup(conf.udp_target_arg[i]); + char *p = strchr(arg, ':'); + int port; + struct in_addr addr; + + if (!p) + goto err; + *p = '\0'; + if (!inet_pton(AF_INET, arg, &addr)) + goto err; + port = atoi(++p); + if (port < 0 || port > 65535) + port = conf.udp_default_port_arg; + udp_add_target(port, &addr); + goto success; +err: + PARA_CRIT_LOG("syntax error for udp target option " + "#%d, ignoring\n", i); +success: + free(arg); + continue; + } +} + +static char *udp_help(void) +{ + return make_message( + "usage: {on|off}\n" + "usage: {add|delete} IP port\n" + "example: add 224.0.1.38 8000 (multicast streaming)\n" + ); +} + +/** + * The init function of para_server's udp sender. + * + * \param s Pointer to the http sender struct. + * + * It initializes all function pointers of \a s and the list of udp targets. + */ +void udp_send_init(struct sender *s) +{ + INIT_LIST_HEAD(&targets); + s->info = udp_info; + s->help = udp_help; + s->send = udp_send; + s->pre_select = NULL; + s->post_select = NULL; + s->shutdown_clients = udp_shutdown_targets; + s->client_cmds[SENDER_ON] = udp_com_on; + s->client_cmds[SENDER_OFF] = udp_com_off; + s->client_cmds[SENDER_DENY] = NULL; + s->client_cmds[SENDER_ALLOW] = NULL; + s->client_cmds[SENDER_ADD] = udp_com_add; + s->client_cmds[SENDER_DELETE] = udp_com_delete; + sender_status = SENDER_OFF; + udp_init_target_list(); + if (!conf.udp_no_autostart_given) + sender_status = SENDER_ON; + PARA_DEBUG_LOG("udp sender init complete\n"); +} diff --git a/user_list.c b/user_list.c index 0748ef62..63bfdfc2 100644 --- a/user_list.c +++ b/user_list.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/user_list.h b/user_list.h index 36e958b2..5baceacc 100644 --- a/user_list.h +++ b/user_list.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/vss.c b/vss.c index 4ed75792..d2053493 100644 --- a/vss.c +++ b/vss.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2008 Andre Noll + * Copyright (C) 1997-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -32,7 +32,7 @@ extern struct misc_meta_data *mmd; extern void dccp_send_init(struct sender *); extern void http_send_init(struct sender *); -extern void ortp_send_init(struct sender *); +extern void udp_send_init(struct sender *); /** The list of supported senders. */ struct sender senders[] = { @@ -44,12 +44,10 @@ struct sender senders[] = { .name = "dccp", .init = dccp_send_init, }, -#ifdef HAVE_ORTP { - .name = "ortp", - .init = ortp_send_init, + .name = "udp", + .init = udp_send_init, }, -#endif { .name = NULL, } @@ -67,15 +65,25 @@ enum afs_socket_status { /** The task structure for the virtual streaming system. */ struct vss_task { + /** Copied from the -announce_time command line option. */ struct timeval announce_tv; + /** End of the announcing interval. */ struct timeval data_send_barrier; + /** End of the EOF interval. */ struct timeval eof_barrier; + /** Only used if --autoplay_delay was given. */ struct timeval autoplay_barrier; + /** Used for afs-server communication. */ int afs_socket; + /** The current state of \a afs_socket. */ enum afs_socket_status afsss; + /** The memory mapped audio file. */ char *map; + /** Used by the scheduler. */ struct task task; + /** Pointer to the header of the mapped audio file. */ const char *header_buf; + /** Length of the audio file header. */ size_t header_len; }; @@ -238,6 +246,8 @@ struct timeval *vss_chunk_time(void) static int need_to_request_new_audio_file(struct vss_task *vsst) { + struct timeval diff; + if (vsst->map) /* have audio file */ return 0; if (!vss_playing()) /* don't need one */ @@ -246,6 +256,9 @@ static int need_to_request_new_audio_file(struct vss_task *vsst) return 0; if (vsst->afsss == AFS_SOCKET_AFD_PENDING) /* already requested one */ return 0; + if (chk_barrier("autoplay_delay", &vsst->autoplay_barrier, + &diff, 1) < 0) + return 0; return 1; } @@ -288,7 +301,6 @@ static void vss_pre_select(struct sched *s, struct task *t) mmd->current_chunk = mmd->repos_request; mmd->new_vss_status_flags &= ~VSS_REPOS; } - if (need_to_request_new_audio_file(vsst)) { PARA_DEBUG_LOG("ready and playing, but no audio file\n"); para_fd_set(vsst->afs_socket, &s->wfds, &s->max_fileno); @@ -370,8 +382,10 @@ static void recv_afs_result(struct vss_task *vsst) } mmd->size = statbuf.st_size; mmd->mtime = statbuf.st_mtime; - vsst->map = para_mmap(mmd->size, PROT_READ, MAP_PRIVATE, - passed_fd, 0); + ret = para_mmap(mmd->size, PROT_READ, MAP_PRIVATE, passed_fd, + 0, &vsst->map); + if (ret < 0) + goto err; close(passed_fd); mmd->chunks_sent = 0; mmd->current_chunk = 0; @@ -379,7 +393,6 @@ static void recv_afs_result(struct vss_task *vsst) mmd->events++; mmd->num_played++; mmd->new_vss_status_flags &= (~VSS_NEXT); - tv_add(now, &vsst->announce_tv, &vsst->data_send_barrier); afh_get_header(&mmd->afd.afhi, vsst->map, &vsst->header_buf, &vsst->header_len); return; @@ -468,6 +481,9 @@ static void vss_post_select(struct sched *s, struct task *t) continue; senders[i].post_select(&s->rfds, &s->wfds); } + if ((vss_playing() && !(mmd->vss_status_flags & VSS_PLAYING)) || + (vss_next() && vss_playing())) + tv_add(now, &vsst->announce_tv, &vsst->data_send_barrier); vss_send_chunk(vsst); } @@ -508,6 +524,8 @@ void init_vss_task(int afs_socket) mmd->new_vss_status_flags |= VSS_PLAYING; ms2tv(autoplay_delay, &tmp); tv_add(now, &tmp, &vsst->autoplay_barrier); + tv_add(&vsst->autoplay_barrier, &vsst->announce_tv, + &vsst->data_send_barrier); } register_task(&vsst->task); } diff --git a/vss.h b/vss.h index f92fc809..5c917e6d 100644 --- a/vss.h +++ b/vss.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/wav_filter.c b/wav_filter.c index f5905f1f..b7329c48 100644 --- a/wav_filter.c +++ b/wav_filter.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/write.c b/write.c index 9b7a9211..238f2ab9 100644 --- a/write.c +++ b/write.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -122,7 +122,8 @@ register_check_wav: s->timeout.tv_usec = 1; } -INIT_STDERR_LOGGING(conf.loglevel_arg) +static int loglevel; +INIT_STDERR_LOGGING(loglevel) static struct writer_node_group *check_args(void) { @@ -130,6 +131,7 @@ static struct writer_node_group *check_args(void) struct writer_node_group *g = NULL; struct initial_delay_task *idt = &the_initial_delay_task; + loglevel = get_loglevel_by_name(conf.loglevel_arg); if (conf.start_time_given) { long unsigned sec, usec; if (sscanf(conf.start_time_arg, "%lu:%lu", diff --git a/write.ggo b/write.ggo deleted file mode 100644 index 8f557bb9..00000000 --- a/write.ggo +++ /dev/null @@ -1,35 +0,0 @@ -option "loglevel" l -#~~~~~~~~~~~~~~~~~~ -"set loglevel (0-6)" -int typestr="level" -default="4" -optional - -option "bufsize" b -#~~~~~~~~~~~~~~~~~ -"input buffer size" -int typestr="kilobytes" -default="64" -optional - -option "writer" w -#~~~~~~~~~~~~~~~~ -"select stream writer" -string typestr="name" -default="alsa (file if alsa is unsupported)" -optional -multiple -details=" - May be give multiple times. The same writer may be specified - more than once. -" - -option "start_time" t -#~~~~~~~~~~~~~~~~~~~~ -"defer playback" -string typestr="timeval" -optional -details=" - Start playback at given time which must be in a:b format where - a denotes seconds and b denotes microseconds since the epoch. -" diff --git a/write.h b/write.h index 7df62f9f..7ecb091b 100644 --- a/write.h +++ b/write.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/write_common.c b/write_common.c index 5896d91c..8fb6c3d1 100644 --- a/write_common.c +++ b/write_common.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/write_common.h b/write_common.h index 7f0bbc57..18e5e838 100644 --- a/write_common.h +++ b/write_common.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */