]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 't/remove_sb_compat'
authorAndre Noll <maan@systemlinux.org>
Sat, 17 Aug 2013 11:57:32 +0000 (13:57 +0200)
committerAndre Noll <maan@systemlinux.org>
Sat, 17 Aug 2013 12:06:35 +0000 (14:06 +0200)
This topic branch was cooking for over 4 months and should now be
ready for prime time. The patches break compatibility with earlier
versions, so we need to bump the version number real soon now.

72ffa7 client: Only start stdin task for addblob commands.
85a6ae Remove old stream cipher API.
d1407f Allow addblob commands to create output.
243e31 client: Remove client_recv_buffer().
277ed4 client: Remove sb-compatibility code.
36ba18 Reject non-sideband connections.
6ca50b blob: Avoid zero sized memcpy().
cf308c Fix typo in documentation of stdin_command().
fdb9d2 blob: Simplify fd2buf().

1  2 
NEWS
afs.c
aft.c
audiod.c
client.c
client_common.c
command.c
error.h
gcrypt.c
para.h

diff --combined NEWS
index 4127e38bcdf0434be750aed7da7fee83acad872e,f323df1dda766dadfdc7fe559e6a39fa42824d58..a1cdf58eb86e172573f6dc446154961b41f8b0fe
--- 1/NEWS
--- 2/NEWS
+++ b/NEWS
@@@ -1,30 -1,6 +1,36 @@@
 -------------------------------------------
 -0.?.? (to be announced) "spectral gravity"
 -------------------------------------------
 +---------------------------------------------
- 0.?.? (to be announced) "invertible validity"
++0.5.0 (to be announced) "invertible validity"
 +---------------------------------------------
 +
++      - The sideband compatibility code has been removed, hence
++        sideband connections (introduced in 0.4.11) are now mandatory.
++      - Addblob commands can produce output.
++      - The stat command no longer sends garbage when para_server was
++        compiled against libgcrypt.
++
 +--------------------------------------
 +0.4.13 (2013-07-29) "spectral gravity"
 +--------------------------------------
 +
 +One more 0.4.x release before the API-breaking changes for 0.5.0 go
 +in. The main features of this release are the ogg/opus audio format,
 +and UTF-8 support, but it includes also tons of other improvements
 +and fixes all over the place.
 +
 +      - New audio format: ogg/opus.
 +      - UTF8 support for para_gui and the mp3 audio format handler.
 +      - Scheduler improvements and fixes.
 +      - The obsolete gettimeofday() function has been replaced
 +        by clock_gettime() on systems which support it.
 +      - Speed and usability improvements for para_gui.
 +      - para_client now restores the fd flags of stdin and stdout
 +        on shutdown.
 +      - Improved manual pages.
 +      - Consistent version strings for all executables.
 +      - Reduced dependencies on generated files result in fewer
 +        recompilations on changes.
 +      - Performance improvements for the compress filter.
 +      - Improved downloads web page.
  
  -----------------------------------------
  0.4.12 (2012-12-20) "volatile relativity"
diff --combined afs.c
index c87fdf78fb9bf60d413503f557cac34573b50edf,da92d6c5bd0058ca09dd95cc2887640b7285b7d5..49ce13ebe1eec3dce17727af33bb51444f3a3108
--- 1/afs.c
--- 2/afs.c
+++ b/afs.c
@@@ -546,31 -546,6 +546,28 @@@ static int activate_mood_or_playlist(ch
        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);
 +/**
 + * Result handler for sending data to the para_client process.
 + *
 + * \param result The data to be sent.
 + * \param band The band designator.
 + * \param private Pointer to the command context.
 + *
 + * \return The return value of the underlying call to \ref command.c::send_sb.
 + *
 + * \sa \ref callback_result_handler, \ref command.c::send_sb.
 + */
 +int afs_cb_result_handler(struct osl_object *result, uint8_t band,
 +              void *private)
 +{
 +      struct command_context *cc = private;
 +
 +      assert(cc);
 +      if (!result->size)
 +              return 1;
++      return send_sb(&cc->scc, result->data, result->size, band, true);
 +}
 +
  static void com_select_callback(int fd, const struct osl_object *query)
  {
        struct para_buffer pb = {
@@@ -613,6 -588,28 +610,6 @@@ out
        free(pb.buf);
  }
  
 -/**
 - * Result handler for sending data to the para_client process.
 - *
 - * \param result The data to be sent.
 - * \param band The band designator.
 - * \param private Pointer to the command context.
 - *
 - * \return The return value of the underlying call to \ref command.c::send_sb.
 - *
 - * \sa \ref callback_result_handler, \ref command.c::send_sb.
 - */
 -int afs_cb_result_handler(struct osl_object *result, uint8_t band,
 -              void *private)
 -{
 -      struct command_context *cc = private;
 -
 -      assert(cc);
 -      if (!result->size)
 -              return 1;
 -      return send_sb(&cc->scc, result->data, result->size, band, true);
 -}
 -
  int com_select(struct command_context *cc)
  {
        struct osl_object query;
@@@ -723,9 -720,9 +720,9 @@@ static void signal_pre_select(struct sc
        para_fd_set(st->fd, &s->rfds, &s->max_fileno);
  }
  
 -static void afs_signal_post_select(struct sched *s, struct task *t)
 +static int afs_signal_post_select(struct sched *s, __a_unused struct task *t)
  {
 -      int signum;
 +      int signum, ret;
  
        if (getppid() == 1) {
                PARA_EMERG_LOG("para_server died\n");
        }
        signum = para_next_signal(&s->rfds);
        if (signum == 0)
 -              return;
 +              return 0;
        if (signum == SIGHUP) {
                close_afs_tables();
                parse_config_or_die(1);
 -              t->error = open_afs_tables();
 -              if (t->error < 0)
 -                      return;
 +              ret = open_afs_tables();
 +              if (ret < 0)
 +                      return ret;
                init_admissible_files(current_mop);
 -              return;
 +              return 0;
        }
        PARA_EMERG_LOG("terminating on signal %d\n", signum);
  shutdown:
        task_notify_all(s, E_AFS_SIGNAL);
 -      t->error = -E_AFS_SIGNAL;
 +      return -E_AFS_SIGNAL;
  }
  
  static void register_signal_task(struct sched *s)
@@@ -915,7 -912,7 +912,7 @@@ err
  /** Shutdown connection if query has not arrived until this many seconds. */
  #define AFS_CLIENT_TIMEOUT 3
  
 -static void command_post_select(struct sched *s, struct task *t)
 +static int command_post_select(struct sched *s, struct task *t)
  {
        struct command_task *ct = container_of(t, struct command_task, task);
        struct sockaddr_un unix_addr;
        int fd, ret;
  
        ret = task_get_notification(t);
 -      if (ret < 0) {
 -              t->error = ret;
 -              return;
 -      }
 +      if (ret < 0)
 +              return ret;
        ret = execute_server_command(&s->rfds);
        if (ret < 0) {
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
                task_notify_all(s, -ret);
 -              t->error = ret;
 -              return;
 +              return ret;
        }
        /* Check the list of connected clients. */
        list_for_each_entry_safe(client, tmp, &afs_client_list, node) {
        if (ret < 0)
                PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
        if (ret <= 0)
 -              return;
 +              return 0;
        ret = mark_fd_nonblocking(fd);
        if (ret < 0) {
                PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
                close(fd);
 -              return;
 +              return 0;
        }
        client = para_malloc(sizeof(*client));
        client->fd = fd;
        client->connect_time = *now;
        para_list_add(&client->node, &afs_client_list);
 +      return 0;
  }
  
  static void register_command_task(uint32_t cookie, struct sched *s)
@@@ -1068,9 -1067,6 +1065,6 @@@ int com_init(struct command_context *cc
        }
        ret = send_callback_request(create_tables_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;
  }
  
diff --combined aft.c
index a2883d4af1438ea9ab30e93a94ebc1a3ba5b1419,0cc2bd070e42e2ac2ed25f89504b6434cc54bea2..adc9a6f2ad92b212790feb59cc7e66aa12506307
--- 1/aft.c
--- 2/aft.c
+++ b/aft.c
@@@ -280,7 -280,7 +280,7 @@@ static struct osl_table_description aud
        .column_descriptions = aft_cols
  };
  
 -/* We don't want dot or dot-dot anywhere. */
 +/* We don't want dot or dot-dot anywhere. */
  static int verify_dotfile(const char *rest)
  {
        /*
@@@ -1694,7 -1694,7 +1694,7 @@@ static void com_add_callback(int fd, co
        uint16_t afhi_offset, chunks_offset;
  
        hash = (unsigned char *)buf + CAB_HASH_OFFSET;
 -      hash_to_asc(hash, asc);;
 +      hash_to_asc(hash, asc);
        objs[AFTCOL_HASH].data = buf + CAB_HASH_OFFSET;
        objs[AFTCOL_HASH].size = HASH_SIZE;
  
@@@ -1876,12 -1876,8 +1876,8 @@@ static int add_one_audio_file(const cha
        ret = 1;
        if (pb && (pad->flags & ADD_FLAG_LAZY)) { /* lazy is really cheap */
                if (pad->flags & ADD_FLAG_VERBOSE)
-                       send_ret = pad->cc->use_sideband?
-                               send_sb_va(&pad->cc->scc, SBD_OUTPUT,
-                                       "lazy-ignore: %s\n", path)
-                       :
-                               sc_send_va_buffer(&pad->cc->scc,
-                                       "lazy-ignore: %s\n", path);
+                       send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
+                               "lazy-ignore: %s\n", path);
                goto out_free;
        }
        /* We still want to add this file. Compute its hash. */
        ret = 1;
        if (pb && hs && hs == pb && !(pad->flags & ADD_FLAG_FORCE)) {
                if (pad->flags & ADD_FLAG_VERBOSE)
-                       send_ret = pad->cc->use_sideband?
-                               send_sb_va(&pad->cc->scc, SBD_OUTPUT,
-                                       "%s exists, not forcing update\n", path)
-                       :
-                               sc_send_va_buffer(&pad->cc->scc,
-                                       "%s exists, not forcing update\n", path);
+                       send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
+                               "%s exists, not forcing update\n", path);
                goto out_unmap;
        }
        /*
        munmap(map.data, map.size);
        close(fd);
        if (pad->flags & ADD_FLAG_VERBOSE) {
-               send_ret = pad->cc->use_sideband?
-                       send_sb_va(&pad->cc->scc, SBD_OUTPUT,
-                               "adding %s\n", path)
-               :
-                       sc_send_va_buffer(&pad->cc->scc,
-                               "adding %s\n", path);
+               send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
+                       "adding %s\n", path);
                if (send_ret < 0)
                        goto out_free;
        }
@@@ -1943,14 -1931,8 +1931,8 @@@ out_unmap
        munmap(map.data, map.size);
  out_free:
        if (ret < 0 && send_ret >= 0)
-               send_ret = pad->cc->use_sideband?
-                       send_sb_va(&pad->cc->scc, SBD_ERROR_LOG,
-                               "failed to add %s (%s)\n", path,
-                               para_strerror(-ret))
-               :
-                       sc_send_va_buffer(&pad->cc->scc,
-                               "failed to add %s (%s)\n", path,
-                               para_strerror(-ret));
+               send_ret = send_sb_va(&pad->cc->scc, SBD_ERROR_LOG,
+                       "failed to add %s (%s)\n", path, para_strerror(-ret));
        free(obj.data);
        clear_afhi(afhi_ptr);
        /* Stop adding files only on send errors. */
