]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 't/clang'
authorAndre Noll <maan@systemlinux.org>
Wed, 11 Jul 2012 23:53:14 +0000 (01:53 +0200)
committerAndre Noll <maan@systemlinux.org>
Wed, 11 Jul 2012 23:57:17 +0000 (01:57 +0200)
869f0e aft.c: Silence scan-build warning.
4cdf53 mood: Improve readability of get_item_score().
ec5040 mood: Add score value only for admissible files.
76d527 client: Remove pointless assignment.
466c9b gcrypt: Remove pointless assignment.
98c13e gcrypt: Remove dead stores.
d458c2 mp3dec: Remove dead store.

Cooking for quite some time - no problems.

45 files changed:
.gitignore
Doxyfile
Makefile.in
NEWS
afs.c
afs.cmd
afs.h
aft.c
alsa_write.c
ao_write.c
attribute.c
audiod.c
audiod.cmd
audiod_command.c
bitstream.h
blob.c
client.c
client.h
client_common.c
command.c
command.h
command_util.sh
configure.ac
crypt.c
crypt.h
crypt_common.c
error.h
file_write.c
flacdec_filter.c
gcc-compat.h
gcrypt.c
mood.c
oggdec_filter.c
oss_write.c
osx_write.c
para.h
playlist.c
server.cmd
sideband.c [new file with mode: 0644]
sideband.h [new file with mode: 0644]
string.c
string.h
wmadata.h
wmadec_filter.c
write.h

index d337367bfe0325eeb6caa6b6f6982eb35bdb14c2..141424f058ffadc11a675bb511d4e712c794cb84 100644 (file)
@@ -18,7 +18,6 @@ Makefile.deps
 TODO
 NONE
 aclocal.m4
-*_command_list.c
 *_command_list.h
 *_command_list.man
 paraslash-git.tar.bz2
index 83973cfb9dc23c6d6eaacb2efe7406c74d6b5d73..3fed0c2e759c155e90534bc6f092f967f2a67f5c 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
@@ -623,7 +623,7 @@ EXCLUDE_PATTERNS       = *.cmdline.* \
                          gcc-compat.h \
                          fade.c \
                          config.h \
-                       *_command_list.[ch] \
+                       *_command_list.h \
                        *_completion.h
 
 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
index 30a5c015a6f982dd5703aa51498aa89eda347b4e..868cfeac5ad719545b210982fcc934e58b53af3c 100644 (file)
@@ -119,20 +119,21 @@ $(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,\
 $(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X,$(subst y,Y,\
 $(subst z,Z,$1))))))))))))))))))))))))))
 
-%_command_list.c: %.cmd
-       @[ -z "$(Q)" ] || echo 'GEN $@'
-       $(Q) ./command_util.sh c < $< >$@
-%_command_list.h: %.cmd
+%_command_list.h: %.cmd %.c
        @[ -z "$(Q)" ] || echo 'GEN $@'
        $(Q) ./command_util.sh h < $< >$@
-%_command_list.man: %.cmd
+%_command_list.man: %.cmd %.c
        @[ -z "$(Q)" ] || echo 'GEN $@'
        $(Q) ./command_util.sh man < $< > $@
-%_completion.h: %.cmd
+%_completion.h: %.cmd %.c
        @[ -z "$(Q)" ] || echo 'GEN $@'
        $(Q) ./command_util.sh compl $(strip $(call TOUPPER,$(*F)))_COMPLETERS \
                $(strip $(call TOUPPER,$(*F)))_COMMANDS < $< > $@
 
+server_command_list.h server_command_list.man server_completion.h: command.c
+afs_command_list.h afs_command_list.man afs_completion.h: afs.c aft.c attribute.c
+audiod_command_list.h audiod_command_list.man audiod_completion.h: audiod_command.c
+
 server_command_lists_man = server_command_list.man afs_command_list.man
 man/man1/para_server.1: para_server $(server_command_lists_man) | $(man_dir)
        @[ -z "$(Q)" ] || echo 'MAN $<'
diff --git a/NEWS b/NEWS
index 0ac2240effe91e4294c8ec901fe18f9c143e42d3..9a84c332001ddc5d0cdf4fbf769550b4c9387790 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,9 +2,15 @@
 0.4.11 (to be announced) "mutual diversity"
 -------------------------------------------
 
+       - Sideband connections: If both para_server and para_client
+         support this feature, data is sent as a multiplexed stream.
        - The --no_default_filters option of para_filter has been
          removed.
+       - Several fixes and latency improvements to various decoders.
+       - The alsa writer now limits the prebuffer time to 500ms.
        - Documentation improvements.
+       - Overhaul of the command_util.sh script.
+       - Fixes for some minor problems found by the clang analyzer.
 
 ------------------------------------------
 0.4.10 (2012-03-30) "heterogeneous vacuum"
diff --git a/afs.c b/afs.c
index 5939cbe69cc46c1095d7cb82ffc98a741d3f6505..a1381e738a40cf644abeb16cf8356e7c578839a6 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -26,6 +26,7 @@
 #include "signal.h"
 #include "fd.h"
 #include "mood.h"
+#include "sideband.h"
 #include "command.h"
 
 /** The osl tables used by afs. \sa blob.c. */
@@ -141,6 +142,8 @@ struct callback_query {
 struct callback_result {
        /** The number of bytes of the result. */
        size_t result_size;
+       /** The band designator (loglevel for the result). */
+       uint8_t band;
 };
 
 static int dispatch_result(int result_shmid, callback_result_handler *handler,
@@ -148,7 +151,8 @@ static int dispatch_result(int result_shmid, callback_result_handler *handler,
 {
        struct osl_object result;
        void *result_shm;
-       int ret2, ret = shm_attach(result_shmid, ATTACH_RO, &result_shm);
+       /* must attach r/w as result.data might get encrypted in-place. */
+       int ret2, ret = shm_attach(result_shmid, ATTACH_RW, &result_shm);
        struct callback_result *cr = result_shm;
 
        if (ret < 0) {
@@ -159,7 +163,7 @@ static int dispatch_result(int result_shmid, callback_result_handler *handler,
        result.data = result_shm + sizeof(*cr);
        if (result.size) {
                assert(handler);
-               ret = handler(&result, private_result_data);
+               ret = handler(&result, cr->band, private_result_data);
                if (ret < 0)
                        PARA_NOTICE_LOG("result handler error: %s\n",
                                para_strerror(-ret));
@@ -546,8 +550,11 @@ static void com_select_callback(int fd, const struct osl_object *query)
 {
        struct para_buffer pb = {
                .max_size = shm_get_shmmax(),
-               .private_data = &fd,
-               .max_size_handler = pass_buffer_as_shm
+               .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;
@@ -577,7 +584,7 @@ static void com_select_callback(int fd, const struct osl_object *query)
                        current_mop : "dummy mood", num_admissible);
 out:
        if (ret2 >= 0 && pb.offset)
-               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
        free(pb.buf);
 }
 
@@ -585,23 +592,24 @@ out:
  * Result handler for sending data to the para_client process.
  *
  * \param result The data to be sent.
- * \param private Pointer to the context.
+ * \param band The band designator.
+ * \param private Pointer to the command context.
  *
- * \return The return value of the underlying call to sc_send_bin_buffer().
+ * \return The return value of the underlying call to \ref command.c::send_sb.
  *
- * \sa \ref callback_result_handler, \ref sc_send_bin_buffer().
+ * \sa \ref callback_result_handler, \ref command.c::send_sb.
  */
-int sc_send_result(struct osl_object *result, void *private)
+int afs_cb_result_handler(struct osl_object *result, uint8_t band,
+               void *private)
 {
        struct command_context *cc = private;
-       int ret;
 
        if (!result->size)
                return 1;
-       ret = sc_send_bin_buffer(&cc->scc, result->data, result->size);
-       if (ret < 0 || ret == result->size)
-               return ret;
-       return -E_SHORT_WRITE;
+       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);
 }
 
 int com_select(struct command_context *cc)
@@ -613,7 +621,7 @@ int com_select(struct command_context *cc)
        query.data = cc->argv[1];
        query.size = strlen(cc->argv[1]) + 1;
        return send_callback_request(com_select_callback, &query,
-               &sc_send_result, cc);
+               &afs_cb_result_handler, cc);
 }
 
 static void init_admissible_files(char *arg)
@@ -783,30 +791,30 @@ static void command_pre_select(struct sched *s, struct task *t)
 /**
  * Send data as shared memory to a file descriptor.
  *
+ * \param fd File descriptor to send the shmid to.
+ * \param band The band designator for this data.
  * \param buf The buffer holding the data to be sent.
  * \param size The size of \a buf.
- * \param fd_ptr A pointer to the file descriptor.
- *
- * This function is used as the \a max_size handler in a \ref para_buffer
- * structure. If used this way, it is called by \ref para_printf() whenever
- * the buffer passed to para_printf() is about to exceed its maximal size.
  *
  * This function creates a shared memory area large enough to hold
  * the content given by \a buf and \a size and sends the identifier
- * of this area to the file descriptor given by \a fd_ptr.
+ * of this area to the file descriptor \a fd.
+ *
+ * It is called by the AFS max_size handler as well as directly by the AFS
+ * command callbacks to send command output to the command handlers.
  *
  * \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(char *buf, size_t size, void *fd_ptr)
+int pass_buffer_as_shm(int fd, uint8_t band, char *buf, size_t size)
 {
-       int ret, shmid, fd = *(int *)fd_ptr;
+       int ret, shmid;
        void *shm;
        struct callback_result *cr;
 
        if (!buf || !size)
                return 0;
-       ret = shm_new(size + sizeof(struct callback_result));
+       ret = shm_new(size + sizeof(*cr));
        if (ret < 0)
                return ret;
        shmid = ret;
@@ -815,6 +823,7 @@ int pass_buffer_as_shm(char *buf, size_t size, void *fd_ptr)
                goto err;
        cr = shm;
        cr->result_size = size;
+       cr->band = band;
        memcpy(shm + sizeof(*cr), buf, size);
        ret = shm_detach(shm);
        if (ret < 0)
@@ -1023,7 +1032,7 @@ out:
        if (ret < 0)
                para_printf(&pb, "%s\n", para_strerror(-ret));
        if (pb.buf)
-               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
        free(pb.buf);
 }
 
@@ -1053,7 +1062,7 @@ int com_init(struct command_context *cc)
                }
        }
        ret = send_callback_request(create_tables_callback, &query,
-               sc_send_result, cc);
+               afs_cb_result_handler, cc);
        if (ret < 0)
                /* ignore return value */
                sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
@@ -1107,19 +1116,19 @@ int com_check(struct command_context *cc)
                flags = ~0U;
        if (flags & CHECK_AFT) {
                ret = send_callback_request(aft_check_callback, NULL,
-                       sc_send_result, cc);
+                       afs_cb_result_handler, cc);
                if (ret < 0)
                        return ret;
        }
        if (flags & CHECK_PLAYLISTS) {
                ret = send_callback_request(playlist_check_callback,
-                       NULL, sc_send_result, cc);
+                       NULL, afs_cb_result_handler, cc);
                if (ret < 0)
                        return ret;
        }
        if (flags & CHECK_MOODS) {
                ret = send_callback_request(mood_check_callback, NULL,
-                       sc_send_result, cc);
+                       afs_cb_result_handler, cc);
                if (ret < 0)
                        return ret;
        }
diff --git a/afs.cmd b/afs.cmd
index c7808040370c7e8c4088e44d7b63820093e255af..f0bd26f1a1bef059126b362478526081e68e3743 100644 (file)
--- a/afs.cmd
+++ b/afs.cmd
@@ -1,10 +1,5 @@
-OF: afs_command_list
+BN: afs
 SF: afs.c aft.c attribute.c
-HC: Prototypes for the commands of the audio file selector.
-CC: Array of commands for the audio file selector.
-AT: server_command
-SI: osl regex
-IN: para error crypt command string afh afs server list user_list
 SN: list of afs commands
 TM: mood lyr img pl
 ---
diff --git a/afs.h b/afs.h
index 7145c41b9ee71d10ad1fcaeb492532176dfcc25a..2789e94e91e0b2fc4def72671165ea043e0970a4 100644 (file)
--- a/afs.h
+++ b/afs.h
@@ -205,9 +205,38 @@ typedef void callback_function(int fd, const struct osl_object *);
  *
  * \sa \ref send_callback_request().
  */
-typedef int callback_result_handler(struct osl_object *result, void *private);
-int sc_send_result(struct osl_object *result, void *private);
-int pass_buffer_as_shm(char *buf, size_t size, void *fd_ptr);
+typedef int callback_result_handler(struct osl_object *result, uint8_t band, void *private);
+int afs_cb_result_handler(struct osl_object *result, uint8_t band, void *private);
+int pass_buffer_as_shm(int fd, uint8_t band, char *buf, size_t size);
+
+/** Structure passed to the AFS max_size handler. */
+struct afs_max_size_handler_data {
+       /** Local socket connecting the command handler and the AFS process. */
+       int fd;
+       /** The sideband designator for this data packet. */
+       uint8_t band;
+};
+
+/**
+ * Standard max_size handler for AFS commands.
+ *
+ * \param buf Contains (part of) the AFS command output.
+ * \param size The number of bytes in \a buf.
+ * \param private Pointer to a \ref afs_max_size_handler_data structure.
+ *
+ * Whenever the output of an AFS command exceeds the maximal size of a shared
+ * memory area, the max size handler of the para_buffer which holds the command
+ * output is called with \a private being a pointer to a structure of type
+ * afs_max_size_handler_data.
+ *
+ * \return The return value of the underlying call to \ref
+ *  pass_buffer_as_shm().
+ */
+_static_inline_ int afs_max_size_handler(char *buf, size_t size, void *private)
+{
+       struct afs_max_size_handler_data *amshd = private;
+       return pass_buffer_as_shm(amshd->fd, amshd->band, buf, size);
+}
 
 __noreturn void afs_init(uint32_t cookie, int socket_fd);
 void afs_event(enum afs_events event, struct para_buffer *pb,
diff --git a/aft.c b/aft.c
index 9e991da65d075c948ad704ed0ecdaead02c7ea45..f01f1868e60b1592c9bde8f0d371234edd60d23a 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -21,6 +21,7 @@
 #include "fd.h"
 #include "ipc.h"
 #include "portable_io.h"
+#include "sideband.h"
 #include "command.h"
 
 static struct osl_table *audio_file_table;
@@ -1351,9 +1352,15 @@ static void com_ls_callback(int fd, const struct osl_object *query)
 {
        struct ls_options *opts = query->data;
        char *p, *pattern_start = (char *)query->data + sizeof(*opts);
-       struct para_buffer b = {.max_size = shm_get_shmmax(),
+       struct para_buffer b = {
+               .max_size = shm_get_shmmax(),
                .flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0,
-               .max_size_handler = pass_buffer_as_shm, .private_data = &fd};
+               .max_size_handler = afs_max_size_handler,
+               .private_data = &(struct afs_max_size_handler_data) {
+                       .fd = fd,
+                       .band = SBD_OUTPUT
+               }
+       };
        int i = 0, ret;
        time_t current_time;
 
@@ -1395,7 +1402,7 @@ static void com_ls_callback(int fd, const struct osl_object *query)
                }
 out:
        if (b.offset)
-               pass_buffer_as_shm(b.buf, b.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, b.buf, b.offset);
        free(b.buf);
        free(opts->data);
        free(opts->data_ptr);
@@ -1517,7 +1524,7 @@ int com_ls(struct command_context *cc)
        opts.mode = mode;
        opts.num_patterns = cc->argc - i;
        ret = send_option_arg_callback_request(&query, opts.num_patterns,
-               cc->argv + i, com_ls_callback, sc_send_result, cc);
+               cc->argv + i, com_ls_callback, afs_cb_result_handler, cc);
        return ret;
 }
 
