]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge topic branch t/overflow into master
authorAndre Noll <maan@tuebingen.mpg.de>
Mon, 3 Oct 2022 15:59:56 +0000 (17:59 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Mon, 3 Oct 2022 16:01:20 +0000 (18:01 +0200)
This series implements a new memory allocation API which checks
for overflows. The first part of the series just renames the main
allocation functions. Later patches in the series implement allocators
which take two size_t arguments (like calloc(3)) and check whether the
multiplication overflows by employing the __builtin_mul_overflow()
primitive supported by gcc and clang. This requires us to bump the
lowest supported gcc and clang version.

* refs/heads/t/overflow:
  build: Compile with -ftrapv.
  string: Introduce arr_zalloc().
  string: Introduce arr_alloc().
  string: Introduce arr_realloc() and check for integer overflow.
  string: Rename para_calloc() -> zalloc().
  string: Rename para_malloc() -> alloc().
  string: Overhaul para_strdup().

55 files changed:
1  2 
Makefile.real
NEWS.md
aac_afh.c
aacdec_filter.c
afh_recv.c
afs.c
alsa_write.c
amp_filter.c
ao_write.c
audioc.c
audiod.c
audiod_command.c
buffer_tree.c
check_wav.c
client.c
client_common.c
command.c
compress_filter.c
dccp_recv.c
dccp_send.c
fd.c
fecdec_filter.c
file_write.c
filter.c
flacdec_filter.c
grab_client.c
gui.c
http_recv.c
http_send.c
interactive.c
mp3dec_filter.c
mp4.c
net.c
oggdec_filter.c
opusdec_filter.c
oss_write.c
para.h
play.c
prebuffer_filter.c
recv_common.c
resample_filter.c
sched.c
send_common.c
server.c
signal.c
spxdec_filter.c
string.c
string.h
sync_filter.c
udp_send.c
vss.c
wav_filter.c
wmadec_filter.c
write.c
write_common.c

diff --combined Makefile.real
index 92ef995249680fabdbcdaa1b257b92fabdfd5d2a,9a6c7e76fe1e9d95d4634e3298ce660b69366e9f..4dac79f9941f5cee0c2848b53e0312b1233a385d
@@@ -10,7 -10,6 +10,7 @@@ endi
  .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
@@@ -113,7 -112,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)
@@@ -121,6 -119,7 +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
diff --combined NEWS.md
index e5b9901054062c92667c3ab403daa5960dd90603,e9713d9826ff74e25aa01ceaad9a9db13f3113a5..a6af789b852b61c56e8122797d708018a7572bf3
+++ b/NEWS.md
@@@ -5,17 -5,6 +5,20 @@@ NEW
  0.7.1 (to be announced) "digital spindrift"
  -------------------------------------------
  
 +- 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).
 +
  [tarball](./releases/paraslash-git.tar.xz)
  
  ----------------------------------
diff --combined aac_afh.c
index 79fa30ddc2f794f5c586c28b2823c7fce26dd9b8,1683841929e58185109c6df8110d1a4655932cbe..c4301a2f178257b19d56b3cf297da3e1eaff337c
+++ 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"
@@@ -22,11 -26,13 +22,11 @@@ 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;
        size_t have, rv;
        if (want == 0 || c->fpos >= c->mapsize)
                return 0;
        have = c->mapsize - c->fpos;
 -      rv = PARA_MIN(have, (size_t)want);
 +      rv = PARA_MIN(have, want);
        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;
        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;
  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(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");
  }
  
  /*
@@@ -124,8 -162,9 +124,8 @@@ static int aac_get_file_info(char *map
                struct afh_info *afhi)
  {
        int ret;
 -      int32_t rv;
        struct aac_afh_context *c;
 -      int64_t tmp;
 +      uint64_t milliseconds;
        const char *buf;
        uint32_t n, len;
  
        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, &len) < 0)
 -                      break;
 +              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, &len) >= 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 = arr_alloc(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;
  }
  
diff --combined aacdec_filter.c
index 36a783c5ecf6ae4538a55fb8c61669707bd4a9b6,53a8853dfeb68696d953daa419784758be3fe970..87a7900af3ee9b43e1a661d66cdd54d1625b0952
@@@ -52,7 -52,7 +52,7 @@@ static int aacdec_execute(struct btr_no
  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 +74,7 @@@ static void aacdec_close(struct filter_
        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 +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 +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 --combined afh_recv.c
index 889fdce821f4b9aec85c1b01976e4fae6e05e176,e9d24e41f0a75d400807c16da03d0953abf15d90..eebac67f9c80a270cd91337190ed9ad1eb569898
@@@ -75,7 -75,7 +75,7 @@@ static int afh_recv_open(struct receive
  
        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 +142,14 @@@ static void afh_recv_close(struct recei
        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)
        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;
                        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);
@@@ -242,7 -242,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 --combined afs.c
index f29080290424d95506ec643df567e24c2d095a74,00a82b02bd55156c887cd21b8076a94ff4c281b0..3da39f324b83b2a6d83d1f468732f74cc4313ae2
--- 1/afs.c
--- 2/afs.c
+++ b/afs.c
@@@ -24,7 -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"
@@@ -708,7 -707,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;
  
                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) {
@@@ -744,8 -743,8 +744,8 @@@ static void register_signal_task(struc
  
        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);
@@@ -763,15 -762,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);
  }
  
  /**
@@@ -863,11 -862,11 +863,11 @@@ static int call_callback(int fd, int qu
        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;
  }
  
  /* 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;
@@@ -918,7 -917,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;
        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);
        }
        /* 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);
                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)
                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);
@@@ -974,20 -973,12 +974,20 @@@ static void register_command_task(struc
  
        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.
   *
@@@ -1012,8 -1003,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 --combined alsa_write.c
index 2bf3fd0e9010539fa6784e554ae7dc8c21441d3d,1dee5ac812eed336f007c8ee25b3cca2eb1e9eb4..92d6cb703b7a4911d0b5aa61282f703ce0bb490c
@@@ -48,7 -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 +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;
                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)
@@@ -254,7 -254,7 +254,7 @@@ 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;
@@@ -296,7 -296,7 +296,7 @@@ again
  
                if (bytes == 0) /* no data available */
                        return 0;
