Was cooking in next for several weeks with no problems so far.
fd864c Fix documentation of pass_buffer_as_shm().
32ffc0 Pass command exit status via sideband to client.
7dc1b5 Send command output via sideband.
0229d7 Introduce send_strerror().
d5a9e8 Simplify pass_buffer_as_shm().
3667d7 command.c: Improve get_status().
aa3fe7 Pass command via sideband.
9c2a26 Use sideband also for "proceed" handshake.
4744d9 Use sideband also for challenge response.
fa9e0e Send and receive challenge via sideband.
2830b9 Add sideband implementation.
e151db Implement client-server feature negotiation.
e31d5d afs: Rename sc_send_result() to afs_cb_result_handler().
68cb0a Introduce afs_max_size_handler_data and afs_max_size_handler().
d04b83 client: Improve error diagnostics.
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.
- Documentation improvements.
#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;
}
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
+IN: para error crypt sideband 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);
}
#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;
}
#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"
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;
}
+/**
+ * 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 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;
+}
+
int com_sender(struct command_context *cc)
{
- int i, ret;
+ 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;
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)
{
+ 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
{
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 */
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;
}
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);
mmd->events++;
mmd->active_connections--;
mutex_unlock(mmd_mutex);
+ if (ret < 0)
+ exit(EXIT_FAILURE);
+ if (!cc->use_sideband)
+ exit(EXIT_SUCCESS);
+ ret = send_sb(&cc->scc, NULL, 0, SBD_EXIT__SUCCESS, true);
+ if (ret < 0)
+ PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
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);
exec send_common ggo udp_recv color fec fecdec_filter
prebuffer_filter audiod_command_list 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
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=""
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"
+ afs_command_list 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
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"
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"), \
* 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);
}
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);
}
CC: array of server commands
AT: server_command
SI: osl regex
-IN: para error crypt command string afh afs server list user_list
+IN: para error crypt sideband 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);