@@@ -1994,11 -1976,7 +1976,7 @@@ int com_add(struct command_context *cc
                char *path;
                ret = verify_path(cc->argv[i], &path);
                if (ret < 0) {
-                       ret = cc->use_sideband?
-                               send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s: %s\n",
-                               cc->argv[i], para_strerror(-ret))
-                       :
-                               sc_send_va_buffer(&cc->scc, "%s: %s\n",
+                       ret = send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s: %s\n",
                                cc->argv[i], para_strerror(-ret));
                        if (ret < 0)
                                return ret;
                }
                ret = stat(path, &statbuf);
                if (ret < 0) {
-                       ret = cc->use_sideband?
-                               send_sb_va(&cc->scc, SBD_ERROR_LOG,
-                                       "failed to stat %s (%s)\n", path,
-                                       strerror(errno))
-                               :
-                               sc_send_va_buffer(&cc->scc,
-                                       "failed to stat %s (%s)\n", path,
-                                       strerror(errno));
+                       ret = send_sb_va(&cc->scc, SBD_ERROR_LOG,
+                               "failed to stat %s (%s)\n", path,
+                               strerror(errno));
                        free(path);
                        if (ret < 0)
                                return ret;
                else
                        ret = add_one_audio_file(path, &pad);
                if (ret < 0) {
-                       if (cc->use_sideband)
-                               send_sb_va(&cc->scc, SBD_OUTPUT, "%s: %s\n", path,
-                                       para_strerror(-ret));
-                       else
-                               sc_send_va_buffer(&cc->scc, "%s: %s\n", path,
-                                       para_strerror(-ret));
+                       send_sb_va(&cc->scc, SBD_OUTPUT, "%s: %s\n", path,
+                               para_strerror(-ret));
                        free(path);
                        return ret;
                }
diff --combined audiod.c
index 8f2a72ac8700ee0699c19d78724cbe1d6ec61296,b2d5c8db452167f9a265ca2c899bebbff630456e..611e72d7d851a68f15053a03f21b93e01b1f9f2d
+++ b/audiod.c
@@@ -343,11 -343,10 +343,11 @@@ static void parse_config_or_die(void
                PARA_EMERG_LOG("can not read config file %s\n", config_file);
                goto err;
        }
 -      if (ret)
 +      if (ret) {
                audiod_cmdline_parser_config_file(config_file, &conf, &params);
 +              daemon_set_loglevel(conf.loglevel_arg);
 +      }
        free(config_file);
 -      daemon_set_loglevel(conf.loglevel_arg);
        return;
  err:
        free(config_file);
@@@ -428,7 -427,7 +428,7 @@@ static void close_filters(struct slot_i
        struct audio_format_info *a = afi + s->format;
        if (a->num_filters == 0)
                return;
 -      for (i = 0; i < a->num_filters; i++) {
 +      for (i = a->num_filters - 1; i >= 0; i--) {
                struct filter_node *fn = s->fns + i;
                struct filter *f;
  
@@@ -497,6 -496,7 +497,6 @@@ static void open_filters(struct slot_in
                fn->conf = a->filter_conf[i];
                fn->task.pre_select = f->pre_select;
                fn->task.post_select = f->post_select;
 -
                fn->btrn = btr_new_node(&(struct btr_node_description)
                        EMBRACE(.name = f->name, .parent = parent,
                                .handler = f->execute, .context = fn));
@@@ -992,7 -992,7 +992,7 @@@ static void signal_pre_select(struct sc
        para_fd_set(st->fd, &s->rfds, &s->max_fileno);
  }
  
 -static void signal_post_select(struct sched *s, __a_unused struct task *t)
 +static int signal_post_select(struct sched *s, __a_unused struct task *t)
  {
        int signum;
  
                PARA_EMERG_LOG("terminating on signal %d\n", signum);
                clean_exit(EXIT_FAILURE, "caught deadly signal");
        }
 +      return 0;
  }
  
  static void signal_setup_default(struct signal_task *st)
@@@ -1020,7 -1019,7 +1020,7 @@@ static void command_pre_select(struct s
        para_fd_set(ct->fd, &s->rfds, &s->max_fileno);
  }
  
 -static void command_post_select(struct sched *s, struct task *t)
 +static int command_post_select(struct sched *s, struct task *t)
  {
        int ret;
        struct command_task *ct = container_of(t, struct command_task, task);
        if (ret < 0)
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
        audiod_status_dump();
 +      return 0;
  }
  
  static void init_command_task(struct command_task *ct)
@@@ -1091,44 -1089,32 +1091,44 @@@ static void set_stat_task_restart_barri
        tv_add(now, &delay, &stat_task->restart_barrier);
  }
  
 -static void try_to_close_slot(int slot_num)
 +static bool must_close_slot(int slot_num)
  {
        struct slot_info *s = &slot[slot_num];
        struct audio_format_info *a = afi + s->format;
        int i;
  
        if (s->format < 0)
 -              return;
 +              return false;
        if (s->receiver_node && s->receiver_node->task.error >= 0)
 -              return;
 +              return false;
        for (i = 0; i < a->num_filters; i++)
                if (s->fns && s->fns[i].task.error >= 0)
 -                      return;
 +                      return false;
        if (a->num_writers > 0) {
                for (i = 0; i < a->num_writers; i++)
                        if (s->wns && s->wns[i].task.error >= 0)
 -                              return;
 +                              return false;
        } else {
                if (s->wns && s->wns[0].task.error >= 0)
 -                      return;
 +                      return false;
 +      }
 +      return true;
 +}
 +
 +static void close_unused_slots(void)
 +{
 +      int i;
 +
 +      FOR_EACH_SLOT(i) {
 +              struct slot_info *s = slot + i;
 +              if (!must_close_slot(i))
 +                      continue;
 +              PARA_INFO_LOG("closing slot %d\n", i);
 +              close_writers(s);
 +              close_filters(s);
 +              close_receiver(i);
 +              clear_slot(i);
        }
 -      PARA_INFO_LOG("closing slot %d\n", slot_num);
 -      close_writers(s);
 -      close_filters(s);
 -      close_receiver(slot_num);
 -      clear_slot(slot_num);
  }
  
  /*
   */
  static void start_stop_decoders(void)
  {
 -      int i, ret;
 +      int ret;
        struct slot_info *sl;
        struct audio_format_info *a;
  
 -      FOR_EACH_SLOT(i)
 -              try_to_close_slot(i);
 +      close_unused_slots();
        if (audiod_status != AUDIOD_ON ||
                        !(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING))
                return notify_receivers(E_NOT_PLAYING);
  static void status_pre_select(struct sched *s, struct task *t)
  {
        struct status_task *st = container_of(t, struct status_task, task);
 -      int ret, cafn = stat_task->current_audio_format_num;
 +      int i, ret, cafn = stat_task->current_audio_format_num;
  
        if (must_start_decoder())
                goto min_delay;
 +      FOR_EACH_SLOT(i)
 +              if (must_close_slot(i))
 +                      goto min_delay;
        ret = btr_node_status(st->btrn, st->min_iqs, BTR_NT_LEAF);
        if (ret > 0)
                goto min_delay;
@@@ -1193,7 -1177,7 +1193,7 @@@ min_delay
  }
  
  /* restart the client task if necessary */
 -static void status_post_select(struct sched *s, struct task *t)
 +static int status_post_select(struct sched *s, struct task *t)
  {
        struct status_task *st = container_of(t, struct status_task, task);
  
                        close_stat_pipe();
                        goto out;
                }
-               if (st->ct->status != CL_RECEIVING)
+               if (st->ct->status != CL_EXECUTING)
                        goto out;
                ret = btr_node_status(st->btrn, st->min_iqs, BTR_NT_LEAF);
                if (ret <= 0) {
        st->last_status_read = *now;
  out:
        start_stop_decoders();
 +      return 0;
  }
  
  static void init_status_task(struct status_task *st)
@@@ -1301,17 -1284,18 +1301,17 @@@ static void set_initial_status(void
  
  __noreturn static void print_help_and_die(void)
  {
 -      int d = conf.detailed_help_given;
 -      const char **p = d? audiod_args_info_detailed_help
 -              : audiod_args_info_help;
 -
 -      printf_or_die("%s\n\n", AUDIOD_CMDLINE_PARSER_PACKAGE "-"
 -              AUDIOD_CMDLINE_PARSER_VERSION);
 -      printf_or_die("%s\n\n", audiod_args_info_usage);
 -      for (; *p; p++)
 -              printf_or_die("%s\n", *p);
 -      print_receiver_helps(d);
 -      print_filter_helps(d);
 -      print_writer_helps(d);
 +      struct ggo_help h = DEFINE_GGO_HELP(audiod);
 +      bool d = conf.detailed_help_given;
 +      unsigned flags;
 +
 +      flags = d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
 +      ggo_print_help(&h, flags);
 +
 +      flags = d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS;
 +      print_receiver_helps(flags);
 +      print_filter_helps(flags);
 +      print_writer_helps(flags);
        exit(0);
  }
  
@@@ -1350,9 -1334,9 +1350,9 @@@ int main(int argc, char *argv[]
        };
  
        valid_fd_012();
 -      if (audiod_cmdline_parser_ext(argc, argv, &conf, &params))
 -              exit(EXIT_FAILURE);
 -      HANDLE_VERSION_FLAG("audiod", conf);
 +      audiod_cmdline_parser_ext(argc, argv, &conf, &params);
 +      daemon_set_loglevel(conf.loglevel_arg);
 +      version_handle_flag("audiod", conf.version_given);
        /* init receivers/filters/writers early to make help work */
        recv_init();
        filter_init();
diff --combined client.c
index 2d6ef31f590bca1736d458cc61c4fbe67459641b,44862ab987015948ef3e9f3c2d6b9fdd4e12e008..873edc163b3e5f5164307d647724cf6655ec0d5f
+++ b/client.c
@@@ -54,7 -54,7 +54,7 @@@ static void exec_pre_select(struct sche
                sched_min_delay(s);
  }
  
 -static void exec_post_select(__a_unused struct sched *s, struct task *t)
 +static int exec_post_select(__a_unused struct sched *s, struct task *t)
  {
        struct exec_task *et = container_of(t, struct exec_task, task);
        struct btr_node *btrn = et->btrn;
        int ret;
  
        ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
 -      if (ret <= 0) {
 -              t->error = ret;
 -              return;
 -      }
 +      if (ret <= 0)
 +              return ret;
        sz = btr_next_buffer(btrn, &buf);
        if (sz <= 1)
                goto out;
@@@ -74,7 -76,6 +74,7 @@@
        et->result_buf[et->result_size - 1] = '\0';
  out:
        btr_consume(btrn, sz);
 +      return 0;
  }
  
  static int make_client_argv(const char *line)
@@@ -445,14 -446,16 +445,14 @@@ static int client_i9e_line_handler(cha
        int ret;
  
        client_disconnect(ct);
 -      if (!line || !*line)
 -              return 0;
 -      PARA_DEBUG_LOG("line handler: %s\n", line);
 +      PARA_DEBUG_LOG("line: %s\n", line);
        ret = make_client_argv(line);
 -      if (ret < 0)
 +      if (ret <= 0)
                return ret;
        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;
  }
  
@@@ -475,7 -478,7 +475,7 @@@ __noreturn static void interactive_sess
                .completers = completers,
        };
  
 -      PARA_NOTICE_LOG("\n%s\n", VERSION_TEXT("client"));
 +      PARA_NOTICE_LOG("\n%s\n", version_text("client"));
        if (ct->conf.history_file_given)
                history_file = para_strdup(ct->conf.history_file_arg);
        else {
@@@ -528,26 -531,39 +528,37 @@@ __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 void supervisor_post_select(struct sched *s, struct task *t)
++static int supervisor_post_select(struct sched *s, struct task *t)
  {
 -      if (ct->task.error < 0) {
 -              t->error = ct->task.error;
 -              return;
 -      }
+       struct supervisor_task *svt = container_of(t, struct supervisor_task,
+               task);
 -              return;
 +      if (ct->task.error < 0)
 +              return ct->task.error;
+       if (!svt->stdout_task_started && ct->status == CL_EXECUTING) {
+               stdout_set_defaults(&sot);
+               register_task(s, &sot.task);
+               svt->stdout_task_started = true;
++              return 1;
+       }
        if (ct->status == CL_SENDING) {
                stdin_set_defaults(&sit);
                register_task(s, &sit.task);
 -              t->error = -E_TASK_STARTED;
 -              return;
 +              return -E_TASK_STARTED;
        }
-       if (ct->status == CL_RECEIVING) {
-               stdout_set_defaults(&sot);
-               register_task(s, &sot.task);
-               return -E_TASK_STARTED;
-       }
 +      return 0;
  }
  
- static struct task svt = {
-       .post_select = supervisor_post_select,
-       .status = "supervisor task"
+ static struct supervisor_task supervisor_task = {
+       .task = {
+               .post_select = supervisor_post_select,
+               .status = "supervisor task"
+       }
  };
  
  /**
@@@ -595,8 -611,8 +606,8 @@@ 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]));
+       register_task(&sched, &supervisor_task.task);
        ret = schedule(&sched);
        if (ret >= 0 && ct->task.error < 0) {
                switch(ct->task.error) {
diff --combined client_common.c
index 1ecba73068f04c4dbf43480f5ccf32a932e01279,3ff43d1b7bffd2c1c2438078d431122ea9f6942d..c19b71218b59f62f1e6a75c1eee43ee332a65900
@@@ -13,6 -13,7 +13,6 @@@
  #include "error.h"
  #include "list.h"
  #include "sched.h"
 -#include "client.cmdline.h"
  #include "crypt.h"
  #include "net.h"
  #include "fd.h"
  #include "client.h"
  #include "buffer_tree.h"
  #include "version.h"
 +#include "ggo.h"
  
  /** The size of the receiving buffer. */
  #define CLIENT_BUFSIZE 4000
  
  /**
 - * Close the connection to para_server and deallocate per-command ressources.
 + * Close the connection to para_server and deallocate per-command resources.
   *
   * \param ct The client task.
   *
 - * This frees all ressources of the current command but keeps the configuration
 + * This frees all resources of the current command but keeps the configuration
   * in \p ct->conf.
   *
   * \sa \ref client_close().
@@@ -49,7 -49,8 +49,8 @@@ void client_disconnect(struct client_ta
        ct->scc.recv = NULL;
        sc_free(ct->scc.send);
        ct->scc.send = NULL;
-       btr_remove_node(&ct->btrn);
+       btr_remove_node(&ct->btrn[0]);
+       btr_remove_node(&ct->btrn[1]);
  }
  
  /**
@@@ -69,7 -70,8 +70,8 @@@ void client_close(struct client_task *c
        free(ct->key_file);
        client_cmdline_parser_free(&ct->conf);
        free(ct->challenge_hash);
-       sb_free(ct->sbc);
+       sb_free(ct->sbc[0]);
+       sb_free(ct->sbc[1]);
        free(ct);
  }
  
@@@ -91,7 -93,6 +93,6 @@@ static void client_pre_select(struct sc
  {
        int ret;
        struct client_task *ct = container_of(t, struct client_task, task);
-       struct btr_node *btrn = ct->btrn;
  
        if (ct->scc.fd < 0)
                return;
        case CL_CONNECTED:
        case CL_SENT_AUTH:
        case CL_SENT_CH_RESPONSE:
-       case CL_SENT_COMMAND:
                para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno);
                return;
  
                para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
                return;
  
-       case CL_RECEIVING:
-               ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
-               if (ret != 0) {
+       case CL_SENDING:
+               if (ct->btrn[1]) {
+                       ret = btr_node_status(ct->btrn[1], 0, BTR_NT_LEAF);
                        if (ret < 0)
                                sched_min_delay(s);
-                       else
-                               para_fd_set(ct->scc.fd, &s->rfds,
-                                       &s->max_fileno);
+                       else if (ret > 0)
+                               para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
                }
-               return;
-       case CL_SENDING:
-               ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
-               if (ret != 0) {
+               /* fall though */
+       case CL_EXECUTING:
+               if (ct->btrn[0]) {
+                       ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT);
                        if (ret < 0)
                                sched_min_delay(s);
-                       else
-                               para_fd_set(ct->scc.fd, &s->wfds,
-                                       &s->max_fileno);
+                       else if (ret > 0)
+                               para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno);
                }
                return;
        }
  }
  
- static int client_recv_buffer(struct client_task *ct, fd_set *rfds,
-               char *buf, size_t sz, size_t *n)
- {
-       int ret;
-       if (ct->status < CL_SENT_CH_RESPONSE)
-               return read_nonblock(ct->scc.fd, buf, sz, rfds, n);
-       *n = 0;
-       ret = sc_recv_buffer(&ct->scc, buf, sz);
-       /*
-        * sc_recv_buffer is used with blocking fds elsewhere, so it
-        * does not use the nonblock-API. Therefore we need to
-        * check for EOF and EAGAIN.
-        */
-       if (ret == 0)
-               return -E_SERVER_EOF;
-       if (ret == -ERRNO_TO_PARA_ERROR(EAGAIN))
-               return 0;
-       if (ret < 0)
-               return ret;
-       *n = ret;
-       return 0;
- }
- static int send_sb(struct client_task *ct, void *buf, size_t numbytes,
+ static int send_sb(struct client_task *ct, int channel, void *buf, size_t numbytes,
                enum sb_designator band, bool dont_free)
  {
        int ret, fd = ct->scc.fd;
        struct iovec iov[2];
  
-       if (!ct->sbc) {
+       if (!ct->sbc[channel]) {
                struct sb_buffer sbb;
                sb_transformation trafo = ct->status < CL_RECEIVED_PROCEED?
                        NULL : sc_trafo;
                sbb = (typeof(sbb))SBB_INIT(band, buf, numbytes);
-               ct->sbc = sb_new_send(&sbb, dont_free, trafo, ct->scc.send);
+               ct->sbc[channel] = sb_new_send(&sbb, dont_free, trafo, ct->scc.send);
        }
-       ret = sb_get_send_buffers(ct->sbc, iov);
+       ret = sb_get_send_buffers(ct->sbc[channel], iov);
        ret = xwritev(fd, iov, ret);
        if (ret < 0) {
-               sb_free(ct->sbc);
-               ct->sbc = NULL;
+               sb_free(ct->sbc[channel]);
+               ct->sbc[channel] = NULL;
                return ret;
        }
-       if (sb_sent(ct->sbc, ret)) {
-               ct->sbc = NULL;
+       if (sb_sent(ct->sbc[channel], ret)) {
+               ct->sbc[channel] = NULL;
                return 1;
        }
        return 0;
@@@ -201,21 -174,21 +174,21 @@@ static int recv_sb(struct client_task *
                trafo = sc_trafo;
                trafo_context = ct->scc.recv;
        }
-       if (!ct->sbc)
-               ct->sbc = sb_new_recv(0, trafo, trafo_context);
+       if (!ct->sbc[0])
+               ct->sbc[0] = sb_new_recv(0, trafo, trafo_context);
  again:
-       sb_get_recv_buffer(ct->sbc, &iov);
+       sb_get_recv_buffer(ct->sbc[0], &iov);
        ret = read_nonblock(ct->scc.fd, iov.iov_base, iov.iov_len, rfds, &n);
        if (ret < 0) {
-               sb_free(ct->sbc);
-               ct->sbc = NULL;
+               sb_free(ct->sbc[0]);
+               ct->sbc[0] = NULL;
                return ret;
        }
        if (n == 0)
                return 0;
-       if (!sb_received(ct->sbc, n, result))
+       if (!sb_received(ct->sbc[0], n, result))
                goto again;
-       ct->sbc = NULL;
+       ct->sbc[0] = NULL;
        return 1;
  }
  
@@@ -251,10 -224,14 +224,14 @@@ static int dispatch_sbb(struct client_t
                PARA_DEBUG_LOG("band: %s\n", designator[sbb->band]);
  
        switch (sbb->band) {
+       case SBD_AWAITING_DATA:
+               ct->status = CL_SENDING;
+               ret = 1;
+               goto out;
        case SBD_OUTPUT:
                if (iov_valid(&sbb->iov))
                        btr_add_output(sbb->iov.iov_base, sbb->iov.iov_len,
-                               ct->btrn);
+                               ct->btrn[0]);
                ret = 1;
                goto out;
        case SBD_DEBUG_LOG:
@@@ -299,8 -276,8 +276,8 @@@ static int send_sb_command(struct clien
        char *command, *p;
        size_t len = 0;
  
-       if (ct->sbc)
-               return send_sb(ct, NULL, 0, 0, false);
+       if (ct->sbc[1])
+               return send_sb(ct, 0, NULL, 0, 0, false);
  
        for (i = 0; i < ct->conf.inputs_num; i++)
                len += strlen(ct->conf.inputs[i]) + 1;
                p += strlen(ct->conf.inputs[i]) + 1;
        }
        PARA_DEBUG_LOG("--> %s\n", command);
-       return send_sb(ct, command, len, SBD_COMMAND, false);
+       return send_sb(ct, 0, command, len, SBD_COMMAND, false);
  }
  
  /**
   *
   * \sa struct sched, struct task.
   */
 -static void client_post_select(struct sched *s, struct task *t)
 +static int client_post_select(struct sched *s, struct task *t)
  {
        struct client_task *ct = container_of(t, struct client_task, task);
-       struct btr_node *btrn = ct->btrn;
        int ret = 0;
        size_t n;
        char buf[CLIENT_BUFSIZE];
        if (ret < 0)
                goto out;
        if (ct->scc.fd < 0)
 -              return;
 +              return 0;
        switch (ct->status) {
        case CL_CONNECTED: /* receive welcome message */
-               ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
+               ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &s->rfds, &n);
                if (ret < 0 || n == 0)
                        goto out;
                ct->features = parse_features(buf);
+               if (!has_feature("sideband", ct)) {
+                       PARA_ERROR_LOG("server has no sideband support\n");
+                       ret = -E_INCOMPAT_FEAT;
+                       goto out;
+               }
                ct->status = CL_RECEIVED_WELCOME;
 -              return;
 +              return 0;
        case CL_RECEIVED_WELCOME: /* send auth command */
                if (!FD_ISSET(ct->scc.fd, &s->wfds))
 -                      return;
 +                      return 0;
-               if (has_feature("sideband", ct)) {
-                       ct->use_sideband = true;
-                       sprintf(buf, AUTH_REQUEST_MSG "%s sideband", ct->user);
-               } else
-                       sprintf(buf, AUTH_REQUEST_MSG "%s", ct->user);
+               sprintf(buf, AUTH_REQUEST_MSG "%s sideband", ct->user);
                PARA_INFO_LOG("--> %s\n", buf);
                ret = write_buffer(ct->scc.fd, buf);
                if (ret < 0)
                        goto out;
                ct->status = CL_SENT_AUTH;
 -              return;
 +              return 0;
        case CL_SENT_AUTH:
                /*
                 * Receive challenge and session keys, decrypt the challenge and
                {
                /* decrypted challenge/session key buffer */
                unsigned char crypt_buf[1024];
-               /* the SHA1 of the decrypted challenge */
+               struct sb_buffer sbb;
  
-               if (ct->use_sideband) {
-                       struct sb_buffer sbb;
-                       ret = recv_sb(ct, &s->rfds, &sbb);
-                       if (ret <= 0)
-                               goto out;
-                       if (sbb.band != SBD_CHALLENGE) {
-                               ret = -E_BAD_BAND;
-                               free(sbb.iov.iov_base);
-                                       goto out;
-                       }
-                       n = sbb.iov.iov_len;
-                       PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n);
-                       ret = priv_decrypt(ct->key_file, crypt_buf,
-                               sbb.iov.iov_base, n);
+               ret = recv_sb(ct, &s->rfds, &sbb);
+               if (ret <= 0)
+                       goto out;
+               if (sbb.band != SBD_CHALLENGE) {
+                       ret = -E_BAD_BAND;
                        free(sbb.iov.iov_base);
-                       if (ret < 0)
-                               goto out;
-               } else {
-                       ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
-                       if (ret < 0 || n == 0)
-                               goto out;
-                       PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n);
-                       ret = priv_decrypt(ct->key_file, crypt_buf,
-                               (unsigned char *)buf, n);
-                       if (ret < 0)
                                goto out;
                }
+               n = sbb.iov.iov_len;
+               PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n);
+               ret = priv_decrypt(ct->key_file, crypt_buf,
+                       sbb.iov.iov_base, n);
+               free(sbb.iov.iov_base);
+               if (ret < 0)
+                       goto out;
                ct->challenge_hash = para_malloc(HASH_SIZE);
                hash_function((char *)crypt_buf, CHALLENGE_SIZE, ct->challenge_hash);
                ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
                hash_to_asc(ct->challenge_hash, buf);
                PARA_INFO_LOG("--> %s\n", buf);
                ct->status = CL_RECEIVED_CHALLENGE;
 -              return;
 +              return 0;
                }
        case CL_RECEIVED_CHALLENGE:
-               if (ct->use_sideband) {
-                       ret = send_sb(ct, ct->challenge_hash, HASH_SIZE,
-                               SBD_CHALLENGE_RESPONSE, false);
-                       if (ret != 0)
-                               ct->challenge_hash = NULL;
-                       if (ret <= 0)
-                               goto out;
-               } else {
-                       ret = write_all(ct->scc.fd, (char *)ct->challenge_hash, HASH_SIZE);
-                       if (ret < 0)
-                               goto out;
-               }
+               ret = send_sb(ct, 0, ct->challenge_hash, HASH_SIZE,
+                       SBD_CHALLENGE_RESPONSE, false);
+               if (ret != 0)
+                       ct->challenge_hash = NULL;
+               if (ret <= 0)
+                       goto out;
                ct->status = CL_SENT_CH_RESPONSE;
                goto out;
        case CL_SENT_CH_RESPONSE: /* read server response */
                {
-               if (ct->use_sideband) {
-                       struct sb_buffer sbb;
-                       ret = recv_sb(ct, &s->rfds, &sbb);
-                       if (ret <= 0)
-                               goto out;
-                       free(sbb.iov.iov_base);
-                       if (sbb.band != SBD_PROCEED)
-                               ret = -E_BAD_BAND;
-                       else
-                               ct->status = CL_RECEIVED_PROCEED;
-                       goto out;
-               }
-               ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
-               if (ret < 0 || n == 0)
-                       goto out;
-               /* check if server has sent "Proceed" message */
-               ret = -E_CLIENT_AUTH;
-               if (n < PROCEED_MSG_LEN)
-                       goto out;
-               if (!strstr(buf, PROCEED_MSG))
+               struct sb_buffer sbb;
+               ret = recv_sb(ct, &s->rfds, &sbb);
+               if (ret <= 0)
                        goto out;
-               ct->status = CL_RECEIVED_PROCEED;
-               return 0;
+               free(sbb.iov.iov_base);
+               if (sbb.band != SBD_PROCEED)
+                       ret = -E_BAD_BAND;
+               else
+                       ct->status = CL_RECEIVED_PROCEED;
+               goto out;
                }
        case CL_RECEIVED_PROCEED: /* concat args and send command */
                {
-               int i;
-               char *command = NULL;
                if (!FD_ISSET(ct->scc.fd, &s->wfds))
 -                      return;
 +                      return 0;
-               if (ct->use_sideband) {
-                       ret = send_sb_command(ct);
-                       if (ret <= 0)
-                               goto out;
-                       ct->status = CL_SENT_COMMAND;
-                       return 0;
-               }
-               for (i = 0; i < ct->conf.inputs_num; i++) {
-                       char *tmp = command;
-                       command = make_message("%s\n%s", command?
-                               command : "", ct->conf.inputs[i]);
-                       free(tmp);
-               }
-               command = para_strcat(command, EOC_MSG "\n");
-               PARA_DEBUG_LOG("--> %s\n", command);
-               ret = sc_send_buffer(&ct->scc, command);
-               free(command);
-               if (ret < 0)
+               ret = send_sb_command(ct);
+               if (ret <= 0)
                        goto out;
-               ct->status = CL_SENT_COMMAND;
+               ct->status = CL_EXECUTING;
 -              return;
 +              return 0;
                }
-       case CL_SENT_COMMAND:
-               {
-               char *buf2;
-               if (ct->use_sideband) {
-                       struct sb_buffer sbb;
-                       ret = recv_sb(ct, &s->rfds, &sbb);
-                       if (ret <= 0)
-                               goto out;
-                       if (sbb.band == SBD_AWAITING_DATA) {
-                               ct->status = CL_SENDING;
-                               free(sbb.iov.iov_base);
-                               goto out;
+       case CL_SENDING:
+               if (ct->btrn[1]) {
+                       char *buf2;
+                       size_t sz;
+                       ret = btr_node_status(ct->btrn[1], 0, BTR_NT_LEAF);
+                       if (ret == -E_BTR_EOF) {
+                               /* empty blob data packet indicates EOF */
+                               PARA_INFO_LOG("blob sent\n");
+                               ret = send_sb(ct, 1, NULL, 0, SBD_BLOB_DATA, true);
+                               if (ret >= 0)
+                                       ret = -E_BTR_EOF;
                        }
-                       ct->status = CL_RECEIVING;
-                       ret = dispatch_sbb(ct, &sbb);
-                       goto out;
-               }
-               /* can not use "buf" here because we need a malloced buffer */
-               buf2 = para_malloc(CLIENT_BUFSIZE);
-               ret = client_recv_buffer(ct, &s->rfds, buf2, CLIENT_BUFSIZE, &n);
-               if (n > 0) {
-                       if (strstr(buf2, AWAITING_DATA_MSG)) {
-                               free(buf2);
-                               ct->status = CL_SENDING;
-                               return 0;
+                       if (ret < 0)
+                               goto close1;
+                       if (ret > 0 && FD_ISSET(ct->scc.fd, &s->wfds)) {
+                               sz = btr_next_buffer(ct->btrn[1], &buf2);
+                               assert(sz);
+                               ret = send_sb(ct, 1, buf2, sz, SBD_BLOB_DATA, true);
+                               if (ret < 0)
+                                       goto close1;
+                               if (ret > 0)
+                                       btr_consume(ct->btrn[1], sz);
                        }
-                       ct->status = CL_RECEIVING;
-                       btr_add_output(buf2, n, btrn);
-               } else
-                       free(buf2);
-               goto out;
                }
-       case CL_SENDING:
-               {
-               char *buf2;
-               size_t sz;
-               ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
-               if (ret < 0)
-                       goto out;
-               if (ret == 0)
-                       return 0;
-               if (!FD_ISSET(ct->scc.fd, &s->wfds))
-                       return 0;
-               sz = btr_next_buffer(btrn, &buf2);
-               ret = sc_send_bin_buffer(&ct->scc, buf2, sz);
-               if (ret < 0)
-                       goto out;
-               btr_consume(btrn, sz);
-               return 0;
-               }
-       case CL_RECEIVING:
-               {
-               char *buf2;
-               ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
-               if (ret < 0)
-                       goto out;
-               if (ret == 0)
-                       return 0;
-               /*
-                * The FD_ISSET() is not strictly necessary, but is allows us
-                * to skip the malloc below if there is nothing to read anyway.
-                */
-               if (!FD_ISSET(ct->scc.fd, &s->rfds))
-                       return 0;
-               if (ct->use_sideband) {
-                       struct sb_buffer sbb;
-                       ret = recv_sb(ct, &s->rfds, &sbb);
-                       if (ret > 0)
-                               ret = dispatch_sbb(ct, &sbb);
-                       goto out;
+               /* fall though */
+       case CL_EXECUTING:
+               if (ct->btrn[0]) {
+                       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)) {
+                               struct sb_buffer sbb;
+                               ret = recv_sb(ct, &s->rfds, &sbb);
+                               if (ret < 0)
+                                       goto close0;
+                               if (ret > 0) {
+                                       ret = dispatch_sbb(ct, &sbb);
+                                       if (ret < 0)
+                                               goto close0;
+                               }
+                       }
                }
-               buf2 = para_malloc(CLIENT_BUFSIZE);
-               ret = client_recv_buffer(ct, &s->rfds, buf2, CLIENT_BUFSIZE, &n);
-               if (n > 0) {
-                       buf2 = para_realloc(buf2, n);
-                       btr_add_output(buf2, n, btrn);
-               } else
-                       free(buf2);
+               ret = 0;
                goto out;
-               }
        }
 -              return;
+ close1:
+       PARA_INFO_LOG("channel 1: %s\n", para_strerror(-ret));
+       btr_remove_node(&ct->btrn[1]);
+       if (ct->btrn[0])
 -              return;
++              return 0;
+       goto out;
+ close0:
+       PARA_INFO_LOG("channel 0: %s\n", para_strerror(-ret));
+       btr_remove_node(&ct->btrn[0]);
+       if (ct->btrn[1] && ct->status == CL_SENDING)
++              return 0;
  out:
-       if (ret < 0) {
-               if (!ct->use_sideband && ret != -E_SERVER_EOF &&
-                               ret != -E_BTR_EOF && ret != -E_EOF)
-                       PARA_ERROR_LOG("%s\n", para_strerror(-ret));
-               btr_remove_node(&ct->btrn);
-       }
+       if (ret >= 0)
 -              return;
++              return 0;
+       btr_remove_node(&ct->btrn[0]);
+       btr_remove_node(&ct->btrn[1]);
+       if (ret != -E_SERVER_CMD_SUCCESS && ret != -E_SERVER_CMD_FAILURE)
+               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
 -      t->error = ret;
 +      return ret;
  }
  
  /**
@@@ -598,8 -501,10 +501,10 @@@ int client_connect(struct client_task *
        if (ret < 0)
                goto err_out;
        ct->status = CL_CONNECTED;
-       ct->btrn = btr_new_node(&(struct btr_node_description)
-               EMBRACE(.name = "client", .parent = parent, .child = child));
+       ct->btrn[0] = btr_new_node(&(struct btr_node_description)
+               EMBRACE(.name = "client recv", .parent = NULL, .child = child));
+       ct->btrn[1] = btr_new_node(&(struct btr_node_description)
+               EMBRACE(.name = "client send", .parent = parent, .child = NULL));
        ct->task.pre_select = client_pre_select;
        ct->task.post_select = client_post_select;
        ct->task.error = 0;
@@@ -612,15 -517,6 +517,15 @@@ err_out
        return ret;
  }
  
 +__noreturn static void print_help_and_die(struct client_task *ct)
 +{
 +      struct ggo_help h = DEFINE_GGO_HELP(client);
 +      bool d = ct->conf.detailed_help_given;
 +
 +      ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
 +      exit(0);
 +}
 +
  /**
   * Parse a client configuration.
   *
@@@ -651,9 -547,7 +556,9 @@@ int client_parse_config(int argc, char 
        ret = -E_CLIENT_SYNTAX;
        if (client_cmdline_parser(argc, argv, &ct->conf))
                goto out;
 -      HANDLE_VERSION_FLAG("client", ct->conf);
 +      version_handle_flag("client", ct->conf.version_given);
 +      if (ct->conf.help_given || ct->conf.detailed_help_given)
 +              print_help_and_die(ct);
  
        ct->config_file = ct->conf.config_file_given?
                para_strdup(ct->conf.config_file_arg) :
diff --combined command.c
index 41a58eac0517e15e44c964250578173708289e60,d4955fb696d7ff88c6f6fd1d263fbf025bcaca73..4bbf494c869b6938b596dbf9dcf45b9613fbf924
+++ b/command.c
@@@ -8,6 -8,7 +8,6 @@@
  
  #include <regex.h>
  #include <signal.h>
 -#include <sys/time.h>
  #include <sys/types.h>
  #include <osl.h>
  
@@@ -123,7 -124,7 +123,7 @@@ static unsigned get_status(struct misc_
                localtime_r(&nmmd->mtime, &mtime_tm);
                strftime(mtime, 29, "%b %d %Y", &mtime_tm);
        }
 -      gettimeofday(&current_time, NULL);
 +      clock_get_realtime(&current_time);
        /*
         * The calls to WRITE_STATUS_ITEM() below never fail because
         * b->max_size is zero (unlimited), see para_printf(). However, clang
@@@ -268,10 -269,7 +268,7 @@@ __printf_3_4 int send_sb_va(struct stre
   */
  int send_strerror(struct command_context *cc, int err)
  {
-       return cc->use_sideband?
-               send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", para_strerror(err))
-       :
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(err));
+       return send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", para_strerror(err));
  }
  
  /**
@@@ -338,23 -336,14 +335,14 @@@ static int com_sender(struct command_co
                        free(msg);
                        msg = tmp;
                }
-               if (cc->use_sideband)
-                       return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
-               ret = sc_send_buffer(&cc->scc, msg);
-               free(msg);
-               return ret;
+               return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
        }
        ret = check_sender_args(cc->argc, cc->argv, &scd);
        if (ret < 0) {
                if (scd.sender_num < 0)
                        return ret;
                msg = senders[scd.sender_num].help();
-               if (cc->use_sideband)
-                       return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT,
-                               false);
-               ret = sc_send_buffer(&cc->scc, msg);
-               free(msg);
-               return ret;
+               return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
        }
  
        switch (scd.cmd_num) {
@@@ -395,8 -384,7 +383,8 @@@ static int com_si(struct command_contex
                free(info);
        }
        ut = get_server_uptime_str(now);
 -      ret = xasprintf(&msg, "version: " GIT_VERSION "\n"
 +      ret = xasprintf(&msg,
 +              "version: %s\n"
                "up: %s\nplayed: %u\n"
                "server_pid: %d\n"
                "afs_pid: %d\n"
                "current loglevel: %s\n"
                "supported audio formats: %s\n"
                "%s",
 +              version_git(),
                ut, mmd->num_played,
                (int)getppid(),
                (int)mmd->afs_pid,
                mmd->num_commands,
                mmd->num_connects,
                conf.loglevel_arg,
 -              SERVER_AUDIO_FORMATS,
 +              AUDIO_FORMAT_HANDLERS,
                sender_info
        );
        mutex_unlock(mmd_mutex);
        free(ut);
        free(sender_info);
-       if (cc->use_sideband)
-               return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
-       ret = sc_send_bin_buffer(&cc->scc, msg, ret);
-       free(msg);
-       return ret;
+       return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
  }
  
  /* version */
@@@ -433,10 -416,10 +417,8 @@@ static int com_version(struct command_c
  
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
 -      msg = VERSION_TEXT("server") "built: " BUILD_DATE "\n" UNAME_RS
 -              ", " CC_VERSION "\n";
 -      len = strlen(msg);
 -      return send_sb(&cc->scc, msg, len, SBD_OUTPUT, true);
 +      len = xasprintf(&msg, "%s", version_text("server"));
-       if (cc->use_sideband)
-               return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false);
-       return sc_send_bin_buffer(&cc->scc, msg, len);
++      return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false);
  }
  
  #define EMPTY_STATUS_ITEMS \
   */
  static unsigned empty_status_items(int parser_friendly, char **result)
  {
 -      static char *esi;
 -      static unsigned len;
 -
 -      if (esi)
 -              goto out;
 +      char *esi;
 +      unsigned len;
  
        if (parser_friendly)
                len = xasprintf(&esi,
                        EMPTY_STATUS_ITEMS
                        #undef ITEM
                );
 -out:
        *result = esi;
        return len;
  }