-               pad = wn->private_data = para_calloc(sizeof(*pad));
+               pad = wn->private_data = zalloc(sizeof(*pad));
                ret = get_btr_sample_rate(btrn, &val);
                if (ret < 0)
                        goto err;
        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;
@@@ -349,7 -349,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,
  };
diff --combined amp_filter.c
index 9369e4bcbeb1f9c9eec251f5136a34d9e223f8bb,3f0aa11e34d061b584da3e21655124564078d5fc..360e3fc2d7429bb7aa8219248ff4d38505a86ac2
@@@ -29,7 -29,7 +29,7 @@@ static void amp_close(struct filter_nod
  
  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 +43,7 @@@
                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 +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 +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,
  };
diff --combined ao_write.c
index 41e609b70ab089acc384fb48c4fc40c58657c711,58da91cf2c7aa066b642504a18b61832accdc20f..950f8f73808e0b6528c0b73a2a43da58ec17f4ee
@@@ -42,7 -42,7 +42,7 @@@ static void aow_close(struct writer_nod
        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 +184,7 @@@ static int aow_init(struct writer_node 
        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 +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;
@@@ -421,7 -421,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,
  };
  
diff --combined audioc.c
index 5f91e3b7d925bf4bd34f67ef0cfeb059a60cef8f,0676f44bd7b40dbd105f99a08c4a16a48d2680fd..d6c14cbcf7245a3a7db4bb3f86cf7e35efb8384b
+++ b/audioc.c
@@@ -107,12 -107,6 +107,12 @@@ static void help_completer(struct i9e_c
        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)
  {
@@@ -149,17 -143,17 +149,17 @@@ static struct i9e_completer audiod_comp
        {.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;
  
        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)
@@@ -217,8 -211,8 +217,8 @@@ static int audioc_i9e_line_handler(cha
                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);
@@@ -256,9 -250,9 +256,9 @@@ __noreturn static void interactive_sess
        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;
@@@ -370,7 -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)
diff --combined audiod.c
index c29cf3ad472dc56a3b5328d2ca9e85e8ea45e288,fbcde91c9633248886c4a586ccfb7c3dae488584..0e8e5981085b7d05b8a2a08f35a9bb7944a4d72d
+++ b/audiod.c
@@@ -44,6 -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 */
@@@ -121,7 -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 {
@@@ -387,11 -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;
@@@ -562,7 -564,7 +562,7 @@@ static void open_filters(struct slot_in
                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];
                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;
@@@ -600,7 -602,7 +600,7 @@@ static void open_writers(struct slot_in
        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;
@@@ -627,7 -629,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)
                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;
@@@ -817,7 -819,7 +817,7 @@@ static int parse_stream_command(const c
                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);
@@@ -833,12 -835,9 +833,9 @@@ static int add_filter(int format, cons
        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;
@@@ -884,8 -883,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]));
@@@ -1053,7 -1052,7 +1050,7 @@@ static void init_local_socket(struct co
        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;
        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:
        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;
        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_NOTICE_LOG("%s\n", para_strerror(-ret));
                if (ret == -E_AUDIOD_TERM) {
@@@ -1130,8 -1129,8 +1127,8 @@@ static void init_command_task(struct co
  
        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);
  }
@@@ -1252,7 -1251,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;
@@@ -1284,7 -1283,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;
@@@ -1375,8 -1374,8 +1372,8 @@@ static void init_status_task(struct sta
  
        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);
  }
@@@ -1460,7 -1459,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));
  
        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);
diff --combined audiod_command.c
index 29c330f36a526d1004cb8595248fb75008033438,89fdc1acabdeb45a9f708a2a8ca72cdb1c85ffa3..6e2f8ee9e4aaf446267270e3be2398a4ee6d7c96
@@@ -114,7 -114,7 +114,7 @@@ static int stat_client_add(int fd, uint
        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,42 -240,6 +240,42 @@@ static int com_help(int fd, struct lls_
  }
  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;
@@@ -396,9 -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.
   * 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;
        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);