@@ -1676,8 +1683,14 @@ static void com_add_callback(int fd, const struct osl_object *query)
        char afsi_buf[AFSI_SIZE];
        uint32_t flags = read_u32(buf + CAB_FLAGS_OFFSET);
        struct afs_info default_afsi = {.last_played = 0};
-       struct para_buffer msg = {.max_size = shm_get_shmmax(),
-               .max_size_handler = pass_buffer_as_shm, .private_data = &fd};
+       struct para_buffer msg = {
+               .max_size = shm_get_shmmax(),
+               .max_size_handler = afs_max_size_handler,
+               .private_data = &(struct afs_max_size_handler_data) {
+                       .fd = fd,
+                       .band = SBD_OUTPUT
+               }
+       };
        uint16_t afhi_offset, chunks_offset;
 
        hash = (unsigned char *)buf + CAB_HASH_OFFSET;
@@ -1798,7 +1811,7 @@ out:
        if (ret < 0)
                para_printf(&msg, "%s\n", para_strerror(-ret));
        if (msg.offset)
-               pass_buffer_as_shm(msg.buf, msg.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, msg.buf, msg.offset);
        free(msg.buf);
 }
 
@@ -1817,7 +1830,8 @@ static void path_brother_callback(int fd, const struct osl_object *query)
        int ret = aft_get_row_of_path(path, &path_brother);
        if (ret < 0)
                return;
-       pass_buffer_as_shm((char *)&path_brother, sizeof(path_brother), &fd);
+       pass_buffer_as_shm(fd, SBD_OUTPUT, (char *)&path_brother,
+               sizeof(path_brother));
 }
 
 static void hash_sister_callback(int fd, const struct osl_object *query)
@@ -1828,10 +1842,12 @@ static void hash_sister_callback(int fd, const struct osl_object *query)
        hash_sister = find_hash_sister(hash);
        if (!hash_sister)
                return;
-       pass_buffer_as_shm((char *)&hash_sister, sizeof(hash_sister), &fd);
+       pass_buffer_as_shm(fd, SBD_OUTPUT, (char *)&hash_sister,
+               sizeof(hash_sister));
 }
 
-static int get_row_pointer_from_result(struct osl_object *result, void *private)
+static int get_row_pointer_from_result(struct osl_object *result,
+               __a_unused uint8_t band, void *private)
 {
        struct osl_row **row = private;
        *row = *(struct osl_row **)(result->data);
@@ -1860,8 +1876,12 @@ static int add_one_audio_file(const char *path, void *private_data)
        ret = 1;
        if (pb && (pad->flags & ADD_FLAG_LAZY)) { /* lazy is really cheap */
                if (pad->flags & ADD_FLAG_VERBOSE)
-                       send_ret = sc_send_va_buffer(&pad->cc->scc,
-                               "lazy-ignore: %s\n", path);
+                       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);
                goto out_free;
        }
        /* We still want to add this file. Compute its hash. */
@@ -1881,8 +1901,12 @@ static int add_one_audio_file(const char *path, void *private_data)
        ret = 1;
        if (pb && hs && hs == pb && !(pad->flags & ADD_FLAG_FORCE)) {
                if (pad->flags & ADD_FLAG_VERBOSE)
-                       send_ret = sc_send_va_buffer(&pad->cc->scc,
-                               "%s exists, not forcing update\n", path);
+                       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);
                goto out_unmap;
        }
        /*
@@ -1899,13 +1923,19 @@ static int add_one_audio_file(const char *path, void *private_data)
        munmap(map.data, map.size);
        close(fd);
        if (pad->flags & ADD_FLAG_VERBOSE) {
-               send_ret = sc_send_va_buffer(&pad->cc->scc, "adding %s\n", path);
+               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);
                if (send_ret < 0)
                        goto out_free;
        }
        save_add_callback_buffer(hash, path, afhi_ptr, pad->flags, format_num, &obj);
        /* Ask afs to consider this entry for adding. */
-       ret = send_callback_request(com_add_callback, &obj, sc_send_result, pad->cc);
+       ret = send_callback_request(com_add_callback, &obj,
+               afs_cb_result_handler, pad->cc);
        goto out_free;
 
 out_unmap:
@@ -1913,8 +1943,14 @@ out_unmap:
        munmap(map.data, map.size);
 out_free:
        if (ret < 0 && send_ret >= 0)
-               send_ret = sc_send_va_buffer(&pad->cc->scc,
-                       "failed to add %s (%s)\n", path, para_strerror(-ret));
+               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));
        free(obj.data);
        clear_afhi(afhi_ptr);
        /* Stop adding files only on send errors. */
@@ -1958,7 +1994,11 @@ int com_add(struct command_context *cc)
                char *path;
                ret = verify_path(cc->argv[i], &path);
                if (ret < 0) {
-                       ret = sc_send_va_buffer(&cc->scc, "%s: %s\n",
+                       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",
                                cc->argv[i], para_strerror(-ret));
                        if (ret < 0)
                                return ret;
@@ -1966,9 +2006,14 @@ int com_add(struct command_context *cc)
                }
                ret = stat(path, &statbuf);
                if (ret < 0) {
-                       ret = sc_send_va_buffer(&cc->scc,
-                               "failed to stat %s (%s)\n", path,
-                               strerror(errno));
+                       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));
                        free(path);
                        if (ret < 0)
                                return ret;
@@ -1980,8 +2025,12 @@ int com_add(struct command_context *cc)
                else
                        ret = add_one_audio_file(path, &pad);
                if (ret < 0) {
-                       sc_send_va_buffer(&cc->scc, "%s: %s\n", path,
-                               para_strerror(-ret));
+                       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));
                        free(path);
                        return ret;
                }
@@ -2084,7 +2133,7 @@ static void com_touch_callback(int fd, const struct osl_object *query)
                .pb = {
                        .max_size = shm_get_shmmax(),
                        .private_data = &fd,
-                       .max_size_handler = pass_buffer_as_shm
+                       .max_size_handler = afs_max_size_handler
                }
        };
        int ret, ret2 = 0;
@@ -2105,7 +2154,7 @@ static void com_touch_callback(int fd, const struct osl_object *query)
        else if (pmd.num_matches == 0)
                ret2 = para_printf(&tad.pb, "no matches\n");
        if (ret2 >= 0 && tad.pb.offset)
-               pass_buffer_as_shm(tad.pb.buf, tad.pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, tad.pb.buf, tad.pb.offset);
        free(tad.pb.buf);
 }
 
@@ -2177,9 +2226,9 @@ int com_touch(struct command_context *cc)
        if (i >= cc->argc)
                return -E_AFT_SYNTAX;
        ret = send_option_arg_callback_request(&query, cc->argc - i,
-               cc->argv + i, com_touch_callback, sc_send_result, cc);
+               cc->argv + i, com_touch_callback, afs_cb_result_handler, cc);
        if (ret < 0)
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+               send_strerror(cc, -ret);
        return ret;
 }
 
@@ -2225,7 +2274,7 @@ static void com_rm_callback(int fd, const struct osl_object *query)
                .pb = {
                        .max_size = shm_get_shmmax(),
                        .private_data = &fd,
-                       .max_size_handler = pass_buffer_as_shm
+                       .max_size_handler = afs_max_size_handler
                }
        };
        int ret;
@@ -2252,7 +2301,7 @@ static void com_rm_callback(int fd, const struct osl_object *query)
                        pmd.num_matches);
        }
        if (ret >= 0 && crd.pb.offset)
-               pass_buffer_as_shm(crd.pb.buf, crd.pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, crd.pb.buf, crd.pb.offset);
        free(crd.pb.buf);
 }
 
@@ -2288,9 +2337,9 @@ int com_rm(struct command_context *cc)
        if (i >= cc->argc)
                return -E_AFT_SYNTAX;
        ret = send_option_arg_callback_request(&query, cc->argc - i,
-               cc->argv + i, com_rm_callback, sc_send_result, cc);
+               cc->argv + i, com_rm_callback, afs_cb_result_handler, cc);
        if (ret < 0)
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+               send_strerror(cc, -ret);
        return ret;
 }
 
@@ -2367,7 +2416,7 @@ static void com_cpsi_callback(int fd, const struct osl_object *query)
                .pb = {
                        .max_size = shm_get_shmmax(),
                        .private_data = &fd,
-                       .max_size_handler = pass_buffer_as_shm
+                       .max_size_handler = afs_max_size_handler
                }
        };
        int ret;
@@ -2398,7 +2447,7 @@ out:
                        para_printf(&cad.pb, "nothing copied\n");
        }
        if (cad.pb.offset)
-               pass_buffer_as_shm(cad.pb.buf, cad.pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, cad.pb.buf, cad.pb.offset);
        free(cad.pb.buf);
 }
 
@@ -2447,9 +2496,9 @@ int com_cpsi(struct command_context *cc)
        if (!(flags & ~CPSI_FLAG_VERBOSE)) /* no copy flags given */
                flags = ~(unsigned)CPSI_FLAG_VERBOSE | flags;
        ret = send_option_arg_callback_request(&options, cc->argc - i,
-               cc->argv + i, com_cpsi_callback, sc_send_result, cc);
+               cc->argv + i, com_cpsi_callback, afs_cb_result_handler, cc);
        if (ret < 0)
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+               send_strerror(cc, -ret);
        return ret;
 }
 
@@ -2461,7 +2510,7 @@ static void afs_stat_callback(int fd, const struct osl_object *query)
 
        if (!buf)
                return;
-       pass_buffer_as_shm(buf, strlen(buf), &fd);
+       pass_buffer_as_shm(fd, SBD_OUTPUT, buf, strlen(buf));
 }
 
 /**
@@ -2483,7 +2532,8 @@ int send_afs_status(struct command_context *cc, int parser_friendly)
        struct osl_object query = {.data = &parser_friendly,
                .size = sizeof(parser_friendly)};
 
-       return send_callback_request(afs_stat_callback, &query, sc_send_result, cc);
+       return send_callback_request(afs_stat_callback, &query,
+               afs_cb_result_handler, cc);
 }
 
 /* TODO: optionally fix problems by removing offending rows */
@@ -2540,8 +2590,11 @@ void aft_check_callback(int fd, __a_unused const struct osl_object *query)
 {
        struct para_buffer pb = {
                .max_size = shm_get_shmmax(),
-               .private_data = &fd,
-               .max_size_handler = pass_buffer_as_shm
+               .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 audio file table...\n");
 
@@ -2549,7 +2602,7 @@ void aft_check_callback(int fd, __a_unused const struct osl_object *query)
                return;
        audio_file_loop(&pb, check_audio_file);
        if (pb.offset)
-               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
        free(pb.buf);
 }
 
index a4863a59a5bc3a426a08e6a69d333bded736d780..f50ad6a36e71a4f66a2747a6e7c4d08a4950089b 100644 (file)
@@ -113,6 +113,8 @@ static int alsa_init(struct private_alsa_write_data *pad,
                NULL);
        if (ret < 0 || buffer_time == 0)
                goto fail;
+       /* buffer at most 500 milliseconds */
+       buffer_time = PARA_MIN(buffer_time, 500U * 1000U);
        msg = "could not set buffer time";
        ret = snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams,
                &buffer_time, NULL);
@@ -356,7 +358,6 @@ void alsa_write_init(struct writer *w)
        w->pre_select = alsa_write_pre_select;
        w->post_select = alsa_write_post_select;
        w->parse_config_or_die = alsa_parse_config_or_die;
-       w->shutdown = NULL; /* nothing to do */
        w->free_config = alsa_free_config;
        w->help = (struct ggo_help) {
                .short_help = alsa_write_args_info_help,
index 6dc3192ccb710b12148bfc7a6417de3f07efd59c..93861ab63f50baf478bf74fe6f216fce6cf39f53 100644 (file)
@@ -375,7 +375,6 @@ void ao_write_init(struct writer *w)
        w->post_select = aow_post_select;
        w->parse_config_or_die = aow_parse_config_or_die;
        w->free_config = aow_free_config;
-       w->shutdown = NULL;
        w->help = (struct ggo_help) {
                .short_help = ao_write_args_info_help,
        };
index 19700944dff7c266db4c78a78e73eb79fda37713..638e22ea70fe5e5bb5ebac55f0e296db387adc5c 100644 (file)
@@ -16,6 +16,7 @@
 #include "afh.h"
 #include "afs.h"
 #include "ipc.h"
+#include "sideband.h"
 #include "command.h"
 
 static struct osl_table *attribute_table;
@@ -151,8 +152,11 @@ static void com_lsatt_callback(int fd, const struct osl_object *query)
                .flags = *(unsigned *) query->data,
                .pb = {
                        .max_size = shm_get_shmmax(),
-                       .private_data = &fd,
-                       .max_size_handler = pass_buffer_as_shm
+                       .private_data = &(struct afs_max_size_handler_data) {
+                               .fd = fd,
+                               .band = SBD_OUTPUT
+                       },
+                       .max_size_handler = afs_max_size_handler
                }
 
        };
@@ -172,7 +176,7 @@ static void com_lsatt_callback(int fd, const struct osl_object *query)
                pmd.pm_flags |= PM_REVERSE_LOOP;
        for_each_matching_row(&pmd);
        if (laad.pb.offset)
-               pass_buffer_as_shm(laad.pb.buf, laad.pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, laad.pb.buf, laad.pb.offset);
        free(laad.pb.buf);
 }
 
@@ -204,12 +208,14 @@ int com_lsatt(struct command_context *cc)
                }
        }
        ret = send_option_arg_callback_request(&options, cc->argc - i, cc->argv + i,
-               com_lsatt_callback, sc_send_result, cc);
-       if (!ret) {
-               if (cc->argc > 1)
-                       ret = sc_send_va_buffer(&cc->scc, "no matches\n");
-       } else if (ret < 0)
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+               com_lsatt_callback, afs_cb_result_handler, cc);
+       if (ret < 0)
+               send_strerror(cc, -ret);
+       else if (ret == 0 && cc->argc > 1)
+               ret = cc->use_sideband?
+                       send_sb_va(&cc->scc, SBD_ERROR_LOG, "no matches\n")
+               :
+                       sc_send_va_buffer(&cc->scc, "no matches\n");
        return ret;
 }
 
@@ -297,8 +303,11 @@ static void com_addatt_callback(int fd, const struct osl_object *query)
        int ret = 1, ret2 = 0;
        struct para_buffer pb = {
                .max_size = shm_get_shmmax(),
-               .private_data = &fd,
-               .max_size_handler = pass_buffer_as_shm
+               .private_data = &(struct afs_max_size_handler_data) {
+                       .fd = fd,
+                       .band = SBD_OUTPUT
+               },
+               .max_size_handler = afs_max_size_handler
        };
        size_t len;
 
@@ -354,7 +363,7 @@ out:
        if (ret < 0 && ret2 >= 0)
                para_printf(&pb, "%s: %s\n", p, para_strerror(-ret));
        if (pb.offset)
-               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
        free(pb.buf);
 }
 