@@@ -505,7 -492,7 +487,7 @@@ static int com_stat(struct command_cont
  {
        int i, ret;
        struct misc_meta_data tmp, *nmmd = &tmp;
 -      char *s, *esi = NULL;
 +      char *s;
        int32_t num = 0;
        int parser_friendly = 0;
  
        for (;;) {
                mmd_dup(nmmd);
                ret = get_status(nmmd, parser_friendly, &s);
-               if (cc->use_sideband)
-                       ret = send_sb(&cc->scc, s, ret, SBD_OUTPUT, false);
-               else {
-                       ret = sc_send_bin_buffer(&cc->scc, s, ret);
-                       free(s);
-               }
+               ret = send_sb(&cc->scc, s, ret, SBD_OUTPUT, false);
                if (ret < 0)
                        goto out;
                if (nmmd->vss_status_flags & VSS_NEXT) {
 +                      char *esi;
                        ret = empty_status_items(parser_friendly, &esi);
-                       if (cc->use_sideband)
-                               ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT,
-                                       false);
-                       else {
-                               ret = sc_send_bin_buffer(&cc->scc, esi, ret);
-                               free(esi);
-                       }
 -                      ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT, true);
++                      ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT, false);
                        if (ret < 0)
                                goto out;
                } else
                        goto out;
        }
  out:
 -      free(esi);
        return ret;
  }
  
  static int send_list_of_commands(struct command_context *cc, struct server_command *cmd,
                const char *handler)
  {
-       int ret;
        char *msg = NULL;
  
        for (; cmd->name; cmd++) {
                msg = para_strcat(msg, tmp);
                free(tmp);
        }
-       if (cc->use_sideband)
-               return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
-       ret = sc_send_buffer(&cc->scc, msg);
-       free(msg);
-       return ret;
+       return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
  }
  
  /* returns string that must be freed by the caller */
