*.swp
error2.h
web_sync
+confdefs.h
+conftest
+conftest.c
+
# instead of the = operator.
PREDEFINED = HAVE_MAD \
- HAVE_ORTP \
HAVE_FAAD \
HAVE_OGGVORBIS \
__GNUC__=4 \
* 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
<<
<a href="REQUIREMENTS.html"> REQUIREMENTS </a>
>>
-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.
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
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
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"
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)
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)
*.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 < $< >$@
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
man2html $< | sed -e '/^<\/BODY>/,$$d' -e '1,/<\/HEAD><BODY>/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
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/*
$(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
----------------------------------------------
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"
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...
* 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.
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.
-----------
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.
-----------
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
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.
- 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/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2008-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2008-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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)
{
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;
+++ /dev/null
-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.
-"
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 1997-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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;
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)
{
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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;
};
/** 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};
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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,
+++ /dev/null
-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.
-"
/*
- * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
+++ /dev/null
-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.
-"
/*
- * Copyright (C) 1997-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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;
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/** 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)
{
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");
+++ /dev/null
-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
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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;
);
}
-/**
- * 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);
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);
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
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)
init_command_task(cmd_task);
if (conf.daemon_given)
- daemon_init();
+ daemonize();
register_task(&sig_task->task);
register_task(&cmd_task->task);
+++ /dev/null
-#########################
-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.<host_name>.
-
- para_audioc, the client program used to connect to para_audiod,
- opens this socket in order to talk to para_audiod. If the
- default value for para_audiod is changed, para_audioc must be
- instructed to use also \"filename\" for connecting para_audiod.
-"
-
-option "user_allow" -
-#~~~~~~~~~~~~~~~~~~~~
-"allow this 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.
-"
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
#
# 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=
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 1997-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
.status = "supervisor task"
};
-INIT_STDERR_LOGGING(ct->conf.loglevel_arg);
+static int client_loglevel; /* loglevel */
+INIT_STDERR_LOGGING(client_loglevel);
/**
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)
+++ /dev/null
-# 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="<current user>" optional
-option "server_port" p "port to connect" int typestr="port" default="2990" optional
-option "key_file" k "(default='~/.paraslash/key.<user>')" 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
/*
- * Copyright (C) 1997-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 1997-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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,
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
--- /dev/null
+/** \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;
+}
--- /dev/null
+/** \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);
/*
- * Copyright (C) 1997-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
"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",
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
+++ /dev/null
-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
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"
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=""
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"
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])
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:
~~~~~~~~~~~~~~~~~~~~~~~~
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 1997-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
#include "para.h"
#include "daemon.h"
#include <pwd.h>
-
-/* getgrnam() */
-#include <sys/types.h>
+#include <sys/types.h> /* getgrnam() */
#include <grp.h>
#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.
*
* \sa fork(2), setsid(2), dup(2).
*/
-void daemon_init(void)
+void daemonize(void)
{
pid_t pid;
int null;
}
/**
- * 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);
}
/**
*
* \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;
*/
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;
}
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);
+}
/** \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,
+};
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
+++ /dev/null
-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
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
#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
#define DCCP_SEND_ERRORS
#define HTTP_SEND_ERRORS
#define GGO_ERRORS
+#define COLOR_ERRORS
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 \
#define FD_ERRORS \
PARA_ERROR(FGETS, "fgets error"), \
- PARA_ERROR(EMPTY, "file empty"), \
- PARA_ERROR(MMAP, "mmap error"), \
#define WRITE_ERRORS \
/*
- * Copyright (C) 2003-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2003-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 1998-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1998-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
+++ /dev/null
-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
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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.
*
*/
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);
* \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);
}
/**
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);
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;
+ }
+ }
+}
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
__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);
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);
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
+++ /dev/null
-option "filename" f
-#~~~~~~~~~~~~~~~~~~
-"specify output file name"
-string typestr="filename"
-optional
-details="
- Defaults to a random filename in ~/.paraslash.
-"
-
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/** 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)
{
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;
+++ /dev/null
-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'
-"
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
*/
int (*parse_config)(int argc, char **argv, void **config);
+ /** The help texts for this filter. */
struct ggo_help help;
};
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
// 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;
/*
- * Copyright (C) 1997-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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] = {
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;
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 {
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)
+++ /dev/null
-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
-
/*
- * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2008-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
#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;
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;
+/*
+ * Copyright (C) 2008-2009 Andre Noll <maan@systemlinux.org>
+ *
+ * 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, ...);
--- /dev/null
+include(header.m4)
+<qu>
+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.
+"
+</qu>
+
+include(loglevel.m4)
+
+<qu>
+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.
+"
+</qu>
--- /dev/null
+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.
+"
--- /dev/null
+option "amp" a
+#~~~~~~~~~~~~~
+"amplification value"
+int typestr="number"
+default="32"
+optional
+details="
+ The amplification value determines the scaling factor by
+ which the amplitude of the audio stream is multiplied. The
+ formula for the scaling factor is
+
+ factor = 1 + amp / 64.
+
+ For example, an amplifiction value of zero results in a
+ scaling factor of one while an amplification value of 64
+ means to double the volume.
+"
--- /dev/null
+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
--- /dev/null
+include(header.m4)
+define(CURRENT_PROGRAM,para_audiod)
+define(DEFAULT_CONFIG_FILE,~/.paraslash/audiod.conf)
+
+<qu>
+#########################
+section "General options"
+#########################
+</qu>
+
+include(loglevel.m4)
+include(color.m4)
+include(config_file.m4)
+include(logfile.m4)
+include(daemon.m4)
+include(user.m4)
+include(group.m4)
+
+<qu>
+########################
+section "Audiod options"
+########################
+
+option "force" F
+#~~~~~~~~~~~~~~~
+"force startup"
+flag off
+details="
+ If this flag is not given, para_audiod refuses to start if the
+ well-known socket file (see the --socket option) already exists
+ because this usually means that para_audiod is already running
+ and listening on that socket. After a crash or if para_audiod
+ received a SIGKILL signal, a stale socket file might remain and
+ you have to use --force once to force startup of para_audiod.
+"
+
+option "mode" m
+#~~~~~~~~~~~~~~
+"startup mode"
+string typestr="mode"
+default="on"
+optional
+details="
+ Para_audiod supports three modes of operation: On, off and
+ standby (sb). This option selects the mode that should be
+ used on startup. If para_audiod operates in \"on\" mode, it
+ will connect to para_server in order to receive its status
+ information. If para_server announces the availability of an
+ audio stream, para_audiod will automatically download, decode
+ and play the audio stream according to the given stream I/O
+ options, see below.
+
+ In \"standby\" mode, para_audiod will only receive the
+ status information from para_server but will not download
+ the audio stream.
+
+ In \"off\" mode, para_audiod does not connect para_server at
+ all, but still listens on the local socket for connections.
+"
+
+option "socket" s
+#~~~~~~~~~~~~~~~~
+"well-known socket"
+string typestr="filename"
+optional
+details="
+ Para_audiod uses a \"well-known\" socket to listen
+ on for connections from para_audioc. This socket is a
+ special file in the file system; its location defaults to
+ /var/paraslash/audiod_sock.<host_name>.
+
+ para_audioc, the client program used to connect to para_audiod,
+ opens this socket in order to talk to para_audiod. If the
+ default value for para_audiod is changed, para_audioc must be
+ instructed to use also \"filename\" for connecting para_audiod.
+"
+
+option "user_allow" -
+#~~~~~~~~~~~~~~~~~~~~
+"allow this 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.
+"
+</qu>
--- /dev/null
+include(header.m4)
+define(CURRENT_PROGRAM,para_client)
+define(DEFAULT_CONFIG_FILE,~/.paraslash/client.conf)
+<qu>
+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="<current user>" optional
+option "server_port" p "port to connect" int typestr="port" default="2990" optional
+option "key_file" k "(default='~/.paraslash/key.<user>')" string typestr="filename" optional
+</qu>
+
+include(loglevel.m4)
+include(config_file.m4)
+
+<qu>
+option "plain" - "request an uncrypted session" flag off
+</qu>
--- /dev/null
+<qu>
+
+option "color" C
+#~~~~~~~~~~~~~~~
+"activate color output"
+enum typestr="when"
+values = "yes","no","auto"
+default = "auto"
+optional
+
+option "log_color" -
+#~~~~~~~~~~~~~~~~~~~
+"select a color for one type of log message"
+string typestr="color_spec"
+multiple
+optional
+details="
+ The format of \"color_spec\" is [fg [bg]] [attr].
+
+ Valid colors for \"fg\" and \"bg\" are \"normal\", \"black\",
+ \"red\", \"green\", \"yellow\", \"blue\", \"magenta\",
+ \"cyan\", and \"white\".
+
+ The \"attr\" value must be one of \"bold\", \"dim\", \"ul\",
+ \"blink\", \"reverse\".
+
+ Examples:
+
+ --log_color \"debug:green\"
+ --log_color \"info:yellow bold\"
+ --log_color \"notice:white red bold\"
+"
+
+</qu>
--- /dev/null
+option "blocksize" b
+#~~~~~~~~~~~~~~~~~~~
+"adjust block size"
+int typestr="number"
+default="15"
+optional
+details = "
+ Larger blocksize means fewer volume adjustments per time unit.
+"
+
+option "aggressiveness" a
+#~~~~~~~~~~~~~~~~~~~~~~~~
+ "controls the maximum amount to amplify by"
+int typestr="number"
+default="4"
+optional
+
+option "inertia" i
+#~~~~~~~~~~~~~~~~~
+ "how much inertia ramping has"
+ int typestr="number"
+default="6"
+optional
+
+option "target_level" t
+#~~~~~~~~~~~~~~~~~~~~~~
+"target signal level (0-32768)"
+int typestr="number"
+default="20000"
+optional
+
+option "damp" d
+#~~~~~~~~~~~~~~
+"if non-zero, scale down after normalizing"
+int typestr="number"
+default="0"
+optional
--- /dev/null
+<qu>
+option "config_file" c
+#~~~~~~~~~~~~~~~~~~~~~
+"(default='</qu>DEFAULT_CONFIG_FILE<qu>'"
+string typestr="filename"
+optional
+details="
+ </qu>CURRENT_PROGRAM<qu> reads its config file right after parsing
+ the options that were given at the command line. If an
+ option is given both at the command line and in the
+ config file, the value that was specified at the command line
+ takes precedence.
+"
+</qu>
--- /dev/null
+<qu>
+option "daemon" d
+#~~~~~~~~~~~~~~~~
+"run as background daemon"
+flag off
+dependon="logfile"
+details="
+ Note that </qu>CURRENT_PROGRAM<qu> refuses to start in daemon mode if no
+ logfile was specified.
+"
+</qu>
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+option "filename" f
+#~~~~~~~~~~~~~~~~~~
+"specify output file name"
+string typestr="filename"
+optional
+details="
+ Defaults to a random filename in ~/.paraslash.
+"
+
--- /dev/null
+include(header.m4)
+include(loglevel.m4)
+<qu>
+option "filter" f
+#~~~~~~~~~~~~~~~~
+"Specify filter."
+string typestr="filter_spec"
+optional
+multiple
+details="
+ May be given multiple times to 'pipe' the stream through
+ arbitrary many filters 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'
+"
+</qu>
--- /dev/null
+include(header.m4)
+include(loglevel.m4)
+
+<qu>
+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
+
+</qu>
--- /dev/null
+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
--- /dev/null
+option "group" g
+#~~~~~~~~~~~~~~~
+"set group id"
+string typestr="group"
+optional
+details="
+ This option sets the group id according to 'group'. This option
+ is silently ignored if EUID != 0. Otherwise, real/effective
+ GID and the saved set-group ID are all set to the GID given by
+ 'group'. Must not be given in the config file.
+"
+
--- /dev/null
+include(header.m4)
+define(CURRENT_PROGRAM,para_gui)
+
+<qu>
+#########################
+section "general options"
+#########################
+</qu>
+
+include(config_file.m4)
+include(loglevel.m4)
+
+<qu>
+option "timeout" t
+#~~~~~~~~~~~~~~~~~
+"set timeout"
+
+ int typestr="milliseconds"
+ default="30"
+ optional
+
+option "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
+</qu>
--- /dev/null
+changequote(<qu>,</qu>)
--- /dev/null
+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
--- /dev/null
+<qu>
+option "logfile" L
+#~~~~~~~~~~~~~~~~~
+"where to write log output"
+string typestr="filename"
+optional
+details="
+ If this option is not given, </qu>CURRENT_PROGRAM<qu> writes the log
+ messages to to stderr
+"
+</qu>
--- /dev/null
+<qu>
+option "loglevel" l
+#~~~~~~~~~~~~~~~~~~
+"set loglevel"
+string typestr="level"
+values = "debug","info","notice","warning","error","crit","emerg"
+default="warning"
+optional
+details="
+ Log only messages with severity greater or equal the given
+ value.
+
+ debug: Produces really noisy output.
+ info: Still noisy, but won't fill up the disk 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.
+"
+
+</qu>
--- /dev/null
+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,,$(<F)).cmdline \
+ --func-name $(subst .ggo,,$(<F))_cmdline_parser < $<
+
+%_recv.cmdline.h %_recv.cmdline.c: $(ggo_dir)/%_recv.ggo
+ gengetopt $(module_ggo_opts) \
+ --set-package=$(subst .ggo,,$(<F)) \
+ --arg-struct-name=$(subst .ggo,,$(<F))_args_info \
+ --file-name=$(subst .ggo,,$(<F)).cmdline \
+ --func-name $(subst .ggo,,$(<F))_cmdline_parser < $<
+
+%_filter.cmdline.h %_filter.cmdline.c: $(ggo_dir)/%_filter.ggo
+ gengetopt $(module_ggo_opts) \
+ --set-package=$(subst .ggo,,$(<F)) \
+ --arg-struct-name=$(subst .ggo,,$(<F))_args_info \
+ --file-name=$(subst .ggo,,$(<F)).cmdline \
+ --func-name $(subst _filter.ggo,,$(<F))_cmdline_parser < $<
+%_write.cmdline.h %_write.cmdline.c: $(ggo_dir)/%_write.ggo
+ gengetopt -S $(module_ggo_opts) \
+ --set-package=$(subst .ggo,,$(<F)) \
+ --arg-struct-name=$(subst .ggo,,$(<F))_args_info \
+ --file-name=$(subst .ggo,,$(<F)).cmdline \
+ --func-name $(subst _write.ggo,,$(<F))_cmdline_parser < $<
+
+%.cmdline.h %.cmdline.c: $(ggo_dir)/%.ggo
+ case $(<F) 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 $(<F) != 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)" < $<
+
+$(ggo_dir)/server.ggo $(ggo_dir)/audiod.ggo: \
+ $(ggo_dir)/loglevel.m4 $(ggo_dir)/color.m4 \
+ $(ggo_dir)/config_file.m4 $(ggo_dir)/logfile.m4 \
+ $(ggo_dir)/daemon.m4 $(ggo_dir)/user.m4 \
+ $(ggo_dir)/group.m4
+
+$(ggo_dir)/afh.ggo: $(ggo_dir)/loglevel.m4
+$(ggo_dir)/audioc.ggo: $(ggo_dir)/loglevel.m4
+$(ggo_dir)/filter.ggo: $(ggo_dir)/loglevel.m4
+$(ggo_dir)/fsck.ggo: $(ggo_dir)/loglevel.m4
+$(ggo_dir)/gui.ggo: $(ggo_dir)/loglevel.m4
+$(ggo_dir)/recv.ggo: $(ggo_dir)/loglevel.m4
+$(ggo_dir)/write.ggo: $(ggo_dir)/loglevel.m4
+$(ggo_dir)/client.ggo: $(ggo_dir)/loglevel.m4 $(ggo_dir)/config_file.m4
+
+$(ggo_dir)/%.ggo: $(ggo_dir)/%.m4 $(ggo_dir)/header.m4
+ (cd $(ggo_dir) && m4 $(<F)) > $@
--- /dev/null
+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.
+"
--- /dev/null
+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.
+"
--- /dev/null
+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
--- /dev/null
+include(header.m4)
+include(loglevel.m4)
+
+<qu>
+option "receiver" r
+"Select receiver"
+string typestr="receiver_spec"
+default="http"
+optional
+details="
+ Any options for the selected receiver must
+ be quoted. Example:
+
+ -r 'http -i www.paraslash.org -p 8009'
+"
+</qu>
--- /dev/null
+include(header.m4)
+define(CURRENT_PROGRAM,para_server)
+define(DEFAULT_CONFIG_FILE,~/.paraslash/server.conf)
+
+<qu>
+#########################
+section "General options"
+#########################
+</qu>
+
+include(loglevel.m4)
+include(color.m4)
+include(daemon.m4)
+include(user.m4)
+include(group.m4)
+
+<qu>
+option "port" p
+#~~~~~~~~~~~~~~
+"listening port"
+int typestr="portnumber"
+default="2990"
+optional
+details="
+ para_server listens on this tcp port for incoming connections
+ from clients such as para_client. If the default port is
+ changed, the corresponding option of para_client must be used
+ to connect to para_server.
+"
+
+#############################
+section "Configuration files"
+#############################
+</qu>
+
+include(logfile.m4)
+include(config_file.m4)
+
+<qu>
+option "user_list" -
+#~~~~~~~~~~~~~~~~~~~
+"(default='~/.paraslash/server.users')"
+
+string typestr="filename"
+optional
+
+
+##################################
+section "virtual streaming system"
+##################################
+
+
+option "autoplay" a
+#~~~~~~~~~~~~~~~~~~
+"start playing on startup"
+flag off
+
+option "autoplay_delay" -
+#~~~~~~~~~~~~~~~~~~~~~~~~
+"time to wait before streaming"
+int typestr="ms"
+default="0"
+optional
+dependon="autoplay"
+details="
+ If para_server is started with the autoplay option, this option
+ may be used to set up a delay before para_server streams its
+ first audio file. This is useful for example if para_server
+ and para_audiod are started during system startup. The delay
+ time should be choosen large enough so that para_audiod is
+ already up when para_server starts to stream. Of course, this
+ option depends on the autoplay option.
+"
+option "announce_time" A
+#~~~~~~~~~~~~~~~~~~~~~~~
+"grace time for clients"
+
+int typestr="ms"
+default="300"
+optional
+details="
+ Clients such as para_audiod connect to para_server and execute
+ the stat command to find out whether an audio stream is
+ currently available. This sets the delay betweeen announcing
+ the stream via the output of the stat command and sending
+ the first chunk of data.
+"
+
+#############################
+section "audio file selector"
+#############################
+
+option "afs_database_dir" D
+#~~~~~~~~~~~~~~~~~~~~~~~~~~
+"location of the database"
+string typestr="path"
+optional
+details="
+ Where para_server should look for the osl database of the audio
+ file selector. The default is '~/.paraslash/afs_database'.
+"
+
+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="<specifier>/<name>"
+optional
+
+details="
+ The argument of this option must be prefixed with either 'p/'
+ or 'm/' to indicate whether a playlist or a mood should be
+ loaded. Example:
+ --afs_initial_mode p/foo
+ loads the playlist named 'foo'.
+"
+
+#####################
+section "http sender"
+#####################
+
+
+option "http_port" -
+#~~~~~~~~~~~~~~~~~~~
+"tcp port for http streaming"
+int typestr="portnumber"
+default="8000"
+optional
+details="
+ The http sender of para_server listens on this port for
+ incoming connections. Clients are expected to send the usual
+ http request message such as 'GET / HTTP/'.
+"
+
+option "http_default_deny" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"make the http ACL a whitelist"
+flag off
+details="
+ The default is to use blacklists instead, i.e. connections
+ to the http sender are allowed unless the connecting host
+ matches a pattern given by a http_access option. This allows
+ to use access control the other way round: Connections are
+ denied from hosts which are not explicitly allowed by one or
+ more http_access options.
+"
+
+option "http_access" -
+#~~~~~~~~~~~~~~~~~~~~~
+"add an entry to the http ACL"
+string typestr="a.b.c.d/n"
+optional
+multiple
+details="
+ Add given host/network to access control list (whitelist if
+ http_default_deny was given, blacklist otherwise) before
+ opening the tcp port. This option can be given multiple
+ times. Example: '192.168.0.0/24' whitelists/blacklists the
+ 256 hosts 192.168.0.x
+"
+
+option "http_no_autostart" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"do not open tcp port on startup"
+flag off
+details="
+ If this option is given, the http sender does not listen on
+ its tcp port. It may be instructed to open this port at a
+ later time by using the sender command.
+"
+
+option "http_max_clients" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~
+"maximal number of connections"
+int typestr="number"
+default="-1"
+optional
+details="
+ The http sender will refuse connections if already that number
+ of clients are currently connected. A non-positive value
+ (the default) allows an unlimited number of simultaneous
+ connections.
+"
+
+#####################
+section "dccp sender"
+#####################
+
+
+option "dccp_port" -
+#~~~~~~~~~~~~~~~~~~~
+"port for dccp streaming"
+int typestr="portnumber"
+default="8000"
+optional
+details="
+ See http_port for details.
+"
+
+option "dccp_default_deny" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"make the dccp ACL a whitelist"
+flag off
+details="
+ See http_default_deny for details.
+"
+
+option "dccp_access" -
+#~~~~~~~~~~~~~~~~~~~~~
+"add an entry to the dccp ACL"
+string typestr="a.b.c.d/n"
+optional
+multiple
+details="
+ See http_access for details.
+"
+
+option "dccp_max_clients" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~
+"maximal number of connections"
+int typestr="number"
+default="-1"
+optional
+details="
+ See http_max_clients for details.
+"
+
+####################
+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.
+"
+</qu>
--- /dev/null
+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
--- /dev/null
+<qu>
+option "user" u
+#~~~~~~~~~~~~~~
+"run as the given user"
+string typestr="name"
+optional
+details="
+ </qu>CURRENT_PROGRAM<qu> does not need any special privileges.
+
+ If started as root (EUID == 0) this option must
+ be given at the command line (not in the configuration
+ file) so that </qu>CURRENT_PROGRAM<qu> can drop the root
+ privileges right after parsing the command line options,
+ but before parsing the configuration file. In this case,
+ real/effective/saved UID are all set to the UID of 'name'. As
+ the configuration file is read afterwards, those options that
+ have a default value depending on the UID (e.g. the directory
+ for the configuration file) are computed by using the uid of
+ 'name'. This option has no effect if </qu>CURRENT_PROGRAM<qu>
+ is started as a non-root user (i.e. EUID != 0).
+" </qu>
--- /dev/null
+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.
+"
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
+++ /dev/null
-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
/*
- * Copyright (C) 1998-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1998-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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:
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);
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);
}
/*
};
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);
+++ /dev/null
-#------------------------
-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
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
+++ /dev/null
-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
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2003-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2003-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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;
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
+++ /dev/null
-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.
-"
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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;
+}
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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);
/*
- * Copyright (C) 2004-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2004-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
+++ /dev/null
-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.
-"
+++ /dev/null
-/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
- *
- * 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)
+++ /dev/null
-/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-/** \file ortp_recv.c paraslash's ortp receiver */
-
-#include <ortp/ortp.h>
-#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();
-}
+++ /dev/null
-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
+++ /dev/null
-/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file ortp_send.c para_server's ortp sender */
-
-#include <ortp/ortp.h>
-#include <ortp/port.h>
-
-#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");
-}
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
#include <sys/mman.h>
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
+++ /dev/null
-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
/*
- * Copyright (C) 1997-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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
/** 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" \
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
#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;
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);
+++ /dev/null
-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'
-"
-
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/**
* Describes one supported paraslash receiver.
*
- * \sa http_recv.c, ortp_recv.c
+ * \sa http_recv.c, udp_recv.c
*/
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);
/**
*/
void (*post_select)(struct sched *s, struct task *t);
+ /** The two help texts of this receiver. */
struct ggo_help help;
};
#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 */
#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);
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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;
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/**
* 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. */
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);
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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)
{
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;
}
}
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;
/*
- * Copyright (C) 1997-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
* - 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,
#include <signal.h>
#include <dirent.h>
+#include <sys/time.h>
#include "para.h"
#include "error.h"
#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;
/** 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;
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));
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
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,
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);
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);
/* 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");
+++ /dev/null
-#########################
-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="<specifier>/<name>"
-optional
-
-details="
- The argument of this option must be prefixed with either 'p/'
- or 'm/' to indicate whether a playlist or a mood should be
- loaded. Example:
- --afs_initial_mode p/foo
- loads the playlist named 'foo'.
-"
-
-#####################
-section "http sender"
-#####################
-
-
-option "http_port" -
-#~~~~~~~~~~~~~~~~~~~
-"tcp port for http streaming"
-int typestr="portnumber"
-default="8000"
-optional
-details="
- The http sender of para_server listens on this port for
- incoming connections. Clients are expected to send the usual
- http request message such as 'GET / HTTP/'.
-"
-
-option "http_default_deny" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"make the http ACL a whitelist"
-flag off
-details="
- The default is to use blacklists instead, i.e. connections
- to the http sender are allowed unless the connecting host
- matches a pattern given by a http_access option. This allows
- to use access control the other way round: Connections are
- denied from hosts which are not explicitly allowed by one or
- more http_access options.
-"
-
-option "http_access" -
-#~~~~~~~~~~~~~~~~~~~~~
-"add an entry to the http ACL"
-string typestr="a.b.c.d/n"
-optional
-multiple
-details="
- Add given host/network to access control list (whitelist if
- http_default_deny was given, blacklist otherwise) before
- opening the tcp port. This option can be given multiple
- times. Example: '192.168.0.0/24' whitelists/blacklists the
- 256 hosts 192.168.0.x
-"
-
-option "http_no_autostart" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"do not open tcp port on startup"
-flag off
-details="
- If this option is given, the http sender does not listen on
- its tcp port. It may be instructed to open this port at a
- later time by using the sender command.
-"
-
-option "http_max_clients" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"maximal number of connections"
-int typestr="number"
-default="-1"
-optional
-details="
- The http sender will refuse connections if already that number
- of clients are currently connected. A non-positive value
- (the default) allows an unlimited number of simultaneous
- connections.
-"
-
-#####################
-section "dccp sender"
-#####################
-
-
-option "dccp_port" -
-#~~~~~~~~~~~~~~~~~~~
-"port for dccp streaming"
-int typestr="portnumber"
-default="8000"
-optional
-details="
- See http_port for details.
-"
-
-option "dccp_default_deny" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"make the dccp ACL a whitelist"
-flag off
-details="
- See http_default_deny for details.
-"
-
-option "dccp_access" -
-#~~~~~~~~~~~~~~~~~~~~~
-"add an entry to the dccp ACL"
-string typestr="a.b.c.d/n"
-optional
-multiple
-details="
- See http_access for details.
-"
-
-option "dccp_max_clients" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"maximal number of connections"
-int typestr="number"
-default="-1"
-optional
-details="
- See http_max_clients for details.
-"
-
-#####################
-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.
-"
/*
- * Copyright (C) 1997-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
extern struct server_args_info conf;
__noreturn void handle_connect(int fd, const char *peername);
+void parse_config_or_die(int override);
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2004-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2004-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2004-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2004-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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.
*
*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;
+}
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
__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 *);
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);
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
--- /dev/null
+/*
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
+ *
+ * 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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+/** \file udp_recv.c Paraslash's udp receiver */
+
+#include <dirent.h>
+
+#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
+ };
+}
--- /dev/null
+/*
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file udp_send.c Para_server's udp sender. */
+
+
+#include <sys/time.h>
+#include <dirent.h>
+
+#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");
+}
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 1997-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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[] = {
.name = "dccp",
.init = dccp_send_init,
},
-#ifdef HAVE_ORTP
{
- .name = "ortp",
- .init = ortp_send_init,
+ .name = "udp",
+ .init = udp_send_init,
},
-#endif
{
.name = NULL,
}
/** 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;
};
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 */
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;
}
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);
}
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;
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;
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);
}
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);
}
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
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)
{
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",
+++ /dev/null
-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.
-"
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/