]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 'maint'
authorAndre Noll <maan@tuebingen.mpg.de>
Fri, 1 Apr 2016 23:33:47 +0000 (01:33 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Fri, 1 Apr 2016 23:33:47 +0000 (01:33 +0200)
A couple of overflow bugs and a aslignment issue, all detected by ubsan. Plus
two unrelated old bugs.

* maint:
  client: Fix lsatt completer.
  playlist: Do not update score if no playlist is open.
  Avoid member access within misaligned address for ancillary data buffer.
  mood.c: Avoid overflow in update_quadratic_deviation().
  mood.c: Avoid integer underflow.
  mood.c: Avoid integer overflow.

1  2 
afs.c
client.c
mood.c
net.c
playlist.c
vss.c

diff --combined afs.c
index d5afc6d9eb12eb731fecfdb8209bcd150e0e01cd,1a5e602dfbe1db769fef82c8f202ed0c9f5eca74..ee1c6b5d0edad1472551274d0de9a4288504c3af
--- 1/afs.c
--- 2/afs.c
+++ b/afs.c
@@@ -1,20 -1,15 +1,20 @@@
  /*
 - * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
 + * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
   *
   * Licensed under the GPL v2. For licencing details see COPYING.
   */
  
  /** \file afs.c Paraslash's audio file selector. */
  
 +#include <netinet/in.h>
 +#include <sys/socket.h>
  #include <regex.h>
  #include <signal.h>
  #include <fnmatch.h>
  #include <osl.h>
 +#include <arpa/inet.h>
 +#include <sys/un.h>
 +#include <netdb.h>
  
  #include "server.cmdline.h"
  #include "para.h"
@@@ -28,8 -23,8 +28,8 @@@
  #include "ipc.h"
  #include "list.h"
  #include "sched.h"
 -#include "signal.h"
  #include "fd.h"
 +#include "signal.h"
  #include "mood.h"
  #include "sideband.h"
  #include "command.h"
@@@ -81,7 -76,7 +81,7 @@@ struct command_task 
         */
        uint32_t cookie;
        /** The associated task structure. */
 -      struct task task;
 +      struct task *task;
  };
  
  extern int mmd_mutex;
@@@ -89,7 -84,7 +89,7 @@@ extern struct misc_meta_data *mmd
  
  static int server_socket;
  static struct command_task command_task_struct;
 -static struct signal_task signal_task_struct;
 +static struct signal_task *signal_task;
  
  static enum play_mode current_play_mode;
  static char *current_mop; /* mode or playlist specifier. NULL means dummy mood */
  /**
   * A random number used to "authenticate" the connection.
   *
 - * para_server picks this number by random before forking the afs process.  The
 - * command handlers write this number together with the id of the shared memory
 - * area containing the query. This way, a malicious local user has to know this
 - * number to be able to cause the afs process to crash by sending fake queries.
 + * para_server picks this number by random before it forks the afs process. The
 + * command handlers know this number as well and write it to the afs socket,
 + * together with the id of the shared memory area which contains the payload of
 + * the afs command. A local process has to know this number to abuse the afs
 + * service provided by the local socket.
   */
  extern uint32_t afs_socket_cookie;
  
   */
  struct callback_query {
        /** The function to be called. */
 -      callback_function *handler;
 +      afs_callback *handler;
        /** The number of bytes of the query */
        size_t query_size;
  };