@@@ -644,11 -615,7 +610,7 @@@ static int com_help(struct command_cont
        );
        free(perms);
        free(handler);
-       if (cc->use_sideband)
-               return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false);
-       ret = sc_send_buffer(&cc->scc, buf);
-       free(buf);
-       return ret;
+       return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false);
  }
  
  /* hup */
@@@ -810,58 -777,6 +772,6 @@@ static int check_perms(unsigned int per
        return (cmd_ptr->perms & perms) < cmd_ptr->perms ? -E_PERM : 0;
  }
  
- /*
-  * Parse first string from *cmd and lookup in table of valid commands.
-  * On error, NULL is returned.
-  */
- static struct server_command *parse_cmd(const char *cmdstr)
- {
-       char buf[255];
-       int n = 0;
-       sscanf(cmdstr, "%200s%n", buf, &n);
-       if (!n)
-               return NULL;
-       buf[n] = '\0';
-       return get_cmd_ptr(buf, NULL);
- }
- static int read_command(struct stream_cipher_context *scc, char **result)
- {
-       int ret;
-       char buf[4096];
-       char *command = NULL;
-       for (;;) {
-               size_t numbytes;
-               char *p;
-               ret = sc_recv_buffer(scc, buf, sizeof(buf));
-               if (ret < 0)
-                       goto out;
-               if (!ret)
-                       break;
-               numbytes = ret;
-               ret = -E_COMMAND_SYNTAX;
-               if (command && numbytes + strlen(command) > MAX_COMMAND_LEN) /* DOS */
-                       goto out;
-               command = para_strcat(command, buf);
-               p = strstr(command, EOC_MSG);
-               if (p) {
-                       *p = '\0';
-                       break;
-               }
-       }
-       ret = command? 1 : -E_COMMAND_SYNTAX;
- out:
-       if (ret < 0)
-               free(command);
-       else
-               *result = command;
-       return ret;
- }
  static void reset_signals(void)
  {
        para_sigaction(SIGCHLD, SIG_IGN);
        para_sigaction(SIGHUP, SIG_DFL);
  }
  
