From: Andre Noll Date: Wed, 11 Jul 2012 23:53:14 +0000 (+0200) Subject: Merge branch 't/clang' X-Git-Tag: v0.4.11~7 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=02dd632ab2a6696aff7b6c6d108069704cfe871a;hp=869f0e06ec4e15abc9230c1b2d7615da5250802e Merge branch 't/clang' 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. --- diff --git a/.gitignore b/.gitignore index d337367b..141424f0 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,6 @@ Makefile.deps TODO NONE aclocal.m4 -*_command_list.c *_command_list.h *_command_list.man paraslash-git.tar.bz2 diff --git a/Doxyfile b/Doxyfile index 83973cfb..3fed0c2e 100644 --- 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 diff --git a/Makefile.in b/Makefile.in index 30a5c015..868cfeac 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 0ac2240e..9a84c332 100644 --- 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 5939cbe6..a1381e73 100644 --- 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 c7808040..f0bd26f1 100644 --- 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 7145c41b..2789e94e 100644 --- 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 9e991da6..f01f1868 100644 --- 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); } diff --git a/alsa_write.c b/alsa_write.c index a4863a59..f50ad6a3 100644 --- a/alsa_write.c +++ b/alsa_write.c @@ -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, diff --git a/ao_write.c b/ao_write.c index 6dc3192c..93861ab6 100644 --- a/ao_write.c +++ b/ao_write.c @@ -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, }; diff --git a/attribute.c b/attribute.c index 19700944..638e22ea 100644 --- a/attribute.c +++ b/attribute.c @@ -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; } diff --git a/audiod.c b/audiod.c index 40603c4e..93bc8da3 100644 --- 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; } diff --git a/audiod.cmd b/audiod.cmd index c5890744..6c99d4c7 100644 --- a/audiod.cmd +++ b/audiod.cmd @@ -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 diff --git a/audiod_command.c b/audiod_command.c index 9d92d1c3..35b76b5f 100644 --- a/audiod_command.c +++ b/audiod_command.c @@ -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: diff --git a/bitstream.h b/bitstream.h index 5d6be3be..a3393380 100644 --- a/bitstream.h +++ b/bitstream.h @@ -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 409e3487..f51f4de0 100644 --- 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); diff --git a/client.c b/client.c index d98c6327..f0f3b261 100644 --- 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; } diff --git a/client.h b/client.h index 8490b72f..b841b7e1 100644 --- 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); diff --git a/client_common.c b/client_common.c index 5bd2241b..649a1b4f 100644 --- a/client_common.c +++ b/client_common.c @@ -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); } diff --git a/command.c b/command.c index f520e3ea..f9bace34 100644 --- 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); } diff --git a/command.h b/command.h index 05510cc0..e4159e6b 100644 --- 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); diff --git a/command_util.sh b/command_util.sh index 76b3445f..b37c5b03 100755 --- a/command_util.sh +++ b/command_util.sh @@ -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 ;; diff --git a/configure.ac b/configure.ac index a57d8d72..90521dfe 100644 --- a/configure.ac +++ b/configure.ac @@ -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 f33f769f..b754c091 100644 --- 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 c4666695..ac110882 100644 --- 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 diff --git a/crypt_common.c b/crypt_common.c index 5ad4d43d..705c7249 100644 --- a/crypt_common.c +++ b/crypt_common.c @@ -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 cfd2b1bd..2474e0ab 100644 --- 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"), \ diff --git a/file_write.c b/file_write.c index 458c7f3e..5c5c5149 100644 --- a/file_write.c +++ b/file_write.c @@ -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 diff --git a/flacdec_filter.c b/flacdec_filter.c index 40246f24..e8baa6b4 100644 --- a/flacdec_filter.c +++ b/flacdec_filter.c @@ -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; } diff --git a/gcc-compat.h b/gcc-compat.h index 5d207288..dd6afe1d 100644 --- a/gcc-compat.h +++ b/gcc-compat.h @@ -16,9 +16,10 @@ * 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)) diff --git a/gcrypt.c b/gcrypt.c index ea1e7710..aaf97d42 100644 --- 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 b1ee78e4..e905f92c 100644 --- 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); } diff --git a/oggdec_filter.c b/oggdec_filter.c index 79716a36..9498313c 100644 --- a/oggdec_filter.c +++ b/oggdec_filter.c @@ -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; diff --git a/oss_write.c b/oss_write.c index f1e90199..eae4167a 100644 --- a/oss_write.c +++ b/oss_write.c @@ -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 diff --git a/osx_write.c b/osx_write.c index edb9f923..0f9d9605 100644 --- a/osx_write.c +++ b/osx_write.c @@ -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 0a14b99a..5e0501a6 100644 --- 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; \ diff --git a/playlist.c b/playlist.c index 13c29404..48ee4ca7 100644 --- a/playlist.c +++ b/playlist.c @@ -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); } diff --git a/server.cmd b/server.cmd index c9b34d1c..83097f1f 100644 --- a/server.cmd +++ b/server.cmd @@ -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 index 00000000..bf990889 --- /dev/null +++ b/sideband.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2012 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file sideband.c Implementation of the sideband API. */ + +#include +#include + +#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 index 00000000..4f4ed0ac --- /dev/null +++ b/sideband.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2012 Andre Noll + * + * 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); diff --git a/string.c b/string.c index aa3bcbdd..7123ba1a 100644 --- 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. * diff --git a/string.h b/string.h index cdc55d2d..aebb6516 100644 --- 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); diff --git a/wmadata.h b/wmadata.h index b5dad0f5..185ec1fc 100644 --- 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; }; diff --git a/wmadec_filter.c b/wmadec_filter.c index 7d89d112..e58754f5 100644 --- a/wmadec_filter.c +++ b/wmadec_filter.c @@ -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 56a9711a..ddf43b69 100644 --- 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; /**