diff --combined buffer_tree.c
index 49e40fb952c2e6c36a62001b1bc0a46601f412e6,4c17e824bb81631929fc5526140d38f74d52b1f1..05cdfa83630004e49561f0c7bef0138bd0a35c27
@@@ -72,8 -72,8 +72,8 @@@ struct btr_pool *btr_pool_new(const cha
        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 +262,7 @@@ static void btr_pool_deallocate(struct 
   */
  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;
@@@ -307,7 -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 +354,7 @@@ static void add_btrb_to_children(struc
        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 +570,7 @@@ bool btr_no_parent(struct btr_node *btr
   * 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 +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 +1067,13 @@@ static int merge_input(struct btr_node 
        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 +1181,7 @@@ struct btr_node *btr_search_node(const 
   * \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.
diff --combined check_wav.c
index 100975dc79c4b5a0daf78facfbc632d2e9972916,c24a402eccb46a5ece335c99cab75f3c982b71fb..3789f30aa5a20ef5433df83362a6be02563c8d17
@@@ -39,15 -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 +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 +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 +207,7 @@@ struct check_wav_context *check_wav_ini
                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;
   *
   * \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)
diff --combined client.c
index 36e851f4b571a2dc095290f18f5e0d96afc5331e,ee7d980e88ffcf3cfcfa0cc3a75227da176091b3..a6aee0f8b9afc79439032db3695ba9c82bb594a1
+++ b/client.c
@@@ -42,7 -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 +51,7 @@@
                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 +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,
                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);
@@@ -246,12 -246,6 +246,12 @@@ I9E_DUMMY_COMPLETER(init)
  
  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)
  {
@@@ -347,7 -341,7 +347,7 @@@ static void setatt_completer(struct i9e
        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);
@@@ -441,7 -435,7 +441,7 @@@ static void select_completer(struct i9e
                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++)
@@@ -538,7 -532,7 +538,7 @@@ __noreturn static void interactive_sess
        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)
@@@ -584,7 -578,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);
@@@ -630,7 -624,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)
                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);
  