- static int parse_auth_request(char *buf, int len, struct user **u,
-               bool *use_sideband)
+ static int parse_auth_request(char *buf, int len, struct user **u)
  {
        int ret;
        char *p, *username, **features = NULL;
        size_t auth_rq_len = strlen(AUTH_REQUEST_MSG);
+       bool sideband_requested = false;
  
        *u = NULL;
-       *use_sideband = false;
        if (len < auth_rq_len + 2)
                return -E_AUTH_REQUEST;
        if (strncmp(buf, AUTH_REQUEST_MSG, auth_rq_len) != 0)
                create_argv(p, ",", &features);
                for (i = 0; features[i]; i++) {
                        if (strcmp(features[i], "sideband") == 0)
-                               *use_sideband = true;
+                               sideband_requested = true;
                        else {
                                ret = -E_BAD_FEATURE;
                                goto out;
                        }
                }
        }
-       PARA_DEBUG_LOG("received auth request for user %s (sideband = %s)\n",
-               username, *use_sideband? "true" : "false");
+       if (sideband_requested == false) { /* sideband is mandatory */
+               PARA_ERROR_LOG("client did not request sideband\n");
+               ret = -E_BAD_FEATURE;
+               goto out;
+       }
+       PARA_DEBUG_LOG("received auth request for user %s\n", username);
        *u = lookup_user(username);
        ret = 1;
  out:
