]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 'maint'
authorAndre Noll <maan@tuebingen.mpg.de>
Mon, 14 Nov 2022 21:18:04 +0000 (22:18 +0100)
committerAndre Noll <maan@tuebingen.mpg.de>
Mon, 14 Nov 2022 21:18:04 +0000 (22:18 +0100)
To bring in the 0.5.9 tag.

* maint:
  paraslash 0.5.9

138 files changed:
.gitignore
Doxyfile
Makefile.in
Makefile.real
NEWS.md
aac_afh.c
aacdec_filter.c
acl.c
afh.c
afh.h
afh_common.c
afh_recv.c
afs.c
aft.c
alsa_mix.c
alsa_write.c
amp_filter.c
ao_write.c
attribute.c
audioc.c
audiod.c
audiod.h
audiod_command.c
autogen.sh
base64.c
bitstream.c
blob.c
buffer_tree.c
check_wav.c
check_wav.h
chunk_queue.c
client.c
client_common.c
close_on_fork.c
command.c
compress_filter.c
configure.ac
crypt.h
crypt_common.c
daemon.c
daemon.h
dccp_recv.c
dccp_send.c
error.h
fd.c
fd.h
fec.c
fecdec_filter.c
file_write.c
filter.c
filter.h
filter_common.c
flac_afh.c
flacdec_filter.c
gcrypt.c
grab_client.c
gui.c
gui_theme.c
http_recv.c
http_send.c
imdct.c
interactive.c
interactive.h
list.h
m4/lls/audiod_cmd.suite.m4
m4/lls/include/com_ll.m4 [new file with mode: 0644]
m4/lls/makefile
m4/lls/server.suite.m4
m4/lls/server_cmd.suite.m4
m4/lls/upgrade_db.suite.m4 [new file with mode: 0644]
mm.c [deleted file]
mm.h [deleted file]
mood.c
mp.c
mp.h
mp3_afh.c
mp3dec_filter.c
mp4.c [new file with mode: 0644]
mp4.h [new file with mode: 0644]
net.c
net.h
ogg_afh_common.c
oggdec_filter.c
openssl.c
opus_afh.c
opusdec_filter.c
oss_mix.c
oss_write.c
para.h
play.c
prebuffer_filter.c
recv.c
recv.h
recv_common.c
resample_filter.c
ringbuffer.c
sched.c
sched.h
score.c
send.h
send_common.c
server.c
server.h
sideband.c
signal.c
signal.h
spx_afh.c
spxdec_filter.c
stdin.c
stdout.c
string.c
string.h
sync_filter.c
t/t0004-server.sh
udp_recv.c
udp_send.c
upgrade_db.c [new file with mode: 0644]
user_list.c
vss.c
wav_filter.c
web/about.in.html
web/documentation.in.html
web/download.in.html
web/footer.html
web/header.html
web/header2.html
web/images/paraslash.ico
web/images/paraslash.png [deleted file]
web/images/paraslash.svg [new file with mode: 0644]
web/manual.md
web/para.css
wma_afh.c
wmadec_filter.c
write.c
write.h
write_common.c
yy/mp.lex
yy/mp.y

index bd5e04801c4bec2714dd515810296f1bc89cbab0..8f8d0af736cee205b638502171eea526d613cc22 100644 (file)
@@ -24,3 +24,4 @@ confdefs.h
 conftest
 conftest.c
 git-version.h
+*-local*
index b11683e424443baef8754b34097ad3cb8dbed4fe..e147548f31f090b1fc351a537caa894d3fc87f1f 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
@@ -2019,8 +2019,7 @@ INCLUDE_FILE_PATTERNS  =
 # recursively expanded use the := operator instead of the = operator.
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-PREDEFINED             = __GNUC__=4 \
-                         __GNUC_MINOR__=4
+PREDEFINED             =
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The
index d4a83a776eec0dfd602aa4329a72718cc15ea7f4..c618561d4dfeb18ebeb31f93a90ac69e32f84c63 100644 (file)
@@ -23,6 +23,7 @@ audiod_objs := @audiod_objs@
 audioc_objs := @audioc_objs@
 mixer_objs := @mixer_objs@
 server_objs := @server_objs@
+upgrade_db_objs := @upgrade_db_objs@
 write_objs := @write_objs@
 afh_objs := @afh_objs@
 play_objs := @play_objs@
@@ -67,4 +68,6 @@ curses_ldflags := @curses_ldflags@
 crypto_ldflags := @crypto_ldflags@
 iconv_ldflags := @iconv_ldflags@
 
+ENABLE_UBSAN := @ENABLE_UBSAN@
+
 include Makefile.real
index b88de22533048979bcfef8f3396b7608297afb3b..bf3cb6e0ff0616f3618c778c11731651d9a4151c 100644 (file)
@@ -10,6 +10,7 @@ endif
 .SHELLFLAGS := -ec
 
 LOGLEVELS := LL_DEBUG,LL_INFO,LL_NOTICE,LL_WARNING,LL_ERROR,LL_CRIT,LL_EMERG
+SEVERITIES := \"debug\",\"info\",\"notice\",\"warning\",\"error\",\"crit\",\"emerg\"
 vardir := /var/paraslash
 mandir := $(datarootdir)/man/man1
 MKDIR_P := mkdir -p
@@ -20,7 +21,7 @@ uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS")
 uname_rs := $(shell uname -rs)
 cc_version := $(shell $(CC) --version | head -n 1)
 GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h)
-COPYRIGHT_YEAR := 2021
+COPYRIGHT_YEAR := 2022
 
 ifeq ("$(origin O)", "command line")
        build_dir := $(O)
@@ -55,6 +56,7 @@ gui_objs += gui.lsg.o
 play_objs += $(addsuffix _cmd.lsg.o, recv filter play write) play.lsg.o
 recv_objs += recv_cmd.lsg.o recv.lsg.o
 server_objs += server_cmd.lsg.o server.lsg.o
+upgrade_db_objs += upgrade_db.lsg.o
 write_objs += write_cmd.lsg.o write.lsg.o
 
 cmd_suites := $(addsuffix _cmd, audiod server play recv filter write)
@@ -77,6 +79,7 @@ audiod_objs := $(addprefix $(object_dir)/, $(audiod_objs))
 audioc_objs := $(addprefix $(object_dir)/, $(audioc_objs))
 mixer_objs := $(addprefix $(object_dir)/, $(mixer_objs))
 server_objs := $(addprefix $(object_dir)/, $(server_objs))
+upgrade_db_objs := $(addprefix $(object_dir)/, $(upgrade_db_objs))
 write_objs := $(addprefix $(object_dir)/, $(write_objs))
 afh_objs := $(addprefix $(object_dir)/, $(afh_objs))
 play_objs := $(addprefix $(object_dir)/, $(play_objs))
@@ -110,6 +113,7 @@ CPPFLAGS += -DBINDIR='"$(bindir)"'
 CPPFLAGS += -DCOPYRIGHT_YEAR='"$(COPYRIGHT_YEAR)"'
 CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
 CPPFLAGS += -DLOGLEVELS='$(LOGLEVELS)'
+CPPFLAGS += -DSEVERITIES=$(SEVERITIES)
 CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
 CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
 CPPFLAGS += -I$(lls_suite_dir)
@@ -117,6 +121,7 @@ CPPFLAGS += -I$(yy_build_dir)
 CPPFLAGS += $(lopsub_cppflags)
 
 STRICT_CFLAGS += -fno-strict-aliasing
+STRICT_CFLAGS += -ftrapv
 STRICT_CFLAGS += -g
 STRICT_CFLAGS += -Os
 STRICT_CFLAGS += -Wundef -W -Wuninitialized
@@ -129,6 +134,11 @@ STRICT_CFLAGS += -Wno-sign-compare -Wno-unknown-pragmas
 STRICT_CFLAGS += -Wdeclaration-after-statement
 STRICT_CFLAGS += -Wformat -Wformat-security -Wmissing-format-attribute
 
+ifeq ($(ENABLE_UBSAN), yes)
+       STRICT_CFLAGS += -fsanitize=undefined
+       LDFLAGS += -lubsan
+endif
+
 ifeq ($(uname_s),Linux)
        # these cause warnings on *BSD
        CPPFLAGS += -Wunused-macros
@@ -185,79 +195,52 @@ $(man_dir)/para_%.1: $(lls_suite_dir)/%.lsg.man \
 
 $(object_dir)/%.o: %.c | $(object_dir)
 
-$(object_dir)/opus%.o: CPPFLAGS += $(opus_cppflags)
-$(object_dir)/gui.o $(object_dir)/gui%.o \
-: CPPFLAGS += $(curses_cppflags)
-$(object_dir)/spx%.o: CPPFLAGS += $(speex_cppflags)
-$(object_dir)/flac%.o: CPPFLAGS += $(flac_cppflags)
-
-$(object_dir)/mp3_afh.o: CPPFLAGS += $(id3tag_cppflags)
-$(object_dir)/openssl.o: CPPFLAGS += $(openssl_cppflags)
-$(object_dir)/gcrypt.o: CPPFLAGS += $(gcrypt_cppflags)
-$(object_dir)/ao_write.o: CPPFLAGS += $(ao_cppflags)
-$(object_dir)/alsa%.o: CPPFLAGS += $(alsa_cppflags)
-
-$(object_dir)/interactive.o \
-: CPPFLAGS += $(readline_cppflags)
-
-$(object_dir)/resample_filter.o \
-: CPPFLAGS += $(samplerate_cppflags)
-
-$(object_dir)/oss_write.o \
-: CPPFLAGS += $(oss_cppflags)
-
-$(object_dir)/ao_write.o \
-: CPPFLAGS += $(ao_cppflags) $(pthread_cppflags)
-
-$(object_dir)/mp3dec_filter.o \
-: CPPFLAGS += $(mad_cppflags)
-
-$(object_dir)/aacdec_filter.o \
-$(object_dir)/aac_afh.o \
-: CPPFLAGS += $(faad_cppflags)
-
-$(object_dir)/ogg_afh.o \
-$(object_dir)/oggdec_filter.o \
-: CPPFLAGS += $(vorbis_cppflags)
-
-$(object_dir)/spx_common.o \
-$(object_dir)/spxdec_filter.o \
-$(object_dir)/spx_afh.o \
-$(object_dir)/oggdec_filter.o \
-$(object_dir)/ogg_afh.o \
-$(object_dir)/ogg_afh_common.o \
-$(object_dir)/opus%.o \
-: CPPFLAGS += $(ogg_cppflags)
-
-$(object_dir)/afs.o \
-$(object_dir)/aft.o \
-$(object_dir)/attribute.o \
-$(object_dir)/blob.o  \
-$(object_dir)/mood.o \
-$(object_dir)/playlist.o \
-$(object_dir)/score.o \
-$(object_dir)/server.o \
-$(object_dir)/vss.o \
-$(object_dir)/command.o \
-$(object_dir)/http_send.o \
-$(object_dir)/dccp_send.o \
-$(object_dir)/udp_send.o \
-$(object_dir)/send_common.o \
-$(object_dir)/mm.o \
-: CPPFLAGS += $(osl_cppflags)
-
-$(object_dir)/compress_filter.o: CFLAGS += -O3
-
-$(object_dir)/%.o: %.c | $(object_dir) $(dep_dir) $(lsg_h) $(yy_h)
+OD = $(addsuffix .d, $(addprefix $(dep_dir)/, $(1))) \
+       $(addsuffix .o, $(addprefix $(object_dir)/, $(1)))
+
+$(call OD, opus%): CPPFLAGS += $(opus_cppflags)
+$(call OD, gui gui%): CPPFLAGS += $(curses_cppflags)
+$(call OD, spx%): CPPFLAGS += $(speex_cppflags)
+$(call OD, flac%): CPPFLAGS += $(flac_cppflags)
+$(call OD, mp3_afh): CPPFLAGS += $(id3tag_cppflags)
+$(call OD, openssl): CPPFLAGS += $(openssl_cppflags)
+$(call OD, gcrypt): CPPFLAGS += $(gcrypt_cppflags)
+$(call OD, ao_write): CPPFLAGS += $(ao_cppflags)
+$(call OD, alsa%): CPPFLAGS += $(alsa_cppflags)
+$(call OD, interactive): CPPFLAGS += $(readline_cppflags)
+$(call OD, resample_filter): CPPFLAGS += $(samplerate_cppflags)
+$(call OD, oss_write): CPPFLAGS += $(oss_cppflags)
+$(call OD, ao_write): CPPFLAGS += $(ao_cppflags) $(pthread_cppflags)
+$(call OD, mp3dec_filter): CPPFLAGS += $(mad_cppflags)
+$(call OD, aacdec_filter aac_afh): CPPFLAGS += $(faad_cppflags)
+$(call OD, ogg_afh oggdec_filter): CPPFLAGS += $(vorbis_cppflags)
+$(call OD, spx_common spxdec_filter spx_afh oggdec_filter ogg_afh \
+       ogg_afh_common opus%): CPPFLAGS += $(ogg_cppflags)
+$(call OD, afs aft attribute blob mood playlist score server vss command \
+       http_send dccp_send udp_send send_common mm.o): \
+       CPPFLAGS += $(osl_cppflags)
+
+$(call OD, compress_filter): CFLAGS += -O3
+
+define CC_CMD
        $(call SAY, CC $<)
-       $(CC) -c -o $@ -MMD -MF $(dep_dir)/$(*F).d -MT $@ $(CPPFLAGS) \
-               $(STRICT_CFLAGS) $(CFLAGS) $<
+       $(CC) -c -o $(object_dir)/$(*F).o -MMD -MF \
+               $(dep_dir)/$(*F).d -MT $(object_dir)/$(*F).o \
+               $(CPPFLAGS) $(STRICT_CFLAGS) $(CFLAGS) $<
+endef
+CC_PREREQUISITES := %.c | $(object_dir) $(dep_dir) $(lsg_h) $(yy_h)
+# These two have the same prerequisites and the same recipe. There should be a
+# better way to write this.
+$(object_dir)/%.o: $(CC_PREREQUISITES)
+       $(CC_CMD)
+$(dep_dir)/%.d: $(CC_PREREQUISITES)
+       $(CC_CMD)
 
 para_recv para_afh para_play para_server: LDFLAGS += $(id3tag_ldflags)
 para_write para_play para_audiod \
 : LDFLAGS += $(ao_ldflags) $(pthread_ldflags)
 para_client para_audioc para_play : LDFLAGS += $(readline_ldflags)
-para_server: LDFLAGS += $(osl_ldflags)
+para_server para_upgrade_db: LDFLAGS += $(osl_ldflags)
 para_gui: LDFLAGS += $(curses_ldflags)
 para_server \
 para_client \
@@ -294,6 +277,7 @@ para_gui \
 para_play \
 para_recv \
 para_server \
+para_upgrade_db \
 para_write \
 : LDFLAGS += $(lopsub_ldflags)
 
@@ -329,7 +313,7 @@ distclean: clean
        $(call SAY, DISTCLEAN)
        rm -f Makefile autoscan.log config.status config.log
        rm -f config.h configure config.h.in
-maintainer-clean: distclean
+maintainer-clean: distclean test-clean
        $(call SAY, MAINTAINER-CLEAN)
        rm -f *.tar.bz2 *.tar.xz
        rm -f GPATH GRTAGS GSYMS GTAGS
diff --git a/NEWS.md b/NEWS.md
index f2341e3347fca0ec03688295937e3465bef75a04..45e46bc627a13398a24dbe9dc220beeeb657bed4 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,6 +1,85 @@
 NEWS
 ====
 
+------------------------------------------
+0.7.2 (to be announced) "optical friction"
+------------------------------------------
+
+- Minor cleanup of the net subsystem.
+
+Downloads:
+[tarball](./releases/paraslash-git.tar.xz)
+
+--------------------------------------
+0.7.1 (2022-10-03) "digital spindrift"
+--------------------------------------
+
+The two new ll commands and the internal mp4ff library are the most
+user-visible changes of this release. On top of that there are two
+core changes which aim to improve the robustness of the code but which
+are otherwise invisible: the switch from select(2) to poll(2) and the
+revised memory allocation API which checks for integer overflows. The
+release also comes with a slight change to the build system and the
+usual mix of bug fixes and minor improvements not mentioned here.
+
+- The autogen.sh script now only creates the autoconf specific files
+  but no longer runs configure, make and the test suite.
+- A stripped down copy of the discontinued libmp4ff library has become
+  part of the paraslash code base. As a result it is no longer necessary
+  to install faad from source to get support for aac/m4a files. The
+  faad decoder package must still be installed.
+- The log level of the running daemon can now be changed with the
+  new ll command. It is available for para_server and para_audiod.
+- All calls to select(2) have been replaced by calls to poll(2)
+  to avoid known shortcomings of the select API.
+- All allocation functions now check for integer overflow. Since this
+  requires support from the compiler, the oldest supported gcc version
+  has been bumped to gcc-5.4 (released in 2015).
+
+Downloads:
+[tarball](./releases/paraslash-0.7.1.tar.xz),
+[signature](./releases/paraslash-0.7.1.tar.xz.asc)
+
+----------------------------------
+0.7.0 (2022-03-12) "seismic orbit"
+----------------------------------
+
+The major incompatible change which requires to bump the major version
+is the switch from sha1 to sha256, see below for details. However,
+there are many other improvements, the usual amount of bug fixes and
+a couple of new features.
+
+- Starting with paraslash-0.7.0, the sha256 hash value of each known
+  audio file is stored in the database while older versions employed the
+  sha1 hash algorithm which has been considered insecure since 2005
+  and should no longer be used today. The switch from sha1 to sha256
+  requires users to upgrade their database using the new para_upgrade_db
+  program, followed by re-adding all files to recompute the hashes. With
+  this approach all metadata stored in the database (last played date,
+  num played value, moods, playlists, attributes etc.) are kept. An
+  simpler alternative is to start over from scratch by running the
+  "init" command but this will lose these metadata.
+- Server and client now hash the session keys with sha256 rather
+  than sha1 during the initial handshake. This feature is optional and
+  backwards compatible: old clients can still connect to a new server
+  (using sha1). Also new clients can connect to an old server (again
+  using sha1).
+- The new "duration" keyword of the mood grammar makes it possible to
+  impose a constraint on the duration of the admissible files.
+- The long deprecated version 1 mood syntax is no longer supported.
+- Paraslash writers handle early end-of-file more gracefully.
+- The alsa writer no longer warns about spurious underruns.
+- The score formula  of the audio file selector has been reworked.
+- Cleanups of the doubly linked lists code.
+- New option for configure: --enable-ubsan to detect and report undefined
+  behaviour.
+- The "tasks" server command has been removed.
+- The fancy new logo and a minor overhaul of the web pages.
+
+Downloads:
+[tarball](./releases/paraslash-0.7.0.tar.xz),
+[signature](./releases/paraslash-0.7.0.tar.xz.asc)
+
 --------------------------------------
 0.6.4 (2021-11-04) "fuzzy calibration"
 --------------------------------------
index 2b3dd2cc538a57a233813b77adcb332bd77929ce..c4301a2f178257b19d56b3cf297da3e1eaff337c 100644 (file)
--- a/aac_afh.c
+++ b/aac_afh.c
 #include <neaacdec.h>
 
 #include "para.h"
-
-/* To get the mp4ff_tag_t and mp4ff_metadata_t typedefs. */
-#define USE_TAGGING
-#include <mp4ff.h>
-
+#include "mp4.h"
 #include "error.h"
 #include "portable_io.h"
 #include "afh.h"
@@ -26,63 +22,44 @@ struct aac_afh_context {
        const void *map;
        size_t mapsize;
        size_t fpos;
-       int32_t track;
-       mp4ff_t *mp4ff;
-       mp4AudioSpecificConfig masc;
-       mp4ff_callback_t cb;
+       struct mp4 *mp4;
+       struct mp4_callback cb;
 };
 
-static uint32_t aac_afh_read_cb(void *user_data, void *dest, uint32_t want)
+static ssize_t aac_afh_read_cb(void *user_data, void *dest, size_t want)
 {
        struct aac_afh_context *c = user_data;
-       uint32_t have, rv;
+       size_t have, rv;
 
-       if (want == 0 || c->fpos >= c->mapsize) {
-               PARA_INFO_LOG("failed attempt to read %u bytes @%zu\n", want,
-                       c->fpos);
-               errno = EAGAIN;
-               return -1;
-       }
+       if (want == 0 || c->fpos >= c->mapsize)
+               return 0;
        have = c->mapsize - c->fpos;
        rv = PARA_MIN(have, want);
-       PARA_DEBUG_LOG("reading %u bytes @%zu\n", rv, c->fpos);
+       PARA_DEBUG_LOG("reading %zu bytes @%zu\n", rv, c->fpos);
        memcpy(dest, c->map + c->fpos, rv);
        c->fpos += rv;
        return rv;
 }
 
-static uint32_t aac_afh_seek_cb(void *user_data, uint64_t pos)
+static off_t aac_afh_seek_cb(void *user_data, off_t offset, int whence)
 {
        struct aac_afh_context *c = user_data;
-       c->fpos = pos;
-       return 0;
-}
 
-static int32_t aac_afh_get_track(mp4ff_t *mp4ff, mp4AudioSpecificConfig *masc)
-{
-       int32_t i, rc, num_tracks = mp4ff_total_tracks(mp4ff);
-
-       assert(num_tracks >= 0);
-       for (i = 0; i < num_tracks; i++) {
-               unsigned char *buf = NULL;
-               unsigned buf_size = 0;
-
-               mp4ff_get_decoder_config(mp4ff, i, &buf, &buf_size);
-               if (buf) {
-                       rc = NeAACDecAudioSpecificConfig(buf, buf_size, masc);
-                       free(buf);
-                       if (rc < 0)
-                               continue;
-                       return i;
-               }
-       }
-       return -1; /* no audio track */
+       if (whence == SEEK_SET)
+               c->fpos = offset;
+       else if (whence == SEEK_CUR)
+               c->fpos += offset;
+       else if (whence == SEEK_END)
+               c->fpos = c->mapsize + offset;
+       else
+               assert(false);
+       return c->fpos;
 }
 
 static int aac_afh_open(const void *map, size_t mapsize, void **afh_context)
 {
        int ret;
-       struct aac_afh_context *c = para_malloc(sizeof(*c));
+       struct aac_afh_context *c = alloc(sizeof(*c));
 
        c->map = map;
        c->mapsize = mapsize;
@@ -91,18 +68,11 @@ static int aac_afh_open(const void *map, size_t mapsize, void **afh_context)
        c->cb.seek = aac_afh_seek_cb;
        c->cb.user_data = c;
 
-       ret = -E_MP4FF_OPEN;
-       c->mp4ff = mp4ff_open_read(&c->cb);
-       if (!c->mp4ff)
+       ret = mp4_open(&c->cb, &c->mp4);
+       if (ret < 0)
                goto free_ctx;
-       c->track = aac_afh_get_track(c->mp4ff, &c->masc);
-       ret = -E_MP4FF_TRACK;
-       if (c->track < 0)
-               goto close_mp4ff;
        *afh_context = c;
        return 0;
-close_mp4ff:
-       mp4ff_close(c->mp4ff);
 free_ctx:
        free(c);
        *afh_context = NULL;
@@ -112,51 +82,39 @@ free_ctx:
 static void aac_afh_close(void *afh_context)
 {
        struct aac_afh_context *c = afh_context;
-       mp4ff_close(c->mp4ff);
+       mp4_close(c->mp4);
        free(c);
 }
 
-/**
- * Libmp4ff function to reposition the file to the given sample.
- *
- * \param f The opaque handle returned by mp4ff_open_read().
- * \param track The number of the (audio) track.
- * \param sample Destination.
- *
- * We need this function to obtain the offset of the sample within the audio
- * file. Unfortunately, it is not exposed in the mp4ff header.
- *
- * \return This function always returns 0.
- */
-int32_t mp4ff_set_sample_position(mp4ff_t *f, const int32_t track, const int32_t sample);
-
-static int aac_afh_get_chunk(long unsigned chunk_num, void *afh_context,
-               const char **buf, size_t *len)
+static int aac_afh_get_chunk(uint32_t chunk_num, void *afh_context,
+               const char **buf, uint32_t *len)
 {
        struct aac_afh_context *c = afh_context;
-       int32_t ss;
+       uint32_t ss;
        size_t offset;
+       int ret;
 
-       assert(chunk_num <= INT_MAX);
-       /* this function always returns zero */
-       mp4ff_set_sample_position(c->mp4ff, c->track, chunk_num);
+       ret = mp4_set_sample_position(c->mp4, chunk_num);
+       if (ret < 0)
+               return ret;
        offset = c->fpos;
-       ss = mp4ff_read_sample_getsize(c->mp4ff, c->track, chunk_num);
-       if (ss <= 0)
-               return -E_MP4FF_BAD_SAMPLE;
-       assert(ss + offset <= c->mapsize);
+       ret = mp4_get_sample_size(c->mp4, chunk_num, &ss);
+       if (ret < 0)
+               return ret;
+       if (ss + offset > c->mapsize) /* file got truncated?! */
+               return -E_MP4_CORRUPT;
        *buf = c->map + offset;
        *len = ss;
        return 1;
 }
 
-static void _aac_afh_get_taginfo(const mp4ff_t *mp4ff, struct taginfo *tags)
+static void aac_afh_get_taginfo(const struct mp4 *mp4, struct taginfo *tags)
 {
-       mp4ff_meta_get_artist(mp4ff, &tags->artist);
-       mp4ff_meta_get_title(mp4ff, &tags->title);
-       mp4ff_meta_get_date(mp4ff, &tags->year);
-       mp4ff_meta_get_album(mp4ff, &tags->album);
-       mp4ff_meta_get_comment(mp4ff, &tags->comment);
+       tags->artist = mp4_get_tag_value(mp4, "artist");
+       tags->title = mp4_get_tag_value(mp4, "title");
+       tags->year = mp4_get_tag_value(mp4, "date");
+       tags->album = mp4_get_tag_value(mp4, "album");
+       tags->comment = mp4_get_tag_value(mp4, "comment");
 }
 
 /*
@@ -166,168 +124,125 @@ static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
                struct afh_info *afhi)
 {
        int ret;
-       int32_t rv;
        struct aac_afh_context *c;
-       int64_t tmp;
+       uint64_t milliseconds;
        const char *buf;
-       size_t sz;
-       uint32_t n;
+       uint32_t n, len;
 
        ret = aac_afh_open(map, numbytes, (void **)&c);
        if (ret < 0)
                return ret;
 
-       ret = -E_MP4FF_BAD_SAMPLERATE;
-       rv = mp4ff_get_sample_rate(c->mp4ff, c->track);
-       if (rv <= 0)
-               goto close;
-       afhi->frequency = rv;
-
-       ret = -E_MP4FF_BAD_CHANNEL_COUNT;
-       rv = mp4ff_get_channel_count(c->mp4ff, c->track);
-       if (rv <= 0)
-               goto close;
-       afhi->channels = rv;
-
-       ret = -E_MP4FF_BAD_SAMPLE_COUNT;
-       rv = mp4ff_num_samples(c->mp4ff, c->track);
-       if (rv <= 0)
-               goto close;
-       afhi->chunks_total = rv;
+       afhi->frequency = mp4_get_sample_rate(c->mp4);
+       assert(afhi->frequency > 0);
+       afhi->channels = mp4_get_channel_count(c->mp4);
+       assert(afhi->channels > 0);
+       afhi->chunks_total = mp4_num_samples(c->mp4);
+       assert(afhi->chunks_total > 0);
+
        afhi->max_chunk_size = 0;
        for (n = 0; n < afhi->chunks_total; n++) {
-               if (aac_afh_get_chunk(n, c, &buf, &sz) < 0)
-                       break;
-               afhi->max_chunk_size = PARA_MAX((size_t)afhi->max_chunk_size, sz);
+               ret = aac_afh_get_chunk(n, c, &buf, &len);
+               if (ret < 0)
+                       goto out;
+               afhi->max_chunk_size = PARA_MAX(afhi->max_chunk_size, len);
        }
-
-       tmp = c->masc.sbr_present_flag == 1? 2048 : 1024;
-       afhi->seconds_total = tmp * afhi->chunks_total / afhi->frequency;
-       ms2tv(1000 * tmp / afhi->frequency, &afhi->chunk_tv);
-
-       if (aac_afh_get_chunk(0, c, &buf, &sz) >= 0)
-               numbytes -= buf - map;
+       milliseconds = mp4_get_duration(c->mp4);
+       afhi->seconds_total = milliseconds / 1000;
+       ms2tv(milliseconds / afhi->chunks_total, &afhi->chunk_tv);
+       if (aac_afh_get_chunk(0, c, &buf, &len) < 0)
+               goto out;
+       numbytes -= buf - map;
        afhi->bitrate = 8 * numbytes / afhi->seconds_total / 1000;
-       _aac_afh_get_taginfo(c->mp4ff, &afhi->tags);
+       aac_afh_get_taginfo(c->mp4, &afhi->tags);
        ret = 1;
-close:
+out:
        aac_afh_close(c);
        return ret;
 }
 
-static uint32_t aac_afh_meta_read_cb(void *user_data, void *dest, uint32_t want)
+static ssize_t aac_afh_meta_read_cb(void *user_data, void *dest, size_t want)
 {
        int fd = *(int *)user_data;
        return read(fd, dest, want);
 }
 
-static uint32_t aac_afh_meta_seek_cb(void *user_data, uint64_t pos)
+static off_t aac_afh_meta_seek_cb(void *user_data, off_t offset, int whence)
 {
        int fd = *(int *)user_data;
-       return lseek(fd, pos, SEEK_SET);
+       off_t ret = lseek(fd, offset, whence);
+
+       assert(ret != (off_t)-1);
+       return ret;
 }
 
-static uint32_t aac_afh_meta_write_cb(void *user_data, void *dest, uint32_t want)
+static ssize_t aac_afh_meta_write_cb(void *user_data, void *dest, size_t count)
 {
        int fd = *(int *)user_data;
-       return write(fd, dest, want);
+       return write(fd, dest, count);
 }
 
-static uint32_t aac_afh_meta_truncate_cb(void *user_data)
+static int aac_afh_meta_truncate_cb(void *user_data)
 {
        int fd = *(int *)user_data;
        off_t offset = lseek(fd, 0, SEEK_CUR);
        return ftruncate(fd, offset);
 }
 
-static void replace_tag(mp4ff_tag_t *tag, const char *new_val, bool *found)
-{
-       free(tag->value);
-       tag->value = para_strdup(new_val);
-       *found = true;
-}
-
-static void add_tag(mp4ff_metadata_t *md, const char *item, const char *value)
+static void replace_or_add_tag(const char *item, const char *value,
+               struct mp4_metadata *meta)
 {
-       md->tags[md->count].item = para_strdup(item);
-       md->tags[md->count].value = para_strdup(value);
-       md->count++;
+       uint32_t n;
+       struct mp4_tag *t;
+
+       for (n = 0; n < meta->count; n++) {
+               t = meta->tags + n;
+               if (strcasecmp(t->item, item))
+                       continue;
+               free(t->value);
+               t->value = para_strdup(value);
+               return;
+       }
+       /* item not found, add new tag */
+       meta->tags = para_realloc(meta->tags, (meta->count + 1)
+               * sizeof(struct mp4_tag));
+       t = meta->tags + meta->count;
+       t->item = para_strdup(item);
+       t->value = para_strdup(value);
+       meta->count++;
 }
 
 static int aac_afh_rewrite_tags(const char *map, size_t mapsize,
                struct taginfo *tags, int fd, __a_unused const char *filename)
 {
-       int ret, i;
-       int32_t rv;
-       mp4ff_metadata_t metadata;
-       mp4ff_t *mp4ff;
-       mp4ff_callback_t cb = {
+       int ret;
+       struct mp4_metadata *metadata;
+       struct mp4 *mp4;
+       struct mp4_callback cb = {
                .read = aac_afh_meta_read_cb,
                .seek = aac_afh_meta_seek_cb,
                .write = aac_afh_meta_write_cb,
                .truncate = aac_afh_meta_truncate_cb,
                .user_data = &fd
        };
-       bool found_artist = false, found_title = false, found_album = false,
-               found_year = false, found_comment = false;
 
        ret = write_all(fd, map, mapsize);
        if (ret < 0)
                return ret;
        lseek(fd, 0, SEEK_SET);
 
-       mp4ff = mp4ff_open_read_metaonly(&cb);
-       if (!mp4ff)
-               return -E_MP4FF_OPEN;
-
-       ret = -E_MP4FF_META_READ;
-       rv = mp4ff_meta_get_num_items(mp4ff);
-       if (rv < 0)
-               goto close;
-       metadata.count = rv;
-       PARA_NOTICE_LOG("%d metadata item(s) found\n", rv);
-
-       metadata.tags = para_malloc((metadata.count + 5) * sizeof(mp4ff_tag_t));
-       for (i = 0; i < metadata.count; i++) {
-               mp4ff_tag_t *tag = metadata.tags + i;
-
-               ret = -E_MP4FF_META_READ;
-               if (!mp4ff_meta_get_by_index(mp4ff, i, &tag->item, &tag->value))
-                       goto free_tags;
-               PARA_INFO_LOG("found: %s: %s\n", tag->item, tag->value);
-               if (!strcmp(tag->item, "artist"))
-                       replace_tag(tag, tags->artist, &found_artist);
-               else if (!strcmp(tag->item, "title"))
-                       replace_tag(tag, tags->title, &found_title);
-               else if (!strcmp(tag->item, "album"))
-                       replace_tag(tag, tags->album, &found_album);
-               else if (!strcmp(tag->item, "date"))
-                       replace_tag(tag, tags->year, &found_year);
-               else if (!strcmp(tag->item, "comment"))
-                       replace_tag(tag, tags->comment, &found_comment);
-       }
-       if (!found_artist)
-               add_tag(&metadata, "artist", tags->artist);
-       if (!found_title)
-               add_tag(&metadata, "title", tags->title);
-       if (!found_album)
-               add_tag(&metadata, "album", tags->album);
-       if (!found_year)
-               add_tag(&metadata, "date", tags->year);
-       if (!found_comment)
-               add_tag(&metadata, "comment", tags->comment);
-       ret = -E_MP4FF_META_WRITE;
-       if (!mp4ff_meta_update(&cb, &metadata))
-               goto free_tags;
-       ret = 1;
-free_tags:
-       for (; i > 0; i--) {
-               free(metadata.tags[i - 1].item);
-               free(metadata.tags[i - 1].value);
-       }
-       free(metadata.tags);
-close:
-       mp4ff_close(mp4ff);
+       ret = mp4_open_meta(&cb, &mp4);
+       if (ret < 0)
+               return ret;
+       metadata = mp4_get_meta(mp4);
+       PARA_NOTICE_LOG("%u metadata item(s) found\n", metadata->count);
+       replace_or_add_tag("artist", tags->artist, metadata);
+       replace_or_add_tag("title", tags->title, metadata);
+       replace_or_add_tag("album", tags->album, metadata);
+       replace_or_add_tag("date", tags->year, metadata);
+       replace_or_add_tag("comment", tags->comment, metadata);
+       ret = mp4_update_meta(mp4);
+       mp4_close(mp4);
        return ret;
 }
 
index a2459d82b31991a8e9ac578e0559a398c83f4e10..87a7900af3ee9b43e1a661d66cdd54d1625b0952 100644 (file)
@@ -52,7 +52,7 @@ static int aacdec_execute(struct btr_node *btrn, const char *cmd, char **result)
 static void aacdec_open(struct filter_node *fn)
 {
        NeAACDecConfigurationPtr c;
-       struct private_aacdec_data *padd = para_calloc(sizeof(*padd));
+       struct private_aacdec_data *padd = zalloc(sizeof(*padd));
 
        padd->handle = NeAACDecOpen();
        c = NeAACDecGetCurrentConfiguration(padd->handle);
@@ -74,7 +74,7 @@ static void aacdec_close(struct filter_node *fn)
        fn->private_data = NULL;
 }
 
-static int aacdec_post_select(__a_unused struct sched *s, void *context)
+static int aacdec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
@@ -136,7 +136,7 @@ next_buffer:
        consumed += frame_info.bytesconsumed;
        if (!frame_info.samples)
                goto success;
-       btrbuf = para_malloc(2 * frame_info.samples);
+       btrbuf = arr_alloc(2, frame_info.samples);
        for (i = 0; i < frame_info.samples; i++) {
                short sh = ((short *)outbuf)[i];
                write_int16_host_endian(btrbuf + loaded, sh);
@@ -158,7 +158,7 @@ err:
 const struct filter lsg_filter_cmd_com_aacdec_user_data = {
        .open = aacdec_open,
        .close = aacdec_close,
-       .pre_select = generic_filter_pre_select,
-       .post_select = aacdec_post_select,
+       .pre_monitor = generic_filter_pre_monitor,
+       .post_monitor = aacdec_post_monitor,
        .execute = aacdec_execute
 };
diff --git a/acl.c b/acl.c
index 2c90052658eda947ea586a55f292dc85f24e68f7..ddf93ecc6e083299daf7a573b0adce16c3b4051f 100644 (file)
--- a/acl.c
+++ b/acl.c
@@ -81,7 +81,7 @@ no_match:
  */
 void acl_add_entry(struct list_head *acl, char *addr, int netmask)
 {
-       struct access_info *ai = para_malloc(sizeof(struct access_info));
+       struct access_info *ai = alloc(sizeof(struct access_info));
 
        inet_pton(AF_INET, addr, &ai->addr);
        ai->netmask = netmask;
@@ -101,7 +101,7 @@ static void acl_del_entry(struct list_head *acl, char *addr, unsigned netmask)
        struct access_info *ai, *tmp;
        struct in_addr to_delete;
 
-       PARA_NOTICE_LOG("removing entries matching %s/%u\n", addr, netmask);
+       PARA_INFO_LOG("removing entries matching %s/%u\n", addr, netmask);
        inet_pton(AF_INET, addr, &to_delete);
 
        list_for_each_entry_safe(ai, tmp, acl, node) {
@@ -111,7 +111,7 @@ static void acl_del_entry(struct list_head *acl, char *addr, unsigned netmask)
                        const char *p = inet_ntop(AF_INET, &ai->addr.s_addr,
                                dst, sizeof(dst));
                        if (p)
-                               PARA_INFO_LOG("removing %s/%u\n", p,
+                               PARA_DEBUG_LOG("removing %s/%u\n", p,
                                        ai->netmask);
                        list_del(&ai->node);
                        free(ai);
diff --git a/afh.c b/afh.c
index c896a7d1eff4a843e2e61a4ec8b6dac4982ed197..e419d2708d7224aaa5330515b11d45947c0d5fab 100644 (file)
--- a/afh.c
+++ b/afh.c
@@ -159,7 +159,7 @@ static void print_chunk_table(struct afh_info *afhi, int audio_format_id,
                struct timeval tv;
                long unsigned from, to;
                const char *buf;
-               size_t len;
+               uint32_t len;
                tv_scale(i, &afhi->chunk_tv, &tv);
                from = tv2ms(&tv);
                tv_scale(i + 1, &afhi->chunk_tv, &tv);
@@ -177,7 +177,7 @@ static void print_chunk_table(struct afh_info *afhi, int audio_format_id,
                printf("%td - %td", buf - (const char *)map,
                        buf + len - (const char *)map);
                if (!OPT_GIVEN(PARSER_FRIENDLY))
-                       printf(" (%zu)", len);
+                       printf(" (%u)", len);
                printf("\n");
        }
        afh_close(ctx, audio_format_id);
diff --git a/afh.h b/afh.h
index b3295f6e2fda111bd30ec5d20cd2431bbea325a6..ba72d80e5917b103e879ce221526d1a1a5e3fccc 100644 (file)
--- a/afh.h
+++ b/afh.h
@@ -111,8 +111,8 @@ struct audio_format_handler {
         * portion of the memory mapped audio file. The caller must not call
         * free() on it.
         */
-       int (*get_chunk)(long unsigned chunk_num, void *afh_context,
-               const char **buf, size_t *len);
+       int (*get_chunk)(uint32_t chunk_num, void *afh_context,
+               const char **buf, uint32_t *len);
        /** Deallocate the resources occupied by ->open(). */
        void (*close)(void *afh_context);
        /**
@@ -131,7 +131,7 @@ int compute_afhi(const char *path, char *data, size_t size,
 const char *audio_format_name(int);
 __must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
                uint8_t audio_format_id, const void *map, size_t mapsize,
-               const char **buf, size_t *len, void **afh_context);
+               const char **buf, uint32_t *len, void **afh_context);
 void afh_close(void *afh_context, uint8_t audio_format_id);
 int32_t afh_get_start_chunk(int32_t approx_chunk_num,
                const struct afh_info *afhi, uint8_t audio_format_id);
index a267f58b106b9f0df09d3a8dad99368c3c519401..7e8f63d22a2126494d53e94457278be06142d4f7 100644 (file)
@@ -219,7 +219,7 @@ void clear_afhi(struct afh_info *afhi)
        free(afhi->tags.comment);
 }
 
-static inline size_t get_chunk_len(long unsigned chunk_num,
+static inline uint32_t get_chunk_len(long unsigned chunk_num,
                const struct afh_info *afhi)
 {
        return afhi->chunk_table[chunk_num + 1] - afhi->chunk_table[chunk_num];
@@ -247,7 +247,7 @@ static inline size_t get_chunk_len(long unsigned chunk_num,
  */
 __must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
                uint8_t audio_format_id, const void *map, size_t mapsize,
-               const char **buf, size_t *len, void **afh_context)
+               const char **buf, uint32_t *len, void **afh_context)
 {
        struct audio_format_handler *afh = afl[audio_format_id];
 
index 4f8ff4974f018ef92e5055ac7a28bf337e3aa301..eebac67f9c80a270cd91337190ed9ad1eb569898 100644 (file)
@@ -75,7 +75,7 @@ static int afh_recv_open(struct receiver_node *rn)
 
        if (!fn || *fn == '\0')
                return -E_AFH_RECV_BAD_FILENAME;
-       rn->private_data = pard = para_calloc(sizeof(*pard));
+       rn->private_data = pard = zalloc(sizeof(*pard));
        afhi = &pard->afhi;
        ret = mmap_full_file(fn, O_RDONLY, &pard->map,
                &pard->map_size, &pard->fd);
@@ -142,14 +142,14 @@ static void afh_recv_close(struct receiver_node *rn)
        freep(&rn->private_data);
 }
 
-static void afh_recv_pre_select(struct sched *s, void *context)
+static void afh_recv_pre_monitor(struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
        struct private_afh_recv_data *pard = rn->private_data;
        struct afh_info *afhi = &pard->afhi;
        struct lls_parse_result *lpr = rn->lpr;
        struct timeval chunk_time;
-       int state = generic_recv_pre_select(s, rn);
+       int state = generic_recv_pre_monitor(s, rn);
        unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
 
        if (state <= 0)
@@ -163,7 +163,7 @@ static void afh_recv_pre_select(struct sched *s, void *context)
        sched_request_barrier_or_min_delay(&chunk_time, s);
 }
 
-static int afh_recv_post_select(__a_unused struct sched *s, void *context)
+static int afh_recv_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
        struct lls_parse_result *lpr = rn->lpr;
@@ -174,6 +174,7 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context)
        char *buf;
        const char *start;
        size_t size;
+       uint32_t len;
        struct timeval chunk_time;
        unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
        unsigned H_given = RECV_CMD_OPT_GIVEN(AFH, NO_HEADER, lpr);
@@ -187,7 +188,7 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context)
                        pard->map_size, &header, &size);
                if (size > 0) {
                        PARA_INFO_LOG("writing header (%zu bytes)\n", size);
-                       buf = para_malloc(size);
+                       buf = alloc(size);
                        memcpy(buf, header, size);
                        btr_add_output(buf, size, btrn);
                        afh_free_header(header, pard->audio_format_num);
@@ -197,12 +198,12 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context)
                long unsigned n;
                for (n = pard->first_chunk; n < pard->last_chunk; n++) {
                        ret = afh_get_chunk(n, afhi, pard->audio_format_num,
-                               pard->map, pard->map_size, &start, &size,
+                               pard->map, pard->map_size, &start, &len,
                                &pard->afh_context);
                        if (ret < 0)
                                goto out;
-                       PARA_DEBUG_LOG("adding %zu bytes\n", size);
-                       btr_add_output_dont_free(start, size, btrn);
+                       PARA_DEBUG_LOG("adding %u bytes\n", len);
+                       btr_add_output_dont_free(start, len, btrn);
                }
                ret = -E_RECV_EOF;
                goto out;
@@ -218,12 +219,12 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context)
        }
        ret = afh_get_chunk(pard->current_chunk, afhi,
                pard->audio_format_num, pard->map,
-               pard->map_size, &start, &size,
+               pard->map_size, &start, &len,
                &pard->afh_context);
        if (ret < 0)
                goto out;
        PARA_DEBUG_LOG("adding chunk %u\n", pard->current_chunk);
-       btr_add_output_dont_free(start, size, btrn);
+       btr_add_output_dont_free(start, len, btrn);
        if (pard->current_chunk >= pard->last_chunk) {
                ret = -E_RECV_EOF;
                goto out;
@@ -241,7 +242,7 @@ out:
 const struct receiver lsg_recv_cmd_com_afh_user_data = {
        .open = afh_recv_open,
        .close = afh_recv_close,
-       .pre_select = afh_recv_pre_select,
-       .post_select = afh_recv_post_select,
+       .pre_monitor = afh_recv_pre_monitor,
+       .post_monitor = afh_recv_post_monitor,
        .execute = afh_execute,
 };
diff --git a/afs.c b/afs.c
index 23ba2ad60cc9b133017ed006ce89ce2e91899808..3da39f324b83b2a6d83d1f468732f74cc4313ae2 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -24,6 +24,7 @@
 #include "afs.h"
 #include "net.h"
 #include "server.h"
+#include "daemon.h"
 #include "ipc.h"
 #include "list.h"
 #include "sched.h"
@@ -39,11 +40,10 @@ enum afs_table_num {
        TBLNUM_AUDIO_FILES,
        /** The table for the paraslash attributes. See \ref attribute.c. */
        TBLNUM_ATTRIBUTES,
-       /**
-        * Paraslash's scoring system is based on Gaussian normal
-        * distributions, and the relevant data is stored in the rbtrees of an
-        * osl table containing only volatile columns. See \ref score.c for
-        * details.
+       /*
+        * Moods and playlists organize the current set of admissible files in
+        * an osl table which contains only volatile columns. Each row consists
+        * of a pointer to an audio file and the score value of this file.
         */
        TBLNUM_SCORES,
        /**
@@ -91,26 +91,26 @@ static char *current_mop; /* mode or playlist specifier. NULL means dummy mood *
 extern uint32_t afs_socket_cookie;
 
 /**
- * Struct to let command handlers execute a callback in afs context.
+ * Passed from command handlers to afs.
  *
- * Commands that need to change the state of afs can't change the relevant data
- * structures directly because commands are executed in a child process, i.e.
- * they get their own virtual address space.
+ * Command handlers cannot change the afs database directly because they run in
+ * a separate process. The callback query structure circumvents this
+ * restriction as follows. To instruct the afs process to execute a particular
+ * function, the command hander writes an instance of this structure to a
+ * shared memory area, along with the arguments to the callback function. The
+ * identifier of the shared memory area is transferred to the afs process via
+ * the command socket.
  *
- * This structure is used by \p send_callback_request() (executed from handler
- * context) in order to let the afs process call the specified function. An
- * instance of that structure is written to a shared memory area together with
- * the arguments to the callback function. The identifier of the shared memory
- * area is written to the command socket.
+ * The afs process reads the shared memory id from the command socket, attaches
+ * the corresponding area, and calls the callback function whose address is
+ * stored in the area.
  *
- * The afs process accepts connections on the command socket and reads the
- * shared memory id, attaches the corresponding area, calls the given handler to
- * perform the desired action and to optionally compute a result.
- *
- * The result and a \p callback_result structure is then written to another
- * shared memory area. The identifier for that area is written to the handler's
- * command socket, so that the handler process can read the id, attach the
- * shared memory area and use the result.
+ * The command output, if any, is transferred back to the command handler in
+ * the same way: The afs process writes the output to a second shared memory
+ * area together with a fixed size metadata header whose format corresponds to
+ * the \ref callback_result structure. The identifier of this area is sent back
+ * to the command handler which attaches the area and forwards the output to
+ * the remote client.
  *
  * \sa \ref struct callback_result.
  */
@@ -482,6 +482,12 @@ static int activate_mood_or_playlist(const char *arg, int *num_admissible,
        if (num_admissible)
                *num_admissible = ret;
        current_play_mode = mode;
+       /*
+        * We get called with arg == current_mop from the signal dispatcher
+        * after SIGHUP and from the error path of the select command to
+        * re-select the current mood or playlist. In this case the assignment
+        * to current_mop below would result in a use-after-free condition.
+        */
        if (arg != current_mop) {
                free(current_mop);
                if (arg) {
@@ -648,7 +654,7 @@ static char *database_dir;
 static void close_afs_tables(void)
 {
        int i;
-       PARA_NOTICE_LOG("closing afs_tables\n");
+       PARA_NOTICE_LOG("closing afs tables\n");
        for (i = 0; i < NUM_AFS_TABLES; i++)
                afs_tables[i].close();
        free(database_dir);
@@ -663,7 +669,7 @@ static void get_database_dir(void)
                else {
                        char *home = para_homedir();
                        database_dir = make_message(
-                               "%s/.paraslash/afs_database-0.4", home);
+                               "%s/.paraslash/afs_database-0.7", home);
                        free(home);
                }
        }
@@ -702,7 +708,7 @@ static int open_afs_tables(void)
        return ret;
 }
 
-static int afs_signal_post_select(struct sched *s, __a_unused void *context)
+static int afs_signal_post_monitor(struct sched *s, __a_unused void *context)
 {
        int signum, ret;
 
@@ -710,7 +716,7 @@ static int afs_signal_post_select(struct sched *s, __a_unused void *context)
                PARA_EMERG_LOG("para_server died\n");
                goto shutdown;
        }
-       signum = para_next_signal(&s->rfds);
+       signum = para_next_signal();
        if (signum == 0)
                return 0;
        if (signum == SIGHUP) {
@@ -738,8 +744,8 @@ static void register_signal_task(struct sched *s)
 
        signal_task->task = task_register(&(struct task_info) {
                .name = "signal",
-               .pre_select = signal_pre_select,
-               .post_select = afs_signal_post_select,
+               .pre_monitor = signal_pre_monitor,
+               .post_monitor = afs_signal_post_monitor,
                .context = signal_task,
 
        }, s);
@@ -757,15 +763,15 @@ struct afs_client {
        struct timeval connect_time;
 };
 
-static void command_pre_select(struct sched *s, void *context)
+static void command_pre_monitor(struct sched *s, void *context)
 {
        struct command_task *ct = context;
        struct afs_client *client;
 
-       para_fd_set(server_socket, &s->rfds, &s->max_fileno);
-       para_fd_set(ct->fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(server_socket, s);
+       sched_monitor_readfd(ct->fd, s);
        list_for_each_entry(client, &afs_client_list, node)
-               para_fd_set(client->fd, &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(client->fd, s);
 }
 
 /**
@@ -857,11 +863,11 @@ static int call_callback(int fd, int query_shmid)
        return ret;
 }
 
-static int execute_server_command(fd_set *rfds)
+static int execute_server_command(void)
 {
        char buf[8];
        size_t n;
-       int ret = read_nonblock(server_socket, buf, sizeof(buf) - 1, rfds, &n);
+       int ret = read_nonblock(server_socket, buf, sizeof(buf) - 1, &n);
 
        if (ret < 0 || n == 0)
                return ret;
@@ -872,13 +878,13 @@ static int execute_server_command(fd_set *rfds)
 }
 
 /* returns 0 if no data available, 1 else */
-static int execute_afs_command(int fd, fd_set *rfds)
+static int execute_afs_command(int fd)
 {
        uint32_t cookie;
        int query_shmid;
        char buf[sizeof(cookie) + sizeof(query_shmid)];
        size_t n;
-       int ret = read_nonblock(fd, buf, sizeof(buf), rfds, &n);
+       int ret = read_nonblock(fd, buf, sizeof(buf), &n);
 
        if (ret < 0)
                goto err;
@@ -912,7 +918,7 @@ err:
 /** Shutdown connection if query has not arrived until this many seconds. */
 #define AFS_CLIENT_TIMEOUT 3
 
-static int command_post_select(struct sched *s, void *context)
+static int command_post_monitor(struct sched *s, void *context)
 {
        struct command_task *ct = context;
        struct sockaddr_un unix_addr;
@@ -922,7 +928,7 @@ static int command_post_select(struct sched *s, void *context)
        ret = task_get_notification(ct->task);
        if (ret < 0)
                return ret;
-       ret = execute_server_command(&s->rfds);
+       ret = execute_server_command();
        if (ret < 0) {
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
                task_notify_all(s, -ret);
@@ -930,7 +936,7 @@ static int command_post_select(struct sched *s, void *context)
        }
        /* Check the list of connected clients. */
        list_for_each_entry_safe(client, tmp, &afs_client_list, node) {
-               ret = execute_afs_command(client->fd, &s->rfds);
+               ret = execute_afs_command(client->fd);
                if (ret == 0) { /* prevent bogus connection flooding */
                        struct timeval diff;
                        tv_diff(now, &client->connect_time, &diff);
@@ -943,7 +949,7 @@ static int command_post_select(struct sched *s, void *context)
                free(client);
        }
        /* Accept connections on the local socket. */
-       ret = para_accept(ct->fd, &s->rfds, &unix_addr, sizeof(unix_addr), &fd);
+       ret = para_accept(ct->fd, &unix_addr, sizeof(unix_addr), &fd);
        if (ret < 0)
                PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
        if (ret <= 0)
@@ -954,7 +960,7 @@ static int command_post_select(struct sched *s, void *context)
                close(fd);
                return 0;
        }
-       client = para_malloc(sizeof(*client));
+       client = alloc(sizeof(*client));
        client->fd = fd;
        client->connect_time = *now;
        para_list_add(&client->node, &afs_client_list);
@@ -968,12 +974,20 @@ static void register_command_task(struct sched *s)
 
        ct->task = task_register(&(struct task_info) {
                .name = "afs command",
-               .pre_select = command_pre_select,
-               .post_select = command_post_select,
+               .pre_monitor = command_pre_monitor,
+               .post_monitor = command_post_monitor,
                .context = ct,
        }, s);
 }
 
+static int afs_poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+       mutex_lock(mmd_mutex);
+       daemon_set_loglevel(mmd->loglevel);
+       mutex_unlock(mmd_mutex);
+       return xpoll(fds, nfds, timeout);
+}
+
 /**
  * Initialize the audio file selector process.
  *
@@ -985,7 +999,7 @@ __noreturn void afs_init(int socket_fd)
        int i, ret;
 
        register_signal_task(&s);
-       INIT_LIST_HEAD(&afs_client_list);
+       init_list_head(&afs_client_list);
        for (i = 0; i < NUM_AFS_TABLES; i++)
                afs_tables[i].init(&afs_tables[i]);
        ret = open_afs_tables();
@@ -998,8 +1012,8 @@ __noreturn void afs_init(int socket_fd)
        PARA_INFO_LOG("server_socket: %d\n", server_socket);
        init_admissible_files(OPT_STRING_VAL(AFS_INITIAL_MODE));
        register_command_task(&s);
-       s.default_timeout.tv_sec = 0;
-       s.default_timeout.tv_usec = 999 * 1000;
+       s.default_timeout = 1000;
+       s.poll_function = afs_poll;
        ret = write(socket_fd, "\0", 1);
        if (ret != 1) {
                if (ret == 0)
diff --git a/aft.c b/aft.c
index c8c98e7ab679b5a5254eed9405afa3cca6cf870e..f690e81951b4c7911c2f726b36fee9a673badce7 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -42,7 +42,7 @@ struct ls_data {
  */
 static struct osl_table *audio_file_table; /* NULL if table not open */
 static struct osl_row *current_aft_row; /* NULL if no audio file open */
-static unsigned char current_hash[HASH_SIZE]; /* only used on sighup */
+static unsigned char current_hash[HASH2_SIZE]; /* only used on sighup */
 
 static char *status_items;
 static char *parser_friendly_status_items;
@@ -226,7 +226,7 @@ static struct osl_column_description aft_cols[] = {
                .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE,
                .name = "hash",
                .compare_function = aft_hash_compare,
-               .data_size = HASH_SIZE
+               .data_size = HASH2_SIZE
        },
        [AFTCOL_PATH] = {
                .storage_type = OSL_MAPPED_STORAGE,
@@ -251,7 +251,7 @@ static struct osl_column_description aft_cols[] = {
 };
 
 static struct osl_table_description audio_file_table_desc = {
-       .name = "audio_files",
+       .name = "audio-files",
        .num_columns = NUM_AFT_COLUMNS,
        .flags = OSL_LARGE_TABLE,
        .column_descriptions = aft_cols
@@ -412,7 +412,7 @@ static void load_chunk_table(struct afh_info *afhi, const struct osl_object *ct)
                return;
        }
        sz  = PARA_MIN(((size_t)afhi->chunks_total + 1) * 4, ct->size) + 1;
-       afhi->chunk_table = para_malloc(sz);
+       afhi->chunk_table = alloc(sz);
        for (i = 0; i <= afhi->chunks_total && i * 4 + 3 < ct->size; i++)
                afhi->chunk_table[i] = read_u32(ct->data + 4 * i);
 }
@@ -442,7 +442,7 @@ int aft_get_row_of_path(const char *path, struct osl_row **row)
  */
 static int aft_get_row_of_hash(unsigned char *hash, struct osl_row **row)
 {
-       const struct osl_object obj = {.data = hash, .size = HASH_SIZE};
+       const struct osl_object obj = {.data = hash, .size = HASH2_SIZE};
        return osl(osl_get_row(audio_file_table, AFTCOL_HASH, &obj, row));
 }
 
@@ -779,18 +779,17 @@ static void write_image_items(struct para_buffer *b, struct afs_info *afsi)
 static void write_filename_items(struct para_buffer *b, const char *path,
                bool basename)
 {
-       char *val;
+       const char *slash;
 
        if (basename) {
                WRITE_STATUS_ITEM(b, SI_basename, "%s\n", path);
                return;
        }
        WRITE_STATUS_ITEM(b, SI_path, "%s\n", path);
-       val = para_basename(path);
-       WRITE_STATUS_ITEM(b, SI_basename, "%s\n", val? val : "");
-       val = para_dirname(path);
-       WRITE_STATUS_ITEM(b, SI_directory, "%s\n", val? val : "");
-       free(val);
+       slash = strrchr(path, '/');
+       WRITE_STATUS_ITEM(b, SI_basename, "%s\n", slash? slash + 1 : path);
+       WRITE_STATUS_ITEM(b, SI_directory, "%.*s\n",
+               slash? (int)(slash - path) : (int)strlen(path), path);
 }
 
 static int print_chunk_table(struct ls_data *d, struct para_buffer *b)
@@ -838,7 +837,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
        char duration_buf[30]; /* nobody has an audio file long enough to overflow this */
        struct afs_info *afsi = &d->afsi;
        struct afh_info *afhi = &d->afhi;
-       char asc_hash[2 * HASH_SIZE + 1];
+       char asc_hash[2 * HASH2_SIZE + 1];
 
        if (opts->mode == LS_MODE_SHORT) {
                para_printf(b, "%s\n", d->path);
@@ -897,13 +896,13 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                goto out;
        }
        if (opts->mode == LS_MODE_MBOX) {
-               const char *bn = para_basename(d->path);
+               const char *slash = strrchr(d->path, '/');
                para_printf(b,
                        "From foo@localhost %s\n"
                        "Received: from\nTo: bar\nFrom: a\n"
                        "Subject: %s\n\n",
                        last_played_time,
-                       bn? bn : "?");
+                       slash? slash + 1 : "?");
        }
        write_filename_items(b, d->path, lls_opt_given(r_b));
        if (lls_opt_given(r_a))
@@ -1041,7 +1040,7 @@ out:
  */
 int open_and_update_audio_file(int *fd)
 {
-       unsigned char file_hash[HASH_SIZE];
+       unsigned char file_hash[HASH2_SIZE];
        struct osl_object afsi_obj;
        struct afs_info new_afsi;
        int ret;
@@ -1067,7 +1066,7 @@ again:
        if (ret < 0)
                return ret;
        if (!d->hash)
-               d->hash = para_malloc(HASH_SIZE);
+               d->hash = alloc(HASH_SIZE);
        memcpy(d->hash, tmp_hash, HASH_SIZE);
        free(d->path);
        ret = get_audio_file_path_of_row(current_aft_row, &d->path);
@@ -1101,7 +1100,7 @@ again:
        ret = mmap_full_file(d->path, O_RDONLY, &map.data, &map.size, fd);
        if (ret < 0)
                goto out;
-       hash_function(map.data, map.size, file_hash);
+       hash2_function(map.data, map.size, file_hash);
        ret = hash_compare(file_hash, d->hash);
        para_munmap(map.data, map.size);
        if (ret) {
@@ -1141,7 +1140,7 @@ out:
 static int ls_hash_compare(const void *a, const void *b)
 {
        struct ls_data *d1 = *(struct ls_data **)a, *d2 = *(struct ls_data **)b;
-       return memcmp(d1->hash, d2->hash, HASH_SIZE);
+       return memcmp(d1->hash, d2->hash, HASH2_SIZE);
 }
 
 static int ls_audio_format_compare(const void *a, const void *b)
@@ -1225,7 +1224,7 @@ static int sort_matching_paths(struct ls_options *options)
        int (*compar)(const void *, const void *);
        int i;
 
-       options->data_ptr = para_malloc(nmemb * sizeof(*options->data_ptr));
+       options->data_ptr = arr_alloc(nmemb, sizeof(*options->data_ptr));
        for (i = 0; i < nmemb; i++)
                options->data_ptr[i] = options->data + i;
 
@@ -1318,8 +1317,8 @@ static int prepare_ls_row(struct osl_row *row, void *ls_opts)
        if (options->num_matching_paths > options->array_size) {
                options->array_size++;
                options->array_size *= 2;
-               options->data = para_realloc(options->data, options->array_size
-                       sizeof(*options->data));
+               options->data = arr_realloc(options->data, options->array_size,
+                       sizeof(*options->data));
        }
        d = options->data + tmp;
        ret = get_afsi_of_row(aft_row, &d->afsi);
@@ -1425,7 +1424,7 @@ static int com_ls(struct command_context *cc, struct lls_parse_result *lpr)
        ret = lls_serialize_parse_result(lpr, cmd, NULL, &query.size);
        assert(ret >= 0);
        query.size += sizeof(*opts);
-       query.data = para_malloc(query.size);
+       query.data = alloc(query.size);
        opts = query.data;
        memset(opts, 0, sizeof(*opts));
        slpr = query.data + sizeof(*opts);
@@ -1448,7 +1447,7 @@ static int com_ls(struct command_context *cc, struct lls_parse_result *lpr)
                else if (!strcmp(val, "p") || !strcmp(val, "parser-friendly"))
                        opts->mode = LS_MODE_PARSER;
                else {
-                       ret = -E_AFT_SYNTAX;
+                       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
                        goto out;
                }
        }
@@ -1479,7 +1478,7 @@ static int com_ls(struct command_context *cc, struct lls_parse_result *lpr)
                else if (!strcmp(val, "h") || !strcmp(val, "hash"))
                        opts->sorting = LS_SORT_BY_HASH;
                else {
-                       ret = -E_AFT_SYNTAX;
+                       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
                        goto out;
                }
        }
@@ -1536,7 +1535,7 @@ enum com_add_buffer_offsets {
        /** The hash of the audio file being added. */
        CAB_HASH_OFFSET = 13,
        /** Start of the path of the audio file. */
-       CAB_PATH_OFFSET = (CAB_HASH_OFFSET + HASH_SIZE),
+       CAB_PATH_OFFSET = (CAB_HASH_OFFSET + HASH2_SIZE),
 };
 
 /*
@@ -1554,12 +1553,12 @@ static void save_add_callback_buffer(unsigned char *hash, const char *path,
        size_t afhi_size = sizeof_afhi_buf(afhi);
        size_t size = CAB_PATH_OFFSET + path_len + afhi_size
                + sizeof_chunk_table(afhi) + slpr_size;
-       char *buf = para_malloc(size);
+       char *buf = alloc(size);
        uint32_t pos;
 
        assert(size <= ~(uint32_t)0);
        write_u8(buf + CAB_AUDIO_FORMAT_ID_OFFSET, audio_format_num);
-       memcpy(buf + CAB_HASH_OFFSET, hash, HASH_SIZE);
+       memcpy(buf + CAB_HASH_OFFSET, hash, HASH2_SIZE);
        strcpy(buf + CAB_PATH_OFFSET, path);
        pos = CAB_PATH_OFFSET + path_len;
        write_u32(buf + CAB_AFHI_OFFSET_POS, pos);
@@ -1635,7 +1634,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        struct osl_row *hs;
        struct osl_object objs[NUM_AFT_COLUMNS];
        unsigned char *hash;
-       char asc[2 * HASH_SIZE + 1];
+       char asc[2 * HASH2_SIZE + 1];
        int ret;
        char afsi_buf[AFSI_SIZE];
        char *slpr = buf + read_u32(buf + CAB_LPR_OFFSET);
@@ -1652,7 +1651,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        hash = (unsigned char *)buf + CAB_HASH_OFFSET;
        hash_to_asc(hash, asc);
        objs[AFTCOL_HASH].data = buf + CAB_HASH_OFFSET;
-       objs[AFTCOL_HASH].size = HASH_SIZE;
+       objs[AFTCOL_HASH].size = HASH2_SIZE;
 
        path = buf + CAB_PATH_OFFSET;
        objs[AFTCOL_PATH].data = path;
@@ -1715,7 +1714,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        objs[AFTCOL_CHUNKS].data = buf + chunks_offset;
        objs[AFTCOL_CHUNKS].size = aca->query.size - chunks_offset;
        if (pb && !hs) { /* update pb's hash */
-               char old_asc[2 * HASH_SIZE + 1];
+               char old_asc[2 * HASH2_SIZE + 1];
                unsigned char *old_hash;
                ret = get_hash_of_row(pb, &old_hash);
                if (ret < 0)
@@ -1820,7 +1819,7 @@ static int add_one_audio_file(const char *path, void *private_data)
        struct afh_info afhi, *afhi_ptr = NULL;
        struct osl_row *pb = NULL, *hs = NULL; /* path brother/hash sister */
        struct osl_object map, obj = {.data = NULL}, query;
-       unsigned char hash[HASH_SIZE];
+       unsigned char hash[HASH2_SIZE];
        bool a_given = SERVER_CMD_OPT_GIVEN(ADD, ALL, pad->lpr);
        bool f_given = SERVER_CMD_OPT_GIVEN(ADD, FORCE, pad->lpr);
        bool l_given = SERVER_CMD_OPT_GIVEN(ADD, LAZY, pad->lpr);
@@ -1848,11 +1847,11 @@ static int add_one_audio_file(const char *path, void *private_data)
        ret = mmap_full_file(path, O_RDONLY, &map.data, &map.size, &fd);
        if (ret < 0)
                goto out_free;
-       hash_function(map.data, map.size, hash);
+       hash2_function(map.data, map.size, hash);
 
        /* Check whether the database contains a file with the same hash. */
        query.data = hash;
-       query.size = HASH_SIZE;
+       query.size = HASH2_SIZE;
        ret = send_callback_request(hash_sister_callback, &query,
                get_row_pointer_from_result, &hs);
        if (ret < 0)
@@ -2321,7 +2320,7 @@ static int com_setatt_callback(struct afs_callback_arg *aca)
                                goto out; /* no attribute modifier given */
                        goto set_atts;
                }
-               p = para_malloc(len);
+               p = alloc(len);
                memcpy(p, arg, len - 1);
                p[len - 1] = '\0';
                ret = get_attribute_bitnum_by_name(p, &bitnum);
@@ -2517,7 +2516,7 @@ static void aft_close(void)
                        PARA_WARNING_LOG("hash lookup failure\n");
                        current_aft_row = NULL;
                } else
-                       memcpy(current_hash, p, HASH_SIZE);
+                       memcpy(current_hash, p, HASH2_SIZE);
        }
        osl_close_table(audio_file_table, OSL_MARK_CLEAN);
        audio_file_table = NULL;
index 1d81e5d9dbabbcef772d182eabe6a03ba5a51915..af4adc46382da236e7fec5786fa3d40234f16546 100644 (file)
@@ -52,7 +52,7 @@ static int alsa_mix_open(const char *dev, struct mixer_handle **handle)
 
        PARA_INFO_LOG("snd_mixer_{open,attach,register,load}\n");
        *handle = NULL;
-       h = para_calloc(sizeof(*h));
+       h = zalloc(sizeof(*h));
        h->card = para_strdup(dev? dev : "hw:0");
        ret = snd_mixer_open(&h->mixer, 0);
        if (ret < 0) {
index bc06fc315bc51fb3fc1fa9729ae429b47a70d4b4..92d6cb703b7a4911d0b5aa61282f703ce0bb490c 100644 (file)
@@ -48,7 +48,7 @@ struct private_alsa_write_data {
        /* time until buffer underrun occurs, in milliseconds */
        unsigned buffer_time;
        struct timeval drain_barrier;
-       /* File descriptor for select(). */
+       /* File descriptor to monitor for reading. */
        int poll_fd;
 };
 
@@ -202,7 +202,7 @@ out:
        return ret;
 }
 
-static void alsa_write_pre_select(struct sched *s, void *context)
+static void alsa_write_pre_monitor(struct sched *s, void *context)
 {
        struct pollfd pfd;
        struct writer_node *wn = context;
@@ -230,7 +230,7 @@ static void alsa_write_pre_select(struct sched *s, void *context)
                return;
        }
        pad->poll_fd = pfd.fd;
-       para_fd_set(pfd.fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(pfd.fd, s);
 }
 
 static void alsa_close(struct writer_node *wn)
@@ -240,6 +240,8 @@ static void alsa_close(struct writer_node *wn)
 
        if (!pad)
                return;
+       if (!pad->handle)
+               goto free_pad;
        /*
         * It's OK to have a blocking operation here because we already made
         * sure that the PCM output buffer is (nearly) empty.
@@ -248,10 +250,11 @@ static void alsa_close(struct writer_node *wn)
        snd_pcm_drain(pad->handle);
        snd_pcm_close(pad->handle);
        snd_config_update_free_global();
+free_pad:
        free(pad);
 }
 
-static int alsa_write_post_select(__a_unused struct sched *s, void *context)
+static int alsa_write_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct writer_node *wn = context;
        struct private_alsa_write_data *pad = wn->private_data;
@@ -293,22 +296,24 @@ again:
 
                if (bytes == 0) /* no data available */
                        return 0;
-               pad = wn->private_data = para_calloc(sizeof(*pad));
-               get_btr_sample_rate(btrn, &val);
+               pad = wn->private_data = zalloc(sizeof(*pad));
+               ret = get_btr_sample_rate(btrn, &val);
+               if (ret < 0)
+                       goto err;
                pad->sample_rate = val;
-               get_btr_channels(btrn, &val);
+               ret = get_btr_channels(btrn, &val);
+               if (ret < 0)
+                       goto err;
                pad->channels = val;
-               get_btr_sample_format(btrn, &val);
+               ret = get_btr_sample_format(btrn, &val);
+               if (ret < 0)
+                       goto err;
                pad->sample_format = get_alsa_pcm_format(val);
-
                PARA_INFO_LOG("%u channel(s), %uHz\n", pad->channels,
                        pad->sample_rate);
                ret = alsa_init(wn);
-               if (ret < 0) {
-                       free(wn->private_data);
-                       wn->private_data = NULL;
+               if (ret < 0)
                        goto err;
-               }
                wn->min_iqs = pad->bytes_per_frame;
                goto again;
        }
@@ -316,7 +321,7 @@ again:
        frames = snd_pcm_writei(pad->handle, data, frames);
        if (frames == 0 || frames == -EAGAIN) {
                char buf[100];
-               if (pad->poll_fd >= 0 && FD_ISSET(pad->poll_fd, &s->rfds))
+               if (pad->poll_fd >= 0 && sched_read_ok(pad->poll_fd, s))
                        if (read(pad->poll_fd, buf, 100))
                                do_nothing;
                return 0;
@@ -326,7 +331,11 @@ again:
                goto again;
        }
        if (frames == -EPIPE) {
-               PARA_WARNING_LOG("underrun (tried to write %zu bytes)\n", bytes);
+               snd_pcm_status_t *status;
+               snd_pcm_status_malloc(&status);
+               if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN)
+                       PARA_WARNING_LOG("underrun\n");
+               snd_pcm_status_free(status);
                snd_pcm_prepare(pad->handle);
                return 0;
        }
@@ -340,7 +349,7 @@ err:
 
 struct writer lsg_write_cmd_com_alsa_user_data = {
 
-       .pre_select = alsa_write_pre_select,
-       .post_select = alsa_write_post_select,
+       .pre_monitor = alsa_write_pre_monitor,
+       .post_monitor = alsa_write_post_monitor,
        .close = alsa_close,
 };
index 61b1653ea069ce479b6b609da5703fc57bc717c1..360e3fc2d7429bb7aa8219248ff4d38505a86ac2 100644 (file)
@@ -29,7 +29,7 @@ static void amp_close(struct filter_node *fn)
 
 static void amp_open(struct filter_node *fn)
 {
-       struct private_amp_data *pad = para_calloc(sizeof(*pad));
+       struct private_amp_data *pad = zalloc(sizeof(*pad));
        unsigned given = FILTER_CMD_OPT_GIVEN(AMP, AMP, fn->lpr);
        uint32_t amp_arg = FILTER_CMD_OPT_UINT32_VAL(AMP, AMP, fn->lpr);
 
@@ -43,7 +43,7 @@ static void amp_open(struct filter_node *fn)
                pad->amp, pad->amp / 64.0 + 1.0);
 }
 
-static int amp_post_select(__a_unused struct sched *s, void *context)
+static int amp_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct private_amp_data *pad = fn->private_data;
@@ -74,7 +74,7 @@ next_buffer:
        if (inplace)
                out = in;
        else
-               out = para_malloc(len * 2);
+               out = alloc(len * 2);
 
        for (i = 0; i < len; i++) {
                int x = (in[i] * factor) >> 6;
@@ -100,6 +100,6 @@ err:
 const struct filter lsg_filter_cmd_com_amp_user_data = {
        .open = amp_open,
        .close = amp_close,
-       .pre_select = generic_filter_pre_select,
-       .post_select = amp_post_select,
+       .pre_monitor = generic_filter_pre_monitor,
+       .post_monitor = amp_post_monitor,
 };
index 447dea84c00a468330fe8229bfd70b74292264bd..950f8f73808e0b6528c0b73a2a43da58ec17f4ee 100644 (file)
@@ -42,7 +42,7 @@ static void aow_close(struct writer_node *wn)
        ao_shutdown();
 }
 
-static void aow_pre_select(struct sched *s, void *context)
+static void aow_pre_monitor(struct sched *s, void *context)
 {
        struct writer_node *wn = context;
        struct private_aow_data *pawd = wn->private_data;
@@ -184,7 +184,7 @@ static int aow_init(struct writer_node *wn, unsigned sample_rate,
        ao_info *info;
        const struct lls_opt_result *r;
        unsigned n;
-       struct private_aow_data *pawd = para_malloc(sizeof(*pawd));
+       struct private_aow_data *pawd = alloc(sizeof(*pawd));
 
        ao_initialize();
        aow_show_drivers();
@@ -342,7 +342,7 @@ fail:
        return -E_AO_PTHREAD;
 }
 
-static int aow_post_select(__a_unused struct sched *s, void *context)
+static int aow_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct writer_node *wn = context;
        struct private_aow_data *pawd = wn->private_data;
@@ -357,9 +357,15 @@ static int aow_post_select(__a_unused struct sched *s, void *context)
                        goto remove_btrn;
                if (ret == 0)
                        return 0;
-               get_btr_sample_rate(wn->btrn, &rate);
-               get_btr_channels(wn->btrn, &ch);
-               get_btr_sample_format(wn->btrn, &format);
+               ret = get_btr_sample_rate(wn->btrn, &rate);
+               if (ret < 0)
+                       goto remove_btrn;
+               ret = get_btr_channels(wn->btrn, &ch);
+               if (ret < 0)
+                       goto remove_btrn;
+               ret = get_btr_sample_format(wn->btrn, &format);
+               if (ret < 0)
+                       goto remove_btrn;
                ret = aow_init(wn, rate, ch, format);
                if (ret < 0)
                        goto remove_btrn;
@@ -415,7 +421,7 @@ out:
 
 struct writer lsg_write_cmd_com_ao_user_data = {
        .close = aow_close,
-       .pre_select = aow_pre_select,
-       .post_select = aow_post_select,
+       .pre_monitor = aow_pre_monitor,
+       .post_monitor = aow_post_monitor,
 };
 
index 414a65e6526fabc08ccabaec07694423665b4442..fb1b3eac3482f1c0725e0e79a4b80166be511350 100644 (file)
@@ -104,7 +104,7 @@ int get_attribute_bitnum_by_name(const char *att_name, unsigned char *bitnum)
        return 1;
 }
 
-/** Data passed to the action function of lsatt */
+/* Data passed to the action function of lsatt */
 static int print_attribute(struct osl_table *table, struct osl_row *row,
                const char *name, void *data)
 {
@@ -470,26 +470,12 @@ int attribute_check_callback(struct afs_callback_arg *aca)
        return aft_check_attributes(att_mask, &aca->pbout);
 }
 
-/**
- * Close the attribute table.
- *
- * \sa \ref osl_close_table().
- */
 static void attribute_close(void)
 {
        osl_close_table(attribute_table, OSL_MARK_CLEAN);
        attribute_table = NULL;
 }
 
-/**
- * Open the attribute table.
- *
- * \param dir The database directory.
- *
- * \return Positive on success, negative on errors.
- *
- * \sa \ref osl_open_table().
- */
 static int attribute_open(const char *dir)
 {
        int ret;
index af67063367044b675877173a1ed351bdd28f2dd1..d6c14cbcf7245a3a7db4bb3f86cf7e35efb8384b 100644 (file)
--- a/audioc.c
+++ b/audioc.c
@@ -107,6 +107,12 @@ static void help_completer(struct i9e_completion_info *ci,
        cr->matches = i9e_complete_commands(ci->word, audiod_completers);
 }
 
+static void ll_completer(struct i9e_completion_info *ci,
+               struct i9e_completion_result *cr)
+{
+       i9e_ll_completer(ci, cr);
+}
+
 static void version_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
@@ -143,17 +149,17 @@ static struct i9e_completer audiod_completers[] = {
        {.name = NULL}
 };
 
-static void audioc_pre_select(struct sched *s, void *context)
+static void audioc_pre_monitor(struct sched *s, void *context)
 {
        struct audioc_task *at = context;
        int ret = btr_node_status(at->btrn, 0, BTR_NT_ROOT);
 
        if (ret < 0)
                sched_min_delay(s);
-       para_fd_set(at->fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(at->fd, s);
 }
 
-static int audioc_post_select(struct sched *s, void *context)
+static int audioc_post_monitor(struct sched *s, void *context)
 {
        char *buf = NULL;
        struct audioc_task *at = context;
@@ -162,10 +168,10 @@ static int audioc_post_select(struct sched *s, void *context)
 
        if (ret < 0)
                goto out;
-       if (!FD_ISSET(at->fd, &s->rfds))
+       if (!sched_read_ok(at->fd, s))
                return 0;
        bufsize = PARA_MAX(1024U, OPT_UINT32_VAL(BUFSIZE));
-       buf = para_malloc(bufsize);
+       buf = alloc(bufsize);
        ret = recv_bin_buffer(at->fd, buf, bufsize);
        PARA_DEBUG_LOG("recv: %d\n", ret);
        if (ret == 0)
@@ -211,8 +217,8 @@ static int audioc_i9e_line_handler(char *line)
                EMBRACE(.name = "audioc line handler"));
        at->task = task_register(&(struct task_info) {
                .name = "audioc",
-               .pre_select = audioc_pre_select,
-               .post_select = audioc_post_select,
+               .pre_monitor = audioc_pre_monitor,
+               .post_monitor = audioc_post_monitor,
                .context = at,
        }, &sched);
        i9e_attach_to_stdout(at->btrn);
@@ -250,9 +256,9 @@ __noreturn static void interactive_session(void)
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGINT, &act, NULL);
-       sched.select_function = i9e_select;
+       sched.poll_function = i9e_poll;
 
-       sched.default_timeout.tv_sec = 1;
+       sched.default_timeout = 1000;
        ret = i9e_open(&ici, &sched);
        if (ret < 0)
                goto out;
@@ -364,7 +370,7 @@ int main(int argc, char *argv[])
        if (ret < 0)
                goto out;
        bufsize = PARA_MAX(1024U, OPT_UINT32_VAL(BUFSIZE));
-       buf = para_malloc(bufsize);
+       buf = alloc(bufsize);
        do {
                size_t n = ret = recv_bin_buffer(fd, buf, bufsize);
                if (ret <= 0)
index 88599c3fa297337b6a3f598b31f92d6e6b4b0993..0e8e5981085b7d05b8a2a08f35a9bb7944a4d72d 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -44,8 +44,6 @@ static struct lls_parse_result *lpr;
 #define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
 #define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
 #define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
-#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \
-       lls_opt(LSG_AUDIOD_PARA_AUDIOD_OPT_ ## _name, CMD_PTR)))
 
 __printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
 /** define the array containing all supported audio formats */
@@ -123,7 +121,7 @@ enum vss_status_flags {
  * This is needed also in audiod_command.c (for the tasks command), so it can
  * not be made static.
  */
-struct sched sched = {.max_fileno = 0};
+struct sched sched = {.timeout = 0};
 
 /* The task for obtaining para_server's status (para_client stat). */
 struct status_task {
@@ -389,11 +387,11 @@ static void parse_config_or_die(void)
                        para_strerror(-ret));
                exit(EXIT_FAILURE);
        }
-       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
        n = OPT_GIVEN(USER_ALLOW);
        if (n == 0)
                return;
-       uid_whitelist = para_malloc(n * sizeof(uid_t));
+       uid_whitelist = arr_alloc(n, sizeof(uid_t));
        for (i = 0; i < n; i++) {
                const char *arg = lls_string_val(i, OPT_RESULT(USER_ALLOW));
                int32_t val;
@@ -445,7 +443,8 @@ static void close_receiver(int slot_num)
        task_reap(&s->receiver_node->task);
        free(s->receiver_node);
        s->receiver_node = NULL;
-       stat_task->current_audio_format_num = -1;
+       if (audiod_status == AUDIOD_ON)
+               stat_task->current_audio_format_num = -1;
        tv_add(now, &(struct timeval)EMBRACE(0, 200 * 1000),
                &a->restart_barrier);
 }
@@ -479,6 +478,23 @@ static void close_writers(struct slot_info *s)
        s->wns = NULL;
 }
 
+static void notify_writers(int error)
+{
+       int i;
+
+       FOR_EACH_SLOT(i) {
+               struct slot_info *s = slot + i;
+               struct audio_format_info *a;
+               int j;
+
+               if (s->format < 0)
+                       continue;
+               a = afi + s->format;
+               for (j = 0; j < a->num_writers; j++)
+                       task_notify(s->wns[j].task, error);
+       }
+}
+
 static void close_filters(struct slot_info *s)
 {
        int i;
@@ -546,7 +562,7 @@ static void open_filters(struct slot_info *s)
                return;
        PARA_INFO_LOG("opening %s filters\n", audio_formats[s->format]);
        assert(s->fns == NULL);
-       s->fns = para_calloc(nf * sizeof(struct filter_node));
+       s->fns = zalloc(nf * sizeof(struct filter_node));
        parent = s->receiver_node->btrn;
        for (i = 0; i < nf; i++) {
                char buf[20];
@@ -566,8 +582,8 @@ static void open_filters(struct slot_info *s)
                sprintf(buf, "%s (slot %d)", name, (int)(s - slot));
                fn->task = task_register(&(struct task_info) {
                        .name = buf,
-                       .pre_select = f->pre_select,
-                       .post_select = f->post_select,
+                       .pre_monitor = f->pre_monitor,
+                       .post_monitor = f->post_monitor,
                        .context = fn,
                }, &sched);
                parent = fn->btrn;
@@ -584,7 +600,7 @@ static void open_writers(struct slot_info *s)
        struct btr_node *parent = s->fns[a->num_filters - 1].btrn;
 
        assert(s->wns == NULL);
-       s->wns = para_calloc(PARA_MAX(1U, a->num_writers)
+       s->wns = zalloc(PARA_MAX(1U, a->num_writers)
                * sizeof(struct writer_node));
        for (i = 0; i < a->num_writers; i++) {
                wn = s->wns + i;
@@ -611,7 +627,7 @@ static int open_receiver(int format)
        if (ret < 0)
                return ret;
        slot_num = ret;
-       rn = para_calloc(sizeof(*rn));
+       rn = zalloc(sizeof(*rn));
        rn->receiver = r;
        rn->lpr = a->receiver_lpr;
        rn->btrn = btr_new_node(&(struct btr_node_description)
@@ -630,8 +646,8 @@ static int open_receiver(int format)
                audio_formats[format], name, slot_num);
        rn->task = task_register(&(struct task_info) {
                .name = name,
-               .pre_select = r->pre_select,
-               .post_select = r->post_select,
+               .pre_monitor = r->pre_monitor,
+               .post_monitor = r->post_monitor,
                .context = rn,
        }, &sched);
        return slot_num;
@@ -731,8 +747,8 @@ static void compute_time_diff(const struct timeval *status_time)
        if (count > 5) {
                int s = tv_diff(&diff, &stat_task->sa_time_diff, &tmp);
                if (tv_diff(&max_deviation, &tmp, NULL) < 0)
-                       PARA_WARNING_LOG("time diff jump: %lums\n",
-                               s * tv2ms(&tmp));
+                       PARA_WARNING_LOG("time diff jump: %c%lums\n",
+                               s < 0? '-' : '+', tv2ms(&tmp));
        }
        count++;
        sa_time_diff_sign = tv_convex_combination(
@@ -801,7 +817,7 @@ static int parse_stream_command(const char *txt, const char **cmd)
                return -E_MISSING_COLON;
        *cmd = p + 1;
        len = p - txt;
-       re = para_malloc(len + 1);
+       re = alloc(len + 1);
        strncpy(re, txt, len);
        re[len] = '\0';
        ret = get_matching_audio_format_nums(re);
@@ -817,12 +833,9 @@ static int add_filter(int format, const char *cmdline)
        struct lls_parse_result *flpr;
 
        filter_num = filter_setup(cmdline, &cfg, &flpr);
-       a->filter_lpr = para_realloc(a->filter_lpr,
-               (nf + 1) * sizeof(flpr));
-       a->filter_conf = para_realloc(a->filter_conf,
-               (nf + 1) * sizeof(void *));
-       a->filter_nums = para_realloc(a->filter_nums,
-               (nf + 1) * sizeof(unsigned));
+       a->filter_lpr = arr_realloc(a->filter_lpr, nf + 1, sizeof(flpr));
+       a->filter_conf = arr_realloc(a->filter_conf, nf + 1, sizeof(void *));
+       a->filter_nums = arr_realloc(a->filter_nums, nf + 1, sizeof(unsigned));
 
        a->filter_nums[nf] = filter_num;
        a->filter_conf[nf] = cfg;
@@ -868,8 +881,8 @@ static int parse_writer_args(void)
                if (a->num_writers > 0)
                        continue; /* already set up */
                a->num_writers = 1;
-               a->wids = para_malloc(sizeof(int));
-               a->writer_lpr = para_malloc(sizeof(struct lls_parse_result *));
+               a->wids = alloc(sizeof(int));
+               a->writer_lpr = alloc(sizeof(struct lls_parse_result *));
                a->wids[0] = check_writer_arg_or_die(NULL, a->writer_lpr);
                PARA_INFO_LOG("%s writer: %s (default)\n", audio_formats[i],
                        writer_name(a->wids[0]));
@@ -1037,7 +1050,7 @@ static void init_local_socket(struct command_task *ct)
        exit(EXIT_FAILURE);
 }
 
-static int signal_post_select(struct sched *s, void *context)
+static int signal_post_monitor(struct sched *s, void *context)
 {
        struct signal_task *st = context;
        int ret, signum;
@@ -1045,25 +1058,25 @@ static int signal_post_select(struct sched *s, void *context)
        ret = task_get_notification(st->task);
        if (ret < 0)
                return ret;
-       signum = para_next_signal(&s->rfds);
+       signum = para_next_signal();
        switch (signum) {
        case SIGINT:
        case SIGTERM:
        case SIGHUP:
-               PARA_NOTICE_LOG("received signal %d\n", signum);
+               PARA_WARNING_LOG("terminating on signal %d\n", signum);
                task_notify_all(s, E_AUDIOD_SIGNAL);
                return -E_AUDIOD_SIGNAL;
        }
        return 0;
 }
 
-static void command_pre_select(struct sched *s, void *context)
+static void command_pre_monitor(struct sched *s, void *context)
 {
        struct command_task *ct = context;
-       para_fd_set(ct->fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(ct->fd, s);
 }
 
-static int command_post_select(struct sched *s, void *context)
+static int command_post_monitor(struct sched *s, void *context)
 {
        int ret;
        struct command_task *ct = context;
@@ -1074,9 +1087,9 @@ static int command_post_select(struct sched *s, void *context)
        ret = task_get_notification(ct->task);
        if (ret < 0)
                return ret;
-       ret = handle_connect(ct->fd, &s->rfds);
+       ret = dispatch_local_connection(ct->fd);
        if (ret < 0) {
-               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+               PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
                if (ret == -E_AUDIOD_TERM) {
                        task_notify_all(s, -ret);
                        return ret;
@@ -1114,8 +1127,8 @@ static void init_command_task(struct command_task *ct)
 
        ct->task = task_register(&(struct task_info) {
                .name = "command",
-               .pre_select = command_pre_select,
-               .post_select = command_post_select,
+               .pre_monitor = command_pre_monitor,
+               .post_monitor = command_post_monitor,
                .context = ct,
        }, &sched);
 }
@@ -1217,8 +1230,9 @@ static void start_stop_decoders(void)
        struct slot_info *sl;
 
        close_unused_slots();
-       if (audiod_status != AUDIOD_ON ||
-                       !(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING))
+       if (audiod_status != AUDIOD_ON)
+               return notify_writers(E_NOT_PLAYING);
+       if (!(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING))
                return notify_receivers(E_NOT_PLAYING);
        if (!must_start_decoder())
                return;
@@ -1235,7 +1249,7 @@ static void start_stop_decoders(void)
        audiod_status_dump(true);
 }
 
-static void status_pre_select(struct sched *s, void *context)
+static void status_pre_monitor(struct sched *s, void *context)
 {
        struct status_task *st = context;
        int i, ret, cafn = stat_task->current_audio_format_num;
@@ -1267,7 +1281,7 @@ min_delay:
 }
 
 /* restart the client task if necessary */
-static int status_post_select(struct sched *s, void *context)
+static int status_post_monitor(struct sched *s, void *context)
 {
        struct status_task *st = context;
        int ret;
@@ -1358,8 +1372,8 @@ static void init_status_task(struct status_task *st)
 
        stat_task->task = task_register(&(struct task_info) {
                .name = "stat",
-               .pre_select = status_pre_select,
-               .post_select = status_post_select,
+               .pre_monitor = status_pre_monitor,
+               .post_monitor = status_post_monitor,
                .context = stat_task,
        }, &sched);
 }
@@ -1443,7 +1457,7 @@ int main(int argc, char *argv[])
        ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
        if (ret < 0)
                goto out;
-       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
        daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
                OPT_STRING_VAL(GROUP));
        version_handle_flag("audiod", OPT_GIVEN(VERSION));
@@ -1486,13 +1500,12 @@ int main(int argc, char *argv[])
 
        signal_task->task = task_register(&(struct task_info) {
                .name = "signal",
-               .pre_select = signal_pre_select,
-               .post_select = signal_post_select,
+               .pre_monitor = signal_pre_monitor,
+               .post_monitor = signal_post_monitor,
                .context = signal_task,
        }, &sched);
 
-       sched.default_timeout.tv_sec = 2;
-       sched.default_timeout.tv_usec = 999 * 1000;
+       sched.default_timeout = 2999;
        ret = schedule(&sched);
        audiod_cleanup();
        sched_shutdown(&sched);
index b40fdd6743faa4650888c2bdf69756d6c12e40d8..dedb038fbf772848330b024b93efabce58c1c9d3 100644 (file)
--- a/audiod.h
+++ b/audiod.h
@@ -21,5 +21,5 @@ bool uid_is_whitelisted(uid_t uid);
 /* defined in audiod_command.c */
 void audiod_status_dump(bool force);
 void close_stat_clients(void);
-int handle_connect(int accept_fd, fd_set *rfds);
+int dispatch_local_connection(int accept_fd);
 void stat_client_write_item(int item_num);
index bb54dfab87f7965a18f6cccbe6bc4077c147fa29..6e2f8ee9e4aaf446267270e3be2398a4ee6d7c96 100644 (file)
@@ -114,7 +114,7 @@ static int stat_client_add(int fd, uint64_t mask, int parser_friendly)
        ret = dup(fd);
        if (ret < 0)
                return -ERRNO_TO_PARA_ERROR(errno);
-       new_client = para_calloc(sizeof(*new_client));
+       new_client = zalloc(sizeof(*new_client));
        new_client->fd = ret;
        PARA_INFO_LOG("adding client on fd %d\n", new_client->fd);
        new_client->item_mask = mask;
@@ -240,6 +240,42 @@ static int com_help(int fd, struct lls_parse_result *lpr)
 }
 EXPORT_AUDIOD_CMD_HANDLER(help)
 
+static int com_ll(int fd, struct lls_parse_result *lpr)
+{
+       unsigned ll;
+       char *errctx;
+       const char *sev[] = {SEVERITIES};
+       const char *arg;
+       int ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+
+       if (ret < 0) {
+               char *tmp = make_message("%s\n", errctx);
+               free(errctx);
+               client_write(fd, tmp);
+               free(tmp);
+               return ret;
+       }
+       if (lls_num_inputs(lpr) == 0) {
+               char *msg;
+               ll = daemon_get_loglevel();
+               msg = make_message("%s\n", sev[ll]);
+               ret = client_write(fd, msg);
+               free(msg);
+               return ret;
+       }
+       arg = lls_input(0, lpr);
+       for (ll = 0; ll < NUM_LOGLEVELS; ll++) {
+               if (!strcmp(arg, sev[ll]))
+                       break;
+       }
+       if (ll >= NUM_LOGLEVELS)
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
+       PARA_INFO_LOG("new log level: %s\n", sev[ll]);
+       daemon_set_loglevel(ll);
+       return 1;
+}
+EXPORT_AUDIOD_CMD_HANDLER(ll)
+
 static int com_tasks(int fd, __a_unused struct lls_parse_result *lpr)
 {
        int ret;
@@ -360,10 +396,9 @@ EXPORT_AUDIOD_CMD_HANDLER(version)
  * Handle arriving connections on the local socket.
  *
  * \param accept_fd The fd to accept connections on.
- * \param rfds If \a accept_fd is not set in \a rfds, do nothing.
  *
- * This is called in each iteration of the select loop. If there is an incoming
- * connection on \a accept_fd, this function reads the command sent by the peer,
+ * This is called in each iteration of the main loop of the scheduler. If there
+ * is an incoming connection, the function reads the command sent by the peer,
  * checks the connecting user's permissions by using unix socket credentials
  * (if supported by the OS) and calls the corresponding command handler if
  * permissions are OK.
@@ -372,8 +407,8 @@ EXPORT_AUDIOD_CMD_HANDLER(version)
  * connection to accept.
  *
  * \sa \ref para_accept(), \ref recv_cred_buffer().
- * */
-int handle_connect(int accept_fd, fd_set *rfds)
+ */
+int dispatch_local_connection(int accept_fd)
 {
        int argc, ret, clifd;
        char buf[MAXLINE], **argv = NULL;
@@ -384,7 +419,7 @@ int handle_connect(int accept_fd, fd_set *rfds)
        char *errctx = NULL;
        const struct audiod_command_info *aci;
 
-       ret = para_accept(accept_fd, rfds, &unix_addr, sizeof(struct sockaddr_un), &clifd);
+       ret = para_accept(accept_fd, &unix_addr, sizeof(struct sockaddr_un), &clifd);
        if (ret <= 0)
                return ret;
        ret = recv_cred_buffer(clifd, buf, sizeof(buf) - 1);
index caf1401d8bf02e2d299bdcdf58ae902cfb260092..cbfea7fdfb2ea5e2b8ad84c8faba815b67b8a966 100755 (executable)
@@ -1,28 +1,8 @@
 #!/bin/sh
-# check if we have multiple processors/cores
-n=$(nproc 2>/dev/null)
-if [ -z "$n" ]; then
-       n=$(grep ^processor /proc/cpuinfo 2>/dev/null | wc -l)
-       [ $n -eq 0 ] && n=1
-fi
-# If we are compiling with distcc, try to guess a reasonable number
-# based on (a) the number of cores on this machine and (b) the number
-# of words in the DISTCC_HOSTS variable.
-d="$(echo $DISTCC_HOSTS | wc -w)"
-n=$(($n + 2 * $n * $d))
-echo preparing, parallel=$n...
-if test -f Makefile; then
-       make maintainer-clean > /dev/null 2>&1
-fi
 autom4te \
        --language=autoconf \
        --output=configure \
        --no-cache \
        --warnings=all \
-       configure.ac
+       configure.ac &&
 autoheader
-echo configuring...
-./configure $@ > /dev/null
-echo compiling...
-make clean > /dev/null 2>&1
-make -j $n > /dev/null && make check
index 122465f8feea79cf06d09bfd759d2446ee0f3f12..ac6f65aa85d469df3ba4a70a058ed85b31eb6d11 100644 (file)
--- a/base64.c
+++ b/base64.c
@@ -79,7 +79,7 @@ int base64_decode(char const *src, size_t encoded_size, char **result,
 
        if (encoded_size == (size_t)-1)
                encoded_size = strlen(src);
-       target = para_malloc(BASE64_MAX_DECODED_SIZE(encoded_size) + 1);
+       target = alloc(BASE64_MAX_DECODED_SIZE(encoded_size) + 1);
 
        for (
                i = 0, j = 0, state = 0;
index dfc1e55e67aced611014fc90b86128e8bd61a457..a1c7c943138d41993807109be09969a58e914a0b 100644 (file)
@@ -46,8 +46,8 @@ static void alloc_table(struct vlc *vlc, int size)
        vlc->table_size += size;
        if (vlc->table_size > vlc->table_allocated) {
                vlc->table_allocated += (1 << vlc->bits);
-               vlc->table = para_realloc(vlc->table,
-                       sizeof(int16_t) * 2 * vlc->table_allocated);
+               vlc->table = arr_realloc(vlc->table, vlc->table_allocated,
+                       sizeof(int16_t) * 2);
        }
 }
 
diff --git a/blob.c b/blob.c
index 4ecbc45bb15f42c15342e7900f4de94522ef45bb..b63724014138513dfb89d4300cb38056bfaa601a 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -403,7 +403,7 @@ static int stdin_command(struct command_context *cc,
        if (ret < 0)
                return ret;
        query.size = len + 1 + stdin_obj.size;
-       query.data = para_malloc(query.size);
+       query.data = alloc(query.size);
        memcpy(query.data, lls_input(0, lpr), len + 1);
        if (stdin_obj.size > 0)
                memcpy((char *)query.data + len + 1, stdin_obj.data,
index 8a3175133e69aa57eff7da5155341cbf05532d03..05cdfa83630004e49561f0c7bef0138bd0a35c27 100644 (file)
@@ -72,8 +72,8 @@ struct btr_pool *btr_pool_new(const char *name, size_t area_size)
        struct btr_pool *btrp;
 
        PARA_INFO_LOG("%s, %zu bytes\n", name, area_size);
-       btrp = para_malloc(sizeof(*btrp));
-       btrp->area_start = para_malloc(area_size);
+       btrp = alloc(sizeof(*btrp));
+       btrp->area_start = alloc(area_size);
        btrp->area_end = btrp->area_start + area_size;
        btrp->rhead = btrp->area_start;
        btrp->whead = btrp->area_start;
@@ -262,7 +262,7 @@ static void btr_pool_deallocate(struct btr_pool *btrp, size_t size)
  */
 struct btr_node *btr_new_node(struct btr_node_description *bnd)
 {
-       struct btr_node *btrn = para_malloc(sizeof(*btrn));
+       struct btr_node *btrn = alloc(sizeof(*btrn));
 
        btrn->name = para_strdup(bnd->name);
        btrn->parent = bnd->parent;
@@ -270,8 +270,8 @@ struct btr_node *btr_new_node(struct btr_node_description *bnd)
        btrn->context = bnd->context;
        btrn->start.tv_sec = 0;
        btrn->start.tv_usec = 0;
-       INIT_LIST_HEAD(&btrn->children);
-       INIT_LIST_HEAD(&btrn->input_queue);
+       init_list_head(&btrn->children);
+       init_list_head(&btrn->input_queue);
        if (!bnd->child) {
                if (bnd->parent) {
                        list_add_tail(&btrn->node, &bnd->parent->children);
@@ -307,7 +307,7 @@ out:
  */
 static struct btr_buffer *new_btrb(char *buf, size_t size)
 {
-       struct btr_buffer *btrb = para_calloc(sizeof(*btrb));
+       struct btr_buffer *btrb = zalloc(sizeof(*btrb));
 
        btrb->buf = buf;
        btrb->size = size;
@@ -354,7 +354,7 @@ static void add_btrb_to_children(struct btr_buffer *btrb,
        if (btrn->start.tv_sec == 0)
                btrn->start = *now;
        FOR_EACH_CHILD(ch, btrn) {
-               struct btr_buffer_reference *br = para_calloc(sizeof(*br));
+               struct btr_buffer_reference *br = zalloc(sizeof(*br));
                br->btrb = btrb;
                br->consumed = consumed;
                list_add_tail(&br->node, &ch->input_queue);
@@ -570,7 +570,7 @@ bool btr_no_parent(struct btr_node *btrn)
  * buffer.
  *
  * Since the buffer tree may change at any time, this function should be called
- * during each post_select call.
+ * during each post_monitor call.
  *
  * \return True if \a btrn has no siblings.
  */
@@ -1007,12 +1007,12 @@ next:
        if (!wbr) {
                /* Make a new wrap buffer combining buf1 and buf2. */
                sz = sz1 + sz2;
-               buf = para_malloc(sz);
+               buf = alloc(sz);
                PARA_DEBUG_LOG("merging input buffers: (%p:%zu, %p:%zu) -> %p:%zu\n",
                        buf1, sz1, buf2, sz2, buf, sz);
                memcpy(buf, buf1, sz1);
                memcpy(buf + sz1, buf2, sz2);
-               br = para_calloc(sizeof(*br));
+               br = zalloc(sizeof(*br));
                br->btrb = new_btrb(buf, sz);
                br->btrb->refcount = 1;
                br->consumed = 0;
@@ -1067,13 +1067,13 @@ static int merge_input(struct btr_node *btrn)
        assert(i == 2);
        /* make a new btrb that combines the two buffers and a br to it. */
        sz = szs[0] + szs[1];
-       buf = para_malloc(sz);
+       buf = alloc(sz);
        PARA_DEBUG_LOG("%s: memory merging input buffers: (%zu, %zu) -> %zu\n",
                btrn->name, szs[0], szs[1], sz);
        memcpy(buf, bufs[0], szs[0]);
        memcpy(buf + szs[0], bufs[1], szs[1]);
 
-       br = para_calloc(sizeof(*br));
+       br = zalloc(sizeof(*br));
        br->btrb = new_btrb(buf, sz);
        br->btrb->refcount = 1;
 
@@ -1181,7 +1181,7 @@ struct btr_node *btr_search_node(const char *name, struct btr_node *root)
  * \param type The supposed type of \a btrn.
  *
  * Most users of the buffer tree subsystem call this function from both
- * their pre_select and the post_select methods.
+ * their ->pre_monitor() and ->post_monitor() methods.
  *
  * \return Negative if an error condition was detected, zero if there
  * is nothing to do and positive otherwise.
index 89ebdacc0805e9575acea92d09ed3b2dd81ebac3..3789f30aa5a20ef5433df83362a6be02563c8d17 100644 (file)
@@ -39,15 +39,15 @@ struct check_wav_context {
 };
 
 /**
- * Set select timeout according to the given context.
+ * Request a minimal timeout if not idle.
  *
- * \param s Contains the timeval that should be set.
- * \param cwc Contains a pointer to the buffer tree node.
+ * \param s The scheduler instance.
+ * \param cwc The buffer tree node is derived from this.
  *
- * This requests a minimal timeout from the scheduler if btrn of \a cwc is not
- * idle.
+ * If no data is available and the buffer tree node is not in error state, the
+ * function does nothing.
  */
-void check_wav_pre_select(struct sched *s, struct check_wav_context *cwc)
+void check_wav_pre_monitor(struct sched *s, struct check_wav_context *cwc)
 {
        int ret = btr_node_status(cwc->btrn, cwc->min_iqs, BTR_NT_INTERNAL);
        if (ret != 0)
@@ -121,7 +121,7 @@ out:
  *
  * \return Standard.
  */
-int check_wav_post_select(struct check_wav_context *cwc)
+int check_wav_post_monitor(struct check_wav_context *cwc)
 {
        struct btr_node *btrn = cwc->btrn;
        unsigned char *a;
@@ -198,8 +198,8 @@ out:
  * children of this node can figure out channel count, sample rate, etc.
  *
  * \return The (opaque) handle of the newly created check_wav instance. It is
- * supposed to be passed to \ref check_wav_pre_select() and \ref
- * check_wav_post_select().
+ * supposed to be passed to \ref check_wav_pre_monitor() and \ref
+ * check_wav_post_monitor().
  *
  * \sa \ref btr_new_node.
  */
@@ -207,7 +207,7 @@ struct check_wav_context *check_wav_init(struct btr_node *parent,
                struct btr_node *child, struct wav_params *params,
                struct btr_node **cw_btrn)
 {
-       struct check_wav_context *cwc = para_calloc(sizeof(*cwc));
+       struct check_wav_context *cwc = zalloc(sizeof(*cwc));
 
        cwc->state = CWS_NEED_HEADER;
        cwc->min_iqs = WAV_HEADER_LEN;
@@ -225,7 +225,7 @@ struct check_wav_context *check_wav_init(struct btr_node *parent,
  *
  * \param cwc Determines the instance to shut down.
  *
- * This function may only be called after check_wav_post_select() has returned
+ * This function may only be called after check_wav_post_monitor() has returned
  * negative.
  */
 void check_wav_shutdown(struct check_wav_context *cwc)
index 79b11962baea9b3fe743f08ad68bca6a70e552bc..e6188c5243db16d82c524fc47f4066c8f9aec9df 100644 (file)
@@ -42,6 +42,6 @@ struct wav_params {
 struct check_wav_context *check_wav_init(struct btr_node *parent,
                struct btr_node *child, struct wav_params *params,
                struct btr_node **cw_btrn);
-void check_wav_pre_select(struct sched *s, struct check_wav_context *cwc);
-int check_wav_post_select(struct check_wav_context *cwc);
+void check_wav_pre_monitor(struct sched *s, struct check_wav_context *cwc);
+int check_wav_post_monitor(struct check_wav_context *cwc);
 void check_wav_shutdown(struct check_wav_context *cwc);
index 08f57e9d1ecf9bae1cadead0757fe29418c446c3..1f11359fcb16e4e55073a5fd36d5d5cff7f5504c 100644 (file)
@@ -52,7 +52,7 @@ int cq_enqueue(struct chunk_queue *cq, const char *buf, size_t num_bytes)
 
        if (cq->num_pending + num_bytes > cq->max_pending)
                return -E_QUEUE;
-       qc = para_malloc(sizeof(struct queued_chunk));
+       qc = alloc(sizeof(struct queued_chunk));
        cq->num_pending += num_bytes;
        qc->buf = buf;
        qc->num_bytes = num_bytes;
@@ -130,8 +130,8 @@ int cq_get(struct queued_chunk *qc, const char **buf, size_t *num_bytes)
  */
 struct chunk_queue *cq_new(size_t max_pending)
 {
-       struct chunk_queue *cq = para_malloc(sizeof(*cq));
-       INIT_LIST_HEAD(&cq->q);
+       struct chunk_queue *cq = alloc(sizeof(*cq));
+       init_list_head(&cq->q);
        cq->max_pending = max_pending;
        cq->num_pending = 0;
        return cq;
index 3edaab5dbbf2ec6f086a9483500e431069770b28..a6aee0f8b9afc79439032db3695ba9c82bb594a1 100644 (file)
--- a/client.c
+++ b/client.c
@@ -42,7 +42,7 @@ struct exec_task {
        size_t result_size;
 };
 
-static void exec_pre_select(struct sched *s, void *context)
+static void exec_pre_monitor(struct sched *s, void *context)
 {
        struct exec_task *et = context;
        int ret = btr_node_status(et->btrn, 0, BTR_NT_LEAF);
@@ -51,7 +51,7 @@ static void exec_pre_select(struct sched *s, void *context)
                sched_min_delay(s);
 }
 
-static int exec_post_select(__a_unused struct sched *s, void *context)
+static int exec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct exec_task *et = context;
        struct btr_node *btrn = et->btrn;
@@ -123,7 +123,7 @@ fail:
 static int execute_client_command(const char *cmd, char **result)
 {
        int ret;
-       struct sched command_sched = {.default_timeout = {.tv_sec = 1}};
+       struct sched command_sched = {.default_timeout = 1000};
        struct exec_task exec_task = {
                .result_buf = para_strdup(""),
                .result_size = 1,
@@ -138,8 +138,8 @@ static int execute_client_command(const char *cmd, char **result)
                EMBRACE(.name = "exec_collect"));
        exec_task.task = task_register(&(struct task_info) {
                .name = "client exec",
-               .pre_select = exec_pre_select,
-               .post_select = exec_post_select,
+               .pre_monitor = exec_pre_monitor,
+               .post_monitor = exec_post_monitor,
                .context = &exec_task,
        }, &command_sched);
        ret = client_connect(ct, &command_sched, NULL, exec_task.btrn);
@@ -243,10 +243,15 @@ I9E_DUMMY_COMPLETER(term);
 I9E_DUMMY_COMPLETER(stop);
 I9E_DUMMY_COMPLETER(addatt);
 I9E_DUMMY_COMPLETER(init);
-I9E_DUMMY_COMPLETER(tasks);
 
 static struct i9e_completer completers[];
 
+static void ll_completer(struct i9e_completion_info *ci,
+               struct i9e_completion_result *cr)
+{
+       i9e_ll_completer(ci, cr);
+}
+
 static void help_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
@@ -342,7 +347,7 @@ static void setatt_completer(struct i9e_completion_info *ci,
        if (ret < 0)
                goto out;
        num_atts = ret;
-       sl = para_realloc(sl, (2 * num_atts + 1) * sizeof(char *));
+       sl = arr_realloc(sl, 2 * num_atts + 1, sizeof(char *));
        for (i = 0; i < num_atts; i++) {
                char *orig = sl[i];
                sl[i] = make_message("%s+", orig);
@@ -436,7 +441,7 @@ static void select_completer(struct i9e_completion_info *ci,
                goto free_moods;
        num_pl = ret;
        n = num_moods + num_pl;
-       mops = para_malloc((n + 1) * sizeof(char *));
+       mops = arr_alloc(n + 1, sizeof(char *));
        for (i = 0; i < num_moods; i++)
                mops[i] = make_message("m/%s", moods[i]);
        for (i = 0; i < num_pl; i++)
@@ -533,7 +538,7 @@ __noreturn static void interactive_session(void)
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGINT, &act, NULL);
-       sched.select_function = i9e_select;
+       sched.poll_function = i9e_poll;
 
        ret = i9e_open(&ici, &sched);
        if (ret < 0)
@@ -579,7 +584,7 @@ struct supervisor_task {
        struct task *task;
 };
 
-static int supervisor_post_select(struct sched *s, void *context)
+static int supervisor_post_monitor(struct sched *s, void *context)
 {
        struct supervisor_task *svt = context;
        int ret = task_status(ct->task);
@@ -625,7 +630,7 @@ int main(int argc, char *argv[])
        int ret;
 
        crypt_init();
-       sched.default_timeout.tv_sec = 1;
+       sched.default_timeout = 1000;
 
        ret = client_parse_config(argc, argv, &ct, &client_loglevel);
        if (ret < 0)
@@ -649,7 +654,7 @@ int main(int argc, char *argv[])
                EMBRACE(.name = "stdout", .parent = ct->btrn[0]));
        supervisor_task.task = task_register(&(struct task_info) {
                .name = "supervisor",
-               .post_select = supervisor_post_select,
+               .post_monitor = supervisor_post_monitor,
                .context = &supervisor_task,
        }, &sched);
 
index c25da96b169126ab36f5b6e7f95a2161d19a3aa9..95e59fd29de2015fcc65443a4ffc6442387ec0f8 100644 (file)
@@ -57,7 +57,7 @@ void client_close(struct client_task *ct)
  * The context pointer is assumed to refer to a client task structure that was
  * initialized earlier by client_open().
  */
-static void client_pre_select(struct sched *s, void *context)
+static void client_pre_monitor(struct sched *s, void *context)
 {
        int ret;
        struct client_task *ct = context;
@@ -68,13 +68,13 @@ static void client_pre_select(struct sched *s, void *context)
        case CL_CONNECTED:
        case CL_SENT_AUTH:
        case CL_SENT_CH_RESPONSE:
-               para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(ct->scc.fd, s);
                return;
 
        case CL_RECEIVED_WELCOME:
        case CL_RECEIVED_PROCEED:
        case CL_RECEIVED_CHALLENGE:
-               para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
+               sched_monitor_writefd(ct->scc.fd, s);
                return;
 
        case CL_SENDING:
@@ -83,7 +83,7 @@ static void client_pre_select(struct sched *s, void *context)
                        if (ret < 0)
                                sched_min_delay(s);
                        else if (ret > 0)
-                               para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
+                               sched_monitor_writefd(ct->scc.fd, s);
                }
                __attribute__ ((fallthrough));
        case CL_EXECUTING:
@@ -92,7 +92,7 @@ static void client_pre_select(struct sched *s, void *context)
                        if (ret < 0)
                                sched_min_delay(s);
                        else if (ret > 0)
-                               para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno);
+                               sched_monitor_readfd(ct->scc.fd, s);
                }
                return;
        }
@@ -125,8 +125,7 @@ static int send_sb(struct client_task *ct, int channel, void *buf, size_t numbyt
        return 0;
 }
 
-static int recv_sb(struct client_task *ct, fd_set *rfds,
-               struct sb_buffer *result)
+static int recv_sb(struct client_task *ct, struct sb_buffer *result)
 {
        int ret;
        size_t n;
@@ -134,8 +133,6 @@ static int recv_sb(struct client_task *ct, fd_set *rfds,
        void *trafo_context;
        struct iovec iov;
 
-       if (!FD_ISSET(ct->scc.fd, rfds))
-               return 0;
        if (ct->status < CL_SENT_CH_RESPONSE)
                trafo = trafo_context = NULL;
        else {
@@ -146,7 +143,7 @@ static int recv_sb(struct client_task *ct, fd_set *rfds,
                ct->sbc[0] = sb_new_recv(0, trafo, trafo_context);
 again:
        sb_get_recv_buffer(ct->sbc[0], &iov);
-       ret = read_nonblock(ct->scc.fd, iov.iov_base, iov.iov_len, rfds, &n);
+       ret = read_nonblock(ct->scc.fd, iov.iov_base, iov.iov_len, &n);
        if (ret < 0) {
                sb_free(ct->sbc[0]);
                ct->sbc[0] = NULL;
@@ -248,7 +245,7 @@ static int send_sb_command(struct client_task *ct)
 
        for (i = 0; i < num_inputs; i++)
                len += strlen(lls_input(i, ct->lpr)) + 1;
-       p = command = para_malloc(len);
+       p = command = alloc(len);
        for (i = 0; i < num_inputs; i++) {
                const char *str = lls_input(i, ct->lpr);
                strcpy(p, str);
@@ -258,6 +255,17 @@ static int send_sb_command(struct client_task *ct)
        return send_sb(ct, 0, command, len, SBD_COMMAND, false);
 }
 
+/* Find out if the given string is contained in the features vector. */
+static bool has_feature(const char *feature, struct client_task *ct)
+{
+       if (!ct->features)
+               return false;
+       for (int i = 0; ct->features[i]; i++)
+               if (strcmp(feature, ct->features[i]) == 0)
+                       return i;
+       return false;
+}
+
 /*
  * This function reads or writes to the socket file descriptor which
  * corresponds to an established connection between the client and the server.
@@ -269,7 +277,7 @@ static int send_sb_command(struct client_task *ct)
  * The context pointer refers to a client task structure that was initialized
  * earlier by client_open().
  */
-static int client_post_select(struct sched *s, void *context)
+static int client_post_monitor(struct sched *s, void *context)
 {
        struct client_task *ct = context;
        int ret = 0;
@@ -283,23 +291,32 @@ static int client_post_select(struct sched *s, void *context)
                return 0;
        switch (ct->status) {
        case CL_CONNECTED: /* receive welcome message */
-               ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &s->rfds, &n);
+               ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &n);
                if (ret < 0 || n == 0)
                        goto out;
                ct->features = parse_features(buf);
                ct->status = CL_RECEIVED_WELCOME;
                return 0;
        case CL_RECEIVED_WELCOME: /* send auth command */
-               if (!FD_ISSET(ct->scc.fd, &s->wfds))
+               {
+               /*
+                * Use sha256 iff the server announced the feature. After 0.7.0
+                * we may request and use the feature unconditionally. After
+                * 0.8.0 we no longer need to request the feature.
+                */
+               bool has_sha256;
+               if (!sched_write_ok(ct->scc.fd, s))
                        return 0;
-               sprintf(buf, AUTH_REQUEST_MSG "%s sideband,aes_ctr128",
-                       ct->user);
+               has_sha256 = has_feature("sha256", ct);
+               sprintf(buf, AUTH_REQUEST_MSG "%s%s", ct->user, has_sha256?
+                       " sha256" : "");
                PARA_INFO_LOG("--> %s\n", buf);
                ret = write_buffer(ct->scc.fd, buf);
                if (ret < 0)
                        goto out;
                ct->status = CL_SENT_AUTH;
                return 0;
+               }
        case CL_SENT_AUTH:
                /*
                 * Receive challenge and session keys, decrypt the challenge and
@@ -310,7 +327,7 @@ static int client_post_select(struct sched *s, void *context)
                unsigned char crypt_buf[1024];
                struct sb_buffer sbb;
 
-               ret = recv_sb(ct, &s->rfds, &sbb);
+               ret = recv_sb(ct, &sbb);
                if (ret <= 0)
                        goto out;
                if (sbb.band != SBD_CHALLENGE) {
@@ -325,19 +342,28 @@ static int client_post_select(struct sched *s, void *context)
                free(sbb.iov.iov_base);
                if (ret < 0)
                        goto out;
-               ct->challenge_hash = para_malloc(HASH_SIZE);
-               hash_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash);
+               ct->challenge_hash = alloc(HASH2_SIZE);
+               if (has_feature("sha256", ct)) {
+                       hash2_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash);
+                       hash2_to_asc(ct->challenge_hash, buf);
+               } else {
+                       hash_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash);
+                       hash_to_asc(ct->challenge_hash, buf);
+               }
                ct->scc.send = sc_new(crypt_buf + APC_CHALLENGE_SIZE, SESSION_KEY_LEN);
                ct->scc.recv = sc_new(crypt_buf + APC_CHALLENGE_SIZE + SESSION_KEY_LEN,
                        SESSION_KEY_LEN);
-               hash_to_asc(ct->challenge_hash, buf);
                PARA_INFO_LOG("--> %s\n", buf);
                ct->status = CL_RECEIVED_CHALLENGE;
                return 0;
                }
        case CL_RECEIVED_CHALLENGE:
-               ret = send_sb(ct, 0, ct->challenge_hash, HASH_SIZE,
-                       SBD_CHALLENGE_RESPONSE, false);
+               if (has_feature("sha256", ct))
+                       ret = send_sb(ct, 0, ct->challenge_hash, HASH2_SIZE,
+                               SBD_CHALLENGE_RESPONSE, false);
+               else
+                       ret = send_sb(ct, 0, ct->challenge_hash, HASH_SIZE,
+                               SBD_CHALLENGE_RESPONSE, false);
                if (ret != 0)
                        ct->challenge_hash = NULL;
                if (ret <= 0)
@@ -347,7 +373,7 @@ static int client_post_select(struct sched *s, void *context)
        case CL_SENT_CH_RESPONSE: /* read server response */
                {
                struct sb_buffer sbb;
-               ret = recv_sb(ct, &s->rfds, &sbb);
+               ret = recv_sb(ct, &sbb);
                if (ret <= 0)
                        goto out;
                free(sbb.iov.iov_base);
@@ -359,7 +385,7 @@ static int client_post_select(struct sched *s, void *context)
                }
        case CL_RECEIVED_PROCEED: /* concat args and send command */
                {
-               if (!FD_ISSET(ct->scc.fd, &s->wfds))
+               if (!sched_write_ok(ct->scc.fd, s))
                        return 0;
                ret = send_sb_command(ct);
                if (ret <= 0)
@@ -381,7 +407,7 @@ static int client_post_select(struct sched *s, void *context)
                        }
                        if (ret < 0)
                                goto close1;
-                       if (ret > 0 && FD_ISSET(ct->scc.fd, &s->wfds)) {
+                       if (ret > 0 && sched_write_ok(ct->scc.fd, s)) {
                                sz = btr_next_buffer(ct->btrn[1], &buf2);
                                assert(sz);
                                ret = send_sb(ct, 1, buf2, sz, SBD_BLOB_DATA, true);
@@ -397,9 +423,9 @@ static int client_post_select(struct sched *s, void *context)
                        ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT);
                        if (ret < 0)
                                goto close0;
-                       if (ret > 0 && FD_ISSET(ct->scc.fd, &s->rfds)) {
+                       if (ret > 0 && sched_read_ok(ct->scc.fd, s)) {
                                struct sb_buffer sbb;
-                               ret = recv_sb(ct, &s->rfds, &sbb);
+                               ret = recv_sb(ct, &sbb);
                                if (ret < 0)
                                        goto close0;
                                if (ret > 0) {
@@ -428,8 +454,7 @@ out:
                return 0;
        btr_remove_node(&ct->btrn[0]);
        btr_remove_node(&ct->btrn[1]);
-       if (ret != -E_SERVER_CMD_SUCCESS && ret != -E_SERVER_CMD_FAILURE)
-               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       PARA_NOTICE_LOG("closing connection (%s)\n", para_strerror(-ret));
        if (ct->scc.fd >= 0) {
                close(ct->scc.fd);
                ct->scc.fd = -1;
@@ -465,7 +490,7 @@ int client_connect(struct client_task *ct, struct sched *s,
 
        PARA_NOTICE_LOG("connecting %s:%u\n", host, port);
        ct->scc.fd = -1;
-       ret = para_connect_simple(IPPROTO_TCP, host, port);
+       ret = para_connect(IPPROTO_TCP, host, port);
        if (ret < 0)
                return ret;
        ct->scc.fd = ret;
@@ -480,8 +505,8 @@ int client_connect(struct client_task *ct, struct sched *s,
 
        ct->task = task_register(&(struct task_info) {
                .name = "client",
-               .pre_select = client_pre_select,
-               .post_select = client_post_select,
+               .pre_monitor = client_pre_monitor,
+               .post_monitor = client_post_monitor,
                .context = ct,
        }, s);
        return 1;
@@ -562,7 +587,7 @@ int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
        PARA_INFO_LOG("user: %s\n", user);
        PARA_INFO_LOG("key file: %s\n", kf);
        PARA_INFO_LOG("loglevel: %d\n", ll);
-       ct = para_calloc(sizeof(*ct));
+       ct = zalloc(sizeof(*ct));
        ct->scc.fd = -1;
        ct->lpr = lpr;
        ct->key_file = kf;
index 28c5eabb1ae9a4821b60b638be559e837dc54f36..809f027e278d44f4413482ba433b8893b8be0843 100644 (file)
@@ -31,10 +31,10 @@ struct close_on_fork {
  */
 void add_close_on_fork_list(int fd)
 {
-       struct close_on_fork *cof = para_malloc(sizeof(struct close_on_fork));
+       struct close_on_fork *cof = alloc(sizeof(struct close_on_fork));
 
        if (!initialized) {
-               INIT_LIST_HEAD(&close_on_fork_list);
+               init_list_head(&close_on_fork_list);
                initialized = 1;
        }
        cof->fd = fd;
index 7b3d6faf90849a92ecbafb98538f277cfef5b02a..00d2c5a61ada8fdcedfad2989e43844265ca46d5 100644 (file)
--- a/command.c
+++ b/command.c
@@ -6,14 +6,10 @@
 #include <sys/socket.h>
 #include <regex.h>
 #include <signal.h>
-#include <sys/types.h>
-#include <osl.h>
 #include <arpa/inet.h>
-#include <sys/un.h>
 #include <netdb.h>
 #include <lopsub.h>
 
-#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "lsu.h"
 #include "command.h"
 #include "string.h"
 #include "afh.h"
-#include "afs.h"
 #include "net.h"
 #include "server.h"
 #include "list.h"
-#include "send.h"
 #include "sched.h"
+#include "send.h"
 #include "vss.h"
 #include "daemon.h"
 #include "fd.h"
@@ -83,7 +78,7 @@ static char *vss_status_tohuman(unsigned int flags)
  */
 static char *vss_get_status_flags(unsigned int flags)
 {
-       char *msg = para_malloc(5 * sizeof(char));
+       char *msg = alloc(5 * sizeof(char));
 
        msg[0] = (flags & VSS_PLAYING)? 'P' : '_';
        msg[1] = (flags & VSS_NOMORE)? 'O' : '_';
@@ -395,7 +390,6 @@ static int com_si(struct command_context *cc,
                "server_pid: %d\n"
                "afs_pid: %d\n"
                "connections (active/accepted/total): %u/%u/%u\n"
-               "current loglevel: %s\n"
                "supported audio formats: %s\n",
                ut, mmd->num_played,
                (int)getppid(),
@@ -403,7 +397,6 @@ static int com_si(struct command_context *cc,
                mmd->active_connections,
                mmd->num_commands,
                mmd->num_connects,
-               ENUM_STRING_VAL(LOGLEVEL),
                AUDIO_FORMAT_HANDLERS
        );
        mutex_unlock(mmd_mutex);
@@ -594,9 +587,56 @@ static int com_hup(__a_unused struct command_context *cc,
 }
 EXPORT_SERVER_CMD_HANDLER(hup);
 
+static int com_ll(struct command_context *cc, struct lls_parse_result *lpr)
+{
+       unsigned ll, perms;
+       char *errctx;
+       const char *sev[] = {SEVERITIES}, *arg;
+       int ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       if (lls_num_inputs(lpr) == 0) { /* reporting is an unprivileged op. */
+               const char *severity;
+               mutex_lock(mmd_mutex);
+               severity = sev[mmd->loglevel];
+               mutex_unlock(mmd_mutex);
+               return send_sb_va(&cc->scc, SBD_OUTPUT, "%s\n", severity);
+       }
+       /*
+        * Changing the loglevel changes the state of both the afs and the vss,
+        * so we require both AFS_WRITE and VSS_WRITE.
+        */
+       perms = AFS_WRITE | VSS_WRITE;
+       if ((cc->u->perms & perms) != perms)
+               return -ERRNO_TO_PARA_ERROR(EPERM);
+       arg = lls_input(0, lpr);
+       for (ll = 0; ll < NUM_LOGLEVELS; ll++)
+               if (!strcmp(arg, sev[ll]))
+                       break;
+       if (ll >= NUM_LOGLEVELS)
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
+       PARA_INFO_LOG("new log level: %s\n", sev[ll]);
+       /* Ask the server and afs processes to adjust their log level. */
+       mutex_lock(mmd_mutex);
+       mmd->loglevel = ll;
+       mutex_unlock(mmd_mutex);
+       return 1;
+}
+EXPORT_SERVER_CMD_HANDLER(ll);
+
 static int com_term(__a_unused struct command_context *cc,
                __a_unused struct lls_parse_result *lpr)
 {
+       /*
+        * The server catches SIGTERM and propagates this signal to all its
+        * children. We are about to exit anyway, but we'd leak tons of memory
+        * if being terminated by the signal. So we ignore the signal here and
+        * terminate via the normal exit path, deallocating all memory.
+        */
+       para_sigaction(SIGTERM, SIG_IGN);
        kill(getppid(), SIGTERM);
        return 1;
 }
@@ -745,14 +785,6 @@ out:
 }
 EXPORT_SERVER_CMD_HANDLER(jmp);
 
-/* deprecated, does nothing */
-static int com_tasks(__a_unused struct command_context *cc,
-               __a_unused struct lls_parse_result *lpr)
-{
-       return 1;
-}
-EXPORT_SERVER_CMD_HANDLER(tasks);
-
 static void reset_signals(void)
 {
        para_sigaction(SIGCHLD, SIG_IGN);
@@ -762,7 +794,7 @@ static void reset_signals(void)
 }
 
 struct connection_features {
-       int dummy; /* none at the moment */
+       bool sha256_requested; /* can be removed after 0.7.0 */
 };
 
 static int parse_auth_request(char *buf, int len, const struct user **u,
@@ -787,11 +819,22 @@ static int parse_auth_request(char *buf, int len, const struct user **u,
                *p = '\0';
                p++;
                create_argv(p, ",", &features);
+               /*
+                * Still accept sideband and AES feature requests (as a no-op)
+                * because some 0.6.x clients request them. The two checks
+                * below may be removed after 0.7.1.
+                */
                for (i = 0; features[i]; i++) {
                        if (strcmp(features[i], "sideband") == 0)
                                continue;
                        if (strcmp(features[i], "aes_ctr128") == 0)
                                continue;
+                       /*
+                        * ->sha256_requested can go away after 0.7.0 but the
+                        * check has to stay until 0.9.0.
+                        */
+                       if (strcmp(features[i], "sha256") == 0)
+                               cf->sha256_requested = true;
                        else {
                                ret = -E_BAD_FEATURE;
                                goto out;
@@ -829,13 +872,13 @@ static int run_command(struct command_context *cc, struct iovec *iov)
        }
        perms = server_command_perms[ret];
        if ((perms & cc->u->perms) != perms)
-               return -E_PERM;
+               return -ERRNO_TO_PARA_ERROR(EPERM);
        lcmd = lls_cmd(ret, server_cmd_suite);
        end = iov->iov_base + iov->iov_len;
        for (i = 0; p < end; i++)
                p += strlen(p) + 1;
        argc = i;
-       argv = para_malloc((argc + 1) * sizeof(char *));
+       argv = arr_alloc(argc + 1, sizeof(char *));
        for (i = 0, p = iov->iov_base; p < end; i++) {
                argv[i] = para_strdup(p);
                p += strlen(p) + 1;
@@ -889,8 +932,8 @@ int handle_connect(int fd)
 {
        int ret;
        unsigned char rand_buf[APC_CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
-       unsigned char challenge_hash[HASH_SIZE];
-       char *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
+       unsigned char challenge_hash[HASH2_SIZE];
+       char *command = NULL, *buf = alloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
        size_t numbytes;
        struct command_context cc_struct = {.u = NULL}, *cc = &cc_struct;
        struct iovec iov;
@@ -905,7 +948,7 @@ int handle_connect(int fd)
        /* send Welcome message */
        ret = write_va_buffer(fd, "This is para_server, version "
                PACKAGE_VERSION ".\n"
-               "Features: sideband,aes_ctr128\n"
+               "Features: sha256\n" /* no longer announce this after 0.8.0 */
        );
        if (ret < 0)
                goto net_err;
@@ -953,11 +996,19 @@ int handle_connect(int fd)
         * of the random data.
         */
        ret = -E_BAD_AUTH;
-       if (numbytes != HASH_SIZE)
-               goto net_err;
-       hash_function((char *)rand_buf, APC_CHALLENGE_SIZE, challenge_hash);
-       if (memcmp(challenge_hash, buf, HASH_SIZE))
-               goto net_err;
+       if (cf.sha256_requested) {
+               if (numbytes != HASH2_SIZE)
+                       goto net_err;
+               hash2_function((char *)rand_buf, APC_CHALLENGE_SIZE, challenge_hash);
+               if (memcmp(challenge_hash, buf, HASH2_SIZE))
+                       goto net_err;
+       } else { /* old client. This can be removed after 0.7.0 */
+               if (numbytes != HASH_SIZE)
+                       goto net_err;
+               hash_function((char *)rand_buf, APC_CHALLENGE_SIZE, challenge_hash);
+               if (memcmp(challenge_hash, buf, HASH_SIZE))
+                       goto net_err;
+       }
        /* auth successful */
        alarm(0);
        PARA_INFO_LOG("good auth for %s\n", cc->u->name);
index ff4ce6fb7663c5a0c35e1a42b433060b3b162b89..a6a001919acc5038039d5de0b0590a265052e978 100644 (file)
@@ -37,7 +37,7 @@ static void compress_close(struct filter_node *fn)
        free(fn->private_data);
 }
 
-static int compress_post_select(__a_unused struct sched *s, void *context)
+static int compress_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct private_compress_data *pcd = fn->private_data;
@@ -66,7 +66,7 @@ next_buffer:
        if (inplace)
                op = ip;
        else
-               op = para_malloc(length);
+               op = alloc(length);
        for (i = 0; i < length / 2; i++) {
                /* be careful in that heat, my dear */
                int sample = *ip++;
@@ -116,7 +116,7 @@ err:
 
 static void compress_open(struct filter_node *fn)
 {
-       struct private_compress_data *pcd = para_calloc(sizeof(*pcd));
+       struct private_compress_data *pcd = zalloc(sizeof(*pcd));
        uint32_t inertia = U32_OPTVAL(INERTIA, fn->lpr);
        uint32_t aggressiveness = U32_OPTVAL(AGGRESSIVENESS, fn->lpr);
 
@@ -162,6 +162,6 @@ const struct filter lsg_filter_cmd_com_compress_user_data = {
        .setup = compress_setup,
        .open = compress_open,
        .close = compress_close,
-       .pre_select = generic_filter_pre_select,
-       .post_select = compress_post_select,
+       .pre_monitor = generic_filter_pre_monitor,
+       .post_monitor = compress_post_monitor,
 };
index f518f89af154933ffaa64401ba1086b7e3f369c9..c72581118326924a25b5ca9b020316439135d60b 100644 (file)
@@ -291,12 +291,10 @@ AC_DEFUN([NEED_FLAC_OBJECTS], [{
 }])
 ########################################################################### faad
 STASH_FLAGS
-LIB_ARG_WITH([faad], [-lfaad -lmp4ff])
+LIB_ARG_WITH([faad], [-lfaad])
 HAVE_FAAD=yes
 AC_CHECK_HEADER(neaacdec.h, [], HAVE_FAAD=no)
-AC_CHECK_HEADER(mp4ff.h, [], HAVE_FAAD=no)
 AC_CHECK_LIB([faad], [NeAACDecOpen], [], HAVE_FAAD=no)
-AC_CHECK_LIB([mp4ff], [mp4ff_meta_get_artist], [], HAVE_FAAD=no)
 LIB_SUBST_FLAGS(faad)
 UNSTASH_FLAGS
 ########################################################################### mad
@@ -373,11 +371,16 @@ AC_CHECK_HEADER(samplerate.h, [], HAVE_SAMPLERATE=no)
 AC_CHECK_LIB([samplerate], [src_process], [], HAVE_SAMPLERATE=no)
 LIB_SUBST_FLAGS(samplerate)
 UNSTASH_FLAGS
+######################################################################### ubsan
+AC_ARG_ENABLE([ubsan], [AS_HELP_STRING(--enable-ubsan,
+       [Detect and report undefined behaviour.])],
+       [ENABLE_UBSAN=yes], [ENABLE_UBSAN=no])
+AC_SUBST(ENABLE_UBSAN)
 ######################################################################### server
 if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes && test -n "$BISON" && \
                test -n "$FLEX"; then
        build_server="yes"
-       executables="$executables server"
+       executables="$executables server upgrade_db"
        server_errlist_objs="
                server
                afh_common
@@ -391,7 +394,6 @@ if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes && test -n "$BISON" && \
                daemon
                http_send
                close_on_fork
-               mm
                crypt_common
                base64
                ipc
@@ -430,13 +432,24 @@ if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes && test -n "$BISON" && \
        NEED_OPUS_OBJECTS() && server_errlist_objs="$server_errlist_objs opus_afh opus_common"
        NEED_FLAC_OBJECTS && server_errlist_objs="$server_errlist_objs flac_afh"
        if test $HAVE_FAAD = yes; then
-               server_errlist_objs="$server_errlist_objs aac_afh"
+               server_errlist_objs="$server_errlist_objs aac_afh mp4"
        fi
        server_objs="$server_errlist_objs"
        AC_SUBST(server_objs, add_dot_o($server_objs))
 else
        build_server="no"
 fi
+############################################################# upgrade_db
+upgrade_db_objs='
+       crypt_common
+       exec
+       fd
+       string
+       upgrade_db
+       version
+       base64
+'
+AC_SUBST(upgrade_db_objs, add_dot_o($upgrade_db_objs))
 ############################################################# client
 if test -n "$CRYPTOLIB"; then
        build_client="yes"
@@ -571,7 +584,7 @@ fi
 if test $HAVE_OSS = yes -o $HAVE_ALSA = yes; then
        build_mixer="yes"
        executables="$executables mixer"
-       mixer_errlist_objs="mixer exec string fd lsu version"
+       mixer_errlist_objs="mixer exec string fd time lsu version"
        if test $HAVE_OSS = yes; then
                mixer_errlist_objs="$mixer_errlist_objs oss_mix"
        fi
@@ -678,7 +691,7 @@ NEED_OPUS_OBJECTS && recv_errlist_objs="$recv_errlist_objs opus_afh opus_common"
 NEED_FLAC_OBJECTS && recv_errlist_objs="$recv_errlist_objs flac_afh"
 
 if test $HAVE_FAAD = yes; then
-       recv_errlist_objs="$recv_errlist_objs aac_afh"
+       recv_errlist_objs="$recv_errlist_objs aac_afh mp4"
 fi
 recv_objs="$recv_errlist_objs"
 AC_SUBST(recv_objs, add_dot_o($recv_objs))
@@ -713,7 +726,7 @@ NEED_FLAC_OBJECTS && {
        audio_format_handlers="$audio_format_handlers flac"
 }
 if test $HAVE_FAAD = yes; then
-       afh_errlist_objs="$afh_errlist_objs aac_afh"
+       afh_errlist_objs="$afh_errlist_objs aac_afh mp4"
        audio_format_handlers="$audio_format_handlers aac"
 fi
 
@@ -771,7 +784,7 @@ NEED_FLAC_OBJECTS && {
        play_errlist_objs="$play_errlist_objs flacdec_filter flac_afh"
 }
 if test $HAVE_FAAD = yes; then
-       play_errlist_objs="$play_errlist_objs aac_afh aacdec_filter"
+       play_errlist_objs="$play_errlist_objs aac_afh aacdec_filter mp4"
 fi
 if test $HAVE_MAD = yes; then
        play_errlist_objs="$play_errlist_objs mp3dec_filter"
@@ -827,6 +840,7 @@ audioc_errlist_objs="
        lsu
        net
        fd
+       time
        version
 "
 if test $HAVE_READLINE = yes; then
@@ -834,7 +848,6 @@ if test $HAVE_READLINE = yes; then
                buffer_tree
                interactive
                sched
-               time
        "
 fi
 audioc_objs="$audioc_errlist_objs"
@@ -855,6 +868,7 @@ id3 version 2 support: $HAVE_ID3TAG
 faad: $HAVE_FAAD
 audio format handlers: $audio_format_handlers
 
+exe: $executables
 para_server: $build_server
 para_gui: $build_gui
 para_mixer: $build_mixer
diff --git a/crypt.h b/crypt.h
index 9623a0035a3b1a89818bd99c3c09c1dac041519b..5ca6a54112b60f6d61b16485248fb28ff708df6b 100644 (file)
--- a/crypt.h
+++ b/crypt.h
@@ -200,3 +200,42 @@ void hash_to_asc(const unsigned char *hash, char *asc);
  * less than or equal to h2, respectively.
  */
 int hash_compare(const unsigned char *h1, const unsigned char *h2);
+
+/** Size of the hash value in bytes. */
+#define HASH2_SIZE 32
+
+/**
+ * Compute the hash2 of the given input data.
+ *
+ * \param data Pointer to the data to compute the hash value from.
+ * \param len The length of \a data in bytes.
+ * \param hash Result pointer.
+ *
+ * \a hash must point to an area at least \p HASH2_SIZE bytes large.
+ *
+ * \sa sha(3), openssl(1).
+ * */
+void hash2_function(const char *data, unsigned long len, unsigned char *hash);
+
+/**
+ * Convert a hash2 value to ascii format.
+ *
+ * \param hash the hash value.
+ * \param asc Result pointer.
+ *
+ * \a asc must point to an area of at least 2 * \p HASH2_SIZE + 1 bytes which
+ * will be filled by the function with the ascii representation of the hash
+ * value given by \a hash, and a terminating \p NULL byte.
+ */
+void hash2_to_asc(const unsigned char *hash, char *asc);
+
+/**
+ * Compare two version 2 hashes.
+ *
+ * \param h1 Pointer to the first hash value.
+ * \param h2 Pointer to the second hash value.
+ *
+ * \return 1, -1, or zero, depending on whether \a h1 is greater than,
+ * less than or equal to h2, respectively.
+ */
+int hash2_compare(const unsigned char *h1, const unsigned char *h2);
index ff24e356ab8d837f8d59d67a3c983d5264376db1..d7471235682855d3f376f0d5796b39567498937b 100644 (file)
@@ -160,6 +160,31 @@ int hash_compare(const unsigned char *h1, const unsigned char *h2)
        return 0;
 }
 
+void hash2_to_asc(const unsigned char *hash, char *asc)
+{
+       int i;
+       const char hexchar[] = "0123456789abcdef";
+
+       for (i = 0; i < HASH2_SIZE; i++) {
+               asc[2 * i] = hexchar[hash[i] >> 4];
+               asc[2 * i + 1] = hexchar[hash[i] & 0xf];
+       }
+       asc[2 * HASH2_SIZE] = '\0';
+}
+
+int hash2_compare(const unsigned char *h1, const unsigned char *h2)
+{
+       int i;
+
+       for (i = 0; i < HASH2_SIZE; i++) {
+               if (h1[i] < h2[i])
+                       return -1;
+               if (h1[i] > h2[i])
+                       return 1;
+       }
+       return 0;
+}
+
 /**
  * Check header of an openssh private key and compute bignum offset.
  *
@@ -292,7 +317,7 @@ int decode_private_key(const char *key_file, unsigned char **result,
                goto unmap;
 
        key_size = footer - begin;
-       key = para_malloc(key_size + 1);
+       key = alloc(key_size + 1);
        for (i = 0, j = 0; begin + i < footer; i++) {
                if (para_isspace(begin[i]))
                        continue;
index a4e2f3193730d456ce9908eb55888cdb759154e0..d8f598bef0c2eb1709fa46169c314e17a212d7b8 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -58,24 +58,30 @@ static void daemon_set_default_log_colors(void)
 }
 
 /**
- * Set the color for one loglevel.
+ * Set the color for log messages of the given severity level.
  *
- * \param arg Must be of the form "ll:[fg [bg]] [attr]".
+ * \param arg Must be of the form "severity:[fg [bg]] [attr]".
  */
 void daemon_set_log_color_or_die(const char *arg)
 {
+       unsigned ll;
+       const char * const sev[] = {SEVERITIES};
        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++;
-       color_parse_or_die(p, me->log_colors[ll]);
-       return;
+       for (ll = 0; ll < NUM_LOGLEVELS; ll++) {
+               const char *name = sev[ll];
+               /*
+                * Parse only the first part of the string so that, for
+                * example, the argument "info:something_else" is recognized.
+                * Note that the string comparison is performed
+                * case-insensitively.
+                */
+               if (strncasecmp(arg, name, strlen(name)))
+                       continue;
+               return color_parse_or_die(p + 1, me->log_colors[ll]);
+       }
 err:
        PARA_EMERG_LOG("%s: invalid color argument\n", arg);
        exit(EXIT_FAILURE);
@@ -134,16 +140,29 @@ void daemon_set_logfile(const char *logfile_name)
 }
 
 /**
- * Suppress log messages with severity lower than the given loglevel.
+ * Control the verbosity for logging.
  *
- * \param loglevel The smallest level that should be logged.
+ * This instructs the daemon to not log subsequent messages whose severity is
+ * lower than the given value.
+ *
+ * \param loglevel The new log level.
  */
-void daemon_set_loglevel(const char *loglevel)
+void daemon_set_loglevel(int loglevel)
 {
-       int ret = get_loglevel_by_name(loglevel);
+       assert(loglevel >= 0);
+       assert(loglevel < NUM_LOGLEVELS);
+       me->loglevel = loglevel;
+}
 
-       assert(ret >= 0);
-       me->loglevel = ret;
+/**
+ * Get the current log level of the daemon.
+ *
+ * \return Greater or equal than zero and less than NUM_LOGLEVELS. This
+ * function never fails.
+ */
+int daemon_get_loglevel(void)
+{
+       return me->loglevel;
 }
 
 /**
@@ -263,9 +282,9 @@ void daemon_close_log(void)
 }
 
 /**
- * fopen() the logfile in append mode.
+ * Open the logfile in append mode.
  *
- * \return Either succeeds or exits.
+ * This function either succeeds or exits.
  */
 void daemon_open_log_or_die(void)
 {
index b530b0d76b48b673fd7ae7626c48a6dd91ab7751..54879924954d0352d25cec928c1bcff9a6f9e58e 100644 (file)
--- a/daemon.h
+++ b/daemon.h
@@ -13,7 +13,8 @@ __malloc char *daemon_get_uptime_str(const struct timeval *current_time);
 void daemon_set_logfile(const char *logfile_name);
 void daemon_set_hooks(void (*pre_log_hook)(void), void (*post_log_hook)(void));
 void daemon_set_flag(unsigned flag);
-void daemon_set_loglevel(const char *loglevel);
+int daemon_get_loglevel(void);
+void daemon_set_loglevel(int loglevel);
 bool daemon_init_colors_or_die(int color_arg, int color_arg_auto,
                int color_arg_no, bool logfile_given);
 void daemon_set_log_color_or_die(const char *arg);
index 639c93fcbf99bc93ddc054ad43d03f30811270ac..0b20bcc8b1149132c4a1fa81a238c4479cb60df6 100644 (file)
 #include "net.h"
 #include "fd.h"
 
+#ifndef DCCP_SOCKOPT_CCID
+#define DCCP_SOCKOPT_CCID 13 /**< Sets both TX/RX CCID. */
+#endif
+
 static void dccp_recv_close(struct receiver_node *rn)
 {
        if (rn->fd > 0)
@@ -59,6 +63,9 @@ static int dccp_recv_ccid_support_check(const struct lls_parse_result *lpr)
        return 1;
 }
 
+/** Flowopt shortcut */
+#define OPT_ADD(fo, lev, opt, val, len) flowopt_add(fo, lev, opt, #opt, val, len)
+
 static int dccp_recv_open(struct receiver_node *rn)
 {
        struct lls_parse_result *lpr = rn->lpr;
@@ -76,14 +83,14 @@ static int dccp_recv_open(struct receiver_node *rn)
        /* Copy CCID preference list (u8 array required) */
        given = lls_opt_given(r_c);
        if (given) {
-               ccids = para_malloc(given);
+               ccids = alloc(given);
                fo = flowopt_new();
                for (i = 0; i < given; i++)
                        ccids[i] = lls_int32_val(i, r_c);
                OPT_ADD(fo, SOL_DCCP, DCCP_SOCKOPT_CCID, ccids, i);
        }
 
-       fd = makesock(IPPROTO_DCCP, 0, host, port, fo);
+       fd = makesock(IPPROTO_DCCP, false, host, port, fo);
        flowopt_cleanup(fo);
        free(ccids);
        if (fd < 0)
@@ -109,16 +116,16 @@ err:
        return ret;
 }
 
-static void dccp_recv_pre_select(struct sched *s, void *context)
+static void dccp_recv_pre_monitor(struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
 
-       if (generic_recv_pre_select(s, rn) <= 0)
+       if (generic_recv_pre_monitor(s, rn) <= 0)
                return;
-       para_fd_set(rn->fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(rn->fd, s);
 }
 
-static int dccp_recv_post_select(struct sched *s, void *context)
+static int dccp_recv_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
        struct btr_node *btrn = rn->btrn;
@@ -136,7 +143,7 @@ static int dccp_recv_post_select(struct sched *s, void *context)
        ret = -E_DCCP_OVERRUN;
        if (iovcnt == 0)
                goto out;
-       ret = readv_nonblock(rn->fd, iov, iovcnt, &s->rfds, &num_bytes);
+       ret = readv_nonblock(rn->fd, iov, iovcnt, &num_bytes);
        if (num_bytes == 0)
                goto out;
        if (num_bytes <= iov[0].iov_len) /* only the first buffer was filled */
@@ -154,6 +161,6 @@ out:
 const struct receiver lsg_recv_cmd_com_dccp_user_data = {
        .open = dccp_recv_open,
        .close = dccp_recv_close,
-       .pre_select = dccp_recv_pre_select,
-       .post_select = dccp_recv_post_select,
+       .pre_monitor = dccp_recv_pre_monitor,
+       .post_monitor = dccp_recv_post_monitor,
 };
index bca7ad6781e29d2bb1bea686d3971e02ede59909..6182c964fde04719ee4736dc43482caf434ee395 100644 (file)
@@ -24,8 +24,8 @@
 #include "net.h"
 #include "server.h"
 #include "list.h"
-#include "send.h"
 #include "sched.h"
+#include "send.h"
 #include "vss.h"
 #include "fd.h"
 
@@ -36,16 +36,19 @@ struct dccp_fec_client {
        struct fec_client *fc;
 };
 
-static void dccp_pre_select(int *max_fileno, fd_set *rfds,
-               __a_unused fd_set *wfds)
+static void dccp_pre_monitor(struct sched *s)
 {
        unsigned n;
 
        FOR_EACH_LISTEN_FD(n, dss)
                if (dss->listen_fds[n] >= 0)
-                       para_fd_set(dss->listen_fds[n], rfds, max_fileno);
+                       sched_monitor_readfd(dss->listen_fds[n], s);
 }
 
+#ifndef DCCP_SOCKOPT_TX_CCID
+#define DCCP_SOCKOPT_TX_CCID 14 /**< Set/get the TX CCID. */
+#endif
+
 /**
  * Query the TX CCID used on the sender-client half connection.
  * \param sockfd Server socket descriptor to query (after accept(2)).
@@ -90,6 +93,13 @@ static void dccp_shutdown(void)
        free_sender_status(dss);
 }
 
+#ifndef DCCP_SOCKOPT_GET_CUR_MPS
+#define DCCP_SOCKOPT_GET_CUR_MPS 5 /**< Max packet size, RFC 4340, 14. */
+#endif
+
+/** Estimated worst-case length of a DCCP header including options. */
+#define DCCP_MAX_HEADER 128
+
 /** * Obtain current MPS according to RFC 4340, sec. 14. */
 static int dccp_init_fec(struct sender_client *sc)
 {
@@ -119,14 +129,14 @@ static void dccp_send_fec(struct sender_client *sc, char *buf, size_t len)
                dccp_shutdown_client(sc);
 }
 
-static void dccp_post_select(fd_set *rfds, __a_unused fd_set *wfds)
+static void dccp_post_monitor(__a_unused struct sched *s)
 {
        struct sender_client *sc;
        struct dccp_fec_client *dfc;
        int tx_ccid;
        uint32_t k, n;
 
-       sc = accept_sender_client(dss, rfds);
+       sc = accept_sender_client(dss);
        if (!sc)
                return;
 
@@ -148,7 +158,7 @@ static void dccp_post_select(fd_set *rfds, __a_unused fd_set *wfds)
                shutdown_client(sc, dss);
                return;
        }
-       dfc = para_calloc(sizeof(*dfc));
+       dfc = zalloc(sizeof(*dfc));
        sc->private_data = dfc;
        k = OPT_UINT32_VAL(DCCP_DATA_SLICES_PER_GROUP);
        n = OPT_UINT32_VAL(DCCP_SLICES_PER_GROUP);
@@ -249,8 +259,8 @@ const struct sender dccp_sender = {
        .name = "dccp",
        .init = dccp_send_init,
        .shutdown = dccp_shutdown,
-       .pre_select = dccp_pre_select,
-       .post_select = dccp_post_select,
+       .pre_monitor = dccp_pre_monitor,
+       .post_monitor = dccp_post_monitor,
        .shutdown_clients = dccp_shutdown_clients,
        .client_cmds = {
                [SENDER_on] = dccp_com_on,
diff --git a/error.h b/error.h
index fe44ff5c50487744881ea1d21d1007528a2ecd70..f19aa634bf1f8a2306249a8898eacfefd0078a06 100644 (file)
--- a/error.h
+++ b/error.h
@@ -2,6 +2,7 @@
 
 /** \file error.h List of error codes and messages. */
 
+/** \cond para_error */
 /** Codes and messages. */
 #define PARA_ERRORS \
        PARA_ERROR(SUCCESS, "success"), \
@@ -13,7 +14,6 @@
        PARA_ERROR(AFS_SHORT_READ, "short read from afs socket"), \
        PARA_ERROR(AFS_SIGNAL, "afs caught deadly signal"), \
        PARA_ERROR(AFS_SOCKET, "afs socket not writable"), \
-       PARA_ERROR(AFT_SYNTAX, "audio file table syntax error"), \
        PARA_ERROR(ALSA, "alsa error"), \
        PARA_ERROR(ALSA_MIX_GET_VAL, "could not read control element state"), \
        PARA_ERROR(ALSA_MIX_OPEN, "could not open mixer"), \
@@ -30,7 +30,6 @@
        PARA_ERROR(AO_OPEN_LIVE, "ao: could not open audio device"), \
        PARA_ERROR(AO_PLAY, "ao_play() failed"), \
        PARA_ERROR(AO_PTHREAD, "pthread error"), \
-       PARA_ERROR(ARG_NOT_FOUND, "argument not found in arg vector"), \
        PARA_ERROR(ASN1_PARSE, "could not parse ASN.1 key"), \
        PARA_ERROR(ATOI_JUNK_AT_END, "further characters after number"), \
        PARA_ERROR(ATOI_NO_DIGITS, "no digits found in string"), \
@@ -52,7 +51,6 @@
        PARA_ERROR(BAD_CT, "invalid chunk table or bad FEC configuration"), \
        PARA_ERROR(BAD_FEATURE, "invalid feature request"), \
        PARA_ERROR(BAD_FEC_HEADER, "invalid fec header"), \
-       PARA_ERROR(BAD_LL, "invalid loglevel"), \
        PARA_ERROR(BAD_PATH, "invalid path"), \
        PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \
        PARA_ERROR(BAD_SAMPLE_FORMAT, "sample format not supported"), \
        PARA_ERROR(MAKESOCK, "makesock error"), \
        PARA_ERROR(MAX_CLIENTS, "maximal number of clients exceeded"), \
        PARA_ERROR(MISSING_COLON, "syntax error: missing colon"), \
-       PARA_ERROR(MOOD_SYNTAX, "mood syntax error"), \
        PARA_ERROR(MOOD_PARSE, "mood parse error"), \
        PARA_ERROR(MP3DEC_CORRUPT, "too many corrupt frames"), \
        PARA_ERROR(MP3DEC_EOF, "mp3dec: end of file"), \
        PARA_ERROR(MP3_INFO, "could not read mp3 info"), \
-       PARA_ERROR(MP4FF_BAD_CHANNEL_COUNT, "mp4ff: invalid number of channels"), \
-       PARA_ERROR(MP4FF_BAD_SAMPLE, "mp4ff: invalid sample number"), \
-       PARA_ERROR(MP4FF_BAD_SAMPLERATE, "mp4ff: invalid sample rate"), \
-       PARA_ERROR(MP4FF_BAD_SAMPLE_COUNT, "mp4ff: invalid number of samples"), \
-       PARA_ERROR(MP4FF_META_READ, "mp4ff: could not read mp4 metadata"), \
-       PARA_ERROR(MP4FF_META_WRITE, "mp4ff: could not update mp4 metadata"), \
-       PARA_ERROR(MP4FF_OPEN, "mp4ff: open failed"), \
-       PARA_ERROR(MP4FF_TRACK, "mp4ff: no audio track"), \
+       PARA_ERROR(MP4_READ, "mp4: read error or unexpected end of file"), \
+       PARA_ERROR(MP4_CORRUPT, "invalid/corrupt mp4 file"), \
+       PARA_ERROR(MP4_BAD_SAMPLE, "mp4: invalid sample number"), \
+       PARA_ERROR(MP4_BAD_SAMPLERATE, "mp4: invalid sample rate"), \
+       PARA_ERROR(MP4_BAD_SAMPLE_COUNT, "mp4: invalid number of samples"), \
+       PARA_ERROR(MP4_TRACK, "mp4: no audio track"), \
+       PARA_ERROR(MP4_MISSING_ATOM, "mp4: essential atom not found"), \
        PARA_ERROR(MPI_SCAN, "could not scan multi-precision integer"), \
        PARA_ERROR(NAME_TOO_LONG, "name too long for struct sockaddr_un"), \
        PARA_ERROR(NO_AFHI, "audio format handler info required"), \
        PARA_ERROR(OGGDEC_VERSION, "vorbis version mismatch"), \
        PARA_ERROR(OGG_EMPTY, "no ogg pages found"), \
        PARA_ERROR(OGG_PACKET_IN, "ogg_stream_packetin() failed"), \
-       PARA_ERROR(OGG_STREAM_FLUSH, "ogg_stream_flush() failed"), \
        PARA_ERROR(OGG_SYNC, "internal ogg storage overflow"), \
        PARA_ERROR(OPENSSH_PARSE, "could not parse openssh private key"), \
        PARA_ERROR(OPUS_COMMENT, "invalid or corrupted opus comment"), \
        PARA_ERROR(OPUS_HEADER, "invalid opus header"), \
        PARA_ERROR(OPUS_SET_GAIN, "opus: could not set gain"), \
        PARA_ERROR(PATH_FOUND, ""), /* not really an error */ \
-       PARA_ERROR(PERM, "permission denied"), \
        PARA_ERROR(PLAYLIST_EMPTY, "attempted to load empty playlist"), \
        PARA_ERROR(PLAYLIST_LOADED, ""), /* not really an error */ \
        PARA_ERROR(PREBUFFER_SUCCESS, "prebuffering complete"), \
  * 'E_') and gets later redefined to expand to the error text only
  */
 #define PARA_ERROR(err, msg) E_ ## err
+/**
+ * Numeric error codes.
+ *
+ * Public functions which can fail return the negated value of one of the
+ * constants defined here to indicate the cause of the error.
+ *
+ * \sa \ref para_strerror().
+ */
 enum para_error_codes {PARA_ERRORS};
 #undef PARA_ERROR
 #define PARA_ERROR(err, msg) msg
@@ -260,6 +262,7 @@ enum para_error_codes {PARA_ERRORS};
 extern const char * const para_errlist[];
 /** Exactly one .c file per executable must define the array. */
 #define DEFINE_PARA_ERRLIST const char * const para_errlist[] = {PARA_ERRORS}
+/** \endcond para_error */
 
 /**
  * This bit indicates whether a number is considered a system error number
diff --git a/fd.c b/fd.c
index 33891d2e6c9f1c3568428b1df93b4b92e295ef61..763f756cdc3b5086ee9d26f432dbbfb760e02e4a 100644 (file)
--- a/fd.c
+++ b/fd.c
@@ -176,14 +176,11 @@ __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...)
  * \param fd The file descriptor to read from.
  * \param iov Scatter/gather array used in readv().
  * \param iovcnt Number of elements in \a iov.
- * \param rfds An optional fd set pointer.
  * \param num_bytes Result pointer. Contains the number of bytes read from \a fd.
  *
- * If rfds is not NULL and the (non-blocking) file descriptor fd is not set in
- * rfds, this function returns early without doing anything. Otherwise it tries
- * to read up to sz bytes from fd, where sz is the sum of the lengths of all
- * vectors in iov. Like \ref xwrite(), EAGAIN and EINTR are not considered
- * error conditions. However, EOF is.
+ * This function tries to read up to sz bytes from fd, where sz is the sum of
+ * the lengths of all vectors in iov. Like \ref xwrite(), EAGAIN and EINTR are
+ * not considered error conditions. However, EOF is.
  *
  * \return Zero or a negative error code. If the underlying call to readv(2)
  * returned zero (indicating an end of file condition) or failed for some
@@ -197,24 +194,12 @@ __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...)
  *
  * \sa \ref xwrite(), read(2), readv(2).
  */
-int readv_nonblock(int fd, struct iovec *iov, int iovcnt, fd_set *rfds,
-               size_t *num_bytes)
+int readv_nonblock(int fd, struct iovec *iov, int iovcnt, size_t *num_bytes)
 {
        int ret, i, j;
 
        *num_bytes = 0;
-       /*
-        * Avoid a shortcoming of select(): Reads from a non-blocking fd might
-        * return EAGAIN even if FD_ISSET() returns true. However, FD_ISSET()
-        * returning false definitely means that no data can currently be read.
-        * This is the common case, so it is worth to avoid the overhead of the
-        * read() system call in this case.
-        */
-       if (rfds && !FD_ISSET(fd, rfds))
-               return 0;
-
        for (i = 0, j = 0; i < iovcnt;) {
-
                /* fix up the first iov */
                assert(j < iov[i].iov_len);
                iov[i].iov_base += j;
@@ -251,7 +236,6 @@ int readv_nonblock(int fd, struct iovec *iov, int iovcnt, fd_set *rfds,
  * \param fd The file descriptor to read from.
  * \param buf The buffer to read data to.
  * \param sz The size of \a buf.
- * \param rfds \see \ref readv_nonblock().
  * \param num_bytes \see \ref readv_nonblock().
  *
  * This is a simple wrapper for readv_nonblock() which uses an iovec with a single
@@ -259,10 +243,10 @@ int readv_nonblock(int fd, struct iovec *iov, int iovcnt, fd_set *rfds,
  *
  * \return The return value of the underlying call to readv_nonblock().
  */
-int read_nonblock(int fd, void *buf, size_t sz, fd_set *rfds, size_t *num_bytes)
+int read_nonblock(int fd, void *buf, size_t sz, size_t *num_bytes)
 {
        struct iovec iov = {.iov_base = buf, .iov_len = sz};
-       return readv_nonblock(fd, &iov, 1, rfds, num_bytes);
+       return readv_nonblock(fd, &iov, 1, num_bytes);
 }
 
 /**
@@ -271,7 +255,6 @@ int read_nonblock(int fd, void *buf, size_t sz, fd_set *rfds, size_t *num_bytes)
  * \param fd The file descriptor to receive from.
  * \param pattern The expected pattern.
  * \param bufsize The size of the internal buffer.
- * \param rfds Passed to read_nonblock().
  *
  * This function tries to read at most \a bufsize bytes from the non-blocking
  * file descriptor \a fd. If at least \p strlen(\a pattern) bytes have been
@@ -283,11 +266,11 @@ int read_nonblock(int fd, void *buf, size_t sz, fd_set *rfds, size_t *num_bytes)
  *
  * \sa \ref read_nonblock(), \sa strncasecmp(3).
  */
-int read_pattern(int fd, const char *pattern, size_t bufsize, fd_set *rfds)
+int read_pattern(int fd, const char *pattern, size_t bufsize)
 {
        size_t n, len;
-       char *buf = para_malloc(bufsize + 1);
-       int ret = read_nonblock(fd, buf, bufsize, rfds, &n);
+       char *buf = alloc(bufsize + 1);
+       int ret = read_nonblock(fd, buf, bufsize, &n);
 
        buf[n] = '\0';
        if (ret < 0)
@@ -325,36 +308,6 @@ bool file_exists(const char *fn)
        return !stat(fn, &statbuf);
 }
 
-/**
- * Paraslash's wrapper for select(2).
- *
- * It calls select(2) (with no exceptfds) and starts over if select() was
- * interrupted by a signal.
- *
- * \param n The highest-numbered descriptor in any of the two sets, plus 1.
- * \param readfds fds that should be checked for readability.
- * \param writefds fds that should be checked for writablility.
- * \param timeout_tv upper bound on the amount of time elapsed before select()
- * returns.
- *
- * \return The return value of the underlying select() call on success, the
- * negative system error code on errors.
- *
- * All arguments are passed verbatim to select(2).
- * \sa select(2) select_tut(2).
- */
-int para_select(int n, fd_set *readfds, fd_set *writefds,
-               struct timeval *timeout_tv)
-{
-       int ret;
-       do
-               ret = select(n, readfds, writefds, NULL, timeout_tv);
-       while (ret < 0 && errno == EINTR);
-       if (ret < 0)
-               return -ERRNO_TO_PARA_ERROR(errno);
-       return ret;
-}
-
 /**
  * Set a file descriptor to blocking mode.
  *
@@ -391,34 +344,6 @@ __must_check int mark_fd_nonblocking(int fd)
        return 1;
 }
 
-/**
- * Set a file descriptor in a fd_set.
- *
- * \param fd The file descriptor to be set.
- * \param fds The file descriptor set.
- * \param max_fileno Highest-numbered file descriptor.
- *
- * This wrapper for FD_SET() passes its first two arguments to \p FD_SET. Upon
- * return, \a max_fileno contains the maximum of the old_value and \a fd.
- *
- * \sa \ref para_select.
-*/
-void para_fd_set(int fd, fd_set *fds, int *max_fileno)
-{
-       assert(fd >= 0 && fd < FD_SETSIZE);
-#if 0
-       {
-               int flags = fcntl(fd, F_GETFL);
-               if (!(flags & O_NONBLOCK)) {
-                       PARA_EMERG_LOG("fd %d is a blocking file descriptor\n", fd);
-                       exit(EXIT_FAILURE);
-               }
-       }
-#endif
-       FD_SET(fd, fds);
-       *max_fileno = PARA_MAX(*max_fileno, fd);
-}
-
 /**
  * Paraslash's wrapper for mmap.
  *
@@ -642,6 +567,47 @@ int para_munmap(void *start, size_t length)
        return -ERRNO_TO_PARA_ERROR(err);
 }
 
+/**
+ * Simple wrapper for poll(2).
+ *
+ * It calls poll(2) and starts over if the call was interrupted by a signal.
+ *
+ * \param fds See poll(2).
+ * \param nfds See poll(2).
+ * \param timeout See poll(2).
+ *
+ * \return The return value of the underlying poll() call on success, the
+ * negative paraslash error code on errors.
+ *
+ * All arguments are passed verbatim to poll(2).
+ */
+int xpoll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+       int ret;
+
+       do
+               ret = poll(fds, nfds, timeout);
+       while (ret < 0 && errno == EINTR);
+       return ret < 0? -ERRNO_TO_PARA_ERROR(errno) : ret;
+}
+
+/**
+ * Check a file descriptor for readability.
+ *
+ * \param fd The file descriptor.
+ *
+ * \return positive if fd is ready for reading, zero if it isn't, negative if
+ * an error occurred.
+ *
+ * \sa \ref write_ok().
+ */
+int read_ok(int fd)
+{
+       struct pollfd pfd = {.fd = fd, .events = POLLIN};
+       int ret = xpoll(&pfd, 1, 0);
+       return ret < 0? ret : pfd.revents & POLLIN;
+}
+
 /**
  * Check a file descriptor for writability.
  *
@@ -649,18 +615,14 @@ int para_munmap(void *start, size_t length)
  *
  * \return positive if fd is ready for writing, zero if it isn't, negative if
  * an error occurred.
+ *
+ * \sa \ref read_ok().
  */
-
 int write_ok(int fd)
 {
-       struct timeval tv;
-       fd_set wfds;
-
-       FD_ZERO(&wfds);
-       FD_SET(fd, &wfds);
-       tv.tv_sec = 0;
-       tv.tv_usec = 0;
-       return para_select(fd + 1, NULL, &wfds, &tv);
+       struct pollfd pfd = {.fd = fd, .events = POLLOUT};
+       int ret = xpoll(&pfd, 1, 0);
+       return ret < 0? ret : pfd.revents & POLLOUT;
 }
 
 /**
diff --git a/fd.h b/fd.h
index c9e79426f5c27ebf621367ce24b1c073c4e97e23..270d0ce200df30f16333fcfbdb47173500cacddc 100644 (file)
--- a/fd.h
+++ b/fd.h
@@ -6,11 +6,9 @@ int xrename(const char *oldpath, const char *newpath);
 int write_all(int fd, const char *buf, size_t len);
 __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...);
 bool file_exists(const char *);
-int para_select(int n, fd_set *readfds, fd_set *writefds,
-               struct timeval *timeout_tv);
+int xpoll(struct pollfd *fds, nfds_t nfds, int timeout);
 __must_check int mark_fd_nonblocking(int fd);
 __must_check int mark_fd_blocking(int fd);
-void para_fd_set(int fd, fd_set *fds, int *max_fileno);
 int para_mmap(size_t length, int prot, int flags, int fd, void *map);
 int para_open(const char *path, int flags, mode_t mode);
 int para_mkdir(const char *path, mode_t mode);
@@ -18,12 +16,12 @@ int para_chdir(const char *path);
 int mmap_full_file(const char *filename, int open_mode, void **map,
        size_t *size, int *fd_ptr);
 int para_munmap(void *start, size_t length);
+int read_ok(int fd);
 int write_ok(int fd);
 void valid_fd_012(void);
-int readv_nonblock(int fd, struct iovec *iov, int iovcnt, fd_set *rfds,
-               size_t *num_bytes);
-int read_nonblock(int fd, void *buf, size_t sz, fd_set *rfds, size_t *num_bytes);
-int read_pattern(int fd, const char *pattern, size_t bufsize, fd_set *rfds);
+int readv_nonblock(int fd, struct iovec *iov, int iovcnt, size_t *num_bytes);
+int read_nonblock(int fd, void *buf, size_t sz, size_t *num_bytes);
+int read_pattern(int fd, const char *pattern, size_t bufsize);
 int xwrite(int fd, const char *buf, size_t len);
 int xwritev(int fd, struct iovec *iov, int iovcnt);
 int for_each_file_in_dir(const char *dirname,
diff --git a/fec.c b/fec.c
index 2301cc8d2d4653b8bc82a8bcc46b5867b5d7af14..932e0693e3786fc28b5a180428a9b296534a1fd5 100644 (file)
--- a/fec.c
+++ b/fec.c
@@ -97,7 +97,7 @@ static void init_mul_table(void)
 
 static unsigned char *alloc_matrix(int rows, int cols)
 {
-       return para_malloc(rows * cols);
+       return arr_alloc(rows, cols);
 }
 
 /*
@@ -245,9 +245,9 @@ static void matmul(unsigned char *a, unsigned char *b, unsigned char *c,
 static int invert_mat(unsigned char *src, int k)
 {
        int irow, icol, row, col, ix, error;
-       int *indxc = para_malloc(k * sizeof(int));
-       int *indxr = para_malloc(k * sizeof(int));
-       int *ipiv = para_malloc(k * sizeof(int)); /* elements used as pivots */
+       int *indxc = arr_alloc(k, sizeof(int));
+       int *indxr = arr_alloc(k, sizeof(int));
+       int *ipiv = arr_alloc(k, sizeof(int)); /* elements used as pivots */
        unsigned char c, *p, *id_row = alloc_matrix(1, k),
                *temp_row = alloc_matrix(1, k);
 
@@ -371,9 +371,9 @@ static void invert_vdm(unsigned char *src, int k)
         * c holds the coefficient of P(x) = Prod (x - p_i), i=0..k-1
         * b holds the coefficient for the matrix inversion
         */
-       c = para_malloc(k);
-       b = para_malloc(k);
-       p = para_malloc(k);
+       c = alloc(k);
+       b = alloc(k);
+       p = alloc(k);
 
        for (j = 1, i = 0; i < k; i++, j += k) {
                c[i] = 0;
@@ -466,7 +466,7 @@ int fec_new(int k, int n, struct fec_parms **result)
 
        if (k < 1 || k > GF_SIZE + 1 || n > GF_SIZE + 1 || k > n)
                return -E_FEC_PARMS;
-       parms = para_malloc(sizeof(struct fec_parms));
+       parms = alloc(sizeof(struct fec_parms));
        parms->k = k;
        parms->n = n;
        parms->enc_matrix = alloc_matrix(n, k);
@@ -607,10 +607,10 @@ int fec_decode(struct fec_parms *parms, unsigned char **data, int *idx,
        if (ret < 0)
                return ret;
        /* do the actual decoding */
-       slice = para_malloc(k * sizeof(unsigned char *));
+       slice = arr_alloc(k, sizeof(unsigned char *));
        for (row = 0; row < k; row++) {
                if (idx[row] >= k) {
-                       slice[row] = para_calloc(sz);
+                       slice[row] = zalloc(sz);
                        for (col = 0; col < k; col++)
                                addmul(slice[row], data[col],
                                        m_dec[row * k + col], sz);
index 13d4f7b22f6a46da75ef4b629275d603fc5f39d1..1d8fbc16db71094d4f81b2c8c6bb88014087582c 100644 (file)
@@ -225,8 +225,8 @@ static int add_slice(char *buf, struct fecdec_group *fg)
        }
        if (fg->num_slices == 0) {
                fg->num_slices = fg->h.slices_per_group;
-               fg->idx = para_malloc(fg->num_slices * sizeof(int));
-               fg->data = para_calloc(fg->num_slices * sizeof(unsigned char *));
+               fg->idx = arr_alloc(fg->num_slices, sizeof(int));
+               fg->data = arr_zalloc(fg->num_slices, sizeof(unsigned char *));
        }
        r = fg->num_received_slices;
        /* Check if we already have this slice. */
@@ -236,7 +236,7 @@ static int add_slice(char *buf, struct fecdec_group *fg)
                return 0;
        }
        fg->idx[r] = slice_num;
-       fg->data[r] = para_malloc(fg->h.slice_bytes);
+       fg->data[r] = alloc(fg->h.slice_bytes);
        memcpy(fg->data[r], buf, fg->h.slice_bytes);
        fg->num_received_slices++;
        return 1;
@@ -431,7 +431,7 @@ static void fecdec_close(struct filter_node *fn)
        fn->private_data = NULL;
 }
 
-static int fecdec_post_select(__a_unused struct sched *s, void *context)
+static int fecdec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
@@ -471,14 +471,14 @@ out:
 static void fecdec_open(struct filter_node *fn)
 {
        struct private_fecdec_data *pfd;
-       pfd = para_calloc(sizeof(*pfd));
+       pfd = zalloc(sizeof(*pfd));
        fn->private_data = pfd;
        fn->min_iqs = FEC_HEADER_SIZE;
 }
 
 const struct filter lsg_filter_cmd_com_fecdec_user_data = {
        .open = fecdec_open,
-       .pre_select = generic_filter_pre_select,
-       .post_select = fecdec_post_select,
+       .pre_monitor = generic_filter_pre_monitor,
+       .post_monitor = fecdec_post_monitor,
        .close = fecdec_close,
 };
index 9a5ed5d7fab7dc01fe46fa5ecab6940ea3a28828..ba902070d9498677e94872860aeb9bbcbcd5235e 100644 (file)
@@ -64,12 +64,12 @@ static int prepare_output_file(struct writer_node *wn)
                close(fd);
                return ret;
        }
-       pfwd = wn->private_data = para_calloc(sizeof(*pfwd));
+       pfwd = wn->private_data = zalloc(sizeof(*pfwd));
        pfwd->fd = fd;
        return 1;
 }
 
-static void file_write_pre_select(struct sched *s, void *context)
+static void file_write_pre_monitor(struct sched *s, void *context)
 {
        struct writer_node *wn = context;
        struct private_file_write_data *pfwd = wn->private_data;
@@ -79,7 +79,7 @@ static void file_write_pre_select(struct sched *s, void *context)
                return;
        if (ret < 0 || !pfwd)
                return sched_min_delay(s);
-       para_fd_set(pfwd->fd, &s->wfds, &s->max_fileno);
+       sched_monitor_writefd(pfwd->fd, s);
 }
 
 static void file_write_close(struct writer_node *wn)
@@ -92,7 +92,7 @@ static void file_write_close(struct writer_node *wn)
        free(pfwd);
 }
 
-static int file_write_post_select(__a_unused struct sched *s, void *context)
+static int file_write_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct writer_node *wn = context;
        struct private_file_write_data *pfwd = wn->private_data;
@@ -111,7 +111,7 @@ static int file_write_post_select(__a_unused struct sched *s, void *context)
                ret = prepare_output_file(wn);
                goto out;
        }
-       if (!FD_ISSET(pfwd->fd, &s->wfds))
+       if (!sched_write_ok(pfwd->fd, s))
                return 0;
        bytes = btr_next_buffer(btrn, &buf);
        assert(bytes > 0);
@@ -128,7 +128,7 @@ out:
 
 /** the init function of the file writer */
 struct writer lsg_write_cmd_com_file_user_data = {
-       .pre_select = file_write_pre_select,
-       .post_select = file_write_post_select,
+       .pre_monitor = file_write_pre_monitor,
+       .post_monitor = file_write_post_monitor,
        .close = file_write_close,
 };
index d4a2423904ca96f87f1ec11600f1c67e2b101ae1..722cb16fb35bfaec09bbaca046ebf62931df8036 100644 (file)
--- a/filter.c
+++ b/filter.c
@@ -120,14 +120,14 @@ int main(int argc, char *argv[])
                EMBRACE(.name = "stdin"));
        stdin_task_register(sit, &s);
 
-       fns = para_malloc(OPT_GIVEN(FILTER) * sizeof(*fns));
+       fns = arr_alloc(OPT_GIVEN(FILTER), sizeof(*fns));
        for (i = 0, parent = sit->btrn; i < OPT_GIVEN(FILTER); i++) {
                const char *fa = lls_string_val(i, OPT_RESULT(FILTER));
                const char *name;
                struct filter_node *fn;
                struct task_info ti;
 
-               fn = fns[i] = para_calloc(sizeof(*fn));
+               fn = fns[i] = zalloc(sizeof(*fn));
                fn->filter_num = filter_setup(fa, &fn->conf, &filter_lpr);
                name = filter_name(fn->filter_num);
                fn->lpr = filter_lpr;
@@ -137,8 +137,8 @@ int main(int argc, char *argv[])
                        EMBRACE(.name = name, .parent = parent,
                        .handler = f->execute, .context = fn));
                ti.name = name;
-               ti.pre_select = f->pre_select;
-               ti.post_select = f->post_select;
+               ti.pre_monitor = f->pre_monitor;
+               ti.post_monitor = f->post_monitor;
                ti.context = fn;
                if (f->open)
                        f->open(fn);
@@ -149,8 +149,7 @@ int main(int argc, char *argv[])
                EMBRACE(.name = "stdout", .parent = parent));
        stdout_task_register(sot, &s);
 
-       s.default_timeout.tv_sec = 1;
-       s.default_timeout.tv_usec = 0;
+       s.default_timeout = 1000;
        btr_log_tree(sit->btrn, LL_INFO);
        ret = schedule(&s);
        sched_shutdown(&s);
index 69d4dfee8534941ab74bb17d7d2a5ff8e23eed6b..77057e6a7d2b9a997219d907b3ddd459716283ad 100644 (file)
--- a/filter.h
+++ b/filter.h
@@ -28,16 +28,18 @@ struct filter_node {
 };
 
 /**
- * The structure associated with a paraslash filter.
+ * Describes a method to convert audio data.
  *
- * Paraslash filters are "modules" which transform an audio stream. struct
- * filter contains methods which are implemented by each filter.
+ * Paraslash filters are "modules" which transform the data of an audio stream.
+ * This structure contains the methods which have to be implemented by each
+ * filter.
  *
- * Note: As several instances of the same filter may be running at the same
- * time, all these filter functions must be reentrant; no static non-constant
- * variables may be used.
+ * As several instances of the same filter may be running at the same time, all
+ * filter methods must be reentrant and no static non-constant variables must
+ * be used.
  *
- * \sa \ref filter_node.
+ * \sa \ref filter_node, struct \ref receiver, struct \ref writer, struct \ref
+ * sched.
  */
 struct filter {
        /**
@@ -81,24 +83,10 @@ struct filter {
         * This should free whatever ->setup() has allocated.
         */
        void (*teardown)(const struct lls_parse_result *lpr, void *conf);
-       /**
-        * Set scheduler timeout and add file descriptors to fd sets.
-        *
-        * This function controls the timeout value for the next call to
-        * select(2). It may decrease the current timeout but shall never
-        * increase it. The second purpose of this function is to add file
-        * descriptors to the two fd sets of the sched structure. The
-        * descriptors in these sets will be watched by the subsequent
-        * select(2) call.
-        */
-       void (*pre_select)(struct sched *s, void *context);
-       /**
-        * Convert (filter) the given data.
-        *
-        * Pointer to the converting function of the filter. On errors, the
-        * post_select function is supposed to return a negative error code.
-        */
-       int (*post_select)(struct sched *s, void *context);
+       /** Force a zero timeout if data is available in the buffer tree. */
+       void (*pre_monitor)(struct sched *s, void *context);
+       /** Convert (filter) input data into output data. */
+       int (*post_monitor)(struct sched *s, void *context);
        /**
         * Answer a buffer tree query.
         *
@@ -124,7 +112,7 @@ int filter_setup(const char *fa, void **conf, struct lls_parse_result **lprp);
 #define FILTER_CMD_OPT_STRING_VAL(_cmd, _opt, _lpr) \
        (lls_string_val(0, FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
 
-void generic_filter_pre_select(struct sched *s, void *context);
+void generic_filter_pre_monitor(struct sched *s, void *context);
 int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels,
                char **result);
 
index add788a8f30465251ff111822b7131cd2e94054b..f48e457005ca3510fb51d5e5f95405af15d927c1 100644 (file)
@@ -169,17 +169,16 @@ void print_filter_list(void)
 }
 
 /**
- * Set select timeout of the scheduler.
+ * Request a minimal timeout if not idle.
  *
- * \param s The scheduler.
- * \param context Pointer to the filter node (task context).
+ * \param s The scheduler instance.
+ * \param context Pointer to the filter node.
  *
- * This looks at the status of the btr node of the filter. If data is available
- * in the input queue of the filter, or if an error occurred, a minimal timeout
- * for the next select call is requested from the scheduler. Otherwise the
- * scheduler timeout is left unchanged.
+ * If the buffer tree node of the given filter node has data available (or is
+ * in error state) a minimal I/O timeout is requested from the scheduler.
+ * Otherwise the function does nothing.
  */
-void generic_filter_pre_select(struct sched *s, void *context)
+void generic_filter_pre_monitor(struct sched *s, void *context)
 {
        struct filter_node *fn = context;
 
index 6e23683937f6932aaa739095ef8281fc9075da55..2b5b6c1d9aa758e3d2e769756cc3d75f3bb57120 100644 (file)
@@ -374,8 +374,8 @@ static int flac_afh_read_chunks(struct private_flac_afh_data *pfad)
                        goto free_decoder;
                if (c >= chunk_table_size) {
                        chunk_table_size = 2 * chunk_table_size + 100;
-                       afhi->chunk_table = para_realloc(afhi->chunk_table,
-                               chunk_table_size * sizeof(uint32_t));
+                       afhi->chunk_table = arr_realloc(afhi->chunk_table,
+                               chunk_table_size, sizeof(uint32_t));
                }
                afhi->chunk_table[c] = pos;
 
@@ -481,7 +481,7 @@ static int flac_rewrite_tags(const char *map, size_t map_bytes,
        FLAC__Metadata_Iterator *iter;
        FLAC__StreamMetadata *b = NULL;
        FLAC__bool ok;
-       struct private_flac_afh_data *pfad = para_calloc(sizeof(*pfad));
+       struct private_flac_afh_data *pfad = zalloc(sizeof(*pfad));
 
        pfad->map = map;
        pfad->map_bytes = map_bytes;
index 6a3a8effaf83b3b9e333e429762f0ff2d3938afe..f3060f5721c1c22753cceb4229ee4dbe178dbf52 100644 (file)
@@ -135,7 +135,7 @@ static FLAC__StreamDecoderWriteStatus write_cb(
        struct btr_node *btrn = fn->btrn;
        size_t k, n = frame->header.blocksize;
        unsigned channels = FLAC__stream_decoder_get_channels(decoder);
-       char *outbuffer = para_malloc(n * channels * 2);
+       char *outbuffer = arr_alloc(n, channels * 2);
 
        if (channels == 1) {
                for (k = 0; k < n; k++) {
@@ -205,7 +205,7 @@ static bool output_queue_full(struct btr_node *btrn)
        return btr_get_output_queue_size(btrn) > FLACDEC_MAX_OUTPUT_SIZE;
 }
 
-static void flacdec_pre_select(struct sched *s, void *context)
+static void flacdec_pre_monitor(struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct private_flacdec_data *pfd = fn->private_data;
@@ -221,7 +221,7 @@ static void flacdec_pre_select(struct sched *s, void *context)
                return sched_min_delay(s);
 }
 
-static int flacdec_post_select(__a_unused struct sched *s, void *context)
+static int flacdec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct private_flacdec_data *pfd = fn->private_data;
@@ -286,7 +286,7 @@ static void flacdec_close(struct filter_node *fn)
 
 static void flacdec_open(struct filter_node *fn)
 {
-       struct private_flacdec_data *pfd = para_calloc(sizeof(*pfd));
+       struct private_flacdec_data *pfd = zalloc(sizeof(*pfd));
        fn->private_data = pfd;
        fn->min_iqs = 0;
 }
@@ -294,7 +294,7 @@ static void flacdec_open(struct filter_node *fn)
 const struct filter lsg_filter_cmd_com_flacdec_user_data = {
        .open = flacdec_open,
        .close = flacdec_close,
-       .pre_select = flacdec_pre_select,
-       .post_select = flacdec_post_select,
+       .pre_monitor = flacdec_pre_monitor,
+       .post_monitor = flacdec_post_monitor,
        .execute = flacdec_execute,
 };
index dbe4900862fef83a552d9c60d9ac21d4c8253d5e..763fa6de3252d2f5f188fc3f447f2029d0f5775a 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -46,6 +46,22 @@ void hash_function(const char *data, unsigned long len, unsigned char *hash)
        gcry_md_close(handle);
 }
 
+void hash2_function(const char *data, unsigned long len, unsigned char *hash)
+{
+       gcry_error_t gret;
+       gcry_md_hd_t handle;
+       unsigned char *md;
+
+       gret = gcry_md_open(&handle, GCRY_MD_SHA256, 0);
+       assert(gret == 0);
+       gcry_md_write(handle, data, (size_t)len);
+       gcry_md_final(handle);
+       md = gcry_md_read(handle, GCRY_MD_SHA256);
+       assert(md);
+       memcpy(hash, md, HASH2_SIZE);
+       gcry_md_close(handle);
+}
+
 void get_random_bytes_or_die(unsigned char *buf, int num)
 {
        gcry_randomize(buf, (size_t)num, GCRY_STRONG_RANDOM);
@@ -390,7 +406,7 @@ static int get_private_key(const char *key_file, struct asymmetric_key **result)
                ret = -E_SEXP_BUILD;
                goto free_params;
        }
-       key = para_malloc(sizeof(*key));
+       key = alloc(sizeof(*key));
        key->sexp = sexp;
        *result = key;
        ret = bits;
@@ -440,7 +456,7 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
                goto release_n;
        }
        PARA_INFO_LOG("successfully read %u bit ssh public key\n", bits);
-       key = para_malloc(sizeof(*key));
+       key = alloc(sizeof(*key));
        key->num_bytes = ret;
        key->sexp = sexp;
        *result = key;
@@ -608,7 +624,7 @@ struct stream_cipher {
 struct stream_cipher *sc_new(const unsigned char *data, int len)
 {
        gcry_error_t gret;
-       struct stream_cipher *sc = para_malloc(sizeof(*sc));
+       struct stream_cipher *sc = alloc(sizeof(*sc));
 
        assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
        gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_AES128,
index 8370649301906eaef68a4c30d1b1f3975283dea8..1019e579a39d8559b2aecc658c7651fd6b7704dc 100644 (file)
@@ -89,7 +89,7 @@ err:
        return -E_GC_WRITE;
 }
 
-static void gc_pre_select(struct sched *s, void *context)
+static void gc_pre_monitor(struct sched *s, void *context)
 {
        struct grab_client *gc = context;
        int ret = btr_node_status(gc->btrn, 0, BTR_NT_LEAF);
@@ -98,14 +98,14 @@ static void gc_pre_select(struct sched *s, void *context)
                return;
        if (ret < 0)
                sched_min_delay(s);
-       para_fd_set(gc->fd, &s->wfds, &s->max_fileno);
+       sched_monitor_writefd(gc->fd, s);
 }
 
 /*
- * We need this forward declaration as post_select() needs
+ * We need this forward declaration as gc_post_monitor() needs
  * activate_grab_client and vice versa.
  */
-static int gc_post_select(struct sched *s, void *context);
+static int gc_post_monitor(struct sched *s, void *context);
 
 /**
  * Move a grab client to the active list and start it.
@@ -129,8 +129,8 @@ static void gc_activate(struct grab_client *gc, struct sched *s)
 
        gc->task = task_register(&(struct task_info) {
                .name = name,
-               .pre_select = gc_pre_select,
-               .post_select = gc_post_select,
+               .pre_monitor = gc_pre_monitor,
+               .post_monitor = gc_post_monitor,
                .context = gc,
        }, s);
 }
@@ -171,7 +171,7 @@ static int gc_close(struct grab_client *gc, int err)
                /*
                 * We must not free the gc structure here as it contains ->task
                 * which is still used because this function is called from
-                * post_select().
+                * post_monitor().
                 */
                close(gc->fd);
                gc->fd = -1;
@@ -182,7 +182,7 @@ static int gc_close(struct grab_client *gc, int err)
        return 0;
 }
 
-static int gc_post_select(__a_unused struct sched *s, void *context)
+static int gc_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct grab_client *gc = context;
        struct btr_node *btrn = gc->btrn;
@@ -261,7 +261,7 @@ static int gc_check_args(struct lls_parse_result *lpr, struct grab_client *gc)
 int grab_client_new(int fd, struct lls_parse_result *lpr, struct sched *s)
 {
        int ret;
-       struct grab_client *gc = para_calloc(sizeof(struct grab_client));
+       struct grab_client *gc = zalloc(sizeof(struct grab_client));
 
        ret = gc_check_args(lpr, gc);
        if (ret < 0)
diff --git a/gui.c b/gui.c
index d779ff864ddebfc38a34ba1a0d166b5c04089556..ebfab3564836e7b1708dc430e9375815b00d49b7 100644 (file)
--- a/gui.c
+++ b/gui.c
@@ -431,7 +431,7 @@ static void rb_add_entry(int color, char *msg)
 
        if (strwidth(msg, &len) < 0)
                return;
-       new = para_malloc(sizeof(struct rb_entry));
+       new = alloc(sizeof(struct rb_entry));
        new->color = color;
        new->len = len;
        new->msg = msg;
@@ -609,19 +609,19 @@ static void clear_all_items(void)
        }
 }
 
-static void status_pre_select(struct sched *s, void *context)
+static void status_pre_monitor(struct sched *s, void *context)
 {
        struct status_task *st = context;
 
        if (st->fd >= 0)
-               para_fd_set(st->fd, &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(st->fd, s);
        if (task_get_notification(st->task) < 0)
                return sched_min_delay(s);
        if (st->fd < 0)
                sched_request_barrier_or_min_delay(&st->next_exec, s);
 }
 
-static int status_post_select(struct sched *s, void *context)
+static int status_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct status_task *st = context;
        size_t sz;
@@ -667,7 +667,7 @@ static int status_post_select(struct sched *s, void *context)
        }
        assert(st->loaded < st->bufsize);
        ret = read_nonblock(st->fd, st->buf + st->loaded,
-               st->bufsize - st->loaded, &s->rfds, &sz);
+               st->bufsize - st->loaded, &sz);
        st->loaded += sz;
        ret2 = for_each_stat_item(st->buf, st->loaded, update_item);
        if (ret < 0 || ret2 < 0) {
@@ -892,9 +892,9 @@ static void reread_conf(void)
 }
 
 /* React to various signal-related events. */
-static int signal_post_select(struct sched *s, __a_unused void *context)
+static int signal_post_monitor(struct sched *s, __a_unused void *context)
 {
-       int ret = para_next_signal(&s->rfds);
+       int ret = para_next_signal();
 
        if (ret <= 0)
                return 0;
@@ -931,18 +931,18 @@ static enum exec_status exec_status(void)
        return EXEC_IDLE;
 }
 
-static void exec_pre_select(struct sched *s, void *context)
+static void exec_pre_monitor(struct sched *s, void *context)
 {
        struct exec_task *et = context;
        if (exec_fds[0] >= 0)
-               para_fd_set(exec_fds[0], &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(exec_fds[0], s);
        if (exec_fds[1] >= 0)
-               para_fd_set(exec_fds[1], &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(exec_fds[1], s);
        if (task_get_notification(et->task) < 0)
                sched_min_delay(s);
 }
 
-static int exec_post_select(struct sched *s, void *context)
+static int exec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct exec_task *ct = context;
        int i, ret;
@@ -963,7 +963,7 @@ static int exec_post_select(struct sched *s, void *context)
                        continue;
                ret = read_nonblock(exec_fds[i],
                        ct->command_buf[i] + ct->cbo[i],
-                       COMMAND_BUF_SIZE - 1 - ct->cbo[i], &s->rfds, &sz);
+                       COMMAND_BUF_SIZE - 1 - ct->cbo[i], &sz);
                ct->cbo[i] += sz;
                sz = ct->cbo[i];
                ct->cbo[i] = for_each_line(ct->flags[i], ct->command_buf[i],
@@ -992,10 +992,10 @@ static int exec_post_select(struct sched *s, void *context)
        return 0;
 }
 
-static void input_pre_select(struct sched *s, __a_unused void *context)
+static void input_pre_monitor(struct sched *s, __a_unused void *context)
 {
        if (exec_status() != EXEC_XCMD)
-               para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(STDIN_FILENO, s);
        if (window_update_needed())
                sched_min_delay(s);
 }
@@ -1089,7 +1089,7 @@ static void handle_command(int c)
                keyname);
 }
 
-static int input_post_select(__a_unused struct sched *s,
+static int input_post_monitor(__a_unused struct sched *s,
                __a_unused void *context)
 {
        int ret;
@@ -1115,7 +1115,7 @@ static int input_post_select(__a_unused struct sched *s,
        ret = wgetch(top.win);
        if (ret == ERR)
                return 0;
-       if (ret == KEY_RESIZE) /* already handled in signal_post_select() */
+       if (ret == KEY_RESIZE) /* already handled in signal_post_monitor() */
                return 0;
        if (exs == EXEC_IDLE)
                handle_command(ret);
@@ -1391,26 +1391,26 @@ static int setup_tasks_and_schedule(void)
        struct status_task status_task = {.fd = -1};
        struct input_task input_task = {.task = NULL};
        struct signal_task *signal_task;
-       struct sched sched = {.default_timeout = {.tv_sec = 1}};
+       struct sched sched = {.default_timeout = 1000};
 
        exec_task.task = task_register(&(struct task_info) {
                .name = "exec",
-               .pre_select = exec_pre_select,
-               .post_select = exec_post_select,
+               .pre_monitor = exec_pre_monitor,
+               .post_monitor = exec_post_monitor,
                .context = &exec_task,
        }, &sched);
 
        status_task.task = task_register(&(struct task_info) {
                .name = "status",
-               .pre_select = status_pre_select,
-               .post_select = status_post_select,
+               .pre_monitor = status_pre_monitor,
+               .post_monitor = status_post_monitor,
                .context = &status_task,
        }, &sched);
 
        input_task.task = task_register(&(struct task_info) {
                .name = "input",
-               .pre_select = input_pre_select,
-               .post_select = input_post_select,
+               .pre_monitor = input_pre_monitor,
+               .post_monitor = input_post_monitor,
                .context = &input_task,
        }, &sched);
 
@@ -1422,8 +1422,8 @@ static int setup_tasks_and_schedule(void)
        para_install_sighandler(SIGWINCH);
        signal_task->task = task_register(&(struct task_info) {
                .name = "signal",
-               .pre_select = signal_pre_select,
-               .post_select = signal_post_select,
+               .pre_monitor = signal_pre_monitor,
+               .post_monitor = signal_post_monitor,
                .context = signal_task,
        }, &sched);
        ret = schedule(&sched);
index 6f4acf37a318834f95614ccc1365c12208048b90..f71e802b5c6561d4d5b7340209a76cebf3235206 100644 (file)
@@ -122,16 +122,16 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
        d[SI_status_flags].align = LEFT;
        d[SI_status_flags].x = 11;
        d[SI_status_flags].y = 17;
-       d[SI_status_flags].len = 10;
+       d[SI_status_flags].len = 8;
 
        d[SI_image_id].prefix = "img: ";
        d[SI_image_id].postfix = "";
        d[SI_image_id].color.fg = COLOR_RED;
        d[SI_image_id].color.bg = COLOR_BLACK;
        d[SI_image_id].align = CENTER;
-       d[SI_image_id].x = 21;
+       d[SI_image_id].x = 19;
        d[SI_image_id].y = 17;
-       d[SI_image_id].len = 10;
+       d[SI_image_id].len = 12;
 
        d[SI_lyrics_id].prefix = "lyr: ";
        d[SI_lyrics_id].postfix = "";
index 1fb60bad810d86dbbb0098a3714c2caa3d9e835b..32c9e7b9af6b24ca0fc93f3d7d65fce15c32e03a 100644 (file)
@@ -56,17 +56,17 @@ static char *make_request_msg(void)
        return ret;
 }
 
-static void http_recv_pre_select(struct sched *s, void *context)
+static void http_recv_pre_monitor(struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
        struct private_http_recv_data *phd = rn->private_data;
 
-       if (generic_recv_pre_select(s, rn) <= 0)
+       if (generic_recv_pre_monitor(s, rn) <= 0)
                return;
        if  (phd->status == HTTP_CONNECTED)
-               para_fd_set(rn->fd, &s->wfds, &s->max_fileno);
+               sched_monitor_writefd(rn->fd, s);
        else
-               para_fd_set(rn->fd, &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(rn->fd, s);
 }
 
 /*
@@ -74,7 +74,7 @@ static void http_recv_pre_select(struct sched *s, void *context)
  * area with data read from the socket. In any case, update the state of the
  * connection if necessary.
  */
-static int http_recv_post_select(struct sched *s, void *context)
+static int http_recv_post_monitor(struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
        struct private_http_recv_data *phd = rn->private_data;
@@ -93,7 +93,7 @@ static int http_recv_post_select(struct sched *s, void *context)
                return 0;
        if (phd->status == HTTP_CONNECTED) {
                char *rq;
-               if (!FD_ISSET(rn->fd, &s->wfds))
+               if (!sched_write_ok(rn->fd, s))
                        return 0;
                rq = make_request_msg();
                PARA_INFO_LOG("sending http request\n");
@@ -105,7 +105,7 @@ static int http_recv_post_select(struct sched *s, void *context)
                return 0;
        }
        if (phd->status == HTTP_SENT_GET_REQUEST) {
-               ret = read_pattern(rn->fd, HTTP_OK_MSG, strlen(HTTP_OK_MSG), &s->rfds);
+               ret = read_pattern(rn->fd, HTTP_OK_MSG, strlen(HTTP_OK_MSG));
                if (ret < 0) {
                        PARA_ERROR_LOG("did not receive HTTP OK message\n");
                        goto out;
@@ -120,7 +120,7 @@ static int http_recv_post_select(struct sched *s, void *context)
        iovcnt = btr_pool_get_buffers(rn->btrp, iov);
        if (iovcnt == 0)
                goto out;
-       ret = readv_nonblock(rn->fd, iov, iovcnt, &s->rfds, &num_bytes);
+       ret = readv_nonblock(rn->fd, iov, iovcnt, &num_bytes);
        if (num_bytes == 0)
                goto out;
        if (num_bytes <= iov[0].iov_len) /* only the first buffer was filled */
@@ -150,7 +150,7 @@ static int http_recv_open(struct receiver_node *rn)
        struct lls_parse_result *lpr = rn->lpr;
        const char *r_i = RECV_CMD_OPT_STRING_VAL(HTTP, HOST, lpr);
        uint32_t r_p = RECV_CMD_OPT_UINT32_VAL(HTTP, PORT, lpr);
-       int fd, ret = para_connect_simple(IPPROTO_TCP, r_i, r_p);
+       int fd, ret = para_connect(IPPROTO_TCP, r_i, r_p);
 
        if (ret < 0)
                return ret;
@@ -160,7 +160,7 @@ static int http_recv_open(struct receiver_node *rn)
                close(fd);
                return ret;
        }
-       rn->private_data = phd = para_calloc(sizeof(struct private_http_recv_data));
+       rn->private_data = phd = zalloc(sizeof(struct private_http_recv_data));
        rn->fd = fd;
        phd->status = HTTP_CONNECTED;
        rn->btrp = btr_pool_new("http_recv", 320 * 1024);
@@ -170,6 +170,6 @@ static int http_recv_open(struct receiver_node *rn)
 const struct receiver lsg_recv_cmd_com_http_user_data = {
        .open = http_recv_open,
        .close = http_recv_close,
-       .pre_select = http_recv_pre_select,
-       .post_select = http_recv_post_select,
+       .pre_monitor = http_recv_pre_monitor,
+       .post_monitor = http_recv_post_monitor,
 };
index c6b9decca941489c82b629982ddb6d6dbdb2ea33..90e3ee57d1b6d40152ef6fc4026331766e2524c5 100644 (file)
@@ -20,8 +20,8 @@
 #include "server.h"
 #include "http.h"
 #include "list.h"
-#include "send.h"
 #include "sched.h"
+#include "send.h"
 #include "vss.h"
 #include "close_on_fork.h"
 #include "fd.h"
@@ -158,7 +158,7 @@ static void http_send(long unsigned current_chunk,
        }
 }
 
-static void http_post_select(fd_set *rfds, __a_unused fd_set *wfds)
+static void http_post_monitor(__a_unused struct sched *s)
 {
        struct sender_client *sc, *tmp;
        struct private_http_sender_data *phsd;
@@ -170,7 +170,7 @@ static void http_post_select(fd_set *rfds, __a_unused fd_set *wfds)
                case HTTP_STREAMING: /* nothing to do */
                        break;
                case HTTP_CONNECTED: /* need to recv get request */
-                       ret = read_pattern(sc->fd, HTTP_GET_MSG, MAXLINE, rfds);
+                       ret = read_pattern(sc->fd, HTTP_GET_MSG, MAXLINE);
                        if (ret < 0)
                                phsd->status = HTTP_INVALID_GET_REQUEST;
                        else if (ret > 0) {
@@ -188,15 +188,15 @@ static void http_post_select(fd_set *rfds, __a_unused fd_set *wfds)
                        break;
                }
        }
-       sc = accept_sender_client(hss, rfds);
+       sc = accept_sender_client(hss);
        if (!sc)
                return;
-       phsd = para_malloc(sizeof(*phsd));
+       phsd = alloc(sizeof(*phsd));
        sc->private_data = phsd;
        phsd->status = HTTP_CONNECTED;
 }
 
-static void http_pre_select(int *max_fileno, fd_set *rfds, fd_set *wfds)
+static void http_pre_monitor(struct sched *s)
 {
        struct sender_client *sc, *tmp;
        unsigned n;
@@ -204,15 +204,15 @@ static void http_pre_select(int *max_fileno, fd_set *rfds, fd_set *wfds)
        FOR_EACH_LISTEN_FD(n, hss) {
                if (hss->listen_fds[n] < 0)
                        continue;
-               para_fd_set(hss->listen_fds[n], rfds, max_fileno);
+               sched_monitor_readfd(hss->listen_fds[n], s);
        }
        list_for_each_entry_safe(sc, tmp, &hss->client_list, node) {
                struct private_http_sender_data *phsd = sc->private_data;
                if (phsd->status == HTTP_CONNECTED) /* need to recv get request */
-                       para_fd_set(sc->fd, rfds, max_fileno);
+                       sched_monitor_readfd(sc->fd, s);
                if (phsd->status == HTTP_GOT_GET_REQUEST ||
                                phsd->status == HTTP_INVALID_GET_REQUEST)
-                       para_fd_set(sc->fd, wfds, max_fileno);
+                       sched_monitor_writefd(sc->fd, s);
        }
 }
 
@@ -274,8 +274,8 @@ const struct sender http_sender = {
        .name = "http",
        .init = http_send_init,
        .shutdown = http_shutdown,
-       .pre_select = http_pre_select,
-       .post_select = http_post_select,
+       .pre_monitor = http_pre_monitor,
+       .post_monitor = http_post_monitor,
        .send = http_send,
        .shutdown_clients = http_shutdown_clients,
        .client_cmds = {
diff --git a/imdct.c b/imdct.c
index 93577b5451a0cbb8c8da58e997a9f4d77179243e..2e1089f1f818be4bcdeb20be3d4d7a865943e00e 100644 (file)
--- a/imdct.c
+++ b/imdct.c
@@ -336,7 +336,7 @@ static int fft_init(struct fft_context *s, int nbits)
        s->nbits = nbits;
        n = 1 << nbits;
 
-       s->revtab = para_malloc(n * sizeof(uint16_t));
+       s->revtab = arr_alloc(n, sizeof(uint16_t));
        for (j = 4; j <= nbits; j++) {
                int k = 1 << j;
                double freq = 2 * M_PI / k;
@@ -366,13 +366,13 @@ int imdct_init(int nbits, struct mdct_context **result)
        double alpha;
        struct mdct_context *s;
 
-       s = para_calloc(sizeof(*s));
+       s = zalloc(sizeof(*s));
        n = 1 << nbits;
        s->nbits = nbits;
        s->n = n;
        n4 = n >> 2;
-       s->tcos = para_malloc(n4 * sizeof(fftsample_t));
-       s->tsin = para_malloc(n4 * sizeof(fftsample_t));
+       s->tcos = arr_alloc(n4, sizeof(fftsample_t));
+       s->tsin = arr_alloc(n4, sizeof(fftsample_t));
 
        for (i = 0; i < n4; i++) {
                alpha = 2 * M_PI * (i + 1.0 / 8.0) / n;
index 9f4dec175caf12c9ff3bf9ad30b2c90281a9828e..e367a65920c53982af55436d7b96ea53138408f8 100644 (file)
@@ -45,7 +45,7 @@ static struct i9e_private i9e_private, *i9ep = &i9e_private;
  * running.
  *
  * \return A negative return value of zero means the i9e task terminated. Only
- * in this case it is safe to call ie9_close().
+ * in this case it is safe to call i9e_close().
  */
 int i9e_get_error(void)
 {
@@ -189,8 +189,6 @@ static char **i9e_completer(const char *text, int start, __a_unused int end)
  *
  * This function attaches the i9e input queue to an output queue of \a
  * producer.
- *
- * \return Standard.
  */
 void i9e_attach_to_stdout(struct btr_node *producer)
 {
@@ -258,18 +256,6 @@ static void clear_bottom_line(void)
        rl_point = point;
 }
 
-static bool input_available(void)
-{
-       fd_set rfds;
-       struct timeval tv = {0, 0};
-       int ret;
-
-       FD_ZERO(&rfds);
-       FD_SET(i9ep->ici->fds[0], &rfds);
-       ret = para_select(1, &rfds, NULL, &tv);
-       return ret > 0;
-}
-
 static void i9e_line_handler(char *line)
 {
        int ret;
@@ -294,7 +280,7 @@ free_line:
        free(line);
 }
 
-static int i9e_post_select(__a_unused struct sched *s, __a_unused void *context)
+static int i9e_post_monitor(__a_unused struct sched *s, __a_unused void *context)
 {
        int ret;
        struct i9e_client_info *ici = i9ep->ici;
@@ -310,7 +296,7 @@ static int i9e_post_select(__a_unused struct sched *s, __a_unused void *context)
        ret = 0;
        if (i9ep->caught_sigint)
                goto rm_btrn;
-       while (input_available()) {
+       while (read_ok(i9ep->ici->fds[0]) > 0) {
                if (i9ep->stdout_btrn) {
                        while (i9ep->key_sequence_length < sizeof(i9ep->key_sequence) - 1) {
                                buf = i9ep->key_sequence + i9ep->key_sequence_length;
@@ -327,7 +313,7 @@ static int i9e_post_select(__a_unused struct sched *s, __a_unused void *context)
                                i9ep->key_sequence_length++;
                                rl_stuff_char((int)(unsigned char)*buf);
                                rl_callback_read_char();
-                               if (!input_available())
+                               if (read_ok(i9ep->ici->fds[0]) <= 0)
                                        break;
                        }
                        i9ep->key_sequence_length = 0;
@@ -374,7 +360,7 @@ out:
        return ret;
 }
 
-static void i9e_pre_select(struct sched *s, __a_unused void *context)
+static void i9e_pre_monitor(struct sched *s, __a_unused void *context)
 {
        int ret;
 
@@ -389,7 +375,7 @@ static void i9e_pre_select(struct sched *s, __a_unused void *context)
                        return;
                }
                if (ret > 0)
-                       para_fd_set(i9ep->ici->fds[1], &s->wfds, &s->max_fileno);
+                       sched_monitor_writefd(i9ep->ici->fds[1], s);
        }
        /*
         * fd[0] might have been reset to blocking mode if our job was moved to
@@ -400,7 +386,7 @@ static void i9e_pre_select(struct sched *s, __a_unused void *context)
        if (ret < 0)
                PARA_WARNING_LOG("set to nonblock failed: (fd0 %d, %s)\n",
                        i9ep->ici->fds[0], para_strerror(-ret));
-       para_fd_set(i9ep->ici->fds[0], &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(i9ep->ici->fds[0], s);
 }
 
 static void update_winsize(void)
@@ -477,8 +463,8 @@ int i9e_open(struct i9e_client_info *ici, struct sched *s)
                return ret;
        i9ep->task = task_register(&(struct task_info) {
                .name = "i9e",
-               .pre_select = i9e_pre_select,
-               .post_select = i9e_post_select,
+               .pre_monitor = i9e_pre_monitor,
+               .post_monitor = i9e_post_monitor,
                .context = i9ep,
        }, s);
 
@@ -561,7 +547,7 @@ __printf_2_3 void i9e_log(int ll, const char* fmt,...)
  * the given text. If the length of this text exceeds the width of the
  * terminal, the text is shortened by leaving out a part in the middle.
  */
-void ie9_print_status_bar(char *buf, unsigned len)
+void i9e_print_status_bar(char *buf, unsigned len)
 {
        size_t x = i9ep->num_columns, y = (x - 4) / 2;
 
@@ -605,23 +591,21 @@ void i9e_signal_dispatch(int sig_num)
 }
 
 /**
- * Wrapper for select(2) which does not restart on interrupts.
+ * Wrapper for poll(2) which handles EINTR and returns paraslash error codes.
  *
- * \param n \sa \ref para_select().
- * \param readfds \sa \ref para_select().
- * \param writefds \sa \ref para_select().
- * \param timeout_tv \sa \ref para_select().
+ * \param fds See poll(2).
+ * \param nfds See poll(2).
+ * \param timeout See poll(2).
  *
- * \return \sa \ref para_select().
+ * \return See poll(2).
  *
- * The only difference between this function and \ref para_select() is that
- * \ref i9e_select() returns zero if the select call returned \p EINTR.
+ * The only difference between this function and \ref xpoll() is that \ref
+ * i9e_poll() returns zero if the system call was interrupted while xpoll()
+ * restarts the system call in this case.
  */
-int i9e_select(int n, fd_set *readfds, fd_set *writefds,
-               struct timeval *timeout_tv)
+int i9e_poll(struct pollfd *fds, nfds_t nfds, int timeout)
 {
-       int ret = select(n, readfds, writefds, NULL, timeout_tv);
-
+       int ret = poll(fds, nfds, timeout);
        if (ret < 0) {
                if (errno == EINTR)
                        ret = 0;
@@ -650,7 +634,7 @@ int i9e_select(int n, fd_set *readfds, fd_set *writefds,
 int i9e_extract_completions(const char *word, char **string_list,
                char ***result)
 {
-       char **matches = para_malloc(sizeof(char *));
+       char **matches = alloc(sizeof(char *));
        int match_count = 0, matches_len = 1;
        char **p;
        int len = strlen(word);
@@ -661,8 +645,8 @@ int i9e_extract_completions(const char *word, char **string_list,
                match_count++;
                if (match_count >= matches_len) {
                        matches_len *= 2;
-                       matches = para_realloc(matches,
-                               matches_len * sizeof(char *));
+                       matches = arr_realloc(matches, matches_len,
+                               sizeof(char *));
                }
                matches[match_count - 1] = para_strdup(*p);
        }
@@ -698,7 +682,7 @@ char **i9e_complete_commands(const char *word, struct i9e_completer *completers)
                if (is_prefix(word, cmd, len))
                        match_count++;
        }
-       matches = para_malloc((match_count + 1) * sizeof(*matches));
+       matches = arr_alloc(match_count + 1, sizeof(*matches));
        for (i = 0, match_count = 0; (cmd = completers[i].name); i++)
                if (is_prefix(word, cmd, len))
                        matches[match_count++] = para_strdup(cmd);
@@ -782,7 +766,7 @@ int i9e_print_completions(struct i9e_completer *completers)
        if (*p == ' ')
                p++;
        n = end - p + 1;
-       ci.word = para_malloc(n + 1);
+       ci.word = alloc(n + 1);
        strncpy(ci.word, p, n);
        ci.word[n] = '\0';
 create_matches:
@@ -810,3 +794,25 @@ create_matches:
        free(ci.word);
        return ret;
 }
+
+/**
+ * Complete on severity strings.
+ *
+ * \param ci See struct \ref i9e_completer.
+ * \param cr See struct \ref i9e_completer.
+ *
+ * This is used by para_client and para_audioc which need the same completion
+ * primitive for the ll server/audiod command. Both define their own completer
+ * which is implemented as a trivial wrapper that calls this function.
+ */
+void i9e_ll_completer(struct i9e_completion_info *ci,
+               struct i9e_completion_result *cr)
+{
+       char *sev[] = {SEVERITIES, NULL};
+
+       if (ci->word_num != 1) {
+               cr->matches = NULL;
+               return;
+       }
+       i9e_extract_completions(ci->word, sev, &cr->matches);
+}
index 40ff294018b221708dd0d1db9cac23c80fb70533..6ef7f8e259b0045a5c808c01560b5a81166b01fd 100644 (file)
@@ -80,12 +80,11 @@ struct i9e_client_info {
 
 int i9e_open(struct i9e_client_info *ici, struct sched *s);
 void i9e_attach_to_stdout(struct btr_node *producer);
-void ie9_print_status_bar(char *buf, unsigned len);
+void i9e_print_status_bar(char *buf, unsigned len);
 void i9e_close(void);
 void i9e_signal_dispatch(int sig_num);
 __printf_2_3 void i9e_log(int ll, const char* fmt,...);
-int i9e_select(int n, fd_set *readfds, fd_set *writefds,
-               struct timeval *timeout_tv);
+int i9e_poll(struct pollfd *fds, nfds_t nfds, int timeout);
 int i9e_extract_completions(const char *word, char **string_list,
                char ***result);
 char **i9e_complete_commands(const char *word, struct i9e_completer *completers);
@@ -93,3 +92,5 @@ void i9e_complete_option(char **opts, struct i9e_completion_info *ci,
                struct i9e_completion_result *cr);
 int i9e_print_completions(struct i9e_completer *completers);
 int i9e_get_error(void);
+void i9e_ll_completer(struct i9e_completion_info *ci,
+               struct i9e_completion_result *cr);
diff --git a/list.h b/list.h
index 66c6d91525031261ff94cd3028e672cb340c06cd..78c302fa322fe6bc2dae2926e95e58189c10c944 100644 (file)
--- a/list.h
+++ b/list.h
  * Copied from the Linux kernel source tree, version 2.6.13.
  *
  * Licensed under the GPL v2 as per the whole kernel source tree.
- *
  */
 
-/** \file list.h doubly linked list implementation */
+/** \file list.h Doubly linked list implementation. */
 
 #include <stddef.h> /* offsetof */
 
-/** get the struct this entry is embedded in */
+/** Get the struct this entry is embedded in. */
 #define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})
 
-/**
- * Non-NULL pointers that will result in page faults under normal
- * circumstances, used to verify that nobody uses non-initialized list entries.
- * Used for poisoning the \a next pointer of struct list_head.
- */
-#define LIST_POISON1  ((void *) 0x00100100)
-/** Non-null pointer, used for poisoning the \a prev pointer of struct
- * list_head
- */
-#define LIST_POISON2  ((void *) 0x00200200)
-
-/** Simple doubly linked list implementation. */
+/** A list head is just a pair of pointers. */
 struct list_head {
-       /** pointer to the next list entry */
+       /** Pointer to the next list entry. */
        struct list_head *next;
-       /** pointer to the previous list entry */
+       /** Pointer to the previous list entry. */
        struct list_head *prev;
 };
 
 /** Define an initialized list head. */
-#define INITIALIZED_LIST_HEAD(name) struct list_head name = { &(name), &(name) }
-
-
-/** must be called before using any other list functions */
-#define INIT_LIST_HEAD(ptr) do { \
-       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
-} while (0)
+#define INITIALIZED_LIST_HEAD(name) struct list_head name = {&(name), &(name)}
 
-
-/*
- * Some of the internal functions ("__xxx") are useful when
- * manipulating whole lists rather than single entries, as
- * sometimes we already know the next/prev entries and we can
- * generate better code by using them directly rather than
- * using the generic single-entry routines.
- */
-
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_add(struct list_head *new,
-                             struct list_head *prev,
-                             struct list_head *next)
+/** This must be called before using any other list functions. */
+static inline void init_list_head(struct list_head *head)
 {
-       next->prev = new;
-       new->next = next;
-       new->prev = prev;
-       prev->next = new;
+       head->next = head;
+       head->prev = head;
 }
 
 /**
- * add a new entry
+ * Insert a new entry after the specified head.
  *
- * \param new new entry to be added
- * \param head list head to add it after
+ * \param entry The new entry to add.
+ * \param head The list head to add it after.
  *
- * Insert a new entry after the specified head.
  * This is good for implementing stacks.
  */
-static inline void para_list_add(struct list_head *new, struct list_head *head)
+static inline void para_list_add(struct list_head *entry, struct list_head *head)
 {
-       __list_add(new, head, head->next);
+       entry->prev = head;
+       entry->next = head->next;
+       head->next->prev = entry;
+       head->next = entry;
 }
 
 /**
- * add a new entry
+ * Insert a new entry before the specified head.
  *
- * \param new new entry to be added
- * \param head list head to add it before
+ * \param entry The new entry to add.
+ * \param head list head to add it before.
  *
- * Insert a new entry before the specified head.
  * This is useful for implementing queues.
  */
-static inline void list_add_tail(struct list_head *new, struct list_head *head)
+static inline void list_add_tail(struct list_head *entry, struct list_head *head)
 {
-       __list_add(new, head->prev, head);
+       entry->prev = head->prev;
+       entry->next = head;
+       head->prev->next = entry;
+       head->prev = entry;
 }
 
-/*
- * Delete a list entry by making the prev/next entries
- * point to each other.
+/**
+ * Delete an entry from a list.
+ *
+ * \param entry The element to delete.
  *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
+ * The list entry is in an undefined state after this and \ref list_empty()
+ * does not return true.
  */
-static inline void __list_del(struct list_head * prev, struct list_head * next)
+static inline void list_del(struct list_head *entry)
 {
-       next->prev = prev;
-       prev->next = next;
+       entry->prev->next = entry->next;
+       entry->next->prev = entry->prev;
+       /*
+        * These non-NULL pointers result in page faults when dereferenced.
+        * This helps to catch bugs resulting from using deleted list heads.
+        */
+       entry->next = (void *)0x00100100;
+       entry->prev = (void *)0x00200200;
 }
 
 /**
- * Delete entry from list.
- *
- * \param entry the element to delete from the list.
+ * Delete an entry from one list and add it as another list's head.
  *
- * Note: list_empty on entry does not return true after this, the entry is
- * in an undefined state.
+ * \param entry The entry to move.
+ * \param head The head that will precede our entry.
  */
-static inline void list_del(struct list_head *entry)
+static inline void list_move(struct list_head *entry, struct list_head *head)
 {
-       __list_del(entry->prev, entry->next);
-       entry->next = LIST_POISON1;
-       entry->prev = LIST_POISON2;
+        list_del(entry);
+        para_list_add(entry, head);
 }
 
 /**
- * delete from one list and add as another's head
+ * Test whether a list contains no entries.
  *
- * \param list: the entry to move
- * \param head: the head that will precede our entry
+ * \param head The list to test.
  */
-static inline void list_move(struct list_head *list, struct list_head *head)
+static inline int list_empty(const struct list_head *head)
 {
-        __list_del(list->prev, list->next);
-        para_list_add(list, head);
+       return head->next == head;
 }
 
 /**
- * test whether a list is empty
+ * Test whether a list has just one entry.
  *
- * \param head the list to test.
+ * \param head The list to test.
  */
-static inline int list_empty(const struct list_head *head)
+static inline int list_is_singular(const struct list_head *head)
 {
-       return head->next == head;
+       return !list_empty(head) && (head->next == head->prev);
 }
 
 /**
- * get the struct for this entry
+ * Get the struct in which this entry is embedded in.
  *
- * \param ptr the &struct list_head pointer.
- * \param type the type of the struct this is embedded in.
- * \param member the name of the list_struct within the struct.
+ * \param ptr The list head pointer.
+ * \param type The type of containing structure.
+ * \param member The name of the list head member within the structure.
  */
-#define list_entry(ptr, type, member) \
-       container_of(ptr, type, member)
+#define list_entry(ptr, type, member) container_of(ptr, type, member)
 
 /**
- * iterate over list of given type
+ * Iterate over a list.
  *
- * \param pos the type * to use as a loop counter.
- * \param head the head for your list.
- * \param member the name of the list_struct within the struct.
+ * \param pos A struct pointer which serves as the iterator.
+ * \param head The head of the list.
+ * \param member The name of the list head member within the structure.
  */
 #define list_for_each_entry(pos, head, member)                         \
        for (pos = list_entry((head)->next, typeof(*pos), member);      \
@@ -169,49 +137,27 @@ static inline int list_empty(const struct list_head *head)
             pos = list_entry(pos->member.next, typeof(*pos), member))
 
 /**
- * iterate over list of given type safe against removal of list entry
+ * Iterate over list, safe against removal of list entry.
  *
- * \param pos the type * to use as a loop counter.
- * \param n another type * to use as temporary storage
- * \param head the head for your list.
- * \param member the name of the list_struct within the struct.
+ * \param pos The iterator struct pointer.
+ * \param n A second struct pointer which is used as temporary storage.
+ * \param head The head of the list.
+ * \param member The name of the list head member within the structure.
  */
 #define list_for_each_entry_safe(pos, n, head, member)                 \
        for (pos = list_entry((head)->next, typeof(*pos), member),      \
                n = list_entry(pos->member.next, typeof(*pos), member); \
             &pos->member != (head);                                    \
             pos = n, n = list_entry(n->member.next, typeof(*n), member))
-/**
- * iterate backwards over list of given type safe against removal of list entry
- * \param pos the type * to use as a loop counter.
- * \param n another type * to use as temporary storage
- * \param head the head for your list.
- * \param member the name of the list_struct within the struct.
- */
-#define list_for_each_entry_safe_reverse(pos, n, head, member)         \
-       for (pos = list_entry((head)->prev, typeof(*pos), member),      \
-               n = list_entry(pos->member.prev, typeof(*pos), member); \
-            &pos->member != (head);                                    \
-            pos = n, n = list_entry(n->member.prev, typeof(*n), member))
 
 /**
- * Get the first element from a list
- * \param ptr the list head to take the element from.
+ * Get the first element of a list.
+ *
+ * \param ptr The list head to take the element from.
  * \param type The type of the struct this is embedded in.
  * \param member The name of the list_struct within the struct.
  *
- * Note that list is expected to be not empty.
+ * Note that the list is expected to be non-empty.
  */
 #define list_first_entry(ptr, type, member) \
-        list_entry((ptr)->next, type, member)
-
-/**
- * Test whether a list has just one entry.
- *
- * \param head The list to test.
- */
-static inline int list_is_singular(const struct list_head *head)
-{
-       return !list_empty(head) && (head->next == head->prev);
-}
-
+       list_entry((ptr)->next, type, member)
index 80ae2e42be9773f0e619e1746b9bfdefbe02f2c4..bd2a23e2354dde09cc8090a08ccf6ad31d7ac9b9 100644 (file)
@@ -51,6 +51,8 @@ caption = list of audiod commands
                short_opt = o
                summary = One-shot mode: Stop grabbing if audio file changes
 
+m4_include(`com_ll.m4')
+
 [subcommand off]
        purpose = deactivate para_audiod
        [description]
diff --git a/m4/lls/include/com_ll.m4 b/m4/lls/include/com_ll.m4
new file mode 100644 (file)
index 0000000..d7576ea
--- /dev/null
@@ -0,0 +1,10 @@
+[subcommand ll]
+       purpose = Query or set the log level of the daemon
+       m4_ifelse(SUITE, `server_cmd', `aux_info = NO_PERMISSION_REQUIRED')
+       non-opts-name = [severity]
+       [description]
+               If no argument is given, the command prints the severity string (one
+               of the possible string arguments to --loglevel) which corresponds to
+               the current loglevel. Otherwise, if the given argument is a severity
+               string, the current log level is set accordingly.
+       [/description]
index daf6de92525d334d3c1791e4d0b53907ddd7fb7f..a13f079e9b60195240bb48f3b847978263a486bc 100644 (file)
@@ -11,6 +11,7 @@ $(lls_suite_dir)/%.suite: $(lls_m4_dir)/%.suite.m4 | $(lls_suite_dir)
        $(call SAY, M4 $<)
        $(M4) -Pg -I $(lls_m4_include_dir) -D GIT_VERSION=$(GIT_VERSION) \
                -D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) -D LOGLEVELS=$(LOGLEVELS) \
+               -D SUITE=$(basename $(notdir $<)) \
                $< > $@
 
 $(lls_suite_dir)/%.lsg.c: $(lls_suite_dir)/%.suite
index 93e4b573e5ae694581b5f111f1a898d45b6c4f7b..1c941c433b8fcd753beb09f1c619e1976e8c5817 100644 (file)
@@ -133,7 +133,7 @@ version-string = GIT_VERSION()
                typestr = directory
                [help]
                        The directory which contains the database for the audio file
-                       selector. The default is ~/.paraslash/afs_database-0.4.
+                       selector. The default is ~/.paraslash/afs_database-0.7.
 
                        If no database was found, the "init" command must be executed to
                        initialize the database. Once initialized, audio files may added with
index 2a39907cee624219d1fb166b07dbf84288f67bb4..8200c6249449f81b5743775f04796462fc912b83 100644 (file)
@@ -169,6 +169,8 @@ aux_info_prefix = Permissions:
                playlists. Otherwise only the given tables are created.
        [/description]
 
+m4_include(`com_ll.m4')
+
 [subcommand jmp]
        purpose = reposition the current stream
        non-opts-name = n
@@ -500,15 +502,6 @@ aux_info_prefix = Permissions:
                the vss status flags, effectively stopping playback.
        [/description]
 
-[subcommand tasks]
-       purpose = list active server tasks (deprecated)
-       aux_info = NO_PERMISSION_REQUIRED
-       [description]
-               This used to print the ID, the status and the name of each task,
-               mainly for debugging purposes. As of version 0.6.2, the subcommand
-               prints nothing. It will be removed in 0.7.0. Don't use.
-       [/description]
-
 [subcommand term]
        purpose = ask the server to terminate
        aux_info = VSS_READ | VSS_WRITE
diff --git a/m4/lls/upgrade_db.suite.m4 b/m4/lls/upgrade_db.suite.m4
new file mode 100644 (file)
index 0000000..7f46b74
--- /dev/null
@@ -0,0 +1,33 @@
+m4_define(PROGRAM, para_upgrade_db)
+[suite upgrade_db]
+version-string = GIT_VERSION()
+[supercommand para_upgrade_db]
+       purpose = upgrade the paraslash database to version 0.7
+       [description]
+               The database format changes with paraslash-0.7.0. This program converts
+               the database from the older 0.4 format that was used in paraslash 0.4.x
+               through 0.6.x. In has to be executed only once.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       [option src-database-dir]
+               summary = location of the old afs database
+               arg_info = required_arg
+               arg_type = string
+               typestr = directory
+               [help]
+                       The directory which contains the database to be converted. The default
+                       is ~/.paraslash/afs_database-0.4.
+               [/help]
+       [option dst-database-dir]
+               summary = location of the new afs database
+               arg_info = required_arg
+               arg_type = string
+               typestr = directory
+               [help]
+                       The directory which contains the converted database after the program
+                       has terminated. The default is ~/.paraslash/afs_database-0.7.
+               [/help]
diff --git a/mm.c b/mm.c
deleted file mode 100644 (file)
index 358783a..0000000
--- a/mm.c
+++ /dev/null
@@ -1,375 +0,0 @@
-/* Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
-
-/** \file mm.c Paraslash's mood methods. */
-
-#include <regex.h>
-#include <fnmatch.h>
-#include <osl.h>
-#include <lopsub.h>
-
-#include "para.h"
-#include "error.h"
-#include "string.h"
-#include "afh.h"
-#include "afs.h"
-#include "mm.h"
-
-/** The comparators for numeric mood methods (year, bitrate, ...). */
-#define MOOD_COMPARATORS \
-       MC(LESS, <) \
-       MC(LESS_OR_EQUAL, <=) \
-       MC(EQUAL, =) \
-       MC(EQUAL2, ==) \
-       MC(NOT_EQUAL, !=) \
-       MC(NOT_EQUAL2, <>) \
-       MC(GREATER, >) \
-       MC(GREATER_OR_EQUAL, >=) \
-
-/** Prefix mood comparator name with "_MC", example: MC_LESS. */
-#define MC(a, b) MC_ ## a,
-/** Each mood comparator is identified by an integer of this type. */
-enum mood_comparator_id {MOOD_COMPARATORS NUM_MOOD_COMPARATORS};
-#undef MC
-/** Stringfied mood comparator, example: "<". */
-#define MC(a, b) # b,
-/** Array of mood comparators represented as C strings ("<", "<=", ...). */
-static const char *mood_comparators[] = {MOOD_COMPARATORS};
-#undef MC
-
-static int parse_mood_comparator(const char *word)
-{
-       int i;
-
-       for (i = 0; i < NUM_MOOD_COMPARATORS; i++)
-               if (!strcmp(word, mood_comparators[i]))
-                       return i;
-       return -E_MOOD_SYNTAX;
-}
-
-struct mm_compare_num_data {
-       /** <, <=, =, !=, >=, or >. */
-       enum mood_comparator_id id;
-       /** The value given at the mood line. */
-       int32_t arg;
-};
-
-static int mm_compare_num_score_function(int32_t val,
-               const struct mm_compare_num_data *cnd)
-{
-       int res;
-       int32_t arg = cnd->arg;
-
-       switch (cnd->id) {
-       case MC_LESS:
-               res = val < arg; break;
-       case MC_LESS_OR_EQUAL:
-               res = val <= arg; break;
-       case MC_EQUAL:
-       case MC_EQUAL2:
-               res = val == arg; break;
-       case MC_NOT_EQUAL:
-       case MC_NOT_EQUAL2:
-               res = val != arg; break;
-       case MC_GREATER:
-               res = val > arg; break;
-       case MC_GREATER_OR_EQUAL:
-               res = val >= arg; break;
-       default:
-               PARA_EMERG_LOG("BUG: invalid mood comparator\n");
-               exit(EXIT_FAILURE);
-       }
-       return res? 100 : -100;
-}
-
-static int mm_compare_num_parser(int argc, char **argv, void **private)
-{
-       int ret;
-       enum mood_comparator_id id;
-       int32_t arg;
-       struct mm_compare_num_data *cnd;
-       if (argc != 2)
-               return -E_MOOD_SYNTAX;
-       ret = parse_mood_comparator(argv[1]);
-       if (ret < 0)
-               return ret;
-       id = ret;
-       ret = para_atoi32(argv[2], &arg);
-       if (ret < 0)
-               return ret;
-       cnd = para_malloc(sizeof(struct mm_compare_num_data));
-       cnd->id = id;
-       cnd->arg = arg;
-       *private = cnd;
-       return 1;
-}
-
-static int mm_regex_parser(int argc, char **argv, void **private)
-{
-       regex_t *preg;
-       int ret;
-
-       if (argc != 1)
-               return -E_MOOD_SYNTAX;
-       preg = para_malloc(sizeof(*preg));
-       ret = para_regcomp(preg, argv[1], REG_EXTENDED | REG_NOSUB);
-       if (ret < 0) {
-               free(preg);
-               return ret;
-       }
-       *private = preg;
-       return 1;
-}
-
-static int mm_regex_score_function(const regex_t *preg, const char *pattern)
-{
-       return regexec(preg, pattern, 0, NULL, 0) == 0? 100 : -100;
-}
-
-static void mm_regex_cleanup(void *private)
-{
-       regex_t *preg = private;
-       regfree(preg);
-       free(preg);
-}
-
-static int mm_artist_matches_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_regex_score_function(private, afhi->tags.artist);
-}
-
-static int mm_title_matches_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_regex_score_function(private, afhi->tags.title);
-}
-
-static int mm_album_matches_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_regex_score_function(private, afhi->tags.album);
-}
-
-static int mm_comment_matches_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_regex_score_function(private, afhi->tags.comment);
-}
-
-static int mm_bitrate_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afhi->bitrate, private);
-}
-
-static int mm_frequency_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afhi->frequency, private);
-}
-
-static int mm_channels_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afhi->channels, private);
-}
-
-static int mm_image_id_score_function(__a_unused const char *path,
-               const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afsi->image_id, private);
-}
-
-static int mm_lyrics_id_score_function(__a_unused const char *path,
-               const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afsi->lyrics_id, private);
-}
-
-static int mm_num_played_score_function(__a_unused const char *path,
-               const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afsi->num_played, private);
-}
-
-struct mm_year_data {
-       /** Comparator and year given at the mood line. */
-       struct mm_compare_num_data *cnd;
-       /** Used to detect Y2K issues. */
-       int32_t current_year;
-};
-
-static int mm_year_parser(int argc, char **argv, void **private)
-{
-       int ret;
-       struct mm_year_data *mmyd = para_malloc(sizeof(*mmyd));
-       time_t current_time;
-       struct tm *gmt;
-
-       ret = mm_compare_num_parser(argc, argv, (void **)&mmyd->cnd);
-       if (ret < 0)
-               goto err;
-       current_time = time(NULL);
-       gmt = gmtime(&current_time);
-       /* tm_year is the number of years since 1900 */
-       mmyd->current_year = gmt->tm_year + 1900;
-       *private = mmyd;
-       return 1;
-err:
-       free(mmyd);
-       return ret;
-}
-
-static int mm_year_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       const struct mm_year_data *mmyd = private;
-       int32_t tag_year;
-       int ret = para_atoi32(afhi->tags.year, &tag_year);
-
-       if (ret < 0) /* year tag not present or not a number */
-               return -100;
-       if (tag_year < 0)
-               return -100;
-       /* try to work around Y2K issues */
-       if (tag_year < 100) {
-               tag_year += 1900;
-               if (tag_year + 100 <= mmyd->current_year)
-                       tag_year += 100; /* assume tag_year >= 2000 */
-       }
-       return mm_compare_num_score_function(tag_year, mmyd->cnd);
-}
-
-static void mm_year_cleanup(void *private)
-{
-       struct mm_year_data *mmyd = private;
-
-       free(mmyd->cnd);
-       free(mmyd);
-}
-
-static int mm_no_attributes_set_parser(int argc, __a_unused char **argv,
-               __a_unused void **ignored)
-{
-       return argc? -E_MOOD_SYNTAX : 1;
-}
-
-static int mm_no_attributes_set_score_function(__a_unused const char *path,
-               const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               __a_unused const void *data)
-{
-       if (!afsi->attributes)
-               return 100;
-       return -100;
-}
-
-static int mm_path_matches_score_function(const char *path,
-               __a_unused const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               const void *data)
-{
-       if (fnmatch(data, path, 0))
-               return -100;
-       return 100;
-}
-
-static int mm_path_matches_parser(int argc, char **argv, void **data)
-{
-       if (argc != 1)
-               return -E_MOOD_SYNTAX;
-       *data = para_strdup(argv[1]);
-       return 1;
-}
-
-static void mm_path_matches_cleanup(void *data)
-{
-       free(data);
-}
-
-static int mm_is_set_parser(int argc, char **argv, void **bitnum)
-{
-       int ret;
-       unsigned char c, *res;
-
-       if (argc != 1)
-               return -E_MOOD_SYNTAX;
-       ret = get_attribute_bitnum_by_name(argv[1], &c);
-       if (ret < 0)
-               return ret;
-       res = para_malloc(1);
-       *res = c;
-       *bitnum = res;
-       return 1;
-}
-
-static int mm_is_set_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               const void *data)
-{
-       const unsigned char *bn = data;
-       if (afsi->attributes & (1ULL << *bn))
-               return 100;
-       return -100;
-}
-
-#define DEFINE_MOOD_METHOD(_name) \
-.parser = mm_ ## _name ## _parser, \
-.score_function = mm_ ## _name ## _score_function, \
-.name = #_name
-
-#define DEFINE_MOOD_METHOD_WITH_CLEANUP(_name) \
-       DEFINE_MOOD_METHOD(_name), \
-       .cleanup = mm_ ## _name ## _cleanup
-
-#define DEFINE_REGEX_MOOD_METHOD(_name) \
-       .name = #_name, \
-       .parser = mm_regex_parser, \
-       .score_function = mm_ ## _name ## _score_function, \
-       .cleanup = mm_regex_cleanup
-
-#define DEFINE_COMPARE_NUM_MOOD_METHOD(_name) \
-       .name = #_name, \
-       .parser = mm_compare_num_parser, \
-       .score_function = mm_ ## _name ## _score_function
-
-const struct mood_method mood_methods[] = {
-       {DEFINE_MOOD_METHOD(no_attributes_set)},
-       {DEFINE_MOOD_METHOD(is_set)},
-       {DEFINE_MOOD_METHOD_WITH_CLEANUP(path_matches)},
-       {DEFINE_MOOD_METHOD_WITH_CLEANUP(year)},
-       {DEFINE_REGEX_MOOD_METHOD(artist_matches)},
-       {DEFINE_REGEX_MOOD_METHOD(title_matches)},
-       {DEFINE_REGEX_MOOD_METHOD(album_matches)},
-       {DEFINE_REGEX_MOOD_METHOD(comment_matches)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(bitrate)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(frequency)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(channels)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(num_played)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(image_id)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(lyrics_id)},
-       {.parser = NULL}
-};
diff --git a/mm.h b/mm.h
deleted file mode 100644 (file)
index 28038e7..0000000
--- a/mm.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
-
-/** \file mm.h Symbols and declarations for mood methods. */
-
-/**
- * Assign scores according to a mood_method.
- *
- * Each mood_method has its own mood_score_function. The first three parameters
- * passed to that function are informations about the audio file whose score is
- * to be computed. The data argument depends on the mood method this function
- * is used for. It usually is the argument given at the end of a mood line.
- *
- * Mood score functions must return values between -100 and +100 inclusively.
- * Boolean score functions should always return either -100 or +100.
- *
- * \sa struct \ref mood_method, \ref mood_parser.
- */
-typedef int mood_score_function(const char *path, const struct afs_info *afsi,
-               const struct afh_info *afhi, const void *data);
-
-/**
- * Pre-process a mood line.
- *
- * The mood_parser of a mood_method is called once at mood open time for each
- * line of the current mood definition that contains the mood_method's name as
- * a keyword. The line is passed to the mood_parser as the first argument. The
- * mood_parser must determine whether the line is syntactically correct and
- * return a positive value if so and a negative value otherwise.
- *
- * Some mood parsers pre-process the data given in the mood line to compute a
- * structure which depends of the particular mood_method and which is used
- * later in the mood_score_function of the mood_method. The mood_parser may
- * store a pointer to its structure via the void** pointer.
- *
- * \sa \ref mood_cleanup_function, \ref mood_score_function.
- */
-typedef int mood_parser(int, char **, void **);
-
-/**
- * Deallocate resources which were allocated by the mood_parser.
- *
- * Function to free the resources allocated in \ref mood_method::parser. The
- * argument is a pointer to mood method specific data returned by ->parser().
- */
-typedef void mood_cleanup_function(void *);
-
-/**
- * Used for scoring and to determine whether a file is admissible.
- */
-struct mood_method {
-       /** The name of the method. */
-       const char *name;
-       /** Pointer to the mood parser. */
-       mood_parser *parser;
-       /** Pointer to the score function */
-       mood_score_function *score_function;
-       /** Optional cleanup function. */
-       mood_cleanup_function *cleanup;
-};
-
-/** The array of available mood methods. */
-extern const struct mood_method mood_methods[];
diff --git a/mood.c b/mood.c
index 5268e77fe03c7e56d1b146935f683e30dea9a2f6..d47c54efa360b856c63b660fd4cdb5b81d442b68 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -12,7 +12,6 @@
 #include "afh.h"
 #include "afs.h"
 #include "list.h"
-#include "mm.h"
 #include "mood.h"
 
 /*
@@ -39,47 +38,21 @@ struct afs_statistics {
        int64_t num_played_qd;
        /** Quadratic deviation of last played time. */
        int64_t last_played_qd;
+       /** Correction factor for the num played score. */
+       int64_t num_played_correction;
+       /** Correction factor for the last played score. */
+       int64_t last_played_correction;
+       /** Common divisor of the correction factors. */
+       int64_t normalization_divisor;
        /** Number of admissible files */
        unsigned num;
 };
-static struct afs_statistics statistics;
+static struct afs_statistics statistics = {.normalization_divisor = 1};
 
-/**
- * Each line of the current mood corresponds to a mood_item.
- */
-struct mood_item {
-       /** The method this line is referring to. */
-       const struct mood_method *method;
-       /** The data structure computed by the mood parser. */
-       void *parser_data;
-       /** The given score value, or zero if none was given. */
-       int32_t score_arg;
-       /** Non-zero if random scoring was requested. */
-       int random_score;
-       /** Whether the "not" keyword was given in the mood line. */
-       int logical_not;
-       /** The position in the list of items. */
-       struct list_head mood_item_node;
-};
-
-/*
- * Created from the mood definition by \ref change_current_mood().
- *
- * When a mood is opened, each line of its definition is investigated, and a
- * corresponding mood item is produced. Each mood line starts with accept,
- * deny, or score which determines the type of the mood line. For each such
- * type a linked list is maintained whose entries are the mood items.
- */
 struct mood {
        /** The name of this mood. */
        char *name;
-       /** The list of mood items of type \p accept. */
-       struct list_head accept_list;
-       /** The list of mood items of type \p deny. */
-       struct list_head deny_list;
-       /** The list of mood items of type \p score. */
-       struct list_head score_list;
-       /* Only used for version 2 moods. */
+       /** Info for the bison parser. */
        struct mp_context *parser_context;
 };
 
@@ -146,277 +119,38 @@ __a_const static uint64_t int_sqrt(uint64_t x)
        return res;
 }
 
-/*
- * Returns true if row matches, false if it does not match. In any case score
- * and score_arg_sum are set/increased accordingly.
- */
-static bool get_item_score(struct mood_item *item, const struct afs_info *afsi,
-               const struct afh_info *afhi, const char *path, long *score,
-               long *score_arg_sum)
-{
-       int ret;
-       bool match = true;
-
-       *score_arg_sum += item->random_score? 100 : PARA_ABS(item->score_arg);
-       ret = 100;
-       if (item->method) {
-               ret = item->method->score_function(path, afsi, afhi,
-                       item->parser_data);
-               if ((ret < 0 && !item->logical_not) || (ret >= 0 && item->logical_not))
-                       match = false;
-       }
-       if (item->random_score)
-               *score = PARA_ABS(ret) * para_random(100);
-       else
-               *score = PARA_ABS(ret) * item->score_arg;
-       return match;
-}
-
 /* returns 1 if row admissible, 0 if not, negative on errors */
-static int row_is_admissible(const struct osl_row *aft_row, struct mood *m,
-               long *scorep)
+static int row_is_admissible(const struct osl_row *aft_row, struct mood *m)
 {
-       struct mood_item *item;
-       int ret;
-       bool match;
-       long score_arg_sum = 0, score = 0, item_score;
-       struct afs_info afsi;
-       struct afh_info afhi;
-       char *path;
-
        if (!m)
                return -E_NO_MOOD;
-       if (m->parser_context) {
-               *scorep = 0;
-               return mp_eval_row(aft_row, m->parser_context);
-       }
-       ret = get_afsi_of_row(aft_row, &afsi);
-       if (ret < 0)
-               return ret;
-       ret = get_afhi_of_row(aft_row, &afhi);
-       if (ret < 0)
-               return ret;
-       ret = get_audio_file_path_of_row(aft_row, &path);
-       if (ret < 0)
-               return ret;
-       /* reject audio file if it matches any entry in the deny list */
-       list_for_each_entry(item, &m->deny_list, mood_item_node) {
-               match = get_item_score(item, &afsi, &afhi, path, &item_score,
-                       &score_arg_sum);
-               if (match) /* not admissible */
-                       return 0;
-               score += item_score;
-       }
-       match = false;
-       list_for_each_entry(item, &m->accept_list, mood_item_node) {
-               ret = get_item_score(item, &afsi, &afhi, path, &item_score,
-                       &score_arg_sum);
-               if (ret == 0)
-                       continue;
-               match = true;
-               score += item_score;
-       }
-       /* reject if there is no matching entry in the accept list */
-       if (!match && !list_empty(&m->accept_list))
-               return 0;
-       list_for_each_entry(item, &m->score_list, mood_item_node) {
-               match = get_item_score(item, &afsi, &afhi, path, &item_score,
-                       &score_arg_sum);
-               if (match)
-                       score += item_score;
-       }
-       if (score_arg_sum)
-               score /= score_arg_sum;
-       *scorep = score;
-       return 1;
-}
-
-static void cleanup_list_entry(struct mood_item *item)
-{
-       if (item->method && item->method->cleanup)
-               item->method->cleanup(item->parser_data);
-       else
-               free(item->parser_data);
-       list_del(&item->mood_item_node);
-       free(item);
+       return mp_eval_row(aft_row, m->parser_context);
 }
 
 static void destroy_mood(struct mood *m)
 {
-       struct mood_item *tmp, *item;
-
        if (!m)
                return;
-       list_for_each_entry_safe(item, tmp, &m->accept_list, mood_item_node)
-               cleanup_list_entry(item);
-       list_for_each_entry_safe(item, tmp, &m->deny_list, mood_item_node)
-               cleanup_list_entry(item);
-       list_for_each_entry_safe(item, tmp, &m->score_list, mood_item_node)
-               cleanup_list_entry(item);
-       free(m->name);
        mp_shutdown(m->parser_context);
+       free(m->name);
        free(m);
 }
 
 static struct mood *alloc_new_mood(const char *name)
 {
-       struct mood *m = para_calloc(sizeof(struct mood));
+       struct mood *m = zalloc(sizeof(struct mood));
        if (name)
                m->name = para_strdup(name);
-       INIT_LIST_HEAD(&m->accept_list);
-       INIT_LIST_HEAD(&m->deny_list);
-       INIT_LIST_HEAD(&m->score_list);
        return m;
 }
 
-/** The different types of a mood line. */
-enum mood_line_type {
-       /** Invalid. */
-       ML_INVALID,
-       /** Accept line. */
-       ML_ACCEPT,
-       /** Deny line. */
-       ML_DENY,
-       /** Score line. */
-       ML_SCORE
-};
-
-/** Data passed to the parser of a mood line. */
-struct mood_line_parser_data {
-       /** The mood this mood line belongs to. */
-       struct mood *m;
-       /** The line number in the mood definition. */
-       unsigned line_num;
-};
-
-/*
- * <accept [with score <score>] | deny [with score <score>]  | score <score>>
- *     [if] [not] <mood_method> [options]
- * <score> is either an integer or "random" which assigns a random score to
- * all matching files
- */
-static int parse_mood_line(char *mood_line, void *data)
-{
-       struct mood_line_parser_data *mlpd = data;
-       char **argv;
-       unsigned num_words;
-       char **w;
-       int i, ret;
-       enum mood_line_type mlt = ML_INVALID;
-       struct mood_item *mi = NULL;
-
-       mlpd->line_num++;
-       ret = create_argv(mood_line, " \t", &argv);
-       if (ret < 0)
-               return ret;
-       num_words = ret;
-       if (!num_words) /* empty line */
-               goto out;
-       w = argv;
-       if (**w == '#') /* comment */
-               goto out;
-       if (!strcmp(*w, "accept"))
-               mlt = ML_ACCEPT;
-       else if (!strcmp(*w, "deny"))
-               mlt = ML_DENY;
-       else if (!strcmp(*w, "score"))
-               mlt = ML_SCORE;
-       ret = -E_MOOD_SYNTAX;
-       if (mlt == ML_INVALID)
-               goto out;
-       mi = para_calloc(sizeof(struct mood_item));
-       if (mlt != ML_SCORE) {
-               ret = -E_MOOD_SYNTAX;
-               w++;
-               if (!*w)
-                       goto out;
-               if (strcmp(*w, "with"))
-                       goto check_for_if;
-               w++;
-               if (!*w)
-                       goto out;
-               if (strcmp(*w, "score"))
-                       goto out;
-       }
-       if (mlt == ML_SCORE || !strcmp(*w, "score")) {
-               ret = -E_MOOD_SYNTAX;
-               w++;
-               if (!*w)
-                       goto out;
-               if (strcmp(*w, "random")) {
-                       mi->random_score = 0;
-                       ret = para_atoi32(*w, &mi->score_arg);
-                       if (ret < 0)
-                               goto out;
-               } else {
-                       mi->random_score = 1;
-                       if (!*(w + 1))
-                       goto success; /* the line "score random" is valid */
-               }
-       } else
-               mi->score_arg = 0;
-       ret = -E_MOOD_SYNTAX;
-       w++;
-       if (!*w)
-               goto out;
-check_for_if:
-       if (!strcmp(*w, "if")) {
-               ret = -E_MOOD_SYNTAX;
-               w++;
-               if (!*w)
-                       goto out;
-       }
-       if (!strcmp(*w, "not")) {
-               ret = -E_MOOD_SYNTAX;
-               w++;
-               if (!*w)
-                       goto out;
-               mi->logical_not = 1;
-       } else
-               mi->logical_not = 0;
-       for (i = 0; mood_methods[i].parser; i++) {
-               if (strcmp(*w, mood_methods[i].name))
-                       continue;
-               break;
-       }
-       ret = -E_MOOD_SYNTAX;
-       if (!mood_methods[i].parser)
-               goto out;
-       ret = mood_methods[i].parser(num_words - 1 - (w - argv), w,
-               &mi->parser_data);
-       if (ret < 0)
-               goto out;
-       mi->method = &mood_methods[i];
-success:
-       if (mlpd->m) {
-               if (mlt == ML_ACCEPT)
-                       para_list_add(&mi->mood_item_node, &mlpd->m->accept_list);
-               else if (mlt == ML_DENY)
-                       para_list_add(&mi->mood_item_node, &mlpd->m->deny_list);
-               else
-                       para_list_add(&mi->mood_item_node, &mlpd->m->score_list);
-       }
-       PARA_DEBUG_LOG("%s entry added, method: %p\n", mlt == ML_ACCEPT? "accept" :
-               (mlt == ML_DENY? "deny" : "score"), mi->method);
-       ret = 1;
-out:
-       free_argv(argv);
-       if (mi && (ret < 0 || !mlpd->m)) { /* mi was not added to any list */
-               free(mi->parser_data);
-               free(mi);
-       }
-       return ret;
-}
-
 static int load_mood(const struct osl_row *mood_row, struct mood **m,
                char **errmsg)
 {
        char *mood_name;
        struct osl_object mood_def;
-       struct mood_line_parser_data mlpd = {.line_num = 0};
        int ret;
 
-       *m = NULL;
        ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
        if (ret < 0) {
                if (errmsg)
@@ -425,33 +159,21 @@ static int load_mood(const struct osl_row *mood_row, struct mood **m,
                return ret;
        }
        assert(*mood_name);
-       mlpd.m = alloc_new_mood(mood_name);
-       ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
-               parse_mood_line, &mlpd);
-       if (ret < 0) {
-               PARA_INFO_LOG("opening version 2 mood %s\n", mlpd.m->name);
-               ret = mp_init(mood_def.data, mood_def.size, &mlpd.m->parser_context,
-                       errmsg);
-               if (ret < 0)
-                       destroy_mood(mlpd.m);
-       } else {
-               PARA_WARNING_LOG("loaded version 1 mood %s\n", mlpd.m->name);
-               PARA_WARNING_LOG("please convert to version 2\n");
-               ret = 1;
-       }
+       *m = alloc_new_mood(mood_name);
+       PARA_INFO_LOG("opening mood %s\n", mood_name);
+       ret = mp_init(mood_def.data, mood_def.size, &(*m)->parser_context, errmsg);
        osl_close_disk_object(&mood_def);
-       if (ret >= 0)
-               *m = mlpd.m;
+       if (ret < 0)
+               destroy_mood(*m);
        return ret;
 }
 
 static int check_mood(struct osl_row *mood_row, void *data)
 {
        struct para_buffer *pb = data;
-       char *mood_name;
+       char *mood_name, *errmsg;
        struct osl_object mood_def;
-       struct mood_line_parser_data mlpd = {.line_num = 0};
-
+       struct mood *m;
        int ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
 
        if (ret < 0) {
@@ -460,24 +182,15 @@ static int check_mood(struct osl_row *mood_row, void *data)
        }
        if (!*mood_name) /* ignore dummy row */
                goto out;
-       ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
-               parse_mood_line, &mlpd);
+       m = alloc_new_mood("check");
+       ret = mp_init(mood_def.data, mood_def.size, &m->parser_context,
+               &errmsg);
        if (ret < 0) {
-               char *errmsg;
-               struct mood *m = alloc_new_mood("check");
-               ret = mp_init(mood_def.data, mood_def.size, &m->parser_context,
-                       &errmsg);
-               if (ret < 0) {
-                       para_printf(pb, "%s: %s\n", mood_name, errmsg);
-                       free(errmsg);
-                       para_printf(pb, "%s\n", para_strerror(-ret));
-               } else
-                       destroy_mood(m);
-       } else {
-               para_printf(pb, "%s: v1 mood, please convert to v2\n",
-                       mood_name);
-
-       }
+               para_printf(pb, "%s: %s\n", mood_name, errmsg);
+               free(errmsg);
+               para_printf(pb, "%s\n", para_strerror(-ret));
+       } else
+               destroy_mood(m);
        ret = 1; /* don't fail the loop on invalid mood definitions */
 out:
        osl_close_disk_object(&mood_def);
@@ -499,20 +212,59 @@ int mood_check_callback(struct afs_callback_arg *aca)
                check_mood));
 }
 
-static int64_t normalized_value(int64_t x, int64_t n, int64_t sum, int64_t qd)
-{
-       if (!n || !qd)
-               return 0;
-       return 100 * (n * x - sum) / (int64_t)int_sqrt(n) / (int64_t)int_sqrt(qd);
-}
-
-static long compute_score(struct afs_info *afsi, long mood_score)
+/*
+ * The normalized num_played and last_played values are defined as
+ *
+ *     nn := -(np - mean_n) / sigma_n and nl := -(lp - mean_l) / sigma_l
+ *
+ *  For a (hypothetical) file with np = 0 and lp = now we thus have
+ *
+ *     nn =  mean_n / sigma_n =: hn > 0
+ *     nl = -(now - mean_l) / sigma_l =: hl < 0
+ *
+ * We design the score function so that both contributions get the same
+ * weight. Define the np and lp score of an arbitrary file as
+ *
+ *     sn := nn * -hl and sl := nl * hn
+ *
+ * Example:
+ *     num_played mean/sigma: 87/14
+ *     last_played mean/sigma: 45/32 days
+ *
+ *     We have hn = 87 / 14 = 6.21 and hl = -45 / 32 = -1.41. Multiplying
+ *     nn of every file with the correction factor 1.41 and nl with
+ *     6.21 makes the weight of the two contributions equal.
+ *
+ * The total score s := sn + sl has the representation
+ *
+ *     s = -cn * (np - mean_n) - cl * (lp - mean_l)
+ *
+ * with positive correction factors
+ *
+ *     cn = (now - mean_l) / (sqrt(ql) * sqrt(qn) / n)
+ *     cl = mean_n / (sqrt(ql) * sqrt(qn) / n)
+ *
+ * where ql and qn are the quadratic deviations stored in the statistics
+ * structure and n is the number of admissible files. To avoid integer
+ * overflows and rounding errors we store the common divisor of the
+ * correction factors separately.
+ */
+static long compute_score(struct afs_info *afsi)
 {
-       mood_score -= normalized_value(afsi->num_played, statistics.num,
-               statistics.num_played_sum, statistics.num_played_qd);
-       mood_score -= normalized_value(afsi->last_played, statistics.num,
-               statistics.last_played_sum, statistics.last_played_qd);
-       return mood_score / 3;
+       int64_t mean_n, mean_l,score_n, score_l;
+
+       assert(statistics.normalization_divisor > 0);
+       assert(statistics.num > 0);
+       mean_n = statistics.num_played_sum / statistics.num;
+       mean_l = statistics.last_played_sum / statistics.num;
+
+       score_n = -((int64_t)afsi->num_played - mean_n)
+               * statistics.num_played_correction
+               / statistics.normalization_divisor;
+       score_l = -((int64_t)afsi->last_played - mean_l)
+               * statistics.last_played_correction
+               / statistics.normalization_divisor;
+       return (score_n + score_l) / 2;
 }
 
 static int add_afs_statistics(const struct osl_row *row)
@@ -556,6 +308,7 @@ static int del_afs_statistics(const struct osl_row *row)
        assert(n);
        if (n == 1) {
                memset(&statistics, 0, sizeof(statistics));
+               statistics.normalization_divisor = 1;
                return 1;
        }
 
@@ -581,63 +334,42 @@ static int del_afs_statistics(const struct osl_row *row)
 
 /*
  * At mood open time we determine the set of admissible files for the given
- * mood. The mood score of each admissible file is computed by adding up all
- * mood item scores. Next, we update the afs statistics and append a struct
- * admissible_file_info to a temporary array.
- *
- * When all files have been processed in this way, the final score of each
- * admissible file is computed by adding the dynamic score (which depends on
- * the afs_statistics and the current time) to the mood score. Finally, all
- * audio files in the temporary array are added to the score table and the
- * array is freed.
+ * mood where each file is identified by a pointer to a row of the audio file
+ * table. In the first pass the pointers are added to a temporary array and
+ * statistics are computed. When all admissible files have been processed in
+ * this way, the score of each admissible file is computed and the (row, score)
+ * pair is added to the score table. This has to be done in a second pass
+ * since the score depends on the statistics. Finally, the array is freed.
  */
-struct admissible_file_info
-{
-       /** The admissible audio file. */
-       struct osl_row *aft_row;
-       /** Its score. */
-       long score;
-};
-
-/** The temporary array of admissible files. */
 struct admissible_array {
        /** Files are admissible wrt. this mood. */
        struct mood *m;
        /** The size of the array */
        unsigned size;
        /** Pointer to the array of admissible files. */
-       struct admissible_file_info *array;
+       struct osl_row **array;
 };
 
-/**
- * Add an entry to the array of admissible files.
- *
- * \param aft_row The audio file to be added.
- * \param private_data Pointer to a struct admissible_file_info.
- *
- * \return 1 if row admissible, 0 if not, negative on errors.
+/*
+ * Check whether the given audio file is admissible. If it is, add it to array
+ * of admissible files.
  */
 static int add_if_admissible(struct osl_row *aft_row, void *data)
 {
        struct admissible_array *aa = data;
        int ret;
-       long score = 0;
 
-       ret = row_is_admissible(aft_row, aa->m, &score);
+       ret = row_is_admissible(aft_row, aa->m);
        if (ret <= 0)
                return ret;
        if (statistics.num >= aa->size) {
                aa->size *= 2;
                aa->size += 100;
-               aa->array = para_realloc(aa->array,
-                       aa->size * sizeof(struct admissible_file_info));
+               aa->array = arr_realloc(aa->array, aa->size,
+                       sizeof(struct osl_row *));
        }
-       aa->array[statistics.num].aft_row = aft_row;
-       aa->array[statistics.num].score = score;
-       ret = add_afs_statistics(aft_row);
-       if (ret < 0)
-               return ret;
-       return 1;
+       aa->array[statistics.num] = aft_row;
+       return add_afs_statistics(aft_row);
 }
 
 /**
@@ -704,7 +436,7 @@ static int update_afs_statistics(struct afs_info *old_afsi,
        return 1;
 }
 
-static int add_to_score_table(const struct osl_row *aft_row, long mood_score)
+static int add_to_score_table(const struct osl_row *aft_row)
 {
        long score;
        struct afs_info afsi;
@@ -712,7 +444,7 @@ static int add_to_score_table(const struct osl_row *aft_row, long mood_score)
 
        if (ret < 0)
                return ret;
-       score = compute_score(&afsi, mood_score);
+       score = compute_score(&afsi);
        return score_add(aft_row, score);
 }
 
@@ -770,7 +502,7 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
        if (ret < 0)
                return ret;
        was_admissible = ret;
-       ret = row_is_admissible(aft_row, current_mood, &score);
+       ret = row_is_admissible(aft_row, current_mood);
        if (ret < 0)
                return ret;
        is_admissible = (ret > 0);
@@ -782,7 +514,7 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
                ret = add_afs_statistics(aft_row);
                if (ret < 0)
                        return ret;
-               return add_to_score_table(aft_row, score);
+               return add_to_score_table(aft_row);
        }
        /* update score */
        ret = get_afsi_of_row(aft_row, &afsi);
@@ -793,7 +525,7 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
                if (ret < 0)
                        return ret;
        }
-       score = compute_score(&afsi, score);
+       score = compute_score(&afsi);
        PARA_DEBUG_LOG("score: %li\n", score);
        percent = (score + 100) / 3;
        if (percent > 100)
@@ -804,15 +536,11 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
        return score_update(aft_row, percent);
 }
 
-static void log_statistics(void)
+/* sse: seconds since epoch. */
+static void log_statistics(int64_t sse)
 {
        unsigned n = statistics.num;
        int mean_days, sigma_days;
-       /*
-        * We can not use the "now" pointer from sched.c here because we are
-        * called before schedule(), which initializes "now".
-        */
-       struct timeval rnow;
 
        assert(current_mood);
        PARA_NOTICE_LOG("loaded mood %s\n", current_mood->name?
@@ -822,13 +550,18 @@ static void log_statistics(void)
                return;
        }
        PARA_NOTICE_LOG("%u admissible files\n", statistics.num);
-       clock_get_realtime(&rnow);
-       mean_days = (rnow.tv_sec - statistics.last_played_sum / n) / 3600 / 24;
+       mean_days = (sse - statistics.last_played_sum / n) / 3600 / 24;
        sigma_days = int_sqrt(statistics.last_played_qd / n) / 3600 / 24;
        PARA_NOTICE_LOG("last_played mean/sigma: %d/%d days\n", mean_days, sigma_days);
-       PARA_NOTICE_LOG("num_played mean/sigma: %llu/%llu\n",
-               (long long unsigned)statistics.num_played_sum / n,
-               (long long unsigned)int_sqrt(statistics.num_played_qd / n));
+       PARA_NOTICE_LOG("num_played mean/sigma: %" PRId64 "/%" PRIu64 "\n",
+               statistics.num_played_sum / n,
+               int_sqrt(statistics.num_played_qd / n));
+       PARA_NOTICE_LOG("num_played correction factor: %" PRId64 "\n",
+               statistics.num_played_correction);
+       PARA_NOTICE_LOG("last_played correction factor: %" PRId64 "\n",
+               statistics.last_played_correction);
+       PARA_NOTICE_LOG("normalization divisor: %" PRId64 "\n",
+               statistics.normalization_divisor);
 }
 
 /**
@@ -841,6 +574,25 @@ void close_current_mood(void)
        destroy_mood(current_mood);
        current_mood = NULL;
        memset(&statistics, 0, sizeof(statistics));
+       statistics.normalization_divisor = 1;
+}
+
+static void compute_correction_factors(int64_t sse)
+{
+       struct afs_statistics *s = &statistics;
+
+       if (s->num > 0) {
+               s->normalization_divisor = int_sqrt(s->last_played_qd)
+                       * int_sqrt(s->num_played_qd) / s->num / 100;
+               s->num_played_correction = sse - s->last_played_sum / s->num;
+               s->last_played_correction = s->num_played_sum / s->num;
+       }
+       if (s->num_played_correction == 0)
+               s->num_played_correction = 1;
+       if (s->normalization_divisor == 0)
+               s->normalization_divisor = 1;
+       if (s->last_played_correction == 0)
+               s->last_played_correction = 1;
 }
 
 /**
@@ -868,6 +620,11 @@ int change_current_mood(const char *mood_name, char **errmsg)
                .size = 0,
                .array = NULL
        };
+       /*
+        * We can not use the "now" pointer from sched.c here because we are
+        * called before schedule(), which initializes "now".
+        */
+       struct timeval rnow;
 
        if (mood_name) {
                struct mood *m;
@@ -904,9 +661,11 @@ int change_current_mood(const char *mood_name, char **errmsg)
                        *errmsg = make_message("audio file loop failed");
                goto out;
        }
+       clock_get_realtime(&rnow);
+       compute_correction_factors(rnow.tv_sec);
+       log_statistics(rnow.tv_sec);
        for (i = 0; i < statistics.num; i++) {
-               struct admissible_file_info *a = aa.array + i;
-               ret = add_to_score_table(a->aft_row, a->score);
+               ret = add_to_score_table(aa.array[i]);
                if (ret < 0) {
                        if (errmsg)
                                *errmsg = make_message(
@@ -914,7 +673,6 @@ int change_current_mood(const char *mood_name, char **errmsg)
                        goto out;
                }
        }
-       log_statistics();
        ret = statistics.num;
 out:
        free(aa.array);
@@ -959,9 +717,17 @@ static int reload_current_mood(void)
  * \param pb Unused.
  * \param data Its type depends on the event.
  *
- * This function performs actions required due to the occurrence of the given
- * event. Possible actions include reload of the current mood and update of the
- * score of an audio file.
+ * This function updates the score table according to the event that has
+ * occurred. Two actions are possible: (a) reload the current mood, or (b)
+ * add/remove/update the row of the score table which corresponds to the audio
+ * file that has been modified or whose afs info has been changed. It depends
+ * on the type of the event which action (if any) is performed.
+ *
+ * The callbacks of command handlers such as com_add() or com_touch() which
+ * modify the audio file table call this function. The virtual streaming system
+ * also calls this after it has updated the afs info of the file it is about to
+ * stream (the one with the highest score). If the file stays admissible, its
+ * score is recomputed so that a different file is picked next time.
  *
  * \return Standard.
  */
diff --git a/mp.c b/mp.c
index aea767d99fea4b7bf0954504a03602766831a3b9..fb5a0c07472c46bb7d5ca42fcb4061d54755f96b 100644 (file)
--- a/mp.c
+++ b/mp.c
@@ -90,7 +90,7 @@ unsigned parse_quoted_string(const char *src, const char quote_chars[2],
 
        assert(len >= 2);
        assert(src[0] == quote_chars[0]);
-       p = dst = para_malloc(len - 1);
+       p = dst = alloc(len - 1);
        backslash = false;
        for (n = 1;; n++) {
                char c;
@@ -389,6 +389,27 @@ MP_AFHI(frequency)
 MP_AFHI(channels)
 /** \endcond */
 
+/**
+ * Return the duration of the audio file from the afh info structure.
+ *
+ * \param ctx See \ref mp_path().
+ *
+ * The duration is computed by multiplying the number of chunks and the
+ * duration of one chunk.
+ *
+ * \return The approximate number of milliseconds.
+ */
+int64_t mp_duration(struct mp_context *ctx)
+{
+       struct timeval tmp;
+       int ret = get_afhi(ctx);
+
+       if (ret < 0)
+               return 0;
+       tv_scale(ctx->afhi.chunks_total, &ctx->afhi.chunk_tv, &tmp);
+       return tv2ms(&tmp);
+}
+
 /**
  * Define a function which extracts and returns the value of a meta tag.
  *
@@ -497,7 +518,7 @@ int mp_init(const char *definition, int nbytes, struct mp_context **result,
                        *errmsg = NULL;
                return 0;
        }
-       ctx = para_calloc(sizeof(*ctx));
+       ctx = zalloc(sizeof(*ctx));
        ctx->errmsg = NULL;
        ctx->ast = NULL;
 
diff --git a/mp.h b/mp.h
index febbe3240f49ba81b08ca2168b10dd1189fc3f54..891bfb054e2f4cf1a922e7434a8467808f2e474b 100644 (file)
--- a/mp.h
+++ b/mp.h
@@ -139,6 +139,7 @@ bool mp_is_set(const char *attr, struct mp_context *ctx);
 char *mp_path(struct mp_context *ctx);
 int64_t mp_year(struct mp_context *ctx);
 int64_t mp_num_attributes_set(struct mp_context *ctx);
+int64_t mp_duration(struct mp_context *ctx);
 
 /* Generated with MP_AFSI() */
 /** \cond MP_AFSI */
index 728b25b81f94aa177a1e74ae01fca07419f7fe90..d7493ac01e28bba581b7112a41a37e84426be11a 100644 (file)
--- a/mp3_afh.c
+++ b/mp3_afh.c
@@ -348,7 +348,7 @@ static int mp3_rewrite_tags(const char *map, size_t mapsize,
        if (ret < 0)
                goto out;
        new_v2size = id3_tag_render(v2_tag, NULL);
-       v2_buffer = para_malloc(new_v2size);
+       v2_buffer = alloc(new_v2size);
        id3_tag_render(v2_tag, v2_buffer);
        PARA_INFO_LOG("writing v2 tag (%lu bytes)\n", new_v2size);
        ret = write_all(fd, (char *)v2_buffer, new_v2size);
@@ -598,7 +598,7 @@ static int mp3_read_info(unsigned char *map, size_t numbytes, int fd,
        const char *tag_versions[] = {"no", "id3v1", "id3v2", "id3v1+id3v2"};
 
        afhi->chunks_total = 0;
-       afhi->chunk_table = para_malloc(chunk_table_size * sizeof(uint32_t));
+       afhi->chunk_table = arr_alloc(chunk_table_size, sizeof(uint32_t));
        while (1) {
                int freq, br;
                struct timeval tmp, cct; /* current chunk time */
@@ -633,8 +633,8 @@ static int mp3_read_info(unsigned char *map, size_t numbytes, int fd,
                total_time = tmp;
                if (afhi->chunks_total >= chunk_table_size) {
                        chunk_table_size *= 2;
-                       afhi->chunk_table = para_realloc(afhi->chunk_table,
-                               chunk_table_size * sizeof(uint32_t));
+                       afhi->chunk_table = arr_realloc(afhi->chunk_table,
+                               chunk_table_size, sizeof(uint32_t));
                }
                afhi->chunk_table[afhi->chunks_total] = fpos;
                afhi->chunks_total++;
index ccb1553b820357e13f2cafe2c0e8a7e0502ac8fd..bc8ccdaa99be724530372a03b71544abeece480b 100644 (file)
@@ -73,7 +73,7 @@ static void mp3dec_close(struct filter_node *fn)
 
 #define MP3DEC_MAX_FRAME 8192
 
-static int mp3dec_post_select(__a_unused struct sched *s, void *context)
+static int mp3dec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        int i, ret;
@@ -93,7 +93,7 @@ next_buffer:
        btr_merge(btrn, fn->min_iqs);
        len = btr_next_buffer(btrn, &inbuffer);
        /*
-        * Decode at most 8K in one go to give the post_select() functions of
+        * Decode at most 8K in one go to give the post_monitor() functions of
         * other buffer tree nodes a chance to run. This is necessary to avoid
         * buffer underruns on slow machines.
         */
@@ -144,7 +144,7 @@ decode:
        }
        fn->min_iqs = 0;
        mad_synth_frame(&pmd->synth, &pmd->frame);
-       outbuffer = para_malloc(pmd->synth.pcm.length * 2 * pmd->channels);
+       outbuffer = arr_alloc(pmd->synth.pcm.length, 2 * pmd->channels);
        loaded = 0;
        for (i = 0; i < pmd->synth.pcm.length; i++) {
                int sample = MAD_TO_SHORT(pmd->synth.pcm.samples[0][i]);
@@ -166,7 +166,7 @@ err:
 
 static void mp3dec_open(struct filter_node *fn)
 {
-       struct private_mp3dec_data *pmd = para_calloc(sizeof(*pmd));
+       struct private_mp3dec_data *pmd = zalloc(sizeof(*pmd));
 
        fn->private_data = pmd;
        mad_stream_init(&pmd->stream);
@@ -187,7 +187,7 @@ static int mp3dec_execute(struct btr_node *btrn, const char *cmd, char **result)
 const struct filter lsg_filter_cmd_com_mp3dec_user_data = {
        .open = mp3dec_open,
        .close = mp3dec_close,
-       .pre_select = generic_filter_pre_select,
-       .post_select = mp3dec_post_select,
+       .pre_monitor = generic_filter_pre_monitor,
+       .post_monitor = mp3dec_post_monitor,
        .execute = mp3dec_execute,
 };
diff --git a/mp4.c b/mp4.c
new file mode 100644 (file)
index 0000000..f8515ca
--- /dev/null
+++ b/mp4.c
@@ -0,0 +1,1053 @@
+/*
+ * Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com
+ * FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
+ *
+ * See file COPYING.
+ */
+
+/** \file mp4.c Paraslash's internal mp4 parser. */
+
+/*
+ * This is a stripped down version of the former mp4ff library which used to be
+ * part of the faad decoder project but was removed from the faad code base in
+ * 2017. The original code has been cleaned up substantially and the public API
+ * has been documented. See the git commit log for details.
+ */
+
+#include <regex.h>
+
+#include "para.h"
+#include "error.h"
+#include "portable_io.h"
+#include "string.h"
+#include "mp4.h"
+
+/**
+ * The three states of the mp4 parser. The parser only loads the audio specific
+ * values and tables when it is in the second state.
+ */
+enum audio_track_state {
+       /** We haven't encountered an mp4a atom so far. */
+       ATS_INITIAL,
+       /** We have seen an mp4a atom but no subsequent trak atom yet. */
+       ATS_SEEN_MP4A,
+       /** A trak atom was seen *after* the mp4a atom. */
+       ATS_TRACK_CHANGE,
+};
+
+struct mp4_track {
+       /* determines which atoms we still need to parse. */
+       enum audio_track_state state;
+
+       /* mp4a */
+       uint16_t channel_count;
+       uint16_t sample_rate;
+
+       /* stsz */
+       uint32_t stsz_sample_size;
+       uint32_t stsz_sample_count;
+       uint32_t *stsz_table;
+
+       /* stts */
+       uint32_t stts_entry_count;
+       uint32_t *stts_sample_count;
+
+       /* stsc */
+       uint32_t stsc_entry_count;
+       uint32_t *stsc_first_chunk;
+       uint32_t *stsc_samples_per_chunk;
+
+       /* stsc */
+       uint32_t stco_entry_count;
+       uint32_t *stco_chunk_offset;
+
+       /* mdhd */
+       uint32_t time_scale;
+       uint64_t duration;
+};
+
+struct mp4 {
+       const struct mp4_callback *cb;
+
+       uint64_t moov_offset;
+       uint64_t moov_size;
+       uint64_t meta_offset;
+       uint32_t meta_size;
+       uint64_t ilst_offset;
+       uint32_t ilst_size;
+       uint64_t udta_offset;
+       uint32_t udta_size;
+
+       uint8_t last_atom;
+       struct mp4_track track;
+       struct mp4_metadata meta;
+};
+
+/*
+ * Returns -E_MP4_READ, 0, or 1 on errors/EOF/success. Partial reads followed
+ * by EOF or read errors are treated as errors.
+ */
+static int read_data(struct mp4 *f, void *data, size_t size)
+{
+       while (size > 0) {
+               ssize_t ret = f->cb->read(f->cb->user_data, data, size);
+               if (ret < 0 && errno == EINTR)
+                       continue;
+               /* regard EAGAIN as an error as reads should be blocking. */
+               if (ret <= 0)
+                       return ret < 0? -E_MP4_READ : 0;
+               size -= ret;
+       }
+       return 1;
+}
+
+static int read_int64(struct mp4 *f, uint64_t *result)
+{
+       uint8_t data[8];
+       int ret = read_data(f, data, 8);
+
+       if (ret > 0)
+               *result = read_u64_be(data);
+       return ret;
+}
+
+static int read_int32(struct mp4 *f, uint32_t *result)
+{
+       uint8_t data[4];
+       int ret = read_data(f, data, 4);
+
+       if (ret > 0)
+               *result = read_u32_be(data);
+       return ret;
+}
+
+static int read_int16(struct mp4 *f, uint16_t *result)
+{
+       uint8_t data[2];
+       int ret = read_data(f, data, 2);
+
+       if (ret > 0)
+               *result = read_u16_be(data);
+       return ret;
+}
+
+/** A macro defining the atoms we care about. It gets expanded twice. */
+#define ATOM_ITEMS \
+       ATOM_ITEM(MOOV, 'm', 'o', 'o', 'v') /* movie (top-level container) */ \
+       ATOM_ITEM(TRAK, 't', 'r', 'a', 'k') /* container for a single track */ \
+       ATOM_ITEM(MDIA, 'm', 'd', 'i', 'a') /* media information */ \
+       ATOM_ITEM(MINF, 'm', 'i', 'n', 'f') /* extends mdia */ \
+       ATOM_ITEM(STBL, 's', 't', 'b', 'l') /* sample table container */ \
+       ATOM_ITEM(UDTA, 'u', 'd', 't', 'a') /* user data */ \
+       ATOM_ITEM(ILST, 'i', 'l', 's', 't') /* iTunes Metadata list */ \
+       ATOM_ITEM(ARTIST, 0xa9, 'A', 'R', 'T') /* artist */ \
+       ATOM_ITEM(TITLE, 0xa9, 'n', 'a', 'm') /* title */ \
+       ATOM_ITEM(ALBUM, 0xa9, 'a', 'l', 'b') /* album */ \
+       ATOM_ITEM(DATE, 0xa9, 'd', 'a', 'y') /* date */ \
+       ATOM_ITEM(COMMENT, 0xa9, 'c', 'm', 't') /* comment */ \
+       ATOM_ITEM(MDHD, 'm', 'd', 'h', 'd') /* track header */ \
+       ATOM_ITEM(STSD, 's', 't', 's', 'd') /* sample description box */ \
+       ATOM_ITEM(STTS, 's', 't', 't', 's') /* time to sample box */ \
+       ATOM_ITEM(STSZ, 's', 't', 's', 'z') /* sample size box */ \
+       ATOM_ITEM(STCO, 's', 't', 'c', 'o') /* chunk offset box */ \
+       ATOM_ITEM(STSC, 's', 't', 's', 'c') /* sample to chunk box */ \
+       ATOM_ITEM(MP4A, 'm', 'p', '4', 'a') /* mp4 audio */ \
+       ATOM_ITEM(META, 'm', 'e', 't', 'a') /* iTunes Metadata box */ \
+       ATOM_ITEM(DATA, 'd', 'a', 't', 'a') /* iTunes Metadata data box */ \
+
+/** For the C enumeration we concatenate ATOM_ with the first argument. */
+#define ATOM_ITEM(_name, a, b, c, d) ATOM_ ## _name,
+/** The enumeration of interesting atoms. */
+enum atom {ATOM_ITEMS};
+#undef ATOM_ITEM
+
+/** A cpp version of read_u32_be(). */
+#define ATOM_VALUE(a, b, c, d) ((a << 24) + (b << 16) + (c << 8) + d)
+
+static uint8_t atom_name_to_type(uint8_t *p)
+{
+       /** Expands to an instance of the following unnamed structure. */
+       #define ATOM_ITEM(_name, a, b, c, d) \
+               {.name = # _name, .val = ATOM_VALUE(a, b, c, d)},
+       static const struct {
+               const char *name;
+               uint32_t val;
+       } atom_table[] = {ATOM_ITEMS};
+       #undef ATOM_ITEM
+       uint32_t val = read_u32_be(p);
+
+       for (uint8_t n = 0; n < ARRAY_SIZE(atom_table); n++)
+               if (val == atom_table[n].val)
+                       return n;
+       return 255;
+}
+
+/* read atom header, atom size is returned with header included. */
+static int atom_read_header(struct mp4 *f, uint8_t *atom_type,
+               uint8_t *header_size, uint64_t *atom_size)
+{
+       uint32_t size;
+       int ret;
+       uint8_t atom_header[8];
+
+       ret = read_data(f, atom_header, 8);
+       if (ret <= 0)
+               return ret;
+       size = read_u32_be(atom_header);
+       if (size == 1) { /* 64 bit atom size */
+               if (header_size)
+                       *header_size = 16;
+               ret = read_int64(f, atom_size);
+               if (ret <= 0)
+                       return ret;
+       } else {
+               if (header_size)
+                       *header_size = 8;
+               *atom_size = size;
+       }
+       *atom_type = atom_name_to_type(atom_header + 4);
+       return 1;
+}
+
+static off_t get_position(const struct mp4 *f)
+{
+       return f->cb->seek(f->cb->user_data, 0, SEEK_CUR);
+}
+
+static void set_position(struct mp4 *f, off_t position)
+{
+       f->cb->seek(f->cb->user_data, position, SEEK_SET);
+}
+
+static void skip_bytes(struct mp4 *f, off_t num_skip)
+{
+       f->cb->seek(f->cb->user_data, num_skip, SEEK_CUR);
+}
+
+static int read_stsz(struct mp4 *f)
+{
+       int ret;
+       struct mp4_track *t = &f->track;
+
+       if (t->state != ATS_SEEN_MP4A || t->stsz_table)
+               return 1;
+       skip_bytes(f, 4); /* version (1), flags (3) */
+       ret = read_int32(f, &t->stsz_sample_size);
+       if (ret <= 0)
+               return ret;
+       ret = read_int32(f, &t->stsz_sample_count);
+       if (ret <= 0)
+               return ret;
+       if (t->stsz_sample_size != 0)
+               return 1;
+       t->stsz_table = arr_alloc(t->stsz_sample_count, sizeof(int32_t));
+       for (uint32_t n = 0; n < t->stsz_sample_count; n++) {
+               ret = read_int32(f, &t->stsz_table[n]);
+               if (ret <= 0)
+                       return ret;
+       }
+       return 1;
+}
+
+static int read_stts(struct mp4 *f)
+{
+       int ret;
+       struct mp4_track *t = &f->track;
+
+       if (t->state != ATS_SEEN_MP4A || t->stts_sample_count)
+               return 1;
+       skip_bytes(f, 4); /* version (1), flags (3) */
+       ret = read_int32(f, &t->stts_entry_count);
+       if (ret <= 0)
+               return ret;
+       t->stts_sample_count = arr_alloc(t->stts_entry_count, sizeof(int32_t));
+       for (uint32_t n = 0; n < t->stts_entry_count; n++) {
+               ret = read_int32(f, &t->stts_sample_count[n]);
+               if (ret <= 0)
+                       return ret;
+               skip_bytes(f, 4); /* sample delta */
+       }
+       return 1;
+}
+
+static int read_stsc(struct mp4 *f)
+{
+       int ret;
+       struct mp4_track *t = &f->track;
+
+       if (t->state != ATS_SEEN_MP4A)
+               return 1;
+       if (t->stsc_first_chunk || t->stsc_samples_per_chunk)
+               return 1;
+       skip_bytes(f, 4); /* version (1), flags (3) */
+       ret = read_int32(f, &t->stsc_entry_count);
+       if (ret <= 0)
+               return ret;
+       t->stsc_first_chunk = arr_alloc(t->stsc_entry_count, sizeof(int32_t));
+       t->stsc_samples_per_chunk = arr_alloc(t->stsc_entry_count,
+               sizeof (int32_t));
+       for (uint32_t n = 0; n < t->stsc_entry_count; n++) {
+               ret = read_int32(f, &t->stsc_first_chunk[n]);
+               if (ret <= 0)
+                       return ret;
+               ret = read_int32(f, &t->stsc_samples_per_chunk[n]);
+               if (ret <= 0)
+                       return ret;
+               skip_bytes(f, 4); /* sample desc index */
+       }
+       return 1;
+}
+
+static int read_stco(struct mp4 *f)
+{
+       int ret;
+       struct mp4_track *t = &f->track;
+
+       if (t->state != ATS_SEEN_MP4A || t->stco_chunk_offset)
+               return 1;
+       skip_bytes(f, 4); /* version (1), flags (3) */
+       ret = read_int32(f, &t->stco_entry_count);
+       if (ret <= 0)
+               return ret;
+       t->stco_chunk_offset = arr_alloc(t->stco_entry_count, sizeof(int32_t));
+       for (uint32_t n = 0; n < t->stco_entry_count; n++) {
+               ret = read_int32(f, &t->stco_chunk_offset[n]);
+               if (ret <= 0)
+                       return ret;
+       }
+       return 1;
+}
+
+static int read_stsd(struct mp4 *f)
+{
+       int ret;
+       uint32_t entry_count;
+
+       if (f->track.state != ATS_INITIAL)
+               return 1;
+       skip_bytes(f, 4); /* version (1), flags (3) */
+       ret = read_int32(f, &entry_count);
+       if (ret <= 0)
+               return ret;
+       for (uint32_t n = 0; n < entry_count; n++) {
+               uint64_t skip = get_position(f);
+               uint64_t size;
+               uint8_t atom_type = 0;
+               ret = atom_read_header(f, &atom_type, NULL, &size);
+               if (ret <= 0)
+                       return ret;
+               skip += size;
+               if (atom_type == ATOM_MP4A) {
+                       f->track.state = ATS_SEEN_MP4A;
+                       /* reserved (6), data reference index (2), reserved (8) */
+                       skip_bytes(f, 16);
+                       ret = read_int16(f, &f->track.channel_count);
+                       if (ret <= 0)
+                               return ret;
+                       skip_bytes(f, 6);
+                       ret = read_int16(f, &f->track.sample_rate);
+                       if (ret <= 0)
+                               return ret;
+               }
+               set_position(f, skip);
+       }
+       return 1;
+}
+
+static const char *get_metadata_name(uint8_t atom_type)
+{
+       switch (atom_type) {
+       case ATOM_TITLE: return "title";
+       case ATOM_ARTIST: return "artist";
+       case ATOM_ALBUM: return "album";
+       case ATOM_DATE: return "date";
+       case ATOM_COMMENT: return "comment";
+       default: return "unknown";
+       }
+}
+
+static int parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
+{
+       int ret;
+       uint64_t subsize, sumsize;
+       char *value = NULL;
+       uint32_t len = 0;
+       uint64_t destpos;
+       struct mp4_tag *tag;
+
+       for (
+               sumsize = 0;
+               sumsize < size;
+               set_position(f, destpos), sumsize += subsize
+       ) {
+               uint8_t atom_type;
+               uint8_t header_size = 0;
+               ret = atom_read_header(f, &atom_type, &header_size, &subsize);
+               if (ret <= 0)
+                       goto fail;
+               destpos = get_position(f) + subsize - header_size;
+               if (atom_type != ATOM_DATA)
+                       continue;
+               skip_bytes(f, 8); /* version (1), flags (3), reserved (4) */
+               ret = -E_MP4_CORRUPT;
+               if (subsize < header_size + 8 || subsize > UINT_MAX)
+                       goto fail;
+               len = subsize - (header_size + 8);
+               free(value);
+               value = alloc(len + 1);
+               ret = read_data(f, value, len);
+               if (ret <= 0)
+                       goto fail;
+               value[len] = '\0';
+       }
+       if (!value)
+               return -E_MP4_CORRUPT;
+       f->meta.tags = para_realloc(f->meta.tags, (f->meta.count + 1)
+               * sizeof(struct mp4_tag));
+       tag = f->meta.tags + f->meta.count;
+       tag->item = para_strdup(get_metadata_name(parent));
+       tag->value = value;
+       f->meta.count++;
+       return 1;
+fail:
+       free(value);
+       return ret;
+}
+
+static int read_mdhd(struct mp4 *f)
+{
+       int ret;
+       uint32_t version;
+       struct mp4_track *t = &f->track;
+
+       if (t->state != ATS_INITIAL)
+               return 1;
+       ret = read_int32(f, &version);
+       if (ret <= 0)
+               return ret;
+       if (version == 1) {
+               skip_bytes(f, 16); /* creation time (8), modification time (8) */
+               ret = read_int32(f, &t->time_scale);
+               if (ret <= 0)
+                       return ret;
+               ret = read_int64(f, &t->duration);
+               if (ret <= 0)
+                       return ret;
+       } else { /* version == 0 */
+               uint32_t temp;
+
+               skip_bytes(f, 8); /* creation time (4), modification time (4) */
+               ret = read_int32(f, &t->time_scale);
+               if (ret <= 0)
+                       return ret;
+               ret = read_int32(f, &temp);
+               if (ret <= 0)
+                       return ret;
+               t->duration = (temp == (uint32_t) (-1))?
+                       (uint64_t) (-1) : (uint64_t) (temp);
+       }
+       skip_bytes(f, 4);
+       return 1;
+}
+
+static int read_ilst(struct mp4 *f, int32_t size)
+{
+       int ret;
+       uint64_t sumsize = 0;
+
+       while (sumsize < size) {
+               uint8_t atom_type;
+               uint64_t subsize, destpos;
+               uint8_t header_size = 0;
+               ret = atom_read_header(f, &atom_type, &header_size, &subsize);
+               if (ret <= 0)
+                       return ret;
+               destpos = get_position(f) + subsize - header_size;
+               switch (atom_type) {
+               case ATOM_ARTIST:
+               case ATOM_TITLE:
+               case ATOM_ALBUM:
+               case ATOM_COMMENT:
+               case ATOM_DATE:
+                       ret = parse_tag(f, atom_type, subsize - header_size);
+                       if (ret <= 0)
+                               return ret;
+               }
+               set_position(f, destpos);
+               sumsize += subsize;
+       }
+       return 1;
+}
+
+static int read_meta(struct mp4 *f, uint64_t size)
+{
+       int ret;
+       uint64_t subsize, sumsize = 0;
+       uint8_t atom_type;
+       uint8_t header_size = 0;
+
+       skip_bytes(f, 4); /* version (1), flags (3) */
+       while (sumsize < (size - (header_size + 4))) {
+               ret = atom_read_header(f, &atom_type, &header_size, &subsize);
+               if (ret <= 0)
+                       return ret;
+               if (subsize <= header_size + 4)
+                       return 1;
+               if (atom_type == ATOM_ILST) {
+                       f->ilst_offset = get_position(f) - header_size;
+                       f->ilst_size = subsize;
+                       ret = read_ilst(f, subsize - (header_size + 4));
+                       if (ret <= 0)
+                               return ret;
+               } else
+                       set_position(f, get_position(f) + subsize - header_size);
+               sumsize += subsize;
+       }
+       return 1;
+}
+
+static bool need_atom(uint8_t atom_type, bool meta_only)
+{
+       /* these are needed in any case */
+       switch (atom_type) {
+       case ATOM_STSD:
+       case ATOM_META:
+       case ATOM_TRAK:
+       case ATOM_MDIA:
+       case ATOM_MINF:
+       case ATOM_STBL:
+       case ATOM_UDTA:
+               return true;
+       }
+       /* meta-only opens don't need anything else */
+       if (meta_only)
+               return false;
+       /* these are only required for regular opens */
+       switch (atom_type) {
+       case ATOM_STTS:
+       case ATOM_STSZ:
+       case ATOM_STCO:
+       case ATOM_STSC:
+       case ATOM_MDHD:
+               return true;
+       }
+       return false;
+}
+
+/* parse atoms that are sub atoms of other atoms */
+static int parse_sub_atoms(struct mp4 *f, uint64_t total_size, bool meta_only)
+{
+       int ret;
+       uint64_t dest, size, end = get_position(f) + total_size;
+
+       for (dest = get_position(f); dest < end; set_position(f, dest)) {
+               uint8_t header_size, atom_type;
+               ret = atom_read_header(f, &atom_type, &header_size, &size);
+               if (ret <= 0)
+                       return ret;
+               if (size == 0)
+                       return -E_MP4_CORRUPT;
+               dest = get_position(f) + size - header_size;
+               if (atom_type == ATOM_TRAK && f->track.state == ATS_SEEN_MP4A) {
+                       f->track.state = ATS_TRACK_CHANGE;
+                       continue;
+               }
+               if (atom_type == ATOM_UDTA) {
+                       f->udta_offset = get_position(f) - header_size;
+                       f->udta_size = size;
+               }
+               if (!need_atom(atom_type, meta_only))
+                       continue;
+               switch (atom_type) {
+               case ATOM_STSZ: ret = read_stsz(f); break;
+               case ATOM_STTS: ret = read_stts(f); break;
+               case ATOM_STSC: ret = read_stsc(f); break;
+               case ATOM_STCO: ret = read_stco(f); break;
+               case ATOM_STSD: ret = read_stsd(f); break;
+               case ATOM_MDHD: ret = read_mdhd(f); break;
+               case ATOM_META:
+                       f->meta_offset = get_position(f) - header_size;
+                       f->meta_size = size;
+                       ret = read_meta(f, size);
+                       break;
+               default:
+                       ret = parse_sub_atoms(f, size - header_size, meta_only);
+               }
+               if (ret <= 0)
+                       return ret;
+       }
+       return 1;
+}
+
+/**
+ * Deallocate all resources associated with an mp4 file handle.
+ *
+ * \param f File handle returned by \ref mp4_open() or \ref mp4_open_meta().
+ *
+ * This frees the metadata items and various tables which were allocated when
+ * the file was opened. The given file handle must not be NULL.
+ */
+void mp4_close(struct mp4 *f)
+{
+       free(f->track.stsz_table);
+       free(f->track.stts_sample_count);
+       free(f->track.stsc_first_chunk);
+       free(f->track.stsc_samples_per_chunk);
+       free(f->track.stco_chunk_offset);
+       for (uint32_t n = 0; n < f->meta.count; n++) {
+               free(f->meta.tags[n].item);
+               free(f->meta.tags[n].value);
+       }
+       free(f->meta.tags);
+       free(f);
+}
+
+static int open_file(const struct mp4_callback *cb, bool meta_only, struct mp4 **result)
+{
+       int ret;
+       uint64_t size;
+       uint8_t atom_type, header_size;
+       struct mp4 *f = zalloc(sizeof(*f));
+
+       f->cb = cb;
+       while ((ret = atom_read_header(f, &atom_type, &header_size, &size)) > 0) {
+               f->last_atom = atom_type;
+               if (atom_type != ATOM_MOOV || size <= header_size) { /* skip */
+                       set_position(f, get_position(f) + size - header_size);
+                       continue;
+               }
+               f->moov_offset = get_position(f) - header_size;
+               f->moov_size = size;
+               ret = parse_sub_atoms(f, size - header_size, meta_only);
+               if (ret <= 0)
+                       break;
+       }
+       if (ret < 0)
+               goto fail;
+       ret = -E_MP4_TRACK;
+       if (f->track.channel_count == 0)
+               goto fail;
+       ret = -E_MP4_BAD_SAMPLERATE;
+       if (f->track.sample_rate == 0)
+               goto fail;
+       ret = -E_MP4_MISSING_ATOM;
+       if (f->udta_size == 0 || f->meta_size == 0 || f->ilst_size == 0)
+               goto fail;
+       *result = f;
+       return 1;
+fail:
+       *result = NULL;
+       mp4_close(f);
+       return ret;
+}
+
+/**
+ * Read the audio track and the metadata of an mp4 file.
+ *
+ * \param cb Only the ->read() and ->seek() methods need to be supplied.
+ * \param result Initialized to a non-NULL pointer iff the function succeeds.
+ *
+ * This detects and parses the first audio track and the metadata information
+ * of the mp4 file. Various error checks are performed after the mp4 atoms have
+ * been parsed successfully.
+ *
+ * This function does not modify the file. However, if the caller intents to
+ * update the metadata later, the ->write() and ->truncate() methods must be
+ * supplied in the callback structure.
+ *
+ * \return Standard. Several errors are possible.
+ *
+ * \sa \ref mp4_open_meta().
+ */
+int mp4_open(const struct mp4_callback *cb, struct mp4 **result)
+{
+       struct mp4 *f;
+       int ret;
+
+       *result = NULL;
+       ret = open_file(cb, false, &f);
+       if (ret < 0)
+               return ret;
+       ret = -E_MP4_BAD_SAMPLE_COUNT;
+       if (f->track.stsz_sample_count == 0)
+               goto fail;
+       ret = -E_MP4_CORRUPT;
+       if (f->track.time_scale == 0)
+               goto fail;
+       *result = f;
+       return 1;
+fail:
+       mp4_close(f);
+       return ret;
+}
+
+static int32_t chunk_of_sample(const struct mp4 *f, int32_t sample,
+               int32_t *chunk)
+{
+       const struct mp4_track *t = &f->track;
+       uint32_t *fc = t->stsc_first_chunk, *spc = t->stsc_samples_per_chunk;
+       uint32_t chunk1, chunk1samples, n, total, k;
+
+       for (k = 1, total = 0; k < t->stsc_entry_count; k++, total += n) {
+               n = (fc[k] - fc[k - 1]) * spc[k - 1]; /* number of samples */
+               if (sample < total + n)
+                       break;
+       }
+       chunk1 = fc[k - 1];
+       chunk1samples = spc[k - 1];
+       if (chunk1samples != 0)
+               *chunk = (sample - total) / chunk1samples + chunk1;
+       else
+               *chunk = 1;
+       return total + (*chunk - chunk1) * chunk1samples;
+}
+
+/**
+ * Compute the duration of an mp4 file.
+ *
+ * \param f See \ref mp4_close().
+ *
+ * \return The number of milliseconds of the audio track. This function never
+ * fails.
+ */
+uint64_t mp4_get_duration(const struct mp4 *f)
+{
+       const struct mp4_track *t = &f->track;
+
+       return t->duration * 1000 / t->time_scale;
+}
+
+/**
+ * Reposition the read/write file offset.
+ *
+ * \param f See \ref mp4_close().
+ * \param sample The number of the sample to reposition to.
+ *
+ * The given sample number must be within range, i.e., strictly less than the
+ * value returned by \ref mp4_num_samples().
+ *
+ * \return Standard. The only possible error is an invalid sample number.
+ */
+int mp4_set_sample_position(struct mp4 *f, uint32_t sample)
+{
+       const struct mp4_track *t = &f->track;
+       int32_t offset, chunk, chunk_sample;
+       uint32_t n, srs; /* sample range size */
+
+       if (sample >= t->stsz_sample_count)
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
+       chunk_sample = chunk_of_sample(f, sample, &chunk);
+       if (t->stsz_sample_size > 0)
+               srs = (sample - chunk_sample) * t->stsz_sample_size;
+       else {
+               for (srs = 0, n = chunk_sample; n < sample; n++)
+                       srs += t->stsz_table[n];
+       }
+       if (t->stco_entry_count > 0 && chunk > t->stco_entry_count)
+               offset = t->stco_chunk_offset[t->stco_entry_count - 1];
+       else if (t->stco_entry_count > 0)
+               offset = t->stco_chunk_offset[chunk - 1];
+       else
+               offset = 8;
+       set_position(f, offset + srs);
+       return 1;
+}
+
+/**
+ * Look up and return the size of the given sample in the stsz table.
+ *
+ * \param f See \ref mp4_close().
+ * \param sample The sample number of interest.
+ * \param result Sample size is returned here.
+ *
+ * For the sample argument the restriction mentioned in the documentation of
+ * \ref mp4_set_sample_position() applies as well.
+ *
+ * \return Standard. Like for \ref mp4_set_sample_position(), EINVAL is the
+ * only possible error.
+ */
+int mp4_get_sample_size(const struct mp4 *f, uint32_t sample, uint32_t *result)
+{
+       const struct mp4_track *t = &f->track;
+
+       if (sample >= t->stsz_sample_count)
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
+       if (t->stsz_sample_size != 0)
+               *result = t->stsz_sample_size;
+       else
+               *result = t->stsz_table[sample];
+       return 1;
+}
+
+/**
+ * Return the sample rate stored in the stsd atom.
+ *
+ * \param f See \ref mp4_close().
+ *
+ * The sample rate is a property of the audio track of the mp4 file and is thus
+ * independent of the sample number.
+ *
+ * \return The function always returns a positive value because the open
+ * operation fails if the sample rate happens to be zero. A typical value is
+ * 44100.
+ */
+uint16_t mp4_get_sample_rate(const struct mp4 *f)
+{
+       return f->track.sample_rate;
+}
+
+/**
+ * Return the number of channels of the audio track.
+ *
+ * \param f See \ref mp4_close().
+ *
+ * \return The returned channel count is guaranteed to be positive because the
+ * open operation fails if the mp4a atom is missing or contains a zero channel
+ * count.
+ */
+uint16_t mp4_get_channel_count(const struct mp4 *f)
+{
+       return f->track.channel_count;
+}
+
+/**
+ * Return the number of samples of the audio track.
+ *
+ * \param f See \ref mp4_close().
+ *
+ * \return The sample count is read from the stsz atom during open.
+ */
+uint32_t mp4_num_samples(const struct mp4 *f)
+{
+       return f->track.stsz_sample_count;
+}
+
+/**
+ * Open an mp4 file in metadata-only mode.
+ *
+ * \param cb See \ref mp4_open().
+ * \param result See \ref mp4_open().
+ *
+ * This is similar to \ref mp4_open() but is cheaper because it only parses the
+ * metadata of the mp4 file. The only functions that can subsequently be called
+ * with the file handle returned here are \ref mp4_get_meta() and \ref
+ * mp4_update_meta().
+ *
+ * \return Standard.
+ *
+ * \sa \ref mp4_open(). The comment about ->write() and ->truncate() applies to
+ * this function as well.
+ */
+int mp4_open_meta(const struct mp4_callback *cb, struct mp4 **result)
+{
+       struct mp4 *f;
+       int ret = open_file(cb, true, &f);
+
+       if (ret < 0)
+               return ret;
+       *result = f;
+       return 1;
+}
+
+/**
+ * Return the metadata of an mp4 file.
+ *
+ * \param f See \ref mp4_close().
+ *
+ * The caller is allowed to add, delete or modify the entries of the returned
+ * structure with the intention to pass the modified version to \ref
+ * mp4_update_meta().
+ *
+ * \return This never returns NULL, even if the file contains no metadata tag
+ * items. However, the meta count will be zero and the ->tags pointer NULL in
+ * this case.
+ */
+struct mp4_metadata *mp4_get_meta(struct mp4 *f)
+{
+       return &f->meta;
+}
+
+/** Total length of an on-disk metadata tag. */
+#define TAG_LEN(_len) (24 + (_len))
+static void create_ilst(const struct mp4_metadata *meta, uint8_t *out)
+{
+       for (unsigned n = 0; n < meta->count; n++) {
+               struct mp4_tag *tag = meta->tags + n;
+               unsigned len = strlen(tag->value);
+               const char *atom_name;
+
+               if (!strcasecmp(tag->item, "title"))
+                       atom_name = "\xA9" "nam";
+               else if (!strcasecmp(tag->item, "artist"))
+                       atom_name = "\xA9" "ART";
+               else if (!strcasecmp(tag->item, "album"))
+                       atom_name = "\xA9" "alb";
+               else if (!strcasecmp(tag->item, "date"))
+                       atom_name = "\xA9" "day";
+               else if (!strcasecmp(tag->item, "comment"))
+                       atom_name = "\xA9" "cmt";
+               else
+                       assert(false);
+               write_u32_be(out, TAG_LEN(len));
+               memcpy(out + 4, atom_name, 4);
+               write_u32_be(out + 8, 8 /* data atom header */
+                       + 8 /* flags + reserved */
+                       + len);
+               memcpy(out + 12, "data", 4);
+               write_u32_be(out + 16, 1); /* flags */
+               write_u32_be(out + 20, 0); /* reserved */
+               memcpy(out + 24, tag->value, len);
+               out += TAG_LEN(len);
+       }
+}
+
+static void *modify_moov(struct mp4 *f, uint32_t *out_size)
+{
+       int ret;
+       uint64_t total_base = f->moov_offset + 8;
+       uint32_t total_size = f->moov_size - 8;
+       uint32_t new_ilst_size = 0;
+       void *out_buffer;
+       uint8_t *p_out;
+       int32_t size_delta;
+       uint32_t tmp;
+
+       for (unsigned n = 0; n < f->meta.count; n++)
+               new_ilst_size += TAG_LEN(strlen(f->meta.tags[n].value));
+       size_delta = new_ilst_size - (f->ilst_size - 8);
+       *out_size = total_size + size_delta;
+       out_buffer = alloc(*out_size);
+       p_out = out_buffer;
+       set_position(f, total_base);
+       ret = read_data(f, p_out, f->udta_offset - total_base);
+       if (ret <= 0)
+               return NULL;
+       p_out += f->udta_offset - total_base;
+       ret = read_int32(f, &tmp);
+       if (ret <= 0)
+               return NULL;
+       write_u32_be(p_out, tmp + size_delta);
+       p_out += 4;
+       ret = read_data(f, p_out, 4);
+       if (ret <= 0)
+               return NULL;
+       p_out += 4;
+       ret = read_data(f, p_out, f->meta_offset - f->udta_offset - 8);
+       if (ret <= 0)
+               return NULL;
+       p_out += f->meta_offset - f->udta_offset - 8;
+       ret = read_int32(f, &tmp);
+       if (ret <= 0)
+               return NULL;
+       write_u32_be(p_out, tmp + size_delta);
+       p_out += 4;
+       ret = read_data(f, p_out, 4);
+       if (ret <= 0)
+               return NULL;
+       p_out += 4;
+       ret = read_data(f, p_out, f->ilst_offset - f->meta_offset - 8);
+       if (ret <= 0)
+               return NULL;
+       p_out += f->ilst_offset - f->meta_offset - 8;
+       ret = read_int32(f, &tmp);
+       if (ret <= 0)
+               return NULL;
+       write_u32_be(p_out, tmp + size_delta);
+       p_out += 4;
+       ret = read_data(f, p_out, 4);
+       if (ret <= 0)
+               return NULL;
+       p_out += 4;
+       create_ilst(&f->meta, p_out);
+       p_out += new_ilst_size;
+       set_position(f, f->ilst_offset + f->ilst_size);
+       ret = read_data(f, p_out, total_size - (f->ilst_offset - total_base)
+               - f->ilst_size);
+       if (ret <= 0)
+               return NULL;
+       return out_buffer;
+}
+
+static int write_data(struct mp4 *f, void *data, size_t size)
+{
+       while (size > 0) {
+               ssize_t ret = f->cb->write(f->cb->user_data, data, size);
+               if (ret < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       return -ERRNO_TO_PARA_ERROR(errno);
+               }
+               size -= ret;
+       }
+       return 1;
+}
+
+/**
+ * Write back the modified metadata items to the mp4 file.
+ *
+ * This is the only public function which modifies the contents of an mp4 file.
+ * This is achieved by calling the ->write() and ->truncate() methods of the
+ * callback structure passed to \ref mp4_open() or \ref mp4_open_meta().
+ *
+ * \param f See \ref mp4_close().
+ *
+ * The modified metadata structure does not need to be supplied to this
+ * function because it is part of the mp4 structure.
+ *
+ * \return Standard.
+ */
+int mp4_update_meta(struct mp4 *f)
+{
+       void *new_moov_data;
+       uint32_t new_moov_size;
+       uint8_t buf[8] = "----moov";
+       int ret;
+
+       set_position(f, 0);
+       new_moov_data = modify_moov(f, &new_moov_size);
+       if (!new_moov_data ) {
+               mp4_close(f);
+               return 0;
+       }
+       if (f->last_atom != ATOM_MOOV) {
+               set_position(f, f->moov_offset + 4);
+               ret = write_data(f, "free", 4); /* rename old moov to free */
+               if (ret < 0)
+                       goto free_moov;
+               /* write new moov atom at EOF */
+               f->cb->seek(f->cb->user_data, 0, SEEK_END);
+       } else /* overwrite old moov atom */
+               set_position(f, f->moov_offset);
+       write_u32_be(buf, new_moov_size + 8);
+       ret = write_data(f, buf, sizeof(buf));
+       if (ret < 0)
+               goto free_moov;
+       ret = write_data(f, new_moov_data, new_moov_size);
+       if (ret < 0)
+               goto free_moov;
+       ret = f->cb->truncate(f->cb->user_data);
+       if (ret < 0)
+               ret = -ERRNO_TO_PARA_ERROR(errno);
+free_moov:
+       free(new_moov_data);
+       return ret;
+}
+
+/**
+ * Return the value of the given tag item.
+ *
+ * \param f See \ref mp4_close().
+ * \param item "artist", "title", "album", "comment", or "date".
+ *
+ * \return The function returns NULL if the given item is not in the above
+ * list. Otherwise, if the file does not contain a tag for the given item, the
+ * function also returns NULL. Otherwise a copy of the tag value is returned
+ * and the caller should free this memory when it is no longer needed.
+ */
+char *mp4_get_tag_value(const struct mp4 *f, const char *item)
+{
+       for (unsigned n = 0; n < f->meta.count; n++)
+               if (!strcasecmp(f->meta.tags[n].item, item))
+                       return para_strdup(f->meta.tags[n].value);
+       return NULL;
+}
diff --git a/mp4.h b/mp4.h
new file mode 100644 (file)
index 0000000..1618aa3
--- /dev/null
+++ b/mp4.h
@@ -0,0 +1,87 @@
+/** \file mp4.h Public API of the mp4 parser. */
+
+/**
+ * Callbacks provided by the user of the mp4 parsing API.
+ *
+ * A pointer to this structure is passed to the two public open functions. If
+ * the file is opened in read-only mode, the ->write() and ->truncate() methods
+ * won't be called and may thus be NULL. The ->read() and ->seek() methods
+ * must be supplied for either open type.
+ *
+ * All methods are supposed to work like their corresponding system calls.
+ * That is, they should return non-negative for success and -1 on failure. In
+ * the error case errno is expected to be set accordingly.
+ *
+ * \sa \ref mp4_open(), \ref mp4_open_meta().
+ */
+struct mp4_callback {
+       /** This pointer is propagated to each call of all methods. */
+       void *user_data;
+       /**
+        * This should return the number of bytes read on success. Short reads
+        * are OK: the function may return less than length.
+        */
+       ssize_t (*read)(void *user_data, void *buffer, size_t length);
+       /**
+        * This method is assumed to succeed. The implementation should simply
+        * abort on errors. Note that offsets beyond EOF must not be regarded
+        * as invalid arguments.
+        */
+       off_t (*seek)(void *user_data, off_t offset, int whence);
+       /**
+        * Like the write() system call, this should return the number of bytes
+        * written. Short writes are OK: the function may return less than
+        * count.
+        */
+       ssize_t (*write)(void *user_data, void *buffer, size_t count);
+       /**
+        * Unlike the truncate system call, this function does not receive an
+        * offset. The method is expected to truncate the file to the offset
+        * given by the current file position instead.
+        */
+       int (*truncate)(void *user_data);
+};
+
+/** Specifies one metadata tag. Both fields are 0-terminated strings. */
+struct mp4_tag {
+       /** The item name: "artist", "title", "album", "comment", or "date". */
+       char *item;
+       /** An arbitrary string value. */
+       char *value;
+};
+
+/**
+ * An array of name/value pairs.
+ *
+ * This structure is initialized when the mp4 file is opened in either mode.
+ * If the file contains metadata items other than the standard five, those
+ * non-standard items are not included in the array. After a successful open, a
+ * pointer to the metadata structure can be obtained via \ref mp4_get_meta().
+ */
+struct mp4_metadata {
+       /** It's OK to change this, for example by calling realloc(). */
+       struct mp4_tag *tags;
+       /** The number of entries of the array. */
+       unsigned count;
+};
+
+/**
+ * The mp4 file handle.
+ *
+ * A pointer to this opaque structure is returned by the two open functions.
+ * All other functions of the mp4 API receive a pointer of this type.
+ */
+struct mp4;
+
+int mp4_set_sample_position(struct mp4 *f, uint32_t sample);
+int mp4_open(const struct mp4_callback *cb, struct mp4 **result);
+void mp4_close(struct mp4 *f);
+int mp4_get_sample_size(const struct mp4 *f, uint32_t sample, uint32_t *result);
+uint16_t mp4_get_sample_rate(const struct mp4 *f);
+uint16_t mp4_get_channel_count(const struct mp4 *f);
+uint32_t mp4_num_samples(const struct mp4 *f);
+uint64_t mp4_get_duration(const struct mp4 *f);
+int mp4_open_meta(const struct mp4_callback *cb, struct mp4 **result);
+struct mp4_metadata *mp4_get_meta(struct mp4 *f);
+int mp4_update_meta(struct mp4 *f);
+char *mp4_get_tag_value(const struct mp4 *f, const char *item);
diff --git a/net.c b/net.c
index 91200fc040bcfebafccf9e737fb65514af9ff8f2..a24081f5123bd5a62236ff6d8202afc4e16c009b 100644 (file)
--- a/net.c
+++ b/net.c
 #include "list.h"
 #include "fd.h"
 
+/* Whether the given address conforms to the IPv4 address format. */
+static inline bool is_valid_ipv4_address(const char *address)
+{
+       struct in_addr test_it;
+       return inet_pton(AF_INET, address, &test_it) != 0;
+}
+
 /**
  * Parse and validate IPv4 address/netmask string.
  *
@@ -58,13 +65,6 @@ failed:
        return NULL;
 }
 
-
-/**
- * Match string as a candidate IPv4 address.
- *
- * \param address The string to match.
- * \return True if \a address has "dot-quad" format.
- */
 static bool is_v4_dot_quad(const char *address)
 {
        bool result;
@@ -77,6 +77,13 @@ static bool is_v4_dot_quad(const char *address)
        return result;
 }
 
+/* Whether a string conforms to IPv6 address format (RFC 4291). */
+static inline bool is_valid_ipv6_address(const char *address)
+{
+       struct in6_addr test_it;
+       return inet_pton(AF_INET6, address, &test_it) != 0;
+}
+
 /**
  * Perform basic syntax checking on the host-part of an URL:
  *
@@ -205,7 +212,7 @@ char *format_url(const char *url, int default_port)
  * \param transport Transport protocol name (e.g. "udp", "tcp"), or NULL.
  * \return Pointer to static result buffer.
  *
- * \sa getservent(3), services(5), nsswitch.conf(5).
+ * \sa getservbyport(3), services(5), nsswitch.conf(5).
  */
 const char *stringify_port(int port, const char *transport)
 {
@@ -224,12 +231,13 @@ const char *stringify_port(int port, const char *transport)
        return service;
 }
 
-/**
- * Determine the socket type for a given layer-4 protocol.
- *
- * \param l4type The symbolic name of the transport-layer protocol.
- *
- * \sa ip(7), socket(2).
+#ifndef SOCK_DCCP
+#define SOCK_DCCP 6 /**< Linux socket type. */
+#endif
+
+/*
+ * Determine the socket type, given the symbolic name of the transport-layer
+ * protocol. See ip(7), socket(2).
  */
 static inline int sock_type(const unsigned l4type)
 {
@@ -241,9 +249,7 @@ static inline int sock_type(const unsigned l4type)
        return -1;              /* not supported here */
 }
 
-/**
- * Pretty-print transport-layer name.
- */
+/* Pretty-print transport-layer name. */
 static const char *layer4_name(const unsigned l4type)
 {
        switch (l4type) {
@@ -273,7 +279,12 @@ struct pre_conn_opt {
        struct list_head node;          /**< FIFO, as sockopt order matters. */
 };
 
-/** FIFO list of pre-connection socket options to be set */
+/**
+ * List of pre-connection socket options to be set.
+ *
+ * This list contains transport-layer independent encapsulation of socket
+ * options that need to be registered prior to setting up a connection.
+ */
 struct flowopts {
        struct list_head sockopts;
 };
@@ -286,9 +297,9 @@ struct flowopts {
  */
 struct flowopts *flowopt_new(void)
 {
-       struct flowopts *new = para_malloc(sizeof(*new));
+       struct flowopts *new = alloc(sizeof(*new));
 
-       INIT_LIST_HEAD(&new->sockopts);
+       init_list_head(&new->sockopts);
        return new;
 }
 
@@ -307,7 +318,7 @@ struct flowopts *flowopt_new(void)
 void flowopt_add(struct flowopts *fo, int lev, int opt,
                const char *name, const void *val, int len)
 {
-       struct pre_conn_opt *new = para_malloc(sizeof(*new));
+       struct pre_conn_opt *new = alloc(sizeof(*new));
 
        new->sock_option = opt;
        new->sock_level  = lev;
@@ -317,7 +328,7 @@ void flowopt_add(struct flowopts *fo, int lev, int opt,
                new->opt_val = NULL;
                new->opt_len = 0;
        } else {
-               new->opt_val = para_malloc(len);
+               new->opt_val = alloc(len);
                new->opt_len = len;
                memcpy(new->opt_val, val, len);
        }
@@ -325,7 +336,7 @@ void flowopt_add(struct flowopts *fo, int lev, int opt,
        list_add_tail(&new->node, &fo->sockopts);
 }
 
-/** Set the entire bunch of pre-connection options at once. */
+/* Set the entire bunch of pre-connection options at once. */
 static void flowopt_setopts(int sockfd, struct flowopts *fo)
 {
        struct pre_conn_opt *pc;
@@ -509,7 +520,7 @@ int makesock(unsigned l4type, bool passive, const char *host, uint16_t port_numb
        if (ai)
                freeaddrinfo(ai);
        if (ret < 0) {
-               PARA_ERROR_LOG("can not create %s socket %s#%d.\n",
+               PARA_NOTICE_LOG("can not create %s socket %s#%d.\n",
                layer4_name(l4type), host? host : (passive?
                "[loopback]" : "[localhost]"), port_number);
        }
@@ -571,11 +582,7 @@ int para_listen_simple(unsigned l4type, uint16_t port)
        return para_listen(l4type, NULL, port);
 }
 
-/**
- * Determine IPv4/v6 socket address length.
- * \param sa Container of IPv4 or IPv6 address.
- * \return Address-family dependent address length.
- */
+/* Compute the address-family dependent address length of an IPv4/v6 socket. */
 static socklen_t salen(const struct sockaddr *sa)
 {
        assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
@@ -585,7 +592,7 @@ static socklen_t salen(const struct sockaddr *sa)
                : sizeof(struct sockaddr_in);
 }
 
-/** True if @ss holds a v6-mapped-v4 address (RFC 4291, 2.5.5.2) */
+/* True if ss holds a v6-mapped-v4 address (RFC 4291, 2.5.5.2) */
 static bool SS_IS_ADDR_V4MAPPED(const struct sockaddr_storage *ss)
 {
        const struct sockaddr_in6 *ia6 = (const struct sockaddr_in6 *)ss;
@@ -593,10 +600,10 @@ static bool SS_IS_ADDR_V4MAPPED(const struct sockaddr_storage *ss)
        return ss->ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&ia6->sin6_addr);
 }
 
-/**
+/*
  * Process IPv4/v6 address, turn v6-mapped-v4 address into normal IPv4 address.
- * \param ss Container of IPv4/6 address.
- * \return Pointer to normalized address (may be static storage).
+ * ss: Container of IPv4/6 address.
+ * Returns: Pointer to normalized address (may be static storage).
  *
  * \sa RFC 3493.
  */
@@ -617,7 +624,7 @@ normalize_ip_address(const struct sockaddr_storage *ss)
        return (const struct sockaddr *)ss;
 }
 
-/**
+/*
  * Generic/fallback MTU values
  *
  * These are taken from RFC 1122, RFC 2460, and RFC 5405.
@@ -632,7 +639,7 @@ static inline int generic_mtu(const int af_type)
        return af_type == AF_INET6 ? 1280 : 576;
 }
 
-/** Crude approximation of IP header overhead - neglecting options. */
+/* Crude approximation of IP header overhead - neglecting options. */
 static inline int estimated_header_overhead(const int af_type)
 {
        return af_type == AF_INET6 ? 40 : 20;
@@ -801,25 +808,21 @@ int recv_buffer(int fd, char *buf, size_t size)
  * Wrapper around the accept system call.
  *
  * \param fd The listening socket.
- * \param rfds An optional fd_set pointer.
  * \param addr Structure which is filled in with the address of the peer socket.
  * \param size Should contain the size of the structure pointed to by \a addr.
  * \param new_fd Result pointer.
  *
- * Accept incoming connections on \a addr, retry if interrupted. If \a rfds is
- * not \p NULL, return 0 if \a fd is not set in \a rfds without calling accept().
+ * Accept incoming connections on addr, retry if interrupted.
  *
  * \return Negative on errors, zero if no connections are present to be accepted,
  * one otherwise.
  *
  * \sa accept(2).
  */
-int para_accept(int fd, fd_set *rfds, void *addr, socklen_t size, int *new_fd)
+int para_accept(int fd, void *addr, socklen_t size, int *new_fd)
 {
        int ret;
 
-       if (rfds && !FD_ISSET(fd, rfds))
-               return 0;
        do
                ret = accept(fd, (struct sockaddr *) addr, &size);
        while (ret < 0 && errno == EINTR);
@@ -833,6 +836,10 @@ int para_accept(int fd, fd_set *rfds, void *addr, socklen_t size, int *new_fd)
        return -ERRNO_TO_PARA_ERROR(errno);
 }
 
+#ifndef DCCP_SOCKOPT_AVAILABLE_CCIDS
+#define DCCP_SOCKOPT_AVAILABLE_CCIDS 12 /**< List of supported CCIDs. */
+#endif
+
 /**
  * Probe the list of DCCP CCIDs configured on this host.
  * \param ccid_array Pointer to return statically allocated array in.
@@ -847,7 +854,7 @@ int dccp_available_ccids(uint8_t **ccid_array)
        socklen_t nccids = sizeof(ccids);
        int ret, fd;
 
-       ret = fd = makesock(IPPROTO_DCCP, 1, NULL, 0, NULL);
+       ret = fd = makesock(IPPROTO_DCCP, true /* passive */, NULL, 0, NULL);
        if (ret < 0)
                return ret;
 
@@ -865,6 +872,18 @@ int dccp_available_ccids(uint8_t **ccid_array)
        return nccids;
 }
 
+/**
+ * The buffer size of the sun_path component of struct sockaddr_un.
+ *
+ * While glibc doesn't define UNIX_PATH_MAX, it documents it has being limited
+ * to 108 bytes. On NetBSD it is only 104 bytes though. We trust UNIX_PATH_MAX
+ * if it is defined and use the size of the ->sun_path member otherwise. This
+ * should be safe everywhere.
+ */
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)0)->sun_path))
+#endif
+
 /*
  * Prepare a structure for AF_UNIX socket addresses.
  *
diff --git a/net.h b/net.h
index 2256f376497b89d3382e2bf80ce09725ef64ab02..d206881c587b8ec185856e23e84bb26ecf032f45 100644 (file)
--- a/net.h
+++ b/net.h
 /* Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
 /** \file net.h exported symbols from net.c */
 
-/**
- * The buffer size of the sun_path component of struct sockaddr_un.
- *
- * While glibc doesn't define \p UNIX_PATH_MAX, it documents it has being
- * limited to 108 bytes. On NetBSD it is only 104 bytes though. We trust \p
- * UNIX_PATH_MAX if it is defined and use the size of the ->sun_path member
- * otherwise. This should be safe everywhere.
- */
-#ifndef UNIX_PATH_MAX
-#define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)0)->sun_path))
-#endif
-
 /* Userland defines for Linux DCCP support. */
 
-#ifndef IPPROTO_DCCP
-#define IPPROTO_DCCP 33 /**< IANA assigned value. */
-#endif
-
-#ifndef SOCK_DCCP
-#define SOCK_DCCP 6 /**< Linux socket type. */
-#endif
-
-#ifndef DCCP_SOCKOPT_RX_CCID
-/** Per-connection CCID support (set/get the RX CCID, since v2.6.30-rc1). */
-#define DCCP_SOCKOPT_RX_CCID 15
-#endif
-
 #ifndef SOL_DCCP
 #define SOL_DCCP 269 /**< Linux socket level. */
 #endif
 
-#ifndef DCCP_SOCKOPT_GET_CUR_MPS
-#define DCCP_SOCKOPT_GET_CUR_MPS  5 /**< Max packet size, RFC 4340, 14. */
-#endif
-
-#ifndef DCCP_SOCKOPT_AVAILABLE_CCIDS
-#define DCCP_SOCKOPT_AVAILABLE_CCIDS 12 /**< List of supported CCIDs. */
-#endif
-
-#ifndef DCCP_SOCKOPT_CCID
-#define DCCP_SOCKOPT_CCID 13 /**< Sets both TX/RX CCID. */
-#endif
-
-#ifndef DCCP_SOCKOPT_TX_CCID
-#define DCCP_SOCKOPT_TX_CCID 14 /**< Set/get the TX CCID. */
-#endif
-
 /** The maximum length of the host component in an URL. */
 #define MAX_HOSTLEN 256
 
-/**
- * Flowopts: Transport-layer independent encapsulation of socket options
- *           that need to be registered prior to setting up a connection.
- */
+/* Opaque, only known to net.c. */
 struct flowopts;
 
-extern struct flowopts *flowopt_new(void);
-extern void flowopt_add(struct flowopts *fo, int level, int opt,
+struct flowopts *flowopt_new(void);
+void flowopt_add(struct flowopts *fo, int level, int opt,
                const char *name, const void *val, int len);
 void flowopt_cleanup(struct flowopts *fo);
-/** Flowopt shortcut macros */
-#define OPT_ADD(fo, lev, opt, val, len)        flowopt_add(fo, lev, opt, #opt, val, len)
 
 /**
  * Functions to parse and validate (parts of) URLs.
  */
-extern char *parse_cidr(const char *cidr,
-                       char *addr, ssize_t addrlen, int32_t *netmask);
-extern char *parse_url(const char *url,
-                      char *host, ssize_t hostlen, int32_t *port);
+char *parse_cidr(const char *cidr,
+               char *addr, ssize_t addrlen, int32_t *netmask);
+char *parse_url(const char *url,
+               char *host, ssize_t hostlen, int32_t *port);
 char *format_url(const char *url, int default_port);
-extern const char *stringify_port(int port, const char *transport);
-/**
- * Ensure that string conforms to the IPv4 address format.
- *
- * \param address The address string to check.
- *
- * \return 1 if \a address conforms to the IPv4 address format, else 0.
- */
-_static_inline_ bool is_valid_ipv4_address(const char *address)
-{
-       struct in_addr test_it;
-
-       return inet_pton(AF_INET, address, &test_it) != 0;
-}
-
-/**
- * Ensure that string conforms to IPv6 address format.
- *
- * \param address The address string to check.
- *
- * \return 1 if string has a valid IPv6 address syntax, 0 if not.
- * \sa RFC 4291.
- */
-_static_inline_ bool is_valid_ipv6_address(const char *address)
-{
-       struct in6_addr test_it;
-
-       return inet_pton(AF_INET6, address, &test_it) != 0;
-}
+const char *stringify_port(int port, const char *transport);
 
 int lookup_address(unsigned l4type, bool passive, const char *host,
                int port_number, struct addrinfo **result);
@@ -114,10 +40,9 @@ int makesock(unsigned l4type, bool passive, const char *host,
 int makesock_addrinfo(unsigned l4type, bool passive, struct addrinfo *ai,
                struct flowopts *fo);
 
-static inline int para_connect_simple(unsigned l4type,
-                                     const char *host, uint16_t port)
+static inline int para_connect(unsigned l4type, const char *host, uint16_t port)
 {
-       return makesock(l4type, 0, host, port, NULL);
+       return makesock(l4type, false, host, port, NULL);
 }
 
 void extract_v4_addr(const struct sockaddr_storage *ss, struct in_addr *ia);
@@ -133,17 +58,17 @@ int para_listen(unsigned l4type, const char *addr, uint16_t port);
 int para_listen_simple(unsigned l4type, uint16_t port);
 
 /** Pretty-printing of IPv4/6 socket addresses */
-extern char *remote_name(int sockfd);
+char *remote_name(int sockfd);
 
 /**
  * Determining maximum payload (packet) size
  */
-extern int generic_max_transport_msg_size(int sockfd);
+int generic_max_transport_msg_size(int sockfd);
 
 int recv_bin_buffer(int fd, char *buf, size_t size);
 int recv_buffer(int fd, char *buf, size_t size);
 
-int para_accept(int fd, fd_set *rfds, void *addr, socklen_t size, int *new_fd);
+int para_accept(int fd, void *addr, socklen_t size, int *new_fd);
 int create_local_socket(const char *name);
 int connect_local_socket(const char *name);
 int recv_cred_buffer(int, char *, size_t);
@@ -152,8 +77,6 @@ ssize_t send_cred_buffer(int, char*);
 /**
  * Functions and definitions to support \p IPPROTO_DCCP
  */
-/** Estimated worst-case length of a DCCP header including options. */
-#define DCCP_MAX_HEADER                128
 /** Hardcoded maximum number of separate CCID modules compiled into a host. */
 #define DCCP_MAX_HOST_CCIDS    20
-extern int dccp_available_ccids(uint8_t **ccid_array);
+int dccp_available_ccids(uint8_t **ccid_array);
index 3e36bdd5b1e9d3615695f975541bf69c0cc05093..3a5c263cbc09169f15c973040ddc2e0118b27c9c 100644 (file)
@@ -167,7 +167,7 @@ int oac_get_file_info(char *map, size_t numbytes, struct afh_info *afhi,
        PARA_INFO_LOG("%" PRIu32 " seconds, %d frames/chunk\n",
                afhi->seconds_total, frames_per_chunk);
        ct_size = 250;
-       afhi->chunk_table = para_malloc(ct_size * sizeof(uint32_t));
+       afhi->chunk_table = arr_alloc(ct_size, sizeof(uint32_t));
        afhi->chunk_table[0] = 0;
        afhi->chunk_table[1] = afhi->header_len;
        oss.returned = afhi->header_len;
@@ -179,9 +179,9 @@ int oac_get_file_info(char *map, size_t numbytes, struct afh_info *afhi,
                        j++;
                        if (j >= ct_size) {
                                ct_size *= 2;
-                               afhi->chunk_table = para_realloc(
+                               afhi->chunk_table = arr_realloc(
                                        afhi->chunk_table,
-                                       ct_size * sizeof(uint32_t));
+                                       ct_size, sizeof(uint32_t));
                        }
                        afhi->chunk_table[j] = oss.returned;
                }
@@ -367,7 +367,7 @@ struct oac_custom_header {
  */
 struct oac_custom_header *oac_custom_header_new(void)
 {
-       return para_calloc(sizeof(struct oac_custom_header));
+       return zalloc(sizeof(struct oac_custom_header));
 }
 
 /**
index 708a27e52b68c1c2add01a3b3fe39f721485e39f..da70d4c04a2e2ec2dce8b955a5430442cd912519 100644 (file)
@@ -88,7 +88,7 @@ static const ov_callbacks ovc = {
 
 static void ogg_open(struct filter_node *fn)
 {
-       fn->private_data = para_calloc(sizeof(struct private_oggdec_data));
+       fn->private_data = zalloc(sizeof(struct private_oggdec_data));
        fn->min_iqs = 8000;
 }
 
@@ -121,7 +121,7 @@ static int ogg_init(struct filter_node *fn)
        struct btr_node *btrn = fn->btrn;
        int ret, oret;
        size_t iqs;
-       struct OggVorbis_File *vf = para_malloc(sizeof(*vf));
+       struct OggVorbis_File *vf = alloc(sizeof(*vf));
 
        PARA_NOTICE_LOG("iqs: %zu, min_iqs: %zu, opening ov callbacks\n",
                btr_get_input_queue_size(btrn), fn->min_iqs);
@@ -178,13 +178,13 @@ out:
 
 /**
   * Allocate chunks of this size and produce at most one chunk of output per
-  * ->post_select() invocation. If the buffer could only be filled partially
+  * ->post_monitor() invocation. If the buffer could only be filled partially
   * due to insufficient input being available, it is shrunk to the real output
   * size and the resized buffer is fed into the output queue.
   */
 #define OGGDEC_OUTPUT_CHUNK_SIZE (32 * 1024)
 
-static void ogg_pre_select(struct sched *s, void *context)
+static void ogg_pre_monitor(struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct private_oggdec_data *pod = fn->private_data;
@@ -201,7 +201,7 @@ static void ogg_pre_select(struct sched *s, void *context)
        sched_min_delay(s);
 }
 
-static int ogg_post_select(__a_unused struct sched *s, void *context)
+static int ogg_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct private_oggdec_data *pod = fn->private_data;
@@ -228,7 +228,7 @@ static int ogg_post_select(__a_unused struct sched *s, void *context)
                goto out;
        }
        have = 0;
-       buf = para_malloc(OGGDEC_OUTPUT_CHUNK_SIZE);
+       buf = alloc(OGGDEC_OUTPUT_CHUNK_SIZE);
        for (;;) {
                ret = ov_read(pod->vf, buf + have, OGGDEC_OUTPUT_CHUNK_SIZE - have,
                        ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL);
@@ -262,7 +262,7 @@ out:
 const struct filter lsg_filter_cmd_com_oggdec_user_data = {
        .open = ogg_open,
        .close = ogg_close,
-       .pre_select = ogg_pre_select,
-       .post_select = ogg_post_select,
+       .pre_monitor = ogg_pre_monitor,
+       .post_monitor = ogg_post_monitor,
        .execute = oggdec_execute
 };
index 0ad9d7db4e7f035dce140365b2b38abc144b6923..9d3ad577da4f5636f409fbacb0f1433ca0c720e1 100644 (file)
--- a/openssl.c
+++ b/openssl.c
@@ -283,7 +283,7 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
        unsigned char *blob;
        size_t decoded_size;
        int ret;
-       struct asymmetric_key *key = para_malloc(sizeof(*key));
+       struct asymmetric_key *key = alloc(sizeof(*key));
 
        ret = decode_public_key(key_file, &blob, &decoded_size);
        if (ret < 0)
@@ -324,7 +324,7 @@ int apc_priv_decrypt(const char *key_file, unsigned char *outbuf,
                return ret;
        if (inlen < 0)
                return -E_RSA;
-       priv = para_malloc(sizeof(*priv));
+       priv = alloc(sizeof(*priv));
        ret = get_private_key(key_file, &priv->rsa);
        if (ret < 0) {
                free(priv);
@@ -366,7 +366,7 @@ struct stream_cipher {
 
 struct stream_cipher *sc_new(const unsigned char *data, int len)
 {
-       struct stream_cipher *sc = para_malloc(sizeof(*sc));
+       struct stream_cipher *sc = alloc(sizeof(*sc));
 
        assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
        sc->aes = EVP_CIPHER_CTX_new();
@@ -390,7 +390,7 @@ static void aes_ctr128_crypt(EVP_CIPHER_CTX *ctx, struct iovec *src,
 
        *dst = (typeof(*dst)) {
                /* Add one for the terminating zero byte. */
-               .iov_base = para_malloc(inlen + 1),
+               .iov_base = alloc(inlen + 1),
                .iov_len = inlen
        };
        ret = EVP_EncryptUpdate(ctx, dst->iov_base, &outlen, src->iov_base, inlen);
@@ -414,3 +414,11 @@ void hash_function(const char *data, unsigned long len, unsigned char *hash)
        SHA1_Update(&c, data, len);
        SHA1_Final(hash, &c);
 }
+
+void hash2_function(const char *data, unsigned long len, unsigned char *hash)
+{
+       SHA256_CTX c;
+       SHA256_Init(&c);
+       SHA256_Update(&c, data, len);
+       SHA256_Final(hash, &c);
+}
index dca6cfbad1ce3ff8cdf6d820b01bf33bf9a02110..0a291bb16c18b6705b70ff0260e97b502068f500 100644 (file)
@@ -179,7 +179,7 @@ static size_t opus_make_meta_packet(struct taginfo *tags, char **result)
        }
        PARA_DEBUG_LOG("meta packet size: %zu bytes\n", sz);
        /* terminating zero byte for the last sprintf() */
-       buf = p = para_malloc(sz + 1);
+       buf = p = alloc(sz + 1);
        memcpy(p, OPUS_COMMENT_HEADER, strlen(OPUS_COMMENT_HEADER));
        p += strlen(OPUS_COMMENT_HEADER);
        write_u32(p, comment_sz);
index 10ed394d295072d909441fe211fbcdc695ff62d0..85287be0c813d4899b2f802735f2684338ea420c 100644 (file)
@@ -86,7 +86,7 @@ static int opusdec_execute(struct btr_node *btrn, const char *cmd,
 
 static void opusdec_open(struct filter_node *fn)
 {
-       struct opusdec_context *ctx = para_calloc(sizeof(*ctx));
+       struct opusdec_context *ctx = zalloc(sizeof(*ctx));
 
        ogg_sync_init(&ctx->oy);
        fn->private_data = ctx;
@@ -153,7 +153,7 @@ static void opusdec_add_output(short *pcm, int frames_available,
 
        if (tmp_skip > 0) {
                short *in = pcm + ctx->channels * tmp_skip;
-               short *out = para_malloc(bytes);
+               short *out = alloc(bytes);
                memcpy(out, in, bytes);
                free(pcm);
                pcm = out;
@@ -193,7 +193,7 @@ static int decode_packet(struct opusdec_context *ctx, ogg_packet *op,
        /* don't care for anything except opus eos */
        if (op->e_o_s && ctx->os.serialno == ctx->opus_serialno)
                ctx->eos = true;
-       output = para_malloc(sizeof(short) * MAX_FRAME_SIZE * ctx->channels);
+       output = arr_alloc(sizeof(short) * ctx->channels, MAX_FRAME_SIZE);
        ret = opus_multistream_decode(ctx->st, (unsigned char *)op->packet,
                op->bytes, output, MAX_FRAME_SIZE, 0);
        if (ret < 0) {
@@ -207,7 +207,7 @@ static int decode_packet(struct opusdec_context *ctx, ogg_packet *op,
 
 #define OPUSDEC_MAX_OUTPUT_SIZE (1024 * 1024)
 
-static int opusdec_post_select(__a_unused struct sched *s, void *context)
+static int opusdec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct opusdec_context *ctx = fn->private_data;
@@ -269,7 +269,7 @@ out:
        return ret;
 }
 
-static void opusdec_pre_select(struct sched *s, void *context)
+static void opusdec_pre_monitor(struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct opusdec_context *ctx = fn->private_data;
@@ -286,7 +286,7 @@ static void opusdec_pre_select(struct sched *s, void *context)
 const struct filter lsg_filter_cmd_com_opusdec_user_data = {
        .open = opusdec_open,
        .close = opusdec_close,
-       .pre_select = opusdec_pre_select,
-       .post_select = opusdec_post_select,
+       .pre_monitor = opusdec_pre_monitor,
+       .post_monitor = opusdec_post_monitor,
        .execute = opusdec_execute,
 };
index f80301e9df282c13ef0e7d8ab2a025dd61624ba7..0814336fdc20fcebce15f344910c658726be831c 100644 (file)
--- a/oss_mix.c
+++ b/oss_mix.c
@@ -56,7 +56,7 @@ static int oss_mix_open(const char *dev, struct mixer_handle **handle)
                PARA_ERROR_LOG("could not open %s\n", dev);
                return ret;
        }
-       h = para_malloc(sizeof(*h));
+       h = alloc(sizeof(*h));
        h->fd = ret;
        *handle = h;
        return 1;
index 311a514dc86ff6bfb01abf53d16a75672827af12..96d7b1871a941bff1662ae2a9512b85c79e7b4b9 100644 (file)
@@ -61,7 +61,7 @@ static int get_oss_format(enum sample_format sf)
        }
 }
 
-static void oss_pre_select(struct sched *s, void *context)
+static void oss_pre_monitor(struct sched *s, void *context)
 {
        struct writer_node *wn = context;
        struct private_oss_write_data *powd = wn->private_data;
@@ -71,7 +71,7 @@ static void oss_pre_select(struct sched *s, void *context)
                return;
        if (ret < 0 || !powd)
                return sched_min_delay(s);
-       para_fd_set(powd->fd, &s->wfds, &s->max_fileno);
+       sched_monitor_writefd(powd->fd, s);
 }
 
 static void oss_close(struct writer_node *wn)
@@ -101,7 +101,7 @@ static int oss_init(struct writer_node *wn, unsigned sample_rate,
 {
        int ret, format;
        unsigned ch, rate;
-       struct private_oss_write_data *powd = para_calloc(sizeof(*powd));
+       struct private_oss_write_data *powd = zalloc(sizeof(*powd));
        const char *dev = WRITE_CMD_OPT_STRING_VAL(OSS, DEVICE, wn->lpr);
 
        PARA_INFO_LOG("opening %s\n", dev);
@@ -178,7 +178,7 @@ err_free:
        return ret;
 }
 
-static int oss_post_select(__a_unused struct sched *s, void *context)
+static int oss_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct writer_node *wn = context;
        struct private_oss_write_data *powd = wn->private_data;
@@ -199,9 +199,15 @@ static int oss_post_select(__a_unused struct sched *s, void *context)
 
                if (sound_device_is_busy())
                        return 0;
-               get_btr_sample_rate(btrn, &rate);
-               get_btr_channels(btrn, &ch);
-               get_btr_sample_format(btrn, &format);
+               ret = get_btr_sample_rate(btrn, &rate);
+               if (ret < 0)
+                       goto out;
+               ret = get_btr_channels(btrn, &ch);
+               if (ret < 0)
+                       goto out;
+               ret = get_btr_sample_format(btrn, &format);
+               if (ret < 0)
+                       goto out;
                ret = oss_init(wn, rate, ch, format);
                if (ret < 0)
                        goto out;
@@ -216,7 +222,7 @@ static int oss_post_select(__a_unused struct sched *s, void *context)
                goto out;
        }
        ret = 0;
-       if (!FD_ISSET(powd->fd, &s->wfds))
+       if (!sched_write_ok(powd->fd, s))
                goto out;
        /* get maximal number of bytes that can be written */
        ret = ioctl(powd->fd, SNDCTL_DSP_GETOSPACE, &abi);
@@ -239,7 +245,7 @@ out:
 }
 
 const struct writer lsg_write_cmd_com_oss_user_data = {
-       .pre_select = oss_pre_select,
-       .post_select = oss_post_select,
+       .pre_monitor = oss_pre_monitor,
+       .post_monitor = oss_post_monitor,
        .close = oss_close,
 };
diff --git a/para.h b/para.h
index b406818bd52a27bfc1794e0c95d8f9c0e9ef12a0..280c282323db369e7d0e84173324016aa425a92d 100644 (file)
--- a/para.h
+++ b/para.h
@@ -20,6 +20,8 @@
 #include <stdbool.h>
 #include <inttypes.h>
 #include <sys/uio.h>
+#include <poll.h>
+
 #include "gcc-compat.h"
 
 /** used in various contexts */
@@ -44,7 +46,6 @@
        typeof(x) _x = (x); \
        _x > 0? _x : -_x; })
 
-
 extern __printf_2_3 void (*para_log)(int, const char*, ...);
 /**
  * Define a standard log function that always writes to stderr.
@@ -221,6 +222,7 @@ enum loglevels {LOGLEVELS, NUM_LOGLEVELS};
 #define PARA_CRIT_LOG(f,...) para_log(LL_CRIT, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 #define PARA_EMERG_LOG(f,...) para_log(LL_EMERG, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 
+/** \cond status_items */
 #define STATUS_ITEMS \
        STATUS_ITEM(basename) \
        STATUS_ITEM(status) \
@@ -268,6 +270,7 @@ enum loglevels {LOGLEVELS, NUM_LOGLEVELS};
 enum status_items {STATUS_ITEMS NUM_STAT_ITEMS};
 #undef STATUS_ITEM
 #define STATUS_ITEM(_name) #_name,
+/** \endcond status items */
 
 extern const char *status_item_list[];
 /** Loop over each status item. */
diff --git a/play.c b/play.c
index ffdc8555ce6686833b846f9610a3d81f6adf03cb..262f69ee4dc3d57686904fafb5597e1d955a189d 100644 (file)
--- a/play.c
+++ b/play.c
@@ -50,7 +50,7 @@ static struct lls_parse_result *play_lpr;
  * Describes a request to change the state of para_play.
  *
  * There is only one variable of this type: \a rq of the global play task
- * structure. Command handlers only set this variable and the post_select()
+ * structure. Command handlers only set this variable and the post_monitor()
  * function of the play task investigates its value during each iteration of
  * the scheduler run and performs the actual work.
  */
@@ -117,7 +117,7 @@ INIT_STDERR_LOGGING(loglevel);
 
 char *stat_item_values[NUM_STAT_ITEMS] = {NULL};
 
-static struct sched sched = {.max_fileno = 0};
+static struct sched sched;
 static struct play_task play_task, *pt = &play_task;
 
 #define AFH_RECV_CMD (lls_cmd(LSG_RECV_CMD_CMD_AFH, recv_cmd_suite))
@@ -192,6 +192,7 @@ static char get_playback_state(void)
        assert(false);
 };
 
+/* returns number of milliseconds */
 static long unsigned get_play_time(void)
 {
        char state = get_playback_state();
@@ -201,16 +202,16 @@ static long unsigned get_play_time(void)
                return 0;
        if (pt->num_chunks == 0 || pt->seconds == 0)
                return 0;
-       /* where the stream started (in seconds) */
-       result = pt->start_chunk * pt->seconds / pt->num_chunks;
+       /* where the stream started (in milliseconds) */
+       result = 1000ULL * pt->start_chunk * pt->seconds / pt->num_chunks;
        if (pt->wn.btrn) { /* Add the uptime of the writer node */
                struct timeval diff = {.tv_sec = 0}, wstime;
                btr_get_node_start(pt->wn.btrn, &wstime);
                if (wstime.tv_sec > 0)
                        tv_diff(now, &wstime, &diff);
-               result += diff.tv_sec;
+               result += tv2ms(&diff);
        }
-       result = PARA_MIN(result, pt->seconds);
+       result = PARA_MIN(result, pt->seconds * 1000);
        result = PARA_MAX(result, 0UL);
        return result;
 }
@@ -288,7 +289,7 @@ static int shuffle_compare(__a_unused const void *a, __a_unused const void *b)
 static void init_shuffle_map(void)
 {
        unsigned n, num_inputs = lls_num_inputs(play_lpr);
-       shuffle_map = para_malloc(num_inputs * sizeof(unsigned));
+       shuffle_map = arr_alloc(num_inputs, sizeof(unsigned));
        for (n = 0; n < num_inputs; n++)
                shuffle_map[n] = n;
        if (!OPT_GIVEN(RANDOMIZE))
@@ -405,16 +406,16 @@ static int load_file(void)
        pt->rn.task = task_register(
                &(struct task_info) {
                        .name = lls_command_name(AFH_RECV_CMD),
-                       .pre_select = AFH_RECV->pre_select,
-                       .post_select = AFH_RECV->post_select,
+                       .pre_monitor = AFH_RECV->pre_monitor,
+                       .post_monitor = AFH_RECV->post_monitor,
                        .context = &pt->rn
                }, &sched);
        sprintf(buf, "%s decoder", af);
        pt->fn.task = task_register(
                &(struct task_info) {
                        .name = buf,
-                       .pre_select = decoder->pre_select,
-                       .post_select = decoder->post_select,
+                       .pre_monitor = decoder->pre_monitor,
+                       .post_monitor = decoder->post_monitor,
                        .context = &pt->fn
                }, &sched);
        register_writer_node(&pt->wn, pt->fn.btrn, &sched);
@@ -591,7 +592,7 @@ static char *get_user_key_map_seq(int key)
        if (!p)
                return NULL;
        len = p - kma;
-       result = para_malloc(len + 1);
+       result = alloc(len + 1);
        memcpy(result, kma, len);
        result[len] = '\0';
        return result;
@@ -611,7 +612,7 @@ static char *get_key_map_seq_safe(int key)
 
        if (len == 1 && isprint(*seq))
                return seq;
-       sseq = para_malloc(2 + 2 * len + 1);
+       sseq = alloc(2 + 2 * len + 1);
        sseq[0] = '0';
        sseq[1] = 'x';
        for (n = 0; n < len; n++) {
@@ -651,7 +652,7 @@ static char **get_mapped_keyseqs(void)
        char **result;
        int i;
 
-       result = para_malloc((NUM_MAPPED_KEYS + 1) * sizeof(char *));
+       result = arr_alloc(NUM_MAPPED_KEYS + 1, sizeof(char *));
        FOR_EACH_MAPPED_KEY(i) {
                char *seq = get_key_map_seq(i);
                result[i] = seq;
@@ -840,20 +841,21 @@ EXPORT_PLAY_CMD_HANDLER(play);
 static int com_pause(__a_unused struct lls_parse_result *lpr)
 {
        char state;
-       long unsigned seconds, ss;
+       uint64_t ms;
+       unsigned long cn; /* chunk num */
 
        state = get_playback_state();
        pt->playing = false;
        if (state != 'P')
                return 0;
-       seconds = get_play_time();
+       ms = get_play_time();
        pt->playing = false;
-       ss = 0;
+       cn = 0;
        if (pt->seconds > 0)
-               ss = seconds * pt->num_chunks / pt->seconds + 1;
-       ss = PARA_MAX(ss, 0UL);
-       ss = PARA_MIN(ss, pt->num_chunks);
-       pt->start_chunk = ss;
+               cn = ms * pt->num_chunks / pt->seconds / 1000 + 1;
+       cn = PARA_MIN(cn, pt->num_chunks);
+       pt->start_chunk = cn;
+       pt->rq = CRT_REPOS;
        kill_stream();
        return 0;
 }
@@ -952,7 +954,7 @@ static int com_ff(struct lls_parse_result *lpr)
                return ret;
        if (pt->playing && !pt->fn.btrn)
                return 0;
-       seconds += get_play_time();
+       seconds += (get_play_time() + 500) / 1000;
        seconds = PARA_MIN(seconds, (typeof(seconds))pt->seconds - 4);
        seconds = PARA_MAX(seconds, 0);
        pt->start_chunk = pt->num_chunks * seconds / pt->seconds;
@@ -1074,7 +1076,7 @@ static void session_open(void)
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGWINCH, &act, NULL);
-       sched.select_function = i9e_select;
+       sched.poll_function = i9e_poll;
 
        ici.bound_keyseqs = get_mapped_keyseqs();
        pt->btrn = ici.producer = btr_new_node(&(struct btr_node_description)
@@ -1102,22 +1104,22 @@ static void session_update_time_string(char *str, unsigned len)
                if (btr_get_input_queue_size(pt->btrn) > 0)
                        return;
        }
-       ie9_print_status_bar(str, len);
+       i9e_print_status_bar(str, len);
 }
 
 /*
  * If we are about to die we must call i9e_close() to reset the terminal.
  * However, i9e_close() must be called in *this* context, i.e. from
- * play_task.post_select() rather than from i9e_post_select(), because
+ * play_task.post_monitor() rather than from i9e_post_monitor(), because
  * otherwise i9e would access freed memory upon return. So the play task must
  * stay alive until the i9e task terminates.
  *
  * We achieve this by sending a fake SIGTERM signal via i9e_signal_dispatch()
- * and reschedule. In the next iteration, i9e->post_select returns an error and
+ * and reschedule. In the next iteration, i9e->post_monitor returns an error and
  * terminates. Subsequent calls to i9e_get_error() then return negative and we
  * are allowed to call i9e_close() and terminate as well.
  */
-static int session_post_select(__a_unused struct sched *s)
+static int session_post_monitor(__a_unused struct sched *s)
 {
        int ret;
 
@@ -1140,11 +1142,11 @@ static int session_post_select(__a_unused struct sched *s)
 
 #else /* HAVE_READLINE */
 
-static int session_post_select(struct sched *s)
+static int session_post_monitor(struct sched *s)
 {
        char c;
 
-       if (!FD_ISSET(STDIN_FILENO, &s->rfds))
+       if (!sched_read_ok(STDIN_FILENO, s))
                return 0;
        if (read(STDIN_FILENO, &c, 1))
                do_nothing;
@@ -1163,11 +1165,11 @@ static void session_update_time_string(char *str, __a_unused unsigned len)
 }
 #endif /* HAVE_READLINE */
 
-static void play_pre_select(struct sched *s, __a_unused void *context)
+static void play_pre_monitor(struct sched *s, __a_unused void *context)
 {
        char state;
 
-       para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(STDIN_FILENO, s);
        state = get_playback_state();
        if (state == 'R' || state == 'F' || state == 'X')
                return sched_min_delay(s);
@@ -1187,7 +1189,7 @@ static unsigned get_time_string(char **result)
        length = pt->seconds;
        if (length == 0)
                return xasprintf(result, "0:00 [0:00] (0%%/0:00)");
-       seconds = get_play_time();
+       seconds = (get_play_time() + 500) / 1000;
        return xasprintf(result, "#%u: %d:%02d [%d:%02d] (%d%%/%d:%02d) %s",
                pt->current_file,
                seconds / 60,
@@ -1201,7 +1203,7 @@ static unsigned get_time_string(char **result)
        );
 }
 
-static int play_post_select(struct sched *s, __a_unused void *context)
+static int play_post_monitor(struct sched *s, __a_unused void *context)
 {
        int ret;
 
@@ -1210,7 +1212,7 @@ static int play_post_select(struct sched *s, __a_unused void *context)
                pt->rq = CRT_TERM_RQ;
                return 0;
        }
-       ret = session_post_select(s);
+       ret = session_post_monitor(s);
        if (ret < 0)
                goto out;
        if (!pt->wn.btrn && !pt->fn.btrn) {
@@ -1254,18 +1256,18 @@ int main(int argc, char *argv[])
        int ret;
        unsigned num_inputs;
 
-       sched.default_timeout.tv_sec = 5;
+       sched.default_timeout = 5000;
        parse_config_or_die(argc, argv);
        session_open();
        num_inputs = lls_num_inputs(play_lpr);
        init_shuffle_map();
-       pt->invalid = para_calloc(sizeof(*pt->invalid) * num_inputs);
+       pt->invalid = arr_zalloc(num_inputs, sizeof(*pt->invalid));
        pt->rq = CRT_FILE_CHANGE;
        pt->playing = true;
        pt->task = task_register(&(struct task_info){
                .name = "play",
-               .pre_select = play_pre_select,
-               .post_select = play_post_select,
+               .pre_monitor = play_pre_monitor,
+               .post_monitor = play_post_monitor,
                .context = pt,
        }, &sched);
        ret = schedule(&sched);
index 9a801900c157e1da2da979c92d7bd60d03e21b9c..28b710108d435bd40d5970ca117b103d6fb0b1a3 100644 (file)
@@ -22,7 +22,7 @@ struct private_prebuffer_data {
        struct timeval barrier;
 };
 
-static void prebuffer_pre_select(struct sched *s, void *context)
+static void prebuffer_pre_monitor(struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
@@ -50,7 +50,7 @@ static void prebuffer_close(struct filter_node *fn)
        free(fn->private_data);
 }
 
-static int prebuffer_post_select(__a_unused struct sched *s, void *context)
+static int prebuffer_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
@@ -80,13 +80,13 @@ fail:
 
 static void prebuffer_open(struct filter_node *fn)
 {
-       struct private_prebuffer_data *ppd = para_calloc(sizeof(*ppd));
+       struct private_prebuffer_data *ppd = zalloc(sizeof(*ppd));
        fn->private_data = ppd;
 }
 
 const struct filter lsg_filter_cmd_com_prebuffer_user_data = {
        .open = prebuffer_open,
        .close = prebuffer_close,
-       .pre_select = prebuffer_pre_select,
-       .post_select = prebuffer_post_select,
+       .pre_monitor = prebuffer_pre_monitor,
+       .post_monitor = prebuffer_post_monitor,
 };
diff --git a/recv.c b/recv.c
index 10d55d218071ccf6a314fc87d4e5f78001012db4..68417187348b058b375993b9a74eb6c2ca7d826b 100644 (file)
--- a/recv.c
+++ b/recv.c
@@ -97,13 +97,12 @@ int main(int argc, char *argv[])
        stdout_task_register(&sot, &s);
 
        ti.name = lls_command_name(cmd);
-       ti.pre_select = r->pre_select;
-       ti.post_select = r->post_select;
+       ti.pre_monitor = r->pre_monitor;
+       ti.post_monitor = r->post_monitor;
        ti.context = &rn;
        rn.task = task_register(&ti, &s);
 
-       s.default_timeout.tv_sec = 1;
-       s.default_timeout.tv_usec = 0;
+       s.default_timeout = 1000;
        ret = schedule(&s);
        sched_shutdown(&s);
        r->close(&rn);
diff --git a/recv.h b/recv.h
index 36b0f1db62e348c9ab7ccdfc3b32ba656d698c5f..391395b241d67abf12eb70996a10ee7950519087 100644 (file)
--- a/recv.h
+++ b/recv.h
@@ -21,11 +21,11 @@ struct receiver_node {
        /**
         * The file descriptor to receive the stream.
         *
-        * The pre_select function of the receiver adds this file descriptor to
+        * The pre_monitor function of the receiver adds this file descriptor to
         * the set of file descriptors which are watched for readability or
         * writability, depending on the state of the connection (if any).
         *
-        * If \a fd is readable, the post_select function of the receiver reads
+        * If \a fd is readable, the post_monitor function of the receiver reads
         * data from this fd into the buffer pool area of \a btrp.
         *
         * \sa \ref receiver.
@@ -34,9 +34,18 @@ struct receiver_node {
 };
 
 /**
- * Describes one supported paraslash receiver.
+ * Describes a possible data source for audio streams.
  *
- * \sa \ref http_recv.c, \ref udp_recv.c.
+ * A paraslash receiver is a modular piece of software which is capable of
+ * receiving an audio data stream from a data source. Received audio data is
+ * fed to consumers through the buffer tree mechanism.
+ *
+ * This structure contains the methods which have to be implemented by each
+ * receiver.
+ *
+ * \sa \ref http_recv.c, \ref udp_recv.c, \ref dccp_recv.c, \ref afh_recv.c,
+ * struct \ref receiver_node, struct \ref filter, struct \ref writer, struct
+ * \ref sched.
  */
 struct receiver {
        /**
@@ -45,8 +54,6 @@ struct receiver {
         * This should allocate the output buffer of the given receiver node
         * and prepare it for retrieving the audio stream according to the
         * configuration stored in rn->lpr.
-        *
-        * \sa struct \ref receiver_node.
         */
        int (*open)(struct receiver_node *rn);
        /**
@@ -58,31 +65,10 @@ struct receiver {
         * \sa \ref receiver_node.
         */
        void (*close)(struct receiver_node *rn);
-       /**
-        * Add file descriptors to fd_sets and compute timeout for select(2).
-        *
-        * If this is not NULL, the function is called in each iteration of the
-        * scheduler's select loop. The receiver may define it to add file
-        * descriptors to the file descriptor sets given by s. Those will be
-        * monitored in the subsequent call to select(2). The function may also
-        * lower the timeout value of s to make select(2) return earlier if no
-        * file descriptors are ready for I/O.
-        *
-        * \sa select(2), \ref time.c, struct \ref sched.
-        */
-       void (*pre_select)(struct sched *s, void *context);
-       /**
-        * Evaluate the result from select(2).
-        *
-        * This is called after the call to select(2). It should check all file
-        * descriptors which were added to any of the fd sets in the previous
-        * call to ->pre_select() and perform (non-blocking) I/O operations on
-        * those fds which are ready for I/O, for example in order to establish
-        * a connection or to receive a part of the audio stream.
-        *
-        * \sa select(2), struct \ref receiver.
-        */
-       int (*post_select)(struct sched *s, void *context);
+       /** Ask the scheduler to monitor receive fds. */
+       void (*pre_monitor)(struct sched *s, void *context);
+       /** Receive data and make it available to consumers. */
+       int (*post_monitor)(struct sched *s, void *context);
        /**
         * Answer a buffer tree query.
         *
@@ -110,4 +96,4 @@ struct receiver {
 
 int check_receiver_arg(const char *ra, struct lls_parse_result **lprp);
 void print_receiver_helps(bool detailed);
-int generic_recv_pre_select(struct sched *s, struct receiver_node *rn);
+int generic_recv_pre_monitor(struct sched *s, struct receiver_node *rn);
index 31fd81f1ec52c3d0b6204e8b3663a7d36e14fbb3..1939300a7c946d68d1b324ee6beb8c0bf146893b 100644 (file)
@@ -41,7 +41,7 @@ int check_receiver_arg(const char *ra, struct lls_parse_result **lprp)
        *lprp = NULL;
        if (!ra || !*ra) {
                argc = 1;
-               argv = para_malloc(2 * sizeof(char*));
+               argv = alloc(2 * sizeof(char*));
                argv[0] = para_strdup("http");
                argv[1] = NULL;
        } else {
@@ -98,19 +98,19 @@ void print_receiver_helps(bool detailed)
 }
 
 /**
- * Simple pre-select hook, used by all receivers.
+ * Request a minimal timeout in case of buffer tree errors.
  *
- * \param s Scheduler info.
- * \param rn The receiver node.
+ * \param s The scheduler instance.
+ * \param rn The buffer tree node is derived from this.
  *
- * This requests a minimal delay from the scheduler if the status of the buffer
- * tree node indicates an error/eof condition. No file descriptors are added to
- * the fd sets of \a s.
+ * If the buffer tree node of the given receiver node is in error or EOF state,
+ * a minimal I/O timeout is requested from the scheduler. Otherwise, the
+ * function does nothing. No file descriptors are asked to be monitored.
  *
- * \return The status of the btr node of the receiver node, i.e. the return
- * value of the underlying call to \ref btr_node_status().
+ * \return The status of of the receiver node's buffer tree node. That is, the
+ * return value of the underlying call to \ref btr_node_status().
  */
-int generic_recv_pre_select(struct sched *s, struct receiver_node *rn)
+int generic_recv_pre_monitor(struct sched *s, struct receiver_node *rn)
 {
        int ret = btr_node_status(rn->btrn, 0, BTR_NT_ROOT);
 
index bbdda51c525630c6d1411dfa547193f8ccdae9ce..bf28e975de2720f001be95ee06b269a1a8ecdd2c 100644 (file)
@@ -51,7 +51,7 @@ static void resample_close(struct filter_node *fn)
 
 static void resample_open(struct filter_node *fn)
 {
-       struct resample_context *ctx = para_calloc(sizeof(*ctx));
+       struct resample_context *ctx = zalloc(sizeof(*ctx));
        struct btr_node *btrn = fn->btrn;
        struct wav_params wp;
 
@@ -62,7 +62,7 @@ static void resample_open(struct filter_node *fn)
        btr_log_tree(btr_parent(btr_parent(btrn)), LL_INFO);
 }
 
-static void resample_pre_select(struct sched *s, void *context)
+static void resample_pre_monitor(struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct resample_context *ctx = fn->private_data;
@@ -70,7 +70,7 @@ static void resample_pre_select(struct sched *s, void *context)
 
        if (ret != 0)
                return sched_min_delay(s);
-       check_wav_pre_select(s, ctx->cwc);
+       check_wav_pre_monitor(s, ctx->cwc);
 }
 
 static int get_btr_val(const char *what, struct btr_node *btrn)
@@ -167,10 +167,10 @@ static int resample_frames(int16_t *in, size_t num_frames, bool have_more,
        data.output_frames = num_frames * ctx->ratio + 1;
        out_samples = data.output_frames * ctx->channels;
 
-       in_float = para_malloc(num_samples * sizeof(float));
+       in_float = arr_alloc(num_samples, sizeof(float));
        src_short_to_float_array(in, in_float, num_samples);
        data.data_in = in_float;
-       data.data_out = para_malloc(out_samples * sizeof(float));
+       data.data_out = arr_alloc(out_samples, sizeof(float));
        ret = src_process(ctx->src_state, &data);
        free(in_float);
        if (ret != 0) {
@@ -179,7 +179,7 @@ static int resample_frames(int16_t *in, size_t num_frames, bool have_more,
                return -E_LIBSAMPLERATE;
        }
        out_samples = data.output_frames_gen * ctx->channels;
-       out = para_malloc(out_samples * sizeof(short));
+       out = arr_alloc(out_samples, sizeof(short));
        src_float_to_short_array(data.data_out, out, out_samples);
        free(data.data_out);
        *result = out;
@@ -187,7 +187,7 @@ static int resample_frames(int16_t *in, size_t num_frames, bool have_more,
        return data.input_frames_used;
 }
 
-static int resample_post_select(__a_unused struct sched *s, void *context)
+static int resample_post_monitor(__a_unused struct sched *s, void *context)
 {
        int ret;
        struct filter_node *fn = context;
@@ -197,7 +197,7 @@ static int resample_post_select(__a_unused struct sched *s, void *context)
        size_t in_bytes, num_frames;
        bool have_more;
 
-       ret = check_wav_post_select(ctx->cwc);
+       ret = check_wav_post_monitor(ctx->cwc);
        if (ret < 0)
                goto out;
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
@@ -236,7 +236,7 @@ out:
        if (ret < 0) {
                btr_remove_node(&fn->btrn);
                /* This releases the check_wav btr node */
-               check_wav_post_select(ctx->cwc);
+               check_wav_post_monitor(ctx->cwc);
        }
        return ret;
 }
@@ -277,8 +277,8 @@ static void resample_teardown(__a_unused const struct lls_parse_result *lpr,
 const struct filter lsg_filter_cmd_com_resample_user_data = {
        .setup = resample_setup,
        .open = resample_open,
-       .pre_select = resample_pre_select,
-       .post_select = resample_post_select,
+       .pre_monitor = resample_pre_monitor,
+       .post_monitor = resample_post_monitor,
        .close = resample_close,
        .teardown = resample_teardown,
        .execute = resample_execute
index 76e2d7afc8ab7caea1207a36c37c8b6452af2816..0e706f0f1ad6e906732f3bc95e8b653da53f9a6d 100644 (file)
@@ -41,8 +41,8 @@ struct ringbuffer
  */
 struct ringbuffer *ringbuffer_new(unsigned size)
 {
-       struct ringbuffer *rb = para_calloc(sizeof(struct ringbuffer));
-       rb->entries = para_calloc(size * sizeof(void *));
+       struct ringbuffer *rb = zalloc(sizeof(struct ringbuffer));
+       rb->entries = zalloc(size * sizeof(void *));
        rb->size = size;
        return rb;
 }
diff --git a/sched.c b/sched.c
index a2903940fdaea1b24d6a49cfc2f54766c136070f..c786c9f26f9b5fc47cfd3c8d883671c5433bd048 100644 (file)
--- a/sched.c
+++ b/sched.c
@@ -17,9 +17,9 @@
  * The possible states of a task.
  *
  * In addition to the states listed here, a task may also enter zombie state.
- * This happens when its ->post_select function returns negative, the ->status
+ * This happens when its ->post_monitor function returns negative, the ->status
  * field is then set to this return value. Such tasks are not scheduled any
- * more (i.e. ->pre_select() and ->post_select() are no longer called), but
+ * more (i.e. ->pre_monitor() and ->post_monitor() are no longer called), but
  * they stay on the scheduler task list until \ref task_reap() or
  * \ref sched_shutdown() is called.
  */
@@ -46,7 +46,7 @@ struct task {
 static struct timeval now_struct;
 const struct timeval *now = &now_struct;
 
-static void sched_preselect(struct sched *s)
+static void sched_pre_monitor(struct sched *s)
 {
        struct task *t, *tmp;
 
@@ -55,8 +55,8 @@ static void sched_preselect(struct sched *s)
                        continue;
                if (t->notification != 0)
                        sched_min_delay(s);
-               if (t->info.pre_select)
-                       t->info.pre_select(s, t->info.context);
+               if (t->info.pre_monitor)
+                       t->info.pre_monitor(s, t->info.context);
        }
 }
 
@@ -72,29 +72,29 @@ static void unlink_and_free_task(struct task *t)
 }
 
 //#define SCHED_DEBUG 1
-static inline void call_post_select(struct sched *s, struct task *t)
+static inline void call_post_monitor(struct sched *s, struct task *t)
 {
        int ret;
 
 #ifndef SCHED_DEBUG
-       ret = t->info.post_select(s, t->info.context);
+       ret = t->info.post_monitor(s, t->info.context);
 #else
        struct timeval t1, t2, diff;
        unsigned long pst;
 
        clock_get_realtime(&t1);
-       ret = t->info.post_select(s, t->info.context);
+       ret = t->info.post_monitor(s, t->info.context);
        clock_get_realtime(&t2);
        tv_diff(&t1, &t2, &diff);
        pst = tv2ms(&diff);
        if (pst > 50)
-               PARA_WARNING_LOG("%s: post_select time: %lums\n",
+               PARA_WARNING_LOG("%s: post_monitor time: %lums\n",
                        t->name, pst);
 #endif
        t->status = ret < 0? ret : TS_RUNNING;
 }
 
-static unsigned sched_post_select(struct sched *s)
+static unsigned sched_post_monitor(struct sched *s)
 {
        struct task *t, *tmp;
        unsigned num_running_tasks = 0;
@@ -103,7 +103,7 @@ static unsigned sched_post_select(struct sched *s)
                if (t->status == TS_DEAD) /* task has been reaped */
                        unlink_and_free_task(t);
                else if (t->status == TS_RUNNING) {
-                       call_post_select(s, t); /* sets t->status */
+                       call_post_monitor(s, t); /* sets t->status */
                        t->notification = 0;
                        if (t->status == TS_RUNNING)
                                num_running_tasks++;
@@ -117,13 +117,13 @@ static unsigned sched_post_select(struct sched *s)
  *
  * \param s Pointer to the scheduler struct.
  *
- * This function updates the global \a now pointer, calls all registered
- * pre_select hooks which may set the timeout and add any file descriptors to
- * the fd sets of \a s.  Next, it calls para_select() and makes the result available
- * to the registered tasks by calling their post_select hook.
+ * This function updates the global now pointer, calls all registered
+ * pre_monitor hooks which may set the timeout and add any file descriptors to
+ * the pollfd array. Next, it calls the poll function and makes the result
+ * available to the registered tasks by calling their post_monitor hook.
  *
  * \return Zero if no more tasks are left in the task list, negative if the
- * select function returned an error.
+ * poll function returned an error.
  *
  * \sa \ref now.
  */
@@ -132,31 +132,20 @@ int schedule(struct sched *s)
        int ret;
        unsigned num_running_tasks;
 
-       if (!s->select_function)
-               s->select_function = para_select;
+       if (!s->poll_function)
+               s->poll_function = xpoll;
 again:
-       FD_ZERO(&s->rfds);
-       FD_ZERO(&s->wfds);
-       s->select_timeout = s->default_timeout;
-       s->max_fileno = -1;
+       s->num_pfds = 0;
+       if (s->pidx)
+               memset(s->pidx, 0xff, s->pidx_array_len * sizeof(unsigned));
+       s->timeout = s->default_timeout;
        clock_get_realtime(&now_struct);
-       sched_preselect(s);
-       ret = s->select_function(s->max_fileno + 1, &s->rfds, &s->wfds,
-               &s->select_timeout);
+       sched_pre_monitor(s);
+       ret = s->poll_function(s->pfd, s->num_pfds, s->timeout);
        if (ret < 0)
                return ret;
-       if (ret == 0) {
-               /*
-                * APUE: Be careful not to check the descriptor sets on return
-                * unless the return value is greater than zero. The return
-                * state of the descriptor sets is implementation dependent if
-                * either a signal is caught or the timer expires.
-                */
-               FD_ZERO(&s->rfds);
-               FD_ZERO(&s->wfds);
-       }
        clock_get_realtime(&now_struct);
-       num_running_tasks = sched_post_select(s);
+       num_running_tasks = sched_post_monitor(s);
        if (num_running_tasks == 0)
                return 0;
        goto again;
@@ -197,7 +186,7 @@ int task_reap(struct task **tptr)
        /*
         * With list_for_each_entry_safe() it is only safe to remove the
         * _current_ list item. Since we are being called from the loop in
-        * schedule() via some task's ->post_select() function, freeing the
+        * schedule() via some task's ->post_monitor() function, freeing the
         * given task here would result in use-after-free bugs in schedule().
         * So we only set the task status to TS_DEAD which tells schedule() to
         * free the task in the next iteration of its loop.
@@ -226,6 +215,8 @@ void sched_shutdown(struct sched *s)
                                t->name);
                unlink_and_free_task(t);
        }
+       free(s->pfd);
+       free(s->pidx);
 }
 
 /**
@@ -239,12 +230,12 @@ void sched_shutdown(struct sched *s)
  */
 struct task *task_register(struct task_info *info, struct sched *s)
 {
-       struct task *t = para_malloc(sizeof(*t));
+       struct task *t = alloc(sizeof(*t));
 
-       assert(info->post_select);
+       assert(info->post_monitor);
 
        if (!s->task_list.next)
-               INIT_LIST_HEAD(&s->task_list);
+               init_list_head(&s->task_list);
 
        t->info = *info;
        t->name = para_strdup(info->name);
@@ -288,14 +279,14 @@ char *get_task_list(struct sched *s)
  * \param err A positive error code.
  *
  * Tasks which honor notifications are supposed to call \ref
- * task_get_notification() in their post_select function and act on the
+ * task_get_notification() in their post_monitor function and act on the
  * returned notification value.
  *
- * If the scheduler detects during its pre_select loop that at least one task
- * has been notified, the loop terminates, and the post_select methods of all
+ * If the scheduler detects during its pre_monitor loop that at least one task
+ * has been notified, the loop terminates, and the post_monitor methods of all
  * taks are immediately called again.
  *
- * The notification for a task is reset after the call to its post_select
+ * The notification for a task is reset after the call to its post_monitor
  * method.
  *
  * \sa \ref task_get_notification().
@@ -316,7 +307,7 @@ void task_notify(struct task *t, int err)
  *
  * \return The notification value. If this is negative, the task has been
  * notified by another task. Tasks are supposed to check for notifications by
- * calling this function from their post_select method.
+ * calling this function from their post_monitor method.
  *
  * \sa \ref task_notify().
  */
@@ -362,43 +353,43 @@ void task_notify_all(struct sched *s, int err)
 }
 
 /**
- * Set the select timeout to the minimal possible value.
+ * Set the I/O timeout to the minimal possible value.
  *
  * \param s Pointer to the scheduler struct.
  *
- * This causes the next select() call to return immediately.
+ * This causes the next poll() call to return immediately.
  */
 void sched_min_delay(struct sched *s)
 {
-       s->select_timeout.tv_sec = s->select_timeout.tv_usec = 0;
+       s->timeout = 0;
 }
 
 /**
- * Impose an upper bound for the timeout of the next select() call.
+ * Impose an upper bound for the I/O timeout.
  *
  * \param to Maximal allowed timeout.
  * \param s Pointer to the scheduler struct.
  *
- * If the current scheduler timeout is already smaller than \a to, this
- * function does nothing. Otherwise the timeout for the next select() call is
- * set to the given value.
+ * If the current I/O timeout is already smaller than to, this function does
+ * nothing. Otherwise the timeout is set to the given value.
  *
  * \sa \ref sched_request_timeout_ms().
  */
 void sched_request_timeout(struct timeval *to, struct sched *s)
 {
-       if (tv_diff(&s->select_timeout, to, NULL) > 0)
-               s->select_timeout = *to;
+       long unsigned ms = tv2ms(to);
+       if (s->timeout > ms)
+               s->timeout = ms;
 }
 
 /**
- * Force the next select() call to return before the given amount of milliseconds.
+ * Bound the I/O timeout to at most the given amount of milliseconds.
  *
  * \param ms The maximal allowed timeout in milliseconds.
  * \param s Pointer to the scheduler struct.
  *
- * Like sched_request_timeout() this imposes an upper bound on the timeout
- * value for the next select() call.
+ * Like \ref sched_request_timeout() this imposes an upper bound on the I/O
+ * timeout.
  */
 void sched_request_timeout_ms(long unsigned ms, struct sched *s)
 {
@@ -408,13 +399,13 @@ void sched_request_timeout_ms(long unsigned ms, struct sched *s)
 }
 
 /**
- * Force the next select() call to return before the given future time.
+ * Bound the I/O timeout by an absolute time in the future.
  *
- * \param barrier Absolute time before select() should return.
+ * \param barrier Defines the upper bound for the timeout.
  * \param s Pointer to the scheduler struct.
  *
- * \return If \a barrier is in the past, this function does nothing and returns
- * zero. Otherwise it returns one.
+ * \return If the barrier is in the past, this function does nothing and
+ * returns zero. Otherwise it returns one.
  *
  * \sa \ref sched_request_barrier_or_min_delay().
  */
@@ -429,12 +420,12 @@ int sched_request_barrier(struct timeval *barrier, struct sched *s)
 }
 
 /**
- * Force the next select() call to return before the given time.
+ * Bound the I/O timeout or request a minimal delay.
  *
- * \param barrier Absolute time before select() should return.
+ * \param barrier Absolute time as in \ref sched_request_barrier().
  * \param s Pointer to the scheduler struct.
  *
- * \return If \a barrier is in the past, this function requests a minimal
+ * \return If the barrier is in the past, this function requests a minimal
  * timeout and returns zero. Otherwise it returns one.
  *
  * \sa \ref sched_min_delay(), \ref sched_request_barrier().
@@ -450,3 +441,126 @@ int sched_request_barrier_or_min_delay(struct timeval *barrier, struct sched *s)
        sched_request_timeout(&diff, s);
        return 1;
 }
+
+static void add_pollfd(int fd, struct sched *s, short events)
+{
+       assert(fd >= 0);
+#if 0
+       {
+               int flags = fcntl(fd, F_GETFL);
+               if (!(flags & O_NONBLOCK)) {
+                       PARA_EMERG_LOG("fd %d is a blocking file descriptor\n", fd);
+                       exit(EXIT_FAILURE);
+               }
+       }
+#endif
+       if (s->pidx_array_len > fd) { /* is fd already registered? */
+               if (s->pidx[fd] < s->pfd_array_len) { /* yes, it is */
+                       assert(s->pfd[s->pidx[fd]].fd == fd);
+                       s->pfd[s->pidx[fd]].events |= events;
+                       return;
+               }
+       } else { /* need to extend the index array */
+               unsigned old_len = s->pidx_array_len;
+               while (s->pidx_array_len <= fd)
+                       s->pidx_array_len = s->pidx_array_len * 2 + 1;
+               PARA_INFO_LOG("pidx array len: %u\n", s->pidx_array_len);
+               s->pidx = para_realloc(s->pidx,
+                       s->pidx_array_len * sizeof(unsigned));
+               memset(s->pidx + old_len, 0xff,
+                       (s->pidx_array_len - old_len) * sizeof(unsigned));
+       }
+       /*
+        * The given fd is not part of the pfd array yet. Initialize pidx[fd]
+        * to point at the next unused slot of this array and initialize the
+        * slot.
+        */
+       s->pidx[fd] = s->num_pfds;
+       if (s->pfd_array_len <= s->num_pfds) {
+               unsigned old_len = s->pfd_array_len;
+               s->pfd_array_len = old_len * 2 + 1;
+               PARA_INFO_LOG("pfd array len: %u\n", s->pfd_array_len);
+               s->pfd = para_realloc(s->pfd,
+                       s->pfd_array_len * sizeof(struct pollfd));
+               memset(s->pfd + old_len, 0,
+                       (s->pfd_array_len - old_len) * sizeof(struct pollfd));
+       }
+       s->pfd[s->num_pfds].fd = fd;
+       s->pfd[s->num_pfds].events = events;
+       s->pfd[s->num_pfds].revents = 0;
+       s->num_pfds++;
+}
+
+/**
+ * Instruct the scheduler to monitor an fd for readiness for reading.
+ *
+ * \param fd The file descriptor.
+ * \param s The scheduler.
+ *
+ * \sa \ref sched_monitor_writefd().
+ */
+void sched_monitor_readfd(int fd, struct sched *s)
+{
+       add_pollfd(fd, s, POLLIN);
+}
+
+/**
+ * Instruct the scheduler to monitor an fd for readiness for writing.
+ *
+ * \param fd The file descriptor.
+ * \param s The scheduler.
+ *
+ * \sa \ref sched_monitor_readfd().
+ */
+void sched_monitor_writefd(int fd, struct sched *s)
+{
+       add_pollfd(fd, s, POLLOUT);
+}
+
+static int get_revents(int fd, const struct sched *s)
+{
+       if (fd < 0)
+               return 0;
+       if (fd >= s->pidx_array_len)
+               return 0;
+       if (s->pidx[fd] >= s->num_pfds)
+               return 0;
+       if (s->pfd[s->pidx[fd]].fd != fd)
+               return 0;
+       assert((s->pfd[s->pidx[fd]].revents & POLLNVAL) == 0);
+       return s->pfd[s->pidx[fd]].revents;
+}
+
+/**
+ * Check whether there is data to read on the given fd.
+ *
+ * To be called from the ->post_monitor() method of a task.
+ *
+ * \param fd Should have been monitored with \ref sched_monitor_readfd().
+ * \param s The scheduler instance.
+ *
+ * \return True if the file descriptor is ready for reading, false otherwise.
+ * If fd is negative, or has not been monitored in the current iteration of the
+ * scheduler's main loop, the function also returns false.
+ *
+ * \sa \ref sched_write_ok().
+ */
+bool sched_read_ok(int fd, const struct sched *s)
+{
+       return get_revents(fd, s) & (POLLIN | POLLERR | POLLHUP);
+}
+
+/**
+ * Check whether writing is possible (i.e., does not block).
+ *
+ * \param fd Should have been monitored with \ref sched_monitor_writefd().
+ * \param s The scheduler instance.
+ *
+ * \return True if the file descriptor is ready for writing, false otherwise.
+ * The comment in \ref sched_read_ok() about invalid file descriptors applies
+ * to this function as well.
+ */
+bool sched_write_ok(int fd, const struct sched *s)
+{
+       return get_revents(fd, s) & (POLLOUT | POLLERR | POLLHUP);
+}
diff --git a/sched.h b/sched.h
index 35e2503e383be3611fc302f5a732e45cf322d506..ede5e67ea2ac042c5765a92e6100bc00ab47e103 100644 (file)
--- a/sched.h
+++ b/sched.h
@@ -7,24 +7,30 @@
  * Paraslash's scheduler.
  *
  * Designed with KISS in mind. It maintains a list of task structures which is
- * extended when a new task is registered. Each task may define a pre_select
+ * extended when a new task is registered. Each task may define a pre_monitor
  * function which is called from the scheduler main loop before it calls
- * select(). Similarly, each task must define a post_select function which is
- * called after the select call.
+ * poll(2). Similarly, each task must define a post_monitor function which is
+ * called after poll(2) returns.
+ *
+ * \sa select(2), poll(2).
  */
 struct sched {
-       /** Initial value before any pre_select call. */
-       struct timeval default_timeout;
-       /** The current timeout for the upcoming select call. */
-       struct timeval select_timeout;
-       /** fds that should be watched for readability. */
-       fd_set rfds;
-       /** fds that should be watched for writability. */
-       fd_set wfds;
-       /** Highest numbered file descriptor in any of the above fd sets. */
-       int max_fileno;
-       /** If non-NULL, use this function instead of para_select. */
-       int (*select_function)(int, fd_set *, fd_set *, struct timeval *);
+       /** Initial value (in milliseconds) before any pre_monitor call. */
+       int default_timeout;
+       /** The timeout (also in milliseconds) for the next iteration. */
+       int timeout;
+       /** Passed to poll(2). */
+       struct pollfd *pfd;
+       /** Number of elements in the above array, passed to poll(2). */
+       unsigned pfd_array_len;
+       /** Number of fds registered for montitoring so far. */
+       unsigned num_pfds;
+       /** Maps fds to indices of the pfd array. */
+       unsigned *pidx;
+       /** Mumber of elements in the above pidx array. */
+       unsigned pidx_array_len;
+       /** If non-NULL, use this function instead of \ref xpoll(). */
+       int (*poll_function)(struct pollfd *fds, nfds_t nfds, int timeout);
        /** Tasks which have been registered to the scheduler. */
        struct list_head task_list;
 };
@@ -36,23 +42,32 @@ struct task_info {
        /** Used for log messages and by \ref get_task_list(). */
        const char *name;
        /**
-        * The optional pre select method.
+        * Configure watch fds and impose an upper bound on the I/O timeout.
+        *
+        * If this is not NULL, the function is called at each iteration of the
+        * scheduler's main loop. Its purpose is to tell the scheduler that
+        * certain file descriptors should be monitored for readiness for I/O.
+        * The function may also lower the scheduler's timeout value (but shall
+        * never increase it) to impose an upper bound on the waiting time in
+        * case no file descriptors happen to be ready.
         *
-        * Its purpose is to add file descriptors to the fd sets of the
-        * scheduler and to decrease the select timeout if necessary.
+        * \sa \ref time.c.
         */
-       void (*pre_select)(struct sched *s, void *context);
+       void (*pre_monitor)(struct sched *s, void *context);
        /**
-        * The mandatory post select method.
+        * Perform I/O on file descriptors which are ready for I/O.
+        *
+        * This mandatory hook is called after the system call which monitors
+        * file descriptors returns. The function should perform non-blocking
+        * I/O on those file descriptors which are reported as being ready.
         *
-        * Its purpose is to evaluate and act upon the results of the previous
-        * select call. If this function returns a negative value, the
-        * scheduler unregisters the task.
+        * If this function returns a negative value, the scheduler unregisters
+        * the task.
         */
-       int (*post_select)(struct sched *s, void *context);
+       int (*post_monitor)(struct sched *s, void *context);
        /**
         * This pointer is saved when the task is registered. It is passed to
-        * ->pre_select() and ->post_select(). Usually this is a pointer to the
+        * ->pre_monitor() and ->post_monitor(). Usually this is a pointer to the
         * struct owned by the caller which contains the task pointer.
         */
        void *context;
@@ -80,3 +95,7 @@ void sched_request_timeout(struct timeval *to, struct sched *s);
 void sched_request_timeout_ms(long unsigned ms, struct sched *s);
 int sched_request_barrier(struct timeval *barrier, struct sched *s);
 int sched_request_barrier_or_min_delay(struct timeval *barrier, struct sched *s);
+void sched_monitor_readfd(int fd, struct sched *s);
+void sched_monitor_writefd(int fd, struct sched *s);
+bool sched_read_ok(int fd, const struct sched *s);
+bool sched_write_ok(int fd, const struct sched *s);
diff --git a/score.c b/score.c
index 983589333f088b600d85d9c0271044d7a21f8eb7..54af56f7c60100ad65ac09cafb52e7769514c958 100644 (file)
--- a/score.c
+++ b/score.c
@@ -21,15 +21,11 @@ static int ptr_compare(const struct osl_object *obj1, const struct osl_object *o
        return NUM_COMPARE(d1, d2);
 }
 
-/**
- * Compare the score of two audio files
- *
- * \param obj1 Pointer to the first score object.
- * \param obj2 Pointer to the second score object.
- *
- * This function first compares the score values as usual integers. If they compare as
- * equal, the address of \a obj1 and \a obj2 are compared. So this compare function
- * returns zero if and only if \a obj1 and \a obj2 point to the same memory area.
+/*
+ * This function first compares the score values. If they are equal, the
+ * addresses of the two objects are compared. Thus, the function returns
+ * "equal" only if the two objects alias each other, i.e., point to the same
+ * memory address.
  */
 static int score_compare(const struct osl_object *obj1, const struct osl_object *obj2)
 {
@@ -92,16 +88,7 @@ int get_num_admissible_files(unsigned *num)
        return osl(osl_get_num_rows(score_table, num));
 }
 
-/**
- * Get the score of the audio file associated with given row of the score table.
- *
- * \param score_row Pointer to the row in the score table.
- * \param score Result is returned here on success.
- *
- * On errors (negative return value) the content of \a score is undefined.
- *
- * \return The return value of the underlying call to osl_get_object().
- */
+/* On errors (negative return value) the content of score is undefined. */
 static int get_score_of_row(void *score_row, long *score)
 {
        struct osl_object obj;
@@ -132,7 +119,7 @@ int score_add(const struct osl_row *aft_row, long score)
        score_objs[SCORECOL_AFT_ROW].size = size;
 
        size = score_table_desc.column_descriptions[SCORECOL_SCORE].data_size;
-       score_objs[SCORECOL_SCORE].data = para_malloc(size);
+       score_objs[SCORECOL_SCORE].data = alloc(size);
        score_objs[SCORECOL_SCORE].size = size;
        *(long *)(score_objs[SCORECOL_SCORE].data) = score;
 
@@ -189,7 +176,7 @@ int score_update(const struct osl_row *aft_row, long percent)
                return ret;
        new_score--;
        obj.size = sizeof(long);
-       obj.data = para_malloc(obj.size);
+       obj.data = alloc(obj.size);
        *(long *)obj.data = new_score;
        PARA_DEBUG_LOG("new score: %ld, rank %u/%u\n", new_score, new_pos, n);
        return osl(osl_update_object(score_table, row, SCORECOL_SCORE, &obj));
@@ -202,8 +189,7 @@ int score_update(const struct osl_row *aft_row, long percent)
  * \param score Result pointer.
  * \param aft_row Result pointer.
  *
- * \return Negative on errors, positive on success. Possible errors: Errors
- * returned by osl_get_object().
+ * \return Standard.
  */
 int get_score_and_aft_row(struct osl_row *score_row, long *score,
                struct osl_row **aft_row)
@@ -250,8 +236,7 @@ int admissible_file_loop(void *data, osl_rbtree_loop_func *func)
  * \param aft_row Points to the row in the aft of the "best" audio file.
  * \param score Highest score value in the score table.
  *
- * \return Positive on success, negative on errors. Possible errors: Errors
- * returned by osl_rbtree_last_row(), osl_get_object().
+ * \return Standard.
  */
 int score_get_best(struct osl_row **aft_row, long *score)
 {
@@ -273,8 +258,7 @@ int score_get_best(struct osl_row **aft_row, long *score)
  *
  * \param aft_row The file which is no longer admissible.
  *
- * \return Positive on success, negative on errors. Possible errors:
- * Errors returned by osl_get_row() and osl_del_row().
+ * \return Standard.
  *
  * \sa \ref score_add().
  */
@@ -315,20 +299,12 @@ int row_belongs_to_score_table(const struct osl_row *aft_row, unsigned *rank)
        return 1;
 }
 
-/* Close the score table. */
 static void score_close(void)
 {
        osl_close_table(score_table, OSL_FREE_VOLATILE);
        score_table = NULL;
 }
 
-/**
- * Open the score table.
- *
- * \param dir Unused.
- *
- * \return The return value of the underlying call to osl_open_table().
- */
 static int score_open(__a_unused const char *dir)
 {
        score_table_desc.dir = NULL; /* this table has only volatile columns */
diff --git a/send.h b/send.h
index f6aafbb41a2bf9b089f2ddcc9968278dea734fa1..dec5b0db1381694fcb35a98785a50068cb1f6330 100644 (file)
--- a/send.h
+++ b/send.h
@@ -76,27 +76,10 @@ struct sender {
        void (*send)(long unsigned current_chunk, long unsigned chunks_sent,
                const char *buf, size_t len, const char *header_buf,
                size_t header_len);
-       /**
-        * Add file descriptors to fd_sets.
-        *
-        * The pre_select function of each supported sender is called just before
-        * para_server enters its main select loop. Each sender may add its own
-        * file descriptors to the \a rfds or the \a wfds set.
-        *
-        * If a file descriptor was added, \a max_fileno must be increased by
-        * this function, if necessary.
-        *
-        * \sa select(2).
-        */
-       void (*pre_select)(int *max_fileno, fd_set *rfds, fd_set *wfds);
-       /**
-        * Handle the file descriptors which are ready for I/O.
-        *
-        * If the pre_select hook added one ore more file descriptors to the
-        * read or write set, this is the hook to check the result and do any
-        * I/O on those descriptors which are ready for reading/writing.
-        */
-       void (*post_select)(fd_set *rfds, fd_set *wfds);
+       /** Ask the scheduler to monitor file descriptors. */
+       void (*pre_monitor)(struct sched *s);
+       /** Perform I/O on the file descriptors which are ready. */
+       void (*post_monitor)(struct sched *s);
        /**
         * Terminate all connected clients.
         *
@@ -222,6 +205,6 @@ void generic_com_on(struct sender_status *ss, unsigned protocol);
 void generic_acl_deplete(struct list_head *acl);
 void generic_com_off(struct sender_status *ss);
 char *generic_sender_help(void);
-struct sender_client *accept_sender_client(struct sender_status *ss, fd_set *rfds);
+struct sender_client *accept_sender_client(struct sender_status *ss);
 int send_queued_chunks(int fd, struct chunk_queue *cq);
 int parse_fec_url(const char *arg, struct sender_command_data *scd);
index ea494d9a7b23f5ee87186917d11e883631ec9cbc..26502cabd08376673b2c46b61ccffae400344274 100644 (file)
 #include "afs.h"
 #include "server.h"
 #include "acl.h"
+#include "sched.h"
 #include "send.h"
 #include "close_on_fork.h"
 #include "chunk_queue.h"
-#include "sched.h"
 #include "vss.h"
 
 /** Clients will be kicked if there are more than that many bytes pending. */
  */
 void shutdown_client(struct sender_client *sc, struct sender_status *ss)
 {
-       PARA_INFO_LOG("shutting down %s on fd %d\n", sc->name, sc->fd);
-       free(sc->name);
        if (!process_is_command_handler()) {
+               PARA_INFO_LOG("shutting down %s on fd %d\n", sc->name, sc->fd);
                close(sc->fd);
                del_close_on_fork_list(sc->fd);
        }
+       free(sc->name);
        cq_destroy(sc->cq);
        list_del(&sc->node);
        free(sc->private_data);
@@ -120,14 +120,14 @@ void init_sender_status(struct sender_status *ss,
 
        if (n == 0) {
                ss->num_listen_fds = 1;
-               ss->listen_addresses = para_malloc(sizeof(char *));
+               ss->listen_addresses = alloc(sizeof(char *));
                ss->listen_addresses[0] = NULL;
-               ss->listen_fds = para_malloc(sizeof(int));
+               ss->listen_fds = alloc(sizeof(int));
                ss->listen_fds[0] = -1;
        } else {
                ss->num_listen_fds = n;
-               ss->listen_addresses = para_malloc(n * sizeof(char *));
-               ss->listen_fds = para_malloc(n * sizeof(int));
+               ss->listen_addresses = alloc(n * sizeof(char *));
+               ss->listen_fds = alloc(n * sizeof(int));
                FOR_EACH_LISTEN_FD(i, ss) {
                        ss->listen_addresses[i] = para_strdup(lls_string_val(i,
                                listen_address_opt_result));
@@ -136,9 +136,9 @@ void init_sender_status(struct sender_status *ss,
        }
        ss->default_port = default_port;
 
-       INIT_LIST_HEAD(&ss->client_list);
+       init_list_head(&ss->client_list);
        /* Initialize an access control list */
-       INIT_LIST_HEAD(&ss->acl);
+       init_list_head(&ss->acl);
        for (i = 0; i < lls_opt_given(acl_opt_result); i++) {
                const char *arg = lls_string_val(i, acl_opt_result);
                char addr[16];
@@ -343,7 +343,6 @@ void generic_com_off(struct sender_status *ss)
  * Accept a connection on the socket(s) this server is listening on.
  *
  * \param ss The sender whose listening fd is ready for reading.
- * \param rfds Passed to para_accept(),
  *
  * This accepts incoming connections on any of the listening sockets of the
  * server. If there is a connection pending, the function
@@ -367,7 +366,7 @@ void generic_com_off(struct sender_status *ss)
  * \sa \ref para_accept(), \ref mark_fd_nonblocking(), \ref acl_check_access(),
  * \ref cq_new(), \ref add_close_on_fork_list().
  */
-struct sender_client *accept_sender_client(struct sender_status *ss, fd_set *rfds)
+struct sender_client *accept_sender_client(struct sender_status *ss)
 {
        struct sender_client *sc;
        int fd, ret;
@@ -376,7 +375,7 @@ struct sender_client *accept_sender_client(struct sender_status *ss, fd_set *rfd
        FOR_EACH_LISTEN_FD(n, ss) {
                if (ss->listen_fds[n] < 0)
                        continue;
-               ret = para_accept(ss->listen_fds[n], rfds, NULL, 0, &fd);
+               ret = para_accept(ss->listen_fds[n], NULL, 0, &fd);
                if (ret < 0)
                        goto warn;
                if (ret == 0)
@@ -391,7 +390,7 @@ struct sender_client *accept_sender_client(struct sender_status *ss, fd_set *rfd
                if (ret < 0)
                        goto close_fd_and_warn;
                ss->num_clients++;
-               sc = para_calloc(sizeof(*sc));
+               sc = zalloc(sizeof(*sc));
                sc->fd = fd;
                sc->name = para_strdup(remote_name(fd));
                sc->cq = cq_new(MAX_CQ_BYTES);
index d51e4e0bfe5b33c341ff744eda4f9380bd666844..ea9cc9c003616762a96aa546c381d54417fd34a2 100644 (file)
--- a/server.c
+++ b/server.c
@@ -2,31 +2,6 @@
 
 /** \file server.c Paraslash's main server. */
 
-/**
- * \mainpage Main data structures and selected APIs:
- *
- *     - Senders: \ref sender,
- *     - Audio file selector: \ref afs_info, \ref afs_table,
- *     - Audio format handler: \ref audio_format_handler, \ref afh_info
- *     - Receivers/filters/writers: \ref receiver, \ref receiver_node,
- *       \ref filter, \ref filter_node, \ref writer_node, \ref writer.
- *     - Scheduling: \ref sched.h,
- *     - Buffer trees: \ref buffer_tree.h,
- *     - Sideband API: \ref sideband.h,
- *     - Crypto: \ref crypt.h, \ref crypt_backend.h,
- *     - Error subsystem: \ref error.h,
- *     - Inter process communication: \ref ipc.h,
- *     - Forward error correction: \ref fec.h,
- *     - Daemons: \ref daemon.h,
- *     - Mixer API: \ref mix.h,
- *     - Interactive sessions: \ref interactive.h,
- *     - File descriptors: \ref fd.h,
- *     - Signals: \ref signal.h,
- *     - Networking: \ref net.h,
- *     - Time: \ref time.c,
- *     - Doubly linked lists: \ref list.h.
- */
-
 #include <netinet/in.h>
 #include <sys/socket.h>
 #include <signal.h>
@@ -49,8 +24,8 @@
 #include "net.h"
 #include "server.h"
 #include "list.h"
-#include "send.h"
 #include "sched.h"
+#include "send.h"
 #include "vss.h"
 #include "config.h"
 #include "close_on_fork.h"
@@ -197,6 +172,7 @@ static void init_ipc_or_die(void)
        mmd->active_connections = 0;
        mmd->vss_status_flags = VSS_NEXT;
        mmd->new_vss_status_flags = VSS_NEXT;
+       mmd->loglevel = OPT_UINT32_VAL(LOGLEVEL);
        return;
 destroy_mmd_mutex:
        mutex_destroy(mmd_mutex);
@@ -205,6 +181,9 @@ err_out:
        exit(EXIT_FAILURE);
 }
 
+/** Get a reference to the supercommand of para_server. */
+#define CMD_PTR (lls_cmd(0, server_suite))
+
 /**
  * (Re-)read the server configuration files.
  *
@@ -230,7 +209,7 @@ void parse_config_or_die(bool reload)
                        para_strerror(-ret));
                exit(EXIT_FAILURE);
        }
-       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
        if (OPT_GIVEN(LOGFILE)) {
                daemon_set_logfile(OPT_STRING_VAL(LOGFILE));
                daemon_open_log_or_die();
@@ -275,14 +254,14 @@ static void handle_sighup(void)
                kill(afs_pid, SIGHUP);
 }
 
-static int signal_post_select(struct sched *s, __a_unused void *context)
+static int signal_post_monitor(struct sched *s, __a_unused void *context)
 {
        int ret, signum;
 
        ret = task_get_notification(signal_task->task);
        if (ret < 0)
                return ret;
-       signum = para_next_signal(&s->rfds);
+       signum = para_next_signal();
        switch (signum) {
        case 0:
                return 0;
@@ -338,20 +317,20 @@ static void init_signal_task(void)
        add_close_on_fork_list(signal_task->fd);
        signal_task->task = task_register(&(struct task_info) {
                .name = "signal",
-               .pre_select = signal_pre_select,
-               .post_select = signal_post_select,
+               .pre_monitor = signal_pre_monitor,
+               .post_monitor = signal_post_monitor,
                .context = signal_task,
 
        }, &sched);
 }
 
-static void command_pre_select(struct sched *s, void *context)
+static void command_pre_monitor(struct sched *s, void *context)
 {
        unsigned n;
        struct server_command_task *sct = context;
 
        for (n = 0; n < sct->num_listen_fds; n++)
-               para_fd_set(sct->listen_fds[n], &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(sct->listen_fds[n], s);
 }
 
 static int command_task_accept(unsigned listen_idx, struct sched *s,
@@ -362,7 +341,7 @@ static int command_task_accept(unsigned listen_idx, struct sched *s,
        pid_t child_pid;
        uint32_t *chunk_table;
 
-       ret = para_accept(sct->listen_fds[listen_idx], &s->rfds, NULL, 0, &new_fd);
+       ret = para_accept(sct->listen_fds[listen_idx], NULL, 0, &new_fd);
        if (ret <= 0)
                goto out;
        mmd->num_connects++;
@@ -408,13 +387,13 @@ static int command_task_accept(unsigned listen_idx, struct sched *s,
        /*
         * After we return, the scheduler calls server_select() with a minimal
         * timeout value, because the remaining tasks have a notification
-        * pending. Next it calls the ->post_select method of these tasks,
+        * pending. Next it calls the ->post_monitor method of these tasks,
         * which will return negative in view of the notification. This causes
         * schedule() to return as there are no more runnable tasks.
         *
         * Note that semaphores are not inherited across a fork(), so we don't
-        * hold the lock at this point. Since server_select() drops the lock
-        * prior to calling para_select(), we need to acquire it here.
+        * hold the lock at this point. Since server_poll() drops the lock
+        * prior to calling poll(), we need to acquire it here.
         */
        mutex_lock(mmd_mutex);
        return -E_CHILD_CONTEXT;
@@ -424,7 +403,7 @@ out:
        return 0;
 }
 
-static int command_post_select(struct sched *s, void *context)
+static int command_post_monitor(struct sched *s, void *context)
 {
        struct server_command_task *sct = context;
        unsigned n;
@@ -457,14 +436,14 @@ static void init_server_command_task(struct server_command_task *sct,
        sct->argv = argv;
        if (!OPT_GIVEN(LISTEN_ADDRESS)) {
                sct->num_listen_fds = 1;
-               sct->listen_fds = para_malloc(sizeof(int));
+               sct->listen_fds = alloc(sizeof(int));
                ret = para_listen_simple(IPPROTO_TCP, port);
                if (ret < 0)
                        goto err;
                sct->listen_fds[0] = ret;
        } else {
                sct->num_listen_fds = OPT_GIVEN(LISTEN_ADDRESS);
-               sct->listen_fds = para_malloc(sct->num_listen_fds * sizeof(int));
+               sct->listen_fds = alloc(sct->num_listen_fds * sizeof(int));
                for (n = 0; n < OPT_GIVEN(LISTEN_ADDRESS); n++) {
                        const char *arg;
                        arg = lls_string_val(n, OPT_RESULT(LISTEN_ADDRESS));
@@ -484,8 +463,8 @@ static void init_server_command_task(struct server_command_task *sct,
 
        sct->task = task_register(&(struct task_info) {
                .name = "server command",
-               .pre_select = command_pre_select,
-               .post_select = command_post_select,
+               .pre_monitor = command_pre_monitor,
+               .post_monitor = command_post_monitor,
                .context = sct,
        }, &sched);
        /*
@@ -568,7 +547,7 @@ static void server_init(int argc, char **argv, struct server_command_task *sct)
        if (ret < 0)
                goto fail;
        server_lpr = cmdline_lpr;
-       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
        daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
                OPT_STRING_VAL(GROUP));
        version_handle_flag("server", OPT_GIVEN(VERSION));
@@ -642,14 +621,14 @@ out:
        killpg(0, SIGUSR1);
 }
 
-static int server_select(int max_fileno, fd_set *readfds, fd_set *writefds,
-               struct timeval *timeout_tv)
+static int server_poll(struct pollfd *fds, nfds_t nfds, int timeout)
 {
        int ret;
 
+       daemon_set_loglevel(mmd->loglevel);
        status_refresh();
        mutex_unlock(mmd_mutex);
-       ret = para_select(max_fileno + 1, readfds, writefds, timeout_tv);
+       ret = xpoll(fds, nfds, timeout);
        mutex_lock(mmd_mutex);
        return ret;
 }
@@ -683,15 +662,15 @@ int main(int argc, char *argv[])
        struct server_command_task server_command_task_struct,
                *sct = &server_command_task_struct;
 
-       sched.default_timeout.tv_sec = 1;
-       sched.select_function = server_select;
+       sched.default_timeout = 1000;
+       sched.poll_function = server_poll;
 
        server_init(argc, argv, sct);
        mutex_lock(mmd_mutex);
        ret = schedule(&sched);
        /*
-        * We hold the mmd lock: it was re-acquired in server_select()
-        * after the select call.
+        * We hold the mmd lock: it was re-acquired in server_poll()
+        * after the poll(2) call.
         */
        mutex_unlock(mmd_mutex);
        sched_shutdown(&sched);
@@ -704,12 +683,13 @@ int main(int argc, char *argv[])
                deplete_close_on_fork_list();
                if (ret < 0)
                        PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+               vss_shutdown();
        } else {
+               vss_shutdown();
                alarm(ALARM_TIMEOUT);
                close_listed_fds();
                ret = handle_connect(sct->child_fd);
        }
-       vss_shutdown();
        shm_detach(mmd);
        user_list_deplete();
        free_lpr();
index da75d86bdf191b130d02da12f49172ac5e0482d7..10bb6172860ddb467c3f9fd26ef6ce9e3cb8aab1 100644 (file)
--- a/server.h
+++ b/server.h
@@ -73,6 +73,8 @@ struct misc_meta_data {
        char afs_mode_string[MAXLINE];
        /** Used by the sender command. */
        struct sender_command_data sender_cmd_data;
+       /** Set by the ll command. */
+       int loglevel;
        /** Describes the current audio file. */
        struct audio_file_data afd;
 };
@@ -80,15 +82,6 @@ struct misc_meta_data {
 extern pid_t afs_pid;
 extern struct lls_parse_result *server_lpr;
 
-/**
- * Get a reference to the supercommand of para_server.
- *
- * This is needed for parsing the command line and for the ENUM_STRING_VAL()
- * macro below. The latter macro is used in command.c, so CMD_PTR() can not
- * be made local to server.c.
- */
-#define CMD_PTR (lls_cmd(0, server_suite))
-
 /** Get the parse result of an option to para_server. */
 #define OPT_RESULT(_name) (lls_opt_result( \
                LSG_SERVER_PARA_SERVER_OPT_ ## _name, server_lpr))
@@ -105,10 +98,6 @@ extern struct lls_parse_result *server_lpr;
 /** The (first) argument to a server option of type int32. */
 #define OPT_INT32_VAL(_name) (lls_int32_val(0, OPT_RESULT(_name)))
 
-/** Get the string which corresponds to an enum constant. */
-#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \
-       lls_opt(LSG_SERVER_PARA_SERVER_OPT_ ## _name, CMD_PTR)))
-
 int handle_connect(int fd);
 void parse_config_or_die(bool reload);
 char *server_get_tasks(void);
index ed7867a1c37027d7a49d1047bd349c5976d0e8d7..d4876234a192a956f112c48f871ed1fb0d5321b7 100644 (file)
@@ -37,7 +37,7 @@ struct sb_context {
 struct sb_context *sb_new_recv(size_t max_size, sb_transformation t,
                void *trafo_context)
 {
-       struct sb_context *c = para_calloc(sizeof(*c));
+       struct sb_context *c = zalloc(sizeof(*c));
 
        c->max_size = max_size;
        c->trafo = t;
@@ -62,7 +62,7 @@ struct sb_context *sb_new_recv(size_t max_size, sb_transformation t,
 struct sb_context *sb_new_send(struct sb_buffer *sbb, bool dont_free,
                sb_transformation t, void *trafo_context)
 {
-       struct sb_context *c = para_calloc(sizeof(*c));
+       struct sb_context *c = zalloc(sizeof(*c));
        struct iovec src, dst, *srcp, *dstp;
 
        assert(sbb);
@@ -266,7 +266,7 @@ int sb_received(struct sb_context *c, size_t nbytes, struct sb_buffer *result)
         */
        if (sbb->iov.iov_len == (size_t)-1)
                return -E_SB_PACKET_SIZE;
-       sbb->iov.iov_base = para_malloc(sbb->iov.iov_len + 1);
+       sbb->iov.iov_base = alloc(sbb->iov.iov_len + 1);
        return 0; /* ready to read body */
 success:
        *result = c->sbb;
index 32d6ab6624e5f493ac393b630a603c0968f11497..bdafeaf296e026508698c306e4b498e0710a9cee 100644 (file)
--- a/signal.c
+++ b/signal.c
@@ -27,7 +27,7 @@ static int signal_pipe[2];
  * signal arrives, the signal handler writes the number of the signal received
  * to one end of the signal pipe. The application can test for pending signals
  * by checking if the file descriptor of the other end of the signal pipe is
- * ready for reading, see select(2).
+ * ready for reading.
  *
  * \return This function either succeeds or calls exit(3) to terminate the
  * current process. On success, a signal task structure is returned.
@@ -48,7 +48,7 @@ struct signal_task *signal_init_or_die(void)
        ret = mark_fd_nonblocking(signal_pipe[1]);
        if (ret < 0)
                goto err_out;
-       st = para_calloc(sizeof(*st));
+       st = zalloc(sizeof(*st));
        st->fd = signal_pipe[0];
        return st;
 err_out:
@@ -202,16 +202,14 @@ void para_unblock_signal(int sig)
 /**
  * Return the number of the next pending signal.
  *
- * \param rfds The fd_set containing the signal pipe.
- *
  * \return On success, the number of the received signal is returned. If there
  * is no signal currently pending, the function returns zero. On read errors
  * from the signal pipe, the process is terminated.
  */
-int para_next_signal(fd_set *rfds)
+int para_next_signal(void)
 {
        size_t n;
-       int s, ret = read_nonblock(signal_pipe[0], &s, sizeof(s), rfds, &n);
+       int s, ret = read_nonblock(signal_pipe[0], &s, sizeof(s), &n);
 
        if (ret < 0) {
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
index e5532ded5a9378619f7951708b49c5b38bf52d18..d9e98e78a635930577038d6090c9d5260772ddec 100644 (file)
--- a/signal.h
+++ b/signal.h
@@ -13,32 +13,31 @@ struct signal_task {
 };
 
 /**
- * A generic pre-select method for signal tasks.
+ * Monitor the signal fd for reading.
  *
- * \param s Passed to para_fd_set().
+ * \param s The scheduler instance.
  * \param context Signal task pointer.
  *
  * This convenience helper is called from several programs which need to handle
- * signals, including para_server and para_audiod. These programs define a
- * signal task structure and set its ->pre_select method to this function which
- * adds the file descriptor of the signal task to the set of descriptors to be
- * watched in the next select() call.
+ * signals, including para_server and para_audiod. These programs set up a
+ * signal pipe and a signal task structure, and use this function to tell the
+ * scheduler to monitor the read end of the pipe.
  *
  * Although the second parameter must be in fact a pointer to a signal_task
- * structure, the parameter is specified as void * here to match the
- * ->pre_select method of struct task.
+ * structure, the parameter is specified as void * here to match the signature
+ * declared in struct \ref task_info.
  */
-_static_inline_ void signal_pre_select(struct sched *s, void *context)
+_static_inline_ void signal_pre_monitor(struct sched *s, void *context)
 {
        struct signal_task *st = context;
-       para_fd_set(st->fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(st->fd, s);
 }
 
 struct signal_task *signal_init_or_die(void);
 void para_sigaction(int sig, void (*handler)(int));
 void para_install_sighandler(int);
 int para_reap_child(pid_t *pid);
-int para_next_signal(fd_set *rfds);
+int para_next_signal(void);
 void signal_shutdown(struct signal_task *st);
 void para_block_signal(int sig);
 void para_unblock_signal(int sig);
index caeacb1921341b04b26f117d5de6607a80ab95e9..cd3b7cde907388465f04ead39af10bd125d154ca 100644 (file)
--- a/spx_afh.c
+++ b/spx_afh.c
@@ -195,7 +195,7 @@ static size_t spx_make_meta_packet(struct taginfo *tags, char **result)
        }
        PARA_DEBUG_LOG("meta packet size: %zu bytes\n", sz);
        /* terminating zero byte for the last sprintf() */
-       buf = p = para_malloc(sz + 1);
+       buf = p = alloc(sz + 1);
        write_u32(p, comment_sz);
        p += 4;
        strcpy(p, tags->comment);
index 7be817ddaa49bf97e5d4025c43be63b42da9cd4c..08eac02a557b2d503646666edd7ffb35e1c5c5f7 100644 (file)
@@ -81,7 +81,7 @@ struct private_spxdec_data {
 
 static void spxdec_open(struct filter_node *fn)
 {
-       struct private_spxdec_data *psd = para_calloc(sizeof(*psd));
+       struct private_spxdec_data *psd = zalloc(sizeof(*psd));
 
        fn->private_data = psd;
        fn->min_iqs = 200;
@@ -171,7 +171,7 @@ static int speexdec_write_frames(int packet_no,
                if (new_frame_size <= 0)
                        continue;
                samples = new_frame_size * psd->shi.channels;
-               btr_output = para_malloc(2 * samples);
+               btr_output = arr_alloc(samples, 2);
                for (i = 0; i < samples; i++)
                        btr_output[i] = read_u16(output + i + skip_idx);
                btr_add_output((char *)btr_output, samples * 2, btrn);
@@ -246,7 +246,7 @@ static int compute_skip_samples(ogg_page *og, struct private_spxdec_data *psd)
        return ret;
 }
 
-static int speexdec_post_select(__a_unused struct sched *s, void *context)
+static int speexdec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct private_spxdec_data *psd = fn->private_data;
@@ -305,7 +305,7 @@ fail:
 const struct filter lsg_filter_cmd_com_spxdec_user_data = {
        .open = spxdec_open,
        .close = speexdec_close,
-       .pre_select = generic_filter_pre_select,
-       .post_select = speexdec_post_select,
+       .pre_monitor = generic_filter_pre_monitor,
+       .post_monitor = speexdec_post_monitor,
        .execute = speexdec_execute,
 };
diff --git a/stdin.c b/stdin.c
index 9408235a045a6767e2acf443208d95196990d379..d025b949e8e8d2566abde3f2f214db7998ba44ff 100644 (file)
--- a/stdin.c
+++ b/stdin.c
 #include "string.h"
 
 /*
- * If there is space left in the buffer of the stdin task add STDIN_FILENO to
- * the read fd set of s.
+ * If there is space left in the buffer of the stdin task, ask the scheduler to
+ * monitor STDIN_FILENO.
  */
-static void stdin_pre_select(struct sched *s, void *context)
+static void stdin_pre_monitor(struct sched *s, void *context)
 {
        struct stdin_task *sit = context;
        int ret;
@@ -28,16 +28,15 @@ static void stdin_pre_select(struct sched *s, void *context)
        if (ret <= 0)
                return;
        if (btr_pool_unused(sit->btrp) > 0)
-               return para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
+               return sched_monitor_readfd(STDIN_FILENO, s);
        sched_request_timeout_ms(100, s);
 }
 
 /*
- * This function checks if STDIN_FILENO was included by in the read fd set of s
- * during the previous pre_select call. If so, and if STDIN_FILENO is readable,
- * data is read from stdin and fed into the buffer tree.
+ * Feed data from stdin into the buffer tree if STDIN_FILENO is ready for
+ * reading.
  */
-static int stdin_post_select(struct sched *s, void *context)
+static int stdin_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct stdin_task *sit = context;
        ssize_t ret;
@@ -64,7 +63,7 @@ static int stdin_post_select(struct sched *s, void *context)
         * reference can not be freed, we're stuck.
         */
        sz = PARA_MIN(sz, btr_pool_size(sit->btrp) / 2);
-       ret = read_nonblock(STDIN_FILENO, buf, sz, &s->rfds, &n);
+       ret = read_nonblock(STDIN_FILENO, buf, sz, &n);
        if (n > 0)
                btr_add_output_pool(sit->btrp, n, sit->btrn);
        if (ret >= 0)
@@ -91,8 +90,8 @@ void stdin_task_register(struct stdin_task *sit, struct sched *s)
        int ret;
        struct task_info ti = {
                .name = "stdin",
-               .pre_select = stdin_pre_select,
-               .post_select = stdin_post_select,
+               .pre_monitor = stdin_pre_monitor,
+               .post_monitor = stdin_post_monitor,
                .context = sit,
        };
 
index 1f7791094a33a961c193c273a4c09e9b16f2be07..ba5f19670fce6c3c04d256a2056477064f5df7d3 100644 (file)
--- a/stdout.c
+++ b/stdout.c
 #include "stdout.h"
 #include "buffer_tree.h"
 
-/* Add STDOUT_FILENO to the write fd set if there is input data available. */
-static void stdout_pre_select(struct sched *s, void *context)
+/* Monitor STDOUT_FILENO if there is input data available. */
+static void stdout_pre_monitor(struct sched *s, void *context)
 {
        struct stdout_task *sot = context;
        int ret;
 
        ret = btr_node_status(sot->btrn, 0, BTR_NT_LEAF);
        if (ret > 0)
-               para_fd_set(STDOUT_FILENO, &s->wfds, &s->max_fileno);
+               sched_monitor_writefd(STDOUT_FILENO, s);
        else if (ret < 0)
                sched_min_delay(s);
 }
 
 /*
- * This function writes input data from the buffer tree to stdout if
- * STDOUT_FILENO is writable.
+ * If input from the buffer tree is available and STDOUT_FILENO is ready, write
+ * as much as possible.
  */
-static int stdout_post_select(struct sched *s, void *context)
+static int stdout_post_monitor(struct sched *s, void *context)
 {
        struct stdout_task *sot = context;
        struct btr_node *btrn = sot->btrn;
@@ -40,7 +40,7 @@ static int stdout_post_select(struct sched *s, void *context)
                goto out;
        if (ret == 0)
                return 0;
-       if (!FD_ISSET(STDOUT_FILENO, &s->wfds))
+       if (!sched_write_ok(STDOUT_FILENO, s))
                return 0;
 
        if (sot->must_set_nonblock_flag) {
@@ -79,8 +79,8 @@ void stdout_task_register(struct stdout_task *sot, struct sched *s)
 {
        int ret;
        struct task_info ti = {
-               .pre_select = stdout_pre_select,
-               .post_select = stdout_post_select,
+               .pre_monitor = stdout_pre_monitor,
+               .post_monitor = stdout_post_monitor,
                .context = sot,
                .name = "stdout",
        };
index 198e9f1d286ee2228638a2a7994fe9e65f41ccac..46b346235f17ffb696f43d22d3dcfde4c75ba4ca 100644 (file)
--- a/string.c
+++ b/string.c
 #include "error.h"
 
 /**
- * Paraslash's version of realloc().
+ * Reallocate an array, abort on failure or bugs.
  *
- * \param p Pointer to the memory block, may be \p NULL.
- * \param size The desired new size.
+ * \param ptr Pointer to the memory block, may be NULL.
+ * \param nmemb Number of elements.
+ * \param size The size of one element in bytes.
  *
- * A wrapper for realloc(3). It calls \p exit(\p EXIT_FAILURE) on errors,
- * i.e. there is no need to check the return value in the caller.
+ * A wrapper for realloc(3) which aborts on invalid arguments or integer
+ * overflow. The wrapper also terminates the current process on allocation
+ * errors, so the caller does not need to check for failure.
  *
  * \return A pointer to newly allocated memory which is suitably aligned for
- * any kind of variable and may be different from \a p.
+ * any kind of variable and may be different from ptr.
  *
  * \sa realloc(3).
  */
-__must_check void *para_realloc(void *p, size_t size)
+__must_check void *arr_realloc(void *ptr, size_t nmemb, size_t size)
 {
+       size_t pr;
+
+       assert(size > 0);
+       assert(nmemb > 0);
+       assert(!__builtin_mul_overflow(nmemb, size, &pr));
+       assert(pr != 0);
+       ptr = realloc(ptr, pr);
+       assert(ptr);
+       return ptr;
+}
+
+/**
+ * Allocate an array, abort on failure or bugs.
+ *
+ * \param nmemb See \ref arr_realloc().
+ * \param size See \ref arr_realloc().
+ *
+ * Like \ref arr_realloc(), this aborts on invalid arguments, integer overflow
+ * and allocation errors.
+ *
+ * \return A pointer to newly allocated memory which is suitably aligned for
+ * any kind of variable.
+ *
+ * \sa See \ref arr_realloc().
+ */
+__must_check __malloc void *arr_alloc(size_t nmemb, size_t size)
+{
+       return arr_realloc(NULL, nmemb, size);
+}
+
+/**
+ * Allocate and initialize an array, abort on failure or bugs.
+ *
+ * \param nmemb See \ref arr_realloc().
+ * \param size See \ref arr_realloc().
+ *
+ * This calls \ref arr_alloc() and zeroes-out the array.
+ *
+ * \return See \ref arr_alloc().
+ */
+__must_check __malloc void *arr_zalloc(size_t nmemb, size_t size)
+{
+       void *ptr = arr_alloc(nmemb, size);
+
        /*
-        * No need to check for NULL pointers: If p is NULL, the call
-        * to realloc is equivalent to malloc(size)
+        * This multiplication can not overflow because the above call to \ref
+        * arr_alloc() aborts on overflow.
         */
-       assert(size);
-       if (!(p = realloc(p, size))) {
-               PARA_EMERG_LOG("realloc failed (size = %zu), aborting\n",
-                       size);
-               exit(EXIT_FAILURE);
-       }
-       return p;
+       memset(ptr, 0, nmemb * size);
+       return ptr;
 }
 
 /**
- * Paraslash's version of malloc().
+ * Allocate and initialize memory.
  *
  * \param size The desired new size.
  *
- * A wrapper for malloc(3) which exits on errors.
- *
- * \return A pointer to the allocated memory, which is suitably aligned for any
- * kind of variable.
+ * \return A pointer to the allocated and zeroed-out memory, which is suitably
+ * aligned for any kind of variable.
  *
- * \sa malloc(3).
+ * \sa \ref alloc(), calloc(3).
  */
-__must_check __malloc void *para_malloc(size_t size)
+__must_check void *zalloc(size_t size)
 {
-       void *p;
-
-       assert(size);
-       p = malloc(size);
-       if (!p) {
-               PARA_EMERG_LOG("malloc failed (size = %zu), aborting\n",
-                       size);
-               exit(EXIT_FAILURE);
-       }
-       return p;
+       return arr_zalloc(1, size);
 }
 
 /**
- * Paraslash's version of calloc().
+ * Paraslash's version of realloc().
  *
+ * \param p Pointer to the memory block, may be \p NULL.
  * \param size The desired new size.
  *
- * A wrapper for calloc(3) which exits on errors.
+ * A wrapper for realloc(3). It calls \p exit(\p EXIT_FAILURE) on errors,
+ * i.e. there is no need to check the return value in the caller.
  *
- * \return A pointer to the allocated and zeroed-out memory, which is suitably
- * aligned for any kind of variable.
+ * \return A pointer to newly allocated memory which is suitably aligned for
+ * any kind of variable and may be different from \a p.
  *
- * \sa calloc(3)
+ * \sa realloc(3).
  */
-__must_check __malloc void *para_calloc(size_t size)
+__must_check void *para_realloc(void *p, size_t size)
 {
-       void *ret = para_malloc(size);
+       return arr_realloc(p, 1, size);
+}
 
-       memset(ret, 0, size);
-       return ret;
+/**
+ * Paraslash's version of malloc().
+ *
+ * \param size The desired new size.
+ *
+ * A wrapper for malloc(3) which exits on errors.
+ *
+ * \return A pointer to the allocated memory, which is suitably aligned for any
+ * kind of variable.
+ *
+ * \sa malloc(3).
+ */
+__must_check __malloc void *alloc(size_t size)
+{
+       return arr_alloc(1, size);
 }
 
 /**
@@ -94,22 +140,23 @@ __must_check __malloc void *para_calloc(size_t size)
  *
  * \param s The string to be duplicated.
  *
- * A wrapper for strdup(3). It calls \p exit(EXIT_FAILURE) on errors, i.e.
- * there is no need to check the return value in the caller.
+ * A strdup(3)-like function which aborts if insufficient memory was available
+ * to allocate the duplicated string, absolving the caller from the
+ * responsibility to check for failure.
  *
- * \return A pointer to the duplicated string. If \a s was the \p NULL pointer,
- * an pointer to an empty string is returned.
+ * \return A pointer to the duplicated string. Unlike strdup(3), the caller may
+ * pass NULL, in which case the function returns a pointer to an empty string.
+ * Regardless of whether or not NULL was passed, the returned string is
+ * allocated on the heap and has to be freed by the caller.
  *
- * \sa strdup(3)
+ * \sa strdup(3).
  */
 __must_check __malloc char *para_strdup(const char *s)
 {
-       char *ret;
+       char *dupped_string = strdup(s? s: "");
 
-       if ((ret = strdup(s? s: "")))
-               return ret;
-       PARA_EMERG_LOG("strdup failed, aborting\n");
-       exit(EXIT_FAILURE);
+       assert(dupped_string);
+       return dupped_string;
 }
 
 /**
@@ -137,7 +184,7 @@ __printf_2_0 unsigned xvasprintf(char **result, const char *fmt, va_list ap)
        size_t size = 150;
        va_list aq;
 
-       *result = para_malloc(size + 1);
+       *result = alloc(size + 1);
        va_copy(aq, ap);
        ret = vsnprintf(*result, size, fmt, aq);
        va_end(aq);
@@ -245,56 +292,6 @@ __must_check __malloc char *para_strcat(char *a, const char *b)
        return tmp;
 }
 
-/**
- * Paraslash's version of dirname().
- *
- * \param name Pointer to the full path.
- *
- * Compute the directory component of \p name.
- *
- * \return If \a name is \p NULL or the empty string, return \p NULL.
- * Otherwise, Make a copy of \a name and return its directory component. Caller
- * is responsible to free the result.
- */
-__must_check __malloc char *para_dirname(const char *name)
-{
-       char *p, *ret;
-
-       if (!name || !*name)
-               return NULL;
-       ret = para_strdup(name);
-       p = strrchr(ret, '/');
-       if (!p)
-               *ret = '\0';
-       else
-               *p = '\0';
-       return ret;
-}
-
-/**
- * Paraslash's version of basename().
- *
- * \param name Pointer to the full path.
- *
- * Compute the filename component of \a name.
- *
- * \return \p NULL if (a) \a name is the empty string or \p NULL, or (b) name
- * ends with a slash.  Otherwise, a pointer within \a name is returned.  Caller
- * must not free the result.
- */
-__must_check char *para_basename(const char *name)
-{
-       char *ret;
-
-       if (!name || !*name)
-               return NULL;
-       ret = strrchr(name, '/');
-       if (!ret)
-               return (char *)name;
-       ret++;
-       return ret;
-}
-
 /**
  * Get the logname of the current user.
  *
@@ -389,7 +386,7 @@ int for_each_line(unsigned flags, char *buf, size_t size,
                if (!(flags & FELF_DISCARD_FIRST) || start != buf) {
                        if (flags & FELF_READ_ONLY) {
                                size_t s = end - start;
-                               char *b = para_malloc(s + 1);
+                               char *b = alloc(s + 1);
                                memcpy(b, start, s);
                                b[s] = '\0';
                                ret = line_handler(b, private_data);
@@ -493,7 +490,7 @@ __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...)
        int ret, sz_off = (b->flags & PBF_SIZE_PREFIX)? 5 : 0;
 
        if (!b->buf) {
-               b->buf = para_malloc(128);
+               b->buf = alloc(128);
                b->size = 128;
                b->offset = 0;
        }
@@ -602,37 +599,6 @@ int para_atoi32(const char *str, int32_t *value)
        return 1;
 }
 
-static inline int loglevel_equal(const char *arg, const char * const ll)
-{
-       return !strncasecmp(arg, ll, strlen(ll));
-}
-
-/**
- * Compute the loglevel number from its name.
- *
- * \param txt The name of the loglevel (debug, info, ...).
- *
- * \return The numeric representation of the loglevel name.
- */
-int get_loglevel_by_name(const char *txt)
-{
-       if (loglevel_equal(txt, "debug"))
-               return LL_DEBUG;
-       if (loglevel_equal(txt, "info"))
-               return LL_INFO;
-       if (loglevel_equal(txt, "notice"))
-               return LL_NOTICE;
-       if (loglevel_equal(txt, "warning"))
-               return LL_WARNING;
-       if (loglevel_equal(txt, "error"))
-               return LL_ERROR;
-       if (loglevel_equal(txt, "crit"))
-               return LL_CRIT;
-       if (loglevel_equal(txt, "emerg"))
-               return LL_EMERG;
-       return -E_BAD_LL;
-}
-
 static int get_next_word(const char *buf, const char *delim, char **word)
 {
        enum line_state_flags {LSF_HAVE_WORD = 1, LSF_BACKSLASH = 2,
@@ -641,7 +607,7 @@ static int get_next_word(const char *buf, const char *delim, char **word)
        char *out;
        int ret, state = 0;
 
-       out = para_malloc(strlen(buf) + 1);
+       out = alloc(strlen(buf) + 1);
        *out = '\0';
        *word = out;
        for (in = buf; *in; in++) {
@@ -773,7 +739,7 @@ void free_argv(char **argv)
 static int create_argv_offset(int offset, const char *buf, const char *delim,
                char ***result)
 {
-       char *word, **argv = para_malloc((offset + 1) * sizeof(char *));
+       char *word, **argv = arr_alloc(offset + 1, sizeof(char *));
        const char *p;
        int i, ret;
 
@@ -785,7 +751,7 @@ static int create_argv_offset(int offset, const char *buf, const char *delim,
                        goto err;
                if (!ret)
                        break;
-               argv = para_realloc(argv, (i + 2) * sizeof(char*));
+               argv = arr_realloc(argv, i + 2, sizeof(char*));
                argv[i] = word;
        }
        argv[i] = NULL;
@@ -840,27 +806,6 @@ int create_shifted_argv(const char *buf, const char *delim, char ***result)
        return create_argv_offset(1, buf, delim, result);
 }
 
-/**
- * Find out if the given string is contained in the arg vector.
- *
- * \param arg The string to look for.
- * \param argv The array to search.
- *
- * \return The first index whose value equals \a arg, or \p -E_ARG_NOT_FOUND if
- * arg was not found in \a argv.
- */
-int find_arg(const char *arg, char **argv)
-{
-       int i;
-
-       if (!argv)
-               return -E_ARG_NOT_FOUND;
-       for (i = 0; argv[i]; i++)
-               if (strcmp(arg, argv[i]) == 0)
-                       return i;
-       return -E_ARG_NOT_FOUND;
-}
-
 /**
  * Compile a regular expression.
  *
@@ -881,7 +826,7 @@ int para_regcomp(regex_t *preg, const char *regex, int cflags)
        if (ret == 0)
                return 1;
        size = regerror(ret, preg, NULL, 0);
-       buf = para_malloc(size);
+       buf = alloc(size);
        regerror(ret, preg, buf, size);
        PARA_ERROR_LOG("%s\n", buf);
        free(buf);
@@ -907,7 +852,7 @@ char *safe_strdup(const char *src, size_t len)
        char *p;
 
        assert(len < (size_t)-1);
-       p = para_malloc(len + 1);
+       p = alloc(len + 1);
        if (len > 0)
                memcpy(p, src, len);
        p[len] = '\0';
@@ -1061,7 +1006,7 @@ __must_check int strwidth(const char *s, size_t *result)
                return -ERRNO_TO_PARA_ERROR(errno);
        if (num_wchars == 0)
                return 0;
-       dest = para_malloc((num_wchars + 1) * sizeof(*dest));
+       dest = arr_alloc(num_wchars + 1, sizeof(*dest));
        src = s;
        memset(&state, 0, sizeof(state));
        num_wchars = mbsrtowcs(dest, &src, num_wchars, &state);
@@ -1116,7 +1061,7 @@ __must_check int sanitize_str(const char *src, size_t max_width,
        num_wchars = mbsrtowcs(NULL, &src, 0, &state);
        if (num_wchars == (size_t)-1)
                return -ERRNO_TO_PARA_ERROR(errno);
-       wcs = para_malloc((num_wchars + 1) * sizeof(*wcs));
+       wcs = arr_alloc(num_wchars + 1, sizeof(*wcs));
        memset(&state, 0, sizeof(state));
        num_wchars = mbsrtowcs(wcs, &src, num_wchars + 1, &state);
        assert(num_wchars != (size_t)-1);
@@ -1127,7 +1072,7 @@ __must_check int sanitize_str(const char *src, size_t max_width,
        }
        wcs[n] = L'\0';
        n = wcstombs(NULL, wcs, 0) + 1;
-       *result = para_malloc(n);
+       *result = alloc(n);
        num_wchars = wcstombs(*result, wcs, n);
        assert(num_wchars != (size_t)-1);
        free(wcs);
index 10379a0e83098f392e8653467b826925376eda15..d773600fbf0a0168584235a5098beabb110a3416 100644 (file)
--- a/string.h
+++ b/string.h
@@ -67,28 +67,27 @@ int for_each_line(unsigned flags, char *buf, size_t size,
 } \
 )
 
+__must_check void *arr_realloc(void *ptr, size_t nmemb, size_t size);
 __must_check void *para_realloc(void *p, size_t size);
-__must_check __malloc void *para_malloc(size_t size);
-__must_check __malloc void *para_calloc(size_t size);
+__must_check __malloc void *alloc(size_t size);
+__must_check __malloc void *zalloc(size_t size);
+__must_check __malloc void *arr_alloc(size_t nmemb, size_t size);
+__must_check __malloc void *arr_zalloc(size_t nmemb, size_t size);
 __must_check __malloc char *para_strdup(const char *s);
 
 __printf_2_0 unsigned xvasprintf(char **result, const char *fmt, va_list ap);
 __printf_2_3 unsigned xasprintf(char **result, const char *fmt, ...);
 __must_check __malloc __printf_1_2 char *make_message(const char *fmt, ...);
 __must_check __malloc char *para_strcat(char *a, const char *b);
-__must_check __malloc char *para_dirname(const char *name);
-__must_check char *para_basename(const char *name);
 __must_check __malloc char *para_logname(void);
 __must_check __malloc char *para_homedir(void);
 __malloc char *para_hostname(void);
 __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...);
 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);
 int read_size_header(const char *buf);
 int create_argv(const char *buf, const char *delim, char ***result);
 int create_shifted_argv(const char *buf, const char *delim, char ***result);
-int find_arg(const char *arg, char **argv);
 void free_argv(char **argv);
 int para_regcomp(regex_t *preg, const char *regex, int cflags);
 void freep(void *arg);
index f23545adb68342322fbeaef4c45c73c663a969be..b4710f6704ce7a4fbd03567d806ce633df164008 100644 (file)
@@ -102,8 +102,8 @@ static void sync_open(struct filter_node *fn)
        unsigned buddy_given;
        const struct lls_opt_result *r_b;
 
-       ctx = fn->private_data = para_calloc(sizeof(*ctx));
-       INIT_LIST_HEAD(&ctx->buddies);
+       ctx = fn->private_data = zalloc(sizeof(*ctx));
+       init_list_head(&ctx->buddies);
 
        /* create socket to listen for incoming packets */
        ret = makesock(
@@ -148,7 +148,7 @@ static void sync_open(struct filter_node *fn)
                        close(fd);
                        goto fail;
                }
-               buddy = para_malloc(sizeof(*buddy));
+               buddy = alloc(sizeof(*buddy));
                buddy->fd = fd;
                buddy->sbi = sbi + i;
                buddy->ping_received = false;
@@ -176,12 +176,12 @@ static void *sync_setup(const struct lls_parse_result *lpr)
 
        r_b = FILTER_CMD_OPT_RESULT(SYNC, BUDDY, lpr);
        n = lls_opt_given(r_b);
-       sbi = para_malloc(n * sizeof(*sbi));
+       sbi = arr_alloc(n, sizeof(*sbi));
        PARA_INFO_LOG("initializing buddy info array of length %u\n", n);
        for (i = 0; i < n; i++) {
                const char *url = lls_string_val(i, r_b);
                size_t len = strlen(url);
-               char *host = para_malloc(len + 1);
+               char *host = alloc(len + 1);
                int port;
                struct addrinfo *ai;
 
@@ -248,7 +248,7 @@ static void sync_set_timeout(struct sync_filter_context *ctx,
        tv_add(now, &to, &ctx->timeout);
 }
 
-static void sync_pre_select(struct sched *s, void *context)
+static void sync_pre_monitor(struct sched *s, void *context)
 {
        int ret;
        struct filter_node *fn = context;
@@ -261,7 +261,7 @@ static void sync_pre_select(struct sched *s, void *context)
        ret = btr_node_status(fn->btrn, 0, BTR_NT_INTERNAL);
        if (ret < 0)
                return sched_min_delay(s);
-       para_fd_set(ctx->listen_fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(ctx->listen_fd, s);
        if (ret == 0)
                return;
        if (ctx->timeout.tv_sec == 0) { /* must ping buddies */
@@ -284,7 +284,7 @@ static struct sync_buddy *sync_find_buddy(struct sockaddr *addr,
        return NULL;
 }
 
-static int sync_post_select(__a_unused struct sched *s, void *context)
+static int sync_post_monitor(__a_unused struct sched *s, void *context)
 {
        int ret;
        struct filter_node *fn = context;
@@ -324,7 +324,7 @@ static int sync_post_select(__a_unused struct sched *s, void *context)
                }
                ctx->ping_sent = true;
        }
-       if (FD_ISSET(ctx->listen_fd, &s->rfds)) {
+       if (sched_read_ok(ctx->listen_fd, s)) {
                char c;
                for (;;) {
                        struct sockaddr src_addr;
@@ -377,8 +377,8 @@ out:
 const struct filter lsg_filter_cmd_com_sync_user_data = {
        .setup = sync_setup,
        .open = sync_open,
-       .pre_select = sync_pre_select,
-       .post_select = sync_post_select,
+       .pre_monitor = sync_pre_monitor,
+       .post_monitor = sync_post_monitor,
        .close = sync_close,
        .teardown = sync_teardown
 };
index 03957464e17e2a50596149e59627cd7584424fe9..9e681107646e85e0fdcc3e12ec1ed13694376628 100755 (executable)
@@ -24,7 +24,8 @@ get_audio_file_paths ogg
 declare -a oggs=($result)
 declare -a oggs_base=(${oggs[@]##*/})
 
-declare -a commands=() cmdline=() required_objects=() good=() bad=()
+declare -a commands=() cmdline=() required_objects=() good=() bad=() \
+       expect_failure=()
 i=0
 commands[$i]="help"
 cmdline[$i]="help -l"
@@ -72,6 +73,15 @@ required_objects[$i]='ogg_afh'
 cmdline[$i]="ls -l=v ${oggs[@]}"
 good[$i]='^attributes_txt: 33'
 
+let i++
+commands[$i]='addmood'
+cmdline[$i]="addmood test-mood"
+
+let i++
+commands[$i]='empty-mood-parameter'
+cmdline[$i]="select m/"
+expect_failure[$i]='true'
+
 let i++
 commands[$i]="term"
 cmdline[$i]="term"
@@ -137,14 +147,19 @@ for ((i=0; i < ${#commands[@]}; i++)); do
                        continue
                fi
        fi
-       test_expect_success "$command" "
+       if [[ -n "${expect_failure[$i]}" ]]; then
+               f=test_expect_failure
+       else
+               f=test_expect_success
+       fi
+       $f "$command" "
        $PARA_CLIENT \
                --loglevel $loglevel \
                --server-port $port \
                --key-file $privkey \
                --config-file /dev/null \
                -- \
-               ${cmdline[$i]} > $command.out &&
+               ${cmdline[$i]} > $command.out < /dev/null &&
                { [[ -z \"${good[$i]}\" ]] || grep \"${good[$i]}\"; } < $command.out &&
                { [[ -z \"${bad[$i]}\" ]]  || ! grep \"${bad[$i]}\"; } < $command.out
        "
index 58d45ab43d6508619d34186b3f4a23f6d455ecc7..365b6863087ca78c1bdc446099ff7f17c8b15c6c 100644 (file)
 #include "net.h"
 #include "fd.h"
 
-static void udp_recv_pre_select(struct sched *s, void *context)
+static void udp_recv_pre_monitor(struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
 
-       if (generic_recv_pre_select(s, rn) <= 0)
+       if (generic_recv_pre_monitor(s, rn) <= 0)
                return;
-       para_fd_set(rn->fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(rn->fd, s);
 }
 
 static int udp_check_eof(size_t sz, struct iovec iov[2])
@@ -50,7 +50,7 @@ static int udp_check_eof(size_t sz, struct iovec iov[2])
        return -E_RECV_EOF;
 }
 
-static int udp_recv_post_select(__a_unused struct sched *s, void *context)
+static int udp_recv_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
        struct btr_node *btrn = rn->btrn;
@@ -68,7 +68,7 @@ static int udp_recv_post_select(__a_unused struct sched *s, void *context)
        ret = -E_UDP_OVERRUN;
        if (iovcnt == 0)
                goto out;
-       ret = readv_nonblock(rn->fd, iov, iovcnt, &s->rfds, &num_bytes);
+       ret = readv_nonblock(rn->fd, iov, iovcnt, &num_bytes);
        if (num_bytes == 0)
                goto out;
        readv_ret = ret;
@@ -168,7 +168,7 @@ static int udp_recv_open(struct receiver_node *rn)
        uint32_t port = RECV_CMD_OPT_UINT32_VAL(UDP, PORT, lpr);
        int ret;
 
-       ret = makesock(IPPROTO_UDP, 1, host, port, NULL);
+       ret = makesock(IPPROTO_UDP, true /* passive */, host, port, NULL);
        if (ret < 0)
                return ret;
        rn->fd = ret;
@@ -189,6 +189,6 @@ err:
 const struct receiver lsg_recv_cmd_com_udp_user_data = {
        .open = udp_recv_open,
        .close = udp_recv_close,
-       .pre_select = udp_recv_pre_select,
-       .post_select = udp_recv_post_select,
+       .pre_monitor = udp_recv_pre_monitor,
+       .post_monitor = udp_recv_post_monitor,
 };
index 96a25d370a99d3fdcb6782ef9451ae86b1cfc3a4..fe001025bc1ad6c73c0e64c8589977db1e3203af 100644 (file)
@@ -21,8 +21,8 @@
 #include "net.h"
 #include "server.h"
 #include "list.h"
-#include "send.h"
 #include "sched.h"
+#include "send.h"
 #include "vss.h"
 #include "portable_io.h"
 #include "fd.h"
@@ -60,7 +60,7 @@ static void udp_close_target(struct sender_client *sc)
                return;
        if (ut->sent_fec_eof)
                return;
-       PARA_NOTICE_LOG("sending FEC EOF\n");
+       PARA_INFO_LOG("sending FEC EOF\n");
        len = vss_get_fec_eof_packet(&buf);
        /* Ignore write() errors since we are closing the target anyway. */
        if (write(sc->fd, buf, len))
@@ -72,7 +72,8 @@ static void udp_delete_target(struct sender_client *sc, const char *msg)
 {
        struct udp_target *ut = sc->private_data;
 
-       PARA_NOTICE_LOG("deleting %s (%s) from list\n", sc->name, msg);
+       if (!process_is_command_handler())
+               PARA_NOTICE_LOG("deleting %s (%s) from list\n", sc->name, msg);
        udp_close_target(sc);
        /* command handlers already called close_listed_fds() */
        if (!process_is_command_handler()) {
@@ -186,7 +187,7 @@ static int udp_resolve_target(const char *url, struct sender_command_data *scd)
                return ret;
        port = scd->port > 0 ? scd->port : OPT_UINT32_VAL(UDP_DEFAULT_PORT);
 
-       ret = para_connect_simple(IPPROTO_UDP, scd->host, port);
+       ret = para_connect(IPPROTO_UDP, scd->host, port);
        if (ret < 0)
                return ret;
 
@@ -248,7 +249,7 @@ static int udp_init_fec(struct sender_client *sc)
        struct udp_target *ut = sc->private_data;
        int mps;
 
-       PARA_NOTICE_LOG("sending to udp %s\n", sc->name);
+       PARA_INFO_LOG("sending to udp %s\n", sc->name);
        ut->sent_fec_eof = false;
        mps = generic_max_transport_msg_size(sc->fd) - sizeof(struct udphdr);
        PARA_INFO_LOG("current MPS = %d bytes\n", mps);
@@ -325,8 +326,8 @@ static int udp_com_add(struct sender_command_data *scd)
                                sc->name);
                return -E_TARGET_EXISTS;
        }
-       ut = para_calloc(sizeof(*ut));
-       sc = para_calloc(sizeof(*sc));
+       ut = zalloc(sizeof(*ut));
+       sc = zalloc(sizeof(*sc));
        ut->fcp.slices_per_group      = scd->slices_per_group;
        ut->fcp.data_slices_per_group = scd->data_slices_per_group;
        ut->fcp.init_fec              = udp_init_fec;
@@ -335,7 +336,7 @@ static int udp_com_add(struct sender_command_data *scd)
 
        sc->private_data = ut;
        sc->fd = -1;
-       ret = para_connect_simple(IPPROTO_UDP, scd->host, scd->port);
+       ret = para_connect(IPPROTO_UDP, scd->host, scd->port);
        if (ret < 0)
                goto err;
        sc->fd = ret;
@@ -394,7 +395,7 @@ static void udp_init_target_list(void)
        struct sender_command_data scd;
        int i;
 
-       INIT_LIST_HEAD(&targets);
+       init_list_head(&targets);
        for (i = 0; i < OPT_GIVEN(UDP_TARGET); i++) {
                const char *arg = lls_string_val(i, OPT_RESULT(UDP_TARGET));
                if (udp_resolve_target(arg, &scd) < 0)
@@ -427,7 +428,7 @@ static char *udp_help(void)
 /* Initialize the list of udp targets. */
 static void udp_send_init(void)
 {
-       INIT_LIST_HEAD(&targets);
+       init_list_head(&targets);
        sender_status = SENDER_off;
        udp_init_target_list();
        if (!OPT_GIVEN(UDP_NO_AUTOSTART))
diff --git a/upgrade_db.c b/upgrade_db.c
new file mode 100644 (file)
index 0000000..487d46c
--- /dev/null
@@ -0,0 +1,386 @@
+/* Copyright (C) 2020 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
+
+/** \file upgrade_db.c Prepare the paraslash database for paraslash-0.7. */
+
+#include <osl.h>
+#include <lopsub.h>
+#include <regex.h>
+
+#include "upgrade_db.lsg.h"
+#include "para.h"
+#include "error.h"
+#include "string.h"
+#include "fd.h"
+#include "crypt.h"
+#include "version.h"
+
+#define CMD_PTR (lls_cmd(0, upgrade_db_suite))
+#define OPT_RESULT(_name, _lpr) \
+       (lls_opt_result(LSG_UPGRADE_DB_PARA_UPGRADE_DB_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name, _lpr) (lls_opt_given(OPT_RESULT(_name, _lpr)))
+#define OPT_UINT32_VAL(_name, _lpr) (lls_uint32_val(0, OPT_RESULT(_name, _lpr)))
+#define OPT_STRING_VAL(_name, _lpr) (lls_string_val(0, OPT_RESULT(_name, _lpr)))
+
+static int loglevel;
+INIT_STDERR_LOGGING(loglevel);
+
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
+
+static void handle_help_flag(struct lls_parse_result *lpr)
+{
+       char *help;
+
+       if (OPT_GIVEN(DETAILED_HELP, lpr))
+               help = lls_long_help(CMD_PTR);
+       else if (OPT_GIVEN(HELP, lpr))
+               help = lls_short_help(CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       exit(EXIT_SUCCESS);
+}
+
+static struct stat *path_exists(const char *path)
+{
+       static struct stat sb;
+
+       if (stat(path, &sb) < 0)
+               return NULL;
+       return &sb;
+}
+
+static bool is_dir(const char *path)
+{
+       struct stat *sb = path_exists(path);
+       if (!sb)
+               return false;
+       return (sb->st_mode & S_IFMT) == S_IFDIR;
+}
+
+__noreturn static void die(const char *msg)
+{
+       PARA_EMERG_LOG("%s\n", msg);
+       exit(EXIT_FAILURE);
+}
+
+static int string_compare(const struct osl_object *obj1, const struct osl_object *obj2)
+{
+       const char *str1 = obj1->data;
+       const char *str2 = obj2->data;
+       return strncmp(str1, str2, PARA_MIN(obj1->size, obj2->size));
+}
+
+static char *src_db_dir, *dst_db_dir, *src_aft_dir, *dst_aft_dir;
+
+static void set_paths(const struct lls_parse_result *lpr)
+{
+       char *home = para_homedir();
+
+       if (OPT_GIVEN(SRC_DATABASE_DIR, lpr))
+               src_db_dir = para_strdup(OPT_STRING_VAL(SRC_DATABASE_DIR,
+                       lpr));
+       else
+               src_db_dir = make_message(
+                       "%s/.paraslash/afs_database-0.4", home);
+       if (OPT_GIVEN(DST_DATABASE_DIR, lpr))
+               dst_db_dir = para_strdup(OPT_STRING_VAL(DST_DATABASE_DIR,
+                       lpr));
+       else
+               dst_db_dir = make_message(
+                       "%s/.paraslash/afs_database-0.7", home);
+       free(home);
+       src_aft_dir = make_message("%s/audio_files", src_db_dir);
+       dst_aft_dir = make_message("%s/audio-files", src_db_dir);
+       PARA_NOTICE_LOG("source aft dir: %s\n", src_aft_dir);
+       PARA_NOTICE_LOG("destination aft dir: %s\n", dst_aft_dir);
+}
+
+static void check_sanity(void)
+{
+       PARA_INFO_LOG("checking source and destination directories\n");
+       if (!is_dir(src_db_dir))
+               die("source db directory does not exist");
+       if (path_exists(dst_db_dir))
+               die("destination db already exists");
+       if (!is_dir(src_aft_dir))
+               die("source audio file table does not exist");
+       if (path_exists(dst_aft_dir))
+               die("destination audio file table already exists");
+}
+
+/** The columns of the audio file table (both old and new). */
+enum audio_file_table_columns {
+       /** The hash on the content of the audio file. */
+       AFTCOL_HASH,
+       /** The full path in the filesystem. */
+       AFTCOL_PATH,
+       /** The audio file selector info. */
+       AFTCOL_AFSI,
+       /** The audio format handler info. */
+       AFTCOL_AFHI,
+       /** The chunk table info and the chunk table of the audio file. */
+       AFTCOL_CHUNKS,
+       /** The number of columns of this table. */
+       NUM_AFT_COLUMNS
+};
+
+#define AFSI_SIZE 32
+
+static int src_aft_hash_compare(const struct osl_object *obj1,
+               const struct osl_object *obj2)
+{
+       return hash_compare((unsigned char *)obj1->data,
+               (unsigned char *)obj2->data);
+}
+
+static struct osl_column_description src_aft_cols[] = {
+       [AFTCOL_HASH] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE,
+               .name = "hash",
+               .compare_function = src_aft_hash_compare,
+               .data_size = HASH_SIZE
+       },
+       [AFTCOL_PATH] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .storage_flags = OSL_RBTREE | OSL_UNIQUE,
+               .name = "path",
+               .compare_function = string_compare,
+       },
+       [AFTCOL_AFSI] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .storage_flags = OSL_FIXED_SIZE,
+               .name = "afs_info",
+               .data_size = AFSI_SIZE
+       },
+       [AFTCOL_AFHI] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .name = "afh_info",
+       },
+       [AFTCOL_CHUNKS] = {
+               .storage_type = OSL_DISK_STORAGE,
+               .name = "chunks",
+       }
+};
+
+static struct osl_table_description src_aft_desc = {
+       .name = "audio_files",
+       .num_columns = NUM_AFT_COLUMNS,
+       .flags = OSL_LARGE_TABLE,
+       .column_descriptions = src_aft_cols
+};
+
+static struct osl_table *src_aft, *dst_aft;
+
+static void open_src_aft(void)
+{
+       int ret;
+
+       PARA_NOTICE_LOG("opening: %s\n", src_aft_dir);
+       src_aft_desc.dir = src_db_dir;
+       ret = osl(osl_open_table(&src_aft_desc, &src_aft));
+       if (ret < 0) {
+               PARA_EMERG_LOG("can not open source audio file table: %s\n",
+                        para_strerror(-ret));
+               exit(EXIT_FAILURE);
+       }
+       PARA_INFO_LOG("successfully opened source audio file table\n");
+}
+
+static int dst_aft_hash_compare(const struct osl_object *obj1,
+               const struct osl_object *obj2)
+{
+       return hash2_compare((unsigned char *)obj1->data,
+               (unsigned char *)obj2->data);
+}
+
+/* identical to src_aft_cols except the comparator and the hash size. */
+static struct osl_column_description dst_aft_cols[] = {
+       [AFTCOL_HASH] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE,
+               .name = "hash",
+               .compare_function = dst_aft_hash_compare,
+               .data_size = HASH2_SIZE
+       },
+       [AFTCOL_PATH] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .storage_flags = OSL_RBTREE | OSL_UNIQUE,
+               .name = "path",
+               .compare_function = string_compare,
+       },
+       [AFTCOL_AFSI] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .storage_flags = OSL_FIXED_SIZE,
+               .name = "afs_info",
+               .data_size = AFSI_SIZE
+       },
+       [AFTCOL_AFHI] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .name = "afh_info",
+       },
+       [AFTCOL_CHUNKS] = {
+               .storage_type = OSL_DISK_STORAGE,
+               .name = "chunks",
+       }
+};
+
+static struct osl_table_description dst_aft_desc = {
+       .name = "audio-files",
+       .num_columns = NUM_AFT_COLUMNS,
+       .flags = OSL_LARGE_TABLE,
+       .column_descriptions = dst_aft_cols
+};
+
+static int create_and_open_dst_aft(void)
+{
+       int ret;
+
+       PARA_NOTICE_LOG("creating %s\n", dst_aft_dir);
+       dst_aft_desc.dir = src_db_dir;
+       ret = osl(osl_create_table(&dst_aft_desc));
+       if (ret < 0) {
+               PARA_EMERG_LOG("could not create destination audio file table\n");
+               return ret;
+       }
+       ret = osl(osl_open_table(&dst_aft_desc, &dst_aft));
+       if (ret < 0) {
+               PARA_EMERG_LOG("could not open destination audio file table: %s\n",
+                        para_strerror(-ret));
+               exit(EXIT_FAILURE);
+       }
+       PARA_INFO_LOG("successfully opened destination audio file table\n");
+       return 0;
+}
+
+static int copy_aft_row(struct osl_row *row, void *data)
+{
+       unsigned *n = data;
+       int i, ret;
+       unsigned char hash2[HASH2_SIZE] = "\0";
+       struct osl_object objs[NUM_AFT_COLUMNS] = {
+               [AFTCOL_HASH] = {.data = hash2, .size = HASH2_SIZE}
+       };
+
+       ret = osl(osl_open_disk_object(src_aft, row, AFTCOL_CHUNKS,
+               objs + AFTCOL_CHUNKS));
+       if (ret < 0) {
+               PARA_ERROR_LOG("can not open disk object: %s\n",
+                       para_strerror(-ret));
+               return ret;
+       }
+       for (i = 0; i < NUM_AFT_COLUMNS; i++) {
+               if (i == AFTCOL_HASH) /* never assign to this index */
+                       continue;
+               if (i == AFTCOL_CHUNKS) /* disk storage object handled above */
+                       continue;
+               /* mapped storage */
+               ret = osl(osl_get_object(src_aft, row, i, objs + i));
+               if (ret < 0) {
+                       PARA_ERROR_LOG("get_object (col = %d): %s\n",
+                               i, para_strerror(-ret));
+                       return ret;
+               }
+               if (i == AFTCOL_PATH)
+                       PARA_DEBUG_LOG("copying %s\n", (char *)objs[i].data);
+       }
+       (*n)++;
+       memcpy(hash2, n, sizeof(*n));
+       ret = osl(osl_add_row(dst_aft, objs));
+       if (ret < 0)
+               PARA_ERROR_LOG("failed to add row: %s\n", para_strerror(-ret));
+       osl_close_disk_object(objs + AFTCOL_CHUNKS);
+       return ret;
+}
+
+static int convert_aft(void)
+{
+       unsigned n;
+       int ret;
+
+       osl_get_num_rows(src_aft, &n);
+       PARA_NOTICE_LOG("converting hash of %u rows to sha256\n", n);
+       n = 0;
+       ret = osl(osl_rbtree_loop(src_aft, AFTCOL_HASH, &n, copy_aft_row));
+       if (ret < 0)
+               PARA_ERROR_LOG("osl_rbtree_loop failed\n");
+       return ret;
+}
+
+static int remove_source_aft(void)
+{
+       pid_t pid;
+       int fds[3] = {-1, -1, -1}; /* no redirection of stdin/stdout/stderr */
+       int ret, wstatus;
+       char *cmdline = make_message("rm -rf %s", src_aft_dir);
+
+       PARA_NOTICE_LOG("removing %s\n", src_aft_dir);
+       ret = para_exec_cmdline_pid(&pid, cmdline, fds);
+       if (ret < 0) {
+               PARA_ERROR_LOG("exec failure\n");
+               goto out;
+       }
+       do {
+               ret = waitpid(pid, &wstatus, 0);
+       } while (ret < 0 && errno == EINTR);
+       if (ret < 0)
+               PARA_ERROR_LOG("waitpid failure\n");
+out:
+       return ret;
+}
+
+static int rename_db(void)
+{
+       PARA_NOTICE_LOG("renaming %s -> %s\n", src_db_dir, dst_db_dir);
+       if (rename(src_db_dir, dst_db_dir) < 0) {
+               int ret = -ERRNO_TO_PARA_ERROR(errno);
+               PARA_ERROR_LOG("rename failed\n");
+               return ret;
+       }
+       return 1;
+}
+
+int main(int argc, char *argv[])
+{
+       struct lls_parse_result *lpr; /* command line */
+       char *errctx;
+       int ret;
+
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL, lpr);
+       version_handle_flag("recv", OPT_GIVEN(VERSION, lpr));
+       handle_help_flag(lpr);
+       set_paths(lpr);
+       check_sanity();
+       open_src_aft();
+       ret = create_and_open_dst_aft();
+       if (ret < 0)
+               goto close_src_aft;
+       ret = convert_aft();
+       if (ret < 0)
+               goto close_dst_aft;
+       ret = remove_source_aft();
+       if (ret < 0)
+               goto close_dst_aft;
+       ret = rename_db();
+close_dst_aft:
+       osl_close_table(dst_aft, OSL_MARK_CLEAN);
+close_src_aft:
+       PARA_INFO_LOG("closing audio file tables\n");
+       osl_close_table(src_aft, OSL_MARK_CLEAN);
+out:
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       } else {
+               PARA_WARNING_LOG("success. Now start para_server and force-add"
+                       " all audio files.\n");
+       }
+       return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
+}
index 32a4309d4360fa73a8e7d0bbef622a7928001bb0..f2436c9e5bf7a835db2553c4c54db112d628b40d 100644 (file)
@@ -110,7 +110,7 @@ void user_list_init(const char *user_list_file)
                        apc_free_pubkey(pubkey);
                        continue;
                }
-               u = para_malloc(sizeof(*u));
+               u = alloc(sizeof(*u));
                u->name = para_strdup(n);
                u->pubkey = pubkey;
                u->perms = 0;
diff --git a/vss.c b/vss.c
index 235219c1148aac2412b1b41879bf045d0b571af8..dd4ac2fbc96f54064f0dacd38ef2c9c39fa4f065 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -28,8 +28,8 @@
 #include "net.h"
 #include "server.h"
 #include "list.h"
-#include "send.h"
 #include "sched.h"
+#include "send.h"
 #include "vss.h"
 #include "ipc.h"
 #include "fd.h"
@@ -43,7 +43,7 @@ const struct sender * const senders[] = {
 enum afs_socket_status {
        /** Socket is inactive. */
        AFS_SOCKET_READY,
-       /** Socket fd was included in the write fd set for select(). */
+       /** Socket fd was monitored for writing. */
        AFS_SOCKET_CHECK_FOR_WRITE,
        /** vss wrote a request to the socket and waits for reply from afs. */
        AFS_SOCKET_AFD_PENDING
@@ -335,10 +335,10 @@ static int initialize_fec_client(struct fec_client *fc, struct vss_task *vsst)
        ret = fec_new(k, n, &fc->parms);
        if (ret < 0)
                return ret;
-       fc->src_data = para_malloc(k * sizeof(char *));
+       fc->src_data = arr_alloc(k, sizeof(char *));
        for (i = 0; i < k; i++)
-               fc->src_data[i] = para_malloc(fc->mps);
-       fc->enc_buf = para_malloc(fc->mps);
+               fc->src_data[i] = alloc(fc->mps);
+       fc->enc_buf = alloc(fc->mps);
 
        fc->state = FEC_STATE_READY_TO_RUN;
        fc->next_header_time.tv_sec = 0;
@@ -348,7 +348,7 @@ static int initialize_fec_client(struct fec_client *fc, struct vss_task *vsst)
 }
 
 static int vss_get_chunk(int chunk_num, struct vss_task *vsst,
-               char **buf, size_t *sz)
+               char **buf, uint32_t *len)
 {
        int ret;
 
@@ -363,15 +363,15 @@ static int vss_get_chunk(int chunk_num, struct vss_task *vsst,
        if (chunk_num == 0 && vsst->header_len > 0) {
                assert(vsst->header_buf);
                *buf = vsst->header_buf; /* stripped header */
-               *sz = vsst->header_len;
+               *len = vsst->header_len;
                return 0;
        }
        ret = afh_get_chunk(chunk_num, &mmd->afd.afhi,
                mmd->afd.audio_format_id, vsst->map, vsst->mapsize,
-               (const char **)buf, sz, &vsst->afh_context);
+               (const char **)buf, len, &vsst->afh_context);
        if (ret < 0) {
                *buf = NULL;
-               *sz = 0;
+               *len = 0;
        }
        return ret;
 }
@@ -380,7 +380,7 @@ static int compute_group_size(struct vss_task *vsst, struct fec_group *g,
                int max_bytes)
 {
        char *buf;
-       size_t len;
+       uint32_t len;
        int ret, i, max_chunks = PARA_MAX(1LU, 150 / tv2ms(vss_chunk_time()));
 
        if (g->first_chunk == 0) {
@@ -587,7 +587,7 @@ static int setup_next_fec_group(struct fec_client *fc, struct vss_task *vsst)
        slice_copied = 0;
        for (c = g->first_chunk; c < g->first_chunk + g->num_chunks; c++) {
                char *buf;
-               size_t src_len;
+               uint32_t src_len;
                ret = vss_get_chunk(c, vsst, &buf, &src_len);
                if (ret < 0)
                        return ret;
@@ -671,7 +671,7 @@ size_t vss_get_fec_eof_packet(const char **buf)
 struct fec_client *vss_add_fec_client(struct sender_client *sc,
                                      struct fec_client_parms *fcp)
 {
-       struct fec_client *fc = para_calloc(sizeof(*fc));
+       struct fec_client *fc = zalloc(sizeof(*fc));
 
        fc->sc  = sc;
        fc->fcp = fcp;
@@ -827,7 +827,7 @@ static void vss_compute_timeout(struct sched *s, struct vss_task *vsst)
        if (sched_request_barrier(&vsst->data_send_barrier, s) == 1)
                return;
        /*
-        * Compute the select timeout as the minimal time until the next
+        * Compute the I/O timeout as the minimal time until the next
         * chunk/slice is due for any client.
         */
        compute_chunk_time(mmd->chunks_sent, &mmd->afd.afhi.chunk_tv,
@@ -892,21 +892,21 @@ static void set_mmd_offset(void)
        mmd->offset = tv2ms(&offset);
 }
 
-static void vss_pre_select(struct sched *s, void *context)
+static void vss_pre_monitor(struct sched *s, void *context)
 {
        int i;
        struct vss_task *vsst = context;
 
        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);
+               sched_monitor_writefd(vsst->afs_socket, s);
                vsst->afsss = AFS_SOCKET_CHECK_FOR_WRITE;
        } else
-               para_fd_set(vsst->afs_socket, &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(vsst->afs_socket, s);
        FOR_EACH_SENDER(i) {
-               if (!senders[i]->pre_select)
+               if (!senders[i]->pre_monitor)
                        continue;
-               senders[i]->pre_select(&s->max_fileno, &s->rfds, &s->wfds);
+               senders[i]->pre_monitor(s);
        }
        vss_compute_timeout(s, vsst);
 }
@@ -950,13 +950,13 @@ static int recv_afs_msg(int afs_socket, int *fd, uint32_t *code, uint32_t *data)
 #define MAP_POPULATE 0
 #endif
 
-static void recv_afs_result(struct vss_task *vsst, fd_set *rfds)
+static void recv_afs_result(struct vss_task *vsst, const struct sched *s)
 {
        int ret, passed_fd, shmid;
        uint32_t afs_code = 0, afs_data = 0;
        struct stat statbuf;
 
-       if (!FD_ISSET(vsst->afs_socket, rfds))
+       if (!sched_read_ok(vsst->afs_socket, s))
                return;
        ret = recv_afs_msg(vsst->afs_socket, &passed_fd, &afs_code, &afs_data);
        if (ret == -ERRNO_TO_PARA_ERROR(EAGAIN))
@@ -1016,7 +1016,7 @@ err:
 /**
  * Main sending function.
  *
- * This function gets called from vss_post_select(). It checks whether the next
+ * This function gets called from vss_post_monitor(). It checks whether the next
  * chunk of data should be pushed out. It obtains a pointer to the data to be
  * sent out as well as its length from mmd->afd.afhi. This information is then
  * passed to each supported sender's send() function as well as to the send()
@@ -1029,7 +1029,7 @@ static void vss_send(struct vss_task *vsst)
        struct timeval due;
        struct fec_client *fc, *tmp_fc;
        char *buf;
-       size_t len;
+       uint32_t len;
 
        if (!vsst->map || !vss_playing())
                return;
@@ -1087,7 +1087,7 @@ static void vss_send(struct vss_task *vsst)
        mmd->current_chunk++;
 }
 
-static int vss_post_select(struct sched *s, void *context)
+static int vss_post_monitor(struct sched *s, void *context)
 {
        int ret, i;
        struct vss_task *vsst = context;
@@ -1137,9 +1137,9 @@ static int vss_post_select(struct sched *s, void *context)
                mmd->sender_cmd_data.cmd_num = -1;
        }
        if (vsst->afsss != AFS_SOCKET_CHECK_FOR_WRITE)
-               recv_afs_result(vsst, &s->rfds);
-       else if (FD_ISSET(vsst->afs_socket, &s->wfds)) {
-               PARA_NOTICE_LOG("requesting new fd from afs\n");
+               recv_afs_result(vsst, s);
+       else if (sched_write_ok(vsst->afs_socket, s)) {
+               PARA_INFO_LOG("requesting new fd from afs\n");
                ret = write_buffer(vsst->afs_socket, "new");
                if (ret < 0)
                        PARA_CRIT_LOG("%s\n", para_strerror(-ret));
@@ -1147,9 +1147,9 @@ static int vss_post_select(struct sched *s, void *context)
                        vsst->afsss = AFS_SOCKET_AFD_PENDING;
        }
        FOR_EACH_SENDER(i) {
-               if (!senders[i]->post_select)
+               if (!senders[i]->post_monitor)
                        continue;
-               senders[i]->post_select(&s->rfds, &s->wfds);
+               senders[i]->post_monitor(s);
        }
        if ((vss_playing() && !(mmd->vss_status_flags & VSS_PLAYING)) ||
                        (vss_next() && vss_playing()))
@@ -1177,7 +1177,7 @@ void vss_init(int afs_socket, struct sched *s)
        vsst->afs_socket = afs_socket;
        ms2tv(announce_time, &vsst->announce_tv);
        PARA_INFO_LOG("announce timeval: %lums\n", tv2ms(&vsst->announce_tv));
-       INIT_LIST_HEAD(&fec_client_list);
+       init_list_head(&fec_client_list);
        FOR_EACH_SENDER(i) {
                PARA_NOTICE_LOG("initializing %s sender\n", senders[i]->name);
                senders[i]->init();
@@ -1194,8 +1194,8 @@ void vss_init(int afs_socket, struct sched *s)
        }
        vsst->task = task_register(&(struct task_info) {
                .name = "vss",
-               .pre_select = vss_pre_select,
-               .post_select = vss_post_select,
+               .pre_monitor = vss_pre_monitor,
+               .post_monitor = vss_post_monitor,
                .context = vsst,
        }, s);
 }
@@ -1208,11 +1208,14 @@ void vss_init(int afs_socket, struct sched *s)
 void vss_shutdown(void)
 {
        int i;
+       bool is_command_handler = process_is_command_handler();
 
        FOR_EACH_SENDER(i) {
                if (!senders[i]->shutdown)
                        continue;
-               PARA_NOTICE_LOG("shutting down %s sender\n", senders[i]->name);
+               if (!is_command_handler)
+                       PARA_NOTICE_LOG("shutting down %s sender\n",
+                               senders[i]->name);
                senders[i]->shutdown();
        }
 }
index e749160d3337e87dc7c908c023ea100c955590e4..7d6c371433b13ae6f6d3d63e8de47f69fe133a17 100644 (file)
@@ -53,12 +53,12 @@ static void wav_open(struct filter_node *fn)
 {
        int *bof;
 
-       fn->private_data = para_malloc(sizeof(int));
+       fn->private_data = alloc(sizeof(int));
        bof = fn->private_data;
        *bof = 1;
 }
 
-static void wav_pre_select(struct sched *s, void *context)
+static void wav_pre_monitor(struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        size_t iqs = btr_get_input_queue_size(fn->btrn);
@@ -68,7 +68,7 @@ static void wav_pre_select(struct sched *s, void *context)
        sched_min_delay(s);
 }
 
-static int wav_post_select(__a_unused struct sched *s, void *context)
+static int wav_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
@@ -101,7 +101,7 @@ static int wav_post_select(__a_unused struct sched *s, void *context)
        free(buf);
        if (ret < 0)
                goto err;
-       header = para_malloc(WAV_HEADER_LEN);
+       header = alloc(WAV_HEADER_LEN);
        make_wav_header(ch, rate, header);
        btr_add_output(header, WAV_HEADER_LEN, btrn);
        ret = -E_WAV_SUCCESS;
@@ -118,6 +118,6 @@ err:
 const struct filter lsg_filter_cmd_com_wav_user_data = {
        .close = wav_close,
        .open = wav_open,
-       .pre_select = wav_pre_select,
-       .post_select = wav_post_select,
+       .pre_monitor = wav_pre_monitor,
+       .post_monitor = wav_post_monitor,
 };
index 8395864a00a6e376634272954708cd96739afc2f..f188ff2566ad30645365e73aa62e399439dfbf5e 100644 (file)
@@ -1,8 +1,8 @@
 <h1>About</h1>
 <hr>
 
-Paraslash is a collection of network audio streaming tools for Unix
-systems. It is written in C and released under the GPLv2.
+<p> Paraslash is a collection of network audio streaming tools for
+Unix systems. It is written in C and released under the GPLv2. </p>
 
 <ul>
        <li> Runs on Linux, FreeBSD, NetBSD </li>
@@ -10,15 +10,17 @@ systems. It is written in C and released under the GPLv2.
        <li> http, dccp and udp network streaming </li>
        <li> Stand-alone decoder, player, tagger </li>
        <li> Curses-based gui (<a href="gui.png">screenshot</a>) </li>
-       <li> Integrated volume normalizer, fader, alarm clock </li>
+       <li> Volume normalizer, fader, alarm clock </li>
        <li> Sophisticated audio file selector </li>
        <li> Command line interface with tab-completion </li>
        <li> Open source and well documented </li>
 </ul>
 
-<b> Author: </b> Andr&eacute; Noll,
-<a href="mailto:maan@tuebingen.mpg.de">maan@tuebingen.mpg.de</a>,
-Homepage: <a href="http://people.tuebingen.mpg.de/maan/">http://people.tuebingen.mpg.de/maan/</a>
-<br>
-Comments and bug reports are welcome. Please provide the version of
-paraslash you are using and relevant parts of the logs.
+<p> Author: Andre Noll, <a
+href="mailto:maan@tuebingen.mpg.de">maan@tuebingen.mpg.de</a>,
+Homepage: <a
+href="http://people.tuebingen.mpg.de/maan/">http://people.tuebingen.mpg.de/maan/</a>
+</p>
+
+<p> Comments and bug reports are welcome. Please provide the version
+of paraslash you are using and relevant parts of the logs. </p>
index d6d690a16bd5299a6fdb8d304cfe48fc13dbc6c3..32efe9c3b191ff2bfa6df658480e63a884e3952f 100644 (file)
        [<a href="para_filter.man.html">para_filter</a>]
        [<a href="para_write.man.html">para_write</a>]
        [<a href="para_gui.man.html">para_gui</a>]
-       [<a href="para_mixer.man.html">para_mixere</a>]
+       [<a href="para_mixer.man.html">para_mixer</a>]
        [<a href="para_play.man.html">para_play</a>]
+       [<a href="para_upgrade_db.man.html">para_upgrade_db</a>]
 </p>
 
 <h2> Source code documentation </h2>
 
 <ul>
-       <li> <a href="doxygen/html/index.html">API Reference</a> </li>
+       <li> <a href="doxygen/html/files.html">API Reference</a> </li>
 </ul>
index 9ef92b7af654ec06f193ad5dd3941648b82a83b8..2b31e5d76c7f9f7c348ba08f852dc6c5a5780f73 100644 (file)
@@ -1,8 +1,9 @@
 <h1>Download</h1>
 <hr>
 
-Paraslash is only available as source code, no binary packages are
-provided at this point. There are several ways to download the source:
+<p> Paraslash is only available as source code, no binary packages
+are provided at this point. There are several ways to download the
+source: </p>
 
 <ul>
        <li> <em> git</em>.
@@ -19,7 +20,7 @@ provided at this point. There are several ways to download the source:
                checkout of any of the four integration branches maint,
                master, next, pu (see the
 
-               <a href="manual.html#Git.branches">Git branches</a>
+               <a href="manual.html#Git-branches">Git branches</a>
 
                section of the manual). All previous releases
                correspond to tagged commits and may be checked out
index 38572c940dac47cafcd170a64c04a21953dc74bf..cceeff1a3097597c261254b020c1ffbe0dd3ef19 100644 (file)
@@ -1,5 +1,3 @@
 <hr>
-       </td>
-       </table>
 </body>
 </html>
index a5d01204939d789cf4841499ce31119bfd5e1c35..1e102a6b3e7732c43d3c8eaa56797540022cb134 100644 (file)
 <body>
        <table>
        <tr>
-               <td>
-                       <a title="paraslash homepage" href=".">
-                               <img src="paraslash.png" alt="paraslash">
+               <td rowspan="2">
+                       <a title="paraslash homepage" href="./index.html">
+                               <img src="paraslash.svg" alt="paraslash">
                        </a>
                </td>
                <td>
-                       <h3>Paraslash network audio streaming tools</h3>
+                       <span class="slogan">Paraslash Audio Streaming</span>
                </td>
        </tr>
        <tr>
-               <td valign="top">
-                       <br>
-                       <a href=".">About</a><br>
-                       <a href="news.html">News</a><br>
-                       <a href="download.html">Download</a><br>
-                       <a href="documentation.html">Documentation</a><br>
-                       <a href="devel.html">Development</a><br>
-               </td>
                <td>
+                       <a href="./index.html">About</a>&nbsp;
+                       &nbsp;<a href="news.html">News</a>&nbsp;
+                       &nbsp;<a href="download.html">Download</a>&nbsp;
+                       &nbsp;<a href="documentation.html">Documentation</a>&nbsp;
+                       &nbsp;<a href="devel.html">Development</a>
+               </td>
+       </tr>
+       </table>
+<hr>
index 98235b8621324bc9176d2f9803ea4cf097afd83a..29ec02d40eb266c3f1eaa2617b6c609cd1d90b40 100644 (file)
 <body>
        <table>
        <tr>
-               <td>
-                       <a title="paraslash homepage" href="../..//">
-                               <img src="../../paraslash.png" alt="paraslash">
+               <td rowspan="2">
+                       <a title="paraslash homepage" href="../../index.html">
+                               <img src="../../paraslash.svg" alt="paraslash">
                        </a>
                </td>
                <td>
-                       <h3>Paraslash network audio streaming tools</h3>
+                       <span class="slogan">Paraslash Audio Streaming</span>
                </td>
        </tr>
        <tr>
-               <td valign="top">
-                       <br>
-                       <a href="../..">About</a><br>
-                       <a href="../../news.html">News</a><br>
-                       <a href="../../download.html">Download</a><br>
-                       <a href="../../documentation.html">Documentation</a><br>
-                       <a href="../../devel.html">Development</a><br>
-               </td>
                <td>
-                       <h1>API Reference</h1>
-                       <hr />
-
+                       <a href="../../index.html">About</a>&nbsp;
+                       &nbsp;<a href="../../news.html">News</a>&nbsp;
+                       &nbsp;<a href="../../download.html">Download</a>&nbsp;
+                       &nbsp;<a href="../../documentation.html">Documentation</a>&nbsp;
+                       &nbsp;<a href="../../devel.html">Development</a>
+               </td>
+       </tr>
+       </table>
+<hr>
index 06ff03362e8a33dfe2b02bd2c214892dd3ba59c4..65a0dd69fd3e52d529e63b8ea039a3b32a7cbce4 100644 (file)
Binary files a/web/images/paraslash.ico and b/web/images/paraslash.ico differ
diff --git a/web/images/paraslash.png b/web/images/paraslash.png
deleted file mode 100644 (file)
index 804752c..0000000
Binary files a/web/images/paraslash.png and /dev/null differ
diff --git a/web/images/paraslash.svg b/web/images/paraslash.svg
new file mode 100644 (file)
index 0000000..b0616a7
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0" standalone="no"?>
+<svg
+       xmlns="http://www.w3.org/2000/svg"
+       width="80" height="100"
+       xmlns:xlink="http://www.w3.org/1999/xlink"
+>
+       <defs>
+               <radialGradient id="gradient" r="70%">
+                       <stop offset="0%" stop-color="#d40"/>
+                       <stop offset="100%" stop-color="#010"/>
+               </radialGradient>
+
+               <g
+                       id="bow"
+                       stroke="#111" stroke-width="0"
+                       fill="url(#gradient)"
+               >
+                       <path d="M 40,50 c 0,-21 30,-21 30,0"/>
+               </g>
+       </defs>
+       <g transform="scale(1.0, 1.3) translate(0, -11)">
+               <circle
+                       cx="40" cy="50" r="38"
+                       stroke-width="0"
+                       fill="url(#gradient)"
+               />
+               <use
+                       xlink:href="#bow"
+                       transform="
+                               scale(1.5)
+                               rotate(320, 40,50)
+                               translate(-14, -24)
+                       "
+               />
+               <use
+                       xlink:href="#bow"
+                       transform="
+                               translate(0, -36)
+                               scale(1.5)
+                               rotate(140, 40,50)
+                       "
+               />
+       </g>
+</svg>
index db28a699bdd05516d17de4bf837cc110f737df7e..b5329ea07f3d8a2f9553f384c9129c4ad1a262e3 100644 (file)
@@ -309,8 +309,8 @@ repository with
                git clone git://git.tuebingen.mpg.de/lopsub
 
 - [gcc](ftp://ftp.gnu.org/pub/gnu/gcc) or
-[clang](http://clang.llvm.org). All gcc versions >= 4.2 are currently
-supported. Clang version 1.1 or newer should work as well.
+[clang](http://clang.llvm.org). All gcc versions >= 5.4 are currently
+supported. Moderately recent versions of clang should work as well.
 
 - [gnu make](ftp://ftp.gnu.org/pub/gnu/make) is also shipped with the
 disto. On BSD systems the gnu make executable is often called gmake.
@@ -488,10 +488,10 @@ An empty database is created with
        para_client init
 
 This initializes a couple of empty tables under
-~/.paraslash/afs_database-0.4. You normally don't need to look at these
+~/.paraslash/afs_database-0.7. You normally don't need to look at these
 tables, but it's good to know that you can start from scratch with
 
-       rm -rf ~/.paraslash/afs_database-0.4
+       rm -rf ~/.paraslash/afs_database-0.7
 
 in case something went wrong.
 
@@ -613,10 +613,11 @@ while the second part is the session key.
 
 - para_client receives the encrypted buffer and decrypts it with the
 user's private key, thereby obtaining the challenge buffer and the
-session key. It sends the SHA1 hash value of the challenge back to
-para_server and stores the session key for further use.
+session key. It hashes the challenge buffer with a crytographic hash
+function, sends the hash value back to para_server and stores the
+session key for further use.
 
-- para_server also computes the SHA1 hash of the challenge and compares
+- para_server also computes the hash value of the challenge and compares
 it against what was sent back by the client.
 
 - If the two hashes do not match, the authentication has failed and
@@ -630,7 +631,7 @@ the session key known to both peers.
 paraslash relies on the quality of the pseudo-random bytes provided
 by the crypto library (openssl or libgcrypt), on the security of
 the implementation of the RSA and AES crypto routines and on the
-infeasibility to invert the SHA1 function.
+infeasibility to invert the hash function.
 
 Neither para_server or para_client create RSA keys on their
 own. This has to be done once for each user as sketched in
@@ -804,10 +805,11 @@ This is the most important and usually also the largest table of the
 AFS database. It contains the information needed to stream each audio
 file. In particular the following data is stored for each audio file.
 
-- SHA1 hash value of the audio file contents. This is computed once
-when the file is added to the database. Whenever AFS selects this
-audio file for streaming the hash value is recomputed and checked
-against the value stored in the database to detect content changes.
+- The cryptographic hash value of the audio file contents. This is
+computed once when the file is added to the database. Whenever AFS
+selects this audio file for streaming the hash value is recomputed
+and checked against the value stored in the database to detect
+content changes.
 
 - The time when this audio file was last played.
 
@@ -1023,6 +1025,7 @@ Keyword              |    Type | Semantic value
 `bitrate`            | integer | The average bitrate
 `frequency`          | integer | The output sample rate
 `channels`           | integer | The number of channels
+`duration`           | integer | The number of milliseconds
 `is_set("foo")`      | boolean | True if attribute "foo" is set.
 
 [\*] For most audio formats, the year tag is stored as a string. It
@@ -1146,7 +1149,7 @@ if the "-a" switch is given:
 File renames and content changes
 --------------------------------
 
-Since the audio file selector knows the SHA1 of each audio file that
+Since the audio file selector knows the hash of each audio file that
 has been added to the afs database, it recognizes if the content of
 a file has changed, e.g. because an ID3 tag was added or modified.
 Also, if a file has been renamed or moved to a different location,
@@ -1175,14 +1178,14 @@ may refuse to start again because of "dirty osl tables". In this
 case you'll have to run the oslfsck program of libosl to fix your
 database:
 
-       oslfsck -fd ~/.paraslash/afs_database-0.4
+       oslfsck -fd ~/.paraslash/afs_database-0.7
 
 However, make sure para_server isn't running before executing oslfsck.
 
 If you don't mind to recreate your database you can start
 from scratch by removing the entire database directory, i.e.
 
-       rm -rf ~/.paraslash/afs_database-0.4
+       rm -rf ~/.paraslash/afs_database-0.7
 
 Be aware that this removes all attribute definitions, all playlists
 and all mood definitions and requires to re-initialize the tables.
@@ -2074,11 +2077,11 @@ here are the most important points.
 - Don't leave whitespace at the end of lines.
 - The limit on the length of lines is 80 columns.
 - Use K&R style for placing braces and spaces:
-
+<pre>
                if (x is true) {
                        we do y
                }
-
+</pre>
 - Use a space after (most) keywords.
 - Do not add spaces around (inside) parenthesized expressions.
 - Use one space around (on each side of) most binary and ternary operators.
index e8c43489f4681bcbd8bb859db80582b5fa83c068..2043fbb70b2afdf9ef243200920910bf564f2a1e 100644 (file)
@@ -1,46 +1,29 @@
-body,h1,h2,h3,h4,h5,h6,p,center,td,th,ul,dl,div {
-       font-family: sans-serif;
-}
-
 body {
+       font-family: sans-serif;
        background-color: black;
        color: #bbbbbb;
-       margin: 0px;
-}
-
-table {
-       padding: 8px 4px;
-}
-
-th {
-       padding: 2px 5px;
-       font-size: 100%;
-       text-align: left;
+       margin: 20px;
 }
 
 td {
-       padding: 2px 5px;
-       font-size: 100%;
+       padding: 2px 10px 2px 10px;
        vertical-align: top;
 }
 
+span.slogan {
+       font-size: 200%;
+       font-weight: bold;
+       color: #ddd;
+}
+
 a {
        color: #cc3322;
 }
 
 hr {
-       height: 1px;
-       border: none;
        border-top: 1px solid yellow;
 }
 
-img {
-       float: right;
-       border-width: 0px;
-}
-
-caption { font-weight: bold }
-
 /* doxgen */
 
 /* Data structure index. Box with clickable letters */
index 63e49677b0f1b85c13c17160a7b249531c1fbcd4..8bff7cfcaa98f8ae3bdfb00f87545c70c7927d94 100644 (file)
--- a/wma_afh.c
+++ b/wma_afh.c
@@ -195,7 +195,7 @@ static int wma_make_chunk_table(char *buf, size_t buf_size, uint32_t packet_size
        size_t ct_size = 250;
        int ret, count = 0, num_frames, num_superframes;
 
-       afhi->chunk_table = para_malloc(ct_size * sizeof(uint32_t));
+       afhi->chunk_table = arr_alloc(ct_size, sizeof(uint32_t));
        afhi->chunk_table[0] = 0;
        afhi->chunk_table[1] = afhi->header_len;
 
@@ -318,7 +318,7 @@ static int convert_utf8_to_utf16(char *src, char **dst)
        int ret;
 
        if (!src || !*src) {
-               *dst = para_calloc(2);
+               *dst = zalloc(2);
                return 0;
        }
        /*
@@ -334,7 +334,7 @@ static int convert_utf8_to_utf16(char *src, char **dst)
        /* even though src is in UTF-8, strlen() should DTRT */
        inbytes = inbytesleft = strlen(src);
        outbytes = outbytesleft = 4 * inbytes + 2; /* hope that's enough */
-       *dst = outbuf = para_malloc(outbytes);
+       *dst = outbuf = alloc(outbytes);
        sz = iconv(cd, ICONV_CAST &inbuf, &inbytesleft, &outbuf, &outbytesleft);
        if (sz == (size_t)-1) {
                ret = -ERRNO_TO_PARA_ERROR(errno);
@@ -411,7 +411,7 @@ static int make_cdo(struct taginfo *tags, const struct asf_object *cdo,
        result->size = 16 + 8 + 5 * 2 + title_bytes + artist_bytes
                + orig_cr_bytes + comment_bytes + orig_rating_bytes;
        PARA_DEBUG_LOG("cdo is %zu bytes\n", (size_t)result->size);
-       p = result->ptr = para_malloc(result->size);
+       p = result->ptr = alloc(result->size);
        memcpy(p, content_description_header, 16);
        p += 16;
        write_u64(p, result->size);
@@ -469,7 +469,7 @@ static int make_ecdo(struct taginfo *tags, struct asf_object *result)
        result->size += 2 + sizeof(album_tag_header) + 2 + 2 + 2 + album_bytes;
        result->size += 2 + sizeof(year_tag_header) + 2 + 2 + 2 + year_bytes;
 
-       p = result->ptr = para_malloc(result->size);
+       p = result->ptr = alloc(result->size);
        memcpy(p, extended_content_header, 16);
        p += 16;
        write_u64(p, result->size);
@@ -622,7 +622,7 @@ static int wma_rewrite_tags(const char *map, size_t mapsize,
        if (top.reserved2 != 2)
                return -E_NO_WMA;
        p++; /* objects start at p */
-       top.objects = para_malloc(top.num_objects * sizeof(struct asf_object));
+       top.objects = arr_alloc(top.num_objects, sizeof(struct asf_object));
        ret = read_asf_objects(p, top.size - (p - map), top.num_objects,
                top.objects, &ton);
        if (ret < 0)
index edf50cb0b3834d8971fe9602f6528e2f2bd86e5d..f7ee2c4dbd1281e49792693afd40e83f275cd919 100644 (file)
@@ -16,7 +16,6 @@
 
 #include <math.h>
 #include <regex.h>
-#include <sys/select.h>
 
 #include "para.h"
 #include "error.h"
@@ -160,8 +159,8 @@ static void init_coef_vlc(struct private_wmadec_data *pwd, int sidx, int didx)
        int i, l, j, k, level, n = src->n;
 
        init_vlc(dst, VLCBITS, n, src->huffbits, src->huffcodes, 4);
-       pwd->run_table[didx] = para_malloc(n * sizeof(uint16_t));
-       pwd->level_table[didx] = para_malloc(n * sizeof(uint16_t));
+       pwd->run_table[didx] = arr_alloc(n, sizeof(uint16_t));
+       pwd->level_table[didx] = arr_alloc(n, sizeof(uint16_t));
        i = 2;
        level = 1;
        k = 0;
@@ -428,7 +427,7 @@ static int wma_decode_init(char *initial_buf, int len, struct private_wmadec_dat
        int ret, i;
 
        PARA_NOTICE_LOG("initial buf: %d bytes\n", len);
-       pwd = para_calloc(sizeof(*pwd));
+       pwd = zalloc(sizeof(*pwd));
        ret = read_asf_header(initial_buf, len, &pwd->ahi);
        if (ret <= 0) {
                free(pwd);
@@ -1159,7 +1158,7 @@ static int wmadec_execute(struct btr_node *btrn, const char *cmd, char **result)
 
 #define WMA_OUTPUT_BUFFER_SIZE (128 * 1024)
 
-static int wmadec_post_select(__a_unused struct sched *s, void *context)
+static int wmadec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        int ret, converted, out_size;
@@ -1197,7 +1196,7 @@ next_buffer:
        if (fn->min_iqs > len)
                goto success;
        out_size = WMA_OUTPUT_BUFFER_SIZE;
-       out = para_malloc(out_size);
+       out = alloc(out_size);
        ret = wma_decode_superframe(pwd, out, &out_size,
                (uint8_t *)in + WMA_FRAME_SKIP);
        if (ret < 0) {
@@ -1229,6 +1228,6 @@ const struct filter lsg_filter_cmd_com_wmadec_user_data = {
        .open = wmadec_open,
        .close = wmadec_close,
        .execute = wmadec_execute,
-       .pre_select = generic_filter_pre_select,
-       .post_select = wmadec_post_select,
+       .pre_monitor = generic_filter_pre_monitor,
+       .post_monitor = wmadec_post_monitor,
 };
diff --git a/write.c b/write.c
index acfb94605b53d259a89ea41e75507b81e2cb2ca4..f7018aa115a632f345f5136c734f44e2b79d87a5 100644 (file)
--- a/write.c
+++ b/write.c
@@ -54,16 +54,16 @@ struct write_task {
        struct check_wav_context *cwc;
 };
 
-static void write_pre_select(struct sched *s, void *context)
+static void write_pre_monitor(struct sched *s, void *context)
 {
        struct write_task *wt = context;
-       check_wav_pre_select(s, wt->cwc);
+       check_wav_pre_monitor(s, wt->cwc);
 }
 
-static int write_post_select(__a_unused struct sched *s, void *context)
+static int write_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct write_task *wt = context;
-       return check_wav_post_select(wt->cwc);
+       return check_wav_post_monitor(wt->cwc);
 }
 
 static int setup_and_schedule(struct lls_parse_result *lpr)
@@ -83,21 +83,20 @@ static int setup_and_schedule(struct lls_parse_result *lpr)
        wt.cwc = check_wav_init(sit.btrn, NULL, &wp, &cw_btrn);
        wt.task = task_register(&(struct task_info) {
                .name = "write",
-               .pre_select = write_pre_select,
-               .post_select = write_post_select,
+               .pre_monitor = write_pre_monitor,
+               .post_monitor = write_post_monitor,
                .context = &wt,
        }, &s);
 
        n = writer_given? writer_given : 1;
-       wns = para_calloc(n * sizeof(*wns));
+       wns = arr_zalloc(n, sizeof(*wns));
        for (i = 0; i < n; i++) {
                const char *arg = i < writer_given?
                        lls_string_val(i, OPT_RESULT(WRITER, lpr)) : NULL;
                wns[i].wid = check_writer_arg_or_die(arg, &wns[i].lpr);
                register_writer_node(wns + i, cw_btrn, &s);
        }
-       s.default_timeout.tv_sec = 10;
-       s.default_timeout.tv_usec = 50000;
+       s.default_timeout = 10500;
        ret = schedule(&s);
        if (ret >= 0) {
                int j, ts;
diff --git a/write.h b/write.h
index cb0beff812121b338e3749e80f63ad2cb99999db..35a8d29f1a3f7dd493b65c394bc8ce63aa7f9f8f 100644 (file)
--- a/write.h
+++ b/write.h
@@ -20,21 +20,23 @@ struct writer_node {
        size_t min_iqs;
 };
 
-/** Describes one supported writer. */
+/**
+ * Describes a data sink for audio streams.
+ *
+ * A paraslash writer obtains data via the buffer tree mechanism from its
+ * parent node. It consumes data without producing output on its own.
+ *
+ * This structure contains the methods which have to be implemented by each
+ * writer.
+ *
+ * \sa struct \ref writer_node, struct \ref receiver, struct \ref filter,
+ * struct \ref sched.
+ */
 struct writer {
-       /**
-        * Prepare the fd sets for select.
-        *
-        * This is called from scheduler. It may use the sched pointer to add
-        * any file descriptors or to decrease the select timeout.
-        */
-       void (*pre_select)(struct sched *s, void *context);
-       /**
-        * Write audio data.
-        *
-        * Called from the post_select function of the writer node's task.
-        */
-       int (*post_select)(struct sched *s, void *context);
+       /** Ask the scheduler to check whether data can be written. */
+       void (*pre_monitor)(struct sched *s, void *context);
+       /** Write audio data. */
+       int (*post_monitor)(struct sched *s, void *context);
        /**
         * Close one instance of the writer.
         *
@@ -66,7 +68,7 @@ const struct writer *writer_get(int wid);
 const char *writer_name(int wid);
 void register_writer_node(struct writer_node *wn, struct btr_node *parent,
                struct sched *s);
-void get_btr_sample_rate(struct btr_node *btrn, int32_t *result);
-void get_btr_channels(struct btr_node *btrn, int32_t *result);
-void get_btr_sample_format(struct btr_node *btrn, int32_t *result);
+int get_btr_sample_rate(struct btr_node *btrn, int32_t *result);
+int get_btr_channels(struct btr_node *btrn, int32_t *result);
+int get_btr_sample_format(struct btr_node *btrn, int32_t *result);
 void print_writer_helps(bool detailed);
index 14cc98a4189a5f74907f8bfcf416f62bcc2515c5..9a13090541895df5b7bc7ee8f42fbc2fda37a9f2 100644 (file)
@@ -85,7 +85,7 @@ int check_writer_arg_or_die(const char *wa, struct lls_parse_result **lprp)
        if (!wa || !*wa) {
                writer_num = default_writer_id();
                cmd = WRITE_CMD(writer_num);
-               argv = para_malloc(2 * sizeof(char *));
+               argv = alloc(2 * sizeof(char *));
                argc = 1;
                argv[0] = para_strdup(lls_command_name(cmd));
                argv[1] = NULL;
@@ -139,8 +139,8 @@ void register_writer_node(struct writer_node *wn, struct btr_node *parent,
                .handler = w->execute, .context = wn));
        wn->task = task_register(&(struct task_info) {
                .name = writer_name(wn->wid),
-               .pre_select = w->pre_select,
-               .post_select = w->post_select,
+               .pre_monitor = w->pre_monitor,
+               .post_monitor = w->post_monitor,
                .context = wn,
        }, s);
 }
@@ -174,24 +174,24 @@ void print_writer_helps(bool detailed)
        }
 }
 
-static void get_btr_value(struct btr_node *btrn, const char *cmd,
+static int get_btr_value(struct btr_node *btrn, const char *cmd,
                int32_t *result)
 {
        char *buf = NULL;
        int ret = btr_exec_up(btrn, cmd, &buf);
 
-       if (ret < 0) {
-               /*
-                * This really should not happen. It means one of our parent
-                * nodes died unexpectedly. Proceed with fingers crossed.
-                */
-               PARA_CRIT_LOG("cmd %s: %s\n", cmd, para_strerror(-ret));
-               *result = 0;
-               return;
-       }
+       *result = 0;
+       /*
+        * Errors may happen when the decoder returns EOF before the writer had
+        * a chance to query the buffer tree for the channel count, sample rate
+        * etc.
+        */
+       if (ret < 0)
+               return ret;
        ret = para_atoi32(buf, result);
        assert(ret >= 0);
        free(buf);
+       return ret;
 }
 
 /**
@@ -200,11 +200,11 @@ static void get_btr_value(struct btr_node *btrn, const char *cmd,
  * \param btrn Where to start the search.
  * \param result Filled in by this function.
  *
- * This function is assumed to succeed and terminates on errors.
+ * \return Standard.
  */
-void get_btr_sample_rate(struct btr_node *btrn, int32_t *result)
+int get_btr_sample_rate(struct btr_node *btrn, int32_t *result)
 {
-       get_btr_value(btrn, "sample_rate", result);
+       return get_btr_value(btrn, "sample_rate", result);
 }
 
 /**
@@ -212,10 +212,12 @@ void get_btr_sample_rate(struct btr_node *btrn, int32_t *result)
  *
  * \param btrn See \ref get_btr_sample_rate.
  * \param result See \ref get_btr_sample_rate.
+ *
+ * \return Standard.
  */
-void get_btr_channels(struct btr_node *btrn, int32_t *result)
+int get_btr_channels(struct btr_node *btrn, int32_t *result)
 {
-       get_btr_value(btrn, "channels", result);
+       return get_btr_value(btrn, "channels", result);
 }
 
 /**
@@ -223,8 +225,10 @@ void get_btr_channels(struct btr_node *btrn, int32_t *result)
  *
  * \param btrn See \ref get_btr_sample_rate.
  * \param result Contains the sample format as an enum sample_format type.
+ *
+ * \return Standard.
  */
-void get_btr_sample_format(struct btr_node *btrn, int32_t *result)
+int get_btr_sample_format(struct btr_node *btrn, int32_t *result)
 {
-       get_btr_value(btrn, "sample_format", result);
+       return get_btr_value(btrn, "sample_format", result);
 }
index 2dbe21b779a82eebd79403fa5f60bc56925489dd..4a53db2741754bf6359733af8b42ca60ca255e92 100644 (file)
--- a/yy/mp.lex
+++ b/yy/mp.lex
@@ -74,6 +74,7 @@ lyrics_id {return LYRICS_ID;}
 bitrate {return BITRATE;}
 frequency {return FREQUENCY;}
 channels {return CHANNELS;}
+duration {return DURATION;}
 true {return TRUE;}
 false {return FALSE;}
 
diff --git a/yy/mp.y b/yy/mp.y
index 0f2c9cb8b256a82eeb2fac78114f6026902a24f2..8df4f20ea04ebd2ea23b77ad77451e23e0022dbc 100644 (file)
--- a/yy/mp.y
+++ b/yy/mp.y
@@ -59,7 +59,7 @@ enum semantic_types {
 
 static struct mp_ast_node *ast_node_raw(int id)
 {
-       struct mp_ast_node *node = para_malloc(sizeof(struct mp_ast_node));
+       struct mp_ast_node *node = alloc(sizeof(struct mp_ast_node));
        node->id = id;
        return node;
 }
@@ -76,7 +76,7 @@ static struct mp_ast_node *ast_node_new_unary(int id, struct mp_ast_node *child)
 {
        struct mp_ast_node *node = ast_node_raw(id);
        node->num_children = 1;
-       node->children = para_malloc(sizeof(struct mp_ast_node *));
+       node->children = alloc(sizeof(struct mp_ast_node *));
        node->children[0] = child;
        return node;
 }
@@ -86,7 +86,7 @@ static struct mp_ast_node *ast_node_new_binary(int id, struct mp_ast_node *left,
 {
        struct mp_ast_node *node = ast_node_raw(id);
        node->num_children = 2;
-       node->children = para_malloc(2 * sizeof(struct mp_ast_node *));
+       node->children = arr_alloc(2, sizeof(struct mp_ast_node *));
        node->children[0] = left;
        node->children[1] = right;
        return node;
@@ -210,6 +210,9 @@ static int eval_node(struct mp_ast_node *node, struct mp_context *ctx,
        case CHANNELS:
                result->intval= mp_channels(ctx);
                return ST_INTVAL;
+       case DURATION:
+               result->intval= mp_duration(ctx);
+               return ST_INTVAL;
        /* bools */
        case IS_SET:
                arg = node->children[0]->sv.strval;
@@ -327,6 +330,7 @@ bool mp_eval_ast(struct mp_ast_node *root, struct mp_context *ctx)
 %token <node> BITRATE
 %token <node> FREQUENCY
 %token <node> CHANNELS
+%token <node> DURATION
 %token <node> FALSE TRUE
 
 /* keywords without semantic value */
@@ -377,6 +381,7 @@ exp: NUM {$$ = $1;}
        | BITRATE {$$ = mp_new_ast_leaf_node(BITRATE);}
        | FREQUENCY {$$ = mp_new_ast_leaf_node(FREQUENCY);}
        | CHANNELS {$$ = mp_new_ast_leaf_node(CHANNELS);}
+       | DURATION {$$ = mp_new_ast_leaf_node(DURATION);}
 ;
 
 boolexp: IS_SET '(' STRING_LITERAL ')' {$$ = ast_node_new_unary(IS_SET, $3);}