@@ -365,9 +374,9 @@ int com_addatt(struct command_context *cc)
        if (cc->argc < 2)
                return -E_ATTR_SYNTAX;
        ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
-               com_addatt_callback, sc_send_result, cc);
+               com_addatt_callback, afs_cb_result_handler, cc);
        if (ret < 0)
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+               send_strerror(cc, -ret);
        return ret;
 }
 
@@ -380,8 +389,11 @@ static void com_mvatt_callback(int fd, const struct osl_object *query)
        struct osl_row *row;
        struct para_buffer pb = {
                .max_size = shm_get_shmmax(),
-               .private_data = &fd,
-               .max_size_handler = pass_buffer_as_shm
+               .private_data = &(struct afs_max_size_handler_data) {
+                       .fd = fd,
+                       .band = SBD_OUTPUT
+               },
+               .max_size_handler = afs_max_size_handler,
        };
        int ret;
 
@@ -397,7 +409,7 @@ out:
        else
                afs_event(ATTRIBUTE_RENAME, &pb, NULL);
        if (pb.offset)
-               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
        free(pb.buf);
 }
 
@@ -408,9 +420,9 @@ int com_mvatt(struct command_context *cc)
        if (cc->argc != 3)
                return -E_ATTR_SYNTAX;
        ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
-               com_mvatt_callback, sc_send_result, cc);
+               com_mvatt_callback, afs_cb_result_handler, cc);
        if (ret < 0)
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+               send_strerror(cc, -ret);
        return ret;
 }
 
@@ -450,8 +462,11 @@ static void com_rmatt_callback(int fd, const struct osl_object *query)
                .num_removed = 0,
                .pb = {
                        .max_size = shm_get_shmmax(),
-                       .private_data = &fd,
-                       .max_size_handler = pass_buffer_as_shm
+                       .private_data = &(struct afs_max_size_handler_data) {
+                               .fd = fd,
+                               .band = SBD_OUTPUT
+                       },
+                       .max_size_handler = afs_max_size_handler,
                }
        };
        int ret, ret2 = 0;
@@ -469,7 +484,7 @@ static void com_rmatt_callback(int fd, const struct osl_object *query)
        else if (!raad.num_removed)
                ret2 = para_printf(&raad.pb, "no match -- nothing removed\n");
        if (ret2 >= 0 && raad.pb.offset)
-               pass_buffer_as_shm(raad.pb.buf, raad.pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, raad.pb.buf, raad.pb.offset);
        free(raad.pb.buf);
 }
 
@@ -480,9 +495,9 @@ int com_rmatt(struct command_context *cc)
        if (cc->argc < 2)
                return -E_ATTR_SYNTAX;
        ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
-               com_rmatt_callback, sc_send_result, cc);
+               com_rmatt_callback, afs_cb_result_handler, cc);
        if (ret < 0)
-               sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+               send_strerror(cc, -ret);
        return ret;
 }
 
index 40603c4eff782b0f945ccab6f5193e99f78b7462..93bc8da37c6e8cf16f550b9e88b8c6f2c0c5ef95 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -1199,8 +1199,6 @@ static void status_post_select(struct sched *s, struct task *t)
                        kill_btrn(st->ct->btrn, &st->ct->task, -E_AUDIOD_OFF);
                        goto out;
                }
-               if (st->ct->task.error >= 0)
-                       goto out;
                close_stat_pipe();
                st->clock_diff_count = conf.clock_diff_count_arg;
                goto out;
@@ -1210,8 +1208,6 @@ static void status_post_select(struct sched *s, struct task *t)
                size_t sz;
                int ret;
                if (st->ct->task.error < 0) {
-                       if (st->ct->task.error >= 0)
-                               goto out;
                        close_stat_pipe();
                        goto out;
                }
index c58907448f12eeb44551b98d113c3ef52ba9979f..6c99d4c7d272e73e686bc8a9cd1d5832b3bdf9de 100644 (file)
@@ -1,9 +1,5 @@
-OF: audiod_command_list
+BN: audiod
 SF: audiod_command.c
-HC: prototypes for the audiod command handlers
-CC: array of audiod commands
-AT: audiod_command
-IN: list para sched audiod
 SN: list of audiod commands
 ---
 N: cycle
index 9d92d1c36505ee4599a507e3fbe7d0c0c8e2fc61..35b76b5f6fa277ea2aa55656f557b32ef36b5585 100644 (file)
@@ -29,6 +29,9 @@
 extern struct sched sched;
 extern char *stat_item_values[NUM_STAT_ITEMS];
 
+
+struct audiod_command audiod_cmds[] = {DEFINE_AUDIOD_CMD_ARRAY};
+
 /** Iterate over the array of all audiod commands. */
 #define FOR_EACH_COMMAND(c) for (c = 0; audiod_cmds[c].name; c++)
 
@@ -249,7 +252,7 @@ static int dump_commands(int fd)
  * to each individual command to close the fd if necessary.
  */
 
-int com_help(int fd, int argc, char **argv)
+static int com_help(int fd, int argc, char **argv)
 {
        int i, ret;
        char *buf;
@@ -284,7 +287,7 @@ out:
        return ret;
 }
 
-int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
 {
        char *tl = get_task_list(&sched);
        int ret = 1;
@@ -296,7 +299,7 @@ int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
        return ret;
 }
 
-int com_stat(int fd, int argc, char **argv)
+static int com_stat(int fd, int argc, char **argv)
 {
        int i, ret, parser_friendly = 0;
        uint64_t mask = 0;
@@ -342,39 +345,39 @@ int com_stat(int fd, int argc, char **argv)
        return ret;
 }
 
-int com_grab(int fd, int argc, char **argv)
+static int com_grab(int fd, int argc, char **argv)
 {
        return grab_client_new(fd, argc, argv, &sched);
 }
 
-__noreturn int com_term(int fd, __a_unused int argc, __a_unused char **argv)
+__noreturn static int com_term(int fd, __a_unused int argc, __a_unused char **argv)
 {
        close(fd);
        clean_exit(EXIT_SUCCESS, "terminating on user request");
 }
 
-int com_on(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_on(int fd, __a_unused int argc, __a_unused char **argv)
 {
        audiod_status = AUDIOD_ON;
        close(fd);
        return 1;
 }
 
-int com_off(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_off(int fd, __a_unused int argc, __a_unused char **argv)
 {
        audiod_status = AUDIOD_OFF;
        close(fd);
        return 1;
 }
 
-int com_sb(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_sb(int fd, __a_unused int argc, __a_unused char **argv)
 {
        audiod_status = AUDIOD_STANDBY;
        close(fd);
        return 1;
 }
 
-int com_cycle(int fd, int argc, char **argv)
+static int com_cycle(int fd, int argc, char **argv)
 {
        switch (audiod_status) {
                case  AUDIOD_ON:
index 5d6be3be2693a84f8107504f4a086dfa578c817b..a3393380e5e02801a4312c6f904c1ea25527aad9 100644 (file)
@@ -11,9 +11,9 @@
 
 /** Structure for bistream I/O. */
 struct getbit_context {
-       /* Start of the internal buffer. */
+       /** Start of the internal buffer. */
        const uint8_t *buffer;
-       /* End of the internal buffer. */
+       /** End of the internal buffer. */
        const uint8_t *buffer_end;
        /** Bit counter. */
        int index;
diff --git a/blob.c b/blob.c
index 409e348709de83de48090c0535f3aced79dda2c8..f51f4de0257ede9e9bfaea933d6e1077f823d1fd 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -18,6 +18,7 @@
 #include "afs.h"
 #include "ipc.h"
 #include "portable_io.h"
+#include "sideband.h"
 #include "command.h"
 
 /**
@@ -133,8 +134,11 @@ static void com_lsblob_callback(struct osl_table *table,
                .flags = *(uint32_t *)query->data,
                .pb = {
                        .max_size = shm_get_shmmax(),
-                       .private_data = &fd,
-                       .max_size_handler = pass_buffer_as_shm
+                       .private_data = &(struct afs_max_size_handler_data) {
+                               .fd = fd,
+                               .band = SBD_OUTPUT
+                       },
+                       .max_size_handler = afs_max_size_handler,
                }
        };
        struct pattern_match_data pmd = {
@@ -160,7 +164,7 @@ static void com_lsblob_callback(struct osl_table *table,
        else if (pmd.num_matches == 0 && pmd.patterns.size > 0)
                para_printf(&lbad.pb, "no matches\n");
        if (lbad.pb.offset)
-               pass_buffer_as_shm(lbad.pb.buf, lbad.pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, lbad.pb.buf, lbad.pb.offset);
        free(lbad.pb.buf);
 }
 
@@ -195,20 +199,20 @@ static int com_lsblob(callback_function *f, struct command_context *cc)
 //     if (argc > i)
 //             return -E_BLOB_SYNTAX;
        return send_option_arg_callback_request(&options, cc->argc - i,
-               cc->argv + i, f, sc_send_result, cc);
+               cc->argv + i, f, afs_cb_result_handler, cc);
 }
 
 static int cat_blob(struct osl_table *table, struct osl_row *row,
                __a_unused const char *name, void *data)
 {
-       int ret = 0, ret2;
+       int ret = 0, ret2, fd = *(int *)data;
        struct osl_object obj;
 
        ret = osl(osl_open_disk_object(table, row, BLOBCOL_DEF, &obj));
        if (ret < 0)
                return (ret == osl(-E_OSL_EMPTY))? 0 : ret;
        assert(obj.size > 0);
-       ret = pass_buffer_as_shm(obj.data, obj.size, data);
+       ret = pass_buffer_as_shm(fd, SBD_OUTPUT, obj.data, obj.size);
        ret2 = osl(osl_close_disk_object(&obj));
        return (ret < 0)? ret : ret2;
 }
@@ -228,7 +232,7 @@ static void com_catblob_callback(struct osl_table *table, int fd,
        for_each_matching_row(&pmd);
        if (pmd.num_matches == 0) {
                char err_msg[] = "no matches\n";
-               pass_buffer_as_shm(err_msg, sizeof(err_msg), &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, err_msg, sizeof(err_msg));
        }
 }
 
@@ -237,7 +241,7 @@ static int com_catblob(callback_function *f, struct command_context *cc)
        if (cc->argc < 2)
                return -E_BLOB_SYNTAX;
        return send_standard_callback_request(cc->argc - 1, cc->argv + 1, f,
-               sc_send_result, cc);
+               afs_cb_result_handler, cc);
 }
 
 /** Used for removing rows from a blob table. */
@@ -265,8 +269,11 @@ static void com_rmblob_callback(struct osl_table *table, int fd,
        struct rmblob_data rmbd = {
                .pb = {
                        .max_size = shm_get_shmmax(),
-                       .private_data = &fd,
-                       .max_size_handler = pass_buffer_as_shm
+                       .private_data = &(struct afs_max_size_handler_data) {
+                               .fd = fd,
+                               .band = SBD_OUTPUT
+                       },
+                       .max_size_handler = afs_max_size_handler,
                }
        };
        struct pattern_match_data pmd = {
@@ -292,7 +299,7 @@ static void com_rmblob_callback(struct osl_table *table, int fd,
        }
 out:
        if (ret2 >= 0 && rmbd.pb.offset)
-               pass_buffer_as_shm(rmbd.pb.buf, rmbd.pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, rmbd.pb.buf, rmbd.pb.offset);
        free(rmbd.pb.buf);
 }
 
@@ -301,7 +308,7 @@ static int com_rmblob(callback_function *f, struct command_context *cc)
        if (cc->argc < 2)
                return -E_MOOD_SYNTAX;
        return send_option_arg_callback_request(NULL, cc->argc - 1, cc->argv + 1, f,
-               sc_send_result, cc);
+               afs_cb_result_handler, cc);
 }
 
 static void com_addblob_callback(struct osl_table *table, __a_unused int fd,
@@ -432,7 +439,10 @@ static int stdin_command(struct command_context *cc, struct osl_object *arg_obj,
        struct osl_object query, stdin_obj;
        int ret;
 
-       ret = sc_send_buffer(&cc->scc, AWAITING_DATA_MSG);
+       if (cc->use_sideband)
+               ret = send_sb(&cc->scc, NULL, 0, SBD_AWAITING_DATA, false);
+       else
+               ret = sc_send_buffer(&cc->scc, AWAITING_DATA_MSG);
        if (ret < 0)
                return ret;
        ret = fd2buf(&cc->scc, max_len, &stdin_obj);
index d98c6327c9163d7018f0d94c1c612680205244d7..f0f3b2617be6f3554250cedfafbbcc92c38f54b7 100644 (file)
--- a/client.c
+++ b/client.c
@@ -603,14 +603,23 @@ int main(int argc, char *argv[])
                EMBRACE(.name = "stdout", .parent = ct->btrn));
        register_task(&sched, &svt);
        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 = ct->task.error;
+               }
+       }
 out:
+       if (ret < 0)
+               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
        client_close(ct);
        btr_free_node(sit.btrn);
        btr_free_node(sot.btrn);
-       if (ret < 0) {
-               /* can not use PARA_LOG here because ct is NULL */
-               fprintf(stderr, "%s\n", para_strerror(-ret));
-               return EXIT_FAILURE;
-       }
-       return EXIT_SUCCESS;
+       return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
 }
index 8490b72f8f598aa15798e0a4746fd9176370bebd..b841b7e1b6b6d0698a404887429840f2ea6e9a47 100644 (file)
--- a/client.h
+++ b/client.h
@@ -14,6 +14,8 @@ enum {
        CL_RECEIVED_WELCOME,
        /** Client sends the authentification request. */
        CL_SENT_AUTH,
+       /** Server sends encrypted challenge. */
+       CL_RECEIVED_CHALLENGE,
        /** Client solves the challenge and sends the result. */
        CL_SENT_CH_RESPONSE,
        /** Server accepts this authentication. */
@@ -32,6 +34,12 @@ struct client_task {
        int status;
        /** The file descriptor and the session keys. */
        struct stream_cipher_context scc;
+       /** True if this connections uses the sideband API. */
+       bool use_sideband;
+       /** The sideband context, ignored if \a use_sideband is false. */
+       struct sb_context *sbc;
+       /** The hash value of the decrypted challenge. */
+       unsigned char *challenge_hash;
        /** The configuration (including the command). */
        struct client_args_info conf;
        /** The config file for client options. */
@@ -44,6 +52,8 @@ struct client_task {
        struct task task;
        /** The buffer tree node of the client task. */
        struct btr_node *btrn;
+       /** List of features supported by the server. */
+       char **features;
 };
 
 void client_disconnect(struct client_task *ct);
index 5bd2241b8d933fb69309560d5b2056ed6438cdd9..649a1b4fdf49579e653d7b6cd6d9c977524e6171 100644 (file)
@@ -17,6 +17,7 @@
 #include "crypt.h"
 #include "net.h"
 #include "fd.h"
+#include "sideband.h"
 #include "string.h"
 #include "client.cmdline.h"
 #include "client.h"
@@ -42,6 +43,7 @@ void client_disconnect(struct client_task *ct)
                return;
        if (ct->scc.fd >= 0)
                close(ct->scc.fd);
+       free_argv(ct->features);
        sc_free(ct->scc.recv);
        ct->scc.recv = NULL;
        sc_free(ct->scc.send);
@@ -66,6 +68,8 @@ void client_close(struct client_task *ct)
        free(ct->config_file);
        free(ct->key_file);
        client_cmdline_parser_free(&ct->conf);
+       free(ct->challenge_hash);
+       sb_free(ct->sbc);
        free(ct);
 }
 
@@ -101,6 +105,7 @@ static void client_pre_select(struct sched *s, struct task *t)
 
        case CL_RECEIVED_WELCOME:
        case CL_RECEIVED_PROCEED:
+       case CL_RECEIVED_CHALLENGE:
                para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
                return;
 
@@ -152,6 +157,162 @@ static int client_recv_buffer(struct client_task *ct, fd_set *rfds,
        return 0;
 }
 
+static int send_sb(struct client_task *ct, 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) {
+               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);
+       }
+       ret = sb_get_send_buffers(ct->sbc, iov);
+       ret = xwritev(fd, iov, ret);
+       if (ret < 0) {
+               sb_free(ct->sbc);
+               ct->sbc = NULL;
+               return ret;
+       }
+       if (sb_sent(ct->sbc, ret)) {
+               ct->sbc = NULL;
+               return 1;
+       }
+       return 0;
+}
+
+static int recv_sb(struct client_task *ct, fd_set *rfds,
+               struct sb_buffer *result)
+{
+       int ret;
+       size_t n;
+       sb_transformation trafo;
+       void *trafo_context;
+       struct iovec iov;
+
+       if (!FD_ISSET(ct->scc.fd, rfds))
+               return 0;
+       if (ct->status < CL_SENT_CH_RESPONSE)
+               trafo = trafo_context = NULL;
+       else {
+               trafo = sc_trafo;
+               trafo_context = ct->scc.recv;
+       }
+       if (!ct->sbc)
+               ct->sbc = sb_new_recv(0, trafo, trafo_context);
+again:
+       sb_get_recv_buffer(ct->sbc, &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;
+               return ret;
+       }
+       if (n == 0)
+               return 0;
+       if (!sb_received(ct->sbc, n, result))
+               goto again;
+       ct->sbc = NULL;
+       return 1;
+}
+
+
+static char **parse_features(char *buf)
+{
+       int i;
+       const char id[] = "\nFeatures: ";
+       char *p, *q, **features;
+
+       p = strstr(buf, id);
+       if (!p)
+               return NULL;
+       p += strlen(id);
+       q = strchr(p, '\n');
+       if (!q)
+               return NULL;
+       *q = '\0';
+       create_argv(p, ",", &features);
+       for (i = 0; features[i]; i++)
+               PARA_INFO_LOG("server feature: %s\n", features[i]);
+       return features;
+}
+
+static int dispatch_sbb(struct client_task *ct, struct sb_buffer *sbb)
+{
+       int ret;
+       const char *designator[] = {SB_DESIGNATORS_ARRAY};
+
+       if (!sbb)
+               return 0;
+       if (sbb->band < NUM_SB_DESIGNATORS)
+               PARA_DEBUG_LOG("band: %s\n", designator[sbb->band]);
+
+       switch (sbb->band) {
+       case SBD_OUTPUT:
+               if (iov_valid(&sbb->iov))
+                       btr_add_output(sbb->iov.iov_base, sbb->iov.iov_len,
+                               ct->btrn);
+               ret = 1;
+               goto out;
+       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:
+               if (iov_valid(&sbb->iov)) {
+                       int ll = sbb->band - SBD_DEBUG_LOG;
+                       para_log(ll, "remote: %s", (char *)sbb->iov.iov_base);
+               }
+               ret = 1;
+               goto deallocate;
+       case SBD_EXIT__SUCCESS:
+               ret = -E_SERVER_CMD_SUCCESS;
+               goto deallocate;
+       case SBD_EXIT__FAILURE:
+               ret = -E_SERVER_CMD_FAILURE;
+               goto deallocate;
+       default:
+               PARA_ERROR_LOG("invalid band %d\n", sbb->band);
+               ret = -E_BAD_BAND;
+               goto deallocate;
+       }
+deallocate:
+       free(sbb->iov.iov_base);
+out:
+       sbb->iov.iov_base = NULL;
+       return ret;
+}
+
+static bool has_feature(const char *feature, struct client_task *ct)
+{
+       return find_arg(feature, ct->features) >= 0? true : false;
+}
+
+static int send_sb_command(struct client_task *ct)
+{
+       int i;
+       char *command, *p;
+       size_t len = 0;
+
+       if (ct->sbc)
+               return send_sb(ct, NULL, 0, 0, false);
+
+       for (i = 0; i < ct->conf.inputs_num; i++)
+               len += strlen(ct->conf.inputs[i]) + 1;
+       p = command = para_malloc(len);
+       for (i = 0; i < ct->conf.inputs_num; i++) {
+               strcpy(p, ct->conf.inputs[i]);
+               p += strlen(ct->conf.inputs[i]) + 1;
+       }
+       PARA_DEBUG_LOG("--> %s\n", command);
+       return send_sb(ct, command, len, SBD_COMMAND, false);
+}
+
 /**
  * The post select hook for client commands.
  *
@@ -181,13 +342,18 @@ static void client_post_select(struct sched *s, struct task *t)
                ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
                if (ret < 0 || n == 0)
                        goto out;
+               ct->features = parse_features(buf);
                ct->status = CL_RECEIVED_WELCOME;
                return;
        case CL_RECEIVED_WELCOME: /* send auth command */
-               sprintf(buf, AUTH_REQUEST_MSG "%s", ct->user);
-               PARA_INFO_LOG("--> %s\n", buf);
                if (!FD_ISSET(ct->scc.fd, &s->wfds))
                        return;
+               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);
+               PARA_INFO_LOG("--> %s\n", buf);
                ret = write_buffer(ct->scc.fd, buf);
                if (ret < 0)
                        goto out;
@@ -202,30 +368,73 @@ static void client_post_select(struct sched *s, struct task *t)
                /* decrypted challenge/session key buffer */
                unsigned char crypt_buf[1024];
                /* the SHA1 of the decrypted challenge */
-               unsigned char challenge_hash[HASH_SIZE];
 
-               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;
-               hash_function((char *)crypt_buf, CHALLENGE_SIZE, challenge_hash);
+               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);
+                       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;
+               }
+               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);
                ct->scc.recv = sc_new(crypt_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
                        SESSION_KEY_LEN);
