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.
TODO
NONE
aclocal.m4
-*_command_list.c
*_command_list.h
*_command_list.man
paraslash-git.tar.bz2
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
$(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 $<'
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"
#include "signal.h"
#include "fd.h"
#include "mood.h"
+#include "sideband.h"
#include "command.h"
/** The osl tables used by afs. \sa blob.c. */
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,
{
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) {
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));
{
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;
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);
}
* 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)
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)
/**
* 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;
goto err;
cr = shm;
cr->result_size = size;
+ cr->band = band;
memcpy(shm + sizeof(*cr), buf, size);
ret = shm_detach(shm);
if (ret < 0)
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);
}
}
}
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));
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;
}
-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
---
*
* \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,
#include "fd.h"
#include "ipc.h"
#include "portable_io.h"
+#include "sideband.h"
#include "command.h"
static struct osl_table *audio_file_table;
{
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;
}
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);
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;
}
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;
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);
}
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)
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);
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. */
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;
}
/*
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:
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. */
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;
}
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;
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;
}
.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;
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);
}
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;
}
.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;
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);
}
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;
}
.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;
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);
}
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;
}
if (!buf)
return;
- pass_buffer_as_shm(buf, strlen(buf), &fd);
+ pass_buffer_as_shm(fd, SBD_OUTPUT, buf, strlen(buf));
}
/**
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 */
{
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");
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);
}
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);
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,
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,
};
#include "afh.h"
#include "afs.h"
#include "ipc.h"
+#include "sideband.h"
#include "command.h"
static struct osl_table *attribute_table;
.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
}
};
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);
}
}
}
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;
}
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;
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);
}
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;
}
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;
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);
}
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;
}
.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;
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);
}
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;
}
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;
size_t sz;
int ret;
if (st->ct->task.error < 0) {
- if (st->ct->task.error >= 0)
- goto out;
close_stat_pipe();
goto out;
}
-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
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++)
* 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;
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;
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;
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:
/** 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;
#include "afs.h"
#include "ipc.h"
#include "portable_io.h"
+#include "sideband.h"
#include "command.h"
/**
.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 = {
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);
}
// 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;
}
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));
}
}
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. */
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 = {
}
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);
}
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,
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);
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;
}
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. */
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. */
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);
#include "crypt.h"
#include "net.h"
#include "fd.h"
+#include "sideband.h"
#include "string.h"
#include "client.cmdline.h"
#include "client.h"
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);
free(ct->config_file);
free(ct->key_file);
client_cmdline_parser_free(&ct->conf);
+ free(ct->challenge_hash);
+ sb_free(ct->sbc);
free(ct);
}
case CL_RECEIVED_WELCOME:
case CL_RECEIVED_PROCEED:
+ case CL_RECEIVED_CHALLENGE:
para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
return;
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.
*
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;
/* 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;
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?
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);
*/
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) {
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);
}
#include "para.h"
#include "error.h"
#include "crypt.h"
+#include "sideband.h"
#include "command.h"
#include "server.cmdline.h"
#include "string.h"
#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
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 */
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)
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;
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;
}
/* 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;
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"
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 \
*
* 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
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;
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
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 */
}
/* 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"
);
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;
}
/* term */
-int com_term(struct command_context *cc)
+static int com_term(struct command_context *cc)
{
if (cc->argc != 1)
return -E_COMMAND_SYNTAX;
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;
}
/* stop */
-int com_stop(struct command_context *cc)
+static int com_stop(struct command_context *cc)
{
if (cc->argc != 1)
return -E_COMMAND_SYNTAX;
}
/* pause */
-int com_pause(struct command_context *cc)
+static int com_pause(struct command_context *cc)
{
if (cc->argc != 1)
return -E_COMMAND_SYNTAX;
}
/* next */
-int com_next(struct command_context *cc)
+static int com_next(struct command_context *cc)
{
if (cc->argc != 1)
return -E_COMMAND_SYNTAX;
}
/* nomore */
-int com_nomore(struct command_context *cc)
+static int com_nomore(struct command_context *cc)
{
if (cc->argc != 1)
return -E_COMMAND_SYNTAX;
}
/* ff */
-int com_ff(struct command_context *cc)
+static int com_ff(struct command_context *cc)
{
long promille;
int ret, backwards = 0;
}
/* jmp */
-int com_jmp(struct command_context *cc)
+static int com_jmp(struct command_context *cc)
{
long unsigned int i;
int ret;
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.
*
__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;
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);
}
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;
/* 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);
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);
}
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;
};
/**
/** 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);
#!/usr/bin/env bash
-
read_header()
{
local key value i
---)
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"
;;
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()
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"
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
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()
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
}
arg="$1"
shift
case "$arg" in
- "c")
- com_c_file
- ;;
"h")
com_header
;;
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"
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"
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=""
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"
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;
*/
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.
*
*/
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
#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"
#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
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"), \
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 \
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 \
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"), \
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
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;
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;
}
* 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))
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);
+}
#include "list.h"
#include "ipc.h"
#include "mm.h"
+#include "sideband.h"
/**
* Contains statistical data of the currently admissible audio files.
{
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");
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);
}
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;
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;
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
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
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; \
#include "afh.h"
#include "afs.h"
#include "ipc.h"
+#include "sideband.h"
/** \file playlist.c Functions for loading and saving playlists. */
{
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");
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);
}
-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
--- /dev/null
+/*
+ * Copyright (C) 2012 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file sideband.c Implementation of the sideband API. */
+
+#include <regex.h>
+#include <sys/uio.h>
+
+#include "para.h"
+#include "error.h"
+#include "portable_io.h"
+#include "string.h"
+#include "sideband.h"
+
+/** Each sideband packet consists of a header and a data part. */
+#define SIDEBAND_HEADER_SIZE 5
+
+struct sb_context {
+ char header[SIDEBAND_HEADER_SIZE];
+ size_t bytes_dispatched; /* including header */
+ sb_transformation trafo;
+ void *trafo_context;
+ struct sb_buffer sbb;
+ size_t max_size;
+ bool dont_free;
+};
+
+/**
+ * Prepare to receive a sideband packet.
+ *
+ * \param max_size Do not allocate more than this many bytes.
+ * \param t Optional sideband transformation.
+ * \param trafo_context Passed verbatim to \a t.
+ *
+ * \a trafo_context is ignored if \a t is \p NULL.
+ *
+ * \return An opaque sideband handle.
+ */
+struct sb_context *sb_new_recv(size_t max_size, sb_transformation t,
+ void *trafo_context)
+{
+ struct sb_context *c = para_calloc(sizeof(*c));
+
+ c->max_size = max_size;
+ c->trafo = t;
+ c->trafo_context = trafo_context;
+ return c;
+}
+
+/**
+ * Prepare to write a sideband packet.
+ *
+ * \param sbb Data and meta data to send.
+ * \param dont_free Do not try to deallocate the sideband buffer.
+ * \param t See \ref sb_new_recv().
+ * \param trafo_context See \ref sb_new_recv().
+ *
+ * It's OK to supply a zero-sized buffer in \a sbb. In this case only the band
+ * designator is sent through the sideband channel. Otherwise, if \a dont_free
+ * is false, the buffer of \a sbb is freed after the data has been sent.
+ *
+ * \return See \ref sb_new_recv().
+ */
+struct sb_context *sb_new_send(struct sb_buffer *sbb, bool dont_free,
+ sb_transformation t, void *trafo_context)
+{
+ struct sb_context *c = para_calloc(sizeof(*c));
+ struct iovec src, dst, *srcp, *dstp;
+
+ assert(sbb);
+ c->trafo = t;
+ c->trafo_context = trafo_context;
+ c->dont_free = dont_free;
+ c->sbb = *sbb;
+ write_u32(c->header, sbb->iov.iov_len);
+ write_u8(c->header + 4, sbb->band);
+ if (!t)
+ goto out;
+ src = (typeof(src)){.iov_base = c->header, .iov_len = SIDEBAND_HEADER_SIZE};
+ t(&src, &dst, trafo_context);
+ if (src.iov_base != dst.iov_base) {
+ memcpy(c->header, dst.iov_base, SIDEBAND_HEADER_SIZE);
+ free(dst.iov_base);
+ }
+ if (!iov_valid(&sbb->iov))
+ goto out;
+ srcp = &sbb->iov;
+ dstp = &c->sbb.iov;
+ t(srcp, dstp, trafo_context);
+ if (srcp->iov_base != dstp->iov_base) {
+ if (!c->dont_free)
+ free(srcp->iov_base);
+ c->dont_free = false;
+ }
+out:
+ return c;
+}
+
+/**
+ * Deallocate all memory associated with a sideband handle.
+ *
+ * \param c The sideband handle.
+ *
+ * \a c must point to a handle previously returned by \ref sb_new_recv() or
+ * \ref sb_new_send(). It \a c is \p NULL, the function does nothing.
+ */
+void sb_free(struct sb_context *c)
+{
+ if (!c)
+ return;
+ if (!c->dont_free)
+ free(c->sbb.iov.iov_base);
+ free(c);
+}
+
+/**
+ * Obtain pointer(s) to the sideband send buffer(s).
+ *
+ * \param c The sideband handle.
+ * \param iov Array of two I/O vectors.
+ *
+ * \return The number of buffers that need to be sent, either 1 or 2.
+ *
+ * This function fills out the buffers described by \a iov. The result can be
+ * passed to \ref xwritev() or similar.
+ *
+ * \sa \ref sb_get_recv_buffer().
+ */
+int sb_get_send_buffers(struct sb_context *c, struct iovec iov[2])
+{
+ struct sb_buffer *sbb = &c->sbb;
+ size_t n = c->bytes_dispatched;
+
+ if (n < SIDEBAND_HEADER_SIZE) {
+ iov[0].iov_base = c->header + n;
+ iov[0].iov_len = SIDEBAND_HEADER_SIZE - n;
+ if (!iov_valid(&sbb->iov))
+ goto out;
+ iov[1] = sbb->iov;
+ return 2;
+ }
+ n -= SIDEBAND_HEADER_SIZE;
+ assert(n < sbb->iov.iov_len);
+ iov[0].iov_base = sbb->iov.iov_base + n;
+ iov[0].iov_len = sbb->iov.iov_len - n;
+out:
+ iov[1].iov_base = NULL;
+ iov[1].iov_len = 0;
+ return 1;
+}
+
+/**
+ * Update the sideband context after data has been sent.
+ *
+ * \param c The sideband handle.
+ * \param nbytes The number of sent bytes.
+ *
+ * \return False if more data must be sent to complete the sideband transfer,
+ * true if the transfer is complete. In this case all resources are freed and
+ * the sideband handle must not be used any more.
+ */
+bool sb_sent(struct sb_context *c, size_t nbytes)
+{
+ struct sb_buffer *sbb = &c->sbb;
+ size_t sz = SIDEBAND_HEADER_SIZE + sbb->iov.iov_len;
+
+ assert(c->bytes_dispatched + nbytes <= sz);
+ c->bytes_dispatched += nbytes;
+ if (c->bytes_dispatched < sz)
+ return false;
+ sb_free(c);
+ return true;
+}
+
+/**
+ * Obtain a pointer to the next sideband read buffer.
+ *
+ * \param c The sideband handle.
+ * \param iov Result IO vector.
+ *
+ * This fills in \a iov to point to the buffer to which the next chunk of
+ * received data should be written.
+ */
+void sb_get_recv_buffer(struct sb_context *c, struct iovec *iov)
+{
+ struct sb_buffer *sbb = &c->sbb;
+ size_t n = c->bytes_dispatched;
+
+ if (n < SIDEBAND_HEADER_SIZE) {
+ iov->iov_base = c->header + n;
+ iov->iov_len = SIDEBAND_HEADER_SIZE - n;
+ return;
+ }
+ n -= SIDEBAND_HEADER_SIZE;
+ assert(sbb->iov.iov_base);
+ assert(sbb->iov.iov_len > n);
+ iov->iov_base = sbb->iov.iov_base + n;
+ iov->iov_len = sbb->iov.iov_len - n;
+}
+
+/**
+ * Update the sideband context after data has been received.
+ *
+ * \param c The sideband handle.
+ * \param nbytes The number of bytes that have been received.
+ * \param result The received sideband packet.
+ *
+ * \return Negative on errors, zero if more data needs to be read to complete
+ * this sideband packet, one if the sideband packet has been received
+ * completely.
+ *
+ * Only if the function returns one, the sideband buffer pointed to by \a
+ * result is set to point to the received data.
+ */
+int sb_received(struct sb_context *c, size_t nbytes, struct sb_buffer *result)
+{
+ struct sb_buffer *sbb = &c->sbb;
+ size_t n = c->bytes_dispatched,
+ sz = SIDEBAND_HEADER_SIZE + sbb->iov.iov_len;
+
+ assert(n + nbytes <= sz);
+ c->bytes_dispatched += nbytes;
+ if (c->bytes_dispatched < SIDEBAND_HEADER_SIZE)
+ return 0;
+ if (n >= SIDEBAND_HEADER_SIZE) { /* header has already been received */
+ if (c->bytes_dispatched < sz) /* need to recv more body data */
+ return 0;
+ /* received everything, decrypt and return sbb */
+ if (c->trafo) {
+ struct iovec dst;
+ c->trafo(&sbb->iov, &dst, c->trafo_context);
+ if (sbb->iov.iov_base != dst.iov_base) {
+ free(sbb->iov.iov_base);
+ sbb->iov.iov_base = dst.iov_base;
+ }
+ }
+ ((char *)(sbb->iov.iov_base))[sbb->iov.iov_len] = '\0';
+ goto success;
+ }
+ /* header has been received, decrypt and decode it */
+ if (c->trafo) { /* decrypt */
+ struct iovec dst, src = (typeof(src)) {
+ .iov_base = c->header,
+ .iov_len = SIDEBAND_HEADER_SIZE
+ };
+ c->trafo(&src, &dst, c->trafo_context);
+ if (src.iov_base != dst.iov_base) {
+ memcpy(c->header, dst.iov_base,
+ SIDEBAND_HEADER_SIZE);
+ free(dst.iov_base);
+ }
+ }
+ /* Decode header, setup sbb */
+ sbb->iov.iov_len = read_u32(c->header);
+ sbb->band = read_u8(c->header + 4);
+ sbb->iov.iov_base = NULL;
+ if (sbb->iov.iov_len == 0) /* zero-sized msg */
+ goto success;
+ if (c->max_size > 0 && sbb->iov.iov_len > c->max_size) {
+ PARA_ERROR_LOG("packet too big (is %zu, max %zu)\n",
+ sbb->iov.iov_len, c->max_size);
+ return -E_SB_PACKET_SIZE;
+ }
+ /*
+ * We like to reserve one extra byte for the terminating NULL
+ * character. However, we must make sure the +1 below does not
+ * overflow iov_len.
+ */
+ if (sbb->iov.iov_len == (size_t)-1)
+ return -E_SB_PACKET_SIZE;
+ sbb->iov.iov_base = para_malloc(sbb->iov.iov_len + 1);
+ return 0; /* ready to read body */
+success:
+ *result = c->sbb;
+ free(c);
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2012 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file sideband.h exported symbols from sideband.c */
+
+/**
+ * The valid sideband designators.
+ *
+ * A sideband packet consists of a header and a body. The header is sent prior
+ * to the data and contains the length of the buffer and the sideband
+ * designator, an integer. This scheme allows receivers to never read more data
+ * than what was specified in the header.
+ *
+ * The sideband designator indicates the type of the data. The authentication
+ * handshake between the command handler (child of para_server) and the client
+ * requires to exchange several small packages. Each such package is sent as a
+ * sideband package with a dedicated designator.
+ *
+ * Other designators are employed for normal command output and for log
+ * messages, where each loglevel corresponds to a sideband designator.
+ *
+ * Note that the values of this enum are part of the ABI, so never change
+ * or remove entries. Appending is OK though.
+ */
+#define SB_DESIGNATORS \
+ /* Special value for receiving only: Accept any band. */ \
+ DESIGNATOR(ANY), \
+ /* This packet contains the authentication challenge. */ \
+ DESIGNATOR(CHALLENGE), \
+ /* I solved your challenge and here is the proof. */ \
+ DESIGNATOR(CHALLENGE_RESPONSE), \
+ /* Congratulations, you are authenticated. */ \
+ DESIGNATOR(PROCEED), \
+ /* This is the command I want you to execute. */ \
+ DESIGNATOR(COMMAND), \
+ /* Ready to receive a blob (addblob commands only). */ \
+ DESIGNATOR(AWAITING_DATA), \
+ /* Normal command output (default). */ \
+ DESIGNATOR(OUTPUT), \
+ /* LL_DEBUG. */ \
+ DESIGNATOR(DEBUG_LOG), \
+ /* LL_INFO. */ \
+ DESIGNATOR(INFO_LOG), \
+ /* LL_NOTICE. */ \
+ DESIGNATOR(NOTICE_LOG), \
+ /* LL_WARNING, */ \
+ DESIGNATOR(WARNING_LOG), \
+ /* LL_ERROR. */ \
+ DESIGNATOR(ERROR_LOG), \
+ /* LL_CRIT. */ \
+ DESIGNATOR(CRIT_LOG), \
+ /* LL_EMERG. */ \
+ DESIGNATOR(EMERG_LOG), \
+ /* Command terminated successfully. */ \
+ DESIGNATOR(EXIT__SUCCESS), \
+ /* Command failed. */ \
+ DESIGNATOR(EXIT__FAILURE), \
+
+/** Just prefix with \p SBD_. */
+#define DESIGNATOR(x) SBD_ ## x
+
+/** All valid sideband designators. */
+enum sb_designator {SB_DESIGNATORS NUM_SB_DESIGNATORS};
+#undef DESIGNATOR
+/** One stringified sideband designator. */
+#define DESIGNATOR(x) #x
+/** List of stringified sidedband designators. */
+#define SB_DESIGNATORS_ARRAY SB_DESIGNATORS
+
+/**
+ * The information contained in a sideband buffer.
+ *
+ * This structure is used for both sending and receiving data through a
+ * sideband channel. A pointer to a sideband buffer is passed to the sending
+ * side of a sideband while the receiving end yields a filled out structure
+ * once all data has been received.
+ */
+struct sb_buffer {
+ /** Data and length. */
+ struct iovec iov;
+ /** The band designator. */
+ uint8_t band;
+};
+
+/**
+ * The opaque sideband context structure.
+ *
+ * A pointer to a structure of this type is returned by the two \a sb_new_xxx
+ * functions as an opaque handle. Other public functions of the sideband API
+ * take such a handle as a parameter.
+ */
+struct sb_context;
+
+/**
+ * The type of a sideband transformation.
+ *
+ * The sideband API allows to filter all data through an arbitrary
+ * transformation, which is useful for crypto purposes. The transformation may
+ * either transform the data in place, or return a pointer to a new buffer
+ * which contains the transformed source buffer. The internal sideband
+ * functions can tell the two types of transformations apart by checking
+ * whether the destination buffer coincides with the source buffer.
+ *
+ * If the transformation allocates a new buffer, it _must_ allocate one extra
+ * byte for \p NULL termination.
+ */
+typedef void (*sb_transformation)(struct iovec *src, struct iovec *dst,
+ void *trafo_context);
+
+
+/** Initialize a sideband buffer. */
+#define SBB_INIT(_band, _buf, _numbytes) \
+ { \
+ .band = band, \
+ .iov = { \
+ .iov_base = _buf, \
+ .iov_len = _numbytes \
+ } \
+ };
+
+/* receiving */
+struct sb_context *sb_new_recv(size_t max_size, sb_transformation t,
+ void *trafo_context);
+void sb_get_recv_buffer(struct sb_context *c, struct iovec *iov);
+int sb_received(struct sb_context *c, size_t nbytes, struct sb_buffer *result);
+
+/* sending */
+struct sb_context *sb_new_send(struct sb_buffer *sbb, bool dont_free,
+ sb_transformation t, void *trafo_context);
+int sb_get_send_buffers(struct sb_context *c, struct iovec iov[2]);
+bool sb_sent(struct sb_context *c, size_t nbytes);
+
+void sb_free(struct sb_context *c);
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.
*
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);
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;
};
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];
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);
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;
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;
* 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;
/**