@@@ -979,6 -897,7 +892,7 @@@ __noreturn void handle_connect(int fd, 
        char *p, *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
        size_t numbytes;
        struct command_context cc_struct = {.peer = peername}, *cc = &cc_struct;
+       struct iovec iov;
  
        cc->scc.fd = fd;
        reset_signals();
        ret = recv_buffer(fd, buf, HANDSHAKE_BUFSIZE);
        if (ret < 0)
                goto net_err;
-       ret = parse_auth_request(buf, ret, &cc->u, &cc->use_sideband);
+       ret = parse_auth_request(buf, ret, &cc->u);
        if (ret < 0)
                goto net_err;
        p = buf + strlen(AUTH_REQUEST_MSG);
        }
        PARA_DEBUG_LOG("sending %u byte challenge + rc4 keys (%zu bytes)\n",
                CHALLENGE_SIZE, numbytes);
-       if (cc->use_sideband) {
-               struct iovec iov;
-               ret = send_sb(&cc->scc, buf, numbytes, SBD_CHALLENGE, false);
-               buf = NULL;
-               if (ret < 0)
-                       goto net_err;
-               ret = recv_sb(&cc->scc, SBD_CHALLENGE_RESPONSE,
-                       HANDSHAKE_BUFSIZE, &iov);
-               if (ret < 0)
-                       goto net_err;
-               buf = iov.iov_base;
-               numbytes = iov.iov_len;
-       } else {
-               ret = write_all(fd, buf, numbytes);
-               if (ret < 0)
-                       goto net_err;
-               /* recv challenge response */
-               ret = recv_bin_buffer(fd, buf, HASH_SIZE);
-               if (ret < 0)
-                       goto net_err;
-               numbytes = ret;
-       }
+       ret = send_sb(&cc->scc, buf, numbytes, SBD_CHALLENGE, false);
+       buf = NULL;
+       if (ret < 0)
+               goto net_err;
+       ret = recv_sb(&cc->scc, SBD_CHALLENGE_RESPONSE,
+               HANDSHAKE_BUFSIZE, &iov);
+       if (ret < 0)
+               goto net_err;
+       buf = iov.iov_base;
+       numbytes = iov.iov_len;
        PARA_DEBUG_LOG("received %zu bytes challenge response\n", numbytes);
        ret = -E_BAD_USER;
        if (!cc->u)
        /* init stream cipher keys with the second part of the random buffer */
        cc->scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
        cc->scc.send = sc_new(rand_buf + CHALLENGE_SIZE + SESSION_KEY_LEN, SESSION_KEY_LEN);