-               hash_to_asc(challenge_hash, buf);
+               hash_to_asc(ct->challenge_hash, buf);
                PARA_INFO_LOG("--> %s\n", buf);
-               ret = write_all(ct->scc.fd, (char *)challenge_hash, HASH_SIZE);
-               if (ret < 0)
-                       goto out;
-               ct->status = CL_SENT_CH_RESPONSE;
+               ct->status = CL_RECEIVED_CHALLENGE;
                return;
                }
+       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;
+               }
+               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;
@@ -244,6 +453,13 @@ static void client_post_select(struct sched *s, struct task *t)
                char *command = NULL;
                if (!FD_ISSET(ct->scc.fd, &s->wfds))
                        return;
+               if (ct->use_sideband) {
+                       ret = send_sb_command(ct);
+                       if (ret <= 0)
+                               goto out;
+                       ct->status = CL_SENT_COMMAND;
+                       return;
+               }
                for (i = 0; i < ct->conf.inputs_num; i++) {
                        char *tmp = command;
                        command = make_message("%s\n%s", command?
@@ -262,6 +478,20 @@ static void client_post_select(struct sched *s, struct task *t)
        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;
+                       }
+                       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);
@@ -309,6 +539,13 @@ static void client_post_select(struct sched *s, struct task *t)
                 */
                if (!FD_ISSET(ct->scc.fd, &s->rfds))
                        return;
+               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;
+               }
                buf2 = para_malloc(CLIENT_BUFSIZE);
                ret = client_recv_buffer(ct, &s->rfds, buf2, CLIENT_BUFSIZE, &n);
                if (n > 0) {
@@ -322,7 +559,8 @@ static void client_post_select(struct sched *s, struct task *t)
 out:
        t->error = ret;
        if (ret < 0) {
-               if (ret != -E_SERVER_EOF && ret != -E_BTR_EOF)
+               if (!ct->use_sideband && ret != -E_SERVER_EOF &&
+                               ret != -E_BTR_EOF && ret != -E_EOF)
                        PARA_ERROR_LOG("%s\n", para_strerror(-t->error));
                btr_remove_node(btrn);
        }
index f520e3ea0ac7ff3d210d0d9c30b0a510302dba46..f9bace34146e28ccaf7b0632e0fa34ede51de8aa 100644 (file)
--- a/command.c
+++ b/command.c
@@ -15,6 +15,7 @@
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
+#include "sideband.h"
 #include "command.h"
 #include "server.cmdline.h"
 #include "string.h"
@@ -35,6 +36,9 @@
 #include "signal.h"
 #include "version.h"
 
+struct server_command afs_cmds[] = {DEFINE_AFS_CMD_ARRAY};
+struct server_command server_cmds[] = {DEFINE_SERVER_CMD_ARRAY};
+
 /** Commands including options must be shorter than this. */
 #define MAX_COMMAND_LEN 32768
 
@@ -101,7 +105,8 @@ static char *vss_get_status_flags(unsigned int flags)
        return msg;
 }
 
-static char *get_status(struct misc_meta_data *nmmd, int parser_friendly)
+static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly,
+               char **result)
 {
        char mtime[30] = "";
        char *status, *flags; /* vss status info */
@@ -141,7 +146,8 @@ static char *get_status(struct misc_meta_data *nmmd, int parser_friendly)
        free(flags);
        free(status);
        free(ut);
-       return b.buf;
+       *result = b.buf;
+       return b.offset;
 }
 
 static int check_sender_args(int argc, char * const * argv, struct sender_command_data *scd)
@@ -191,19 +197,142 @@ static int check_sender_args(int argc, char * const * argv, struct sender_comman
        return 1;
 }
 