diff --combined client_common.c
index 3beeed1f49effebe1d6f92ceea0fdebb589a7fe6,a4f48a2755454aeffa9c6da5f1e03c39f0f636a9..f476a1c4ada24ddf4a7c5a825048ab7df9e7d9f5
@@@ -57,7 -57,7 +57,7 @@@ void client_close(struct client_task *c
   * 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;
        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 +83,7 @@@
                        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 +92,7 @@@
                        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,7 -125,8 +125,7 @@@ static int send_sb(struct client_task *
        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;
        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 {
                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;
@@@ -245,7 -248,7 +245,7 @@@ static int send_sb_command(struct clien
  
        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);
@@@ -271,7 -274,7 +271,7 @@@ static bool has_feature(const char *fea
   * 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;
                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);
                 * 0.8.0 we no longer need to request the feature.
                 */
                bool has_sha256;
 -              if (!FD_ISSET(ct->scc.fd, &s->wfds))
 +              if (!sched_write_ok(ct->scc.fd, s))
                        return 0;
                has_sha256 = has_feature("sha256", ct);
                sprintf(buf, AUTH_REQUEST_MSG "%s%s", ct->user, has_sha256?
                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) {
                free(sbb.iov.iov_base);
                if (ret < 0)
                        goto out;
-               ct->challenge_hash = para_malloc(HASH2_SIZE);
+               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);
        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);
                }
        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)
                        }
                        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);
                        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) {
@@@ -500,8 -502,8 +499,8 @@@ int client_connect(struct client_task *
  
        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;
@@@ -582,7 -584,7 +581,7 @@@ int client_parse_config(int argc, char 
        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;
diff --combined command.c
index 0f47110e54b7e661e59b8895e7c79847ad35bae4,407a1c9f33156fd16b06727a645e2748d3b52154..a0102eebe42db14df8bf9893efec500fc56bf6f1
+++ b/command.c
@@@ -10,6 -10,7 +10,6 @@@
  #include <netdb.h>
  #include <lopsub.h>
  
 -#include "server.lsg.h"
  #include "para.h"
  #include "error.h"
  #include "lsu.h"
@@@ -21,8 -22,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 "daemon.h"
  #include "fd.h"
@@@ -78,7 -79,7 +78,7 @@@ static char *vss_status_tohuman(unsigne
   */
  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' : '_';
@@@ -390,6 -391,7 +390,6 @@@ static int com_si(struct command_contex
                "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(),
                mmd->active_connections,
                mmd->num_commands,
                mmd->num_connects,
 -              ENUM_STRING_VAL(LOGLEVEL),
                AUDIO_FORMAT_HANDLERS
        );
        mutex_unlock(mmd_mutex);
@@@ -587,46 -590,6 +587,46 @@@ static int com_hup(__a_unused struct co
  }
  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)
  {
@@@ -871,7 -834,7 +871,7 @@@ static int run_command(struct command_c
        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;
@@@ -926,7 -889,7 +926,7 @@@ int handle_connect(int fd
        int ret;
        unsigned char rand_buf[APC_CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
        unsigned char challenge_hash[HASH2_SIZE];
-       char *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
+       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;
diff --combined compress_filter.c
index 9f9d8515e3794139a65f9f3e7598d3d900829bcc,dd892ca2119506abe6bf6c7a158c651063598be2..a6a001919acc5038039d5de0b0590a265052e978
@@@ -37,7 -37,7 +37,7 @@@ static void compress_close(struct filte
        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 +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 +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 +162,6 @@@ const struct filter lsg_filter_cmd_com_
        .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,
  };
diff --combined dccp_recv.c
index faacd39f8fb0b4377f495a63d115ba5bd3de0336,d3a18e90e8fca9d02ba49a13e1ef2ad4b3ae86a2..fe2f7abf2af5fdb9abb27dcfb41c9da705ebf91a
@@@ -76,7 -76,7 +76,7 @@@ static int dccp_recv_open(struct receiv
        /* 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);
@@@ -109,16 -109,16 +109,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;
        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 -154,6 +154,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,
  };
diff --combined dccp_send.c
index 9e9372715c5fac244ed5943d74e3d98089859831,475177071c8428ca5a67ecf6966b5d796750b247..15a361babfa8570f61d2b51c15e60c83a8bab009
@@@ -24,8 -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,13 -36,14 +36,13 @@@ 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);
  }
  
  /**
@@@ -118,14 -119,14 +118,14 @@@ static void dccp_send_fec(struct sender
                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;
  
                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);
@@@ -248,8 -249,8 +248,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 --combined fd.c
index 800106e132b2bc21085f65803d70b6bad492f854,f7a2802a39db957c35f748af1faa96217a2580fe..763f756cdc3b5086ee9d26f432dbbfb760e02e4a
--- 1/fd.c
--- 2/fd.c
+++ b/fd.c
@@@ -176,11 -176,14 +176,11 @@@ __printf_2_3 int write_va_buffer(int fd
   * \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
   *
   * \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;
   * \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
   *
   * \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);
  }
  
  /**
   * \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
   *
   * \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);
+       char *buf = alloc(bufsize + 1);
 -      int ret = read_nonblock(fd, buf, bufsize, rfds, &n);
 +      int ret = read_nonblock(fd, buf, bufsize, &n);
  
        buf[n] = '\0';
        if (ret < 0)
@@@ -308,6 -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.
   *
@@@ -344,6 -391,34 +344,6 @@@ __must_check int mark_fd_nonblocking(in
        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.
   *
@@@ -567,47 -642,6 +567,47 @@@ int para_munmap(void *start, size_t len
        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.
   *
   *
   * \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 --combined fecdec_filter.c
index d629603c4b11f37825cd71b7bf5335d65625fa83,a3498e02a08f7fd94f70f516c3b045e7e02d5a0d..1d8fbc16db71094d4f81b2c8c6bb88014087582c
@@@ -225,8 -225,8 +225,8 @@@ static int add_slice(char *buf, struct 
        }
        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. */
                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 +431,7 @@@ static void fecdec_close(struct filter_
        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 +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,
  };
diff --combined file_write.c
index 64153178e969191201b869fe6bfa34f37f61af23,eb615153578741f7cdd2a37fb568ea8c5fcaf719..ba902070d9498677e94872860aeb9bbcbcd5235e
@@@ -64,12 -64,12 +64,12 @@@ static int prepare_output_file(struct w
                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 +79,7 @@@
                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 +92,7 @@@
        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;
                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 +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,
  };
diff --combined filter.c
index 85d3da7e57270e21ec6897c3086c93397f9b5e31,adfa8e10fcc8cd8a5ad744565e497e4d6b1e1190..722cb16fb35bfaec09bbaca046ebf62931df8036
+++ b/filter.c
@@@ -120,14 -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;
                        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);
                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);
diff --combined flacdec_filter.c
index 2c9f8607c0d7603664ef13acd3a418f13625b8f7,8747a4be3a57722393bc12755baa6d3e37d0040b..f3060f5721c1c22753cceb4229ee4dbe178dbf52
@@@ -135,7 -135,7 +135,7 @@@ static FLAC__StreamDecoderWriteStatus w
        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 +205,7 @@@ static bool output_queue_full(struct bt
        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;
                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 +286,7 @@@ static void flacdec_close(struct filter
  
  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;
  }
  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,
  };
diff --combined grab_client.c
index 393e2ce331117a6d29231f551b78103b6bfe9f83,8191e114946a0e6ca304f24c0e12adca856a59cf..1019e579a39d8559b2aecc658c7651fd6b7704dc
@@@ -89,7 -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);
                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 +129,8 @@@ static void gc_activate(struct grab_cli
  
        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 +171,7 @@@ static int gc_close(struct grab_client 
                /*
                 * 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;
        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 +261,7 @@@ static int gc_check_args(struct lls_par
  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 --combined gui.c
index 72908f23e46d0762ab4442975070dc5bd46c7269,06aada3cc83411293f16cf101df2de63538494e9..ebfab3564836e7b1708dc430e9375815b00d49b7
--- 1/gui.c
--- 2/gui.c
+++ b/gui.c
@@@ -431,7 -431,7 +431,7 @@@ static void rb_add_entry(int color, cha
  
        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 +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;
        }
        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 +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 +931,18 @@@ static enum exec_status exec_status(voi
        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;
                        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],
        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 +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;
        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 +1391,26 @@@ static int setup_tasks_and_schedule(voi
        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);
  
        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);
diff --combined http_recv.c
index 59e9696b47e082f09bf6c58ccf49eadb3c3153e9,8f9f1af11d841b6344fd2f864462b6cb7a6987e2..5aafacb840df1f863536fac1911c8d1caf8eb2b0
@@@ -56,17 -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 +74,7 @@@
   * 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 +93,7 @@@
                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");
                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;
        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 */
@@@ -160,7 -160,7 +160,7 @@@ static int http_recv_open(struct receiv
                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);
  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,
  };
diff --combined http_send.c
index 0a90e8840cdf76e683a276d569fba3bfcd710c2b,fab703cde721ea894785deaa71d8052d728fab36..90e3ee57d1b6d40152ef6fc4026331766e2524c5
@@@ -20,8 -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 +158,7 @@@ static void http_send(long unsigned cur
        }
  }
  
 -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;
                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) {
                        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;
        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 +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 --combined interactive.c
index 6c9110fd66e23cae6b8de5f80db834738565c64d,01f25fb3d52654f3a45413891b0b170b3da5a7a5..e367a65920c53982af55436d7b96ea53138408f8
@@@ -189,6 -189,8 +189,6 @@@ static char **i9e_completer(const char 
   *
   * 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)
  {
@@@ -256,6 -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;
@@@ -280,7 -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;
        ret = 0;
        if (i9ep->caught_sigint)
                goto rm_btrn;
 -      while (input_available()) {
 +      while (read_ok(i9ep->ici->fds[0]) > 0) {
                if (i9ep->stdout_btrn) {
 -                      unsigned len = i9ep->key_sequence_length;
 -                      assert(len < sizeof(i9ep->key_sequence) - 1);
 -                      buf = i9ep->key_sequence + len;
 -                      ret = read(i9ep->ici->fds[0], buf, 1);
 -                      if (ret < 0) {
 -                              ret = -ERRNO_TO_PARA_ERROR(errno);
 -                              goto rm_btrn;
 +                      while (i9ep->key_sequence_length < sizeof(i9ep->key_sequence) - 1) {
 +                              buf = i9ep->key_sequence + i9ep->key_sequence_length;
 +                              ret = read(i9ep->ici->fds[0], buf, 1);
 +                              if (ret < 0) {
 +                                      ret = -ERRNO_TO_PARA_ERROR(errno);
 +                                      goto rm_btrn;
 +                              }
 +                              if (ret == 0) {
 +                                      ret = -E_I9E_EOF;
 +                                      goto rm_btrn;
 +                              }
 +                              buf[1] = '\0';
 +                              i9ep->key_sequence_length++;
 +                              rl_stuff_char((int)(unsigned char)*buf);
 +                              rl_callback_read_char();
 +                              if (read_ok(i9ep->ici->fds[0]) <= 0)
 +                                      break;
                        }
 -                      ret = -E_I9E_EOF;
 -                      if (ret == 0)
 -                              goto rm_btrn;
 -                      buf[1] = '\0';
 -                      i9ep->key_sequence_length++;
 -                      rl_stuff_char((int)(unsigned char)*buf);
 -              }
 -              rl_callback_read_char();
 +                      i9ep->key_sequence_length = 0;
 +              } else
 +                      rl_callback_read_char();
                ret = 0;
        }
        if (!i9ep->stdout_btrn)
@@@ -360,7 -369,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;
  
                        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
        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)
@@@ -463,8 -472,8 +463,8 @@@ int i9e_open(struct i9e_client_info *ic
                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);
  
@@@ -591,21 -600,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;
  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);
                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);
        }
@@@ -682,7 -693,7 +682,7 @@@ char **i9e_complete_commands(const cha
                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);
@@@ -766,7 -777,7 +766,7 @@@ int i9e_print_completions(struct i9e_co
        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:
        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);
 +}
diff --combined mp3dec_filter.c
index 6a196f3a06c06a42ecca40fde56de3f0d3cf4926,75b052755e22eb09a9abfc977217907eff2a6f63..bc8ccdaa99be724530372a03b71544abeece480b
@@@ -73,7 -73,7 +73,7 @@@ static void mp3dec_close(struct filter_
  
  #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 +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 +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 +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 +187,7 @@@ static int mp3dec_execute(struct btr_no
  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 --combined mp4.c
index 4b8607b185a9a87c845940d86412843654a13c7d,0000000000000000000000000000000000000000..f8515ca290681a271155e9749d903ea9214b9b13
mode 100644,000000..100644
--- 1/mp4.c
--- /dev/null
+++ b/mp4.c
@@@ -1,1055 -1,0 +1,1053 @@@
-       t->stsz_table = para_malloc(t->stsz_sample_count * sizeof(int32_t));
 +/*
 + * 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->stts_sample_count = para_malloc(t->stts_entry_count
-               * sizeof(int32_t));
++      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->stsc_first_chunk = para_malloc(t->stsc_entry_count * sizeof(int32_t));
-       t->stsc_samples_per_chunk = para_malloc(t->stsc_entry_count
-               * sizeof (int32_t));
++      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->stco_chunk_offset = para_malloc(t->stco_entry_count
-               * sizeof(int32_t));
++      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;
-               value = para_malloc(len + 1);
++      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);
-       struct mp4 *f = para_calloc(sizeof(*f));
++              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;
-       out_buffer = para_malloc(*out_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 --combined net.c
index 4a6f9a63cd8475f8e7a0f583be3ac7e3d205c1a7,474808fff234642a5f58456948e2987a01706434..e01af24be27c49a00a60a61a98448bc3d82046f0
--- 1/net.c
--- 2/net.c
+++ b/net.c
@@@ -286,7 -286,7 +286,7 @@@ 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);
        return new;
  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;
                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);
        }
@@@ -801,21 -801,25 +801,21 @@@ int recv_buffer(int fd, char *buf, 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);
diff --combined oggdec_filter.c
index a6fa056aa9bc354082542563b9d53864e01f390a,b05691c3f57ac60efc430f6518b077ed7697e0a9..da70d4c04a2e2ec2dce8b955a5430442cd912519
@@@ -88,7 -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 +121,7 @@@ static int ogg_init(struct filter_node 
        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 +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;
        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;
                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 +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
  };
diff --combined opusdec_filter.c
index 31f9640bbb915fc11ff1665c84fae0a61b902645,d30de0b68aeab658ffccd32238467c38f6767991..85287be0c813d4899b2f802735f2684338ea420c
@@@ -86,7 -86,7 +86,7 @@@ static int opusdec_execute(struct btr_n
  
  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 +153,7 @@@ static void opusdec_add_output(short *p
  
        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 +193,7 @@@ static int decode_packet(struct opusdec
        /* 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) {
  
  #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 +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;
  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,
  };
diff --combined oss_write.c
index 1a837e5700c7cfdb7e86951f8ae999645c49bd67,2afad677377825a8cafd48426fc965613c04b8a0..96d7b1871a941bff1662ae2a9512b85c79e7b4b9
@@@ -61,7 -61,7 +61,7 @@@ static int get_oss_format(enum sample_f
        }
  }
  
 -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 +71,7 @@@
                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 +101,7 @@@ static int oss_init(struct writer_node 
  {
        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 +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;
                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);
@@@ -245,7 -245,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 --combined para.h
index 2525bee6e3a276da039790c0f39e056aed808f94,50ec7eaadce449bfe973b48449bf2ee74b011742..bbf91330855610ce0316986f2195d5161e3bc2bb
--- 1/para.h
--- 2/para.h
+++ b/para.h
@@@ -20,8 -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 */
@@@ -46,7 -44,6 +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.
diff --combined play.c
index 66383ebe0b8cb6a742801cfd33cd08155ee857b1,69b463ac326e5b1341448bc14b438d6b02b3ce29..b60557244f5755b620c10240c60a2b73e28983e1
--- 1/play.c
--- 2/play.c
+++ b/play.c
@@@ -50,7 -50,7 +50,7 @@@ static struct lls_parse_result *play_lp
   * 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 +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))
@@@ -288,7 -288,7 +288,7 @@@ static int shuffle_compare(__a_unused c
  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 -405,16 +405,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 -591,7 +591,7 @@@ static char *get_user_key_map_seq(int k
        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 -611,7 +611,7 @@@ static char *get_key_map_seq_safe(int k
  
        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 -651,7 +651,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;
@@@ -854,7 -854,6 +854,7 @@@ static int com_pause(__a_unused struct 
        ss = PARA_MAX(ss, 0UL);
        ss = PARA_MIN(ss, pt->num_chunks);
        pt->start_chunk = ss;
 +      pt->rq = CRT_REPOS;
        kill_stream();
        return 0;
  }
@@@ -1075,7 -1074,7 +1075,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)
@@@ -1109,16 -1108,16 +1109,16 @@@ static void session_update_time_string(
  /*
   * 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;
  
  
  #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;
@@@ -1164,11 -1163,11 +1164,11 @@@ static void session_update_time_string(
  }
  #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);
@@@ -1202,7 -1201,7 +1202,7 @@@ static unsigned get_time_string(char **
        );
  }
  
 -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;
  
                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) {
@@@ -1255,18 -1254,18 +1255,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);
diff --combined prebuffer_filter.c
index 031aa47e6c57efa0157a241a974c11af8bf64882,ef66ffa13f2308eb4ac1571d9314ba4aee17e8b2..28b710108d435bd40d5970ca117b103d6fb0b1a3
@@@ -22,7 -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 +50,7 @@@ static void prebuffer_close(struct filt
        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 +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 --combined recv_common.c
index ad34991c6a5b281aa6c39e392befa4fe64422335,2d3ae2cddaa9bf776d844f509cc3a3d567d37ce3..1939300a7c946d68d1b324ee6beb8c0bf146893b
@@@ -41,7 -41,7 +41,7 @@@ int check_receiver_arg(const char *ra, 
        *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 +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);
  
diff --combined resample_filter.c
index 01b4ac48ce11b97d3199520a6e6f1342db87d470,ebf85a4a053c9cc193e377ef837469277bc75bc6..bf28e975de2720f001be95ee06b269a1a8ecdd2c
@@@ -51,7 -51,7 +51,7 @@@ static void resample_close(struct filte
  
  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 +62,7 @@@
        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 +70,7 @@@
  
        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 +167,10 @@@ static int resample_frames(int16_t *in
        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) {
                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;
        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;
        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 +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 +277,8 @@@ static void resample_teardown(__a_unuse
  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
diff --combined sched.c
index eb8d03c2bd99fdd1990d307aec977040c2611909,b2e78a1b3cf90c1500dae66b8ca5826c1d5ed675..c786c9f26f9b5fc47cfd3c8d883671c5433bd048
+++ b/sched.c
@@@ -17,9 -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 +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 +55,8 @@@
                        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 +72,29 @@@ static void unlink_and_free_task(struc
  }
  
  //#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;
                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++;
   *
   * \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,20 -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;
@@@ -186,7 -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.
@@@ -215,8 -226,6 +215,8 @@@ void sched_shutdown(struct sched *s
                                t->name);
                unlink_and_free_task(t);
        }
 +      free(s->pfd);
 +      free(s->pidx);
  }
  
  /**
   */
  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);
@@@ -279,14 -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().
@@@ -307,7 -316,7 +307,7 @@@ void task_notify(struct task *t, int er
   *
   * \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().
   */
@@@ -353,43 -362,43 +353,43 @@@ void task_notify_all(struct sched *s, i
  }
  
  /**
 - * 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)
  {
  }
  
  /**
 - * 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().
   */
@@@ -420,12 -429,12 +420,12 @@@ int sched_request_barrier(struct timeva
  }
  
  /**
 - * 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().
@@@ -441,126 -450,3 +441,126 @@@ int sched_request_barrier_or_min_delay(
        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 --combined send_common.c
index ce167542c8c3178986037c806d88af6b0c4623f2,492f275d67e5560200460891aa74cfc120bf013b..26502cabd08376673b2c46b61ccffae400344274
  #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. */
@@@ -120,14 -120,14 +120,14 @@@ void init_sender_status(struct sender_s
  
        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));
@@@ -343,6 -343,7 +343,6 @@@ void generic_com_off(struct sender_stat
   * 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
   * \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;
        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)
                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);
diff --combined server.c
index 2852ee1f10f7a9d8d4f5a93e2233b8b6fd4a4ecc,7ef0fb0b425358877dde953767188664f04bdaae..ea9cc9c003616762a96aa546c381d54417fd34a2
+++ b/server.c
@@@ -24,8 -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 "config.h"
  #include "close_on_fork.h"
@@@ -172,7 -172,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);
@@@ -181,9 -180,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.
   *
@@@ -209,7 -205,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();
@@@ -254,14 -250,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;
@@@ -317,20 -313,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,
        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++;
        /*
         * 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;
@@@ -403,7 -399,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;
@@@ -436,14 -432,14 +436,14 @@@ static void init_server_command_task(st
        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));
  
        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);
        /*
@@@ -547,7 -543,7 +547,7 @@@ static void server_init(int argc, char 
        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));
@@@ -621,14 -617,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;
  }
@@@ -662,15 -658,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);
diff --combined signal.c
index e5ef7a4119dc271320079d778313c5abf1d259d2,4e876cfe6f6d01cb6b51ca05c283a8a466e7b286..bdafeaf296e026508698c306e4b498e0710a9cee
+++ b/signal.c
@@@ -27,7 -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 +48,7 @@@ struct signal_task *signal_init_or_die(
        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,14 -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));
diff --combined spxdec_filter.c
index 94a9c78835e93157be51753bc910f251820501e8,9dc7c065edc0a7161aaa138be19ea079ba0f4a6f..08eac02a557b2d503646666edd7ffb35e1c5c5f7
@@@ -81,7 -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 +171,7 @@@ static int speexdec_write_frames(int pa
                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 +246,7 @@@ static int compute_skip_samples(ogg_pag
        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 +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 --combined string.c
index 0e04d3740b499b3db6721690d3c30419758b8371,7c8b2f0073d2b66d918cdb07908306ee61596246..2c6d35d60e7c69900826f8ddbbd7b1e16fb8cbd4
+++ 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);
  }
  
  /**
   *
   * \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 +184,7 @@@ __printf_2_0 unsigned xvasprintf(char *
        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);
@@@ -339,7 -386,7 +386,7 @@@ int for_each_line(unsigned flags, char 
                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);
@@@ -443,7 -490,7 +490,7 @@@ __printf_2_3 int para_printf(struct par
        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;
        }
@@@ -552,6 -599,37 +599,6 @@@ int para_atoi32(const char *str, int32_
        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,
        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++) {
@@@ -692,7 -770,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;
  
                        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;
@@@ -800,7 -878,7 +847,7 @@@ int para_regcomp(regex_t *preg, const c
        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);
@@@ -826,7 -904,7 +873,7 @@@ char *safe_strdup(const char *src, size
        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';
@@@ -980,7 -1058,7 +1027,7 @@@ __must_check int strwidth(const char *s
                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);
@@@ -1035,7 -1113,7 +1082,7 @@@ __must_check int sanitize_str(const cha
        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);
        }
        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);
diff --combined string.h
index 84b6f787924f18a805eea6b5d2730a4ffa230159,ef7c3ee860b3f000c1c2936eaa4458c6ec4d9a7d..060d65403bc5b0294010b582032eb5ed8ab10d49
+++ b/string.h
@@@ -67,9 -67,12 +67,12 @@@ int for_each_line(unsigned flags, char 
  } \
  )
  
+ __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);
@@@ -82,6 -85,7 +85,6 @@@ __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);
diff --combined sync_filter.c
index 3174a4ef741b0afe1107ff6bd9146f1c82f5f77e,d322395df87fdc556332ca642ea1d54e9bcc2bd9..b4710f6704ce7a4fbd03567d806ce633df164008
@@@ -102,7 -102,7 +102,7 @@@ static void sync_open(struct filter_nod
        unsigned buddy_given;
        const struct lls_opt_result *r_b;
  
-       ctx = fn->private_data = para_calloc(sizeof(*ctx));
+       ctx = fn->private_data = zalloc(sizeof(*ctx));
        init_list_head(&ctx->buddies);
  
        /* create socket to listen for incoming packets */
                        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 +176,12 @@@ static void *sync_setup(const struct ll
  
        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 +248,7 @@@ static void sync_set_timeout(struct syn
        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;
        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 +284,7 @@@ static struct sync_buddy *sync_find_bud
        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;
                }
                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 +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
  };
diff --combined udp_send.c
index ac656ff2cc02a47353fb73231fa96ad517dcd090,baeec439fac345ce7856891a25fe38d84a363267..289479878592f6b50e5e31bb30a1098e71588865
@@@ -21,8 -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"
@@@ -326,8 -326,8 +326,8 @@@ static int udp_com_add(struct sender_co
                                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;
diff --combined vss.c
index 4f270c4a50b35df749c8f9c3cf2bbeba9464eb7b,667d4cac10c69ca0f7e08ee9f20eeec787086d06..dd4ac2fbc96f54064f0dacd38ef2c9c39fa4f065
--- 1/vss.c
--- 2/vss.c
+++ b/vss.c
@@@ -28,8 -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 +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 +335,10 @@@ static int initialize_fec_client(struc
        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;
@@@ -671,7 -671,7 +671,7 @@@ size_t vss_get_fec_eof_packet(const cha
  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 +827,7 @@@ static void vss_compute_timeout(struct 
        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 +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 +950,13 @@@ static int recv_afs_msg(int afs_socket
  #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 +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()
@@@ -1087,7 -1087,7 +1087,7 @@@ static void vss_send(struct vss_task *v
        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;
                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)) {
 +              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)
                        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()))
@@@ -1194,8 -1194,8 +1194,8 @@@ void vss_init(int afs_socket, struct sc
        }
        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);
  }
diff --combined wav_filter.c
index 692306b541fcb28012b1a2a4d873a83d93855437,a26369529c923c6e0ffd6221c1d30fbc10ed350f..7d6c371433b13ae6f6d3d63e8de47f69fe133a17
@@@ -53,12 -53,12 +53,12 @@@ static void wav_open(struct filter_nod
  {
        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 +68,7 @@@
        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;
        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 +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,
  };
diff --combined wmadec_filter.c
index 8061f9aeedb32d6cd0b0cf2083eefd991ed8a1d8,d9a08881900fe223fa3e25b74f284276520cb790..f7ee2c4dbd1281e49792693afd40e83f275cd919
@@@ -16,6 -16,7 +16,6 @@@
  
  #include <math.h>
  #include <regex.h>
 -#include <sys/select.h>
  
  #include "para.h"
  #include "error.h"
@@@ -159,8 -160,8 +159,8 @@@ static void init_coef_vlc(struct privat
        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;
@@@ -427,7 -428,7 +427,7 @@@ static int wma_decode_init(char *initia
        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);
@@@ -1158,7 -1159,7 +1158,7 @@@ static int wmadec_execute(struct btr_no
  
  #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;
@@@ -1196,7 -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) {
@@@ -1228,6 -1229,6 +1228,6 @@@ const struct filter lsg_filter_cmd_com_
        .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 --combined write.c
index 9e2de3d8207d977d074546280304db085990e761,356f35d46c04114881cb2b5516920e2d213b4e0d..f7018aa115a632f345f5136c734f44e2b79d87a5
+++ b/write.c
@@@ -54,16 -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)
        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 --combined write_common.c
index 806d682f7a4b914e019fe152945e13a0d623cd76,aec776a065bd7cb3acfd3a3cabc6b6b2c557e828..9a13090541895df5b7bc7ee8f42fbc2fda37a9f2
@@@ -85,7 -85,7 +85,7 @@@ int check_writer_arg_or_die(const char 
        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 +139,8 @@@ void register_writer_node(struct writer
                .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);
  }