0.4.10 (to be announced) "heterogeneous vacuum"
-----------------------------------------------
+ - The --no_default_filters option of para_filter has been
+ depricated. It still works but has no effect and will be
+ removed in the next version.
+ - Cleanup and consolidation of the various wrappers for
+ write(), writev(), send() and friends.
+ - The obscure error messages on mmap() failures have been
+ replaced by meaningful messages. This affects mainly
+ para_afh.
+
-------------------------------------
0.4.9 (2011-12-06) "hybrid causality"
-------------------------------------
&header, &size);
if (size > 0) {
PARA_INFO_LOG("writing header (%zu bytes)\n", size);
- ret = write(STDOUT_FILENO, header, size); /* FIXME */
+ ret = write_all(STDOUT_FILENO, header, size);
afh_free_header(header, audio_format_id);
if (ret < 0)
return ret;
if (!size)
continue;
PARA_INFO_LOG("writing chunk %lu\n", i);
- ret = write_all(STDOUT_FILENO, buf, &size);
+ ret = write_all(STDOUT_FILENO, buf, size);
if (ret < 0)
return ret;
}
#include "signal.h"
#include "fd.h"
#include "mood.h"
+#include "command.h"
/** The osl tables used by afs. \sa blob.c. */
enum afs_table_num {
if (ret < 0)
goto out;
fd = ret;
- ret = send_bin_buffer(fd, buf, sizeof(buf));
+ ret = write_all(fd, buf, sizeof(buf));
if (ret < 0)
goto out;
/*
no_admissible_files:
*(uint32_t *)buf = NO_ADMISSIBLE_FILES;
*(uint32_t *)(buf + 4) = (uint32_t)0;
- return send_bin_buffer(server_socket, buf, 8);
+ return write_all(server_socket, buf, 8);
}
/* Never fails if arg == NULL */
*/
int sc_send_result(struct osl_object *result, void *private)
{
- struct stream_cipher_context *scc = private;
+ struct command_context *cc = private;
+ int ret;
if (!result->size)
return 1;
- return sc_send_bin_buffer(scc, result->data, result->size);
+ ret = sc_send_bin_buffer(&cc->scc, result->data, result->size);
+ if (ret < 0 || ret == result->size)
+ return ret;
+ return -E_SHORT_WRITE;
}
-int com_select(struct stream_cipher_context *scc, int argc, char * const * const argv)
+int com_select(struct command_context *cc)
{
struct osl_object query;
- if (argc != 2)
+ if (cc->argc != 2)
return -E_AFS_SYNTAX;
- query.data = argv[1];
- query.size = strlen(argv[1]) + 1;
+ query.data = cc->argv[1];
+ query.size = strlen(cc->argv[1]) + 1;
return send_callback_request(com_select_callback, &query,
- &sc_send_result, scc);
+ &sc_send_result, cc);
}
static void init_admissible_files(char *arg)
ret = shm_detach(shm);
if (ret < 0)
goto err;
- ret = send_bin_buffer(fd, (char *)&shmid, sizeof(int));
+ ret = write_all(fd, (char *)&shmid, sizeof(int));
if (ret >= 0)
return ret;
err:
free(pb.buf);
}
-int com_init(struct stream_cipher_context *scc, int argc, char * const * const argv)
+int com_init(struct command_context *cc)
{
int i, j, ret;
uint32_t table_mask = (1 << (NUM_AFS_TABLES + 1)) - 1;
ret = make_database_dir();
if (ret < 0)
return ret;
- if (argc != 1) {
+ if (cc->argc != 1) {
table_mask = 0;
- for (i = 1; i < argc; i++) {
+ for (i = 1; i < cc->argc; i++) {
for (j = 0; j < NUM_AFS_TABLES; j++) {
struct afs_table *t = &afs_tables[j];
- if (strcmp(argv[i], t->name))
+ if (strcmp(cc->argv[i], t->name))
continue;
table_mask |= (1 << j);
break;
}
}
ret = send_callback_request(create_tables_callback, &query,
- sc_send_result, scc);
+ sc_send_result, cc);
if (ret < 0)
/* ignore return value */
- sc_send_va_buffer(scc, "%s\n", para_strerror(-ret));
+ sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
return ret;
}
CHECK_PLAYLISTS = 4
};
-int com_check(struct stream_cipher_context *scc, int argc, char * const * const argv)
+int com_check(struct command_context *cc)
{
unsigned flags = 0;
int i, ret;
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ for (i = 1; i < cc->argc; i++) {
+ const char *arg = cc->argv[i];
if (arg[0] != '-')
break;
if (!strcmp(arg, "--")) {
}
return -E_AFS_SYNTAX;
}
- if (i < argc)
+ if (i < cc->argc)
return -E_AFS_SYNTAX;
if (!flags)
flags = ~0U;
if (flags & CHECK_AFT) {
ret = send_callback_request(aft_check_callback, NULL,
- sc_send_result, scc);
+ sc_send_result, cc);
if (ret < 0)
return ret;
}
if (flags & CHECK_PLAYLISTS) {
ret = send_callback_request(playlist_check_callback,
- NULL, sc_send_result, scc);
+ NULL, sc_send_result, cc);
if (ret < 0)
return ret;
}
if (flags & CHECK_MOODS) {
ret = send_callback_request(mood_check_callback, NULL,
- sc_send_result, scc);
+ sc_send_result, cc);
if (ret < 0)
return ret;
}
---
T: add
N: add@member@
-O: int com_add@member@(struct stream_cipher_context *scc, int argc, char * const * const argv);
+O: int com_add@member@(struct command_context *cc);
P: AFS_READ | AFS_WRITE
D: Read data from stdin and add it as a blob to the @member@ table.
U: add@member@ @member@_name
---
T: cat
N: cat@member@
-O: int com_cat@member@(struct stream_cipher_context *scc, int argc, char * const * const argv);
+O: int com_cat@member@(struct command_context *cc);
P: AFS_READ
D: Dump the contents of a blob of type @member@ to stdout.
U: cat@member@ @member@_name
---
T: ls
N: ls@member@
-O: int com_ls@member@(struct stream_cipher_context *scc, int argc, char * const * const argv);
+O: int com_ls@member@(struct command_context *cc);
P: AFS_READ
D: List blobs of type @member@ matching a pattern.
U: ls@member@ [-i] [-l] [-r] [pattern]
---
T: rm
N: rm@member@
-O: int com_rm@member@(struct stream_cipher_context *scc, int argc, char * const * const argv);
+O: int com_rm@member@(struct command_context *cc);
P: AFS_READ | AFS_WRITE
D: Remove blob(s) of type @member@ from the @member@ table.
U: rm@member@ pattern...
---
T: mv
N: mv@member@
-O: int com_mv@member@(struct stream_cipher_context *scc, int argc, char * const * const argv);
+O: int com_mv@member@(struct command_context *cc);
P: AFS_READ | AFS_WRITE
D: Rename a blob of type @member@.
U: mv@member@ old_@member@_name new_@member@_name
#include "fd.h"
#include "ipc.h"
#include "portable_io.h"
+#include "command.h"
static struct osl_table *audio_file_table;
static char *status_items;
/*
* TODO: flags -h (sort by hash)
*/
-int com_ls(struct stream_cipher_context *scc, int argc, char * const * const argv)
+int com_ls(struct command_context *cc)
{
int i, ret;
unsigned flags = 0;
struct ls_options opts = {.patterns = NULL};
struct osl_object query = {.data = &opts, .size = sizeof(opts)};
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ for (i = 1; i < cc->argc; i++) {
+ const char *arg = cc->argv[i];
if (arg[0] != '-')
break;
if (!strcmp(arg, "--")) {
opts.flags = flags;
opts.sorting = sort;
opts.mode = mode;
- opts.num_patterns = argc - i;
+ opts.num_patterns = cc->argc - i;
ret = send_option_arg_callback_request(&query, opts.num_patterns,
- argv + i, com_ls_callback, sc_send_result, scc);
+ cc->argv + i, com_ls_callback, sc_send_result, cc);
return ret;
}
/** Used by com_add(). */
struct private_add_data {
- /** The socket file descriptor, including stream cipher keys. */
- struct stream_cipher_context *scc;
+ /** The pointer passed to the original command handler. */
+ struct command_context *cc;
/** The given add flags. */
uint32_t flags;
};
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->scc,
+ send_ret = sc_send_va_buffer(&pad->cc->scc,
"lazy-ignore: %s\n", path);
goto out_free;
}
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->scc,
+ send_ret = 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->scc, "adding %s\n", path);
+ send_ret = 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->scc);
+ ret = send_callback_request(com_add_callback, &obj, sc_send_result, 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->scc,
+ send_ret = sc_send_va_buffer(&pad->cc->scc,
"failed to add %s (%s)\n", path, para_strerror(-ret));
free(obj.data);
if (afhi_ptr) {
return send_ret;
}
-int com_add(struct stream_cipher_context *scc, int argc, char * const * const argv)
+int com_add(struct command_context *cc)
{
int i, ret;
- struct private_add_data pad = {.scc = scc, .flags = 0};
+ struct private_add_data pad = {.cc = cc, .flags = 0};
struct stat statbuf;
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ for (i = 1; i < cc->argc; i++) {
+ const char *arg = cc->argv[i];
if (arg[0] != '-')
break;
if (!strcmp(arg, "--")) {
continue;
}
}
- if (argc <= i)
+ if (cc->argc <= i)
return -E_AFT_SYNTAX;
- for (; i < argc; i++) {
+ for (; i < cc->argc; i++) {
char *path;
- ret = verify_path(argv[i], &path);
+ ret = verify_path(cc->argv[i], &path);
if (ret < 0) {
- ret = sc_send_va_buffer(scc, "%s: %s\n", argv[i],
- para_strerror(-ret));
+ ret = sc_send_va_buffer(&cc->scc, "%s: %s\n",
+ cc->argv[i], para_strerror(-ret));
if (ret < 0)
return ret;
continue;
}
ret = stat(path, &statbuf);
if (ret < 0) {
- ret = sc_send_va_buffer(scc, "failed to stat %s (%s)\n", path,
+ ret = sc_send_va_buffer(&cc->scc,
+ "failed to stat %s (%s)\n", path,
strerror(errno));
free(path);
if (ret < 0)
else
ret = add_one_audio_file(path, &pad);
if (ret < 0) {
- sc_send_va_buffer(scc, "%s: %s\n", path, para_strerror(-ret));
+ sc_send_va_buffer(&cc->scc, "%s: %s\n", path,
+ para_strerror(-ret));
free(path);
return ret;
}
free(tad.pb.buf);
}
-int com_touch(struct stream_cipher_context *scc, int argc, char * const * const argv)
+int com_touch(struct command_context *cc)
{
struct com_touch_options cto = {
.num_played = -1,
int i, ret;
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ for (i = 1; i < cc->argc; i++) {
+ const char *arg = cc->argv[i];
if (arg[0] != '-')
break;
if (!strcmp(arg, "--")) {
}
break; /* non-option starting with dash */
}
- if (i >= argc)
+ if (i >= cc->argc)
return -E_AFT_SYNTAX;
- ret = send_option_arg_callback_request(&query, argc - i,
- argv + i, com_touch_callback, sc_send_result, scc);
+ ret = send_option_arg_callback_request(&query, cc->argc - i,
+ cc->argv + i, com_touch_callback, sc_send_result, cc);
if (ret < 0)
- sc_send_va_buffer(scc, "%s\n", para_strerror(-ret));
+ sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
return ret;
}
}
/* TODO options: -r (recursive) */
-int com_rm(struct stream_cipher_context *scc, int argc, char * const * const argv)
+int com_rm(struct command_context *cc)
{
uint32_t flags = 0;
struct osl_object query = {.data = &flags, .size = sizeof(flags)};
int i, ret;
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ for (i = 1; i < cc->argc; i++) {
+ const char *arg = cc->argv[i];
if (arg[0] != '-')
break;
if (!strcmp(arg, "--")) {
}
break;
}
- if (i >= argc)
+ if (i >= cc->argc)
return -E_AFT_SYNTAX;
- ret = send_option_arg_callback_request(&query, argc - i, argv + i,
- com_rm_callback, sc_send_result, scc);
+ ret = send_option_arg_callback_request(&query, cc->argc - i,
+ cc->argv + i, com_rm_callback, sc_send_result, cc);
if (ret < 0)
- sc_send_va_buffer(scc, "%s\n", para_strerror(-ret));
+ sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
return ret;
}
free(cad.pb.buf);
}
-int com_cpsi(struct stream_cipher_context *scc, int argc, char * const * const argv)
+int com_cpsi(struct command_context *cc)
{
unsigned flags = 0;
int i, ret;
struct osl_object options = {.data = &flags, .size = sizeof(flags)};
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ for (i = 1; i < cc->argc; i++) {
+ const char *arg = cc->argv[i];
if (arg[0] != '-')
break;
if (!strcmp(arg, "--")) {
}
break;
}
- if (i + 1 >= argc) /* need at least source file and pattern */
+ if (i + 1 >= cc->argc) /* need at least source file and pattern */
return -E_AFT_SYNTAX;
if (!(flags & ~CPSI_FLAG_VERBOSE)) /* no copy flags given */
flags = ~(unsigned)CPSI_FLAG_VERBOSE | flags;
- ret = send_option_arg_callback_request(&options, argc - i, argv + i,
- com_cpsi_callback, sc_send_result, scc);
+ ret = send_option_arg_callback_request(&options, cc->argc - i,
+ cc->argv + i, com_cpsi_callback, sc_send_result, cc);
if (ret < 0)
- sc_send_va_buffer(scc, "%s\n", para_strerror(-ret));
+ sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
return ret;
}
/**
* Get the current afs status items from the afs process and send it.
*
- * \param scc The stream cipher context for data encryption.
+ * \param cc The command context, used e.g. for data encryption.
* \param parser_friendly Whether parser-friendly output format should be used.
*
* As the contents of the afs status items change in time and the command
*
* \return The return value of the underyling call to \ref send_callback_request().
*/
-int send_afs_status(struct stream_cipher_context *scc, int parser_friendly)
+int send_afs_status(struct command_context *cc, int parser_friendly)
{
struct osl_object query = {.data = &parser_friendly,
.size = sizeof(parser_friendly)};
- return send_callback_request(afs_stat_callback, &query, sc_send_result, scc);
+ return send_callback_request(afs_stat_callback, &query, sc_send_result, cc);
}
/* TODO: optionally fix problems by removing offending rows */
#include "afh.h"
#include "afs.h"
#include "ipc.h"
+#include "command.h"
static struct osl_table *attribute_table;
static int greatest_att_bitnum;
free(laad.pb.buf);
}
-int com_lsatt(struct stream_cipher_context *scc, int argc, char * const * const argv)
+int com_lsatt(struct command_context *cc)
{
unsigned flags = 0;
struct osl_object options = {.data = &flags, .size = sizeof(flags)};
int ret, i;
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ for (i = 1; i < cc->argc; i++) {
+ const char *arg = cc->argv[i];
if (arg[0] != '-')
break;
if (!strcmp(arg, "--")) {
continue;
}
}
- ret = send_option_arg_callback_request(&options, argc - i, argv + i,
- com_lsatt_callback, sc_send_result, scc);
+ ret = send_option_arg_callback_request(&options, cc->argc - i, cc->argv + i,
+ com_lsatt_callback, sc_send_result, cc);
if (!ret) {
- if (argc > 1)
- ret = sc_send_va_buffer(scc, "no matches\n");
+ if (cc->argc > 1)
+ ret = sc_send_va_buffer(&cc->scc, "no matches\n");
} else if (ret < 0)
- sc_send_va_buffer(scc, "%s\n", para_strerror(-ret));
+ sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
return ret;
}
PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
}
-int com_setatt(__a_unused struct stream_cipher_context *scc, int argc, char * const * const argv)
+int com_setatt(struct command_context *cc)
{
- if (argc < 3)
+ if (cc->argc < 3)
return -E_ATTR_SYNTAX;
- return send_standard_callback_request(argc - 1, argv + 1, com_setatt_callback,
- NULL, NULL);
+ return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
+ com_setatt_callback, NULL, NULL);
}
struct addatt_event_data {
free(pb.buf);
}
-int com_addatt(struct stream_cipher_context *scc, int argc, char * const * const argv)
+int com_addatt(struct command_context *cc)
{
int ret;
- if (argc < 2)
+ if (cc->argc < 2)
return -E_ATTR_SYNTAX;
- ret = send_standard_callback_request(argc - 1, argv + 1, com_addatt_callback,
- sc_send_result, scc);
+ ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
+ com_addatt_callback, sc_send_result, cc);
if (ret < 0)
- sc_send_va_buffer(scc, "%s\n", para_strerror(-ret));
+ sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
return ret;
}
free(pb.buf);
}
-int com_mvatt(struct stream_cipher_context *scc, int argc, char * const * const argv)
+int com_mvatt(struct command_context *cc)
{
int ret;
- if (argc != 3)
+ if (cc->argc != 3)
return -E_ATTR_SYNTAX;
- ret = send_standard_callback_request(argc - 1, argv + 1, com_mvatt_callback,
- sc_send_result, scc);
+ ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
+ com_mvatt_callback, sc_send_result, cc);
if (ret < 0)
- sc_send_va_buffer(scc, "%s\n", para_strerror(-ret));
+ sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
return ret;
}
free(raad.pb.buf);
}
-int com_rmatt(struct stream_cipher_context *scc, int argc, char * const * const argv)
+int com_rmatt(struct command_context *cc)
{
int ret;
- if (argc < 2)
+ if (cc->argc < 2)
return -E_ATTR_SYNTAX;
- ret = send_standard_callback_request(argc - 1, argv + 1, com_rmatt_callback,
- sc_send_result, scc);
+ ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
+ com_rmatt_callback, sc_send_result, cc);
if (ret < 0)
- sc_send_va_buffer(scc, "%s\n", para_strerror(-ret));
+ sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
return ret;
}
size_t n = ret = recv_bin_buffer(fd, buf, bufsize);
if (ret <= 0)
break;
- ret = write_all(STDOUT_FILENO, buf, &n);
+ ret = write_all(STDOUT_FILENO, buf, n);
} while (ret >= 0);
out:
if (ret < 0)
{
int i, j, ret, af_mask;
- if (!conf.no_default_filters_given)
- return init_default_filters();
+ if (conf.no_default_filters_given) {
+ PARA_WARNING_LOG("--no_default_filters is deprecated\n");
+ PARA_WARNING_LOG("It has no effect and will be removed soon\n");
+ }
for (i = 0; i < conf.filter_given; i++) {
char *arg;
ret = parse_stream_command(conf.filter_arg[i], &arg);
#include "afs.h"
#include "ipc.h"
#include "portable_io.h"
+#include "command.h"
/**
* Compare two osl objects pointing to unsigned integers of 32 bit size.
free(lbad.pb.buf);
}
-static int com_lsblob(callback_function *f, struct stream_cipher_context *scc, int argc, char * const * const argv)
+static int com_lsblob(callback_function *f, struct command_context *cc)
{
uint32_t flags = 0;
struct osl_object options = {.data = &flags, .size = sizeof(flags)};
int i;
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ for (i = 1; i < cc->argc; i++) {
+ const char *arg = cc->argv[i];
if (arg[0] != '-')
break;
if (!strcmp(arg, "--")) {
}
// if (argc > i)
// return -E_BLOB_SYNTAX;
- return send_option_arg_callback_request(&options, argc - i,
- argv + i, f, sc_send_result, scc);
+ return send_option_arg_callback_request(&options, cc->argc - i,
+ cc->argv + i, f, sc_send_result, cc);
}
static int cat_blob(struct osl_table *table, struct osl_row *row,
}
}
-static int com_catblob(callback_function *f, struct stream_cipher_context *scc, int argc,
- char * const * const argv)
+static int com_catblob(callback_function *f, struct command_context *cc)
{
- if (argc < 2)
+ if (cc->argc < 2)
return -E_BLOB_SYNTAX;
- return send_standard_callback_request(argc - 1, argv + 1, f,
- sc_send_result, scc);
+ return send_standard_callback_request(cc->argc - 1, cc->argv + 1, f,
+ sc_send_result, cc);
}
/** Used for removing rows from a blob table. */
free(rmbd.pb.buf);
}
-static int com_rmblob(callback_function *f, struct stream_cipher_context *scc, int argc,
- char * const * const argv)
+static int com_rmblob(callback_function *f, struct command_context *cc)
{
- if (argc < 2)
+ if (cc->argc < 2)
return -E_MOOD_SYNTAX;
- return send_option_arg_callback_request(NULL, argc - 1, argv + 1, f,
- sc_send_result, scc);
+ return send_option_arg_callback_request(NULL, cc->argc - 1, cc->argv + 1, f,
+ sc_send_result, cc);
}
static void com_addblob_callback(struct osl_table *table, __a_unused int fd,
* \return Negative on errors, the return value of the underlying call to
* send_callback_request() otherwise.
*/
-static int stdin_command(struct stream_cipher_context *scc, struct osl_object *arg_obj,
+static int stdin_command(struct command_context *cc, struct osl_object *arg_obj,
callback_function *f, unsigned max_len,
callback_result_handler *result_handler,
void *private_result_data)
struct osl_object query, stdin_obj;
int ret;
- ret = sc_send_buffer(scc, AWAITING_DATA_MSG);
+ ret = sc_send_buffer(&cc->scc, AWAITING_DATA_MSG);
if (ret < 0)
return ret;
- ret = fd2buf(scc, max_len, &stdin_obj);
+ ret = fd2buf(&cc->scc, max_len, &stdin_obj);
if (ret < 0)
return ret;
query.size = arg_obj->size + stdin_obj.size;
return ret;
}
-static int com_addblob(callback_function *f, struct stream_cipher_context *scc, int argc,
- char * const * const argv)
+static int com_addblob(callback_function *f, struct command_context *cc)
{
struct osl_object arg_obj;
- if (argc != 2)
+ if (cc->argc != 2)
return -E_BLOB_SYNTAX;
- if (!*argv[1]) /* empty name is reserved for the dummy row */
+ if (!*cc->argv[1]) /* empty name is reserved for the dummy row */
return -E_BLOB_SYNTAX;
- arg_obj.size = strlen(argv[1]) + 1;
- arg_obj.data = (char *)argv[1];
- return stdin_command(scc, &arg_obj, f, 10 * 1024 * 1024, NULL, NULL);
+ arg_obj.size = strlen(cc->argv[1]) + 1;
+ arg_obj.data = (char *)cc->argv[1];
+ return stdin_command(cc, &arg_obj, f, 10 * 1024 * 1024, NULL, NULL);
}
/* FIXME: Print output to client, not to log file */
PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
}
-static int com_mvblob(callback_function *f, __a_unused struct stream_cipher_context *scc,
- int argc, char * const * const argv)
+static int com_mvblob(callback_function *f, struct command_context *cc)
{
- if (argc != 3)
+ if (cc->argc != 3)
return -E_MOOD_SYNTAX;
- return send_option_arg_callback_request(NULL, argc - 1, argv + 1, f,
- NULL, NULL);
+ return send_option_arg_callback_request(NULL, cc->argc - 1,
+ cc->argv + 1, f, NULL, NULL);
}
#define DEFINE_BLOB_COMMAND(cmd_name, table_name, cmd_prefix) \
{ \
return com_ ## cmd_name ## blob_callback(table_name ## _table, fd, query); \
} \
- int com_ ## cmd_name ## cmd_prefix(struct stream_cipher_context *scc, int argc, char * const * const argv) \
+ int com_ ## cmd_name ## cmd_prefix(struct command_context *cc) \
{ \
- return com_ ## cmd_name ## blob(com_ ## cmd_name ## cmd_prefix ## _callback, scc, argc, argv); \
+ return com_ ## cmd_name ## blob(com_ ## cmd_name ## cmd_prefix ## _callback, cc); \
}
static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
PARA_INFO_LOG("--> %s\n", buf);
if (!FD_ISSET(ct->scc.fd, &s->wfds))
return;
- ret = send_buffer(ct->scc.fd, buf);
+ ret = write_buffer(ct->scc.fd, buf);
if (ret < 0)
goto out;
ct->status = CL_SENT_AUTH;
SESSION_KEY_LEN);
hash_to_asc(challenge_hash, buf);
PARA_INFO_LOG("--> %s\n", buf);
- ret = send_bin_buffer(ct->scc.fd, (char *)challenge_hash,
- HASH_SIZE);
+ ret = write_all(ct->scc.fd, (char *)challenge_hash, HASH_SIZE);
if (ret < 0)
goto out;
ct->status = CL_SENT_CH_RESPONSE;
extern int mmd_mutex;
extern struct misc_meta_data *mmd;
extern struct sender senders[];
-int send_afs_status(struct stream_cipher_context *scc, int parser_friendly);
+int send_afs_status(struct command_context *cc, int parser_friendly);
const char *status_item_list[] = {STATUS_ITEM_ARRAY};
return 1;
}
-int com_sender(struct stream_cipher_context *scc, int argc, char * const * argv)
+int com_sender(struct command_context *cc)
{
int i, ret;
char *msg = NULL;
struct sender_command_data scd;
- if (argc < 2) {
+ if (cc->argc < 2) {
for (i = 0; senders[i].name; i++) {
char *tmp = make_message("%s%s\n",
msg? msg : "", senders[i].name);
free(msg);
msg = tmp;
}
- ret = sc_send_buffer(scc, msg);
+ ret = sc_send_buffer(&cc->scc, msg);
free(msg);
return ret;
}
- ret = check_sender_args(argc, argv, &scd);
+ ret = check_sender_args(cc->argc, cc->argv, &scd);
if (ret < 0) {
if (scd.sender_num < 0)
return ret;
msg = senders[scd.sender_num].help();
- ret = sc_send_buffer(scc, msg);
+ ret = sc_send_buffer(&cc->scc, msg);
free(msg);
return ret;
}
case SENDER_ADD:
case SENDER_DELETE:
assert(senders[scd.sender_num].resolve_target);
- ret = senders[scd.sender_num].resolve_target(argv[3], &scd);
+ ret = senders[scd.sender_num].resolve_target(cc->argv[3], &scd);
if (ret < 0)
return ret;
}
}
/* server info */
-int com_si(struct stream_cipher_context *scc, int argc, __a_unused char * const * argv)
+int com_si(struct command_context *cc)
{
int i, ret;
char *ut;
char *sender_info = NULL;
- if (argc != 1)
+ if (cc->argc != 1)
return -E_COMMAND_SYNTAX;
mutex_lock(mmd_mutex);
for (i = 0; senders[i].name; i++) {
free(info);
}
ut = get_server_uptime_str(now);
- ret = sc_send_va_buffer(scc, "version: " GIT_VERSION "\n"
+ ret = sc_send_va_buffer(&cc->scc, "version: " GIT_VERSION "\n"
"up: %s\nplayed: %u\n"
"server_pid: %d\n"
"afs_pid: %d\n"
}
/* version */
-int com_version(struct stream_cipher_context *scc, int argc, __a_unused char * const * argv)
+int com_version(struct command_context *cc)
{
- if (argc != 1)
+ if (cc->argc != 1)
return -E_COMMAND_SYNTAX;
- return sc_send_buffer(scc, VERSION_TEXT("server")
+ return sc_send_buffer(&cc->scc, VERSION_TEXT("server")
"built: " BUILD_DATE "\n"
UNAME_RS ", " CC_VERSION "\n"
);
#undef EMPTY_STATUS_ITEMS
/* stat */
-int com_stat(struct stream_cipher_context *scc, int argc, char * const * argv)
+int com_stat(struct command_context *cc)
{
int i, ret;
struct misc_meta_data tmp, *nmmd = &tmp;
para_sigaction(SIGUSR1, dummy);
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ for (i = 1; i < cc->argc; i++) {
+ const char *arg = cc->argv[i];
if (arg[0] != '-')
break;
if (!strcmp(arg, "--")) {
}
return -E_COMMAND_SYNTAX;
}
- if (i != argc)
+ if (i != cc->argc)
return -E_COMMAND_SYNTAX;
for (;;) {
mmd_dup(nmmd);
s = get_status(nmmd, parser_friendly);
- ret = sc_send_buffer(scc, s);
+ ret = sc_send_buffer(&cc->scc, s);
free(s);
if (ret < 0)
goto out;
static char *esi;
if (!esi)
esi = empty_status_items(parser_friendly);
- ret = sc_send_buffer(scc, esi);
+ ret = sc_send_buffer(&cc->scc, esi);
if (ret < 0)
goto out;
} else
- send_afs_status(scc, parser_friendly);
+ send_afs_status(cc, parser_friendly);
ret = 1;
if (num > 0 && !--num)
goto out;
}
/* help */
-int com_help(struct stream_cipher_context *scc, int argc, char * const * argv)
+int com_help(struct command_context *cc)
{
struct server_command *cmd;
char *perms, *handler;
int ret;
- if (argc < 2) {
+ if (cc->argc < 2) {
/* no argument given, print list of commands */
- if ((ret = send_list_of_commands(scc, server_cmds, "server")) < 0)
+ if ((ret = send_list_of_commands(&cc->scc, server_cmds, "server")) < 0)
return ret;
- return send_list_of_commands(scc, afs_cmds, "afs");
+ return send_list_of_commands(&cc->scc, afs_cmds, "afs");
}
/* argument given for help */
- cmd = get_cmd_ptr(argv[1], &handler);
+ 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(scc,
+ ret = sc_send_va_buffer(&cc->scc,
"%s - %s\n\n"
"handler: %s\n"
"permissions: %s\n"
"usage: %s\n\n"
"%s\n",
- argv[1],
+ cc->argv[1],
cmd->description,
handler,
perms,
}
/* hup */
-int com_hup(__a_unused struct stream_cipher_context *scc, int argc, __a_unused char * const * argv)
+int com_hup(struct command_context *cc)
{
- if (argc != 1)
+ if (cc->argc != 1)
return -E_COMMAND_SYNTAX;
kill(getppid(), SIGHUP);
return 1;
}
/* term */
-int com_term(__a_unused struct stream_cipher_context *scc, int argc, __a_unused char * const * argv)
+int com_term(struct command_context *cc)
{
- if (argc != 1)
+ if (cc->argc != 1)
return -E_COMMAND_SYNTAX;
kill(getppid(), SIGTERM);
return 1;
}
-int com_play(__a_unused struct stream_cipher_context *scc, int argc, __a_unused char * const * argv)
+int com_play(struct command_context *cc)
{
- if (argc != 1)
+ if (cc->argc != 1)
return -E_COMMAND_SYNTAX;
mutex_lock(mmd_mutex);
mmd->new_vss_status_flags |= VSS_PLAYING;
mmd->new_vss_status_flags &= ~VSS_NOMORE;
mutex_unlock(mmd_mutex);
return 1;
-
}
/* stop */
-int com_stop(__a_unused struct stream_cipher_context *scc, int argc, __a_unused char * const * argv)
+int com_stop(struct command_context *cc)
{
- if (argc != 1)
+ if (cc->argc != 1)
return -E_COMMAND_SYNTAX;
mutex_lock(mmd_mutex);
mmd->new_vss_status_flags &= ~VSS_PLAYING;
}
/* pause */
-int com_pause(__a_unused struct stream_cipher_context *scc, int argc, __a_unused char * const * argv)
+int com_pause(struct command_context *cc)
{
- if (argc != 1)
+ if (cc->argc != 1)
return -E_COMMAND_SYNTAX;
mutex_lock(mmd_mutex);
if (!vss_paused() && !vss_stopped()) {
}
/* next */
-int com_next(__a_unused struct stream_cipher_context *scc, int argc, __a_unused char * const * argv)
+int com_next(struct command_context *cc)
{
- if (argc != 1)
+ if (cc->argc != 1)
return -E_COMMAND_SYNTAX;
mutex_lock(mmd_mutex);
mmd->events++;
}
/* nomore */
-int com_nomore(__a_unused struct stream_cipher_context *scc, int argc, __a_unused char * const * argv)
+int com_nomore(struct command_context *cc)
{
- if (argc != 1)
+ if (cc->argc != 1)
return -E_COMMAND_SYNTAX;
mutex_lock(mmd_mutex);
if (vss_playing() || vss_paused())
}
/* ff */
-int com_ff(__a_unused struct stream_cipher_context *scc, int argc, char * const * argv)
+int com_ff(struct command_context *cc)
{
long promille;
int ret, backwards = 0;
unsigned i;
char c;
- if (argc != 2)
+ if (cc->argc != 2)
return -E_COMMAND_SYNTAX;
- if (!(ret = sscanf(argv[1], "%u%c", &i, &c)))
+ if (!(ret = sscanf(cc->argv[1], "%u%c", &i, &c)))
return -E_COMMAND_SYNTAX;
if (ret > 1 && c == '-')
backwards = 1; /* jmp backwards */
}
/* jmp */
-int com_jmp(__a_unused struct stream_cipher_context *scc, int argc, char * const * argv)
+int com_jmp(struct command_context *cc)
{
long unsigned int i;
int ret;
- if (argc != 2)
+ if (cc->argc != 2)
return -E_COMMAND_SYNTAX;
- if (sscanf(argv[1], "%lu", &i) <= 0)
+ if (sscanf(cc->argv[1], "%lu", &i) <= 0)
return -E_COMMAND_SYNTAX;
mutex_lock(mmd_mutex);
ret = -E_NO_AUDIO_FILE;
*/
__noreturn void handle_connect(int fd, const char *peername)
{
- int ret, argc;
+ int ret;
char buf[4096];
unsigned char rand_buf[CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
unsigned char challenge_hash[HASH_SIZE];
- struct user *u;
- struct server_command *cmd = NULL;
- char **argv = NULL;
char *p, *command = NULL;
size_t numbytes;
- struct stream_cipher_context scc = {.fd = fd};
+ struct command_context cc_struct = {.peer = peername}, *cc = &cc_struct;
+ cc->scc.fd = fd;
reset_signals();
/* we need a blocking fd here as recv() might return EAGAIN otherwise. */
ret = mark_fd_blocking(fd);
if (ret < 0)
goto net_err;
/* send Welcome message */
- ret = send_va_buffer(fd, "This is para_server, version "
+ ret = write_va_buffer(fd, "This is para_server, version "
PACKAGE_VERSION ".\n" );
if (ret < 0)
goto net_err;
goto net_err;
p = buf + strlen(AUTH_REQUEST_MSG);
PARA_DEBUG_LOG("received auth request for user %s\n", p);
- u = lookup_user(p);
- if (u) {
+ cc->u = lookup_user(p);
+ if (cc->u) {
get_random_bytes_or_die(rand_buf, sizeof(rand_buf));
- ret = pub_encrypt(u->pubkey, rand_buf, sizeof(rand_buf),
+ ret = pub_encrypt(cc->u->pubkey, rand_buf, sizeof(rand_buf),
(unsigned char *)buf);
if (ret < 0)
goto net_err;
}
PARA_DEBUG_LOG("sending %u byte challenge + rc4 keys (%zu bytes)\n",
CHALLENGE_SIZE, numbytes);
- ret = send_bin_buffer(fd, buf, numbytes);
+ ret = write_all(fd, buf, numbytes);
if (ret < 0)
goto net_err;
/* recv challenge response */
numbytes = ret;
PARA_DEBUG_LOG("received %d bytes challenge response\n", ret);
ret = -E_BAD_USER;
- if (!u)
+ if (!cc->u)
goto net_err;
/*
* The correct response is the hash of the first CHALLENGE_SIZE bytes
goto net_err;
/* auth successful */
alarm(0);
- PARA_INFO_LOG("good auth for %s\n", u->name);
+ PARA_INFO_LOG("good auth for %s\n", cc->u->name);
/* init stream cipher keys with the second part of the random buffer */
- scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
- scc.send = sc_new(rand_buf + CHALLENGE_SIZE + SESSION_KEY_LEN, SESSION_KEY_LEN);
- ret = sc_send_buffer(&scc, PROCEED_MSG);
+ 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(&scc, &command);
+ ret = read_command(&cc->scc, &command);
if (ret == -E_COMMAND_SYNTAX)
goto err_out;
if (ret < 0)
goto net_err;
ret = -E_BAD_CMD;
- cmd = parse_cmd(command);
- if (!cmd)
+ cc->cmd = parse_cmd(command);
+ if (!cc->cmd)
goto err_out;
/* valid command, check permissions */
- ret = check_perms(u->perms, cmd);
+ ret = check_perms(cc->u->perms, cc->cmd);
if (ret < 0)
goto err_out;
/* valid command and sufficient perms */
- ret = create_argv(command, "\n", &argv);
+ ret = create_argv(command, "\n", &cc->argv);
if (ret < 0)
goto err_out;
- argc = ret;
- PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cmd->name, u->name,
- peername);
- ret = cmd->handler(&scc, argc, argv);
- free_argv(argv);
+ 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);
+ free_argv(cc->argv);
mutex_lock(mmd_mutex);
mmd->num_commands++;
mutex_unlock(mmd_mutex);
if (ret >= 0)
goto out;
err_out:
- sc_send_va_buffer(&scc, "%s\n", para_strerror(-ret));
+ sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
net_err:
PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
out:
free(command);
- sc_free(scc.recv);
- sc_free(scc.send);
+ sc_free(cc->scc.recv);
+ sc_free(cc->scc.send);
mutex_lock(mmd_mutex);
- if (cmd && (cmd->perms & AFS_WRITE) && ret >= 0)
+ if (cc->cmd && (cc->cmd->perms & AFS_WRITE) && ret >= 0)
mmd->events++;
mmd->active_connections--;
mutex_unlock(mmd_mutex);
/** \file command.h The structure of server and afs commands. */
+/** Per connection data available to command handlers. */
+struct command_context {
+ /** Network address of the peer. */
+ const char *peer;
+ /** The paraslash user that executes this command. */
+ struct user *u;
+ /** Argument count. */
+ int argc;
+ /** Argument vector. */
+ char **argv;
+ /** The command being executed. */
+ struct server_command *cmd;
+ /** File descriptor and crypto keys. */
+ struct stream_cipher_context scc;
+};
+
/**
* Defines one command of para_server.
*/
/** The name of the command. */
const char *name;
/** Pointer to the function that handles the command. */
- int (*handler)(struct stream_cipher_context *, int, char * const * const);
+ int (*handler)(struct command_context *);
/** The privileges a user must have to execute this command. */
unsigned int perms;
/** One-line description of the command. */
memcpy(remainder, buf + l1, len - l1);
RC4(&scc->send->key, len - l1, remainder, tmp + l1);
}
- ret = write_all(scc->fd, (char *)tmp, &len);
+ ret = xwrite(scc->fd, (char *)tmp, len);
free(tmp);
return ret;
}
int sc_send_buffer(struct stream_cipher_context *scc, char *buf)
{
- return sc_send_bin_buffer(scc, buf, strlen(buf));
+ size_t len = strlen(buf);
+ int ret = sc_send_bin_buffer(scc, buf, len);
+
+ if (ret < 0 || ret == len)
+ return ret;
+ return -E_SHORT_WRITE;
}
__printf_2_3 int sc_send_va_buffer(struct stream_cipher_context *scc,
{
char *msg;
int ret;
+ va_list ap;
- PARA_VSPRINTF(fmt, msg);
- ret = sc_send_buffer(scc, msg);
+ va_start(ap, fmt);
+ ret = xvasprintf(&msg, fmt, ap);
+ va_end(ap);
+ ret = sc_send_bin_buffer(scc, msg, ret);
free(msg);
return ret;
}
static void dccp_send_fec(struct sender_client *sc, char *buf, size_t len)
{
- int ret = write_nonblock(sc->fd, buf, len);
+ int ret = xwrite(sc->fd, buf, len);
if (ret < 0)
dccp_shutdown_client(sc);
PARA_ERROR(FGETS, "fgets error"), \
PARA_ERROR(EOF, "end of file"), \
PARA_ERROR(READ_PATTERN, "did not read expected pattern"), \
+ PARA_ERROR(SHORT_WRITE, "unexpected short write"), \
PARA_ERROR(EMPTY, "file is empty"), \
#include "fd.h"
/**
- * Write a buffer to a file descriptor, re-write on short writes.
+ * Write an array of buffers to a file descriptor.
*
* \param fd The file descriptor.
- * \param buf The buffer to be sent.
- * \param len The length of \a buf.
+ * \param iov Pointer to one or more buffers.
+ * \param iovcnt The number of buffers.
+ *
+ * EAGAIN/EWOULDBLOCK is not considered a fatal error condition. For example
+ * DCCP CCID3 has a sending wait queue which fills up and is emptied
+ * asynchronously. The EAGAIN case means that there is currently no space in
+ * the wait queue, but this can change at any moment.
+ *
+ * \return Negative on fatal errors, number of bytes written else.
+ *
+ * For blocking file descriptors, this function returns either the sum of all
+ * buffer sizes, or the error code of the fatal error that caused the last
+ * write call to fail.
+ *
+ * For nonblocking file descriptors there is a third possibility: Any positive
+ * return value less than the sum of the buffer sizes indicates that some bytes
+ * have been written but the next write would block.
*
- * \return Standard. In any case, the number of bytes that have been written is
- * stored in \a len.
+ * \sa writev(2), \ref xwrite().
*/
-int write_all(int fd, const char *buf, size_t *len)
+int xwritev(int fd, struct iovec *iov, int iovcnt)
{
- size_t total = *len;
-
- assert(total);
- *len = 0;
- while (*len < total) {
- int ret = write(fd, buf + *len, total - *len);
- if (ret == -1)
- return -ERRNO_TO_PARA_ERROR(errno);
- *len += ret;
+ size_t written = 0;
+ int i;
+ struct iovec saved_iov, *curiov;
+
+ i = 0;
+ curiov = iov;
+ saved_iov = *curiov;
+ while (i < iovcnt && curiov->iov_len > 0) {
+ ssize_t ret = writev(fd, curiov, iovcnt - i);
+ if (ret >= 0) {
+ written += ret;
+ while (ret > 0) {
+ if (ret < curiov->iov_len) {
+ curiov->iov_base += ret;
+ curiov->iov_len -= ret;
+ break;
+ }
+ ret -= curiov->iov_len;
+ *curiov = saved_iov;
+ i++;
+ if (i >= iovcnt)
+ return written;
+ curiov++;
+ saved_iov = *curiov;
+ }
+ continue;
+ }
+ if (errno == EINTR)
+ /*
+ * The write() call was interrupted by a signal before
+ * any data was written. Try again.
+ */
+ continue;
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ /*
+ * We don't consider this an error. Note that POSIX
+ * allows either error to be returned, and does not
+ * require these constants to have the same value.
+ */
+ return written;
+ /* fatal error */
+ return -ERRNO_TO_PARA_ERROR(errno);
}
- return 1;
+ return written;
}
/**
- * Write a buffer to a non-blocking file descriptor.
+ * Write a buffer to a file descriptor, re-writing on short writes.
*
* \param fd The file descriptor.
- * \param buf the buffer to write.
- * \param len the number of bytes of \a buf.
+ * \param buf The buffer to write.
+ * \param len The number of bytes to write.
*
- * EAGAIN is not considered an error condition. For example CCID3 has a
- * sending wait queue which fills up and is emptied asynchronously. The EAGAIN
- * case means that there is currently no space in the wait queue, but this can
- * change at any moment.
+ * This is a simple wrapper for \ref xwritev().
*
- * \return Negative on errors, number of bytes written else.
+ * \return The return value of the underlying call to \ref xwritev().
*/
-int write_nonblock(int fd, const char *buf, size_t len)
+int xwrite(int fd, const char *buf, size_t len)
{
- size_t written = 0;
- int ret = 0;
+ struct iovec iov = {.iov_base = (void *)buf, .iov_len = len};
+ return xwritev(fd, &iov, 1);
+}
- while (written < len) {
- size_t num = len - written;
+/**
+ * Write all data to a file descriptor.
+ *
+ * \param fd The file descriptor.
+ * \param buf The buffer to be sent.
+ * \param len The length of \a buf.
+ *
+ * This is like \ref xwrite() but returns \p -E_SHORT_WRITE if not
+ * all data could be written.
+ *
+ * \return Number of bytes written on success, negative error code else.
+ */
+int write_all(int fd, const char *buf, size_t len)
+{
+ int ret = xwrite(fd, buf, len);
- ret = write(fd, buf + written, num);
- if (ret < 0 && errno == EAGAIN)
- return written;
- if (ret < 0)
- return -ERRNO_TO_PARA_ERROR(errno);
- written += ret;
- }
- return written;
+ if (ret < 0)
+ return ret;
+ if (ret != len)
+ return -E_SHORT_WRITE;
+ return ret;
+}
+
+/**
+ * Write a buffer given by a format string.
+ *
+ * \param fd The file descriptor.
+ * \param fmt A format string.
+ *
+ * \return The return value of the underlying call to \ref write_all().
+ */
+__printf_2_3 int write_va_buffer(int fd, const char *fmt, ...)
+{
+ char *msg;
+ int ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = xvasprintf(&msg, fmt, ap);
+ ret = write_all(fd, msg, ret);
+ free(msg);
+ return ret;
}
/**
* If \a rfds is not \p NULL and the (non-blocking) file descriptor \a fd is
* not set in \a rfds, this function returns early without doing anything.
* Otherwise The function tries to read up to \a sz bytes from \a fd. As for
- * write_nonblock(), EAGAIN is not considered an error condition. However, EOF
+ * xwrite(), EAGAIN is not considered an error condition. However, EOF
* is.
*
* \return Zero or a negative error code. If the underlying call to readv(2)
* have been read before the error occurred. In this case \a num_bytes is
* positive.
*
- * \sa \ref write_nonblock(), read(2), readv(2).
+ * \sa \ref xwrite(), read(2), readv(2).
*/
int readv_nonblock(int fd, struct iovec *iov, int iovcnt, fd_set *rfds,
size_t *num_bytes)
/** \file fd.h exported symbols from fd.c */
-int write_all(int fd, const char *buf, size_t *len);
+int write_all(int fd, const char *buf, size_t len);
+__printf_2_3 int write_va_buffer(int fd, const char *fmt, ...);
int file_exists(const char *);
int para_select(int n, fd_set *readfds, fd_set *writefds,
struct timeval *timeout_tv);
size_t *num_bytes);
int read_nonblock(int fd, void *buf, size_t sz, fd_set *rfds, size_t *num_bytes);
int read_pattern(int fd, const char *pattern, size_t bufsize, fd_set *rfds);
-int write_nonblock(int fd, const char *buf, size_t len);
+int xwrite(int fd, const char *buf, size_t len);
+int xwritev(int fd, struct iovec *iov, int iovcnt);
int for_each_file_in_dir(const char *dirname,
int (*func)(const char *, void *), void *private_data);
+/**
+ * Write a \p NULL-terminated buffer.
+ *
+ * \param fd The file descriptor.
+ * \param buf The null-terminated buffer to be send.
+ *
+ * This is equivalent to write_all(fd, buf, strlen(buf)).
+ *
+ * \return Standard.
+ */
+_static_inline_ int write_buffer(int fd, const char *buf)
+{
+ return write_all(fd, buf, strlen(buf));
+}
#define __a_aligned(alignment) __attribute__((__aligned__(alignment)))
/*
- * p is the number of the "format string" parameter, and q is
- * the number of the first variadic parameter.
+ * p is the number of the "format string" parameter, and q is the number of the
+ * first variadic parameter. If q is specified as zero, the compiler only
+ * checks the format string for consistency.
*/
# define __printf(p,q) __attribute__ ((format (printf, p, q)))
# define __a_const __attribute__ ((const))
+
/*
- * as direct use of __printf(p,q) confuses doxygen, here are two extra macros
- * for those values p,q that are actually used by paraslash.
+ * 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)
gret = gcry_cipher_encrypt(scc->send->handle, tmp, size,
(unsigned char *)buf, size);
assert(gret == 0);
- ret = write_all(scc->fd, (char *)tmp, &size);
+ ret = xwrite(scc->fd, (char *)tmp, size);
free(tmp);
return ret;
}
option "no_default_filters" D
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"Configure filters manually"
+"deprecated"
flag off
details = "
- If (and only if) this option is set, the --filter options
- (see below) take effect. Otherwise, the compiled-in defaults
- apply. These defaults depend on the receiver being used as
- described below.
-
- For http streams, only a single filter per audio format,
- the decoder for that format, is activated. On the other hand,
- since udp and dccp streams are sent fec-encoded by para_server,
- the client side must feed the output of the receiver into
- the fecdec filter first. Therefore the default for udp and
- dccp streams is to activate the fecdec filter, followed by
- the appropriate decoder.
-
- You must give this option if you want to use any other filter,
- for example the amp or the compress filter.
-
+ This option is was deprecated in paraslash-0.4.10 and has no
+ effect any more. It will be removed in the next version.
"
option "filter" f
#~~~~~~~~~~~~~~~~
-"Use non-default filters"
-string typestr="filter_spec"
+"Specify the filter configuration."
+string typestr = "filter_spec"
optional
multiple
-dependon="no_default_filters"
-details="
+details = "
This option may be given multiple times. The \"filter_spec\"
consists of an audio format specifier (see above), the name
of the filter, and any options for that filter. Note that
order matters.
+ The compiled-in defaults apply to all audio formats for which
+ no --filter option was given. These defaults depend on the
+ receiver being used.
+
+ For HTTP streams, only the decoder for the current audio
+ format is activated. UDP and DCCP streams, on the other
+ hand, are sent FEC-encoded by para_server. In order to play
+ such streams, the receiver output must be FEC-decoded first,
+ i.e. fed to the fecdec filter. Therefore the default for UDP
+ and DCCP streams is to activate the fecdec filter, followed
+ by the decoding filter for the audio format.
+
Examples:
--filter 'mp3:mp3dec'
if (gc->mode == GM_SLOPPY)
return len;
}
- ret = write_nonblock(gc->fd, buf, len);
+ ret = xwrite(gc->fd, buf, len);
if (ret < 0)
goto err;
if (ret > 0)
/**
* Activate inactive grab clients if possible.
*
- * \param sched Needed to schedule the grab client task.
+ * \param s Needed to schedule the grab client task.
*
* This is called from audiod.c when the current audio file changes. It loops
* over all inactive grab clients and checks each grab client's configuration
__printf_2_3 static void print_in_bar(int color, const char *fmt,...)
{
char *msg;
+ va_list ap;
if (!curses_active)
return;
wattron(in.win, COLOR_PAIR(color));
- PARA_VSPRINTF(fmt, msg);
+ va_start(ap, fmt);
+ xvasprintf(&msg, fmt, ap);
+ va_end(ap);
wmove(in.win, 0, 0);
align_str(in.win, msg, sb.cols, LEFT);
free(msg);
__printf_2_3 static void outputf(int color, const char* fmt,...)
{
char *msg;
+ va_list ap;
if (!curses_active)
return;
- PARA_VSPRINTF(fmt, msg);
+ va_start(ap, fmt);
+ xvasprintf(&msg, fmt, ap);
+ va_end(ap);
rb_add_entry(color, msg);
wrefresh(bot.win);
}
{
int color;
char *msg;
+ va_list ap;
if (ll < loglevel || !curses_active)
return;
default:
color = COLOR_ERRMSG;
}
- PARA_VSPRINTF(fmt, msg);
+ va_start(ap, fmt);
+ xvasprintf(&msg, fmt, ap);
+ va_end(ap);
chop(msg);
rb_add_entry(color, msg);
wrefresh(bot.win);
return;
rq = make_request_msg();
PARA_INFO_LOG("sending http request\n");
- ret = send_va_buffer(rn->fd, "%s", rq);
+ ret = write_va_buffer(rn->fd, "%s", rq);
free(rq);
if (ret < 0)
goto out;
static int http_send_msg(struct sender_client *sc, const char *msg)
{
- int ret = send_buffer(sc->fd, msg);
+ int ret = write_buffer(sc->fd, msg);
if (ret < 0)
shutdown_client(sc, hss);
ret = queue_chunk_or_shutdown(sc, ss, buf, len);
goto out;
}
- ret = write_nonblock(sc->fd, buf, len);
+ ret = xwrite(sc->fd, buf, len);
if (ret < 0) {
shutdown_client(sc, ss);
goto out;
sz = btr_next_buffer(btrn, &buf);
if (sz == 0)
goto out;
- ret = write_nonblock(ici->fds[1], buf, sz);
+ ret = xwrite(ici->fds[1], buf, sz);
if (ret < 0)
goto rm_btrn;
btr_consume(btrn, ret);
return ia;
}
-/**
- * Send a binary buffer.
- *
- * \param fd The file descriptor.
- * \param buf The buffer to be sent.
- * \param len The length of \a buf.
- *
- * Send out the buffer and try to resend the remaining part in case of short
- * writes.
- *
- * \return Standard.
- */
-int send_bin_buffer(int fd, const char *buf, size_t len)
-{
- if (!len)
- PARA_CRIT_LOG("len == 0\n");
- return write_all(fd, buf, &len);
-}
-
-/**
- * Send a \p NULL-terminated buffer.
- *
- * \param fd The file descriptor.
- * \param buf The null-terminated buffer to be send.
- *
- * This is equivalent to send_bin_buffer(fd, buf, strlen(buf)).
- *
- * \return Standard.
- */
-int send_buffer(int fd, const char *buf)
-{
- return send_bin_buffer(fd, buf, strlen(buf));
-}
-
-/**
- * Send a buffer given by a format string.
- *
- * \param fd The file descriptor.
- * \param fmt A format string.
- *
- * \return Standard.
- */
-__printf_2_3 int send_va_buffer(int fd, const char *fmt, ...)
-{
- char *msg;
- int ret;
-
- PARA_VSPRINTF(fmt, msg);
- ret = send_buffer(fd, msg);
- free(msg);
- return ret;
-}
-
/**
* Receive data from a file descriptor.
*
#ifndef HAVE_UCRED
ssize_t send_cred_buffer(int sock, char *buf)
{
- return send_buffer(sock, buf);
+ return write_buffer(sock, buf);
}
int recv_cred_buffer(int fd, char *buf, size_t size)
{
*/
extern int generic_max_transport_msg_size(int sockfd);
-int send_bin_buffer(int, const char *, size_t);
-int send_buffer(int, const char *);
-__printf_2_3 int send_va_buffer(int fd, const char *fmt, ...);
-
int recv_bin_buffer(int fd, char *buf, size_t size);
int recv_buffer(int fd, char *buf, size_t size);
ret = 0;
if (!FD_ISSET(powd->fd, &s->wfds))
goto out;
- ret = write_nonblock(powd->fd, data, frames * powd->bytes_per_frame);
+ ret = xwrite(powd->fd, data, frames * powd->bytes_per_frame);
if (ret < 0)
goto out;
btr_consume(btrn, ret);
int for_each_stat_item(char *item_buf, size_t num_bytes,
int (*item_handler)(int, char *));
-/**
- * Write a log message to a dynamically allocated string.
- *
- * \param fmt Usual format string.
- * \param p Result pointer.
- *
- * \sa printf(3). */
-#define PARA_VSPRINTF(fmt, p) \
-{ \
- int n; \
- size_t size = 100; \
- p = para_malloc(size); \
- while (1) { \
- va_list ap; \
- /* Try to print in the allocated space. */ \
- va_start(ap, fmt); \
- n = vsnprintf(p, size, fmt, ap); \
- va_end(ap); \
- /* If that worked, return the string. */ \
- if (n > -1 && n < size) \
- break; \
- /* Else try again with more space. */ \
- if (n > -1) /* glibc 2.1 */ \
- size = n + 1; /* precisely what is needed */ \
- else /* glibc 2.0 */ \
- size *= 2; /* twice the old size */ \
- p = para_realloc(p, size); \
- } \
-}
/**
- * Return a random non-negative integer in an interval.
+ * Return a random non-negative integer in an interval.
*
* \param max Determines maximal possible return value.
*
int ret;
cq_get(qc, &buf, &len);
- ret = write_nonblock(fd, buf, len);
+ ret = xwrite(fd, buf, len);
if (ret < 0)
return ret;
cq_update(cq, ret);
sz = btr_next_buffer(btrn, &buf);
if (sz == 0)
break;
- ret = write_nonblock(STDOUT_FILENO, buf, sz);
+ ret = xwrite(STDOUT_FILENO, buf, sz);
if (ret <= 0)
break;
btr_consume(btrn, ret);
exit(EXIT_FAILURE);
}
+/**
+ * Print a formated message to a dynamically allocated string.
+ *
+ * \param result The formated string is returned here.
+ * \param fmt The format string.
+ * \param ap Initialized list of arguments.
+ *
+ * This function is similar to vasprintf(), a GNU extension which is not in C
+ * or POSIX. It allocates a string large enough to hold the output including
+ * the terminating null byte. The allocated string is returned via the first
+ * argument and must be freed by the caller. However, unlike vasprintf(), this
+ * function calls exit() if insufficient memory is available, while vasprintf()
+ * returns -1 in this case.
+ *
+ * \return Number of bytes written, not including the terminating '\0'.
+ *
+ * \sa printf(3), vsnprintf(3), va_start(3), vasprintf(3), \ref xasprintf().
+ */
+__printf_2_0 unsigned xvasprintf(char **result, const char *fmt, va_list ap)
+{
+ int ret;
+ size_t size;
+ va_list aq;
+
+ va_copy(aq, ap);
+ ret = vsnprintf(NULL, 0, fmt, aq);
+ va_end(aq);
+ assert(ret >= 0);
+ size = ret + 1;
+ *result = para_malloc(size);
+ va_copy(aq, ap);
+ ret = vsnprintf(*result, size, fmt, aq);
+ va_end(aq);
+ assert(ret >= 0 && ret < size);
+ return ret;
+}
+
+/**
+ * Print to a dynamically allocated string, variable number of arguments.
+ *
+ * \param result See \ref xvasprintf().
+ * \param fmt Usual format string.
+ *
+ * \return The return value of the underlying call to \ref xvasprintf().
+ *
+ * \sa \ref xvasprintf() and the references mentioned there.
+ */
+__printf_2_3 unsigned xasprintf(char **result, const char *fmt, ...)
+{
+ va_list ap;
+ unsigned ret;
+
+ va_start(ap, fmt);
+ ret = xvasprintf(result, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
/**
* Allocate a sufficiently large string and print into it.
*
* \return This function either returns a pointer to a string that must be
* freed by the caller or aborts without returning.
*
- * \sa printf(3).
+ * \sa printf(3), xasprintf().
*/
__must_check __printf_1_2 __malloc char *make_message(const char *fmt, ...)
{
char *msg;
+ va_list ap;
- PARA_VSPRINTF(fmt, msg);
+ va_start(ap, fmt);
+ xvasprintf(&msg, fmt, ap);
+ va_end(ap);
return msg;
}
__must_check __malloc void *para_malloc(size_t size);
__must_check __malloc void *para_calloc(size_t size);
__must_check __malloc char *para_strdup(const char *s);
+
+__printf_2_0 unsigned xvasprintf(char **result, const char *fmt, va_list ap);
+__printf_2_3 unsigned xasprintf(char **result, const char *fmt, ...);
__must_check __malloc __printf_1_2 char *make_message(const char *fmt, ...);
__must_check __malloc char *para_strcat(char *a, const char *b);
__must_check __malloc char *para_dirname(const char *name);
ret = udp_check_socket_state(sc);
if (ret < 0)
goto fail;
- ret = write_nonblock(sc->fd, buf, len);
+ ret = xwrite(sc->fd, buf, len);
if (ret == -ERRNO_TO_PARA_ERROR(ECONNREFUSED)) {
/*
* Happens if meanwhile an ICMP Destination / Port Unreachable
recv_afs_result(vsst, &s->rfds);
else if (FD_ISSET(vsst->afs_socket, &s->wfds)) {
PARA_NOTICE_LOG("requesting new fd from afs\n");
- ret = send_buffer(vsst->afs_socket, "new");
+ ret = write_buffer(vsst->afs_socket, "new");
if (ret < 0)
PARA_CRIT_LOG("%s\n", para_strerror(-ret));
else