-int com_sender(struct command_context *cc)
+/**
+ * Send a sideband packet through a blocking file descriptor.
+ *
+ * \param scc fd and crypto keys.
+ * \param buf The buffer to send.
+ * \param numbytes The size of \a buf.
+ * \param band The sideband designator of this packet.
+ * \param dont_free If true, never deallocate \a buf.
+ *
+ * The nonblock flag must be disabled for the file descriptor given by \a scc.
+ *
+ * Stream cipher encryption is automatically activated if neccessary via the
+ * sideband transformation, depending on the value of \a band.
+ *
+ * \return Standard.
+ *
+ * \sa \ref send_sb_va().
+ */
+int send_sb(struct stream_cipher_context *scc, void *buf, size_t numbytes,
+               int band, bool dont_free)
 {
-       int i, ret;
+       int ret;
+       struct sb_context *sbc;
+       struct iovec iov[2];
+       struct sb_buffer sbb = SBB_INIT(band, buf, numbytes);
+       sb_transformation trafo = band < SBD_PROCEED? NULL : sc_trafo;
+
+       sbc = sb_new_send(&sbb, dont_free, trafo, scc->send);
+       do {
+               ret = sb_get_send_buffers(sbc, iov);
+               ret = xwritev(scc->fd, iov, ret);
+               if (ret < 0)
+                       goto fail;
+       } while (sb_sent(sbc, ret) == false);
+       return 1;
+fail:
+       sb_free(sbc);
+       return ret;
+}
+
+/**
+ * Create a variable sized buffer and send it as a sideband packet.
+ *
+ * \param scc Passed to \ref send_sb.
+ * \param band See \ref send_sb.
+ * \param fmt The format string.
+ *
+ * \return The return value of the underlying call to \ref send_sb.
+ */
+__printf_3_4 int send_sb_va(struct stream_cipher_context *scc, int band,
+               const char *fmt, ...)
+{
+       va_list ap;
+       char *msg;
+       int ret;
+
+       va_start(ap, fmt);
+       ret = xvasprintf(&msg, fmt, ap);
+       va_end(ap);
+       return send_sb(scc, msg, ret, band, false);
+}
+
+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));
+}
+
+/**
+ * Send a sideband packet through a blocking file descriptor.
+ *
+ * \param scc fd and crypto keys.
+ * \param expected_band The expected band designator.
+ * \param max_size Passed to \ref sb_new_recv().
+ * \param result Body of the sideband packet is returned here.
+ *
+ * If \a expected_band is not \p SBD_ANY, the band designator of the received
+ * sideband packet is compared to \a expected_band and a mismatch is considered
+ * an error.
+ *
+ * \return Standard.
+ */
+int recv_sb(struct stream_cipher_context *scc,
+               enum sb_designator expected_band,
+               size_t max_size, struct iovec *result)
+{
+       int ret;
+       struct sb_context *sbc;
+       struct iovec iov;
+       struct sb_buffer sbb;
+       sb_transformation trafo;
+
+       trafo = expected_band != SBD_ANY && expected_band < SBD_PROCEED?
+               NULL : sc_trafo;
+       sbc = sb_new_recv(max_size, trafo, scc->recv);
+       for (;;) {
+               sb_get_recv_buffer(sbc, &iov);
+               ret = recv_bin_buffer(scc->fd, iov.iov_base, iov.iov_len);
+               if (ret == 0)
+                       ret = -E_EOF;
+               if (ret < 0)
+                       goto fail;
+               ret = sb_received(sbc, ret, &sbb);
+               if (ret < 0)
+                       goto fail;
+               if (ret > 0)
+                       break;
+       }
+       ret = -E_BAD_BAND;
+       if (expected_band != SBD_ANY && sbb.band != expected_band)
+               goto fail;
+       *result = sbb.iov;
+       return 1;
+fail:
+       sb_free(sbc);
+       return ret;
+}
+
+static int com_sender(struct command_context *cc)
+{
+       int i, ret = 0;
        char *msg = NULL;
        struct sender_command_data scd;
 
        if (cc->argc < 2) {
                for (i = 0; senders[i].name; i++) {
-                       char *tmp = make_message("%s%s\n",
-                               msg? msg : "", senders[i].name);
+                       char *tmp;
+                       ret = xasprintf(&tmp, "%s%s\n", msg? msg : "",
+                               senders[i].name);
                        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;
@@ -213,6 +342,9 @@ int com_sender(struct command_context *cc)
                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;
@@ -242,11 +374,10 @@ int com_sender(struct command_context *cc)
 }
 
 /* server info */
-int com_si(struct command_context *cc)
+static int com_si(struct command_context *cc)
 {
        int i, ret;
-       char *ut;
-       char *sender_info = NULL;
+       char *msg, *ut, *sender_info = NULL;
 
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
@@ -257,7 +388,7 @@ int com_si(struct command_context *cc)
                free(info);
        }
        ut = get_server_uptime_str(now);
-       ret = sc_send_va_buffer(&cc->scc, "version: " GIT_VERSION "\n"
+       ret = xasprintf(&msg, "version: " GIT_VERSION "\n"
                "up: %s\nplayed: %u\n"
                "server_pid: %d\n"
                "afs_pid: %d\n"
@@ -278,18 +409,27 @@ int com_si(struct command_context *cc)
        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;
 }
 
 /* version */
-int com_version(struct command_context *cc)
+static int com_version(struct command_context *cc)
 {
+       char *msg;
+       size_t len;
+
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
-       return sc_send_buffer(&cc->scc, VERSION_TEXT("server")
-               "built: " BUILD_DATE "\n"
-               UNAME_RS ", " CC_VERSION "\n"
-       );
+       msg = VERSION_TEXT("server") "built: " BUILD_DATE "\n" UNAME_RS
+               ", " CC_VERSION "\n";
+       len = strlen(msg);
+       if (cc->use_sideband)
+               return send_sb(&cc->scc, msg, len, SBD_OUTPUT, true);
+       return sc_send_bin_buffer(&cc->scc, msg, len);
 }
 
 #define EMPTY_STATUS_ITEMS \
@@ -325,10 +465,16 @@ int com_version(struct command_context *cc)
  *
  * This is used by vss when currently no audio file is open.
  */
-static char *empty_status_items(int parser_friendly)
+static unsigned empty_status_items(int parser_friendly, char **result)
 {
+       static char *esi;
+       static unsigned len;
+
+       if (esi)
+               goto out;
+
        if (parser_friendly)
-               return make_message(
+               len = xasprintf(&esi,
                        #define ITEM(x) "0004 %02x:\n"
                        EMPTY_STATUS_ITEMS
                        #undef ITEM
@@ -336,23 +482,27 @@ static char *empty_status_items(int parser_friendly)
                        EMPTY_STATUS_ITEMS
                        #undef ITEM
                );
-       return make_message(
-               #define ITEM(x) "%s:\n"
-               EMPTY_STATUS_ITEMS
-               #undef ITEM
-               #define ITEM(x) ,status_item_list[SI_ ## x]
-               EMPTY_STATUS_ITEMS
-               #undef ITEM
-       );
+       else
+               len = xasprintf(&esi,
+                       #define ITEM(x) "%s:\n"
+                       EMPTY_STATUS_ITEMS
+                       #undef ITEM
+                       #define ITEM(x) ,status_item_list[SI_ ## x]
+                       EMPTY_STATUS_ITEMS
+                       #undef ITEM
+               );
+out:
+       *result = esi;
+       return len;
 }
 #undef EMPTY_STATUS_ITEMS
 
 /* stat */
-int com_stat(struct command_context *cc)
+static int com_stat(struct command_context *cc)
 {
        int i, ret;
        struct misc_meta_data tmp, *nmmd = &tmp;
-       char *s;
+       char *s, *esi = NULL;
        int32_t num = 0;
        int parser_friendly = 0;
 
@@ -382,16 +532,22 @@ int com_stat(struct command_context *cc)
                return -E_COMMAND_SYNTAX;
        for (;;) {
                mmd_dup(nmmd);
-               s = get_status(nmmd, parser_friendly);
-               ret = sc_send_buffer(&cc->scc, s);
-               free(s);
+               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);
+               }
                if (ret < 0)
                        goto out;
                if (nmmd->vss_status_flags & VSS_NEXT) {
-                       static char *esi;
-                       if (!esi)
-                               esi = empty_status_items(parser_friendly);
-                       ret = sc_send_buffer(&cc->scc, esi);
+                       ret = empty_status_items(parser_friendly, &esi);
+                       if (cc->use_sideband)
+                               ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT,
+                                       true);
+                       else
+                               ret = sc_send_bin_buffer(&cc->scc, esi, ret);
                        if (ret < 0)
                                goto out;
                } else
@@ -400,29 +556,34 @@ int com_stat(struct command_context *cc)
                if (num > 0 && !--num)
                        goto out;
                sleep(50);
+               ret = -E_SERVER_CRASH;
                if (getppid() == 1)
-                       return -E_SERVER_CRASH;
+                       goto out;
        }
 out:
+       free(esi);
        return ret;
 }
 
-static int send_list_of_commands(struct stream_cipher_context *scc, struct server_command *cmd,
+static int send_list_of_commands(struct command_context *cc, struct server_command *cmd,
                const char *handler)
 {
-       int ret, i;
+       int ret;
+       char *msg = NULL;
 
-       for (i = 1; cmd->name; cmd++, i++) {
-               char *perms = cmd_perms_itohuman(cmd->perms);
-               ret = sc_send_va_buffer(scc, "%s\t%s\t%s\t%s\n", cmd->name,
-                       handler,
-                       perms,
-                       cmd->description);
+       for (; cmd->name; cmd++) {
+               char *tmp, *perms = cmd_perms_itohuman(cmd->perms);
+               tmp = make_message("%s\t%s\t%s\t%s\n", cmd->name, handler,
+                       perms, cmd->description);
                free(perms);
-               if (ret < 0)
-                       return ret;
+               msg = para_strcat(msg, tmp);
+               free(tmp);
        }
-       return 1;
+       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;
 }
 
 /* returns string that must be freed by the caller */
@@ -447,25 +608,24 @@ static struct server_command *get_cmd_ptr(const char *name, char **handler)
 }
 
 /* help */
-int com_help(struct command_context *cc)
+static int com_help(struct command_context *cc)
 {
        struct server_command *cmd;
-       char *perms, *handler;
+       char *perms, *handler, *buf;
        int ret;
 
        if (cc->argc < 2) {
                /* no argument given, print list of commands */
-               if ((ret = send_list_of_commands(&cc->scc, server_cmds, "server")) < 0)
+               if ((ret = send_list_of_commands(cc, server_cmds, "server")) < 0)
                        return ret;
-               return send_list_of_commands(&cc->scc, afs_cmds, "afs");
+               return send_list_of_commands(cc, afs_cmds, "afs");
        }
        /* argument given for help */
        cmd = get_cmd_ptr(cc->argv[1], &handler);
        if (!cmd)
                return -E_BAD_CMD;
        perms = cmd_perms_itohuman(cmd->perms);
-       ret = sc_send_va_buffer(&cc->scc,
-               "%s - %s\n\n"
+       ret = xasprintf(&buf, "%s - %s\n\n"
                "handler: %s\n"
                "permissions: %s\n"
                "usage: %s\n\n"
@@ -479,11 +639,15 @@ int com_help(struct command_context *cc)
        );
        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;
 }
 
 /* hup */
-int com_hup(struct command_context *cc)
+static int com_hup(struct command_context *cc)
 {
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
@@ -492,7 +656,7 @@ int com_hup(struct command_context *cc)
 }
 
 /* term */
-int com_term(struct command_context *cc)
+static int com_term(struct command_context *cc)
 {
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
@@ -500,7 +664,7 @@ int com_term(struct command_context *cc)
        return 1;
 }
 
-int com_play(struct command_context *cc)
+static int com_play(struct command_context *cc)
 {
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
@@ -512,7 +676,7 @@ int com_play(struct command_context *cc)
 }
 
 /* stop */
-int com_stop(struct command_context *cc)
+static int com_stop(struct command_context *cc)
 {
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
@@ -525,7 +689,7 @@ int com_stop(struct command_context *cc)
 }
 
 /* pause */
-int com_pause(struct command_context *cc)
+static int com_pause(struct command_context *cc)
 {
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
@@ -540,7 +704,7 @@ int com_pause(struct command_context *cc)
 }
 
 /* next */
-int com_next(struct command_context *cc)
+static int com_next(struct command_context *cc)
 {
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
@@ -552,7 +716,7 @@ int com_next(struct command_context *cc)
 }
 
 /* nomore */
-int com_nomore(struct command_context *cc)
+static int com_nomore(struct command_context *cc)
 {
        if (cc->argc != 1)
                return -E_COMMAND_SYNTAX;
@@ -564,7 +728,7 @@ int com_nomore(struct command_context *cc)
 }
 
 /* ff */
-int com_ff(struct command_context *cc)
+static int com_ff(struct command_context *cc)
 {
        long promille;
        int ret, backwards = 0;
@@ -603,7 +767,7 @@ out:
 }
 
 /* jmp */
-int com_jmp(struct command_context *cc)
+static int com_jmp(struct command_context *cc)
 {
        long unsigned int i;
        int ret;
@@ -701,6 +865,80 @@ static void reset_signals(void)
        para_sigaction(SIGHUP, SIG_DFL);
 }
 
+static int parse_auth_request(char *buf, int len, struct user **u,
+               bool *use_sideband)
+{
+       int ret;
+       char *p, *username, **features = NULL;
+       size_t auth_rq_len = strlen(AUTH_REQUEST_MSG);
+
+       *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)
+               return -E_AUTH_REQUEST;
+       username = buf + auth_rq_len;
+       p = strchr(username, ' ');
+       if (p) {
+               int i;
+               if (p == username)
+                       return -E_AUTH_REQUEST;
+               *p = '\0';
+               p++;
+               create_argv(p, ",", &features);
+               for (i = 0; features[i]; i++) {
+                       if (strcmp(features[i], "sideband") == 0)
+                               *use_sideband = 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");
+       *u = lookup_user(username);
+       ret = 1;
+out:
+       free_argv(features);
+       return ret;
+}
+
+#define HANDSHAKE_BUFSIZE 4096
+
+static int parse_sb_command(struct command_context *cc, struct iovec *iov)
+{
+       int ret, i;
+       char *p, *end;
+
+       ret = -E_BAD_CMD;
+       if (iov->iov_base == NULL || iov->iov_len == 0)
+               goto out;
+       p = iov->iov_base;
+       p[iov->iov_len - 1] = '\0'; /* just to be sure */
+       cc->cmd = get_cmd_ptr(p, NULL);
+       if (!cc->cmd)
+               goto out;
+       ret = check_perms(cc->u->perms, cc->cmd);
+       if (ret < 0)
+               goto out;
+       end = iov->iov_base + iov->iov_len;
+       for (i = 0, p = iov->iov_base; p < end; i++)
+               p += strlen(p) + 1;
+       cc->argc = i;
+       cc->argv = para_malloc((cc->argc + 1) * sizeof(char *));
+       for (i = 0, p = iov->iov_base; p < end; i++) {
+               cc->argv[i] = para_strdup(p);
+               p += strlen(p) + 1;
+       }
+       cc->argv[cc->argc] = NULL;
+       ret = cc->argc;
+out:
+       free(iov->iov_base);
+       return ret;
+}
+
 /**
  * Perform user authentication and execute a command.
  *
@@ -731,10 +969,9 @@ static void reset_signals(void)
 __noreturn void handle_connect(int fd, const char *peername)
 {
        int ret;
-       char buf[4096];
        unsigned char rand_buf[CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
        unsigned char challenge_hash[HASH_SIZE];
-       char *p, *command = NULL;
+       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;
 
@@ -746,19 +983,17 @@ __noreturn void handle_connect(int fd, const char *peername)
                goto net_err;
        /* send Welcome message */
        ret = write_va_buffer(fd, "This is para_server, version "
-               PACKAGE_VERSION  ".\n" );
+               PACKAGE_VERSION  ".\n"
+               "Features: sideband,foo\n"
+       );
        if (ret < 0)
                goto net_err;
        /* recv auth request line */
-       ret = recv_buffer(fd, buf, sizeof(buf));
+       ret = recv_buffer(fd, buf, HANDSHAKE_BUFSIZE);
        if (ret < 0)
                goto net_err;
-       if (ret < 10) {
-               ret = -E_AUTH_REQUEST;
-               goto net_err;
-       }
-       ret = -E_AUTH_REQUEST;
-       if (strncmp(buf, AUTH_REQUEST_MSG, strlen(AUTH_REQUEST_MSG)))
+       ret = parse_auth_request(buf, ret, &cc->u, &cc->use_sideband);
+       if (ret < 0)
                goto net_err;
        p = buf + strlen(AUTH_REQUEST_MSG);
        PARA_DEBUG_LOG("received auth request for user %s\n", p);
@@ -781,15 +1016,29 @@ __noreturn void handle_connect(int fd, const char *peername)
        }
        PARA_DEBUG_LOG("sending %u byte challenge + rc4 keys (%zu bytes)\n",
                CHALLENGE_SIZE, numbytes);
-       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;
-       PARA_DEBUG_LOG("received %d bytes challenge response\n", ret);
+       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;
+       }
+       PARA_DEBUG_LOG("received %zu bytes challenge response\n", numbytes);
        ret = -E_BAD_USER;
        if (!cc->u)
                goto net_err;
@@ -809,27 +1058,41 @@ __noreturn void handle_connect(int fd, const char *peername)
        /* 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);
-       ret = sc_send_buffer(&cc->scc, PROCEED_MSG);
-       if (ret < 0)
-               goto net_err;
-       ret = read_command(&cc->scc, &command);
-       if (ret == -E_COMMAND_SYNTAX)
-               goto err_out;
+       if (cc->use_sideband)
+               ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false);
+       else
+               ret = sc_send_buffer(&cc->scc, PROCEED_MSG);
        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;
+       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;
+       }
        PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cc->cmd->name,
                cc->u->name, peername);
        ret = cc->cmd->handler(cc);
@@ -840,17 +1103,24 @@ __noreturn void handle_connect(int fd, const char *peername)
        if (ret >= 0)
                goto out;
 err_out:
-       sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
+       if (send_strerror(cc, -ret) >= 0 && cc->use_sideband)
+               send_sb(&cc->scc, NULL, 0, SBD_EXIT__FAILURE, true);
 net_err:
        PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
 out:
+       free(buf);
        free(command);
-       sc_free(cc->scc.recv);
-       sc_free(cc->scc.send);
        mutex_lock(mmd_mutex);
        if (cc->cmd && (cc->cmd->perms & AFS_WRITE) && ret >= 0)
                mmd->events++;
        mmd->active_connections--;
        mutex_unlock(mmd_mutex);
+       if (ret >= 0 && cc->use_sideband) {
+               ret = send_sb(&cc->scc, NULL, 0, SBD_EXIT__SUCCESS, true);
+               if (ret < 0)
+                       PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
+       }
+       sc_free(cc->scc.recv);
+       sc_free(cc->scc.send);
        exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
 }
index 05510cc08aa33d50a104f71a05d353ad3027e7d2..e4159e6bda00131bf4a1ef6b5a46627c9f7d88b1 100644 (file)
--- a/command.h
+++ b/command.h
@@ -14,6 +14,8 @@ struct command_context {
        struct server_command *cmd;
        /** File descriptor and crypto keys. */
        struct stream_cipher_context scc;
+       /** Whether to use the sideband API for this command. */
+       bool use_sideband;
 };
 
 /**
@@ -33,3 +35,12 @@ struct server_command {
        /** The long help text. */
        const char *help;
 };
+
+int send_sb(struct stream_cipher_context *scc, void *buf, size_t numbytes,
+               int band, bool dont_free);
+__printf_3_4 int send_sb_va(struct stream_cipher_context *scc, int band,
+               const char *fmt, ...);
+int send_strerror(struct command_context *cc, int err);
+int recv_sb(struct stream_cipher_context *scc,
+               enum sb_designator expected_band,
+               size_t max_size, struct iovec *result);
index 76b3445fc1557b607ef72ae9408dec38f8a54507..b37c5b03ea5516c2bad778fd82e35a9fa6da2131 100755 (executable)
@@ -1,6 +1,5 @@
 #!/usr/bin/env bash
 
-
 read_header()
 {
        local key value i
@@ -10,36 +9,12 @@ read_header()
                ---)
                        break
                        ;;
-               HC:)
-                       header_comment="$value"
-                       ;;
-               CC:)
-                       c_file_comment="$value"
-                       ;;
-               OF:)
-                       output_file="$value"
-                       array_name=${value%command_list}cmds
+               BN:)
+                       base_name="$value"
                        ;;
                SF:)
                        source_files="$value"
                        ;;
-               AT:)
-                       array_type="$value"
-                       ;;
-               SI:)
-                       for i in $value; do
-                               system_includes="$system_includes
-#include <$i.h>"
-                       done
-                       ;;
-               IN:)
-                       for i in $value; do
-                               includes="$includes
-#include \"$i.h\""
-                       done
-                       includes="$includes
-#include \"$output_file.h\""
-                       ;;
                SN:)
                        section_name="$value"
                        ;;
