mpathpersist: add option --batch-file (-f)
authorMartin Wilck <mwilck@suse.com>
Mon, 27 May 2019 12:59:35 +0000 (14:59 +0200)
committerChristophe Varoqui <christophe.varoqui@opensvc.com>
Wed, 3 Jul 2019 06:27:53 +0000 (08:27 +0200)
Add the option --batch-file (-f) to mpathpersist. The option argument
is a text file that is parsed line-by-line. Every line of the file is
interpreted as an additional input line for mpathpersist. The initial
"mpathpersist" on each line is optional. The '#' character denotes
a comment. '#' is only recognized after whitespace. Empty lines,
or comment lines, are allowed.

If -f is given, other command line options are parsed as usual and
commands (if any) are run before running the batch file. Inside the
batch file, the option -f is forbidden, and -v is ignored. If a command
fails, the batch processing is not aborted. The return status of
mpathpersist is 0 if all commands succeeded, and the status of the
first failed command otherwise.

Signed-off-by: Martin Wilck <mwilck@suse.com>
mpathpersist/main.c
mpathpersist/main.h

index a10ec78..ce2039e 100644 (file)
@@ -15,6 +15,7 @@
 #include <pthread.h>
 #include <ctype.h>
 #include <string.h>
+#include <errno.h>
 #include "version.h"
 
 static const char * pr_type_strs[] = {
@@ -60,7 +61,99 @@ void rcu_unregister_thread_memb(void) {}
 
 struct udev *udev;
 
-int main (int argc, char * argv[])
+static int verbose, loglevel, noisy;
+
+static int handle_args(int argc, char * argv[], int line);
+
+static int do_batch_file(const char *batch_fn)
+{
+       char command[] = "mpathpersist";
+       const int ARGV_CHUNK = 2;
+       const char delims[] = " \t\n";
+       size_t len = 0;
+       char *line = NULL;
+       ssize_t n;
+       int nline = 0;
+       int argl = ARGV_CHUNK;
+       FILE *fl;
+       char **argv = calloc(argl, sizeof(*argv));
+       int ret = MPATH_PR_SUCCESS;
+
+       if (argv == NULL)
+               return MPATH_PR_OTHER;
+
+       fl = fopen(batch_fn, "r");
+       if (fl == NULL) {
+               fprintf(stderr, "unable to open %s: %s\n",
+                       batch_fn, strerror(errno));
+               free(argv);
+               return MPATH_PR_SYNTAX_ERROR;
+       } else {
+               if (verbose >= 2)
+                       fprintf(stderr, "running batch file %s\n",
+                               batch_fn);
+       }
+
+       while ((n = getline(&line, &len, fl)) != -1) {
+               char *_token, *token;
+               int argc = 0;
+               int rv;
+
+               nline++;
+               argv[argc++] = command;
+
+               if (line[n-1] == '\n')
+                       line[n-1] = '\0';
+               if (verbose >= 3)
+                       fprintf(stderr, "processing line %d: %s\n",
+                               nline, line);
+
+               for (token = strtok_r(line, delims, &_token);
+                    token != NULL && *token != '#';
+                    token = strtok_r(NULL, delims, &_token)) {
+
+                       if (argc >= argl) {
+                               int argn = argl + ARGV_CHUNK;
+                               char **tmp;
+
+                               tmp = realloc(argv, argn * sizeof(*argv));
+                               if (tmp == NULL)
+                                       break;
+                               argv = tmp;
+                               argl = argn;
+                       }
+
+                       if (argc == 1 && !strcmp(token, command))
+                               continue;
+
+                       argv[argc++] = token;
+               }
+
+               if (argc <= 1)
+                       continue;
+
+               if (verbose >= 2) {
+                       int i;
+
+                       fprintf(stderr, "## file %s line %d:", batch_fn, nline);
+                       for (i = 0; i < argc; i++)
+                               fprintf(stderr, " %s", argv[i]);
+                       fprintf(stderr, "\n");
+               }
+
+               optind = 0;
+               rv = handle_args(argc, argv, nline);
+               if (rv != MPATH_PR_SUCCESS)
+                       ret = rv;
+       }
+
+       fclose(fl);
+       free(argv);
+       free(line);
+       return ret;
+}
+
+static int handle_args(int argc, char * argv[], int nline)
 {
        int fd, c, res;
        const char *device_name = NULL;
@@ -83,51 +176,42 @@ int main (int argc, char * argv[])
        int prin = 1;
        int prin_sa = -1;
        int prout_sa = -1;
-       int verbose = 0;
-       int loglevel = 0;
-       int noisy = 0;
        int num_transport =0;
+       char *batch_fn = NULL;
        void *resp = NULL;
        struct transportid * tmp;
-       struct config *conf;
-
-       if (optind == argc)
-       {
-
-               fprintf (stderr, "No parameter used\n");
-               usage ();
-               exit (1);
-       }
-
-       if (getuid () != 0)
-       {
-               fprintf (stderr, "need to be root\n");
-               exit (1);
-       }
-
-       udev = udev_new();
-       conf = mpath_lib_init();
-       if(!conf) {
-               udev_unref(udev);
-               exit(1);
-       }
+       struct config *conf = multipath_conf;
 
        memset(transportids, 0, MPATH_MX_TIDS * sizeof(struct transportid));
-       multipath_conf = conf;
 
        while (1)
        {
                int option_index = 0;
 
-               c = getopt_long (argc, argv, "v:Cd:hHioYZK:S:PAT:skrGILcRX:l:",
+               c = getopt_long (argc, argv, "v:Cd:hHioYZK:S:PAT:skrGILcRX:l:f:",
                                long_options, &option_index);
                if (c == -1)
                        break;
 
                switch (c)
                {
+                       case 'f':
+                               if (nline != 0) {
+                                       fprintf(stderr,
+                                               "ERROR: -f option not allowed in batch file\n");
+                                       ret = MPATH_PR_SYNTAX_ERROR;
+                                       goto out;
+                               }
+                               if (batch_fn != NULL) {
+                                       fprintf(stderr,
+                                               "ERROR: -f option can be used at most once\n");
+                                       ret = MPATH_PR_SYNTAX_ERROR;
+                                       goto out;
+                               }
+                               batch_fn = strdup(optarg);
+                               break;
                        case 'v':
-                               if (1 != sscanf (optarg, "%d", &loglevel))
+                               if (nline == 0 && 1 != sscanf (optarg, "%d", &loglevel))
                                {
                                        fprintf (stderr, "bad argument to '--verbose'\n");
                                        return MPATH_PR_SYNTAX_ERROR;
@@ -288,11 +372,13 @@ int main (int argc, char * argv[])
                }
        }
 
-       /* set verbosity */
-       noisy = (loglevel >= 3) ? 1 : hex;
-       verbose = (loglevel >= 3)? 3: loglevel;
+       if (nline == 0) {
+               /* set verbosity */
+               noisy = (loglevel >= 3) ? 1 : hex;
+               verbose = (loglevel >= 3)? 3: loglevel;
+       }
 
-       if ((prout_flag + prin_flag) == 0)
+       if ((prout_flag + prin_flag) == 0 && batch_fn == NULL)
        {
                fprintf (stderr, "choose either '--in' or '--out' \n");
                ret = MPATH_PR_SYNTAX_ERROR;
@@ -342,7 +428,8 @@ int main (int argc, char * argv[])
        }
        else
        {
-               ret = MPATH_PR_SYNTAX_ERROR;
+               if (batch_fn == NULL)
+                       ret = MPATH_PR_SYNTAX_ERROR;
                goto out;
        }
 
@@ -488,10 +575,52 @@ int main (int argc, char * argv[])
        }
 
 out :
-       if (ret == MPATH_PR_SYNTAX_ERROR)
-               usage();
-       mpath_lib_exit(conf);
+       if (ret == MPATH_PR_SYNTAX_ERROR) {
+               free(batch_fn);
+               if (nline == 0)
+                       usage();
+               else
+                       fprintf(stderr, "syntax error on line %d in batch file\n",
+                               nline);
+       } else if (batch_fn != NULL) {
+               int rv = do_batch_file(batch_fn);
+
+               free(batch_fn);
+               ret = ret == 0 ? rv : ret;
+       }
+       return (ret >= 0) ? ret : MPATH_PR_OTHER;
+}
+
+int main(int argc, char *argv[])
+{
+       int ret;
+
+       if (optind == argc)
+       {
+
+               fprintf (stderr, "No parameter used\n");
+               usage ();
+               exit (1);
+       }
+
+       if (getuid () != 0)
+       {
+               fprintf (stderr, "need to be root\n");
+               exit (1);
+       }
+
+       udev = udev_new();
+       multipath_conf = mpath_lib_init();
+       if(!multipath_conf) {
+               udev_unref(udev);
+               exit(1);
+       }
+
+       ret = handle_args(argc, argv, 0);
+
+       mpath_lib_exit(multipath_conf);
        udev_unref(udev);
+
        return (ret >= 0) ? ret : MPATH_PR_OTHER;
 }
 
@@ -693,6 +822,7 @@ static void usage(void)
                        "                   4           Informational messages with trace enabled\n"
                        "    --clear|-C                 PR Out: Clear\n"
                        "    --device=DEVICE|-d DEVICE  query or change DEVICE\n"
+                       "    --batch-file|-f FILE       run commands from FILE\n"
                        "    --help|-h                  output this usage message\n"
                        "    --hex|-H                   output response in hex\n"
                        "    --in|-i                    request PR In command \n"
index beb8a21..bfbb82e 100644 (file)
@@ -2,6 +2,7 @@ static struct option long_options[] = {
        {"verbose", 1, NULL, 'v'},
        {"clear", 0, NULL, 'C'},
        {"device", 1, NULL, 'd'},
+       {"batch-file", 1, NULL, 'f' },
        {"help", 0, NULL, 'h'},
        {"hex", 0, NULL, 'H'},
        {"in", 0, NULL, 'i'},