ctdb-scripts: Update statd-callout to try several configuration files
[vlendec/samba-autobuild/.git] / ctdb / common / cmdline.c
index dc0caa12f5ecdb9c38ffbedda43e1728f0f7a830..ee360d4c9208231635028b21fbb3f8043706483b 100644 (file)
-/* 
-   common commandline code to ctdb test tools
+/*
+   Command line processing
 
-   Copyright (C) Andrew Tridgell  2007
+   Copyright (C) Amitay Isaacs  2018
 
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2 of the License, or (at your option) any later version.
+   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; either version 3 of the License, or
+   (at your option) any later version.
 
-   This library is distributed in the hope that it will be useful,
+   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
-   Lesser General Public License for more details.
+   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 Lesser General Public
-   License along with this library; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
 
-#include "includes.h"
-#include "lib/events/events.h"
-#include "system/filesys.h"
-#include "popt.h"
-#include "../include/ctdb.h"
-#include "../include/ctdb_private.h"
-
-/* Handle common command line options for ctdb test progs
- */
-
-static struct {
-       const char *nlist;
-       const char *transport;
-       const char *myaddress;
-       const char *socketname;
-       int self_connect;
-       const char *db_dir;
-       int torture;
-       const char *logfile;
-       const char *events;
-} ctdb_cmdline = {
-       .nlist = ETCDIR "/ctdb/nodes",
-       .transport = "tcp",
-       .myaddress = NULL,
-       .socketname = CTDB_PATH,
-       .self_connect = 0,
-       .db_dir = VARDIR "/ctdb",
-       .torture = 0,
-       .logfile = NULL,
+#include "replace.h"
+
+#include <popt.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "lib/util/debug.h"
+
+#include "common/cmdline.h"
+
+#define CMDLINE_MAX_LEN                80
+
+struct cmdline_context {
+       const char *prog;
+       struct poptOption *options;
+       struct cmdline_command *commands;
+       int max_len;
+       poptContext pc;
+       int argc, arg0;
+       const char **argv;
+       struct cmdline_command *match_cmd;
 };
 
-enum {OPT_EVENTSYSTEM=1};
+static bool cmdline_show_help = false;
 
-static void ctdb_cmdline_callback(poptContext con, 
-                                 enum poptCallbackReason reason,
-                                 const struct poptOption *opt,
-                                 const char *arg, const void *data)
+static void cmdline_popt_help(poptContext pc,
+                             enum poptCallbackReason reason,
+                             struct poptOption *key,
+                             const char *arg,
+                             void *data)
 {
-       switch (opt->val) {
-       case OPT_EVENTSYSTEM:
-               event_set_default_backend(arg);
-               break;
+       if (key->shortName == 'h') {
+               cmdline_show_help = true;
        }
 }
 
-
-struct poptOption popt_ctdb_cmdline[] = {
-       { NULL, 0, POPT_ARG_CALLBACK, (void *)ctdb_cmdline_callback },  
-       { "nlist", 0, POPT_ARG_STRING, &ctdb_cmdline.nlist, 0, "node list file", "filename" },
-       { "listen", 0, POPT_ARG_STRING, &ctdb_cmdline.myaddress, 0, "address to listen on", "address" },
-       { "socket", 0, POPT_ARG_STRING, &ctdb_cmdline.socketname, 0, "local socket name", "filename" },
-       { "transport", 0, POPT_ARG_STRING, &ctdb_cmdline.transport, 0, "protocol transport", NULL },
-       { "self-connect", 0, POPT_ARG_NONE, &ctdb_cmdline.self_connect, 0, "enable self connect", "boolean" },
-       { "debug", 'd', POPT_ARG_INT, &LogLevel, 0, "debug level"},
-       { "dbdir", 0, POPT_ARG_STRING, &ctdb_cmdline.db_dir, 0, "directory for the tdb files", NULL },
-       { "torture", 0, POPT_ARG_NONE, &ctdb_cmdline.torture, 0, "enable nastiness in library", NULL },
-       { "logfile", 0, POPT_ARG_STRING, &ctdb_cmdline.logfile, 0, "log file location", "filename" },
-       { "events", 0, POPT_ARG_STRING, NULL, OPT_EVENTSYSTEM, "event system", NULL },
-       { NULL }
+struct poptOption cmdline_help_options[] = {
+       { NULL, '\0', POPT_ARG_CALLBACK, cmdline_popt_help, 0, NULL, NULL },
+       { "help", 'h', 0, NULL, 'h', "Show this help message", NULL },
+       POPT_TABLEEND
 };
 
+#define CMDLINE_HELP_OPTIONS \
+       { NULL, '\0', POPT_ARG_INCLUDE_TABLE, cmdline_help_options, \
+         0, "Help Options:", NULL }
 
-/*
-  startup daemon side of ctdb according to command line options
- */
-struct ctdb_context *ctdb_cmdline_init(struct event_context *ev)
+static bool cmdline_option_check(struct poptOption *option)
 {
-       struct ctdb_context *ctdb;
-       int ret;
+       if (option->longName == NULL) {
+               D_ERR("Option has no long name\n");
+               return false;
+       }
+
+       if (option->argInfo != POPT_ARG_STRING &&
+           option->argInfo != POPT_ARG_INT &&
+           option->argInfo != POPT_ARG_LONG &&
+           option->argInfo != POPT_ARG_VAL &&
+           option->argInfo != POPT_ARG_FLOAT &&
+           option->argInfo != POPT_ARG_DOUBLE) {
+               D_ERR("Option '%s' has unsupported type\n", option->longName);
+               return false;
+       }
+
+       if (option->arg == NULL) {
+               D_ERR("Option '%s' has invalid arg\n", option->longName);
+               return false;
+       }
+
+       if (option->descrip == NULL) {
+               D_ERR("Option '%s' has no help msg\n", option->longName);
+               return false;
+       }
+
+       return true;
+}
+
+static bool cmdline_options_check(struct poptOption *options)
+{
+       int i;
+       bool ok;
+
+       if (options == NULL) {
+               return true;
+       }
+
+       i = 0;
+       while (options[i].longName != NULL || options[i].shortName != '\0') {
+               ok = cmdline_option_check(&options[i]);
+               if (!ok) {
+                       return false;
+               }
+               i++;
+       }
+
+       return true;
+}
+
+static int cmdline_options_define(TALLOC_CTX *mem_ctx,
+                                 struct poptOption *user_options,
+                                 struct poptOption **result)
+{
+       struct poptOption *options;
+       int count, i;
+
+       count = (user_options == NULL ? 2 : 3);
+
+       options = talloc_array(mem_ctx, struct poptOption, count);
+       if (options == NULL) {
+               return ENOMEM;
+       }
+
+       i = 0;
+       options[i++] = (struct poptOption) CMDLINE_HELP_OPTIONS;
+       if (user_options != NULL) {
+               options[i++] = (struct poptOption) {
+                       .argInfo = POPT_ARG_INCLUDE_TABLE,
+                       .arg = user_options,
+                       .descrip = "Options:",
+               };
+       }
+       options[i++] = (struct poptOption) POPT_TABLEEND;
+
+       *result = options;
+       return 0;
+}
+
+static bool cmdline_command_check(struct cmdline_command *cmd, int *max_len)
+{
+       size_t len;
 
-       if (ctdb_cmdline.nlist == NULL) {
-               printf("You must provide a node list with --nlist\n");
-               exit(1);
+       if (cmd->name == NULL) {
+               return false;
        }
 
-       /* initialise ctdb */
-       ctdb = ctdb_init(ev);
-       if (ctdb == NULL) {
-               printf("Failed to init ctdb\n");
-               exit(1);
+       if (cmd->fn == NULL) {
+               D_ERR("Command '%s' has no implementation function\n",
+                     cmd->name);
+               return false;
        }
 
-       ret = ctdb_set_logfile(ctdb, ctdb_cmdline.logfile);
-       if (ret == -1) {
-               printf("ctdb_set_logfile failed - %s\n", ctdb_errstr(ctdb));
-               exit(1);
+       if (cmd->msg_help == NULL) {
+               D_ERR("Command '%s' has no help msg\n", cmd->name);
+               return false;
        }
 
-       if (ctdb_cmdline.self_connect) {
-               ctdb_set_flags(ctdb, CTDB_FLAG_SELF_CONNECT);
+       len = strlen(cmd->name);
+       if (cmd->msg_args != NULL) {
+               len += strlen(cmd->msg_args);
        }
-       if (ctdb_cmdline.torture) {
-               ctdb_set_flags(ctdb, CTDB_FLAG_TORTURE);
+       if (len > CMDLINE_MAX_LEN) {
+               D_ERR("Command '%s' is too long (%zu)\n", cmd->name, len);
+               return false;
+       }
+
+       if (len > *max_len) {
+               *max_len = (int)len;
        }
 
-       ret = ctdb_set_transport(ctdb, ctdb_cmdline.transport);
-       if (ret == -1) {
-               printf("ctdb_set_transport failed - %s\n", ctdb_errstr(ctdb));
-               exit(1);
+       len = strlen(cmd->msg_help);
+       if (len > CMDLINE_MAX_LEN) {
+               D_ERR("Command '%s' help too long (%zu)\n", cmd->name, len);
+               return false;
+       }
+
+       return true;
+}
+
+static bool cmdline_commands_check(struct cmdline_command *commands,
+                                  int *max_len)
+{
+       int i;
+       bool ok;
+
+       if (commands == NULL) {
+               return false;
        }
 
-       /* tell ctdb what address to listen on */
-       if (ctdb_cmdline.myaddress) {
-               ret = ctdb_set_address(ctdb, ctdb_cmdline.myaddress);
-               if (ret == -1) {
-                       printf("ctdb_set_address failed - %s\n", ctdb_errstr(ctdb));
-                       exit(1);
+       for (i=0; commands[i].name != NULL; i++) {
+               ok = cmdline_command_check(&commands[i], max_len);
+               if (!ok) {
+                       return false;
                }
        }
 
-       /* tell ctdb the socket address */
-       ret = ctdb_set_socketname(ctdb, ctdb_cmdline.socketname);
-       if (ret == -1) {
-               printf("ctdb_set_socketname failed - %s\n", ctdb_errstr(ctdb));
-               exit(1);
+       return true;
+}
+
+static int cmdline_context_destructor(struct cmdline_context *cmdline);
+
+int cmdline_init(TALLOC_CTX *mem_ctx,
+                const char *prog,
+                struct poptOption *options,
+                struct cmdline_command *commands,
+                struct cmdline_context **result)
+{
+       struct cmdline_context *cmdline;
+       int ret, max_len = 0;
+       bool ok;
+
+       if (prog == NULL) {
+               return EINVAL;
+       }
+
+       ok = cmdline_options_check(options);
+       if (!ok) {
+               return EINVAL;
+       }
+
+       ok = cmdline_commands_check(commands, &max_len);
+       if (!ok) {
+               return EINVAL;
+       }
+
+       cmdline = talloc_zero(mem_ctx, struct  cmdline_context);
+       if (cmdline == NULL) {
+               return ENOMEM;
+       }
+
+       cmdline->prog = talloc_strdup(cmdline, prog);
+       if (cmdline->prog == NULL) {
+               talloc_free(cmdline);
+               return ENOMEM;
+       }
+
+       ret = cmdline_options_define(cmdline, options, &cmdline->options);
+       if (ret != 0) {
+               talloc_free(cmdline);
+               return ret;
+       }
+       cmdline->commands = commands;
+       cmdline->max_len = max_len;
+
+       cmdline->argc = 1;
+       cmdline->argv = talloc_array(cmdline, const char *, 2);
+       if (cmdline->argv == NULL) {
+               talloc_free(cmdline);
+               return ENOMEM;
+       }
+       cmdline->argv[0] = cmdline->prog;
+       cmdline->argv[1] = NULL;
+
+       /* Dummy popt context for generating help */
+       cmdline->pc = poptGetContext(cmdline->prog,
+                                    cmdline->argc,
+                                    cmdline->argv,
+                                    cmdline->options,
+                                    0);
+       if (cmdline->pc == NULL) {
+               talloc_free(cmdline);
+               return ENOMEM;
+       }
+
+       talloc_set_destructor(cmdline, cmdline_context_destructor);
+
+       *result = cmdline;
+       return 0;
+}
+
+static int cmdline_context_destructor(struct cmdline_context *cmdline)
+{
+       if (cmdline->pc != NULL) {
+               poptFreeContext(cmdline->pc);
+       }
+
+       return 0;
+}
+
+static int cmdline_parse_options(struct cmdline_context *cmdline,
+                                int argc,
+                                const char **argv)
+{
+       int opt;
+
+       if (cmdline->pc != NULL) {
+               poptFreeContext(cmdline->pc);
+       }
+
+       cmdline->pc = poptGetContext(cmdline->prog,
+                                    argc,
+                                    argv,
+                                    cmdline->options,
+                                    0);
+       if (cmdline->pc == NULL) {
+               return ENOMEM;
        }
 
-       /* tell ctdb what nodes are available */
-       ret = ctdb_set_nlist(ctdb, ctdb_cmdline.nlist);
-       if (ret == -1) {
-               printf("ctdb_set_nlist failed - %s\n", ctdb_errstr(ctdb));
-               exit(1);
+       while ((opt = poptGetNextOpt(cmdline->pc)) != -1) {
+               D_ERR("Invalid option %s: %s\n",
+                     poptBadOption(cmdline->pc, 0),
+                     poptStrerror(opt));
+               return EINVAL;
        }
 
-       if (ctdb_cmdline.db_dir) {
-               ret = ctdb_set_tdb_dir(ctdb, ctdb_cmdline.db_dir);
-               if (ret == -1) {
-                       printf("ctdb_set_tdb_dir failed - %s\n", ctdb_errstr(ctdb));
-                       exit(1);
+       /* Set up remaining arguments for commands */
+       cmdline->argc = 0;
+       cmdline->argv = poptGetArgs(cmdline->pc);
+       if (cmdline->argv != NULL) {
+               while (cmdline->argv[cmdline->argc] != NULL) {
+                       cmdline->argc++;
                }
        }
 
-       return ctdb;
+       return 0;
 }
 
+static int cmdline_match(struct cmdline_context *cmdline)
+{
+       int i;
 
-/*
-  startup a client only ctdb context
- */
-struct ctdb_context *ctdb_cmdline_client(struct event_context *ev)
+       if (cmdline->argc == 0 || cmdline->argv == NULL) {
+               cmdline->match_cmd = NULL;
+               return EINVAL;
+       }
+
+       for (i=0; cmdline->commands[i].name != NULL; i++) {
+               struct cmdline_command *cmd;
+               char name[CMDLINE_MAX_LEN+1];
+               size_t len;
+               char *t, *str;
+               int n = 0;
+               bool match = false;
+
+               cmd = &cmdline->commands[i];
+               len = strlcpy(name, cmd->name, sizeof(name));
+               if (len >= sizeof(name)) {
+                       D_ERR("Skipping long command '%s'\n", cmd->name);
+                       continue;
+               }
+
+               str = name;
+               while ((t = strtok(str, " ")) != NULL) {
+                       if (n >= cmdline->argc) {
+                               match = false;
+                               break;
+                       }
+                       if (cmdline->argv[n] == NULL) {
+                               match = false;
+                               break;
+                       }
+                       if (strcmp(cmdline->argv[n], t) == 0) {
+                               match = true;
+                               cmdline->arg0 = n+1;
+                       } else {
+                               match = false;
+                               break;
+                       }
+
+                       n += 1;
+                       str = NULL;
+               }
+
+               if (match) {
+                       cmdline->match_cmd = cmd;
+                       return 0;
+               }
+       }
+
+       cmdline->match_cmd = NULL;
+       return ENOENT;
+}
+
+int cmdline_parse(struct cmdline_context *cmdline,
+                 int argc,
+                 const char **argv,
+                 bool parse_options)
 {
-       struct ctdb_context *ctdb;
        int ret;
 
-       /* initialise ctdb */
-       ctdb = ctdb_init(ev);
-       if (ctdb == NULL) {
-               printf("Failed to init ctdb\n");
-               exit(1);
+       if (argc < 2) {
+               return EINVAL;
        }
 
-       /* tell ctdb the socket address */
-       ret = ctdb_set_socketname(ctdb, ctdb_cmdline.socketname);
-       if (ret == -1) {
-               printf("ctdb_set_socketname failed - %s\n", ctdb_errstr(ctdb));
-               exit(1);
+       cmdline_show_help = false;
+
+       if (parse_options) {
+               ret = cmdline_parse_options(cmdline, argc, argv);
+               if (ret != 0) {
+                       return ret;
+               }
+       } else {
+               cmdline->argc = argc;
+               cmdline->argv = argv;
        }
 
-       ret = ctdb_socket_connect(ctdb);
-       if (ret != 0) {
-               DEBUG(0,(__location__ " Failed to connect to daemon\n"));
-               talloc_free(ctdb);
-               return NULL;
+       ret = cmdline_match(cmdline);
+       if (!cmdline_show_help && ret != 0) {
+               return ret;
+       }
+
+       return 0;
+}
+
+static void cmdline_usage_command(struct cmdline_context *cmdline,
+                                 struct cmdline_command *cmd,
+                                 bool print_all)
+{
+       int len;
+
+       len = (int)strlen(cmd->name);
+
+       printf("  %s ", cmd->name);
+       if (print_all) {
+               printf("%-*s",
+                      cmdline->max_len-len,
+                      cmd->msg_args == NULL ? "" : cmd->msg_args);
+       } else {
+               printf("%s", cmd->msg_args == NULL ? "" : cmd->msg_args);
        }
+       printf("     %s\n", cmd->msg_help);
+}
+
+static void cmdline_usage_full(struct cmdline_context *cmdline)
+{
+       int i;
+
+       poptSetOtherOptionHelp(cmdline->pc, "[<options>] <command> [<args>]");
+       poptPrintHelp(cmdline->pc, stdout, 0);
+
+       printf("\nCommands:\n");
+       for (i=0; cmdline->commands[i].name != NULL; i++) {
+               cmdline_usage_command(cmdline, &cmdline->commands[i], true);
+
+       }
+}
+
+void cmdline_usage(struct cmdline_context *cmdline, const char *cmd_name)
+{
+       struct cmdline_command *cmd = NULL;
+       int i;
 
-       /* get our vnn */
-       ctdb->vnn = ctdb_ctrl_getvnn(ctdb, timeval_zero(), CTDB_CURRENT_NODE);
-       if (ctdb->vnn == (uint32_t)-1) {
-               DEBUG(0,(__location__ " Failed to get ctdb vnn\n"));
-               talloc_free(ctdb);
-               return NULL;
+       if (cmd_name == NULL) {
+               cmdline_usage_full(cmdline);
+               return;
        }
 
-       return ctdb;
+       for (i=0; cmdline->commands[i].name != NULL; i++) {
+               if (strcmp(cmdline->commands[i].name, cmd_name) == 0) {
+                       cmd = &cmdline->commands[i];
+                       break;
+               }
+       }
+
+       if (cmd == NULL) {
+               cmdline_usage_full(cmdline);
+               return;
+       }
+
+       poptSetOtherOptionHelp(cmdline->pc, "<command> [<args>]");
+       poptPrintUsage(cmdline->pc, stdout, 0);
+
+       printf("\n");
+       cmdline_usage_command(cmdline, cmd, false);
+}
+
+int cmdline_run(struct cmdline_context *cmdline,
+               void *private_data,
+               int *result)
+{
+       struct cmdline_command *cmd = cmdline->match_cmd;
+       TALLOC_CTX *tmp_ctx;
+       int ret;
+
+       if (cmdline_show_help) {
+               const char *name = NULL;
+
+               if (cmd != NULL) {
+                       name = cmdline->match_cmd->name;
+               }
+
+               cmdline_usage(cmdline, name);
+
+               if (result != NULL) {
+                       *result = 0;
+               }
+               return EAGAIN;
+       }
+
+       if (cmd == NULL) {
+               return ENOENT;
+       }
+
+       tmp_ctx = talloc_new(cmdline);
+       if (tmp_ctx == NULL) {
+               return ENOMEM;
+       }
+
+       ret = cmd->fn(tmp_ctx,
+                     cmdline->argc - cmdline->arg0,
+                     &cmdline->argv[cmdline->arg0],
+                     private_data);
+
+       talloc_free(tmp_ctx);
+
+       if (result != NULL) {
+               *result = ret;
+       }
+       return 0;
 }