@@ -47,11 +22,6 @@ read_header()
                        template_members="$value"
                esac
        done
-       if test -z "$header_comment" -o -z "$c_file_comment" \
-                       -o -z "$output_file"; then
-               echo "header error" 1&>2
-               exit 1
-       fi
 }
 
 read_one_command()
@@ -193,56 +163,53 @@ com_man()
        done
 }
 
-dump_proto()
+make_proto()
 {
-       local source_file match=""
-
-       echo '/**'
-       echo " * $desc_txt"
-       echo ' *'
-       echo " * Usage: $usage_txt"
-       echo ' * '
-       echo "$help_txt" | sed -e 's/^/ * /g'
-       echo ' */'
+       local regex='\(__noreturn \)*\(static \)*int com_'
+       local source_file match="" all_commands CR='
+'
        if test -n "$prototype"; then
-               echo "$prototype"
-               echo
+               result="$prototype$CR"
                return
        fi
+       all_commands="$(cat $source_files | grep "$regex")"
+       result=
        for source_file in $source_files; do
-               match=$(grep "^\(__noreturn \)*int com_$name_txt(" $source_file | head -n 1 | sed -e 's/$/;/1')
+               match=$(grep "$regex$name_txt(" <<< "$all_commands" | head -n 1 | sed -e 's/$/;/1')
                if test -n "$match"; then
-                       echo "$match"
+                       result="$result$match$CR"
                        break
                fi
        done
-       echo
 }
 
-dump_array_member()
+make_array_member()
 {
-       local TAB='     '
-       echo '{'
-       echo ".name = \"$name_txt\","
-       echo ".handler = com_$name_txt,"
+       local TAB='     ' CR='
+'
+       local tmp
+
+       result="{.name = \"$name_txt\", .handler = com_$name_txt, "
        if test -n "$perms_txt"; then
-               echo ".perms = $perms_txt,"
+               result="$result .perms = $perms_txt,"
        fi
-       echo ".description = \"$desc_txt\","
-       echo ".usage = \"$usage_txt\","
-       echo ".help = "
-       printf "%s\n" "$help_txt" | sed -e 's/^/\"/g' -e 's/$/\\n\"/g' \
-               -e "s/$TAB/\\\t/g"
-       echo '},'
+       result="$result.description = \"$desc_txt\", .usage = \"$usage_txt\", \\$CR .help = "
+       tmp="$(printf "%s\n" "$help_txt" | sed -e 's/^/\"/g' -e 's/$/\\n\"/g' \
+               -e "s/$TAB/\\\t/g" -e's/$/\\/g')"
+       result="$result$tmp$CR}, \\$CR"
 }
 
-dump_completion()
+make_completion()
 {
-       echo "  {.name = \"$name_txt\", .completer = ${name_txt}_completer}, \\"
+       local CR='
+'
+       result="  {.name = \"$name_txt\", .completer = ${name_txt}_completer}, \\$CR"
 }
 
 template_loop()
 {
+       local loop_result=
+
        local t_name="$name_txt"
        local t_perms="$perms_txt"
        local t_desc="$desc_txt"
@@ -251,48 +218,31 @@ template_loop()
        local t_source_files="$source_files"
        local member
        for member in $template_members; do
-               local sed_cmd="sed -e s/@member@/$member/g"
-               #echo "sed_cmd: $sed_cmd"
-               name_txt=$(echo "$t_name" | $sed_cmd)
-               #name_txt="$tname"
-               perms_txt=$(echo "$t_perms" | $sed_cmd)
-               desc_txt=$(echo "$t_desc" | $sed_cmd)
-               usage_txt=$(echo "$t_usage" | $sed_cmd)
-               help_txt=$(printf "%s\n" "$t_help" | $sed_cmd)
-               prototype=$(echo "$template_prototype" | $sed_cmd)
+               name_txt="${t_name//@member@/$member}"
+               perms_txt="${t_perms//@member@/$member}"
+               desc_txt="${t_desc//@member@/$member}"
+               usage_txt="${t_usage//@member@/$member}"
+               help_txt="${t_help//@member@/$member}"
+               prototype="${template_prototype//@member@/$member}"
+               result=
                $1
+               loop_result="$loop_result$result"
        done
-
-}
-
-com_c_file()
-{
-       echo "/** \file $output_file.c $c_file_comment */"
-       echo "$system_includes"
-       echo "$includes"
-       echo "struct $array_type $array_name[] = {"
-       while : ; do
-               read_one_command
-               if test $ret -lt 0; then
-                       exit 1
-               fi
-               if test $ret -eq 0; then
-                       break
-               fi
-               if test $template -eq 0; then
-                       dump_array_member
-                       continue
-               fi
-               template_loop dump_array_member
-       done
-       echo '{.name = NULL}};'
+       result="$loop_result"
+       # reset global variables
+       name_txt="$t_name"
+       perms_txt="$t_perms"
+       desc_txt="$t_desc"
+       usage_txt="$t_usage"
+       help_txt="$t_help"
+       source_files="$t_source_files"
 }
 
 com_header()
 {
-       echo "/** \file $output_file.h $header_comment */"
-       echo
-       echo "extern struct $array_type $array_name[];"
+       local array_members CR='
+'
+
        while : ; do
                read_one_command
                if test $ret -lt 0; then
@@ -302,11 +252,19 @@ com_header()
                        break
                fi
                if test $template -eq 0; then
-                       dump_proto
+                       make_proto
+                       printf "%s" "$result"
+                       make_array_member
+                       array_members="$array_members$result"
                        continue
                fi
-               template_loop dump_proto
+               template_loop make_proto
+               printf "%s" "$result"
+               template_loop make_array_member
+               array_members="$array_members$result"
        done
+       array_members="$array_members{.name = NULL} \\$CR"
+       echo "#define DEFINE_$(tr 'a-z' 'A-Z' <<< "$base_name")_CMD_ARRAY $array_members"
 }
 
 com_completion()
@@ -322,10 +280,12 @@ com_completion()
                        break
                fi
                if test $template -eq 0; then
-                       dump_completion
+                       make_completion
+                       printf "%s" "$result"
                        continue
                fi
-               template_loop dump_completion
+               template_loop make_completion
+               printf "%s" "$result"
        done
        echo
 }
@@ -334,9 +294,6 @@ read_header
 arg="$1"
 shift
 case "$arg" in
-       "c")
-               com_c_file
-               ;;
        "h")
                com_header
                ;;
index a57d8d72efb494d97fbcc2b1630dc635715fd950..90521dfe20b0f964f5e90bcbf87276800119fbc9 100644 (file)
@@ -98,9 +98,9 @@ all_errlist_objs="mp3_afh afh_common net string signal time daemon
        dccp_recv recv_common write_common file_write audiod_command
        client_common recv stdout filter stdin audioc write client
        exec send_common ggo udp_recv color fec fecdec_filter
-       prebuffer_filter audiod_command_list bitstream imdct
+       prebuffer_filter bitstream imdct
        wma_afh wma_common wmadec_filter buffer_tree crypt_common
-       gui gui_theme"
+       gui gui_theme sideband"
 
 executables="recv filter audioc write client afh audiod"
 
@@ -122,10 +122,10 @@ audioc_errlist_objs="audioc string net fd"
 audioc_ldflags=""
 
 audiod_cmdline_objs="add_cmdline(audiod compress_filter http_recv dccp_recv file_write client amp_filter udp_recv prebuffer_filter)"
-audiod_errlist_objs="audiod signal string daemon stat net crypt_common
+audiod_errlist_objs="audiod signal string daemon stat net crypt_common sideband
        time grab_client filter_common wav_filter compress_filter amp_filter http_recv dccp_recv
        recv_common fd sched write_common file_write audiod_command fecdec_filter
-       client_common ggo udp_recv color fec prebuffer_filter audiod_command_list
+       client_common ggo udp_recv color fec prebuffer_filter
        bitstream imdct wma_common wmadec_filter buffer_tree"
 audiod_ldflags="-lm"
 audiod_audio_formats="wma"
@@ -142,7 +142,7 @@ writers=" file"
 default_writer="FILE_WRITE"
 
 client_cmdline_objs="add_cmdline(client)"
-client_errlist_objs="client net string fd sched stdin stdout time
+client_errlist_objs="client net string fd sched stdin stdout time sideband
        client_common buffer_tree crypt_common"
 client_ldflags=""
 
@@ -269,13 +269,11 @@ else
                string signal time daemon http_send close_on_fork mm
                crypt_common ipc dccp_send fd user_list chunk_queue
                afs aft mood score attribute blob playlist sched acl
-               send_common udp_send color fec server_command_list
-               afs_command_list wma_afh wma_common"
-
+               send_common udp_send color fec wma_afh wma_common sideband"
        all_errlist_objs="$all_errlist_objs server vss command
                http_send close_on_fork mm ipc dccp_send user_list
                chunk_queue afs aft mood score attribute blob playlist
-               acl udp_send server_command_list afs_command_list"
+               acl udp_send"
 
        server_ldflags=""
        server_audio_formats="mp3 wma"
diff --git a/crypt.c b/crypt.c
index f33f769f6f035c18946c21fea064b37052802583..b754c0919e6fbccca77c0d662bddf59c6351fca6 100644 (file)
--- a/crypt.c
+++ b/crypt.c
@@ -317,6 +317,23 @@ int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf,
        return ret;
 }
 
+void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
+{
+       RC4_KEY *key = &sc->key;
+
+       *dst = (typeof(*dst)) {
+               /*
+                * Add one for the terminating zero byte. Integer overflow is
+                * no problem here as para_malloc() aborts when given a zero
+                * size argument.
+                */
+               .iov_base = para_malloc(src->iov_len + 1),
+               .iov_len = src->iov_len
+       };
+       RC4(key, src->iov_len, src->iov_base, dst->iov_base);
+       ((char *)dst->iov_base)[dst->iov_len] = '\0';
+}
+
 void hash_function(const char *data, unsigned long len, unsigned char *hash)
 {
        SHA_CTX c;
diff --git a/crypt.h b/crypt.h
index c46666950c12a92a1a4d14223f769762281af1fd..ac110882488afb54fb7541ed02944be2c5f3881b 100644 (file)
--- a/crypt.h
+++ b/crypt.h
@@ -128,6 +128,36 @@ struct stream_cipher_context {
  */
 struct stream_cipher *sc_new(const unsigned char *data, int len);
 
+/**
+ * Encrypt or decrypt a buffer using a stream cipher.
+ *
+ * \param sc Crypto key.
+ * \param src The source buffer and length.
+ * \param dst The destination buffer and length, filled out by the function.
+ *
+ * It is up to the implementation to decide whether the crypt operation is
+ * performed in place. The caller can tell by looking if the buffers given by
+ * \a src and \a dst coincide after the call. If (and only if) the crypt
+ * operation was not performed in place, the function allocated a new buffer
+ * for the result, so dst->iov_base is different from src->iov_base. In this
+ * case, the destination buffer must be freed by the caller when it is no
+ * longer needed.
+ */
+void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst);
+
+/**
+ * Wrapper for \ref sc_crypt() that can be used as a sideband transformation.
+ *
+ * \param src Passed verbatim to \ref sc_crypt().
+ * \param dst Passed verbatim to \ref sc_crypt().
+ * \param trafo_context Must point to an initialized stream cipher.
+ */
+_static_inline_ void sc_trafo(struct iovec *src, struct iovec *dst,
+               void *trafo_context)
+{
+       sc_crypt(trafo_context, src, dst);
+}
+
 /**
  * Deallocate a stream cipher structure.
  *
@@ -200,7 +230,6 @@ int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf,
  */
 int sc_recv_buffer(struct stream_cipher_context *scc, char *buf, size_t size);
 
-
 /** Size of the hash value in bytes. */
 #define HASH_SIZE 20
 
index 5ad4d43d883b498989412182a54e880aa3ba0766..705c7249903e2f4e998043e2a483029c43596809 100644 (file)
@@ -11,8 +11,8 @@
 #include "para.h"
 #include "error.h"
 #include "string.h"
-#include "crypt_backend.h"
 #include "crypt.h"
+#include "crypt_backend.h"
 
 /** If the key begins with this text, we treat it as an ssh key. */
 #define KEY_TYPE_TXT "ssh-rsa"
diff --git a/error.h b/error.h
index cfd2b1bdccb29ad4dede7f4f4988ba60f0000411..2474e0ab87778a01da7c8c70290bc22b41d901e0 100644 (file)
--- a/error.h
+++ b/error.h
@@ -29,9 +29,6 @@ DEFINE_ERRLIST_OBJECT_ENUM;
 #define COLOR_ERRORS
 #define SIGNAL_ERRORS
 #define FADE_ERRORS
-#define SERVER_COMMAND_LIST_ERRORS
-#define AFS_COMMAND_LIST_ERRORS
-#define AUDIOD_COMMAND_LIST_ERRORS
 #define STDOUT_ERRORS
 #define FILE_WRITE_ERRORS
 #define STDIN_ERRORS
@@ -39,6 +36,11 @@ DEFINE_ERRLIST_OBJECT_ENUM;
 
 extern const char **para_errlist[];
 
+#define SIDEBAND_ERRORS \
+       PARA_ERROR(BAD_BAND, "invalid or unexpected band designator"), \
+       PARA_ERROR(SB_PACKET_SIZE, "invalid sideband packet size or protocol error"), \
+
+
 #define FLACDEC_FILTER_ERRORS \
        PARA_ERROR(FLACDEC_DECODER_ALLOC, "could not allocate stream decoder"), \
        PARA_ERROR(FLACDEC_DECODER_INIT, "could not init stream decoder"), \
@@ -258,6 +260,8 @@ extern const char **para_errlist[];
        PARA_ERROR(BAD_CONFIG, "syntax error in config file"), \
        PARA_ERROR(CLIENT_AUTH, "authentication failed"), \
        PARA_ERROR(SERVER_EOF, "connection closed by para_server"), \
+       PARA_ERROR(SERVER_CMD_SUCCESS, "command terminated successfully"), \
+       PARA_ERROR(SERVER_CMD_FAILURE, "command failed"), \
 
 
 #define SCHED_ERRORS \
@@ -352,7 +356,8 @@ extern const char **para_errlist[];
        PARA_ERROR(ATOI_NO_DIGITS, "no digits found in string"), \
        PARA_ERROR(ATOI_JUNK_AT_END, "further characters after number"), \
        PARA_ERROR(SIZE_PREFIX, "bad size prefix"), \
-       PARA_ERROR(REGEX, "regular expression error") \
+       PARA_ERROR(REGEX, "regular expression error"), \
+       PARA_ERROR(ARG_NOT_FOUND, "argument not found in arg vector"), \
 
 
 #define EXEC_ERRORS \
@@ -431,6 +436,7 @@ extern const char **para_errlist[];
        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_AUTH, "authentication failure"), \
 
 
index 458c7f3e6fab40f72d66b1c7cbc790ddb0d4131f..5c5c5149ab4d27f7f8853df6a47833948da30f74 100644 (file)
@@ -158,7 +158,6 @@ void file_write_init(struct writer *w)
        w->parse_config_or_die = file_write_parse_config_or_die;
        w->free_config = file_write_free_config;
        w->close = file_write_close;
