From fc8dfbb416ff07cca08fbf4e13efcaa25e17cc54 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Mon, 22 Aug 2016 15:13:40 +0200 Subject: [PATCH] Convert receivers to lopsub. This converts the four receivers (afh, http, dccp, udp) to use the lopsub library instead of gengetopt. The command line options of the receivers are implemented as subcommands of the new recv_cmd lopsub suite. Hence the four gengetopt command line parsers and ->ggo_help of struct receiver can be removed. This change allows to get rid of the receiver array which was defined through the DEFINE_RECEIVER_ARRAY macro. We now store each receiver structure in the user_data pointer provided by lopsub. Since this structure is initialized at compile time (and constant since ->ggo_help is gone), ->init() of struct receiver is no longer needed to initialize the various function pointers. The function is now optional and does not take an argument any more. At the moment, only the afh receiver needs ->init() to initialize all supported audio format handlers. t0005 needs slight adjustment since the section headers of the help text have changed a bit. --- Makefile.in | 1 - Makefile.real | 15 +++-- afh_recv.c | 98 +++++++++++------------------- audiod.c | 72 ++++++++++------------ configure.ac | 13 ---- dccp_recv.c | 123 +++++++++++++++---------------------- error.h | 1 - http_recv.c | 52 ++++------------ m4/gengetopt/afh_recv.m4 | 74 ----------------------- m4/gengetopt/dccp_recv.m4 | 41 ------------- m4/gengetopt/http_recv.m4 | 23 ------- m4/gengetopt/udp_recv.m4 | 21 ------- m4/lls/include/port.m4 | 7 +++ m4/lls/recv_cmd.suite.m4 | 102 +++++++++++++++++++++++++++++++ man_util.bash | 10 +-- play.c | 59 ++++++------------ recv.c | 53 ++++++++-------- recv.h | 73 +++++++--------------- recv_common.c | 124 ++++++++++++++++++++------------------ t/t0005-man.sh | 4 +- udp_recv.c | 68 ++++++--------------- 21 files changed, 402 insertions(+), 632 deletions(-) delete mode 100644 m4/gengetopt/afh_recv.m4 delete mode 100644 m4/gengetopt/dccp_recv.m4 delete mode 100644 m4/gengetopt/http_recv.m4 delete mode 100644 m4/gengetopt/udp_recv.m4 create mode 100644 m4/lls/include/port.m4 create mode 100644 m4/lls/recv_cmd.suite.m4 diff --git a/Makefile.in b/Makefile.in index 986877bc..2a2bdaf5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -16,7 +16,6 @@ HELP2MAN := @HELP2MAN@ ggo_descriptions_declared := @ggo_descriptions_declared@ executables := @executables@ -receivers := @receivers@ filters := @filters@ writers := @writers@ diff --git a/Makefile.real b/Makefile.real index 045d5781..2d381ad3 100644 --- a/Makefile.real +++ b/Makefile.real @@ -47,12 +47,13 @@ converted_executables := audioc unconverted_executables := $(filter-out $(converted_executables), $(executables)) audioc_objs += audioc.lsg.o -audiod_objs += audiod_cmd.lsg.o +audiod_objs += audiod_cmd.lsg.o recv_cmd.lsg.o server_objs += server_cmd.lsg.o -play_objs += play_cmd.lsg.o +play_objs += play_cmd.lsg.o recv_cmd.lsg.o +recv_objs += recv_cmd.lsg.o m4_deps := $(addprefix $(m4depdir)/, $(addsuffix .m4d, $(unconverted_executables))) -m4_lls_deps := audiod_cmd server_cmd play_cmd $(converted_executables) +m4_lls_deps := audiod_cmd server_cmd play_cmd recv_cmd $(converted_executables) m4_lls_deps := $(addprefix $(lls_suite_dir)/, $(addsuffix .m4d, $(m4_lls_deps))) # now prefix all objects with object dir @@ -148,16 +149,21 @@ else endif server_command_lists := $(lls_suite_dir)/server_cmd.lsg.man -audiod_command_lists := $(lls_suite_dir)/audiod_cmd.lsg.man +audiod_command_lists := \ + $(lls_suite_dir)/audiod_cmd.lsg.man \ + $(lls_suite_dir)/recv_cmd.lsg.man play_command_lists := $(lls_suite_dir)/play_cmd.lsg.man +recv_command_lists := $(lls_suite_dir)/recv_cmd.lsg.man $(man_dir)/para_server.1: $(server_command_lists) $(man_dir)/para_audiod.1: $(audiod_command_lists) $(man_dir)/para_play.1: $(play_command_lists) +$(man_dir)/para_recv.1: $(recv_command_lists) $(man_dir)/para_server.1: man_util_command_lists := $(server_command_lists) $(man_dir)/para_audiod.1: man_util_command_lists := $(audiod_command_lists) $(man_dir)/para_play.1: man_util_command_lists := $(play_command_lists) +$(man_dir)/para_recv.1: man_util_command_lists := $(recv_command_lists) $(man_dir)/para_%.1: $(lls_suite_dir)/%.lsg.man $(man_util_command_lists) \ $(lls_m4_dir)/copyright.m4 | $(man_dir) @@ -298,6 +304,7 @@ para_fade \ para_audioc \ para_audiod \ para_play \ +para_recv \ para_server \ : LDFLAGS += $(lopsub_ldflags) diff --git a/afh_recv.c b/afh_recv.c index 28d8f398..b76c405a 100644 --- a/afh_recv.c +++ b/afh_recv.c @@ -8,15 +8,15 @@ #include #include +#include +#include "recv_cmd.lsg.h" #include "para.h" #include "error.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "recv.h" -#include "afh_recv.cmdline.h" #include "string.h" #include "fd.h" #include "afh.h" @@ -65,40 +65,25 @@ static int afh_execute(struct btr_node *btrn, const char *cmd, char **result) return -E_BTR_NAVAIL; } -static void *afh_recv_parse_config(int argc, char **argv) -{ - struct afh_recv_args_info *tmp = para_calloc(sizeof(*tmp)); - - afh_recv_cmdline_parser(argc, argv, tmp); - return tmp; -} - -static void afh_recv_free_config(void *conf) -{ - if (!conf) - return; - afh_recv_cmdline_parser_free(conf); - free(conf); -} - static int afh_recv_open(struct receiver_node *rn) { - struct afh_recv_args_info *conf = rn->conf; + struct lls_parse_result *lpr = rn->lpr; struct private_afh_recv_data *pard; struct afh_info *afhi; - char *filename = conf->filename_arg; - + const char *fn = RECV_CMD_OPT_STRING_VAL(AFH, FILENAME, lpr); + int32_t bc = RECV_CMD_OPT_INT32_VAL(AFH, BEGIN_CHUNK, lpr); + const struct lls_opt_result *r_e = RECV_CMD_OPT_RESULT(AFH, END_CHUNK, lpr); int ret; - if (!filename || *filename == '\0') + if (!fn || *fn == '\0') return -E_AFH_RECV_BAD_FILENAME; rn->private_data = pard = para_calloc(sizeof(*pard)); afhi = &pard->afhi; - ret = mmap_full_file(filename, O_RDONLY, &pard->map, + ret = mmap_full_file(fn, O_RDONLY, &pard->map, &pard->map_size, &pard->fd); if (ret < 0) goto out; - ret = compute_afhi(filename, pard->map, pard->map_size, + ret = compute_afhi(fn, pard->map, pard->map_size, pard->fd, afhi); if (ret < 0) goto out_unmap; @@ -106,23 +91,22 @@ static int afh_recv_open(struct receiver_node *rn) ret = -ERRNO_TO_PARA_ERROR(EINVAL); if (afhi->chunks_total == 0) goto out_clear_afhi; - if (PARA_ABS(conf->begin_chunk_arg) >= afhi->chunks_total) + if (PARA_ABS(bc) >= afhi->chunks_total) goto out_clear_afhi; - if (conf->begin_chunk_arg >= 0) - pard->first_chunk = afh_get_start_chunk( - conf->begin_chunk_arg, &pard->afhi); + if (bc >= 0) + pard->first_chunk = afh_get_start_chunk(bc, &pard->afhi); else - pard->first_chunk = afh_get_start_chunk( - afhi->chunks_total + conf->begin_chunk_arg, + pard->first_chunk = afh_get_start_chunk(afhi->chunks_total + bc, &pard->afhi); - if (conf->end_chunk_given) { + if (lls_opt_given(r_e)) { + int32_t ec = lls_int32_val(0, r_e); ret = -ERRNO_TO_PARA_ERROR(EINVAL); - if (PARA_ABS(conf->end_chunk_arg) > afhi->chunks_total) + if (PARA_ABS(ec) > afhi->chunks_total) goto out_clear_afhi; - if (conf->end_chunk_arg >= 0) - pard->last_chunk = conf->end_chunk_arg; + if (ec >= 0) + pard->last_chunk = ec; else - pard->last_chunk = afhi->chunks_total + conf->end_chunk_arg; + pard->last_chunk = afhi->chunks_total + ec; } else pard->last_chunk = afhi->chunks_total - 1; ret = -ERRNO_TO_PARA_ERROR(EINVAL); @@ -158,13 +142,14 @@ static void afh_recv_pre_select(struct sched *s, void *context) struct receiver_node *rn = context; struct private_afh_recv_data *pard = rn->private_data; struct afh_info *afhi = &pard->afhi; - struct afh_recv_args_info *conf = rn->conf; + struct lls_parse_result *lpr = rn->lpr; struct timeval chunk_time; int state = generic_recv_pre_select(s, rn); + unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr); if (state <= 0) return; - if (!conf->just_in_time_given) { + if (!j_given) { sched_min_delay(s); return; } @@ -176,7 +161,7 @@ static void afh_recv_pre_select(struct sched *s, void *context) static int afh_recv_post_select(__a_unused struct sched *s, void *context) { struct receiver_node *rn = context; - struct afh_recv_args_info *conf = rn->conf; + struct lls_parse_result *lpr = rn->lpr; struct private_afh_recv_data *pard = rn->private_data; struct btr_node *btrn = rn->btrn; struct afh_info *afhi = &pard->afhi; @@ -185,11 +170,13 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context) const char *start, *end; size_t size; struct timeval chunk_time; + unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr); + unsigned H_given = RECV_CMD_OPT_GIVEN(AFH, NO_HEADER, lpr); ret = btr_node_status(btrn, 0, BTR_NT_ROOT); if (ret <= 0) goto out; - if (pard->first_chunk > 0 && !conf->no_header_given) { + if (pard->first_chunk > 0 && !H_given) { char *header; afh_get_header(afhi, pard->audio_format_num, pard->map, pard->map_size, &header, &size); @@ -201,7 +188,7 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context) afh_free_header(header, pard->audio_format_num); } } - if (!conf->just_in_time_given) { + if (!j_given) { afh_get_chunk(pard->first_chunk, afhi, pard->map, &start, &size); afh_get_chunk(pard->last_chunk, afhi, pard->map, &end, &size); end += size; @@ -236,26 +223,11 @@ out: return ret; } -/** - * The init function of the afh receiver. - * - * \param r Pointer to the receiver struct to initialize. - * - * This initializes all function pointers of \a r. - */ -void afh_recv_init(struct receiver *r) -{ - struct afh_recv_args_info dummy; - - afh_init(); - afh_recv_cmdline_parser_init(&dummy); - r->open = afh_recv_open; - r->close = afh_recv_close; - r->pre_select = afh_recv_pre_select; - r->post_select = afh_recv_post_select; - r->parse_config = afh_recv_parse_config; - r->free_config = afh_recv_free_config; - r->execute = afh_execute; - r->help = (struct ggo_help)DEFINE_GGO_HELP(afh_recv); - afh_recv_cmdline_parser_free(&dummy); -} +const struct receiver lsg_recv_cmd_com_afh_user_data = { + .init = afh_init, + .open = afh_recv_open, + .close = afh_recv_close, + .pre_select = afh_recv_pre_select, + .post_select = afh_recv_post_select, + .execute = afh_execute, +}; diff --git a/audiod.c b/audiod.c index fa401916..181d6ab1 100644 --- a/audiod.c +++ b/audiod.c @@ -17,6 +17,7 @@ #include #include +#include "recv_cmd.lsg.h" #include "para.h" #include "error.h" #include "crypt.h" @@ -47,14 +48,12 @@ __printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log; /** define the array containing all supported audio formats */ const char *audio_formats[] = {AUDIOD_AUDIO_FORMAT_ARRAY NULL}; -DEFINE_RECEIVER_ARRAY; - /** Defines how audiod handles one supported audio format. */ struct audio_format_info { - /** pointer to the receiver for this audio format */ - struct receiver *receiver; - /** the receiver configuration */ - void *receiver_conf; + /** the receiver for this audio format */ + int receiver_num; + /** Parsed receiver command line. */ + struct lls_parse_result *receiver_lpr; /** the number of filters that should be activated for this audio format */ unsigned int num_filters; /** Array of filter numbers to be activated. */ @@ -89,6 +88,9 @@ struct slot_info { struct writer_node *wns; }; +#define RECEIVER_CMD(_a) lls_cmd((_a)->receiver_num, recv_cmd_suite) +#define RECEIVER(_a) ((const struct receiver *)lls_user_data(RECEIVER_CMD(_a))) + /** Maximal number of simultaneous instances. */ #define MAX_STREAM_SLOTS 5 @@ -477,7 +479,7 @@ static void close_receiver(int slot_num) a = &afi[s->format]; PARA_NOTICE_LOG("closing %s receiver in slot %d\n", audio_formats[s->format], slot_num); - a->receiver->close(s->receiver_node); + RECEIVER(a)->close(s->receiver_node); btr_remove_node(&s->receiver_node->btrn); task_reap(&s->receiver_node->task); free(s->receiver_node); @@ -639,7 +641,8 @@ static int open_receiver(int format) struct audio_format_info *a = &afi[format]; struct slot_info *s; int ret, slot_num; - struct receiver *r = a->receiver; + const struct receiver *r = RECEIVER(a); + const char *name = lls_command_name(RECEIVER_CMD(a)); struct receiver_node *rn; tv_add(now, &(struct timeval)EMBRACE(2, 0), &a->restart_barrier); @@ -649,9 +652,9 @@ static int open_receiver(int format) slot_num = ret; rn = para_calloc(sizeof(*rn)); rn->receiver = r; - rn->conf = a->receiver_conf; + rn->lpr = a->receiver_lpr; rn->btrn = btr_new_node(&(struct btr_node_description) - EMBRACE(.name = r->name, .context = rn)); + EMBRACE(.name = name, .context = rn)); ret = r->open(rn); if (ret < 0) { btr_remove_node(&rn->btrn); @@ -662,9 +665,9 @@ static int open_receiver(int format) s->format = format; s->receiver_node = rn; PARA_NOTICE_LOG("started %s: %s receiver in slot %d\n", - audio_formats[format], r->name, slot_num); + audio_formats[format], name, slot_num); rn->task = task_register(&(struct task_info) { - .name = r->name, + .name = name, .pre_select = r->pre_select, .post_select = r->post_select, .context = rn, @@ -827,7 +830,7 @@ static int update_item(int itemnum, char *buf) return 1; } -static int parse_stream_command(const char *txt, char **cmd) +static int parse_stream_command(const char *txt, const char **cmd) { int ret, len; char *re, *p = strchr(txt, ':'); @@ -844,7 +847,7 @@ static int parse_stream_command(const char *txt, char **cmd) return ret; } -static int add_filter(int format, char *cmdline) +static int add_filter(int format, const char *cmdline) { struct audio_format_info *a = &afi[format]; int filter_num, nf = a->num_filters; @@ -868,7 +871,7 @@ static int add_filter(int format, char *cmdline) static int parse_writer_args(void) { int i, ret; - char *cmd; + const char *cmd; struct audio_format_info *a; for (i = 0; i < conf.writer_given; i++) { @@ -915,12 +918,13 @@ static int parse_writer_args(void) static int parse_receiver_args(void) { - int i, ret, receiver_num; - char *cmd = NULL; + int i, ret; + const char *arg; struct audio_format_info *a; + FOR_EACH_AUDIO_FORMAT(i) + afi[i].receiver_num = -1; for (i = conf.receiver_given - 1; i >= 0; i--) { - char *arg; int j, af_mask; ret = parse_stream_command(conf.receiver_arg[i], &arg); @@ -937,37 +941,27 @@ static int parse_receiver_args(void) * config here. Since we are iterating backwards, the winning * receiver arg is in fact the first one given. */ - if (a->receiver_conf) - a->receiver->free_config(a->receiver_conf); - a->receiver_conf = check_receiver_arg(arg, &receiver_num); - ret = -E_RECV_SYNTAX; - if (!a->receiver_conf) - goto out; - a->receiver = receivers + receiver_num; + lls_free_parse_result(a->receiver_lpr, RECEIVER_CMD(a)); + a->receiver_num = check_receiver_arg(arg, &a->receiver_lpr); } } /* - * Use the first available receiver with no arguments for those audio - * formats for which no receiver was specified. + * Use the default receiver for those audio formats for which no + * receiver was specified. */ - cmd = para_strdup(receivers[0].name); FOR_EACH_AUDIO_FORMAT(i) { - a = &afi[i]; - if (a->receiver_conf) + a = afi + i; + if (a->receiver_num >= 0) continue; - a->receiver_conf = check_receiver_arg(cmd, &receiver_num); - if (!a->receiver_conf) - return -E_RECV_SYNTAX; - a->receiver = &receivers[receiver_num]; + a->receiver_num = check_receiver_arg(NULL, &a->receiver_lpr); } FOR_EACH_AUDIO_FORMAT(i) { a = afi + i; PARA_INFO_LOG("receiving %s streams via %s receiver\n", - audio_formats[i], a->receiver->name); + audio_formats[i], lls_command_name(RECEIVER_CMD(a))); } ret = 1; out: - free(cmd); return ret; } @@ -977,6 +971,7 @@ static int init_default_filters(void) FOR_EACH_AUDIO_FORMAT(i) { struct audio_format_info *a = &afi[i]; + const char *name = lls_command_name(RECEIVER_CMD(a)); char *tmp; int j; @@ -986,8 +981,7 @@ static int init_default_filters(void) * udp and dccp streams are fec-encoded, so add fecdec as the * first filter. */ - if (strcmp(afi[i].receiver->name, "udp") == 0 || - strcmp(afi[i].receiver->name, "dccp") == 0) { + if (strcmp(name, "udp") == 0 || strcmp(name, "dccp") == 0) { tmp = para_strdup("fecdec"); add_filter(i, tmp); free(tmp); @@ -1020,7 +1014,7 @@ static int parse_filter_args(void) int i, j, ret, af_mask, num_matches; for (i = 0; i < conf.filter_given; i++) { - char *arg; + const char *arg; ret = parse_stream_command(conf.filter_arg[i], &arg); if (ret < 0) goto out; diff --git a/configure.ac b/configure.ac index 6087bac4..d5e52839 100644 --- a/configure.ac +++ b/configure.ac @@ -511,12 +511,9 @@ if test -n "$CRYPTOLIB"; then audiod_cmdline_objs="$audiod_cmdline_objs audiod compress_filter - http_recv - dccp_recv file_write client amp_filter - udp_recv prebuffer_filter sync_filter " @@ -775,10 +772,6 @@ AC_DEFINE_UNQUOTED(FILTER_ARRAY, $array, array of supported filters) ########################################################################## recv recv_cmdline_objs=" recv - http_recv - dccp_recv - udp_recv - afh_recv " recv_errlist_objs=" @@ -792,7 +785,6 @@ recv_errlist_objs=" fd sched stdout - ggo udp_recv buffer_tree afh_recv @@ -812,7 +804,6 @@ if test $HAVE_FAAD = yes -a $HAVE_MP4V2 = yes; then recv_errlist_objs="$recv_errlist_objs aac_afh aac_common" fi recv_objs="add_cmdline($recv_cmdline_objs) $recv_errlist_objs" -AC_SUBST(receivers, "http dccp udp afh") AC_SUBST(recv_objs, add_dot_o($recv_objs)) ########################################################################### afh audio_format_handlers="mp3 wma" @@ -889,10 +880,6 @@ play_errlist_objs=" sync_filter " play_cmdline_objs=" - http_recv - dccp_recv - udp_recv - afh_recv compress_filter amp_filter prebuffer_filter diff --git a/dccp_recv.c b/dccp_recv.c index 253586e1..318969af 100644 --- a/dccp_recv.c +++ b/dccp_recv.c @@ -18,20 +18,19 @@ #include #include #include +#include +#include "recv_cmd.lsg.h" #include "para.h" #include "error.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "recv.h" #include "string.h" #include "net.h" #include "fd.h" -#include "dccp_recv.cmdline.h" - static void dccp_recv_close(struct receiver_node *rn) { if (rn->fd > 0) @@ -39,25 +38,56 @@ static void dccp_recv_close(struct receiver_node *rn) btr_pool_free(rn->btrp); } +/* Check whether the host supports the requested 'ccid' arguments. */ +static int dccp_recv_ccid_support_check(const struct lls_parse_result *lpr) +{ + uint8_t *ccids; + int i, j, ret, nccids; + unsigned given = RECV_CMD_OPT_GIVEN(DCCP, CCID, lpr); + + ret = dccp_available_ccids(&ccids); + if (ret < 0) + return ret; + nccids = ret; + for (i = 0; i < given; i++) { + uint32_t val = lls_uint32_val(i, + RECV_CMD_OPT_RESULT(DCCP, CCID, lpr)); + for (j = 0; j < nccids && ccids[j] != val; j++) + ; + if (j == nccids) { + PARA_ERROR_LOG("'CCID-%u' not supported on this host\n", + val); + return -ERRNO_TO_PARA_ERROR(EINVAL); + } + } + return 1; +} + static int dccp_recv_open(struct receiver_node *rn) { - struct dccp_recv_args_info *conf = rn->conf; + struct lls_parse_result *lpr = rn->lpr; struct flowopts *fo = NULL; uint8_t *ccids = NULL; int fd, ret, i; + const struct lls_opt_result *r_c = RECV_CMD_OPT_RESULT(DCCP, CCID, lpr); + const char *host = RECV_CMD_OPT_STRING_VAL(DCCP, HOST, lpr); + uint32_t port = RECV_CMD_OPT_UINT32_VAL(DCCP, PORT, lpr); + unsigned given; + ret = dccp_recv_ccid_support_check(lpr); + if (ret < 0) + return ret; /* Copy CCID preference list (u8 array required) */ - if (conf->ccid_given) { - ccids = para_malloc(conf->ccid_given); - fo = flowopt_new(); - - for (i = 0; i < conf->ccid_given; i++) - ccids[i] = conf->ccid_arg[i]; - + given = lls_opt_given(r_c); + if (given) { + ccids = para_malloc(given); + fo = flowopt_new(); + for (i = 0; i < given; i++) + ccids[i] = lls_int32_val(i, r_c); OPT_ADD(fo, SOL_DCCP, DCCP_SOCKOPT_CCID, ccids, i); } - fd = makesock(IPPROTO_DCCP, 0, conf->host_arg, conf->port_arg, fo); + fd = makesock(IPPROTO_DCCP, 0, host, port, fo); flowopt_cleanup(fo); free(ccids); if (fd < 0) @@ -83,42 +113,6 @@ err: return ret; } -/** - * Check whether the host supports the requested 'ccid' arguments. - * \param conf DCCP receiver arguments. - * \return True if all CCIDs requested in \a conf are supported. - */ -static bool dccp_recv_ccid_support_check(struct dccp_recv_args_info *conf) -{ - uint8_t *ccids; - int i, j, nccids; - - nccids = dccp_available_ccids(&ccids); - if (nccids <= 0) - return false; - - for (i = 0; i < conf->ccid_given; i++) { - for (j = 0; j < nccids && ccids[j] != conf->ccid_arg[i]; j++) - ; - if (j == nccids) { - PARA_ERROR_LOG("'CCID-%d' not supported on this host.\n", - conf->ccid_arg[i]); - return false; - } - } - return true; -} - -static void *dccp_recv_parse_config(int argc, char **argv) -{ - struct dccp_recv_args_info *tmp = para_calloc(sizeof(*tmp)); - - dccp_recv_cmdline_parser(argc, argv, tmp); - if (!dccp_recv_ccid_support_check(tmp)) - exit(EXIT_FAILURE); - return tmp; -} - static void dccp_recv_pre_select(struct sched *s, void *context) { struct receiver_node *rn = context; @@ -161,30 +155,9 @@ out: return ret; } -static void dccp_recv_free_config(void *conf) -{ - dccp_recv_cmdline_parser_free(conf); - free(conf); -} - -/** - * The init function of the dccp receiver. - * - * \param r Pointer to the receiver struct to initialize. - * - * Initialize all function pointers of \a r. - */ -void dccp_recv_init(struct receiver *r) -{ - struct dccp_recv_args_info dummy; - - dccp_recv_cmdline_parser_init(&dummy); - r->open = dccp_recv_open; - r->close = dccp_recv_close; - r->pre_select = dccp_recv_pre_select; - r->post_select = dccp_recv_post_select; - r->parse_config = dccp_recv_parse_config; - r->free_config = dccp_recv_free_config; - r->help = (struct ggo_help)DEFINE_GGO_HELP(dccp_recv); - dccp_recv_cmdline_parser_free(&dummy); -} +const struct receiver lsg_recv_cmd_com_dccp_user_data = { + .open = dccp_recv_open, + .close = dccp_recv_close, + .pre_select = dccp_recv_pre_select, + .post_select = dccp_recv_post_select, +}; diff --git a/error.h b/error.h index a4d79aba..d10ac76a 100644 --- a/error.h +++ b/error.h @@ -190,7 +190,6 @@ PARA_ERROR(READ_PATTERN, "did not read expected pattern"), \ PARA_ERROR(RECV_EOF, "end of file"), \ PARA_ERROR(RECVMSG, "recvmsg() failed"), \ - PARA_ERROR(RECV_SYNTAX, "recv syntax error"), \ PARA_ERROR(REGEX, "regular expression error"), \ PARA_ERROR(RESAMPLE_EOF, "resample filter: end of file"), \ PARA_ERROR(RSA, "RSA error"), \ diff --git a/http_recv.c b/http_recv.c index 2f334787..d49cf2a8 100644 --- a/http_recv.c +++ b/http_recv.c @@ -13,16 +13,16 @@ #include #include #include +#include +#include "recv_cmd.lsg.h" #include "para.h" #include "error.h" #include "http.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "recv.h" -#include "http_recv.cmdline.h" #include "net.h" #include "string.h" #include "fd.h" @@ -144,20 +144,13 @@ static void http_recv_close(struct receiver_node *rn) free(rn->private_data); } -static void *http_recv_parse_config(int argc, char **argv) -{ - struct http_recv_args_info *tmp = para_calloc(sizeof(*tmp)); - - http_recv_cmdline_parser(argc, argv, tmp); - return tmp; -} - static int http_recv_open(struct receiver_node *rn) { struct private_http_recv_data *phd; - struct http_recv_args_info *conf = rn->conf; - int fd, ret = para_connect_simple(IPPROTO_TCP, conf->host_arg, - conf->port_arg); + struct lls_parse_result *lpr = rn->lpr; + const char *r_i = RECV_CMD_OPT_STRING_VAL(HTTP, HOST, lpr); + uint32_t r_p = RECV_CMD_OPT_UINT32_VAL(HTTP, PORT, lpr); + int fd, ret = para_connect_simple(IPPROTO_TCP, r_i, r_p); if (ret < 0) return ret; @@ -174,30 +167,9 @@ static int http_recv_open(struct receiver_node *rn) return 1; } -static void http_recv_free_config(void *conf) -{ - http_recv_cmdline_parser_free(conf); - free(conf); -} - -/** - * The init function of the http receiver. - * - * \param r Pointer to the receiver struct to initialize. - * - * This initializes all function pointers of \a r. - */ -void http_recv_init(struct receiver *r) -{ - struct http_recv_args_info dummy; - - http_recv_cmdline_parser_init(&dummy); - r->open = http_recv_open; - r->close = http_recv_close; - r->pre_select = http_recv_pre_select; - r->post_select = http_recv_post_select; - r->parse_config = http_recv_parse_config; - r->free_config = http_recv_free_config; - r->help = (struct ggo_help)DEFINE_GGO_HELP(http_recv); - http_recv_cmdline_parser_free(&dummy); -} +const struct receiver lsg_recv_cmd_com_http_user_data = { + .open = http_recv_open, + .close = http_recv_close, + .pre_select = http_recv_pre_select, + .post_select = http_recv_post_select, +}; diff --git a/m4/gengetopt/afh_recv.m4 b/m4/gengetopt/afh_recv.m4 deleted file mode 100644 index 28a0a9ea..00000000 --- a/m4/gengetopt/afh_recv.m4 +++ /dev/null @@ -1,74 +0,0 @@ -args "--no-version --no-help" - -purpose "Make an audio stream from a local file" - -description " - The afh (audio format handler) receiver can be used to write - selected parts of the given audio file without decoding - the data. - - The selected parts of the content of the audio file are passed - to the child nodes of the buffer tree. Only complete chunks - with respect of the underlying audio format are passed. -" - -include(header.m4) - -option "filename" f -#~~~~~~~~~~~~~~~~~~ -"file to open" -string typestr = "filename" -required - -option "begin-chunk" b -#~~~~~~~~~~~~~~~~~~~~~ -"skip the beginning of the file" -int typestr = "chunk_num" -default = "0" -optional -details = " - The chunk_num argument must be between -num_chunks and - num_chunks - 1, inclusively, where num_chunks is the total - number of chunks of the audio file given by the argument to - --filename. If chunk_num is negative, the given number of - chunks are counted backwards from the end of the file. For - example --begin-chunk -100 instructs the afh receiver to - start output at chunk num_chunks - 100. This is useful for - selecting the last part of an audio file. -" - -option "end-chunk" e -#~~~~~~~~~~~~~~~~~~~ -"only write up to chunk chunk_num" -int typestr = "chunk_num" -optional -details = " - For the chunk_num argument the same rules as for --begin-chunk - apply. The default is to write up to the last chunk. -" - -option "just-in-time" j -#~~~~~~~~~~~~~~~~~~~~~~ -"use timed writes" -flag off -details = " - Write the specified chunks of data 'just in time', i.e. the - write of each chunk is delayed until the time it is needed - by the decoder/player in order to guarantee an uninterrupted - audio stream. This may be useful for third-party software - that is capable of reading from stdin. -" - -option "no-header" H -#~~~~~~~~~~~~~~~~~~~ -"do not write an audio file header" -flag off -details = " - If an audio format needs information about the audio file - in a format-specific header in order to be understood by - the decoding software, a suitable header is automatically - send. This option changes the default behaviour, i.e. no - header is written. -" - - diff --git a/m4/gengetopt/dccp_recv.m4 b/m4/gengetopt/dccp_recv.m4 deleted file mode 100644 index 1ba3fb59..00000000 --- a/m4/gengetopt/dccp_recv.m4 +++ /dev/null @@ -1,41 +0,0 @@ -args "--no-version --no-help" - -purpose "Receive a DCCP audio stream" - -option "host" i -"ip or host" -string default="localhost" -optional -details=" - Both IPv4 and IPv6 addresses are supported. -" - -option "port" p -"port to connect to" -int -default="8000" -optional - -option "ccid" c -"CCID preference(s) for this connection" -int -# restrict the maximum number of times this option can be passed -optional multiple(-10) -# currently known CCIDs: -# - CCID-2 (RFC 4341), -# - CCID-3 (RFC 4342), -# - CCID-4 (RFC 5622), -# - CCID-248 ... CCID-254 are experimental (RFC 4340, 19.5) -values="2", "3", "4", "248", "249", "250", "251", "252", "253", "254" -details=" - When present exactly once, this option mandates the CCID for the - sender-receiver connection. If it is passed more than once, it sets - a preference list where the order of appearance signifies descending - priority. For example, passing 4, 2, 3 creates the preference list - (CCID-4, CCID-2, CCID-3), assigning CCID-4 highest preference. - - The request is reconciled with the CCIDs on the server through the - 'server-priority' mechanism of RFC 4340 6.3.1/10. The server CCIDs - can be listed by calling 'para_client si'. - -" diff --git a/m4/gengetopt/http_recv.m4 b/m4/gengetopt/http_recv.m4 deleted file mode 100644 index 6db3ff04..00000000 --- a/m4/gengetopt/http_recv.m4 +++ /dev/null @@ -1,23 +0,0 @@ -args "--no-version --no-help" - -purpose "Receive an HTTP audio stream" - -include(header.m4) - - -option "host" i -#~~~~~~~~~~~~~~ -"ip or host" -string -default="localhost" -optional -details=" - Both IPv4 and IPv6 addresses are supported. -" - -option "port" p -#~~~~~~~~~~~~~~ -"tcp port to connect to" -int default="8000" -optional - diff --git a/m4/gengetopt/udp_recv.m4 b/m4/gengetopt/udp_recv.m4 deleted file mode 100644 index dcdad4f7..00000000 --- a/m4/gengetopt/udp_recv.m4 +++ /dev/null @@ -1,21 +0,0 @@ -args "--no-version --no-help" - -purpose "Receive an UDP audio stream" - -option "host" i -"ip or host to receive udp packets from" -string default="224.0.1.38" -optional -details=" - The default address resolves to DANTZ.MCAST.NET and activates - multicast. -" - -option "port" p "udp port" -int typestr="portnumber" -default="8000" -optional - -option "iface" I "receiving udp multicast interface" -string -optional diff --git a/m4/lls/include/port.m4 b/m4/lls/include/port.m4 new file mode 100644 index 00000000..8dd63b61 --- /dev/null +++ b/m4/lls/include/port.m4 @@ -0,0 +1,7 @@ +[option port] + short_opt = p + summary = TCP port to connect to + typestr = portnumber + arg_info = required_arg + arg_type = uint32 + default_val = 8000 diff --git a/m4/lls/recv_cmd.suite.m4 b/m4/lls/recv_cmd.suite.m4 new file mode 100644 index 00000000..674a4487 --- /dev/null +++ b/m4/lls/recv_cmd.suite.m4 @@ -0,0 +1,102 @@ +[suite recv_cmd] +caption = receivers +[subcommand afh] + purpose = make an audio stream from a local file + [description] + The afh (audio format handler) receiver extracts selected parts of + the given audio file without decoding the file. Only complete chunks + with respect to the underlying audio format are extracted. + [/description] + [option filename] + short_opt = f + summary = file to open + typestr = filename + arg_info = required_arg + arg_type = string + [option begin-chunk] + short_opt = b + summary = skip the beginning of the file + typestr = chunk_num + arg_info = required_arg + arg_type = int32 + [help] + The argument must be an integer between -num_chunks and num_chunks - + 1, inclusively, where num_chunks is the total number of chunks. If + chunk_num is negative, the given number of chunks are counted backwards + from the end of the file. For example --begin-chunk -100 instructs + the afh receiver to start at chunk num_chunks - 100. This is useful + for cutting off the beginning of an audio file. + [/help] + [option end-chunk] + short_opt = e + summary = only write up to chunk chunk_num + typestr = chunk_num + arg_info = required_arg + arg_type = int32 + [help] + For the chunk_num argument the same rules as for --begin-chunk + apply. The default is to write up to the last chunk. + [/help] + [option just-in-time] + short_opt = j + summary = use timed writes + [help] + Write the specified data chunks 'just in time', i.e., delay the write + until data is needed by the decoder/player for an uninterrupted audio + stream. This may be useful for third-party software. + [/help] + [option no-header] + short_opt = h + summary = do not write an audio file header + [help] + Some audio formats store information about the audio file in + a format-specific header which is needed to decode any part of + the file. For such formats the afh receiver generates a suitable + header. This option changes the default behaviour, i.e. no header + is written. + [/help] +[subcommand http] + purpose = receive an audio stream over HTTP + m4_include(host.m4) + m4_include(port.m4) +[subcommand dccp] + purpose = receive an audio stream over DCCP + m4_include(host.m4) + m4_include(port.m4) + [option ccid] + short_opt = c + summary = CCID preference(s) for this connection + typestr = id + arg_info = required_arg + arg_type = uint32 + flag multiple + [help] + When present exactly once, this option mandates the CCID for the + sender-receiver connection. If it is passed more than once, it sets + a preference list where the order of appearance signifies descending + priority. For example, passing 4, 2, 3 creates the preference list + (CCID-4, CCID-2, CCID-3), assigning CCID-4 highest preference. + + The request is reconciled with the CCIDs on the server through the + 'server-priority' mechanism of RFC 4340 6.3.1/10. The server CCIDs + can be listed by calling 'para_client si'. + [/help] +[subcommand udp] + purpose = receive an audio stream over UDP + [option host] + short_opt = i + summary = IP address or hostname + typestr = host + arg_info = required_arg + arg_type = string + default_val = 224.0.1.38 + [help] + The default address resolves to DANTZ.MCAST.NET and activates + multicast. + [/help] + m4_include(port.m4) + [option iface] + summary = receiving udp multicast interface + typestr = iface-name + arg_info = required_arg + arg_type = string diff --git a/man_util.bash b/man_util.bash index 687acaab..38ed86c8 100755 --- a/man_util.bash +++ b/man_util.bash @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Receivers, filters, writers are called "modules" in this script +# filters, writers are called "modules" in this script print_modhelp() { local ggo="$1" @@ -39,14 +39,6 @@ make_help() --set-package "para_$1" \ < "$ggo" - if [[ "$target" == 'recv' || "$target" == 'audiod' ]]; then - for module in $RECEIVERS; do - ggo="$GGO_DIR/${module}_recv.ggo" - [[ ! -f "$ggo" ]] && continue - printf "\nOptions for the $module receiver" - print_modhelp "$ggo" - done - fi if [[ "$target" == 'filter' || "$target" == 'audiod' ]]; then for module in $FILTERS; do ggo="$GGO_DIR/${module}_filter.ggo" diff --git a/play.c b/play.c index 1ab40101..4dab1cad 100644 --- a/play.c +++ b/play.c @@ -11,6 +11,7 @@ #include #include +#include "recv_cmd.lsg.h" #include "para.h" #include "list.h" #include "play.cmdline.h" @@ -110,14 +111,6 @@ struct play_command_info { .handler = com_ ## _cmd \ }; -/* Activate the afh receiver. */ -extern void afh_recv_init(struct receiver *r); -#undef AFH_RECEIVER -/** Initialization code for a receiver struct. */ -#define AFH_RECEIVER {.name = "afh", .init = afh_recv_init}, -/** This expands to the array of all receivers. */ -DEFINE_RECEIVER_ARRAY; - static int loglevel = LL_WARNING; /** The log function which writes log messages to stderr. */ @@ -131,22 +124,9 @@ static struct play_args_info conf; static struct sched sched = {.max_fileno = 0}; static struct play_task play_task; -static struct receiver *afh_recv; -static void check_afh_receiver_or_die(void) -{ - int i; - - FOR_EACH_RECEIVER(i) { - struct receiver *r = receivers + i; - if (strcmp(r->name, "afh")) - continue; - afh_recv = r; - return; - } - PARA_EMERG_LOG("fatal: afh receiver not found\n"); - exit(EXIT_FAILURE); -} +#define AFH_RECV_CMD (lls_cmd(LSG_RECV_CMD_CMD_AFH, recv_cmd_suite)) +#define AFH_RECV ((struct receiver *)lls_user_data(AFH_RECV_CMD)) __noreturn static void print_help_and_die(void) { @@ -242,12 +222,13 @@ static long unsigned get_play_time(struct play_task *pt) return result; } + static void wipe_receiver_node(struct play_task *pt) { PARA_NOTICE_LOG("cleaning up receiver node\n"); btr_remove_node(&pt->rn.btrn); - afh_recv->close(&pt->rn); - afh_recv->free_config(pt->rn.conf); + AFH_RECV->close(&pt->rn); + lls_free_parse_result(pt->rn.lpr, AFH_RECV_CMD); memset(&pt->rn, 0, sizeof(struct receiver_node)); } @@ -320,25 +301,26 @@ static void shuffle(char **base, size_t num) static struct btr_node *new_recv_btrn(struct receiver_node *rn) { return btr_new_node(&(struct btr_node_description) - EMBRACE(.name = afh_recv->name, .context = rn, - .handler = afh_recv->execute)); + EMBRACE(.name = lls_command_name(AFH_RECV_CMD), .context = rn, + .handler = AFH_RECV->execute)); } static int open_new_file(struct play_task *pt) { int ret; - char *tmp, *path = conf.inputs[pt->next_file], *afh_recv_conf[] = - {"play", "-f", path, "-b", "0", NULL}; + char *tmp, *path = conf.inputs[pt->next_file], *errctx = NULL, + *argv[] = {"play", "-f", path, "-b", "0", NULL}; PARA_NOTICE_LOG("next file: %s\n", path); wipe_receiver_node(pt); pt->start_chunk = 0; pt->rn.btrn = new_recv_btrn(&pt->rn); - pt->rn.conf = afh_recv->parse_config(ARRAY_SIZE(afh_recv_conf) - 1, - afh_recv_conf); - assert(pt->rn.conf); - pt->rn.receiver = afh_recv; - ret = afh_recv->open(&pt->rn); + ret = lls(lls_parse(ARRAY_SIZE(argv) - 1, argv, AFH_RECV_CMD, + &pt->rn.lpr, &errctx)); + free(tmp); + assert(ret >= 0); + pt->rn.receiver = AFH_RECV; + ret = AFH_RECV->open(&pt->rn); if (ret < 0) { PARA_ERROR_LOG("could not open %s\n", path); goto fail; @@ -420,9 +402,9 @@ static int load_file(struct play_task *pt) /* success, register tasks */ pt->rn.task = task_register( &(struct task_info) { - .name = afh_recv->name, - .pre_select = afh_recv->pre_select, - .post_select = afh_recv->post_select, + .name = lls_command_name(AFH_RECV_CMD), + .pre_select = AFH_RECV->pre_select, + .post_select = AFH_RECV->post_select, .context = &pt->rn }, &sched); sprintf(buf, "%s decoder", af); @@ -1287,8 +1269,7 @@ int main(int argc, char *argv[]) parse_config_or_die(argc, argv); if (conf.inputs_num == 0) print_help_and_die(); - check_afh_receiver_or_die(); - + AFH_RECV->init(); session_open(pt); if (conf.randomize_given) shuffle(conf.inputs, conf.inputs_num); diff --git a/recv.c b/recv.c index 9de3033f..abebbfc2 100644 --- a/recv.c +++ b/recv.c @@ -8,11 +8,13 @@ #include #include +#include +#include +#include "recv_cmd.lsg.h" #include "para.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "recv.h" #include "recv.cmdline.h" @@ -25,11 +27,6 @@ /** Array of error strings. */ DEFINE_PARA_ERRLIST; -extern void afh_recv_init(struct receiver *r); -#undef AFH_RECEIVER -#define AFH_RECEIVER {.name = "afh", .init = afh_recv_init}, -DEFINE_RECEIVER_ARRAY; - /** The gengetopt args info struct. */ static struct recv_args_info conf; @@ -39,12 +36,13 @@ INIT_STDERR_LOGGING(loglevel); __noreturn static void print_help_and_die(void) { - struct ggo_help h = DEFINE_GGO_HELP(recv); bool d = conf.detailed_help_given; - - ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS); - print_receiver_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS); - exit(0); + if (d) + recv_cmdline_parser_print_detailed_help(); + else + recv_cmdline_parser_print_help(); + print_receiver_helps(d); + exit(EXIT_SUCCESS); } /** @@ -60,12 +58,15 @@ __noreturn static void print_help_and_die(void) */ int main(int argc, char *argv[]) { - int ret, r_opened = 0, receiver_num; - struct receiver *r = NULL; + int ret; + bool r_opened = false; + const struct receiver *r = NULL; struct receiver_node rn; struct stdout_task sot = {.btrn = NULL}; static struct sched s; struct task_info ti; + const struct lls_command *cmd; + struct lls_parse_result *receiver_lpr; /* receiver specific options */ recv_cmdline_parser(argc, argv, &conf); loglevel = get_loglevel_by_name(conf.loglevel_arg); @@ -75,26 +76,25 @@ int main(int argc, char *argv[]) print_help_and_die(); memset(&rn, 0, sizeof(struct receiver_node)); - rn.conf = check_receiver_arg(conf.receiver_arg, &receiver_num); - if (!rn.conf) { - PARA_EMERG_LOG("invalid receiver specifier\n"); - ret = -E_RECV_SYNTAX; + ret = check_receiver_arg(conf.receiver_arg, &receiver_lpr); + if (ret < 0) goto out; - } - r = &receivers[receiver_num]; + cmd = lls_cmd(ret, recv_cmd_suite); + r = lls_user_data(cmd); rn.receiver = r; + rn.lpr = receiver_lpr; rn.btrn = btr_new_node(&(struct btr_node_description) - EMBRACE(.name = r->name)); + EMBRACE(.name = lls_command_name(cmd))); ret = r->open(&rn); if (ret < 0) - goto out; - r_opened = 1; + goto free_receiver_lpr; + r_opened = true; sot.btrn = btr_new_node(&(struct btr_node_description) EMBRACE(.parent = rn.btrn, .name = "stdout")); stdout_task_register(&sot, &s); - ti.name = r->name; + ti.name = lls_command_name(cmd); ti.pre_select = r->pre_select; ti.post_select = r->post_select; ti.context = &rn; @@ -104,13 +104,16 @@ int main(int argc, char *argv[]) s.default_timeout.tv_usec = 0; ret = schedule(&s); sched_shutdown(&s); + r->close(&rn); + btr_remove_node(&sot.btrn); + btr_remove_node(&rn.btrn); +free_receiver_lpr: + lls_free_parse_result(receiver_lpr, cmd); out: if (r_opened) r->close(&rn); btr_remove_node(&rn.btrn); btr_remove_node(&sot.btrn); - if (rn.conf) - r->free_config(rn.conf); if (ret < 0) PARA_ERROR_LOG("%s\n", para_strerror(-ret)); diff --git a/recv.h b/recv.h index 1a0de659..68978a38 100644 --- a/recv.h +++ b/recv.h @@ -11,11 +11,11 @@ */ struct receiver_node { /** Points to the corresponding receiver. */ - struct receiver *receiver; + const struct receiver *receiver; /** Receiver-specific data. */ void *private_data; - /** Pointer to the configuration data for this instance. */ - void *conf; + /** The parsed command line options for this instance. */ + struct lls_parse_result *lpr; /** The task associated with this instance. */ struct task *task; /** The receiver node is always the root of the buffer tree. */ @@ -44,34 +44,13 @@ struct receiver_node { */ struct receiver { /** - * The name of the receiver. - */ - const char *name; - /** - * The receiver init function. + * The optional receiver init function. * - * It must fill in all other function pointers and is assumed to succeed. + * Performs any initialization needed before the receiver can be opened. * * \sa http_recv_init udp_recv_init. */ - void (*init)(struct receiver *r); - /** - * The command line parser of the receiver. - * - * It should check whether the command line options given by \a argc - * and \a argv are valid. On success, it should return a pointer to - * the receiver-specific configuration data determined by \a argc and - * \a argv. Note that this might be called more than once with - * different values of \a argc and \a argv. - */ - void *(*parse_config)(int argc, char **argv); - /** - * Deallocate the configuration structure of a receiver node. - * - * This calls the receiver-specific cleanup function generated by - * gengetopt. - */ - void (*free_config)(void *conf); + void (*init)(void); /** * Open one instance of the receiver. * @@ -117,8 +96,6 @@ struct receiver { */ int (*post_select)(struct sched *s, void *context); - /** The two help texts of this receiver. */ - struct ggo_help help; /** * Answer a buffer tree query. * @@ -128,31 +105,23 @@ struct receiver { btr_command_handler execute; }; -/** Define an array of all available receivers. */ -#define DEFINE_RECEIVER_ARRAY struct receiver receivers[] = { \ - HTTP_RECEIVER \ - DCCP_RECEIVER \ - UDP_RECEIVER \ - AFH_RECEIVER \ - {.name = NULL}}; +#define RECV_CMD(_num) (lls_cmd(_num, recv_cmd_suite)) + +#define RECV_CMD_OPT_RESULT(_recv, _opt, _lpr) \ + (lls_opt_result(LSG_RECV_CMD_ ## _recv ## _OPT_ ## _opt, _lpr)) +#define RECV_CMD_OPT_GIVEN(_recv, _opt, _lpr) \ + (lls_opt_given(RECV_CMD_OPT_RESULT(_recv, _opt, _lpr))) +#define RECV_CMD_OPT_STRING_VAL(_recv, _opt, _lpr) \ + (lls_string_val(0, RECV_CMD_OPT_RESULT(_recv, _opt, _lpr))) +#define RECV_CMD_OPT_UINT32_VAL(_recv, _opt, _lpr) \ + (lls_uint32_val(0, RECV_CMD_OPT_RESULT(_recv, _opt, _lpr))) +#define RECV_CMD_OPT_INT32_VAL(_recv, _opt, _lpr) \ + (lls_int32_val(0, RECV_CMD_OPT_RESULT(_recv, _opt, _lpr))) /** Iterate over all available receivers. */ -#define FOR_EACH_RECEIVER(i) for (i = 0; receivers[i].name; i++) +#define FOR_EACH_RECEIVER(i) for (i = 1; lls_cmd(i, recv_cmd_suite); i++) void recv_init(void); -void *check_receiver_arg(char *ra, int *receiver_num); -void print_receiver_helps(unsigned flags); +int check_receiver_arg(const char *ra, struct lls_parse_result **lprp); +void print_receiver_helps(bool detailed); int generic_recv_pre_select(struct sched *s, struct receiver_node *rn); - -/** \cond receiver */ -extern void http_recv_init(struct receiver *r); -#define HTTP_RECEIVER {.name = "http", .init = http_recv_init}, -extern void dccp_recv_init(struct receiver *r); -#define DCCP_RECEIVER {.name = "dccp", .init = dccp_recv_init}, -extern void udp_recv_init(struct receiver *r); -#define UDP_RECEIVER {.name = "udp", .init = udp_recv_init}, -#define AFH_RECEIVER /* not active by default */ - -extern struct receiver receivers[]; -/** \endcond receiver */ - diff --git a/recv_common.c b/recv_common.c index 59630dfc..7bb775fe 100644 --- a/recv_common.c +++ b/recv_common.c @@ -7,11 +7,14 @@ /** \file recv_common.c common functions of para_recv and para_audiod */ #include +#include +#include +#include "recv_cmd.lsg.h" #include "para.h" +#include "error.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "recv.h" #include "string.h" @@ -23,92 +26,93 @@ void recv_init(void) { int i; - FOR_EACH_RECEIVER(i) - receivers[i].init(&receivers[i]); -} - -static void *parse_receiver_args(int receiver_num, char *options) -{ - struct receiver *r = &receivers[receiver_num]; - char **argv; - int argc; - void *conf; - - if (options) { - argc = create_shifted_argv(options, " \t", &argv); - if (argc < 0) - return NULL; - } else { - argc = 1; - argv = para_malloc(2 * sizeof(char*)); - argv[1] = NULL; + FOR_EACH_RECEIVER(i) { + const struct lls_command *cmd = RECV_CMD(i); + const struct receiver *r = lls_user_data(cmd); + if (r && r->init) + r->init(); } - argv[0] = make_message("%s_recv", r->name); - conf = r->parse_config(argc, argv); - free_argv(argv); - return conf; } /** * Check if the given string is a valid receiver specifier. * - * \param \ra string of the form receiver_name:options - * \param receiver_num contains the number of the receiver upon success + * \param \ra string of the form receiver_name [options...] + * \param lprp Filled in on success, undefined else. * * This function checks whether \a ra starts with the name of a receiver, * optionally followed by options for that receiver. If a valid receiver name * was found the remaining part of \a ra is passed to the receiver's config * parser. * - * \return On success, a pointer to the receiver-specific gengetopt args info - * struct is returned and \a receiver_num contains the number of the receiver. - * On errors, the function returns \p NULL. + * If a NULL pointer or an empty string is passed as the first argument, the + * hhtp receiver with no options is assumed. + * + * \return On success the number of the receiver is returned. On errors, the + * function calls exit(EXIT_FAILURE). */ -void *check_receiver_arg(char *ra, int *receiver_num) +int check_receiver_arg(const char *ra, struct lls_parse_result **lprp) { - int j; + int ret, argc, receiver_num; + char *errctx = NULL, **argv; + const struct lls_command *cmd; - PARA_DEBUG_LOG("checking %s\n", ra); - for (j = 0; receivers[j].name; j++) { - const char *name = receivers[j].name; - size_t len = strlen(name); - char c; - if (strlen(ra) < len) - continue; - if (strncmp(name, ra, len)) - continue; - c = ra[len]; - if (c && c != ' ') - continue; - if (c && !receivers[j].parse_config) - return NULL; - *receiver_num = j; - return parse_receiver_args(j, c? ra + len + 1: NULL); + *lprp = NULL; + if (!ra || !*ra) { + argc = 1; + argv = para_malloc(2 * sizeof(char*)); + argv[0] = para_strdup("http"); + argv[1] = NULL; + } else { + ret = create_argv(ra, " \t\n", &argv); + if (ret < 0) { + PARA_EMERG_LOG("%s\n", para_strerror(-ret)); + exit(EXIT_FAILURE); + } + argc = ret; } - PARA_ERROR_LOG("receiver not found\n"); - return NULL; + ret = lls(lls_lookup_subcmd(argv[0], recv_cmd_suite, &errctx)); + if (ret < 0) { + PARA_EMERG_LOG("%s: %s\n", errctx? errctx : argv[0], + para_strerror(-ret)); + exit(EXIT_FAILURE); + } + receiver_num = ret; + cmd = RECV_CMD(receiver_num); + ret = lls(lls_parse(argc, argv, cmd, lprp, &errctx)); + if (ret < 0) { + if (errctx) + PARA_ERROR_LOG("%s\n", errctx); + PARA_EMERG_LOG("%s\n", para_strerror(-ret)); + exit(EXIT_FAILURE); + } + ret = receiver_num; + free_argv(argv); + return ret; } /** * Print out the help texts to all receivers. * - * \param flags Passed to \ref ggo_print_help(). + * \param detailed Whether to print the short or the detailed help. */ -void print_receiver_helps(unsigned flags) +void print_receiver_helps(bool detailed) { int i; - printf_or_die("\nAvailable receivers: "); - FOR_EACH_RECEIVER(i) - printf_or_die("%s%s", i? " " : "", receivers[i].name); - printf_or_die("\n"); + printf("\nAvailable receivers: "); + FOR_EACH_RECEIVER(i) { + const struct lls_command *cmd = RECV_CMD(i); + printf("%s%s", i? " " : "", lls_command_name(cmd)); + } + printf("\n\n"); FOR_EACH_RECEIVER(i) { - struct receiver *r = receivers + i; - if (!r->help.short_help) + const struct lls_command *cmd = RECV_CMD(i); + char *help = detailed? lls_long_help(cmd) : lls_short_help(cmd); + if (!help) continue; - printf_or_die("\n%s: %s", r->name, - r->help.purpose); - ggo_print_help(&r->help, flags); + printf("%s\n", help); + free(help); } } diff --git a/t/t0005-man.sh b/t/t0005-man.sh index 8f966cd5..ad9fa4c7 100755 --- a/t/t0005-man.sh +++ b/t/t0005-man.sh @@ -25,7 +25,7 @@ grep_man() # in the man pages regex="$rfw_regex" -test_expect_success 'para_recv: receiver options' "grep_man '$regex' recv" +test_expect_success 'para_recv: receiver options' "grep_man 'RECEIVERS' recv" test_expect_success 'para_filter: filter options' "grep_man '$regex' filter" test_expect_success 'para_write: writer options' "grep_man '$regex' write" test_require_objects "audiod" @@ -33,7 +33,7 @@ if [[ -n "$result" ]]; then test_skip 'para_audiod' "missing object(s): $result" else test_expect_success 'para_audiod: receivers' \ - "grep_man 'Options for the http receiver' audiod" + "grep_man 'RECEIVERS' audiod" test_expect_success 'para_audiod: filters' \ "grep_man 'Options for the compress filter' audiod" test_expect_success 'para_audiod: writers' \ diff --git a/udp_recv.c b/udp_recv.c index b803b497..a5dfc879 100644 --- a/udp_recv.c +++ b/udp_recv.c @@ -13,16 +13,16 @@ #include #include #include +#include +#include "recv_cmd.lsg.h" #include "para.h" #include "error.h" #include "portable_io.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "recv.h" -#include "udp_recv.cmdline.h" #include "string.h" #include "net.h" #include "fd.h" @@ -103,13 +103,6 @@ static void udp_recv_close(struct receiver_node *rn) btr_pool_free(rn->btrp); } -static void *udp_recv_parse_config(int argc, char **argv) -{ - struct udp_recv_args_info *tmp = para_calloc(sizeof(*tmp)); - udp_recv_cmdline_parser(argc, argv, tmp); - return tmp; -} - /* * Perform AF-independent joining of multicast receive addresses. * @@ -173,58 +166,33 @@ err: static int udp_recv_open(struct receiver_node *rn) { - struct udp_recv_args_info *c = rn->conf; - char *iface = c->iface_given ? c->iface_arg : NULL; + struct lls_parse_result *lpr = rn->lpr; + const char *iface = RECV_CMD_OPT_STRING_VAL(UDP, IFACE, lpr); + const char *host = RECV_CMD_OPT_STRING_VAL(UDP, HOST, lpr); + uint32_t port = RECV_CMD_OPT_UINT32_VAL(UDP, PORT, lpr); int ret; - ret = makesock(IPPROTO_UDP, 1, c->host_arg, c->port_arg, NULL); + ret = makesock(IPPROTO_UDP, 1, host, port, NULL); if (ret < 0) - goto err; + return ret; rn->fd = ret; - ret = mcast_receiver_setup(rn->fd, iface); - if (ret < 0) { - close(rn->fd); + if (ret < 0) goto err; - } - ret = mark_fd_nonblocking(rn->fd); - if (ret < 0) { - close(rn->fd); + if (ret < 0) goto err; - } - PARA_INFO_LOG("receiving from %s:%d, fd=%d\n", c->host_arg, - c->port_arg, rn->fd); + PARA_INFO_LOG("receiving from %s:%u, fd=%d\n", host, port, rn->fd); rn->btrp = btr_pool_new("udp_recv", 320 * 1024); return rn->fd; err: + close(rn->fd); return ret; } -static void udp_recv_free_config(void *conf) -{ - udp_recv_cmdline_parser_free(conf); - free(conf); -} - -/** - * The init function of the udp receiver. - * - * \param r Pointer to the receiver struct to initialize. - * - * Initialize all function pointers of \a r. - */ -void udp_recv_init(struct receiver *r) -{ - struct udp_recv_args_info dummy; - - udp_recv_cmdline_parser_init(&dummy); - r->open = udp_recv_open; - r->close = udp_recv_close; - r->pre_select = udp_recv_pre_select; - r->post_select = udp_recv_post_select; - r->parse_config = udp_recv_parse_config; - r->free_config = udp_recv_free_config; - r->help = (struct ggo_help)DEFINE_GGO_HELP(udp_recv); - udp_recv_cmdline_parser_free(&dummy); -} +const struct receiver lsg_recv_cmd_com_udp_user_data = { + .open = udp_recv_open, + .close = udp_recv_close, + .pre_select = udp_recv_pre_select, + .post_select = udp_recv_post_select, +}; -- 2.39.5