@@@ -167,8 -161,13 +167,8 @@@ static int dispatch_result(int result_s
        }
        result.size = cr->result_size;
        result.data = result_shm + sizeof(*cr);
 -      if (result.size) {
 -              assert(handler);
 -              ret = handler(&result, cr->band, private_result_data);
 -              if (ret < 0)
 -                      PARA_NOTICE_LOG("result handler error: %s\n",
 -                              para_strerror(-ret));
 -      }
 +      assert(handler);
 +      ret = handler(&result, cr->band, private_result_data);
        ret2 = shm_detach(result_shm);
        if (ret2 < 0) {
                PARA_ERROR_LOG("detach failed: %s\n", para_strerror(-ret2));
   * copied.  It then notifies the afs process that the callback function \a f
   * should be executed by sending the shared memory identifier (shmid) to the
   * socket.
 -
 + *
   * If the callback produces a result, it sends any number of shared memory
   * identifiers back via the socket. For each such identifier received, \a
   * result_handler is called. The contents of the sma identified by the received
   *
   * \sa send_option_arg_callback_request(), send_standard_callback_request().
   */
 -int send_callback_request(callback_function *f, struct osl_object *query,
 +int send_callback_request(afs_callback *f, struct osl_object *query,
                callback_result_handler *result_handler,
                void *private_result_data)
  {
        if (ret < 0)
                goto out;
  
 -      *(uint32_t *) buf = afs_socket_cookie;
 -      *(int *) (buf + sizeof(afs_socket_cookie)) = query_shmid;
 +      *(uint32_t *)buf = afs_socket_cookie;
 +      *(int *)(buf + sizeof(afs_socket_cookie)) = query_shmid;
  
        ret = connect_local_socket(conf.afs_socket_arg);
        if (ret < 0)
                ret = *(int *) buf;
                assert(ret > 0);
                result_shmid = ret;
 -              if (!dispatch_error) {
 -                      ret = dispatch_result(result_shmid, result_handler,
 -                              private_result_data);
 -                      if (ret < 0)
 -                              dispatch_error = 1;
 -              }
 +              ret = dispatch_result(result_shmid, result_handler,
 +                      private_result_data);
 +              if (ret < 0 && dispatch_error >= 0)
 +                      dispatch_error = ret;
                ret = shm_destroy(result_shmid);
                if (ret < 0)
                        PARA_CRIT_LOG("destroy result failed: %s\n",
@@@ -272,11 -273,8 +272,11 @@@ out
                PARA_CRIT_LOG("shm destroy error\n");
        if (fd >= 0)
                close(fd);
 -//    PARA_DEBUG_LOG("callback_ret: %d\n", ret);
 -      return ret < 0? ret : num_dispatched;
 +      if (dispatch_error < 0)
 +              return dispatch_error;
 +      if (ret < 0)
 +              return ret;
 +      return num_dispatched;
  }
  
  /**
   * \param result_handler See \ref send_callback_request.
   * \param private_result_data See \ref send_callback_request.
   *
 - * Some commands have a couple of options that are parsed in child context for
 - * syntactic correctness and are stored in a special options structure for that
 - * command. This function allows to pass such a structure together with a list
 - * of further arguments (often a list of audio files) to the parent process.
 + * Some command handlers pass command-specific options to a callback, together
 + * with a list of further arguments (often a list of audio files). This
 + * function allows to pass an arbitrary structure (given as an osl object) and
 + * a usual argument vector to the specified callback.
   *
   * \return The return value of the underlying call to \ref
   * send_callback_request().
   * \sa send_standard_callback_request(), send_callback_request().
   */
  int send_option_arg_callback_request(struct osl_object *options,
 -              int argc,  char * const * const argv, callback_function *f,
 +              int argc,  char * const * const argv, afs_callback *f,
                callback_result_handler *result_handler,
                void *private_result_data)
  {
   * send_option_arg_callback_request().
   */
  int send_standard_callback_request(int argc,  char * const * const argv,
 -              callback_function *f, callback_result_handler *result_handler,
 +              afs_callback *f, callback_result_handler *result_handler,
                void *private_result_data)
  {
        return send_option_arg_callback_request(NULL, argc, argv, f, result_handler,
@@@ -362,11 -360,8 +362,11 @@@ static int action_if_pattern_matches(st
        name = (char *)name_obj.data;
        if ((!name || !*name) && (pmd->pm_flags & PM_SKIP_EMPTY_NAME))
                return 1;
 -      if (!pmd->patterns.size && (pmd->pm_flags & PM_NO_PATTERN_MATCHES_EVERYTHING))
 +      if (pmd->patterns.size == 0 &&
 +                      (pmd->pm_flags & PM_NO_PATTERN_MATCHES_EVERYTHING)) {
 +              pmd->num_matches++;
                return pmd->action(pmd->table, row, name, pmd->data);
 +      }
        for (p = pattern_txt; p < pattern_txt + pmd->patterns.size;
                        p += strlen(p) + 1) {
                ret = fnmatch(p, name, pmd->fnmatch_flags);
@@@ -424,7 -419,7 +424,7 @@@ static int pass_afd(int fd, char *buf, 
  {
        struct msghdr msg = {.msg_iov = NULL};
        struct cmsghdr *cmsg;
-       char control[255];
+       char control[255] __a_aligned(8);
        int ret;
        struct iovec iov;
  
  }
  
  /**
 - * Open the audio file with highest score.
 + * Pass the fd of the next audio file to the server process.
   *
   * This stores all information for streaming the "best" audio file in a shared
   * memory area. The id of that area and an open file descriptor for the next
   */
  static int open_next_audio_file(void)
  {
 -      struct osl_row *aft_row;
        struct audio_file_data afd;
        int ret, shmid;
        char buf[8];
 -      long score;
 -again:
 -      PARA_NOTICE_LOG("getting next audio file\n");
 -      ret = score_get_best(&aft_row, &score);
 +
 +      ret = open_and_update_audio_file(&afd);
        if (ret < 0) {
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
                goto no_admissible_files;
        }
 -      ret = open_and_update_audio_file(aft_row, score, &afd);
 -      if (ret < 0) {
 -              ret = score_delete(aft_row);
 -              if (ret < 0) {
 -                      PARA_ERROR_LOG("%s\n", para_strerror(-ret));
 -                      goto no_admissible_files;
 -              }
 -              goto again;
 -      }
        shmid = ret;
        if (!write_ok(server_socket)) {
                ret = -E_AFS_SOCKET;
@@@ -502,7 -509,6 +502,7 @@@ static int activate_mood_or_playlist(ch
        enum play_mode mode;
        int ret;
  
 +      PARA_INFO_LOG("new playlist: %s\n", arg);
        if (!arg) {
                ret = change_current_mood(NULL); /* always successful */
                mode = PLAY_MODE_MOOD;
@@@ -557,70 -563,54 +557,70 @@@ int afs_cb_result_handler(struct osl_ob
        struct command_context *cc = private;
  
        assert(cc);
 -      if (!result->size)
 -              return 1;
 -      if (cc->use_sideband)
 -              return send_sb(&cc->scc, result->data, result->size, band,
 -                      true);
 -      return sc_send_bin_buffer(&cc->scc, result->data, result->size);
 +      switch (band) {
 +      case SBD_OUTPUT:
 +      case SBD_DEBUG_LOG:
 +      case SBD_INFO_LOG:
 +      case SBD_NOTICE_LOG:
 +      case SBD_WARNING_LOG:
 +      case SBD_ERROR_LOG:
 +      case SBD_CRIT_LOG:
 +      case SBD_EMERG_LOG:
 +              assert(result->size > 0);
 +              return send_sb(&cc->scc, result->data, result->size, band, true);
 +      case SBD_AFS_CB_FAILURE:
 +              return *(int *)(result->data);
 +      default:
 +              return -E_BAD_BAND;
 +      }
  }
  
 -static void com_select_callback(int fd, const struct osl_object *query)
 +static void flush_and_free_pb(struct para_buffer *pb)
  {
 -      struct para_buffer pb = {
 -              .max_size = shm_get_shmmax(),
 -              .private_data = &(struct afs_max_size_handler_data) {
 -                      .fd = fd,
 -                      .band = SBD_OUTPUT
 -              },
 -              .max_size_handler = afs_max_size_handler,
 -      };
 -      char *arg = query->data;
 -      int num_admissible, ret, ret2;
 +      int ret;
 +      struct afs_max_size_handler_data *amshd = pb->private_data;
 +
 +      if (pb->buf && pb->size > 0) {
 +              ret = pass_buffer_as_shm(amshd->fd, amshd->band, pb->buf,
 +                      pb->offset);
 +              if (ret < 0)
 +                      PARA_ERROR_LOG("%s\n", para_strerror(-ret));
 +      }
 +      free(pb->buf);
 +}
 +
 +static int com_select_callback(struct afs_callback_arg *aca)
 +{
 +      char *arg = aca->query.data;
 +      int num_admissible, ret;
  
        ret = clear_score_table();
        if (ret < 0) {
 -              ret2 = para_printf(&pb, "%s\n", para_strerror(-ret));
 -              goto out;
 +              para_printf(&aca->pbout, "could not clear score table: %s\n",
 +                      para_strerror(-ret));
 +              return ret;
        }
        if (current_play_mode == PLAY_MODE_MOOD)
                close_current_mood();
        else
                playlist_close();
        ret = activate_mood_or_playlist(arg, &num_admissible);
 -      if (ret < 0) {
 -              ret2 = para_printf(&pb, "%s\nswitching back to %s\n",
 -                      para_strerror(-ret), current_mop?
 -                      current_mop : "dummy");
 -              ret = activate_mood_or_playlist(current_mop, &num_admissible);
 -              if (ret < 0) {
 -                      if (ret2 >= 0)
 -                              ret2 = para_printf(&pb, "failed, switching to dummy\n");
 -                      activate_mood_or_playlist(NULL, &num_admissible);
 -              }
 -      } else
 -              ret2 = para_printf(&pb, "activated %s (%d admissible files)\n", current_mop?
 -                      current_mop : "dummy mood", num_admissible);
 +      if (ret >= 0)
 +              goto out;
 +      /* ignore subsequent errors (but log them) */
 +      para_printf(&aca->pbout, "could not activate %s: %s\n"
 +              "switching back to %s\n",
 +              arg, para_strerror(-ret), current_mop? current_mop : "dummy");
 +      ret = activate_mood_or_playlist(current_mop, &num_admissible);
 +      if (ret >= 0)
 +              goto out;
 +      para_printf(&aca->pbout, "could not activate %s: %s\nswitching to dummy\n",
 +              current_mop, para_strerror(-ret));
 +      activate_mood_or_playlist(NULL, &num_admissible);
  out:
 -      if (ret2 >= 0 && pb.offset)
 -              pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
 -      free(pb.buf);
 +      para_printf(&aca->pbout, "activated %s (%d admissible files)\n",
 +              current_mop? current_mop : "dummy mood", num_admissible);
 +      return ret;
  }
  
  int com_select(struct command_context *cc)
@@@ -645,19 -635,25 +645,19 @@@ static int setup_command_socket_or_die(
  {
        int ret, socket_fd;
        char *socket_name = conf.afs_socket_arg;
 -      struct sockaddr_un unix_addr;
  
        unlink(socket_name);
 -      ret = create_local_socket(socket_name, &unix_addr,
 -              S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IWOTH);
 +      ret = create_local_socket(socket_name, 0);
        if (ret < 0) {
 -              PARA_EMERG_LOG("%s: %s\n", para_strerror(-ret), socket_name);
 -              exit(EXIT_FAILURE);
 +              ret = create_local_socket(socket_name,
 +                      S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IWOTH);
 +              if (ret < 0) {
 +                      PARA_EMERG_LOG("%s: %s\n", para_strerror(-ret),
 +                              socket_name);
 +                      exit(EXIT_FAILURE);
 +              }
        }
        socket_fd = ret;
 -      if (listen(socket_fd , 5) < 0) {
 -              PARA_EMERG_LOG("can not listen on socket\n");
 -              exit(EXIT_FAILURE);
 -      }
 -      ret = mark_fd_nonblocking(socket_fd);
 -      if (ret < 0) {
 -              close(socket_fd);
 -              return ret;
 -      }
        PARA_INFO_LOG("listening on socket %s (fd %d)\n", socket_name,
                socket_fd);
        return socket_fd;
@@@ -694,7 -690,7 +694,7 @@@ static int make_database_dir(void
  
        get_database_dir();
        ret = para_mkdir(database_dir, 0777);
 -      if (ret >= 0 || is_errno(-ret, EEXIST))
 +      if (ret >= 0 || ret == -ERRNO_TO_PARA_ERROR(EEXIST))
                return 1;
        return ret;
  }
@@@ -721,7 -717,13 +721,7 @@@ static int open_afs_tables(void
        return ret;
  }
  
 -static void signal_pre_select(struct sched *s, struct task *t)
 -{
 -      struct signal_task *st = container_of(t, struct signal_task, task);
 -      para_fd_set(st->fd, &s->rfds, &s->max_fileno);
 -}
 -
 -static int afs_signal_post_select(struct sched *s, __a_unused struct task *t)
 +static int afs_signal_post_select(struct sched *s, __a_unused void *context)
  {
        int signum, ret;
  
@@@ -749,24 -751,24 +749,24 @@@ shutdown
  
  static void register_signal_task(struct sched *s)
  {
 -      struct signal_task *st = &signal_task_struct;
 -
        para_sigaction(SIGPIPE, SIG_IGN);
 -      st->fd = para_signal_init();
 -      PARA_INFO_LOG("signal pipe: fd %d\n", st->fd);
 +      signal_task = signal_init_or_die();
        para_install_sighandler(SIGINT);
        para_install_sighandler(SIGTERM);
        para_install_sighandler(SIGHUP);
  
 -      st->task.pre_select = signal_pre_select;
 -      st->task.post_select = afs_signal_post_select;
 -      sprintf(st->task.status, "signal task");
 -      register_task(s, &st->task);
 +      signal_task->task = task_register(&(struct task_info) {
 +              .name = "signal",
 +              .pre_select = signal_pre_select,
 +              .post_select = afs_signal_post_select,
 +              .context = signal_task,
 +
 +      }, s);
  }
  
  static struct list_head afs_client_list;
  
 -/** Describes on connected afs client. */
 +/** Describes one connected afs client. */
  struct afs_client {
        /** Position in the afs client list. */
        struct list_head node;
        struct timeval connect_time;
  };
  
 -static void command_pre_select(struct sched *s, struct task *t)
 +static void command_pre_select(struct sched *s, void *context)
  {
 -      struct command_task *ct = container_of(t, struct command_task, task);
 +      struct command_task *ct = context;
        struct afs_client *client;
  
        para_fd_set(server_socket, &s->rfds, &s->max_fileno);
   * \return Zero if \a buf is \p NULL or \a size is zero. Negative on errors,
   * and positive on success.
   */
 -int pass_buffer_as_shm(int fd, uint8_t band, char *buf, size_t size)
 +int pass_buffer_as_shm(int fd, uint8_t band, const char *buf, size_t size)
  {
        int ret, shmid;
        void *shm;
        struct callback_result *cr;
  
 -      if (!buf || !size)
 -              return 0;
 +      if (size == 0)
 +              assert(band != SBD_OUTPUT);
        ret = shm_new(size + sizeof(*cr));
        if (ret < 0)
                return ret;
        cr = shm;
        cr->result_size = size;
        cr->band = band;
 -      memcpy(shm + sizeof(*cr), buf, size);
 +      if (size > 0)
 +              memcpy(shm + sizeof(*cr), buf, size);
        ret = shm_detach(shm);
        if (ret < 0)
                goto err;
@@@ -837,43 -838,26 +837,43 @@@ err
        return ret;
  }
  
 -/*
 - * On errors, negative value is written to fd.
 - * On success: If query produced a result, the result_shmid is written to fd.
 - * Otherwise, zero is written.
 - */
  static int call_callback(int fd, int query_shmid)
  {
        void *query_shm;
        struct callback_query *cq;
 -      struct osl_object query;
 -      int ret;
 +      int ret, ret2;
 +      struct afs_callback_arg aca = {.fd = fd};
  
        ret = shm_attach(query_shmid, ATTACH_RW, &query_shm);
        if (ret < 0)
                return ret;
        cq = query_shm;
 -      query.data = (char *)query_shm + sizeof(*cq);
 -      query.size = cq->query_size;
 -      cq->handler(fd, &query);
 -      return shm_detach(query_shm);
 +      aca.query.data = (char *)query_shm + sizeof(*cq);
 +      aca.query.size = cq->query_size;
 +      aca.pbout.max_size = shm_get_shmmax();
 +      aca.pbout.max_size_handler = afs_max_size_handler;
 +      aca.pbout.private_data = &(struct afs_max_size_handler_data) {
 +              .fd = fd,
 +              .band = SBD_OUTPUT
 +      };
 +      ret = cq->handler(&aca);
 +      ret2 = shm_detach(query_shm);
 +      if (ret2 < 0) {
 +              if (ret < 0) /* ignore (but log) detach error */
 +                      PARA_ERROR_LOG("could not detach sma: %s\n",
 +                              para_strerror(-ret2));
 +              else
 +                      ret = ret2;
 +      }
 +      flush_and_free_pb(&aca.pbout);
 +      if (ret < 0) {
 +              ret2 = pass_buffer_as_shm(fd, SBD_AFS_CB_FAILURE,
 +                      (const char *)&ret, sizeof(ret));
 +              if (ret2 < 0)
 +                      PARA_ERROR_LOG("could not pass cb failure packet: %s\n",
 +                              para_strerror(-ret));
 +      }
 +      return ret;
  }
  
  static int execute_server_command(fd_set *rfds)
@@@ -931,14 -915,14 +931,14 @@@ 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, struct task *t)
 +static int command_post_select(struct sched *s, void *context)
  {
 -      struct command_task *ct = container_of(t, struct command_task, task);
 +      struct command_task *ct = context;
        struct sockaddr_un unix_addr;
        struct afs_client *client, *tmp;
        int fd, ret;
  
 -      ret = task_get_notification(t);
 +      ret = task_get_notification(ct->task);
        if (ret < 0)
                return ret;
        ret = execute_server_command(&s->rfds);
@@@ -986,12 -970,10 +986,12 @@@ static void register_command_task(uint3
        ct->fd = setup_command_socket_or_die();
        ct->cookie = cookie;
  
 -      ct->task.pre_select = command_pre_select;
 -      ct->task.post_select = command_post_select;
 -      sprintf(ct->task.status, "afs command task");
 -      register_task(s, &ct->task);
 +      ct->task = task_register(&(struct task_info) {
 +              .name = "afs command",
 +              .pre_select = command_pre_select,
 +              .post_select = command_post_select,
 +              .context = ct,
 +      }, s);
  }
  
  /**
@@@ -1023,7 -1005,6 +1023,7 @@@ __noreturn void afs_init(uint32_t cooki
        s.default_timeout.tv_sec = 0;
        s.default_timeout.tv_usec = 999 * 1000;
        ret = schedule(&s);
 +      sched_shutdown(&s);
  out_close:
        close_afs_tables();
  out:
        exit(EXIT_FAILURE);
  }
  
 -static void create_tables_callback(int fd, const struct osl_object *query)
 +static int com_init_callback(struct afs_callback_arg *aca)
  {
 -      uint32_t table_mask = *(uint32_t *)query->data;
 +      uint32_t table_mask = *(uint32_t *)aca->query.data;
        int i, ret;
 -      struct para_buffer pb = {.buf = NULL};
  
        close_afs_tables();
        for (i = 0; i < NUM_AFS_TABLES; i++) {
                if (!t->create)
                        continue;
                ret = t->create(database_dir);
 -              if (ret < 0)
 +              if (ret < 0) {
 +                      para_printf(&aca->pbout, "cannot create table %s\n",
 +                              t->name);
                        goto out;
 -              para_printf(&pb, "successfully created %s table\n", t->name);
 +              }
 +              para_printf(&aca->pbout, "successfully created %s table\n",
 +                      t->name);
        }
        ret = open_afs_tables();
 -out:
        if (ret < 0)
 -              para_printf(&pb, "%s\n", para_strerror(-ret));
 -      if (pb.buf)
 -              pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
 -      free(pb.buf);
 +              para_printf(&aca->pbout, "cannot open afs tables\n");
 +out:
 +      return ret;
  }
  
  int com_init(struct command_context *cc)
                                return -E_BAD_TABLE_NAME;
                }
        }
 -      ret = send_callback_request(create_tables_callback, &query,
 +      return send_callback_request(com_init_callback, &query,
                afs_cb_result_handler, cc);
 -      if (ret < 0 && !cc->use_sideband)
 -              /* ignore return value */
 -              sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
 -      return ret;
  }
  
  /**
@@@ -1101,9 -1085,7 +1101,9 @@@ enum com_check_flags 
        /** Check the mood table. */
        CHECK_MOODS = 2,
        /** Check the playlist table. */
 -      CHECK_PLAYLISTS = 4
 +      CHECK_PLAYLISTS = 4,
 +      /** Check the attribute table against the audio file table. */
 +      CHECK_ATTS = 8
  };
  
  int com_check(struct command_context *cc)
                        flags |= CHECK_AFT;
                        continue;
                }
 +              if (!strcmp(arg, "-A")) {
 +                      flags |= CHECK_ATTS;
 +                      continue;
 +              }
                if (!strcmp(arg, "-p")) {
                        flags |= CHECK_PLAYLISTS;
                        continue;
                if (ret < 0)
                        return ret;
        }
 +      if (flags & CHECK_ATTS) {
 +              ret = send_callback_request(attribute_check_callback, NULL,
 +                      afs_cb_result_handler, cc);
 +              if (ret < 0)
 +                      return ret;
 +      }
        if (flags & CHECK_PLAYLISTS) {
                ret = send_callback_request(playlist_check_callback,
                        NULL, afs_cb_result_handler, cc);
   * \param pb May be \p NULL.
   * \param data Type depends on \a event.
   *
 - * This function calls the table handlers of all tables and passes \a pb and \a
 - * data verbatim. It's up to the handlers to interpret the \a data pointer.
 + * This function calls each table event handler, passing \a pb and \a data
 + * verbatim. It's up to the handlers to interpret the \a data pointer. If a
 + * handler returns negative, the loop is aborted.
 + *
 + * \return The (negative) error code of the first handler that failed, or non-negative
 + * if all handlers succeeded.
   */
 -void afs_event(enum afs_events event, struct para_buffer *pb,
 +__must_check int afs_event(enum afs_events event, struct para_buffer *pb,
                void *data)
  {
        int i, ret;
                if (!t->event_handler)
                        continue;
                ret = t->event_handler(event, pb, data);
 -              if (ret < 0)
 +              if (ret < 0) {
                        PARA_CRIT_LOG("table %s, event %d: %s\n", t->name,
                                event, para_strerror(-ret));
 +                      return ret;
 +              }
        }
 +      return 1;
  }
  
  /**
diff --combined client.c
index 34cc9189bb2a1768765be83a5edaf5c0e2be7f5d,40ae9bc87f432b3812182d44da7953052afb2a4b..31cfff09798b2089e2b815a544fd207464f0e76c
+++ b/client.c
@@@ -1,5 -1,5 +1,5 @@@
  /*
 - * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
 + * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
   *
   * Licensed under the GPL v2. For licencing details see COPYING.
   */
@@@ -35,28 -35,28 +35,28 @@@ __printf_2_3 void (*para_log)(int, cons
  
  #ifdef HAVE_READLINE
  #include "interactive.h"
 -#include "server_completion.h"
 -#include "afs_completion.h"
 +#include "server.completion.h"
 +#include "afs.completion.h"
  
  struct exec_task {
 -      struct task task;
 +      struct task *task;
        struct btr_node *btrn;
        char *result_buf;
        size_t result_size;
  };
  
 -static void exec_pre_select(struct sched *s, struct task *t)
 +static void exec_pre_select(struct sched *s, void *context)
  {
 -      struct exec_task *et = container_of(t, struct exec_task, task);
 +      struct exec_task *et = context;
        int ret = btr_node_status(et->btrn, 0, BTR_NT_LEAF);
  
        if (ret != 0)
                sched_min_delay(s);
  }
  
 -static int exec_post_select(__a_unused struct sched *s, struct task *t)
 +static int exec_post_select(__a_unused struct sched *s, void *context)
  {
 -      struct exec_task *et = container_of(t, struct exec_task, task);
 +      struct exec_task *et = context;
        struct btr_node *btrn = et->btrn;
        char *buf;
        size_t sz;
@@@ -93,6 -93,11 +93,6 @@@ static int execute_client_command(cons
        int ret;
        struct sched command_sched = {.default_timeout = {.tv_sec = 1}};
        struct exec_task exec_task = {
 -              .task = {
 -                      .pre_select = exec_pre_select,
 -                      .post_select = exec_post_select,
 -                      .status = "client exec task",
 -              },
                .result_buf = para_strdup(""),
                .result_size = 1,
        };
                goto out;
        exec_task.btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "exec_collect"));
 -      register_task(&command_sched, &exec_task.task);
 +      exec_task.task = task_register(&(struct task_info) {
 +              .name = "client exec",
 +              .pre_select = exec_pre_select,
 +              .post_select = exec_post_select,
 +              .context = &exec_task,
 +      }, &command_sched);
        ret = client_connect(ct, &command_sched, NULL, exec_task.btrn);
        if (ret < 0)
                goto out;
        schedule(&command_sched);
 +      sched_shutdown(&command_sched);
        *result = exec_task.result_buf;
        btr_remove_node(&exec_task.btrn);
 -      client_disconnect(ct);
        ret = 1;
  out:
        btr_remove_node(&exec_task.btrn);
@@@ -204,10 -204,10 +204,10 @@@ I9E_DUMMY_COMPLETER(pause)
  I9E_DUMMY_COMPLETER(play);
  I9E_DUMMY_COMPLETER(si);
  I9E_DUMMY_COMPLETER(term);
 -I9E_DUMMY_COMPLETER(version);
  I9E_DUMMY_COMPLETER(stop);
  I9E_DUMMY_COMPLETER(addatt);
  I9E_DUMMY_COMPLETER(init);
 +I9E_DUMMY_COMPLETER(tasks);
  
  static struct i9e_completer completers[];
  
@@@ -217,13 -217,6 +217,13 @@@ static void help_completer(struct i9e_c
        result->matches = i9e_complete_commands(ci->word, completers);
  }
  
 +static void version_completer(struct i9e_completion_info *ci,
 +              struct i9e_completion_result *cr)
 +{
 +      char *opts[] = {"-v", NULL};
 +      i9e_complete_option(opts, ci, cr);
 +}
 +
  static void stat_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
  {
@@@ -276,9 -269,9 +276,9 @@@ static void ls_completer(struct i9e_com
                struct i9e_completion_result *cr)
  {
        char *opts[] = {
 -              "--", "-l", "-ls", "-ll", "-lv", "-lp", "-lm", "-lc", "-p",
 -              "-a", "-r", "-d", "-sp", "-sl", "-ss", "-sn", "-sf", "-sc",
 -              "-si", "-sy", "-sb", "-sd", "-sa", NULL
 +              "--", "-l", "-l=s", "-l=l", "-l=v", "-l=p", "-l=m", "-l=c",
 +              "-p", "-a", "-r", "-d", "-s=p", "-s=l", "-s=s", "-s=n", "-s=f",
 +              "-s=c", "-s=i", "-s=y", "-s=b", "-s=d", "-s=a", NULL
        };
        if (ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
@@@ -323,11 -316,7 +323,7 @@@ static void lsatt_completer(struct i9e_
                struct i9e_completion_result *cr)
  {
        char *opts[] = {"-i", "-l", "-r", NULL};
-       if (ci->word[0] == '-')
-               i9e_complete_option(opts, ci, cr);
-       else
-               complete_attributes(ci->word, &cr->matches);
+       i9e_complete_option(opts, ci, cr);
  }
  
  static void mvatt_completer(struct i9e_completion_info *ci,
@@@ -451,6 -440,7 +447,6 @@@ static int client_i9e_line_handler(cha
  {
        int ret;
  
 -      client_disconnect(ct);
        PARA_DEBUG_LOG("line: %s\n", line);
        ret = make_client_argv(line);
        if (ret <= 0)
        ret = client_connect(ct, &sched, NULL, NULL);
        if (ret < 0)
                return ret;
 -      i9e_attach_to_stdout(ct->btrn);
 +      i9e_attach_to_stdout(ct->btrn[0]);
        return 1;
  }
  
@@@ -503,7 -493,6 +499,7 @@@ __noreturn static void interactive_sess
                goto out;
        para_log = i9e_log;
        ret = schedule(&sched);
 +      sched_shutdown(&sched);
        i9e_close();
        para_log = stderr_log;
  out:
@@@ -535,31 -524,27 +531,31 @@@ __noreturn static void print_completion
  
  #endif /* HAVE_READLINE */
  
 -static int supervisor_post_select(struct sched *s, __a_unused struct task *t)
 +struct supervisor_task {
 +      bool stdout_task_started;
 +      struct task *task;
 +};
 +
 +static int supervisor_post_select(struct sched *s, void *context)
  {
 -      if (ct->task.error < 0)
 -              return ct->task.error;
 -      if (ct->status == CL_SENDING) {
 -              stdin_set_defaults(&sit);
 -              register_task(s, &sit.task);
 -              return -E_TASK_STARTED;
 +      struct supervisor_task *svt = context;
 +      int ret = task_status(ct->task);
 +
 +      if (ret < 0)
 +              return ret;
 +      if (!svt->stdout_task_started && ct->status == CL_EXECUTING) {
 +              stdout_task_register(&sot, s);
 +              svt->stdout_task_started = true;
 +              return 1;
        }
 -      if (ct->status == CL_RECEIVING) {
 -              stdout_set_defaults(&sot);
 -              register_task(s, &sot.task);
 +      if (ct->status == CL_SENDING) {
 +              stdin_task_register(&sit, s);
                return -E_TASK_STARTED;
        }
        return 0;
  }
  
 -static struct task svt = {
 -      .post_select = supervisor_post_select,
 -      .status = "supervisor task"
 -};
 +static struct supervisor_task supervisor_task;
  
  /**
   * The client program to connect to para_server.
@@@ -606,30 -591,21 +602,30 @@@ int main(int argc, char *argv[]
        if (ret < 0)
                goto out;
        sot.btrn = btr_new_node(&(struct btr_node_description)
 -              EMBRACE(.name = "stdout", .parent = ct->btrn));
 -      register_task(&sched, &svt);
 +              EMBRACE(.name = "stdout", .parent = ct->btrn[0]));
 +      supervisor_task.task = task_register(&(struct task_info) {
 +              .name = "supervisor",
 +              .post_select = supervisor_post_select,
 +              .context = &supervisor_task,
 +      }, &sched);
 +
        ret = schedule(&sched);
 -      if (ret >= 0 && ct->task.error < 0) {
 -              switch(ct->task.error) {
 -              /* these are not errors */
 -              case -E_SERVER_CMD_SUCCESS:
 -              case -E_EOF:
 -              case -E_SERVER_EOF:
 -              case -E_BTR_EOF:
 -                      ret = 0;
 -                      break;
 -              default: ret = -E_SERVER_CMD_FAILURE;
 +      if (ret >= 0) {
 +              ret = task_status(ct->task);
 +              if (ret < 0) {
 +                      switch (ret) {
 +                      /* these are not errors */
 +                      case -E_SERVER_CMD_SUCCESS:
 +                      case -E_EOF:
 +                      case -E_SERVER_EOF:
 +                      case -E_BTR_EOF:
 +                              ret = 0;
 +                              break;
 +                      default: ret = -E_SERVER_CMD_FAILURE;
 +                      }
                }
        }
 +      sched_shutdown(&sched);
  out:
        if (ret < 0)
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
diff --combined mood.c
index d29c62c22b396ac685f140b9d936f041184ca5d3,45b051a7267cdd9cd1a9049a00685a150681a1cb..daa8196ad1a05fc9a7aa3d0ad648fdcaddf56fb0
--- 1/mood.c
--- 2/mood.c
+++ b/mood.c
@@@ -1,5 -1,5 +1,5 @@@
  /*
 - * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
 + * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
   *
   * Licensed under the GPL v2. For licencing details see COPYING.
   */
@@@ -18,7 -18,6 +18,7 @@@
  #include "ipc.h"
  #include "mm.h"
  #include "sideband.h"
 +#include "mood.h"
  
  /**
   * Contains statistical data of the currently admissible audio files.
@@@ -30,7 -29,7 +30,7 @@@ struct afs_statistics 
        int64_t num_played_sum;
        /** Sum of last played times over all admissible files. */
        int64_t last_played_sum;
 -      /** Quadratic deviation of num played time. */
 +      /** Quadratic deviation of num played count. */
        int64_t num_played_qd;
        /** Quadratic deviation of last played time. */
        int64_t last_played_qd;
@@@ -410,18 -409,20 +410,18 @@@ static int check_mood(struct osl_row *m
        int ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
  
        if (ret < 0) {
 -              para_printf(pb, "failed to get mood definition: %s\n",
 -                      para_strerror(-ret));
 +              para_printf(pb, "cannot read mood\n");
                return ret;
        }
        if (!*mood_name) /* ignore dummy row */
                goto out;
 -      ret = para_printf(pb, "checking mood %s...\n", mood_name);
 -      if (ret < 0)
 -              goto out;
 +      para_printf(pb, "checking mood %s...\n", mood_name);
        ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
                parse_mood_line, &mlpd);
        if (ret < 0)
 -              para_printf(pb, "%s line %u: %s\n", mood_name, mlpd.line_num,
 -                      para_strerror(-ret));
 +              para_printf(pb, "mood %s: error in line %u: %s\n", mood_name,
 +                      mlpd.line_num, para_strerror(-ret));
 +      ret = 1; /* don't fail the loop on invalid mood definitions */
  out:
        osl_close_disk_object(&mood_def);
        return ret;
  /**
   * Check all moods for syntax errors.
   *
 - * \param fd The afs socket.
 - * \param query Unused.
 + * \param aca Only ->pbout is used for diagnostics.
 + *
 + * \return Negative on fatal errors. Inconsistent mood definitions are not
 + * considered an error.
   */
 -void mood_check_callback(int fd, __a_unused const struct osl_object *query)
 -{
 -      struct para_buffer pb = {
 -              .max_size = shm_get_shmmax(),
 -              .private_data = &(struct afs_max_size_handler_data) {
 -                      .fd = fd,
 -                      .band = SBD_OUTPUT
 -              },
 -              .max_size_handler = afs_max_size_handler
 -      };
 -
 -      int ret = para_printf(&pb, "checking moods...\n");
 -      if (ret < 0)
 -              return;
 -      osl_rbtree_loop(moods_table, BLOBCOL_ID, &pb,
 -              check_mood);
 -      if (pb.offset)
 -              pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
 -      free(pb.buf);
 -}
 -
 -#if 0
 -static unsigned int_log2(uint64_t x)
 +int mood_check_callback(struct afs_callback_arg *aca)
  {
 -      unsigned res = 0;
 -
 -      while (x) {
 -              x /= 2;
 -              res++;
 -      }
 -      return res;
 +      para_printf(&aca->pbout, "checking moods...\n");
 +      return osl(osl_rbtree_loop(moods_table, BLOBCOL_ID, &aca->pbout,
 +              check_mood));
  }
 -#endif
  
  static int64_t normalized_value(int64_t x, int64_t n, int64_t sum, int64_t qd)
  {
        if (!n || !qd)
                return 0;
-       return 100 * (n * x - sum) / (int64_t)int_sqrt(n * qd);
+       return 100 * (n * x - sum) / (int64_t)int_sqrt(n) / (int64_t)int_sqrt(qd);
  }
  
 -static long compute_num_played_score(struct afs_info *afsi)
 +static long compute_score(struct afs_info *afsi, long mood_score)
  {
 -      return -normalized_value(afsi->num_played, statistics.num,
 +      mood_score -= normalized_value(afsi->num_played, statistics.num,
                statistics.num_played_sum, statistics.num_played_qd);
 -}
 -
 -static long compute_last_played_score(struct afs_info *afsi)
 -{
 -      return -normalized_value(afsi->last_played, statistics.num,
 +      mood_score -= normalized_value(afsi->last_played, statistics.num,
                statistics.last_played_sum, statistics.last_played_qd);
 -}
 -
 -static long compute_dynamic_score(const struct osl_row *aft_row)
 -{
 -      struct afs_info afsi;
 -      int64_t score, nscore = 0, lscore = 0;
 -      int ret;
 -
 -      ret = get_afsi_of_row(aft_row, &afsi);
 -      if (ret < 0)
 -              return -100;
 -      nscore = compute_num_played_score(&afsi);
 -      lscore = compute_last_played_score(&afsi);
 -      score = nscore + lscore;
 -      return score;
 +      return mood_score / 3;
  }
  
  static int add_afs_statistics(const struct osl_row *row)
  {
-       uint64_t n, x, s;
+       uint64_t n, x, s, q;
        struct afs_info afsi;
        int ret;
  
        n = statistics.num;
        x = afsi.last_played;
        s = statistics.last_played_sum;
-       if (n > 0)
-               statistics.last_played_qd += (x - s / n) * (x - s / n) * n / (n + 1);
+       if (n > 0) {
+               q = (x > s / n)? x - s / n : s / n - x;
+               statistics.last_played_qd += q * q * n / (n + 1);
+       }
        statistics.last_played_sum += x;
  
        x = afsi.num_played;
        s = statistics.num_played_sum;
-       if (n > 0)
-               statistics.num_played_qd += (x - s / n) * (x - s / n) * n / (n + 1);
+       if (n > 0) {
+               q = (x > s / n)? x - s / n : s / n - x;
+               statistics.num_played_qd += q * q * n / (n + 1);
+       }
        statistics.num_played_sum += x;
        statistics.num++;
        return 1;
@@@ -521,16 -569,16 +525,16 @@@ static int del_afs_statistics(const str
  /**
   * Structure used during mood_open().
   *
 - * At mood open time, we look at each file in the audio file table in order to
 - * determine whether it is admissible. If a file happens to be admissible, its
 - * mood score is computed by calling each relevant mood_score_function. Next,
 - * we update the afs_statistics and add a struct admissible_file_info to a
 - * temporary array.
 + * At mood open time we determine the set of admissible files for the given
 + * mood. The mood score of each admissible file is computed by adding up all
 + * mood item scores. Next, we update the afs statistics and append a struct
 + * admissible_file_info to a temporary array.
   *
 - * If all files have been processed that way, the final score of each
 + * When all files have been processed in this way, the final score of each
   * admissible file is computed by adding the dynamic score (which depends on
 - * the afs_statistics) to the mood score.  Finally, all audio files in the
 - * array are added to the score table and the admissible array is freed.
 + * the afs_statistics and the current time) to the mood score. Finally, all
 + * audio files in the temporary array are added to the score table and the
 + * array is freed.
   *
   * \sa mood_method, admissible_array.
   */
@@@ -604,7 -652,8 +608,8 @@@ static int add_if_admissible(struct osl
   * the last number a_n was replaced by b) may be computed in O(1) time in terms
   * of n, q, a_n, b, and S as
   *
-  *    q' = q + d * s - (2 * S + d) * d / n,
+  *    q' = q + d * s - (2 * S + d) * d / n
+  *       = q + d * (s - 2 * S / n - d /n),
   *
   * where d = b - a_n, and s = b + a_n.
   *
@@@ -621,7 -670,7 +626,7 @@@ _static_inline_ int64_t update_quadrati
  {
        int64_t delta = new_val - old_val;
        int64_t sigma = new_val + old_val;
-       return old_qd + delta * sigma - (2 * old_sum + delta) * delta / n;
+       return old_qd + delta * (sigma - 2 * old_sum / n - delta / n);
  }
  
  static int update_afs_statistics(struct afs_info *old_afsi, struct afs_info *new_afsi)
  
  static int add_to_score_table(const struct osl_row *aft_row, long mood_score)
  {
 -      long score = (compute_dynamic_score(aft_row) + mood_score) / 3;
 +      long score;
 +      struct afs_info afsi;
 +      int ret = get_afsi_of_row(aft_row, &afsi);
 +
 +      if (ret < 0)
 +              return ret;
 +      score = compute_score(&afsi, mood_score);
        return score_add(aft_row, score);
  }
  
@@@ -734,7 -777,9 +739,7 @@@ static int mood_update_audio_file(cons
                if (ret < 0)
                        return ret;
        }
 -      score += compute_num_played_score(&afsi);
 -      score += compute_last_played_score(&afsi);
 -      score /= 3;
 +      score = compute_score(&afsi, score);
        PARA_DEBUG_LOG("score: %li\n", score);
        percent = (score + 100) / 3;
        if (percent > 100)
@@@ -825,7 -870,7 +830,7 @@@ int change_current_mood(char *mood_name
        if (ret < 0)
                return ret;
        log_statistics();
 -      PARA_INFO_LOG("%d admissible files \n", statistics.num);
 +      PARA_INFO_LOG("%d admissible files\n", statistics.num);
        for (i = 0; i < statistics.num; i++) {
                struct admissible_file_info *a = aa.array + i;
                ret = add_to_score_table(a->aft_row, a->score);
diff --combined net.c
index 463033bb710a36036ca37907ecc59016fd6dd304,708e83f2aae7df4d17a7d59abea182ec657d1911..42418e5f20d9e168eb0af5c7da9c2d1ffa1c2127
--- 1/net.c
--- 2/net.c
+++ b/net.c
@@@ -1,5 -1,5 +1,5 @@@
  /*
 - * Copyright (C) 2005-2013 Andre Noll <maan@systemlinux.org>
 + * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
   *
   * Licensed under the GPL v2. For licencing details see COPYING.
   */
   */
  #define _GNU_SOURCE
  
 +#include <netinet/in.h>
 +#include <arpa/inet.h>
 +#include <sys/un.h>
 +#include <sys/types.h>
 +#include <sys/socket.h>
  #include <netdb.h>
  
  /* At least NetBSD needs these. */
@@@ -48,7 -43,7 +48,7 @@@
   * \param netmask Value of the netmask part in \a cidr or the
   *              default of 32 if not specified.
   *
 - * \return Pointer to \a addr if succesful, NULL on error.
 + * \return Pointer to \a addr if successful, NULL on error.
   * \sa RFC 4632
   */
  char *parse_cidr(const char *cidr,
@@@ -147,9 -142,9 +147,9 @@@ static bool host_string_ok(const char *
   * \param hostlen The maximum length of \a host.
   * \param port          To return the port number (if any) of \a url.
   *
 - * \return Pointer to \a host, or NULL if failed.
 - * If NULL is returned, \a host and \a portnum are undefined. If no
 - * port number was present in \a url, \a portnum is set to -1.
 + * \return Pointer to \a host, or \p NULL if failed.  If \p NULL is returned,
 + * \a host and \a port are undefined. If no port number was present in \a url,
 + * \a port is set to -1.
   *
   * \sa RFC 3986, 3.2.2/3.2.3
   */
@@@ -173,16 -168,16 +173,16 @@@ char *parse_url(const char *url
                if (*o++ != ']' || (*o != '\0' && *o != ':'))
                        goto failed;
        } else {
 -              for (; (*c = *o == ':'? '\0' : *o); c++, o++)
 -                      if (c == end)
 +              for (; (*c = *o == ':'? '\0' : *o); c++, o++) {
 +                      if (c == end && o[1])
                                goto failed;
 +              }
        }
  
        if (*o == ':')
                if (para_atoi32(++o, port) < 0 ||
                    *port < 0 || *port > 0xffff)
                        goto failed;
 -
        if (host_string_ok(host))
                return host;
  failed:
@@@ -208,7 -203,7 +208,7 @@@ const char *stringify_port(int port, co
                struct servent *se = getservbyport(htons(port), transport);
  
                if (se == NULL)
 -                      snprintf(service, sizeof(service), "%u", port);
 +                      snprintf(service, sizeof(service), "%d", port);
                else
                        snprintf(service, sizeof(service), "%s", se->s_name);
        }
@@@ -333,14 -328,7 +333,14 @@@ static void flowopt_setopts(int sockfd
                }
  }
  
 -static void flowopt_cleanup(struct flowopts *fo)
 +/**
 + * Deallocate all resources of a flowopts structure.
 + *
 + * \param fo A pointer as returned from flowopt_new().
 + *
 + * It's OK to pass \p NULL here in which case the function does nothing.
 + */
 +void flowopt_cleanup(struct flowopts *fo)
  {
        struct pre_conn_opt *cur, *next;
  
  }
  
  /**
 - * Resolve IPv4/IPv6 address and create a ready-to-use active or passive socket.
 + * Resolve an IPv4/IPv6 address.
   *
   * \param l4type The layer-4 type (\p IPPROTO_xxx).
 - * \param passive Whether this is a passive (1) or active (0) socket.
 + * \param passive Whether \p AI_PASSIVE should be included as hint.
   * \param host Remote or local hostname or IPv/6 address string.
 - * \param port_number Decimal port number.
 - * \param fo Socket options to be set before making the connection.
 + * \param port_number Used to set the port in each returned address structure.
 + * \param result addrinfo structures are returned here.
   *
 - * This creates a ready-made IPv4/v6 socket structure after looking up the
 - * necessary parameters. The interpretation of \a host depends on the value of
 - * \a passive:
 - *    - on a passive socket host is interpreted as an interface IPv4/6 address
 - *      (can be left NULL);
 - *    - on an active socket, \a host is the peer DNS name or IPv4/6 address
 - *      to connect to;
 - *    - \a port_number is in either case the numeric port number (not service
 - *      string).
 - *
 - * Furthermore, bind(2) is called on passive sockets, and connect(2) on active
 - * sockets. The algorithm tries all possible address combinations until it
 - * succeeds. If \a fo is supplied, options are set and cleanup is performed.
 - *
 - * \return This function returns 1 on success and \a -E_ADDRESS_LOOKUP when no
 - * matching connection could be set up (with details in the error log).
 - *
 - *  \sa ipv6(7), getaddrinfo(3), bind(2), connect(2).
 - */
 -int makesock(unsigned l4type, bool passive,
 -           const char *host, uint16_t port_number,
 -           struct flowopts *fo)
 -{
 -      struct addrinfo *local = NULL, *src = NULL, *remote = NULL,
 -              *dst = NULL, hints;
 -      unsigned int    l3type = AF_UNSPEC;
 -      int             rc, on = 1, sockfd = -1,
 -                      socktype = sock_type(l4type);
 + * The interpretation of \a host depends on the value of \a passive. On a
 + * passive socket host is interpreted as an interface IPv4/6 address (can be
 + * left NULL). On an active socket, \a host is the peer DNS name or IPv4/6
 + * address to connect to.
 + *
 + * \return Standard.
 + *
 + * \sa getaddrinfo(3).
 + */
 +int lookup_address(unsigned l4type, bool passive, const char *host,
 +              int port_number, struct addrinfo **result)
 +{
 +      int ret;
        char port[6]; /* port number has at most 5 digits */
 +      struct addrinfo *addr = NULL, hints;
  
 -      sprintf(port, "%u", port_number);
 +      *result = NULL;
 +      sprintf(port, "%u", port_number & 0xffff);
        /* Set up address hint structure */
        memset(&hints, 0, sizeof(hints));
 -      hints.ai_family = l3type;
 -      hints.ai_socktype = socktype;
 -      /* 
 +      hints.ai_family = AF_UNSPEC;
 +      hints.ai_socktype = sock_type(l4type);
 +      /*
         * getaddrinfo does not support SOCK_DCCP, so for the sake of lookup
         * (and only then) pretend to be UDP.
         */
        if (l4type == IPPROTO_DCCP)
                hints.ai_socktype = SOCK_DGRAM;
 -
        /* only use addresses available on the host */
        hints.ai_flags = AI_ADDRCONFIG;
 -      if (l3type == AF_INET6)
 -              /* use v4-mapped-v6 if no v6 addresses found */
 -              hints.ai_flags |= AI_V4MAPPED | AI_ALL;
 -
        if (passive && host == NULL)
                hints.ai_flags |= AI_PASSIVE;
 -
        /* Obtain local/remote address information */
 -      if ((rc = getaddrinfo(host, port, &hints, passive ? &local : &remote))) {
 -              PARA_ERROR_LOG("can not resolve %s address %s#%s: %s.\n",
 -                              layer4_name(l4type),
 -                              host? host : (passive? "[loopback]" : "[localhost]"),
 -                              port, gai_strerror(rc));
 -              rc = -E_ADDRESS_LOOKUP;
 -              goto out;
 +      ret = getaddrinfo(host, port, &hints, &addr);
 +      if (ret != 0) {
 +              PARA_ERROR_LOG("can not resolve %s address %s#%s: %s\n",
 +                      layer4_name(l4type),
 +                      host? host : (passive? "[loopback]" : "[localhost]"),
 +                      port, gai_strerror(ret));
 +              return -E_ADDRESS_LOOKUP;
        }
 +      *result = addr;
 +      return 1;
 +}
  
 -      /* Iterate over all src/dst combination, exhausting dst first */
 -      for (src = local, dst = remote; src != NULL || dst != NULL; /* no op */ ) {
 -              if (src && dst && src->ai_family == AF_INET
 -                              && dst->ai_family == AF_INET6)
 -                      goto get_next_dst; /* v4 -> v6 is not possible */
 -
 -              sockfd = socket(src ? src->ai_family : dst->ai_family,
 -                      socktype, l4type);
 -              if (sockfd < 0)
 -                      goto get_next_dst;
 +/**
 + * Create an active or passive socket.
 + *
 + * \param l4type \p IPPROTO_TCP, \p IPPROTO_UDP, or \p IPPROTO_DCCP.
 + * \param passive Whether to call bind(2) or connect(2).
 + * \param ai Address information as obtained from \ref lookup_address().
 + * \param fo Socket options to be set before making the connection.
 + *
 + * bind(2) is called on passive sockets, and connect(2) on active sockets. The
 + * algorithm tries all possible address combinations until it succeeds. If \a
 + * fo is supplied, options are set but cleanup must be performed in the caller.
 + *
 + * \return File descriptor on success, \p E_MAKESOCK on errors.
 + *
 + * \sa \ref lookup_address(), \ref makesock(), ip(7), ipv6(7), bind(2),
 + * connect(2).
 + */
 +int makesock_addrinfo(unsigned l4type, bool passive, struct addrinfo *ai,
 +              struct flowopts *fo)
 +{
 +      int ret = -E_MAKESOCK, on = 1;
  
 +      for (; ai; ai = ai->ai_next) {
 +              int fd;
 +              ret = socket(ai->ai_family, sock_type(l4type), l4type);
 +              if (ret < 0)
 +                      continue;
 +              fd = ret;
 +              flowopt_setopts(fd, fo);
 +              if (!passive) {
 +                      if (connect(fd, ai->ai_addr, ai->ai_addrlen) == 0)
 +                              return fd;
 +                      close(fd);
 +                      continue;
 +              }
                /*
                 * Reuse the address on passive sockets to avoid failure on
                 * restart (protocols using listen()) and when creating
                 * multiple listener instances (UDP multicast).
                 */
 -              if (passive && setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
 -                                                &on, sizeof(on)) == -1) {
 -                      rc = errno;
 -                      close(sockfd);
 -                      PARA_ERROR_LOG("can not set SO_REUSEADDR: %s\n",
 -                                     strerror(rc));
 -                      rc = -ERRNO_TO_PARA_ERROR(rc);
 -                      break;
 -              }
 -              flowopt_setopts(sockfd, fo);
 -
 -              if (src) {
 -                      if (bind(sockfd, src->ai_addr, src->ai_addrlen) < 0) {
 -                              close(sockfd);
 -                              goto get_next_src;
 -                      }
 -                      if (!dst) /* bind-only completed successfully */
 -                              break;
 +              if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on,
 +                              sizeof(on)) == -1) {
 +                      close(fd);
 +                      continue;
                }
 -
 -              if (dst && connect(sockfd, dst->ai_addr, dst->ai_addrlen) == 0)
 -                      break; /* connection completed successfully */
 -              close(sockfd);
 -get_next_dst:
 -              if (dst && (dst = dst->ai_next))
 +              if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0) {
 +                      close(fd);
                        continue;
 -get_next_src:
 -              if (src && (src = src->ai_next)) /* restart inner loop */
 -                      dst = remote;
 +              }
 +              return fd;
        }
 -out:
 -      if (local)
 -              freeaddrinfo(local);
 -      if (remote)
 -              freeaddrinfo(remote);
 -      flowopt_cleanup(fo);
 -
 -      if (src == NULL && dst == NULL) {
 -              if (rc >= 0)
 -                      rc = -E_MAKESOCK;
 -              PARA_ERROR_LOG("can not create %s socket %s#%s.\n",
 -                      layer4_name(l4type), host? host : (passive?
 -                      "[loopback]" : "[localhost]"), port);
 -              return rc;
 +      return -E_MAKESOCK;
 +}
 +
 +/**
 + * Resolve IPv4/IPv6 address and create a ready-to-use active or passive socket.
 + *
 + * \param l4type The layer-4 type (\p IPPROTO_xxx).
 + * \param passive Whether this is a passive or active socket.
 + * \param host Passed to \ref lookup_address().
 + * \param port_number Passed to \ref lookup_address().
 + * \param fo Passed to \ref makesock_addrinfo().
 + *
 + * This creates a ready-made IPv4/v6 socket structure after looking up the
 + * necessary parameters. The function first calls \ref lookup_address() and
 + * passes the address information to makesock_addrinfo() to create and
 + * initialize the socket.
 + *
 + * \return The newly created file descriptor on success, a negative error code
 + * on failure.
 + *
 + * \sa \ref lookup_address(), \ref makesock_addrinfo().
 + */
 +int makesock(unsigned l4type, bool passive, const char *host, uint16_t port_number,
 +              struct flowopts *fo)
 +{
 +      struct addrinfo *ai;
 +      int ret = lookup_address(l4type, passive, host, port_number, &ai);
 +
 +      if (ret >= 0)
 +              ret = makesock_addrinfo(l4type, passive, ai, fo);
 +      if (ai)
 +              freeaddrinfo(ai);
 +      if (ret < 0) {
 +              PARA_ERROR_LOG("can not create %s socket %s#%d.\n",
 +              layer4_name(l4type), host? host : (passive?
 +              "[loopback]" : "[localhost]"), port_number);
        }
 -      return sockfd;
 +      return ret;
  }
  
  /**
@@@ -601,7 -578,7 +601,7 @@@ static inline int estimated_header_over
   *
   * \param sockfd The socket file descriptor.
   *
 - * The socket must be connected. See RFC 1122, 3.3.3. If the protocol familiy
 + * The socket must be connected. See RFC 1122, 3.3.3. If the protocol family
   * could not be determined, \p AF_INET is assumed.
   *
   * \return The maximum message size of the address family type.
@@@ -622,9 -599,11 +622,9 @@@ int generic_max_transport_msg_size(int 
  }
  
  /**
 - * Look up the local or remote side of a connected socket structure.
 + * Look up the remote side of a connected socket structure.
   *
   * \param fd The socket descriptor of the connected socket.
 - * \param getname Either \p getsockname() for local, or \p getpeername() for
 - * remote side.
   *
   * \return A static character string identifying hostname and port of the
   * chosen side in numeric host:port format.
   * \sa getsockname(2), getpeername(2), parse_url(), getnameinfo(3),
   * services(5), nsswitch.conf(5).
   */
 -static char *__get_sock_name(int fd, typeof(getsockname) getname)
 +char *remote_name(int fd)
  {
        struct sockaddr_storage ss;
        const struct sockaddr *sa;
        static char output[sizeof(hbuf) + sizeof(sbuf) + 4];
        int ret;
  
 -      if (getname(fd, (struct sockaddr *)&ss, &sslen) < 0) {
 +      if (getpeername(fd, (struct sockaddr *)&ss, &sslen) < 0) {
                PARA_ERROR_LOG("can not determine address from fd %d: %s\n",
                        fd, strerror(errno));
                snprintf(output, sizeof(output), "(unknown)");
  }
  
  /**
 - * Look up the local side of a connected socket structure.
 - *
 - * \param sockfd The file descriptor of the socket.
 + * Extract IPv4 or IPv6-mapped-IPv4 address from sockaddr_storage.
   *
 - * \return A pointer to a static buffer containing hostname an port. This
 - * buffer must not be freed by the caller.
 + * \param ss Container of IPv4/6 address.
 + * \param ia Extracted IPv4 address (different from 0) or 0 if unsuccessful.
   *
 - * \sa remote_name().
 + * \sa RFC 3493.
   */
 -char *local_name(int sockfd)
 +void extract_v4_addr(const struct sockaddr_storage *ss, struct in_addr *ia)
  {
 -      return __get_sock_name(sockfd, getsockname);
 -}
 +      const struct sockaddr *sa = normalize_ip_address(ss);
  
 -/**
 - * Look up the remote side of a connected socket structure.
 - *
 - * \param sockfd The file descriptor of the socket.
 - *
 - * \return Analogous to the return value of \ref local_name() but for the
 - * remote side.
 - *
 - * \sa local_name().
 - */
 -char *remote_name(int sockfd)
 -{
 -      return __get_sock_name(sockfd, getpeername);
 +      memset(ia, 0, sizeof(*ia));
 +      if (sa->sa_family == AF_INET)
 +              *ia = ((struct sockaddr_in *)sa)->sin_addr;
  }
  
  /**
 - * Extract IPv4 or IPv6-mapped-IPv4 address from sockaddr_storage.
 - * \param ss Container of IPv4/6 address
 - * \return Extracted IPv4 address (different from 0) or 0 if unsuccessful.
 + * Compare the address part of IPv4/6 addresses.
   *
 - * \sa RFC 3493
 + * \param sa1 First address.
 + * \param sa2 Second address.
 + *
 + * \return True iff the IP address of \a sa1 and \a sa2 match.
   */
 -struct in_addr extract_v4_addr(const struct sockaddr_storage *ss)
 +bool sockaddr_equal(const struct sockaddr *sa1, const struct sockaddr *sa2)
  {
 -      struct in_addr ia = {.s_addr = 0};
 -      const struct sockaddr *sa = normalize_ip_address(ss);
 -
 -      if (sa->sa_family == AF_INET)
 -              ia = ((struct sockaddr_in *)sa)->sin_addr;
 -      return ia;
 +      if (!sa1 || !sa2)
 +              return false;
 +      if (sa1->sa_family != sa2->sa_family)
 +              return false;
 +      if (sa1->sa_family == AF_INET) {
 +              struct sockaddr_in *a1 = (typeof(a1))sa1,
 +                      *a2 = (typeof (a2))sa2;
 +              return a1->sin_addr.s_addr == a2->sin_addr.s_addr;
 +      } else if (sa1->sa_family == AF_INET6) {
 +              struct sockaddr_in6 *a1 = (typeof(a1))sa1,
 +                      *a2 = (typeof (a2))sa2;
 +              return !memcmp(a1, a2, sizeof(*a1));
 +      } else
 +              return false;
  }
  
  /**
@@@ -834,66 -817,52 +834,66 @@@ int dccp_available_ccids(uint8_t **ccid
   * \return Positive on success, \p -E_NAME_TOO_LONG if \a name is longer
   * than \p UNIX_PATH_MAX.
   */
 -static int init_unix_addr(struct sockaddr_un *u, const char *name)
 +static int init_unix_addr(struct sockaddr_un *u, const char *name,
 +              bool abstract)
  {
 -      if (strlen(name) >= UNIX_PATH_MAX)
 +      if (strlen(name) + abstract >= UNIX_PATH_MAX)
                return -E_NAME_TOO_LONG;
        memset(u->sun_path, 0, UNIX_PATH_MAX);
        u->sun_family = PF_UNIX;
 -      strcpy(u->sun_path, name);
 +      strcpy(u->sun_path + abstract, name);
        return 1;
  }
  
  /**
 - * Prepare, create, and bind a socket for local communication.
 + * Create a socket for local communication and listen on it.
   *
   * \param name The socket pathname.
 - * \param unix_addr Pointer to the \p AF_UNIX socket structure.
 - * \param mode The desired mode of the socket.
 + * \param mode The desired permissions of the socket.
 + *
 + * This function creates a passive local socket for sequenced, reliable,
 + * two-way, connection-based byte streams. The socket file descriptor is set to
 + * nonblocking mode and listen(2) is called to prepare the socket for
 + * accepting incoming connection requests.
   *
 - * This function creates a local socket for sequenced, reliable,
 - * two-way, connection-based byte streams.
 + * If mode is zero, an abstract socket (a non-portable Linux extension) is
 + * created. In this case the socket name has no connection with filesystem
 + * pathnames.
   *
 - * \return The file descriptor, on success, negative on errors.
 + * \return The file descriptor on success, negative error code on failure.
   *
 - * \sa socket(2)
 - * \sa bind(2)
 - * \sa chmod(2)
 + * \sa socket(2), \sa bind(2), \sa chmod(2), listen(2), unix(7).
   */
 -int create_local_socket(const char *name, struct sockaddr_un *unix_addr,
 -              mode_t mode)
 +int create_local_socket(const char *name, mode_t mode)
  {
 +      struct sockaddr_un unix_addr;
        int fd, ret;
 +      bool abstract = mode == 0;
  
 -      ret = init_unix_addr(unix_addr, name);
 +      ret = init_unix_addr(&unix_addr, name, abstract);
        if (ret < 0)
                return ret;
        ret = socket(PF_UNIX, SOCK_STREAM, 0);
        if (ret < 0)
                return -ERRNO_TO_PARA_ERROR(errno);
        fd = ret;
 -      ret = bind(fd, (struct sockaddr *) unix_addr, UNIX_PATH_MAX);
 +      ret = mark_fd_nonblocking(fd);
 +      if (ret < 0)
 +              goto err;
 +      ret = bind(fd, (struct sockaddr *)&unix_addr, sizeof(unix_addr));
        if (ret < 0) {
                ret = -ERRNO_TO_PARA_ERROR(errno);
                goto err;
        }
 -      ret = -E_CHMOD;
 -      if (chmod(name, mode) < 0)
 +      if (!abstract) {
 +              ret = -E_CHMOD;
 +              if (chmod(name, mode) < 0)
 +                      goto err;
 +      }
 +      if (listen(fd , 5) < 0) {
 +              ret = -ERRNO_TO_PARA_ERROR(errno);
                goto err;
 +      }
        return fd;
  err:
        close(fd);
@@@ -919,22 -888,17 +919,22 @@@ int connect_local_socket(const char *na
        int fd, ret;
  
        PARA_DEBUG_LOG("connecting to %s\n", name);
 -      ret = init_unix_addr(&unix_addr, name);
 -      if (ret < 0)
 -              return ret;
        fd = socket(PF_UNIX, SOCK_STREAM, 0);
        if (fd < 0)
                return -ERRNO_TO_PARA_ERROR(errno);
 -      if (connect(fd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) == -1) {
 -              ret = -ERRNO_TO_PARA_ERROR(errno);
 +      /* first try (linux-only) abstract socket */
 +      ret = init_unix_addr(&unix_addr, name, true);
 +      if (ret < 0)
                goto err;
 -      }
 -      return fd;
 +      if (connect(fd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) != -1)
 +              return fd;
 +      /* next try pathname socket */
 +      ret = init_unix_addr(&unix_addr, name, false);
 +      if (ret < 0)
 +              goto err;
 +      if (connect(fd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) != -1)
 +              return fd;
 +      ret = -ERRNO_TO_PARA_ERROR(errno);
  err:
        close(fd);
        return ret;
@@@ -950,18 -914,16 +950,18 @@@ int recv_cred_buffer(int fd, char *buf
        return recv_buffer(fd, buf, size) > 0? 1 : -E_RECVMSG;
  }
  #else /* HAVE_UCRED */
 +
  /**
 - * Send \p NULL-terminated buffer and Unix credentials of the current process.
 + * Send a buffer and the credentials of the current process to a socket.
   *
 - * \param sock The socket file descriptor.
 - * \param buf The buffer to be sent.
 + * \param sock The file descriptor of the sending socket.
 + * \param buf The zero-terminated buffer to send.
   *
 - * \return On success, this call returns the number of characters sent.  On
 - * error, \p -E_SENDMSG is returned.
 + * \return On success, this call returns the number of bytes sent. On errors,
 + * \p -E_SENDMSG is returned.
   *
 - * \sa sendmsg(2), okir's Black Hats Manual.
 + * \sa \ref recv_cred_buffer, sendmsg(2), socket(7), unix(7), okir's Black Hats
 + * Manual.
   */
  ssize_t send_cred_buffer(int sock, char *buf)
  {
  
        /* Response data */
        iov.iov_base = buf;
 -      iov.iov_len  = strlen(buf);
 +      iov.iov_len = strlen(buf);
        c.pid = getpid();
        c.uid = getuid();
        c.gid = getgid();
        *(struct ucred *)CMSG_DATA(cmsg) = c;
        msg.msg_controllen = cmsg->cmsg_len;
        ret = sendmsg(sock, &msg, 0);
 -      if (ret  < 0)
 +      if (ret < 0)
                ret = -E_SENDMSG;
        return ret;
  }
@@@ -1008,17 -970,17 +1008,17 @@@ static void dispose_fds(int *fds, unsig
  /**
   * Receive a buffer and the Unix credentials of the sending process.
   *
 - * \param fd the socket file descriptor.
 - * \param buf the buffer to store the message.
 - * \param size the size of \a buffer.
 + * \param fd The file descriptor of the receiving socket.
 + * \param buf The buffer to store the received message.
 + * \param size The length of \a buf in bytes.
   *
 - * \return negative on errors, the user id on success.
 + * \return Negative on errors, the user id of the sending process on success.
   *
 - * \sa recvmsg(2), okir's Black Hats Manual.
 + * \sa \ref send_cred_buffer and the references given there.
   */
  int recv_cred_buffer(int fd, char *buf, size_t size)
  {
-       char control[255];
+       char control[255] __a_aligned(8);
        struct msghdr msg;
        struct cmsghdr *cmsg;
        struct iovec iov;
                } else
                        if (cmsg->cmsg_level == SOL_SOCKET
                                        && cmsg->cmsg_type == SCM_RIGHTS) {
 -                              dispose_fds((int *) CMSG_DATA(cmsg),
 +                              dispose_fds((int *)CMSG_DATA(cmsg),
                                        (cmsg->cmsg_len - CMSG_LEN(0))
                                        / sizeof(int));
                        }
diff --combined playlist.c
index 9616ed0f0dd6dc9a215e4782375cd6fa081f187d,6441aca7a8293066e70132f57231a4c9b8643bf7..01392030a00305cea41707f4e40016507f0e20e1
@@@ -1,5 -1,5 +1,5 @@@
  /*
 - * Copyright (C) 2007-2013 Andre Noll <maan@systemlinux.org>
 + * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
   *
   * Licensed under the GPL v2. For licencing details see COPYING.
   */
@@@ -95,9 -95,9 +95,9 @@@ static int check_playlist_path(char *pa
        struct osl_row *aft_row;
        int ret = aft_get_row_of_path(path, &aft_row);
  
 -      if (ret >= 0)
 -              return 1;
 -      return para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
 +      if (ret < 0)
 +              para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
 +      return 1; /* do not fail the loop on bad paths */
  }
  
  static int check_playlist(struct osl_row *row, void *data)
        char *playlist_name;
        int ret = pl_get_name_and_def_by_row(row, &playlist_name, &playlist_def);
  
 -      if (ret < 0)
 -              return para_printf(pb, "failed to get playlist data: %s\n",
 +      if (ret < 0) { /* log error, but continue */
 +              para_printf(pb, "failed to get playlist data: %s\n",
                        para_strerror(-ret));
 +              return 1;
 +      }
        if (*playlist_name) { /* skip dummy row */
 -              ret = para_printf(pb, "checking playlist %s...\n", playlist_name);
 -              if (ret < 0)
 -                      return ret;
 -              ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
 +              para_printf(pb, "checking playlist %s...\n", playlist_name);
 +              for_each_line(FELF_READ_ONLY, playlist_def.data,
                        playlist_def.size, check_playlist_path, pb);
        }
        osl_close_disk_object(&playlist_def);
 -      return ret;
 +      return 1;
  }
  
  /**
   * Check the playlist table for inconsistencies.
   *
 - * \param fd The afs socket.
 - * \param query Unused.
 + * \param aca This callback ignores ->query.
   *
 - * \return The return value of the underlying call to osl_rbtree_loop().
 + * \return Standard. Invalid paths are reported, but are not considered an
 + * error.
   */
 -void playlist_check_callback(int fd, __a_unused const struct osl_object *query)
 +int playlist_check_callback(struct afs_callback_arg *aca)
  {
 -      struct para_buffer pb = {
 -              .max_size = shm_get_shmmax(),
 -              .private_data = &(struct afs_max_size_handler_data) {
 -                      .fd = fd,
 -                      .band = SBD_OUTPUT
 -              },
 -              .max_size_handler = afs_max_size_handler,
 -      };
 -      int ret = para_printf(&pb, "checking playlists...\n");
 -
 -      if (ret < 0)
 -              return;
 -      osl_rbtree_loop(playlists_table, BLOBCOL_ID, &pb,
 -              check_playlist);
 -      if (pb.offset)
 -              pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
 -      free(pb.buf);
 +      para_printf(&aca->pbout, "checking playlists...\n");
 +      return osl(osl_rbtree_loop(playlists_table, BLOBCOL_ID, &aca->pbout,
 +              check_playlist));
  }
  
  /**
@@@ -191,8 -205,6 +191,6 @@@ static int handle_audio_file_event(enu
        char *new_path;
        const struct osl_row *row = data;
  
-       if (!current_playlist.name)
-               return 1;
        if (event == AUDIO_FILE_RENAME) {
                ret = row_belongs_to_score_table(row, NULL);
                if (ret < 0)
@@@ -237,7 -249,9 +235,9 @@@ int playlists_event_handler(enum afs_ev
        int ret;
        struct afsi_change_event_data *aced = data;
  
-       switch(event) {
+       if (!current_playlist.name)
+               return 1;
+       switch (event) {
        case AFSI_CHANGE:
                return playlist_update_audio_file(aced->aft_row);
        case AUDIO_FILE_RENAME:
diff --combined vss.c
index cbc05d131b28550d8596cee1bb505a835ef81779,4c9f362344ce5bb8c6ae1e6b826fd78aaa140684..51db7686c6b008c2ac8704967d9cdfd7ea76e071
--- 1/vss.c
--- 2/vss.c
+++ b/vss.c
@@@ -1,5 -1,5 +1,5 @@@
  /*
 - * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
 + * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
   *
   * Licensed under the GPL v2. For licencing details see COPYING.
   */
   * senders.
   */
  
 +#include <sys/socket.h>
 +#include <netinet/in.h>
  #include <regex.h>
  #include <osl.h>
 +#include <sys/types.h>
 +#include <arpa/inet.h>
 +#include <sys/un.h>
 +#include <netdb.h>
  
  #include "para.h"
  #include "error.h"
@@@ -89,7 -83,7 +89,7 @@@ struct vss_task 
        /** The memory mapped audio file. */
        char *map;
        /** Used by the scheduler. */
 -      struct task task;
 +      struct task *task;
        /** Pointer to the header of the mapped audio file. */
        char *header_buf;
        /** Length of the audio file header. */
@@@ -855,6 -849,7 +855,6 @@@ static void vss_eof(struct vss_task *vs
        mmd->afd.afhi.chunk_tv.tv_usec = 0;
        free(mmd->afd.afhi.chunk_table);
        mmd->afd.afhi.chunk_table = NULL;
 -      mmd->mtime = 0;
        mmd->size = 0;
        mmd->events++;
  }
@@@ -884,11 -879,49 +884,11 @@@ static void set_mmd_offset(void
        mmd->offset = tv2ms(&offset);
  }
  
 -/**
 - * Compute the timeout for the main select-loop of the scheduler.
 - *
 - * \param s Pointer to the server scheduler.
 - * \param t Pointer to the vss task structure.
 - *
 - * Before the timeout is computed, the current vss status flags are evaluated
 - * and acted upon by calling appropriate functions from the lower layers.
 - * Possible actions include
 - *
 - *    - request a new audio file from afs,
 - *    - shutdown of all senders (stop/pause command),
 - *    - reposition the stream (ff/jmp command).
 - */
 -static void vss_pre_select(struct sched *s, struct task *t)
 +static void vss_pre_select(struct sched *s, void *context)
  {
        int i;
 -      struct vss_task *vsst = container_of(t, struct vss_task, task);
 +      struct vss_task *vsst = context;
  
 -      if (!vsst->map || vss_next() || vss_paused() || vss_repos()) {
 -              struct fec_client *fc, *tmp;
 -              for (i = 0; senders[i].name; i++)
 -                      if (senders[i].shutdown_clients)
 -                              senders[i].shutdown_clients();
 -              list_for_each_entry_safe(fc, tmp, &fec_client_list, node)
 -                      fc->state = FEC_STATE_NONE;
 -              mmd->stream_start.tv_sec = 0;
 -              mmd->stream_start.tv_usec = 0;
 -      }
 -      if (vss_next())
 -              vss_eof(vsst);
 -      else if (vss_paused()) {
 -              if (mmd->chunks_sent)
 -                      set_eof_barrier(vsst);
 -              mmd->chunks_sent = 0;
 -      } else if (vss_repos()) {
 -              tv_add(now, &vsst->announce_tv, &vsst->data_send_barrier);
 -              set_eof_barrier(vsst);
 -              mmd->chunks_sent = 0;
 -              mmd->current_chunk = mmd->repos_request;
 -              mmd->new_vss_status_flags &= ~VSS_REPOS;
 -              set_mmd_offset();
 -      }
        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);
  
  static int recv_afs_msg(int afs_socket, int *fd, uint32_t *code, uint32_t *data)
  {
-       char control[255], buf[8];
+       char control[255] __a_aligned(8), buf[8];
        struct msghdr msg = {.msg_iov = NULL};
        struct cmsghdr *cmsg;
        struct iovec iov;
@@@ -974,6 -1007,7 +974,6 @@@ static void recv_afs_result(struct vss_
                goto err;
        }
        mmd->size = statbuf.st_size;
 -      mmd->mtime = statbuf.st_mtime;
        ret = para_mmap(mmd->size, PROT_READ, MAP_PRIVATE | MAP_POPULATE,
                passed_fd, 0, &vsst->map);
        if (ret < 0)
@@@ -1086,38 -1120,11 +1086,38 @@@ static void vss_send(struct vss_task *v
        }
  }
  
 -static int vss_post_select(struct sched *s, struct task *t)
 +static int vss_post_select(struct sched *s, void *context)
  {
        int ret, i;
 -      struct vss_task *vsst = container_of(t, struct vss_task, task);
 +      struct vss_task *vsst = context;
  
 +      if (!vsst->map || vss_next() || vss_paused() || vss_repos()) {
 +              /* shut down senders and fec clients */
 +              struct fec_client *fc, *tmp;
 +              for (i = 0; senders[i].name; i++)
 +                      if (senders[i].shutdown_clients)
 +                              senders[i].shutdown_clients();
 +              list_for_each_entry_safe(fc, tmp, &fec_client_list, node)
 +                      fc->state = FEC_STATE_NONE;
 +              mmd->stream_start.tv_sec = 0;
 +              mmd->stream_start.tv_usec = 0;
 +      }
 +      if (vss_next())
 +              vss_eof(vsst);
 +      else if (vss_paused()) {
 +              if (mmd->chunks_sent)
 +                      set_eof_barrier(vsst);
 +              mmd->chunks_sent = 0;
 +      } else if (vss_repos()) { /* repositioning due to ff/jmp command */
 +              tv_add(now, &vsst->announce_tv, &vsst->data_send_barrier);
 +              set_eof_barrier(vsst);
 +              mmd->chunks_sent = 0;
 +              mmd->current_chunk = afh_get_start_chunk(mmd->repos_request,
 +                      &mmd->afd.afhi);
 +              mmd->new_vss_status_flags &= ~VSS_REPOS;
 +              set_mmd_offset();
 +      }
 +      /* If a sender command is pending, run it. */
        if (mmd->sender_cmd_data.cmd_num >= 0) {
                int num = mmd->sender_cmd_data.cmd_num,
                        sender_num = mmd->sender_cmd_data.sender_num;
@@@ -1172,6 -1179,8 +1172,6 @@@ void init_vss_task(int afs_socket, stru
                        conf.autoplay_delay_arg : 0;
        vsst->header_interval.tv_sec = 5; /* should this be configurable? */
        vsst->afs_socket = afs_socket;
 -      vsst->task.pre_select = vss_pre_select;
 -      vsst->task.post_select = vss_post_select;
        ms2tv(announce_time, &vsst->announce_tv);
        PARA_INFO_LOG("announce timeval: %lums\n", tv2ms(&vsst->announce_tv));
        INIT_LIST_HEAD(&fec_client_list);
                tv_add(&vsst->autoplay_barrier, &vsst->announce_tv,
                        &vsst->data_send_barrier);
        }
 -      sprintf(vsst->task.status, "vss task");
 -      register_task(s, &vsst->task);
 +      vsst->task = task_register(&(struct task_info) {
 +              .name = "vss task",
 +              .pre_select = vss_pre_select,
 +              .post_select = vss_post_select,
 +              .context = vsst,
 +      }, s);
  }