/* SPDX-License-Identifier: GPL-2.0 */ /** \file filter.c The stand-alone filter program. * * This file contains the main function of para_filter(1). Besides this * file, the individual filters and \ref filter_common.c are included in the * executable. The buffer tree API and the paraslash scheduler are employed * to implement single-threaded filter chaining. */ /** \cond doxygen_ignore */ #include #include "filter.lsg.h" #include "filter_cmd.lsg.h" #include "para.h" #include "list.h" #include "lsu.h" #include "sched.h" #include "buffer_tree.h" #include "filter.h" #include "string.h" #include "stdin.h" #include "stdout.h" #include "error.h" #include "fd.h" DEFINE_PARA_ERRLIST; static struct lls_parse_result *lpr; /* command line options */ #define CMD_PTR (lls_cmd(0, filter_suite)) #define OPT_RESULT(_name) \ (lls_opt_result(LSG_FILTER_PARA_FILTER_OPT_ ## _name, lpr)) #define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name))) #define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name))) /* * This array contains only NULL pointers. It is needed by the amp filter * which first tries to obtain the amplification value from an element in * this array. */ const char *stat_item_values[NUM_STAT_ITEMS] = {NULL}; static int loglevel; INIT_STDERR_LOGGING(loglevel); static void handle_help_flag(void) { char *help; if (OPT_GIVEN(DETAILED_HELP)) help = lls_long_help(CMD_PTR); else if (OPT_GIVEN(HELP)) help = lls_short_help(CMD_PTR); else return; printf("%s\n", help); free(help); print_filter_helps(OPT_GIVEN(DETAILED_HELP)); exit(EXIT_SUCCESS); } static int parse_config(void) { int ret; version_handle_flag("filter", OPT_GIVEN(VERSION)); handle_help_flag(); ret = lsu_merge_config_file_options(NULL, "filter.conf", &lpr, CMD_PTR, filter_suite, 0 /* default flags */); if (ret < 0) return ret; loglevel = OPT_UINT32_VAL(LOGLEVEL); if (!OPT_GIVEN(FILTER)) { print_filter_list(); exit(EXIT_SUCCESS); } return 1; } /** \endcond * * The main function of para_filter. * * para_filter(1) reads from stdin, passes the input to a chain of filters, * and writes the resulting data to stdout. * * We define one task and one buffer tree node per given filter option, plus * two additional tasks/nodes for stdin and stdout. The filter nodes are * chained together and connected to the stdin and stdout buffer tree nodes * much like a Unix pipe, but without creating additional processes or threads. * * \param argc Options are defined in the filter lopsub suite. * * \param argv The individual filters are a organized in a separate suite file * named filter_cmd.suite. Each filter corresponds to a lopsub subcommand. * * \return EXIT_SUCCESS on success, EXIT_FAILURE on errors. */ int main(int argc, char *argv[]) { struct sched *s = sched_new(NULL); int i, ret; const struct filter *f; struct btr_node *parent; struct filter_node **fns; char *errctx; struct stdin_task *sit; static struct stdout_task *sot; ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx)); if (ret < 0) goto out; ret = parse_config(); if (ret < 0) goto free_lpr; sit = stdin_new(); stdin_register(sit, s); fns = arr_alloc(OPT_GIVEN(FILTER), sizeof(*fns)); for (i = 0, parent = stdin_btrn(sit); i < OPT_GIVEN(FILTER); i++) { const char *fa = lls_string_val(i, OPT_RESULT(FILTER)); const char *name; struct filter_node *fn; struct task_info ti; fn = fns[i] = zalloc(sizeof(*fn)); ret = filter_setup(fa, &fn->conf, &fn->lpr); if (ret < 0) goto teardown; fn->filter_num = ret; name = filter_name(fn->filter_num); PARA_DEBUG_LOG("filter #%d: %s\n", i, name); f = filter_get(fn->filter_num); fn->btrn = btr_new_node(&(struct btr_node_description) EMBRACE(.name = name, .parent = parent, .handler = f->execute, .context = fn)); ti.name = name; ti.pre_monitor = f->pre_monitor; ti.post_monitor = f->post_monitor; ti.context = fn; if (f->open) f->open(fn); fn->task = task_register(&ti, s); parent = fn->btrn; } sot = stdout_new(parent); stdout_register(sot, s); btr_log_tree(stdin_btrn(sit), LL_INFO); ret = schedule(s); sched_shutdown(s); stdout_free(sot); teardown: for (i--; i >= 0; i--) { struct filter_node *fn = fns[i]; const struct lls_command *cmd; f = filter_get(fn->filter_num); if (f->close) f->close(fn); btr_remove_node(&fn->btrn); if (f->teardown) f->teardown(fn->lpr, fn->conf); cmd = lls_cmd(fn->filter_num, filter_cmd_suite); lls_free_parse_result(fn->lpr, cmd); free(fn); } free(fns); stdin_free(sit); free_lpr: lls_free_parse_result(lpr, CMD_PTR); out: if (ret < 0) { if (errctx) PARA_ERROR_LOG("%s\n", errctx); free(errctx); PARA_ERROR_LOG("%s\n", para_strerror(-ret)); } exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS); }