-       if (cc->use_sideband)
-               ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false);
-       else
-               ret = sc_send_buffer(&cc->scc, PROCEED_MSG);
+       ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false);
        if (ret < 0)
                goto net_err;
-       if (cc->use_sideband) {
-               struct iovec iov;
-               ret = recv_sb(&cc->scc, SBD_COMMAND, MAX_COMMAND_LEN, &iov);
-               if (ret < 0)
-                       goto net_err;
-               ret = parse_sb_command(cc, &iov);
-               if (ret < 0)
-                       goto err_out;
-               cc->argc = ret;
-       } else {
-               ret = read_command(&cc->scc, &command);
-               if (ret == -E_COMMAND_SYNTAX)
-                       goto err_out;
-               if (ret < 0)
-                       goto net_err;
-               ret = -E_BAD_CMD;
-               cc->cmd = parse_cmd(command);
-               if (!cc->cmd)
-                       goto err_out;
-               /* valid command, check permissions */
-               ret = check_perms(cc->u->perms, cc->cmd);
-               if (ret < 0)
-                       goto err_out;
-               /* valid command and sufficient perms */
-               ret = create_argv(command, "\n", &cc->argv);
-               if (ret < 0)
-                       goto err_out;
-               cc->argc = ret;
-       }
+       ret = recv_sb(&cc->scc, SBD_COMMAND, MAX_COMMAND_LEN, &iov);
+       if (ret < 0)
+               goto net_err;
+       ret = parse_sb_command(cc, &iov);
+       if (ret < 0)
+               goto err_out;
+       cc->argc = ret;
        PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cc->cmd->name,
                cc->u->name, peername);
        ret = cc->cmd->handler(cc);
        if (ret >= 0)
                goto out;
  err_out:
-       if (send_strerror(cc, -ret) >= 0 && cc->use_sideband)
+       if (send_strerror(cc, -ret) >= 0)
                send_sb(&cc->scc, NULL, 0, SBD_EXIT__FAILURE, true);
  net_err:
        PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
@@@ -1120,7 -1002,7 +997,7 @@@ out
                mmd->events++;
        mmd->active_connections--;
        mutex_unlock(mmd_mutex);
