/* Copyright (c) 2019-2020, Codership Oy. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "options.h" #include // isspace() #include #include #include #include // strtol() #include // strcmp() /* * getopt_long() declarations begin */ #define OPTS_NA no_argument #define OPTS_RA required_argument #define OPTS_OA optional_argument typedef enum opt { OPTS_NOOPT = 0, OPTS_ADDRESS = 'a', OPTS_BOOTSTRAP = 'b', OPTS_DELAY = 'd', OPTS_DATA_DIR = 'f', OPTS_HELP = 'h', OPTS_PERIOD = 'i', OPTS_MASTERS = 'm', OPTS_NAME = 'n', OPTS_OPTIONS = 'o', OPTS_BASE_PORT = 'p', OPTS_RECORDS = 'r', OPTS_SLAVES = 's', OPTS_BASE_HOST = 't', OPTS_PROVIDER = 'v', OPTS_WS_SIZE = 'w', OPTS_OPS = 'x' } opt_t; static struct option s_opts[] = { { "address", OPTS_RA, NULL, OPTS_ADDRESS }, { "bootstrap", OPTS_NA, NULL, OPTS_BOOTSTRAP }, { "delay", OPTS_RA, NULL, OPTS_DELAY }, { "storage", OPTS_RA, NULL, OPTS_DATA_DIR }, { "help", OPTS_NA, NULL, OPTS_HELP }, { "period", OPTS_RA, NULL, OPTS_PERIOD }, { "masters", OPTS_RA, NULL, OPTS_MASTERS }, { "name", OPTS_RA, NULL, OPTS_NAME }, { "options", OPTS_RA, NULL, OPTS_OPTIONS, }, { "base-port", OPTS_RA, NULL, OPTS_BASE_PORT }, { "records", OPTS_RA, NULL, OPTS_RECORDS }, { "slaves", OPTS_RA, NULL, OPTS_SLAVES }, { "base-host", OPTS_RA, NULL, OPTS_BASE_HOST }, { "provider", OPTS_RA, NULL, OPTS_PROVIDER }, { "size", OPTS_RA, NULL, OPTS_WS_SIZE }, { "ops", OPTS_RA, NULL, OPTS_OPS }, { NULL, 0, NULL, 0 } }; static const char* opts_string = "a:d:f:hi:m:n:o:p:r:s:t:v:w:x:"; /* * getopt_long() declarations end */ static const struct node_options opts_defaults = { .provider = "none", .address = "", .options = "", .name = "unnamed", .data_dir = ".", .base_host = "localhost", .masters = 0, .slaves = 1, .ws_size = 1024, .records = 1024*1024, .delay = 0, .base_port = 4567, .period = 10, .operations= 1, .bootstrap = true }; static void opts_print_help(FILE* out, const char* prog_name) { fprintf( out, "Usage: %s [OPTION...]\n" "\n" " -h, --help this thing.\n" " -v, --provider=PATH a path to wsrep provider library file.\n" " -a, --address=STRING list of node addresses in the group.\n" " If not set the node assumes that it is the first\n" " node in the group (default)\n" " -o, --options=STRING a string of wsrep provider options.\n" " -n, --name=STRING human-readable node name.\n" " -f, --data-dir=PATH a directory to save working data in.\n" " Should be private to the process.\n" " -t, --base-host=ADDRESS address of this node at which other members can\n" " connect to it\n" " -p, --base-port=NUM base port which the node shall listen for\n" " connections from other members. This port will be\n" " used for replication, port+1 for IST and port+2\n" " for SST. Default: 4567\n" " -m, --masters=NUM number of concurrent master workers.\n" " -s, --slaves=NUM number of concurrent slave workers.\n" " (can't be less than 1)\n" " -w, --size=NUM desirable size of the resulting writesets\n" " (approximate lower boundary). Default: 1K\n" " -r, --records=NUM number of records in the store. Default: 1M\n" " -x, --ops=NUM number of operations per transaction. Default: 1\n" " -d, --delay=NUM delay in milliseconds between \"commits\"\n" " (per master thread).\n" " -b, --bootstrap bootstrap the cluster with this node.\n" " Default: 'Yes' if --address is not given, 'No'\n" " otherwise.\n" " -i, --period period in seconds between performance stats output\n" "\n" , prog_name); } static void opts_print_config(FILE* out, const struct node_options* opts) { fprintf( out, "Continuing with the following configuration:\n" "provider: %s\n" "address: %s\n" "options: %s\n" "name: %s\n" "data dir: %s\n" "base addr: %s:%ld\n" "masters: %ld\n" "slaves: %ld\n" "writeset size: %ld bytes\n" "records: %ld\n" "operations: %ld\n" "commit delay: %ld ms\n" "stats period: %ld s\n" "bootstrap: %s\n" , opts->provider, opts->address, opts->options, opts->name, opts->data_dir, opts->base_host, opts->base_port, opts->masters, opts->slaves, opts->ws_size, opts->records, opts->operations, opts->delay, opts->period, opts->bootstrap ? "Yes" : "No" ); } static int opts_check_conversion(int cond, const char* ptr, int idx) { if (!cond || errno || (*ptr != '\0' && !isspace(*ptr))) { fprintf(stderr, "Bad value for %s option.\n", s_opts[idx].name); return EINVAL; } return 0; } int node_options_read(int argc, char* argv[], struct node_options* opts) { *opts = opts_defaults; int opt = 0; int opt_idx = 0; char* endptr; int ret = 0; bool address_given = false; bool bootstrap_given = false; while ((opt = getopt_long(argc, argv, opts_string, s_opts, &opt_idx)) != -1) { switch (opt) { case OPTS_ADDRESS: address_given = strcmp(opts->address, optarg); opts->address = optarg; break; case OPTS_BOOTSTRAP: bootstrap_given = true; opts->bootstrap = true; break; case OPTS_DELAY: opts->delay = strtol(optarg, &endptr, 10); if ((ret = opts_check_conversion(opts->delay >= 0, endptr, opt_idx))) goto err; break; case OPTS_DATA_DIR: opts->data_dir = optarg; break; case OPTS_HELP: ret = 1; goto help; case OPTS_PERIOD: opts->period = strtol(optarg, &endptr, 10); if ((ret = opts_check_conversion(opts->period > 0, endptr, opt_idx))) goto err; break; case OPTS_MASTERS: opts->masters = strtol(optarg, &endptr, 10); if ((ret = opts_check_conversion(opts->masters >= 0, endptr, opt_idx))) goto err; break; case OPTS_NAME: opts->name = optarg; break; case OPTS_OPTIONS: opts->options = optarg; break; case OPTS_BASE_PORT: opts->base_port = strtol(optarg, &endptr, 10); if ((ret = opts_check_conversion( opts->base_port > 0 && opts->base_port < 65536, endptr, opt_idx))) goto err; break; case OPTS_RECORDS: opts->records = strtol(optarg, &endptr, 10); if ((ret = opts_check_conversion(opts->records >= 0, endptr, opt_idx))) goto err; break; case OPTS_SLAVES: opts->slaves = strtol(optarg, &endptr, 10); if ((ret = opts_check_conversion(opts->slaves > 0, endptr, opt_idx))) goto err; break; case OPTS_BASE_HOST: opts->base_host = optarg; break; case OPTS_PROVIDER: opts->provider = optarg; break; case OPTS_WS_SIZE: opts->ws_size = strtol(optarg, &endptr, 10); if ((ret = opts_check_conversion(opts->ws_size > 0, endptr, opt_idx))) goto err; break; case OPTS_OPS: opts->operations = strtol(optarg, &endptr, 10); if ((ret = opts_check_conversion(opts->operations >= 1, endptr, opt_idx))) goto err; break; default: ret = EINVAL; } } help: if (ret) { opts_print_help(stderr, argv[0]); } else { if (!bootstrap_given) { opts->bootstrap = !address_given; } opts_print_config(stdout, opts); opts->delay *= 1000; /* convert to microseconds for usleep() */ } err: return ret; }