-       w->shutdown = NULL; /* nothing to do */
        w->help = (struct ggo_help) {
                .short_help = file_write_args_info_help,
                .detailed_help = file_write_args_info_detailed_help
index 40246f24b625c3a5236df47149bb9179633eb5f9..e8baa6b4b35031205f4fb28d858a6c1593e7ae7e 100644 (file)
@@ -234,27 +234,25 @@ static void flacdec_post_select(__a_unused struct sched *s, struct task *t)
                ret = flacdec_init(fn);
                goto out;
        }
-       pfd->unconsumed = 0;
-       for (;;) {
-               if (output_queue_full(btrn)) {
-                       pfd->have_more = true;
-                       break;
-               }
-               pfd->have_more = false;
-               FLAC__StreamDecoderState state;
-               FLAC__stream_decoder_process_single(pfd->decoder);
-               state = FLAC__stream_decoder_get_state(pfd->decoder);
-               //PARA_CRIT_LOG("state: %s\n", FLAC__stream_decoder_get_resolved_state_string(pfd->decoder));
-               ret = -E_FLACDEC_EOF;
-               if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
-                       goto out;
-               if (state == FLAC__STREAM_DECODER_ABORTED) {
-                       FLAC__stream_decoder_flush(pfd->decoder);
-                       fn->min_iqs = pfd->unconsumed + 1;
-                       break;
-               }
-               fn->min_iqs = 0;
+       if (output_queue_full(btrn)) {
+               pfd->have_more = true;
+               ret = 1;
+               goto out;
        }
+       pfd->have_more = false;
+       FLAC__StreamDecoderState state;
+       FLAC__stream_decoder_process_single(pfd->decoder);
+       state = FLAC__stream_decoder_get_state(pfd->decoder);
+       ret = -E_FLACDEC_EOF;
+       if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
+               goto out;
+       if (state == FLAC__STREAM_DECODER_ABORTED) {
+               FLAC__stream_decoder_flush(pfd->decoder);
+               fn->min_iqs = pfd->unconsumed + 1;
+               ret = 1;
+               goto out;
+       }
+       fn->min_iqs = 0;
        ret = 1;
 out:
        t->error = ret;
@@ -264,10 +262,17 @@ out:
 
 static void flacdec_close(struct filter_node *fn)
 {
-       struct private_flacdec_data *pfd = fn->private_data;
+       struct private_flacdec_data *pfd;
 
-       FLAC__stream_decoder_finish(pfd->decoder);
-       FLAC__stream_decoder_delete(pfd->decoder);
+       if (!fn)
+               return;
+       pfd = fn->private_data;
+       if (!pfd)
+               return;
+       if (pfd->decoder) {
+               FLAC__stream_decoder_finish(pfd->decoder);
+               FLAC__stream_decoder_delete(pfd->decoder);
+       }
        free(pfd);
        fn->private_data = NULL;
 }
index 5d207288359f5b9151c6ca55ab54d00b4669b533..dd6afe1d4fb25c73190626ee9206da43a67ffd6f 100644 (file)
  * As direct use of __printf(p,q) confuses doxygen, here are some extra macros
  * for those values p,q that are actually used.
  */
-#define  __printf_2_0 __printf(2,0)
-#define  __printf_1_2 __printf(1,2)
-#define  __printf_2_3 __printf(2,3)
+#define __printf_2_0 __printf(2,0)
+#define __printf_1_2 __printf(1,2)
+#define __printf_2_3 __printf(2,3)
+#define __printf_3_4 __printf(3,4)
 
 # if __GNUC__ > 3  || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
 # define __must_check  __attribute__ ((warn_unused_result))
index ea1e7710d01c53f5fd300444970806715ad14241..aaf97d424b4e5725c025288d4dd5cc341da9a6a2 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -969,3 +969,15 @@ int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf,
        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;
+       gcry_error_t gret;
+
+       /* perform in-place encryption */
+       *dst = *src;
+       gret = gcry_cipher_encrypt(handle, src->iov_base, src->iov_len,
+               NULL, 0);
+       assert(gret == 0);
+}
diff --git a/mood.c b/mood.c
index b1ee78e4e67faac76be61ee9c09085dfde20972f..e905f92cc64b4e99d55691d3d9e5805e3f3985d1 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -17,6 +17,7 @@
 #include "list.h"
 #include "ipc.h"
 #include "mm.h"
+#include "sideband.h"
 
 /**
  * Contains statistical data of the currently admissible audio files.
@@ -432,8 +433,11 @@ void mood_check_callback(int fd, __a_unused const struct osl_object *query)
 {
        struct para_buffer pb = {
                .max_size = shm_get_shmmax(),
-               .private_data = &fd,
-               .max_size_handler = pass_buffer_as_shm
+               .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");
@@ -442,7 +446,7 @@ void mood_check_callback(int fd, __a_unused const struct osl_object *query)
        osl_rbtree_loop(moods_table, BLOBCOL_ID, &pb,
                check_mood);
        if (pb.offset)
-               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
        free(pb.buf);
 }
 
index 79716a36c2e1c5a2f94e30b1212bfa58c3e75ed8..9498313c6ca42cb1cbe2c3e792b88f8f381d1d14 100644 (file)
@@ -208,10 +208,16 @@ static void ogg_post_select(__a_unused struct sched *s, struct task *t)
        char *buf;
 
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
-       if (ret < 0 && ret != -E_BTR_EOF) /* fatal error */
-               goto out;
-       if (ret <= 0 && !pod->have_more) /* nothing to do */
+       if (ret < 0) {
+               if (ret != -E_BTR_EOF) /* fatal error */
+                       goto out;
+               if (fn->min_iqs == 0 && !pod->have_more) /* EOF */
+                       goto out;
+               /* last ov_read() returned OV_HOLE */
+       } else if (ret == 0 && !pod->have_more) /* nothing to do */
                goto out;
+       if (btr_get_output_queue_size(btrn) > OGGDEC_MAX_OUTPUT_SIZE)
+               return;
        if (!pod->vf) {
                if (ret <= 0)
                        goto out;
@@ -239,9 +245,11 @@ static void ogg_post_select(__a_unused struct sched *s, struct task *t)
                have = 0;
        }
        pod->have_more = (ret > 0);
