Imported Upstream version 3.1.0
[debian/amanda] / ndmp-src / ndmjob_args.c
diff --git a/ndmp-src/ndmjob_args.c b/ndmp-src/ndmjob_args.c
new file mode 100644 (file)
index 0000000..dc0e473
--- /dev/null
@@ -0,0 +1,972 @@
+/*
+ * Copyright (c) 1998,1999,2000
+ *     Traakan, Inc., Los Altos, CA
+ *     All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Project:  NDMJOB
+ * Ident:    $Id: $
+ *
+ * Description:
+ *
+ */
+
+
+#include "ndmjob.h"
+
+
+char *help_text[] = {
+       "ndmjob -v  -- print version and configuration info",
+       "ndmjob OPTIONS ... FILES ...",
+        "      FILES can be FILEPATH or NEWFILEPATH=OLDFILEPATH with",
+        "      '=' quoted by backslash.",
+       "Modes (exactly one required)",
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+       "  -c       -- create a backup",
+       "  -t       -- list contents on a backup",
+       "  -x       -- extract from a backup",
+       "  -l       -- list media labels",
+       "  -q       -- query agent(s)",
+       "  -Z       -- clean up zee mess (put robot right)",
+       "  -o init-labels -- init media labels",
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+#ifndef NDMOS_EFFECT_NO_SERVER_AGENTS
+       "  -o daemon      -- launch session for incomming connections",
+       "  -o test-daemon -- launch session for incomming connections, exit when stdin is closed",
+       "  -o tape-size=SIZE -- specify the length, in bytes of the simulated tape",
+#endif /* !NDMOS_EFFECT_NO_SERVER_AGENTS */
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+       "  -o rewind      -- rewind tape in drive, need -T and -f",
+       "  -o eject       -- eject tape in drive, need -T and -f",
+       "  -o move        -- cmd ROBOT to move tape, need -o from/to-addr",
+       "  -o import=ELEMADDR -- cmd ROBOT to import tape from door to slot",
+       "  -o export=ELEMADDR -- cmd ROBOT to export tape from slot to door",
+       "  -o load=ELEMADDR   -- cmd ROBOT to load tape from slot to drive",
+       "  -o unload[=ELEMADDR]-- cmd ROBOT to unload tape, sometimes auto",
+       "  -o init-elem-status -- cmd ROBOT to rescan tape slots",
+       "  -o test-tape   -- test TAPE agent NDMP_TAPE functions",
+       "  -o test-mover  -- test TAPE agent NDMP_MOVER functions",
+       "  -o test-data   -- test DATA agent NDMP_DATA functions",
+       "  -o time-limit=N",
+       "           -- check for reply within specified seconds (default 360)",
+       "  -o swap-connect -- perform DATA LISTEN & MOVER CONNECT",
+#if 0
+       "  -p       -- pass DATA->DATA (ndmpcopy)",
+       "  -P       -- pass TAPE->TAPE",
+       "  -o c-partial   -- partial backup",
+       "  -o c-full      -- full backup",
+       "  -o x-restore   -- extract restoring",
+#endif
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+       "General and Logging parameters",
+       " --MACRO   -- expand MACRO from ndmjob-args file",
+       "  -d N     -- set debug level to N (default 0, max 9)",
+       "  -L FILE  -- set log file (default stderr, includes debug)",
+       "  -n       -- no-op, just show how args were handled",
+       "  -v       -- verbose, same messages as -d1 to standard out",
+       "  -S       -- Perform DATA listen and MOVER CONNECT",
+       "  -p PORT  -- NDMP port to listen on (for -o daemon)",
+       "  -o no-time-stamps -- log w/o time stamps, makes diff(1)s easier",
+       "  -o config-file=PATH",
+       "           -- set config file ($NDMJOB_CONFIG, /usr/local/etc/ndmjob.conf)",
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+       "CONTROL of DATA agent parameters",
+       "  -D AGENT -- data agent (see AGENT below)",
+       "  -B TYPE  -- set backup format (default tar)",
+       "  -C DIR   -- change directory on data agent before operation",
+       "  -e PATN  -- exclude files matching pattern",
+       "  -E NAME=VAL  -- add to data agent environment",
+       "  -F FILE  -- add FILE arg (used to not confuse arg processing)",
+       "  -o load-files=PATHNAME",
+       "           -- load FILES from the specified PATHANME",
+       "  -o import=ELEMADDR -- cmd ROBOT to import tape from door to slot",
+
+       "  -I FILE  -- set output index file, enable FILEHIST (default to log)",
+       "  -J FILE  -- set input index file (default none)",
+       "  -U USER  -- user rights to use on data agent",
+       "  -o rules=RULES -- apply RULES to job (see RULES below)",
+       "CONTROL of TAPE agent parameters",
+       "  -T AGENT -- tape agent if different than -D (see AGENT below)",
+       "  -b N     -- block size in 512-byte records (default 20)",
+       "  -f TAPE  -- tape drive device name",
+       "  -o tape-timeout=SECONDS",
+       "           -- how long to retry opening drive (await tape)",
+       "  -o use-eject=N",
+       "           -- use eject when unloading tapes (default 0)",
+        "  -o tape-tcp=hostname:port -- send the data directly to that tcp port.",
+       "CONTROL of ROBOT agent parameters",
+       "  -R AGENT -- robot agent if different than -T (see AGENT below)",
+       "  -m MEDIA -- add entry to media table (see below)",
+       "  -o tape-addr=ELEMADDR",
+       "           -- robot element address of drive (default first)",
+       "  -o tape-scsi=SCSI",
+       "           -- tape drive SCSI target (see below)",
+       "  -o robot-timeout=SECONDS",
+       "           -- how long to retry moving tapes (await robot)",
+       "  -r SCSI  -- tape robot target (see below)",
+       "",
+       "Definitions:",
+       "  AGENT      HOST[:PORT][/FLAGS][,USERNAME,PASSWORD]",
+       "    FLAGS    [234][ntm] 2->v2 3->v3 4->v4  n->AUTH_NONE t->TEXT m->MD5",
+       "  AGENT      .  (resident)",
+       "  SCSI       DEVICE[,[CNUM,]SID[,LUN]]",
+       "  MEDIA      [TAPE-LABEL][+SKIP-FILEMARKS][@ELEMADDR][/WINDOW-SIZE]",
+       "",
+       "RULES:",
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+       0
+};
+
+
+int
+process_args (int argc, char *argv[])
+{
+       int             c;
+       char            options[100];
+       char **         pp;
+       char *          p;
+       char *          op;
+       char *          av[1000];
+       int             ac = 0;
+
+       progname = argv[0];
+
+       if (argc == 2 && strcmp (argv[1], "-help") == 0) {
+               help();
+               exit(0);
+       }
+
+       if (argc == 2 && strcmp (argv[1], "-v") == 0) {
+               ndmjob_version_info ();
+               exit(0);
+       }
+
+       if (argc < 2)
+               usage();
+
+       o_config_file = g_strdup_printf("%s/ndmjob.conf", amdatadir);
+       if ((p = getenv ("NDMJOB_CONF")) != 0) {
+               o_config_file = p;
+       }
+
+       op = options;
+       for (pp = help_text; *pp; pp++) {
+               p = *pp;
+
+               if (strncmp (p, "  -", 3) != 0)
+                       continue;
+               if (p[3] == 'o')
+                       continue;       /* don't include o: repeatedly */
+               *op++ = p[3];
+               if (p[5] != ' ')
+                       *op++ = ':';
+       }
+       *op++ = 'o';                    /* include o: once */
+       *op++ = ':';
+       *op = 0;
+
+       ac = copy_args_expanding_macros (argc, argv,
+                                        av, sizeof(av)/sizeof(av[0]));
+
+       while ((c = getopt (ac, av, options)) != EOF) {
+           switch (c) {
+           case 'o':
+               handle_long_option (optarg);
+               break;
+
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+           case 'c':   /* -c       -- create a backup */
+               set_job_mode (NDM_JOB_OP_BACKUP);
+               break;
+
+           case 't':   /* -t       -- list contents on a backup */
+               set_job_mode (NDM_JOB_OP_TOC);
+               break;
+
+           case 'x':   /* -x       -- extract from a backup */
+               set_job_mode (NDM_JOB_OP_EXTRACT);
+               break;
+
+           case 'l':   /* -l       -- list media labels */
+               set_job_mode (NDM_JOB_OP_LIST_LABELS);
+               break;
+
+           case 'q':   /* -q       -- query agent(s) */
+               set_job_mode (NDM_JOB_OP_QUERY_AGENTS);
+               break;
+
+           case 'Z':   /* -Z       -- clean up zee mess */
+               set_job_mode (NDM_JOB_OP_REMEDY_ROBOT);
+               break;
+
+           case 'B':   /* -B TYPE  -- set backup format (default tar) */
+               if (B_bu_type) {
+                       error_byebye ("more than one of -B");
+               }
+               B_bu_type = optarg;
+               break;
+
+           case 'b':   /* -b N -- block size in 512-byte records (20) */
+            {
+                long b = strtol(optarg, NULL, 10);
+                if (b < 1 || b > 200 || (!b && EINVAL == errno)) {
+                    error_byebye ("bad -b option");
+                }
+                b_bsize = (int) b;
+               break;
+            }
+
+           case 'p':   /* -p N -- port number for daemon mode (10000) */
+            {
+                long p = strtol(optarg, NULL, 10);
+                if (p < 1 || p > 65535 || (!p && EINVAL == errno)) {
+                    error_byebye ("bad -p option");
+                }
+                p_ndmp_port = (int) p;
+               break;
+            }
+
+           case 'C':   /* -C DIR   -- change directory on data agent */
+#if 0
+               /* allow second to override first. make recover easier */
+               if (C_chdir) {
+                       error_byebye ("more than one of -C");
+               }
+#endif
+               C_chdir = optarg;
+               break;
+
+           case 'D':   /* -D AGENT -- data agent (see below) */
+               if (AGENT_GIVEN(D_data_agent)) {
+                       error_byebye ("more than one of -D");
+               }
+               if (ndmagent_from_str (&D_data_agent, optarg)) {
+                       error_byebye ("bad -D argument");
+               }
+               break;
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+
+           case 'd':   /* -d N     -- set debug level to N */
+               d_debug = atoi(optarg);
+               break;
+
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+           case 'E':   /* -E NAME=VAL  -- add to data agent environment */
+               if (n_E_environment >= NDM_MAX_ENV) {
+                       error_byebye ("too many of -E");
+               }
+               {
+                       char *          p;
+
+                       p = optarg;
+                       E_environment[n_E_environment].name = p;
+                       while (*p && *p != '=') p++;
+                       if (*p != '=') {
+                               error_byebye ("missing value in -E");
+                       }
+                       *p++ = 0;
+                       E_environment[n_E_environment].value = p;
+                       n_E_environment++;
+               }
+               break;
+
+           case 'e':   /* -e PATN  -- exclude files matching pattern */
+               if (n_e_exclude_pattern >= MAX_EXCLUDE_PATTERN) {
+                       error_byebye ("too many of -e");
+               }
+               e_exclude_pattern[n_e_exclude_pattern++] = optarg;
+               break;
+
+           case 'F':   /* -F FILE -- add to list of files */
+               if (n_file_arg >= MAX_FILE_ARG) {
+                       error_byebye ("too many FILE args");
+               }
+               if (strchr(optarg, '=')) {
+                   char *p = strchr(optarg, '=');
+                   *p++ = 0;
+                   file_arg[n_file_arg] = p;
+                   file_arg_new[n_file_arg] = optarg;
+                   n_file_arg++;
+               } else {
+                   file_arg[n_file_arg] = optarg;
+                   file_arg_new[n_file_arg] = 0;
+                   n_file_arg++;
+               }
+
+               break;
+
+           case 'f':   /* -f TAPE  -- tape drive device name */
+               if (f_tape_device) {
+                       error_byebye ("more than one of -f");
+               }
+               f_tape_device = optarg;
+               break;
+
+           case 'I':   /* -I FILE  -- output index, enab FILEHIST */
+               if (I_index_file) {
+                       error_byebye ("more than one of -I");
+               }
+               I_index_file = optarg;
+               break;
+
+           case 'J':   /* -J FILE  -- input index */
+               if (J_index_file) {
+                       error_byebye ("more than one of -J");
+               }
+               J_index_file = optarg;
+               break;
+
+           case 'L':   /* -L FILE  -- set log file (def stderr, incl. dbg) */
+               if (L_log_file) {
+                       error_byebye ("more than one of -L");
+               }
+               L_log_file = optarg;
+               if (d_debug < 2) d_debug = 2;
+               break;
+
+           case 'm':   /* -m MEDIA -- add entry to media table (see below) */
+               if (n_m_media >= NDM_MAX_MEDIA) {
+                       error_byebye ("too many of -m");
+               }
+               if (ndmmedia_from_str (&m_media[n_m_media], optarg)) {
+                       error_byebye ("bad -m argument: %s", optarg);
+               }
+               n_m_media++;
+               break;
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+
+           case 'n':   /* -n       -- no-op, show how args were handled */
+               n_noop++;
+               break;
+
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+           case 'R':   /* -R AGENT -- robot agent if different than -T */
+               if (AGENT_GIVEN(R_robot_agent)) {
+                       error_byebye ("more than one of -R");
+               }
+               if (ndmagent_from_str (&R_robot_agent, optarg)) {
+                       error_byebye ("bad -R argument");
+               }
+               break;
+
+           case 'r':   /* -r SCSI  -- tape robot target (see below) */
+               if (ROBOT_GIVEN()) {
+                       error_byebye ("more than one of -r");
+               }
+               if (ndmscsi_target_from_str (&r_robot_target, optarg)) {
+                       error_byebye ("bad -r argument");
+               }
+               break;
+
+           case 'T':   /* -T AGENT -- tape agent if different than -D */
+               if (AGENT_GIVEN(T_tape_agent)) {
+                       error_byebye ("more than one of -T");
+               }
+               if (ndmagent_from_str (&T_tape_agent, optarg)) {
+                       error_byebye ("bad -T argument");
+               }
+               break;
+
+           case 'U':   /* -U USER  -- user rights to use on data agent */
+               if (U_user) {
+                       error_byebye ("more than one of -U");
+               }
+               U_user = optarg;
+               break;
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+
+           case 'v':   /* -v       -- verbose */
+               v_verbose++;
+               break;
+
+           default:
+               usage();
+               break;
+           }
+       }
+
+       if (n_noop && d_debug > 1) {
+               int             i;
+
+               for (i = 0; i < ac; i++) {
+                       printf (" av[%d] = '%s'\n", i, av[i]);
+               }
+       }
+
+       if (!the_mode) {
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+               printf ("must specify one of -[ctxlqZ] or other mode\n");
+#else /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+               printf ("must specify -o daemon\n");
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+               usage();
+       }
+
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+       for (c = optind; c < ac; c++) {
+               if (n_file_arg >= MAX_FILE_ARG) {
+                       error_byebye ("too many file args");
+               }
+               if (strchr(av[c], '=')) {
+                   char *p = strchr(av[c], '=');
+                   *p++ = 0;
+                   file_arg[n_file_arg] = p;
+                   file_arg_new[n_file_arg] = av[c];
+               } else {
+                   file_arg[n_file_arg] = av[c];
+                   file_arg_new[n_file_arg] = 0;
+               }
+               n_file_arg++;
+       }
+
+       if (o_load_files_file) {
+           char buf[2048];
+           FILE *fp;
+           static struct load_file_entry {
+               struct load_file_entry *next;
+               char name[1];
+           } *load_files_list = 0;
+
+           /* clean up old load_files_list */
+           while (load_files_list) {
+               struct load_file_entry *p;
+               p = load_files_list;
+               load_files_list = p->next;
+               p->next = 0;
+               free(p);
+           }
+
+           fp = fopen(o_load_files_file, "r");
+           if (!fp) {
+               perror (o_load_files_file);
+               error_byebye ("can't open load_files file %s",
+                             o_load_files_file);
+               /* no return */
+           }
+           while (fgets (buf, sizeof buf, fp) != NULL) {
+               char *bp = buf, *p, *ep;
+               int len, slen;
+               struct load_file_entry *lfe;
+
+               bp = buf;
+               while (*bp && isspace(*bp))
+                   bp++;
+               ep = bp;
+               while (*ep && (*ep != '\n') && (*ep != '\r'))
+                   ep++;
+               *ep = 0;
+               if (bp >= ep)
+                   continue;
+
+               if (n_file_arg >= MAX_FILE_ARG) {
+                   error_byebye ("too many FILE args");
+               }
+
+               /* allocate memory */
+               slen = (ep-bp)+2;
+               len = sizeof(struct load_file_entry)+(ep-bp)+1;
+               lfe = malloc(len);
+               if (lfe == 0) {
+                   error_byebye ("can't allocate entry for load_files file line %s",
+                                 bp);
+                   /* no return */
+               }
+               lfe->next = 0;
+
+               /* see if we have destination */
+               if ((p = strchr(bp, '=')) != 0) {
+                   int plen;
+                   char ch = *p;
+                   *p = 0;
+
+                   /* double conversion -- assume the strings shrink */
+                   plen = (p-bp);
+                   ndmcstr_to_str(p, &lfe->name[plen+2], slen-plen-2);
+                   ndmcstr_to_str(bp, lfe->name, plen+1);
+                   file_arg[n_file_arg] = &lfe->name[plen+2];
+                   file_arg_new[n_file_arg] = lfe->name;
+                   *p = ch;
+               } else {
+                   /* simple conversion copy */
+                   ndmcstr_to_str(bp, lfe->name, slen-1);
+                   file_arg[n_file_arg] = lfe->name;
+                   file_arg_new[n_file_arg] = 0;
+               }
+               n_file_arg++;
+
+               /* link into list */
+               lfe->next = load_files_list;
+               load_files_list = lfe;
+           }
+
+           fclose (fp);
+       } /* end of load_files option */
+
+       if (!B_bu_type)
+               B_bu_type = "tar";
+
+       /*
+        * A quirk of the NDMP protocol is that the robot
+        * should be accessed over a different connection
+        * than the TAPE agent. (See the Workflow document).
+        */
+       if (ROBOT_GIVEN()) {
+               if (!AGENT_GIVEN(R_robot_agent)) {
+                       if (AGENT_GIVEN(T_tape_agent))
+                               R_robot_agent = T_tape_agent;
+                       else
+                               R_robot_agent = D_data_agent;
+
+                       if (!AGENT_GIVEN(R_robot_agent)) {
+                               error_byebye ("-r given, can't determine -R");
+                       }
+               }
+       } else if (AGENT_GIVEN(R_robot_agent)) {
+               if (the_mode != NDM_JOB_OP_QUERY_AGENTS) {
+                       error_byebye ("-R but no -r");
+               }
+       }
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+
+       return 0;
+}
+
+struct ndmp_enum_str_table     mode_long_name_table[] = {
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+       { "init-labels",        NDM_JOB_OP_INIT_LABELS },
+       { "test-tape",          NDM_JOB_OP_TEST_TAPE },
+       { "test-mover",         NDM_JOB_OP_TEST_MOVER },
+       { "test-data",          NDM_JOB_OP_TEST_DATA },
+       { "eject",              NDM_JOB_OP_EJECT_TAPE },
+       { "rewind",             NDM_JOB_OP_REWIND_TAPE },
+       { "move",               NDM_JOB_OP_MOVE_TAPE },
+       { "import",             NDM_JOB_OP_IMPORT_TAPE },
+       { "export",             NDM_JOB_OP_EXPORT_TAPE },
+       { "load",               NDM_JOB_OP_LOAD_TAPE },
+       { "unload",             NDM_JOB_OP_UNLOAD_TAPE },
+       { "init-elem-status",   NDM_JOB_OP_INIT_ELEM_STATUS },
+       { "-c",                 NDM_JOB_OP_BACKUP },
+       { "-t",                 NDM_JOB_OP_TOC },
+       { "-x",                 NDM_JOB_OP_EXTRACT },
+       { "-l",                 NDM_JOB_OP_LIST_LABELS },
+       { "-q",                 NDM_JOB_OP_QUERY_AGENTS },
+       { "-Z",                 NDM_JOB_OP_REMEDY_ROBOT },
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+#ifndef NDMOS_EFFECT_NO_SERVER_AGENTS
+       { "daemon",             NDM_JOB_OP_DAEMON },
+       { "test-daemon",        NDM_JOB_OP_TEST_DAEMON },
+#endif /* !NDMOS_EFFECT_NO_SERVER_AGENTS */
+       { 0 }
+};
+
+
+int
+handle_long_option (char *str)
+{
+       char *          name;
+       char *          value;
+       int             mode;
+
+       name = str;
+       for (value = str; *value; value++)
+               if (*value == '=')
+                       break;
+       if (*value)
+               *value++ = 0;
+       else
+               value = 0;
+
+       if (ndmp_enum_from_str (&mode, name, mode_long_name_table)) {
+               set_job_mode (mode);
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+               if (value) {
+                       switch (mode) {
+                       default: /* value part ignored */
+                               break;
+
+                       case NDM_JOB_OP_LOAD_TAPE:
+                       case NDM_JOB_OP_EXPORT_TAPE:
+                               o_from_addr = atoi(value);
+                               break;
+                       case NDM_JOB_OP_UNLOAD_TAPE:
+                       case NDM_JOB_OP_IMPORT_TAPE:
+                               o_to_addr = atoi(value);
+                               break;
+                       }
+               }
+       } else if (strcmp (name, "swap-connect") == 0) {
+               /* value part ignored */
+               o_swap_connect++;
+       } else if (strcmp (name, "time-limit") == 0) {
+               if (!value) {
+                       o_time_limit = 5*60;
+               } else {
+                       o_time_limit = atoi(value);
+               }
+       } else if (strcmp (name, "use-eject") == 0) {
+               if (!value) {
+                       o_use_eject = 1;
+               } else {
+                       o_use_eject = atoi(value);
+               }
+       } else if (strcmp (name, "tape-addr") == 0 && value) {
+               o_tape_addr = atoi(value);
+       } else if (strcmp (name, "from-addr") == 0 && value) {
+               o_from_addr = atoi(value);
+       } else if (strcmp (name, "to-addr") == 0 && value) {
+               o_to_addr = atoi(value);
+       } else if (strcmp (name, "tape-timeout") == 0 && value) {
+               o_tape_timeout = atoi(value);
+       } else if (strcmp (name, "robot-timeout") == 0 && value) {
+               o_robot_timeout = atoi(value);
+       } else if (strcmp (name, "tape-scsi") == 0 && value) {
+               if (ndmscsi_target_from_str (&o_tape_scsi, value)) {
+                       error_byebye ("bad -otape-scsi argument");
+               }
+       } else if (strcmp (name, "rules") == 0 && value) {
+               if (!value)
+                       error_byebye ("missing RULES in -o rules");
+               o_rules = value;
+       } else if (strcmp (name, "load-files") == 0 && value) {
+               o_load_files_file = value;
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+       } else if (strcmp (name, "no-time-stamps") == 0) {
+               /* value part ignored */
+               o_no_time_stamps++;
+       } else if (strcmp (name, "config-file") == 0 && value) {
+               o_config_file = value;
+       } else if (strcmp (name, "tape-tcp") == 0 && value) {
+               o_tape_tcp = value;
+       } else if (strcmp (name, "tape-limit") == 0) {
+               if (!value) {
+                       error_byebye ("tape-limit argument is required");
+               } else {
+                       o_tape_limit = atoi(value);
+               }
+       } else {
+               if (value) value[-1] = '=';
+               error_byebye ("unknown/bad long option -o%s", str);
+       }
+
+       if (value) value[-1] = '=';
+       return 0;
+}
+
+void
+set_job_mode (int mode)
+{
+       if (the_mode) {
+               printf ("more than one -[ctxlqZ] or other mode");
+               usage();
+       }
+       the_mode = mode;
+}
+
+void
+usage (void)
+{
+       error_byebye ("bad usage, use -help");
+}
+
+void
+help (void)
+{
+       char *          p;
+       char **         pp;
+
+       for (pp = help_text; *pp; pp++) {
+               p = *pp;
+               printf ("%s\n", p);
+       }
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+       help_rules();
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+}
+
+void
+ndmjob_version_info (void)
+{
+       char            vbuf[100];
+       char            abuf[100];
+       char            obuf[5];
+
+       *vbuf = 0;
+#ifndef NDMOS_OPTION_NO_NDMP2
+       strcat (vbuf, " NDMPv2");
+#endif /* !NDMOS_OPTION_NO_NDMP2 */
+#ifndef NDMOS_OPTION_NO_NDMP3
+       strcat (vbuf, " NDMPv3");
+#endif /* !NDMOS_OPTION_NO_NDMP3 */
+#ifndef NDMOS_OPTION_NO_NDMP4
+       strcat (vbuf, " NDMPv4");
+#endif /* !NDMOS_OPTION_NO_NDMP4 */
+
+       *abuf = 0;
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+       strcat (abuf, " CONTROL");
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+#ifndef NDMOS_OPTION_NO_DATA_AGENT
+       strcat (abuf, " DATA");
+#endif /* !NDMOS_OPTION_NO_DATA_AGENT */
+#ifndef NDMOS_OPTION_NO_TAPE_AGENT
+       strcat (abuf, " TAPE");
+#endif /* !NDMOS_OPTION_NO_TAPE_AGENT */
+#ifndef NDMOS_OPTION_NO_ROBOT_AGENT
+       strcat (abuf, " ROBOT");
+#endif /* !NDMOS_OPTION_NO_ROBOT_AGENT */
+
+       obuf[0] = (char)(NDMOS_ID >> 24);
+       obuf[1] = (char)(NDMOS_ID >> 16);
+       obuf[2] = (char)(NDMOS_ID >> 8);
+       obuf[3] = (char)(NDMOS_ID >> 0);
+       obuf[4] = 0;
+
+       printf ("%s (%s)\n",
+               NDMOS_CONST_PRODUCT_NAME,
+               NDMOS_CONST_VENDOR_NAME);
+
+       printf ("  Rev %s LIB:%d.%d/%s OS:%s (%s)\n",
+               NDMOS_CONST_PRODUCT_REVISION,
+               NDMJOBLIB_VERSION, NDMJOBLIB_RELEASE,
+               NDMOS_CONST_NDMJOBLIB_REVISION,
+               NDMOS_CONST_NDMOS_REVISION,
+               obuf);
+
+       printf ("  Agents:   %s\n", abuf);
+       printf ("  Protocols:%s\n", vbuf);
+}
+
+
+void
+dump_settings (void)
+{
+       int             i;
+       char            buf[100];
+
+       *buf = 0;               /* shuts up -Wall */
+       i = 0;                  /* shuts up -Wall */
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+       switch (the_mode) {
+       case 'x':
+               printf ("mode = x (extract)\n");
+               break;
+
+       case 'c':
+               printf ("mode = c (create)\n");
+               break;
+
+       case 't':
+               printf ("mode = t (table-of-contents)\n");
+               break;
+
+       case 'q':
+               printf ("mode = q (query-agents)\n");
+               break;
+
+       default:
+               printf ("mode = %c (unknown)\n", the_mode);
+               break;
+       }
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+
+       if (v_verbose)
+               printf ("verbose %d\n", v_verbose);
+       else
+               printf ("not verbose\n");
+
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+       printf ("blocksize = %d (%dkb, %db)\n",
+                       b_bsize, b_bsize/2, b_bsize*512);
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+
+       if (d_debug)
+               printf ("debug %d\n", d_debug);
+       else
+               printf ("no debug\n");
+
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+       printf ("Data agent %s\n", D_data_agent.host);
+       if (AGENT_GIVEN(T_tape_agent))
+               printf ("Tape agent %s\n", T_tape_agent.host);
+       else
+               printf ("Tape agent same as data agent\n");
+
+       printf ("tape device %s\n", f_tape_device);
+
+       printf ("tape format %s\n", B_bu_type);
+
+       if (C_chdir)
+               printf ("Chdir %s\n", C_chdir);
+       else
+               printf ("Chdir / (default)\n");
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+
+       if (L_log_file)
+               printf ("Log to file %s\n", L_log_file);
+       else
+               printf ("Log to stderr (default)\n");
+
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+       if (I_index_file) {
+               if (strcmp (I_index_file, "-") == 0) {
+                       printf ("Index to log, enable FILEHIST\n");
+               } else {
+                       printf ("Index to file %s, enable FILEHIST\n",
+                                                       I_index_file);
+               }
+       } else {
+               printf ("Index off (default), no FILEHIST\n");
+       }
+
+       printf ("%d media entries\n", n_m_media);
+       for (i = 0; i < n_m_media; i++) {
+               ndmmedia_to_str (&m_media[i], buf);
+               printf ("  %2d: %s\n", i, buf);
+       }
+
+       printf ("%d excludes\n", n_e_exclude_pattern);
+       for (i = 0; i < n_e_exclude_pattern; i++) {
+               printf ("  %2d: %s\n", i, e_exclude_pattern[i]);
+       }
+
+       printf ("%d environment values\n", n_E_environment);
+       for (i = 0; i < n_E_environment; i++) {
+               printf ("  %2d: %s=%s\n", i,
+                       E_environment[i].name, E_environment[i].value);
+       }
+
+       printf ("%d files\n", n_file_arg);
+       for (i = 0; i < n_file_arg; i++) {
+               printf ("  %2d: @%-8lld %s\n", i,
+                       nlist[i].fh_info.valid ? nlist[i].fh_info.value : NDMP9_INVALID_U_QUAD,
+                       file_arg[i]);
+       }
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
+
+       return;
+}
+
+int
+copy_args_expanding_macros (int argc, char *argv[], char *av[], int max_ac)
+{
+       int             i, ac = 0, rc;
+       char *          arg;
+       char *          p;
+       char            env_name[50];
+
+       /* expand macros */
+       for (i = 0; i < argc; i++) {
+               arg = argv[i];
+
+               if (strncmp (arg, "--", 2) != 0 || arg[2] == 0) {
+                       av[ac++] = arg;
+                       continue;
+               }
+
+               sprintf (env_name, "NDMJOB_%s", arg+2);
+               if ((p = getenv (env_name)) != 0) {
+                       ac += snarf_macro (&av[ac], p);
+                       continue;
+               }
+
+               rc = lookup_and_snarf (&av[ac], arg+2);
+               if (rc < 0) {
+                       error_byebye ("bad arg macro --%s", arg+2);
+               }
+               ac += rc;
+       }
+
+       av[ac] = 0;
+
+       return ac;
+}
+
+int
+lookup_and_snarf (char *av[], char *name)
+{
+       FILE *          fp;
+       char            buf[512];
+       char *          argfile;
+       int             ac = 0;
+       int             found = 0;
+
+       argfile = o_config_file;
+       assert (argfile);
+
+       fp = fopen (argfile, "r");
+       if (!fp) {
+               perror (argfile);
+               error_byebye ("can't open config file %s", argfile);
+       }
+
+       while (ndmstz_getstanza (fp, buf, sizeof buf) >= 0) {
+               if (buf[0] == '-' && buf[1] == '-'
+                && strcmp (buf+2, name) == 0) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (found) {
+               while (ndmstz_getline (fp, buf, sizeof buf) >= 0) {
+                       if (*buf == 0)
+                               continue;
+                       ac += snarf_macro (&av[ac], buf);
+               }
+       }
+
+       fclose (fp);
+
+       if (!found)
+               return -1;
+
+       return ac;
+}
+
+int
+snarf_macro (char *av[], char *val)
+{
+       char *          p;
+       int             ac = 0;
+       char *          tmp_av[100];
+       int             tmp_ac = 0;
+
+       p = NDMOS_API_STRDUP (val);
+       if (!p) {
+               error_byebye ("bad strdup macro");
+       }
+       for (;;) {
+               while (isspace((int)*p)) p++;
+               if (*p == 0) break;
+               tmp_av[tmp_ac++] = p;
+               while (*p && !isspace((int)*p)) p++;
+               if (*p) *p++ = 0;
+       }
+
+       ac = copy_args_expanding_macros (tmp_ac, tmp_av, av, 100);
+
+       return ac;
+}