-       if (ret >= 0 && cc->use_sideband) {
+       if (ret >= 0) {
                ret = send_sb(&cc->scc, NULL, 0, SBD_EXIT__SUCCESS, true);
                if (ret < 0)
                        PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
diff --combined error.h
index ef54ef27707e262a09c7b638c8264cec549b2c1e,844c0045d0d2a7287d70b0bd1ed95901971fbbe3..51ace98315ed6140ec8bbde35c1c36d5649c3d46
+++ b/error.h
@@@ -34,15 -34,12 +34,15 @@@ DEFINE_ERRLIST_OBJECT_ENUM
  #define STDIN_ERRORS
  #define WRITE_ERRORS
  #define CHECK_WAV_ERRORS
 +#define VERSION_ERRORS
 +#define SCHED_ERRORS
  
  extern const char **para_errlist[];
  
  #define OSS_MIX_ERRORS \
        PARA_ERROR(OSS_MIXER_CHANNEL, "invalid mixer channel"), \
  
 +
  #define ALSA_MIX_ERRORS \
        PARA_ERROR(ALSA_MIX_OPEN, "could not open mixer"), \
        PARA_ERROR(ALSA_MIX_BAD_ELEM, "invalid/unsupported control element"), \
        PARA_ERROR(LIBSAMPLERATE, "secret rabbit code error"), \
  
  
 +#define OPUS_COMMON_ERRORS \
 +      PARA_ERROR(OPUS_HEADER, "invalid opus header"), \
 +
 +
 +#define OPUS_AFH_ERRORS \
 +      PARA_ERROR(OPUS_COMMENT, "invalid or corrupted opus comment"), \
 +
 +
 +#define OPUSDEC_FILTER_ERRORS \
 +      PARA_ERROR(CREATE_OPUS_DECODER, "could not create opus decoder"), \
 +      PARA_ERROR(OPUS_SET_GAIN, "opus: could not set gain"), \
 +      PARA_ERROR(OPUS_DECODE, "opus decode error"), \
 +
  #define SIDEBAND_ERRORS \
        PARA_ERROR(BAD_BAND, "invalid or unexpected band designator"), \
        PARA_ERROR(SB_PACKET_SIZE, "invalid sideband packet size or protocol error"), \
@@@ -76,6 -60,7 +76,6 @@@
  #define PLAY_ERRORS \
        PARA_ERROR(PLAY_SYNTAX, "para_play: syntax error"), \
        PARA_ERROR(NO_VALID_FILES, "no valid file found in playlist"), \
 -      PARA_ERROR(TERM_RQ, "user termination request"), \
        PARA_ERROR(BAD_PLAY_CMD, "invalid command"), \
  
  
  
  
  #define COMPRESS_FILTER_ERRORS \
 -      PARA_ERROR(COMPRESS_SYNTAX, "syntax error in compress filter config"), \
        PARA_ERROR(COMPRESS_EOF, "compress: end of file"), \
  
  
  
  #define AFH_ERRORS \
        PARA_ERROR(AFH_SYNTAX, "afh syntax error"), \
 -      PARA_ERROR(AFH_SHORT_WRITE, "afh short write"), \
  
  
  #define AFH_COMMON_ERRORS \
  
  
  #define AUDIOC_ERRORS \
 -      PARA_ERROR(AUDIOC_SYNTAX, "audioc syntax error"), \
        PARA_ERROR(AUDIOC_EOF, "audioc: end of file"), \
  
  
        PARA_ERROR(SERVER_EOF, "connection closed by para_server"), \
        PARA_ERROR(SERVER_CMD_SUCCESS, "command terminated successfully"), \
        PARA_ERROR(SERVER_CMD_FAILURE, "command failed"), \
+       PARA_ERROR(INCOMPAT_FEAT, "client/server incompatibility"), \
  
  
 -#define SCHED_ERRORS \
 -      PARA_ERROR(NOT_INITIALIZED, "scheduler not yet initialized"), \
 -      PARA_ERROR(SCHED_SHUTDOWN, "scheduler was shut down"), \
 -
 -
  #define NET_ERRORS \
        PARA_ERROR(NAME_TOO_LONG, "name too long for struct sockaddr_un"), \
        PARA_ERROR(ADDRESS_LOOKUP, "can not resolve requested address"),\
  
  
  #define UDP_RECV_ERRORS \
 -      PARA_ERROR(UDP_SYNTAX, "udp_recv syntax error"), \
        PARA_ERROR(UDP_OVERRUN, "output buffer overrun"), \
  
  
        PARA_ERROR(UNSUPPORTED_AUDIO_FORMAT, "given audio format not supported"), \
        PARA_ERROR(NOT_PLAYING, "not playing"), \
        PARA_ERROR(AUDIOD_OFF, "audiod switched off"), \
 -      PARA_ERROR(STATUS_TIMEOUT, "timeout reading server status"), \
  
  
  #define AUDIOD_COMMAND_ERRORS \
  
  #define MP3DEC_FILTER_ERRORS \
        PARA_ERROR(MAD_FRAME_DECODE, "mad frame decode error"), \
 -      PARA_ERROR(MP3DEC_SYNTAX, "syntax error in mp3dec config"), \
        PARA_ERROR(MP3DEC_EOF, "mp3dec: end of file"), \
        PARA_ERROR(MP3DEC_CORRUPT, "too many corrupt frames"), \
  
  
  #define FILTER_ERRORS \
        PARA_ERROR(NO_FILTERS, "at least one filter must be given"), \
 -      PARA_ERROR(FILTER_SYNTAX, "syntax error"), \
  
  
  #define STRING_ERRORS \
        PARA_ERROR(SEXP_BUILD, "could not build S-expression"), \
        PARA_ERROR(SEXP_ENCRYPT, "could not encrypt S-expression"), \
        PARA_ERROR(SEXP_DECRYPT, "could not decrypt S-expression"), \
 -      PARA_ERROR(MD_OPEN, "could not open message digest object"), \
 -      PARA_ERROR(CIPHER_OPEN, "could not create stream cipher handle"), \
        PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \
        PARA_ERROR(KEY_MARKER, "invalid/missing key header or footer"), \
        PARA_ERROR(ASN1_PARSE, "could not parse ASN.1 key"), \
        PARA_ERROR(SENDER_CMD, "command not supported by this sender"), \
        PARA_ERROR(SERVER_CRASH, "para_server crashed -- can not live without it"), \
        PARA_ERROR(BAD_USER, "auth request for invalid user"), \
-       PARA_ERROR(BAD_FEATURE, "request for unknown or invalid feature"), \
+       PARA_ERROR(BAD_FEATURE, "invalid feature request"), \
        PARA_ERROR(BAD_AUTH, "authentication failure"), \
  
  
diff --combined gcrypt.c
index b4718ec0f8c7910dbdbb3c451adeaa790e24e0d2,a96db362278b1dae70e7aec87a920aac1c402e54..2736a6c769835823b39820b0291069f1ca80a534
+++ b/gcrypt.c
@@@ -758,13 -758,13 +758,13 @@@ static int decode_rsa(gcry_sexp_t sexp
  
        PARA_DEBUG_LOG("decrypted buffer before unpad (%d bytes):\n",
                key_size);
 -      dump_buffer("non-unpadded decrypted buffer", oaep_buf, key_size);;
 +      dump_buffer("non-unpadded decrypted buffer", oaep_buf, key_size);
        ret = unpad_oaep(oaep_buf, key_size, outbuf, nbytes);
        if (ret < 0)
                goto out_mpi_release;
        PARA_DEBUG_LOG("decrypted buffer after unpad (%zu bytes):\n",
                *nbytes);
 -      dump_buffer("unpadded decrypted buffer", outbuf, *nbytes);;
 +      dump_buffer("unpadded decrypted buffer", outbuf, *nbytes);
        ret = 1;
  out_mpi_release:
        gcry_mpi_release(out_mpi);
@@@ -937,39 -937,6 +937,6 @@@ void sc_free(struct stream_cipher *sc
        free(sc);
  }
  
- int sc_send_bin_buffer(struct stream_cipher_context *scc, char *buf,
-               size_t size)
- {
-       gcry_error_t gret;
-       int ret;
-       unsigned char *tmp = para_malloc(size);
-       assert(size);
-       gret = gcry_cipher_encrypt(scc->send->handle, tmp, size,
-               (unsigned char *)buf, size);
-       assert(gret == 0);
-       ret = xwrite(scc->fd, (char *)tmp, size);
-       free(tmp);
-       return ret;
- }
- int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf,
-               size_t size)
- {
-       gcry_error_t gret;
-       ssize_t ret = recv(scc->fd, buf, size, 0);
-       if (ret < 0)
-               ret = -ERRNO_TO_PARA_ERROR(errno);
-       if (ret <= 0)
-               return ret;
-       /* perform in-place encryption */
-       gret = gcry_cipher_encrypt(scc->recv->handle, (unsigned char *)buf, ret,
-               NULL, 0);
-       assert(gret == 0);
-       return ret;
- }
  void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
  {
        gcry_cipher_hd_t handle = sc->handle;
diff --combined para.h
index 4de4af4a23dd0f0a6e225279f9e72423a027adc4,46efebdebf22e7e2677ef4d48f066a642ebf4c8c..6655d35672f410ebff1a5678841e9fee967c7996
--- 1/para.h
--- 2/para.h
+++ b/para.h
@@@ -82,37 -82,25 +82,29 @@@ extern __printf_2_3 void (*para_log)(in
  
  /** Sent by para_client to initiate the authentication procedure. */
  #define AUTH_REQUEST_MSG "auth rsa "
- /** Sent by para_server for commands that expect a data file. */
- #define AWAITING_DATA_MSG "\nAwaiting Data."
- /** Sent by para_server if authentication was successful. */
- #define PROCEED_MSG "Proceed."
- /** Length of the \p PROCEED_MSG string. */
- #define PROCEED_MSG_LEN strlen(PROCEED_MSG)
- /** Sent by para_client to indicate the end of the command line. */
- #define EOC_MSG "\nEnd of Command."
  
  /* exec */
  int para_exec_cmdline_pid(pid_t *pid, const char *cmdline, int *fds);
  
  /* time */
 -int tv_diff(const struct timeval *b, const struct timeval *a, struct timeval *diff);
 -long unsigned tv2ms(const struct timeval*);
 -void d2tv(double, struct timeval*);
 -void tv_add(const struct timeval*, const struct timeval *, struct timeval *);
 -void tv_scale(const unsigned long, const struct timeval *, struct timeval *);
 +int tv_diff(const struct timeval *b, const struct timeval *a,
 +              struct timeval *diff);
 +long unsigned tv2ms(const struct timeval *tv);
 +void d2tv(double x, struct timeval *tv);
 +void tv_add(const struct timeval *a, const struct timeval *b,
 +              struct timeval *sum);
 +void tv_scale(const unsigned long mult, const struct timeval *tv,
 +              struct timeval *result);
  void tv_divide(const unsigned long divisor, const struct timeval *tv,
 -      struct timeval *result);
 +              struct timeval *result);
  int tv_convex_combination(const long a, const struct timeval *tv1,
                const long b, const struct timeval *tv2,
                struct timeval *result);
 -void ms2tv(const long unsigned n, struct timeval *tv);
 +void ms2tv(long unsigned n, struct timeval *tv);
  void compute_chunk_time(long unsigned chunk_num,
                struct timeval *chunk_tv, struct timeval *stream_start,
                struct timeval *result);
 +struct timeval *clock_get_realtime(struct timeval *tv);
  
  /** The enum of all status items. */
  enum status_items {STATUS_ITEM_ENUM NUM_STAT_ITEMS};