-       if (have > 0)
+       if (have > 0) {
+               if (have < OGGDEC_OUTPUT_CHUNK_SIZE)
+                       buf = para_realloc(buf, have);
                btr_add_output(buf, have, btrn);
-       else
+       else
                free(buf);
        if (ret == OV_HOLE) /* avoid buffer underruns */
                fn->min_iqs = 9000;
index f1e901993120008eb67a221c438b1b3f5c3a3c10..eae4167a18503b2cc8014a5069b53ce8c8eb9677 100644 (file)
@@ -233,7 +233,6 @@ void oss_write_init(struct writer *w)
        w->post_select = oss_post_select;
        w->parse_config_or_die = oss_parse_config_or_die;
        w->free_config = oss_free_config;
-       w->shutdown = NULL;
        w->help = (struct ggo_help) {
                .short_help = oss_write_args_info_help,
                .detailed_help = oss_write_args_info_detailed_help
index edb9f9230268c65703372e587e63140501e5e697..0f9d9605926a81fa5be0c4643ca6565fbe0391c2 100644 (file)
@@ -333,7 +333,6 @@ void osx_write_init(struct writer *w)
        w->post_select = osx_write_post_select;
        w->parse_config_or_die = osx_write_parse_config_or_die;
        w->free_config = osx_free_config;
-       w->shutdown = NULL; /* nothing to do */
        w->help = (struct ggo_help) {
                .short_help = osx_write_args_info_help,
                .detailed_help = osx_write_args_info_detailed_help
diff --git a/para.h b/para.h
index 0a14b99a51f0096e979983c9954f9617ea669097..5e0501a6d34685a538e0cddbec07c87c2c0785be 100644 (file)
--- a/para.h
+++ b/para.h
@@ -131,6 +131,18 @@ _static_inline_ long int para_random(unsigned max)
        return ((max + 0.0) * (random() / (RAND_MAX + 1.0)));
 }
 
+/**
+ * Simple sanity check for I/O vectors.
+ *
+ * \param iov Pointer to the I/O vector to check.
+ *
+ * \return True if \a iov points to a non-empty buffer.
+ */
+_static_inline_ bool iov_valid(const struct iovec *iov)
+{
+       return iov && iov->iov_len > 0 && iov->iov_base;
+}
+
 /** Round up x to next multiple of y. */
 #define ROUND_UP(x, y) ({ \
        const typeof(y) _divisor = y; \
index 13c2940428fb6720b32e4653207d5df0554c41f0..48ee4ca78d8174ed32da793749566214f5ecd721 100644 (file)
@@ -13,6 +13,7 @@
 #include "afh.h"
 #include "afs.h"
 #include "ipc.h"
+#include "sideband.h"
 
 /** \file playlist.c Functions for loading and saving playlists. */
 
@@ -132,8 +133,11 @@ void playlist_check_callback(int fd, __a_unused const struct osl_object *query)
 {
        struct para_buffer pb = {
                .max_size = shm_get_shmmax(),
-               .private_data = &fd,
-               .max_size_handler = pass_buffer_as_shm
+               .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");
 
@@ -142,7 +146,7 @@ void playlist_check_callback(int fd, __a_unused const struct osl_object *query)
        osl_rbtree_loop(playlists_table, BLOBCOL_ID, &pb,
                check_playlist);
        if (pb.offset)
-               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
        free(pb.buf);
 }
 
index c9b34d1c476d246d389091e42e3be7ac55669dd9..83097f1f2862a719d0b002c229d3b8fc102e5f8e 100644 (file)
@@ -1,10 +1,5 @@
-OF: server_command_list
+BN: server
 SF: command.c
-HC: prototypes for the server command handlers
-CC: array of server commands
-AT: server_command
-SI: osl regex
-IN: para error crypt command string afh afs server list user_list
 SN: list of server commands
 ---
 N: ff
diff --git a/sideband.c b/sideband.c
new file mode 100644 (file)
index 0000000..bf99088
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2012 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file sideband.c Implementation of the sideband API. */
+
+#include <regex.h>
+#include <sys/uio.h>
+
+#include "para.h"
+#include "error.h"
+#include "portable_io.h"
+#include "string.h"
+#include "sideband.h"
+
+/** Each sideband packet consists of a header and a data part. */
+#define SIDEBAND_HEADER_SIZE 5
+
+struct sb_context {
+       char header[SIDEBAND_HEADER_SIZE];
+       size_t bytes_dispatched; /* including header */
+       sb_transformation trafo;
+       void *trafo_context;
+       struct sb_buffer sbb;
+       size_t max_size;
+       bool dont_free;
+};
+
+/**
+ * Prepare to receive a sideband packet.
+ *
+ * \param max_size Do not allocate more than this many bytes.
+ * \param t Optional sideband transformation.
+ * \param trafo_context Passed verbatim to \a t.
+ *
+ * \a trafo_context is ignored if \a t is \p NULL.
+ *
+ * \return An opaque sideband handle.
+ */
+struct sb_context *sb_new_recv(size_t max_size, sb_transformation t,
+               void *trafo_context)
+{
+       struct sb_context *c = para_calloc(sizeof(*c));
+
+       c->max_size = max_size;
+       c->trafo = t;
+       c->trafo_context = trafo_context;
+       return c;
+}
+
+/**
+ * Prepare to write a sideband packet.
+ *
+ * \param sbb Data and meta data to send.
+ * \param dont_free Do not try to deallocate the sideband buffer.
+ * \param t See \ref sb_new_recv().
+ * \param trafo_context See \ref sb_new_recv().
+ *
+ * It's OK to supply a zero-sized buffer in \a sbb. In this case only the band
+ * designator is sent through the sideband channel. Otherwise, if \a dont_free
+ * is false, the buffer of \a sbb is freed after the data has been sent.
+ *
+ * \return See \ref sb_new_recv().
+ */
+struct sb_context *sb_new_send(struct sb_buffer *sbb, bool dont_free,
+               sb_transformation t, void *trafo_context)
+{
+       struct sb_context *c = para_calloc(sizeof(*c));
+       struct iovec src, dst, *srcp, *dstp;
+
+       assert(sbb);
+       c->trafo = t;
+       c->trafo_context = trafo_context;
+       c->dont_free = dont_free;
+       c->sbb = *sbb;
+       write_u32(c->header, sbb->iov.iov_len);
+       write_u8(c->header + 4, sbb->band);
+       if (!t)
+               goto out;
+       src = (typeof(src)){.iov_base = c->header, .iov_len = SIDEBAND_HEADER_SIZE};
+       t(&src, &dst, trafo_context);
+       if (src.iov_base != dst.iov_base) {
+               memcpy(c->header, dst.iov_base, SIDEBAND_HEADER_SIZE);
+               free(dst.iov_base);
+       }
+       if (!iov_valid(&sbb->iov))
+               goto out;
+       srcp = &sbb->iov;
+       dstp = &c->sbb.iov;
+       t(srcp, dstp, trafo_context);
+       if (srcp->iov_base != dstp->iov_base) {
+               if (!c->dont_free)
+                       free(srcp->iov_base);
+               c->dont_free = false;
+       }
+out:
+       return c;
+}
+
+/**
+ * Deallocate all memory associated with a sideband handle.
+ *
+ * \param c The sideband handle.
+ *
+ * \a c must point to a handle previously returned by \ref sb_new_recv() or
+ * \ref sb_new_send(). It \a c is \p NULL, the function does nothing.
+ */
+void sb_free(struct sb_context *c)
+{
+       if (!c)
+               return;
+       if (!c->dont_free)
+               free(c->sbb.iov.iov_base);
+       free(c);
+}
+
+/**
+ * Obtain pointer(s) to the sideband send buffer(s).
+ *
+ * \param c The sideband handle.
+ * \param iov Array of two I/O vectors.
+ *
+ * \return The number of buffers that need to be sent, either 1 or 2.
+ *
+ * This function fills out the buffers described by \a iov. The result can be
+ * passed to \ref xwritev() or similar.
+ *
+ * \sa \ref sb_get_recv_buffer().
+ */
+int sb_get_send_buffers(struct sb_context *c, struct iovec iov[2])
+{
+       struct sb_buffer *sbb = &c->sbb;
+       size_t n = c->bytes_dispatched;
+
+       if (n < SIDEBAND_HEADER_SIZE) {
+               iov[0].iov_base = c->header + n;
+               iov[0].iov_len = SIDEBAND_HEADER_SIZE - n;
+               if (!iov_valid(&sbb->iov))
+                       goto out;
+               iov[1] = sbb->iov;
+               return 2;
+       }
+       n -= SIDEBAND_HEADER_SIZE;
+       assert(n < sbb->iov.iov_len);
+       iov[0].iov_base = sbb->iov.iov_base + n;
+       iov[0].iov_len = sbb->iov.iov_len - n;
+out:
+       iov[1].iov_base = NULL;
+       iov[1].iov_len = 0;
+       return 1;
+}
+
+/**
+ * Update the sideband context after data has been sent.
+ *
+ * \param c The sideband handle.
+ * \param nbytes The number of sent bytes.
+ *
+ * \return False if more data must be sent to complete the sideband transfer,
+ * true if the transfer is complete. In this case all resources are freed and
+ * the sideband handle must not be used any more.
+ */
+bool sb_sent(struct sb_context *c, size_t nbytes)
+{
+       struct sb_buffer *sbb = &c->sbb;
+       size_t sz = SIDEBAND_HEADER_SIZE + sbb->iov.iov_len;
+
+       assert(c->bytes_dispatched + nbytes <= sz);
+       c->bytes_dispatched += nbytes;
+       if (c->bytes_dispatched < sz)
+               return false;
+       sb_free(c);
+       return true;
+}
+
+/**
+ * Obtain a pointer to the next sideband read buffer.
+ *
+ * \param c The sideband handle.
+ * \param iov Result IO vector.
+ *
+ * This fills in \a iov to point to the buffer to which the next chunk of
+ * received data should be written.
+ */
+void sb_get_recv_buffer(struct sb_context *c, struct iovec *iov)
+{
+       struct sb_buffer *sbb = &c->sbb;
+       size_t n = c->bytes_dispatched;
+
+       if (n < SIDEBAND_HEADER_SIZE) {
+               iov->iov_base = c->header + n;
+               iov->iov_len = SIDEBAND_HEADER_SIZE - n;
+               return;
+       }
+       n -= SIDEBAND_HEADER_SIZE;
+       assert(sbb->iov.iov_base);
+       assert(sbb->iov.iov_len > n);
+       iov->iov_base = sbb->iov.iov_base + n;
+       iov->iov_len = sbb->iov.iov_len - n;
+}
+
+/**
+ * Update the sideband context after data has been received.
+ *
+ * \param c The sideband handle.
+ * \param nbytes The number of bytes that have been received.
+ * \param result The received sideband packet.
+ *
+ * \return Negative on errors, zero if more data needs to be read to complete
+ * this sideband packet, one if the sideband packet has been received
+ * completely.
+ *
+ * Only if the function returns one, the sideband buffer pointed to by \a
+ * result is set to point to the received data.
+ */
+int sb_received(struct sb_context *c, size_t nbytes, struct sb_buffer *result)
+{
+       struct sb_buffer *sbb = &c->sbb;
+       size_t n = c->bytes_dispatched,
+               sz = SIDEBAND_HEADER_SIZE + sbb->iov.iov_len;
+
+       assert(n + nbytes <= sz);
+       c->bytes_dispatched += nbytes;
+       if (c->bytes_dispatched < SIDEBAND_HEADER_SIZE)
+               return 0;
+       if (n >= SIDEBAND_HEADER_SIZE) { /* header has already been received */
+               if (c->bytes_dispatched < sz) /* need to recv more body data */
+                       return 0;
+               /* received everything, decrypt and return sbb */
+               if (c->trafo) {
+                       struct iovec dst;
+                       c->trafo(&sbb->iov, &dst, c->trafo_context);
+                       if (sbb->iov.iov_base != dst.iov_base) {
+                               free(sbb->iov.iov_base);
+                               sbb->iov.iov_base = dst.iov_base;
+                       }
+               }
+               ((char *)(sbb->iov.iov_base))[sbb->iov.iov_len] = '\0';
+               goto success;
+       }
+       /* header has been received, decrypt and decode it */
+       if (c->trafo) { /* decrypt */
+               struct iovec dst, src = (typeof(src)) {
+                       .iov_base = c->header,
+                       .iov_len = SIDEBAND_HEADER_SIZE
+               };
+               c->trafo(&src, &dst, c->trafo_context);
+               if (src.iov_base != dst.iov_base) {
+                       memcpy(c->header, dst.iov_base,
+                               SIDEBAND_HEADER_SIZE);
+                       free(dst.iov_base);
+               }
+       }
+       /* Decode header, setup sbb */
+       sbb->iov.iov_len = read_u32(c->header);
+       sbb->band = read_u8(c->header + 4);
+       sbb->iov.iov_base = NULL;
+       if (sbb->iov.iov_len == 0) /* zero-sized msg */
+               goto success;
+       if (c->max_size > 0 && sbb->iov.iov_len > c->max_size) {
+               PARA_ERROR_LOG("packet too big (is %zu, max %zu)\n",
+                       sbb->iov.iov_len, c->max_size);
+               return -E_SB_PACKET_SIZE;
+       }
+       /*
+        * We like to reserve one extra byte for the terminating NULL
+        * character. However, we must make sure the +1 below does not
+        * overflow iov_len.
+        */
+       if (sbb->iov.iov_len == (size_t)-1)
+               return -E_SB_PACKET_SIZE;
+       sbb->iov.iov_base = para_malloc(sbb->iov.iov_len + 1);
+       return 0; /* ready to read body */
+success:
+       *result = c->sbb;
+       free(c);
+       return 1;
+}
diff --git a/sideband.h b/sideband.h
new file mode 100644 (file)
index 0000000..4f4ed0a
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2012 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file sideband.h exported symbols from sideband.c */
+
+/**
+ * The valid sideband designators.
+ *
+ * A sideband packet consists of a header and a body. The header is sent prior
+ * to the data and contains the length of the buffer and the sideband
+ * designator, an integer. This scheme allows receivers to never read more data
+ * than what was specified in the header.
+ *
+ * The sideband designator indicates the type of the data. The authentication
+ * handshake between the command handler (child of para_server) and the client
+ * requires to exchange several small packages. Each such package is sent as a
+ * sideband package with a dedicated designator.
+ *
+ * Other designators are employed for normal command output and for log
+ * messages, where each loglevel corresponds to a sideband designator.
+ *
+ * Note that the values of this enum are part of the ABI, so never change
+ * or remove entries. Appending is OK though.
+ */
+#define SB_DESIGNATORS \
+       /* Special value for receiving only: Accept any band. */ \
+       DESIGNATOR(ANY), \
+       /* This packet contains the authentication challenge. */ \
+       DESIGNATOR(CHALLENGE), \
+       /* I solved your challenge and here is the proof. */ \
+       DESIGNATOR(CHALLENGE_RESPONSE), \
+       /* Congratulations, you are authenticated. */ \
+       DESIGNATOR(PROCEED), \
+       /* This is the command I want you to execute. */ \
+       DESIGNATOR(COMMAND), \
+       /* Ready to receive a blob (addblob commands only). */ \
+       DESIGNATOR(AWAITING_DATA), \
+       /* Normal command output (default). */ \
+       DESIGNATOR(OUTPUT), \
+       /* LL_DEBUG. */ \
+       DESIGNATOR(DEBUG_LOG), \
+       /* LL_INFO. */ \
+       DESIGNATOR(INFO_LOG), \
+       /* LL_NOTICE. */ \
+       DESIGNATOR(NOTICE_LOG), \
+       /* LL_WARNING, */ \
+       DESIGNATOR(WARNING_LOG), \
+       /* LL_ERROR. */ \
+       DESIGNATOR(ERROR_LOG), \
+       /* LL_CRIT. */ \
+       DESIGNATOR(CRIT_LOG), \
+       /* LL_EMERG. */ \
+       DESIGNATOR(EMERG_LOG), \
+       /* Command terminated successfully. */ \
+       DESIGNATOR(EXIT__SUCCESS), \
+       /* Command failed. */ \
+       DESIGNATOR(EXIT__FAILURE), \
+
+/** Just prefix with \p SBD_. */
+#define DESIGNATOR(x) SBD_ ## x
+
+/** All valid sideband designators. */
+enum sb_designator {SB_DESIGNATORS NUM_SB_DESIGNATORS};
+#undef DESIGNATOR
+/** One stringified sideband designator. */
+#define DESIGNATOR(x) #x
+/** List of stringified sidedband designators. */
+#define SB_DESIGNATORS_ARRAY SB_DESIGNATORS
+
+/**
+ * The information contained in a sideband buffer.
+ *
+ * This structure is used for both sending and receiving data through a
+ * sideband channel. A pointer to a sideband buffer is passed to the sending
+ * side of a sideband while the receiving end yields a filled out structure
+ * once all data has been received.
+ */
+struct sb_buffer {
+       /** Data and length. */
+       struct iovec iov;
+       /** The band designator. */
+       uint8_t band;
+};
+
+/**
+ * The opaque sideband context structure.
+ *
+ * A pointer to a structure of this type is returned by the two \a sb_new_xxx
+ * functions as an opaque handle. Other public functions of the sideband API
+ * take such a handle as a parameter.
+ */
+struct sb_context;
+
+/**
+ * The type of a sideband transformation.
+ *
+ * The sideband API allows to filter all data through an arbitrary
+ * transformation, which is useful for crypto purposes. The transformation may
+ * either transform the data in place, or return a pointer to a new buffer
+ * which contains the transformed source buffer. The internal sideband
+ * functions can tell the two types of transformations apart by checking
+ * whether the destination buffer coincides with the source buffer.
+ *
+ * If the transformation allocates a new buffer, it _must_ allocate one extra
+ * byte for \p NULL termination.
+ */
+typedef void (*sb_transformation)(struct iovec *src, struct iovec *dst,
+               void *trafo_context);
+
+
+/** Initialize a sideband buffer. */
+#define SBB_INIT(_band, _buf, _numbytes) \
+       { \
+               .band = band, \
+               .iov = { \
+                       .iov_base = _buf, \
+                       .iov_len = _numbytes \
+               } \
+       };
+
+/* receiving */
+struct sb_context *sb_new_recv(size_t max_size, sb_transformation t,
+               void *trafo_context);
+void sb_get_recv_buffer(struct sb_context *c, struct iovec *iov);
+int sb_received(struct sb_context *c, size_t nbytes, struct sb_buffer *result);
+
+/* sending */
+struct sb_context *sb_new_send(struct sb_buffer *sbb, bool dont_free,
+               sb_transformation t, void *trafo_context);
+int sb_get_send_buffers(struct sb_context *c, struct iovec iov[2]);
+bool sb_sent(struct sb_context *c, size_t nbytes);
+
+void sb_free(struct sb_context *c);
index aa3bcbddabe425cc823e3ad5e567f707f70dbccb..7123ba1ae5e00dfc13e1417bfbafd102c3946175 100644 (file)
--- a/string.c
+++ b/string.c
@@ -855,6 +855,27 @@ err:
        return ret;
 }
 
+/**
+ * Find out if the given string is contained in the arg vector.
+ *
+ * \param arg The string to look for.
+ * \param argv The array to search.
+ *
+ * \return The first index whose value equals \a arg, or \p -E_ARG_NOT_FOUND if
+ * arg was not found in \a argv.
+ */
+int find_arg(const char *arg, char **argv)
+{
+       int i;
+
+       if (!argv)
+               return -E_ARG_NOT_FOUND;
+       for (i = 0; argv[i]; i++)
+               if (strcmp(arg, argv[i]) == 0)
+                       return i;
+       return -E_ARG_NOT_FOUND;
+}
+
 /**
  * Compile a regular expression.
  *
index cdc55d2da64cef1da942543608602b0a6592af2e..aebb6516afb6e0de78811d1329b012d5edd5b348 100644 (file)
--- a/string.h
+++ b/string.h
@@ -83,6 +83,7 @@ int para_atoi32(const char *str, int32_t *value);
 int get_loglevel_by_name(const char *txt);
 int read_size_header(const char *buf);
 int create_argv(const char *buf, const char *delim, char ***result);
+int find_arg(const char *arg, char **argv);
 void free_argv(char **argv);
 int para_regcomp(regex_t *preg, const char *regex, int cflags);
 void freep(void *arg);
index b5dad0f5af40a15773dce43b4a77e65346ea5b66..185ec1fcecb738de2a322d4e81aa67f384026b1b 100644 (file)
--- a/wmadata.h
+++ b/wmadata.h
@@ -29,7 +29,7 @@ struct coef_vlc_table {
        const uint32_t *huffcodes;
        /** VLC bit size. */
        const uint8_t *huffbits;
-       /* Table to build run/level tables. */
+       /** Table to build run/level tables. */
        const uint16_t *levels;
 };
 
index 7d89d112a53d074cf3538cf890062b7afa87278c..e58754f5f5658886f8e4d9c238b5093db1980dbe 100644 (file)
@@ -71,7 +71,9 @@ struct private_wmadec_data {
        int use_exp_vlc;
        /** Whether perceptual noise is added. */
        int use_noise_coding;
+       /** Depends on number of the bits per second and the frame length. */
        int byte_offset_bits;
+       /** Only used if use_exp_vlc is true. */
        struct vlc exp_vlc;
        int exponent_sizes[BLOCK_NB_SIZES];
        uint16_t exponent_bands[BLOCK_NB_SIZES][25];
@@ -502,9 +504,8 @@ static int wma_decode_init(char *initial_buf, int len, struct private_wmadec_dat
 
        if (pwd->use_exp_vlc) {
                PARA_INFO_LOG("using exp_vlc\n");
-               init_vlc(&pwd->exp_vlc, EXPVLCBITS,
-               sizeof(wma_scale_huffbits), wma_scale_huffbits,
-               wma_scale_huffcodes, 4);
+               init_vlc(&pwd->exp_vlc, EXPVLCBITS, sizeof(wma_scale_huffbits),
+                       wma_scale_huffbits, wma_scale_huffcodes, 4);
        } else {
                PARA_INFO_LOG("using curve\n");
                wma_lsp_to_curve_init(pwd, pwd->frame_len);
@@ -1213,11 +1214,11 @@ static int wmadec_execute(struct btr_node *btrn, const char *cmd, char **result)
 static void wmadec_post_select(__a_unused struct sched *s, struct task *t)
 {
        struct filter_node *fn = container_of(t, struct filter_node, task);
-       int ret, converted;
+       int ret, converted, out_size;
        struct private_wmadec_data *pwd = fn->private_data;
        struct btr_node *btrn = fn->btrn;
        size_t len;
-       char *in;
+       char *in, *out;
 
 next_buffer:
        converted = 0;
@@ -1246,22 +1247,20 @@ next_buffer:
                goto success;
        }
        fn->min_iqs = WMA_FRAME_SKIP + pwd->ahi.block_align;
-       for (;;) {
-               char *out;
-               int out_size = WMA_OUTPUT_BUFFER_SIZE;
-               if (converted + fn->min_iqs > len)
-                       break;
-               out = para_malloc(WMA_OUTPUT_BUFFER_SIZE);
-               ret = wma_decode_superframe(pwd, out,
-                       &out_size, (uint8_t *)in + converted + WMA_FRAME_SKIP,
-                       len - WMA_FRAME_SKIP);
-               if (ret < 0) {
-                       free(out);
-                       goto err;
-               }
-               btr_add_output(out, out_size, btrn);
-               converted += ret + WMA_FRAME_SKIP;
+       if (fn->min_iqs > len)
+               goto success;
+       out_size = WMA_OUTPUT_BUFFER_SIZE;
+       out = para_malloc(out_size);
+       ret = wma_decode_superframe(pwd, out, &out_size,
+               (uint8_t *)in + WMA_FRAME_SKIP, len - WMA_FRAME_SKIP);
+       if (ret < 0) {
+               free(out);
+               goto err;
        }
+       out = para_realloc(out, out_size);
+       if (out_size > 0)
+               btr_add_output(out, out_size, btrn);
+       converted += ret + WMA_FRAME_SKIP;
 success:
        btr_consume(btrn, converted);
        return;
diff --git a/write.h b/write.h
index 56a9711a44af6fde00ef23f6f5d203e407daf4d0..ddf43b6932b9b894ce0dd08bb2a123691f86cdc4 100644 (file)
--- a/write.h
+++ b/write.h
@@ -72,12 +72,6 @@ struct writer {
         * This function is assumed to succeed.
         */
        void (*close)(struct writer_node *);
-       /**
-        * Shutdown the writer.
-        *
-        * This is a optional function pointer used for cleaning up.
-        */
-       void (*shutdown)(struct writer_node *);
        /** The short and the log help text of this writer. */
        struct ggo_help help;
        /**