Imported Upstream version 2.6.1
[debian/amanda] / restore-src / amfetchdump.c
index e94ce290f1c843b4965d8a296204552686b04fce..927149da4e94b6f93adbfcbb88784147926a6f32 100644 (file)
  * file named AUTHORS, in the root directory of this distribution.
  */
 /*
- * $Id: amfetchdump.c,v 1.7 2006/03/14 13:12:01 martinea Exp $
+ * $Id: amfetchdump.c,v 1.16 2006/08/24 01:57:15 paddy_s Exp $
  *
  * retrieves specific dumps from a set of amanda tapes
  */
 
 #include "amanda.h"
-#include "tapeio.h"
 #include "fileheader.h"
 #include "util.h"
 #include "restore.h"
 #include "find.h"
 #include "changer.h"
 #include "logfile.h"
+#include "cmdline.h"
+#include "server_util.h"
 
 #define CREAT_MODE     0640
 
-extern char *rst_conf_logdir;
 extern char *rst_conf_logfile;
 extern char *config_dir;
 int get_lock = 0;
@@ -50,222 +50,253 @@ int get_lock = 0;
 typedef struct needed_tapes_s {
     char *label;
     int isafile;
-    find_result_t *files;
-    struct needed_tapes_s *next;
-    struct needed_tapes_s *prev;
+    GSList *files;
 } needed_tape_t;
 
 /* local functions */
 
-void errexit P((void));
-void handle_sigpipe P((int sig));
-tapelist_t *list_needed_tapes P((match_list_t *match_list));
-void usage P((void));
-int main P((int argc, char **argv));
+tapelist_t *list_needed_tapes(GSList *dumpspecs, int only_one, disklist_t *diskqp);
+void usage(void);
+int main(int argc, char **argv);
 
 /* exit routine */
-static int parent_pid = -1;
-static void cleanup P((void));
-
-void errexit()
-/*
- * Do exit(2) after an error, rather than exit(1).
- */
-{
-    exit(2);
-}
+static pid_t parent_pid = -1;
+static void cleanup(void);
 
 
-void usage()
 /*
  * Print usage message and terminate.
  */
+
+void
+usage(void)
 {
-    fprintf(stderr, "Usage: amfetchdump [options] config hostname [diskname [datestamp [level [hostname [diskname [datestamp [level ... ]]]]]]]\n\n");
-    fprintf(stderr, "Goes and grabs a dump from tape, moving tapes around and assembling parts as\n");
-    fprintf(stderr, "necessary.  Files are restored to the current directory, unless otherwise\nspecified.\n\n");
-    fprintf(stderr, "  -p Pipe exactly *one* complete dumpfile to stdout, instead of to disk.\n");
-    fprintf(stderr, "  -o <output dir> Restore files to this directory.\n");
-    fprintf(stderr, "  -d <device> Force restoration from a particular tape device.\n");
-    fprintf(stderr, "  -c Compress output, fastest method available.\n");
-    fprintf(stderr, "  -C Compress output, best filesize method available.\n");
-    fprintf(stderr, "  -l Leave dumps (un)compressed, whichever way they were originally on tape.\n");
-    fprintf(stderr, "  -a Assume all tapes are available via changer, do not prompt for initial load.\n");
-    fprintf(stderr, "  -i <dst_file> Search through tapes and write out an inventory while we\n     restore.  Useful only if normal logs are unavailable.\n");
-    fprintf(stderr, "  -w Wait to put split dumps together until all chunks have been restored.\n");
-    fprintf(stderr, "  -n Do not reassemble split dumpfiles.\n");
-    fprintf(stderr, "  -k Skip the rewind/label read when reading a new tape.\n");
-    fprintf(stderr, "  -s Do not use fast forward to skip files we won't restore.  Use only if fsf\n     causes your tapes to skip too far.\n");
-    fprintf(stderr, "  -b <blocksize> Force a particular block size (default is 32kb).\n");
+    g_fprintf(stderr, _("Usage: amfetchdump [options] config hostname [diskname [datestamp [level [hostname [diskname [datestamp [level ... ]]]]]]] [-o configoption]*\n\n"));
+    g_fprintf(stderr, _("Goes and grabs a dump from tape, moving tapes around and assembling parts as\n"));
+    g_fprintf(stderr, _("necessary.  Files are restored to the current directory, unless otherwise\nspecified.\n\n"));
+    g_fprintf(stderr, _("  -p Pipe exactly *one* complete dumpfile to stdout, instead of to disk.\n"));
+    g_fprintf(stderr, _("  -O <output dir> Restore files to this directory.\n"));
+    g_fprintf(stderr, _("  -d <device> Force restoration from a particular tape device.\n"));
+    g_fprintf(stderr, _("  -c Compress output, fastest method available.\n"));
+    g_fprintf(stderr, _("  -C Compress output, best filesize method available.\n"));
+    g_fprintf(stderr, _("  -l Leave dumps (un)compressed, whichever way they were originally on tape.\n"));
+    g_fprintf(stderr, _("  -a Assume all tapes are available via changer, do not prompt for initial load.\n"));
+    g_fprintf(stderr, _("  -i <dst_file> Search through tapes and write out an inventory while we\n     restore.  Useful only if normal logs are unavailable.\n"));
+    g_fprintf(stderr, _("  -w Wait to put split dumps together until all chunks have been restored.\n"));
+    g_fprintf(stderr, _("  -n Do not reassemble split dumpfiles.\n"));
+    g_fprintf(stderr, _("  -k Skip the rewind/label read when reading a new tape.\n"));
+    g_fprintf(stderr, _("  -s Do not use fast forward to skip files we won't restore.  Use only if fsf\n     causes your tapes to skip too far.\n"));
+    g_fprintf(stderr, _("  -b <blocksize> Force a particular block size (default is 32kb).\n"));
     exit(1);
 }
 
+static gint
+sort_needed_tapes_by_write_timestamp(
+       gconstpointer a,
+       gconstpointer b)
+{
+    needed_tape_t *a_nt = (needed_tape_t *)a;
+    needed_tape_t *b_nt = (needed_tape_t *)b;
+    tape_t *a_t = a_nt->isafile? NULL : lookup_tapelabel(a_nt->label);
+    tape_t *b_t = b_nt->isafile? NULL : lookup_tapelabel(b_nt->label);
+    char *a_ds = a_t? a_t->datestamp : "none";
+    char *b_ds = b_t? b_t->datestamp : "none";
+
+    return strcmp(a_ds, b_ds);
+}
+
 /*
  * Build the list of tapes we'll be wanting, and include data about the
  * files we want from said tapes while we're at it (the whole find_result
  * should do fine)
  */
-tapelist_t *list_needed_tapes(match_list)
-match_list_t *match_list;
+tapelist_t *
+list_needed_tapes(
+    GSList *   dumpspecs,
+    int                only_one,
+    disklist_t *diskqp)
 {
-    needed_tape_t *needed_tapes = NULL, *curtape = NULL;
-    disklist_t diskqp;
-    match_list_t *me = NULL;
+    GSList *needed_tapes = NULL;
+    GSList *seen_dumps = NULL;
+    GSList *iter, *iter2;
     find_result_t *alldumps = NULL;
+    find_result_t *curmatch = NULL;
+    find_result_t *matches = NULL;
     tapelist_t *tapes = NULL;
-    int numtapes = 0;
-    char *conf_diskfile, *conf_tapelist;
-
-    /* For disks and tape lists */
-    conf_diskfile = getconf_str(CNF_DISKFILE);
-    conf_tapelist = getconf_str(CNF_TAPELIST);
-    if (*conf_diskfile == '/') {
-        conf_diskfile = stralloc(conf_diskfile);
-    } else {
-        conf_diskfile = stralloc2(config_dir, conf_diskfile);
-    }
-    if(read_diskfile(conf_diskfile, &diskqp) != 0) {
-        error("could not load disklist \"%s\"", conf_diskfile);
-    }
-    if (*conf_tapelist == '/') {
-        conf_tapelist = stralloc(conf_tapelist);
-    } else {
-        conf_tapelist = stralloc2(config_dir, conf_tapelist);
-    }
+    char *conf_tapelist;
+
+    /* Load the tape list */
+    conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
     if(read_tapelist(conf_tapelist)) {
-        error("could not load tapelist \"%s\"", conf_tapelist);
+        error(_("could not load tapelist \"%s\""), conf_tapelist);
+       /*NOTREACHED*/
     }
-    amfree(conf_diskfile);
     amfree(conf_tapelist);
 
     /* Grab a find_output_t of all logged dumps */
-    alldumps = find_dump(1, &diskqp); 
-    free_disklist(&diskqp);
+    alldumps = find_dump(diskqp);
     if(alldumps == NULL){
-        fprintf(stderr, "No dump records found\n");
+        g_fprintf(stderr, _("No dump records found\n"));
         exit(1);
     }
+
     /* Compare all known dumps to our match list, note what we'll need */
-    for(me = match_list; me; me = me->next) {
-       find_result_t *curmatch = NULL; 
-       find_result_t *matches = NULL;  
-
-       matches = dumps_match(alldumps, me->hostname, me->diskname,
-                                me->datestamp, me->level, 1);
-       sort_find_result("Dhklp", &matches);
-       for(curmatch = matches; curmatch; curmatch = curmatch->next){
-           int havetape = 0;
-           if(strcmp("OK", curmatch->status)){
-               fprintf(stderr,"Dump %d %s %s %d had status '%s', skipping\n",
-                                curmatch->datestamp, curmatch->hostname,
-                                curmatch->diskname, curmatch->level,
-                                curmatch->status);
-               continue;
-           }
-           for(curtape = needed_tapes; curtape; curtape = curtape->next) {
-               if(!strcmp(curtape->label, curmatch->label)){
-                   find_result_t *rsttemp = NULL;
-                   find_result_t *rstfile = alloc(sizeof(find_result_t));
-                   int keep = 1;
-
-                   memcpy(rstfile, curmatch, sizeof(find_result_t));
-
-                   havetape = 1;
-
-                   for(rsttemp = curtape->files;
-                           rsttemp;
-                           rsttemp=rsttemp->next){
-                       if(rstfile->filenum == rsttemp->filenum){
-                           fprintf(stderr, "Seeing multiple entries for tape %s file %d, using most recent\n", curtape->label, rstfile->filenum);
-                           keep = 0;
-                       }
-                   }
-                   if(!keep){
-                       amfree(rstfile);
-                       break;
-                   }
-                   rstfile->next = curtape->files;
+    matches = dumps_match_dumpspecs(alldumps, dumpspecs, 1);
+    sort_find_result("Dhklpw", &matches);
+    for(curmatch = matches; curmatch; curmatch = curmatch->next) {
+       int havetape = 0;
+
+       g_fprintf(stderr, "Examining %s %s on %s\n", curmatch->hostname, curmatch->diskname, curmatch->label);
+       /* keep only first dump if only_one */
+       if (only_one &&
+           curmatch != matches &&
+           (strcmp(curmatch->hostname, matches->hostname) ||
+            strcmp(curmatch->diskname, matches->diskname) ||
+            strcmp(curmatch->timestamp, matches->timestamp) ||
+            curmatch->level != matches->level)) {
+           g_fprintf(stderr, "only_one matched\n");
+           continue;
+       }
+       if(strcmp("OK", curmatch->status)){
+           g_fprintf(stderr,_("Dump %s %s %s %d had status '%s', skipping\n"),
+                            curmatch->timestamp, curmatch->hostname,
+                            curmatch->diskname, curmatch->level,
+                            curmatch->status);
+           g_fprintf(stderr, "!OK\n");
+           continue;
+       }
 
-                   if(curmatch->filenum < 1) curtape->isafile = 1;
-                   else curtape->isafile = 0;
-                   curtape->files = rstfile;
+       for(iter = needed_tapes; iter; iter = iter->next) {
+           needed_tape_t *curtape = iter->data;
+           if (!strcmp(curtape->label, curmatch->label)) {
+               int keep = 1;
+
+               havetape = 1;
+
+               for(iter2 = curtape->files; iter2; iter2 = iter2->next){
+                   find_result_t *rsttemp = iter2->data;
+                   if(curmatch->filenum == rsttemp->filenum){
+                       g_fprintf(stderr, _("Seeing multiple entries for tape "
+                                  "%s file %lld, using most recent\n"),
+                                   curtape->label,
+                                   (long long)curmatch->filenum);
+                       keep = 0;
+                   }
+               }
+               if(!keep){
                    break;
                }
+
+               curtape->isafile = (curmatch->filenum < 1);
+               curtape->files = g_slist_prepend(curtape->files, curmatch);
+               break;
            }
-           if(!havetape){
-               find_result_t *rstfile = alloc(sizeof(find_result_t));
-               needed_tape_t *newtape =
-                                         alloc(sizeof(needed_tape_t));
-               memcpy(rstfile, curmatch, sizeof(find_result_t));
-               rstfile->next = NULL;
-               newtape->files = rstfile;
-               if(curmatch->filenum < 1) newtape->isafile = 1;
-               else newtape->isafile = 0;
-               newtape->label = curmatch->label;
-               if(needed_tapes){
-                   needed_tapes->prev->next = newtape;
-                   newtape->prev = needed_tapes->prev;
-                   needed_tapes->prev = newtape;
-               }
-               else{
-                   needed_tapes = newtape;
-                   needed_tapes->prev = needed_tapes;
-               }
-               newtape->next = NULL;
-               numtapes++;
-#if 0
-//             free_find_result(rstfile);
-#endif
-           } /* if(!havetape) */
-
-       } /* for(curmatch = matches ... */
-    } /* for(me = match_list ... */
-
-    if(numtapes == 0){
-      fprintf(stderr, "No matching dumps found\n");
+       }
+       if (!havetape) {
+           needed_tape_t *newtape = g_new0(needed_tape_t, 1);
+           newtape->files = g_slist_prepend(newtape->files, curmatch);
+           newtape->isafile = (curmatch->filenum < 1);
+           newtape->label = curmatch->label;
+           needed_tapes = g_slist_prepend(needed_tapes, newtape);
+       } /* if(!havetape) */
+
+    } /* for(curmatch = matches ... */
+
+    if(g_slist_length(needed_tapes) == 0){
+      g_fprintf(stderr, _("No matching dumps found\n"));
       exit(1);
+      /* NOTREACHED */
     }
 
-    /* stick that list in a structure that librestore will understand */
-    for(curtape = needed_tapes; curtape; curtape = curtape->next) {
-       find_result_t *curfind = NULL;
-       for(curfind = curtape->files; curfind; curfind = curfind->next) {
-           tapes = append_to_tapelist(tapes, curtape->label,
-                                      curfind->filenum, curtape->isafile);
+    /* sort the tapelist by tape write_timestamp */
+    needed_tapes = g_slist_sort(needed_tapes, sort_needed_tapes_by_write_timestamp);
+
+    /* stick that list in a structure that librestore will understand, removing
+     * files we have already seen in the process; this prefers the earliest written
+     * copy of any dumps which are available on multiple tapes */
+    seen_dumps = NULL;
+    for(iter = needed_tapes; iter; iter = iter->next) {
+       needed_tape_t *curtape = iter->data;
+       for(iter2 = curtape->files; iter2; iter2 = iter2->next) {
+           find_result_t *curfind = iter2->data;
+           find_result_t *prev;
+           GSList *iter;
+           int have_part;
+
+           /* have we already seen this? */
+           have_part = 0;
+           for (iter = seen_dumps; iter; iter = iter->next) {
+               prev = iter->data;
+
+               if (!strcmp(prev->partnum, curfind->partnum) &&
+                   !strcmp(prev->hostname, curfind->hostname) &&
+                   !strcmp(prev->diskname, curfind->diskname) &&
+                   !strcmp(prev->timestamp, curfind->timestamp) &&
+                   prev->level == curfind->level) {
+                   have_part = 1;
+                   break;
+               }
+           }
+
+           if (!have_part) {
+               seen_dumps = g_slist_prepend(seen_dumps, curfind);
+               tapes = append_to_tapelist(tapes, curtape->label,
+                                          curfind->filenum, -1, curtape->isafile);
+           }
        }
     }
 
-    fprintf(stderr, "%d tape(s) needed for restoration\n", numtapes);
+    /* free our resources */
+    for (iter = needed_tapes; iter; iter = iter->next) {
+       needed_tape_t *curtape = iter->data;
+       g_slist_free(curtape->files);
+       g_free(curtape);
+    }
+    g_slist_free(seen_dumps);
+    g_slist_free(needed_tapes);
+    free_find_result(&matches);
+
+    /* and we're done */
+    g_fprintf(stderr, _("%d tape(s) needed for restoration\n"), num_entries(tapes));
     return(tapes);
 }
 
-int main(argc, argv)
-int argc;
-char **argv;
+
 /*
  * Parses command line, then loops through all files on tape, restoring
  * files that match the command line criteria.
  */
+
+int
+main(
+    int                argc,
+    char **    argv)
 {
     extern int optind;
     int opt;
-    char *errstr;
-    match_list_t *match_list = NULL;
-    match_list_t *me = NULL;
+    GSList *dumpspecs = NULL;
     int fd;
-    char *config_name = NULL;
-    char *conffile = NULL;
     tapelist_t *needed_tapes = NULL;
     char *e;
-    int arg_state;
     rst_flags_t *rst_flags;
-    struct passwd *pwent;
+    int minimum_arguments;
+    config_overwrites_t *cfg_ovr = NULL;
+    disklist_t diskq;
+    char * conf_diskfile = NULL;
 
-    for(fd = 3; fd < FD_SETSIZE; fd++) {
+    /*
+     * Configure program for internationalization:
+     *   1) Only set the message locale for now.
+     *   2) Set textdomain for all amanda related programs to "amanda"
+     *      We don't want to be forced to support dozens of message catalogs.
+     */  
+    setlocale(LC_MESSAGES, "C");
+    textdomain("amanda"); 
+
+    for(fd = 3; fd < (int)FD_SETSIZE; fd++) {
        /*
         * Make sure nobody spoofs us with a lot of extra open files
-        * that would cause an open we do to get a very high file
+        * that would cause a successful open to get a very high file
         * descriptor, which in turn might be used as an index into
         * an array (e.g. an fd_set).
         */
@@ -274,72 +305,55 @@ char **argv;
 
     set_pname("amfetchdump");
 
-#ifdef FORCE_USERID
-
-    /* we'd rather not run as root */
-
-    if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
-       client_uid = pwent->pw_uid;
-       client_gid = pwent->pw_gid;
-       endpwent();
-    }
-    if(geteuid() == 0) {
-       if(client_uid == (uid_t) -1) {
-           error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
-       }
-
-       initgroups(CLIENT_LOGIN, client_gid);
-       setgid(client_gid);
-       setuid(client_uid);
-    }
-
-#endif /* FORCE_USERID */
-
     /* Don't die when child closes pipe */
     signal(SIGPIPE, SIG_IGN);
 
-    erroutput_type = ERR_INTERACTIVE;
-
-    onerror(errexit);
+    dbopen(DBG_SUBDIR_SERVER);
 
-    if(argc <= 1) usage();
+    erroutput_type = ERR_INTERACTIVE;
+    error_exit_status = 2;
 
     rst_flags = new_rst_flags();
     rst_flags->wait_tape_prompt = 1;
-    
+
     /* handle options */
-    while( (opt = getopt(argc, argv, "alht:scCpb:nwi:d:o:")) != -1) {
+    cfg_ovr = new_config_overwrites(argc/2);
+    while( (opt = getopt(argc, argv, "alht:scCpb:nwi:d:O:o:")) != -1) {
        switch(opt) {
-       case 'b': rst_flags->compress = 1; break;
-            rst_flags->blocksize = strtol(optarg, &e, 10);
+       case 'b':
+            rst_flags->blocksize = (ssize_t)strtol(optarg, &e, 10);
             if(*e == 'k' || *e == 'K') {
                rst_flags->blocksize *= 1024;
            } else if(*e == 'm' || *e == 'M') {
                rst_flags->blocksize *= 1024 * 1024;
            } else if(*e != '\0') {
-               error("invalid blocksize value \"%s\"", optarg);
+               error(_("invalid blocksize value \"%s\""), optarg);
+               /*NOTREACHED*/
            }
            if(rst_flags->blocksize < DISK_BLOCK_BYTES) {
-               error("minimum block size is %dk", DISK_BLOCK_BYTES / 1024);
+               error(_("minimum block size is %dk"), DISK_BLOCK_BYTES / 1024);
+               /*NOTREACHED*/
            }
            break;
        case 'c': rst_flags->compress = 1; break;
-       case 'o': rst_flags->restore_dir = stralloc(optarg) ; break;
+       case 'O': rst_flags->restore_dir = stralloc(optarg) ; break;
        case 'd': rst_flags->alt_tapedev = stralloc(optarg) ; break;
        case 'C':
            rst_flags->compress = 1;
            rst_flags->comp_type = COMPRESS_BEST_OPT;
            break;
-       case 'p': rst_flags->pipe_to_fd = fileno(stdout); break;
-       case 's': rst_flags->fsf = 0; break;
+       case 'p': rst_flags->pipe_to_fd = STDOUT_FILENO; break;
+       case 's': rst_flags->fsf = (off_t)0; break;
        case 'l': rst_flags->leave_comp = 1; break;
        case 'i': rst_flags->inventory_log = stralloc(optarg); break;
        case 'n': rst_flags->inline_assemble = 0; break;
        case 'w': rst_flags->delay_assemble = 1; break;
        case 'a': rst_flags->wait_tape_prompt = 0; break;
        case 'h': rst_flags->headers = 1; break;
+       case 'o': add_config_overwrite_opt(cfg_ovr, optarg); break;
        default:
            usage();
+           /*NOTREACHED*/
        }
     }
 
@@ -349,127 +363,93 @@ char **argv;
        rst_flags->inline_assemble = 0;
        rst_flags->leave_comp = 1;
        if(rst_flags->compress){
-           error("Cannot force compression when doing inventory/search");
+           error(_("Cannot force compression when doing inventory/search"));
+           /*NOTREACHED*/
        }
-       fprintf(stderr, "Doing inventory/search, dumps will not be uncompressed or assembled on-the-fly.\n");
+       g_fprintf(stderr, _("Doing inventory/search, dumps will not be uncompressed or assembled on-the-fly.\n"));
     }
     else{
        if(rst_flags->delay_assemble){
-           fprintf(stderr, "Using -w, split dumpfiles will *not* be automatically uncompressed.\n");
+           g_fprintf(stderr, _("Using -w, split dumpfiles will *not* be automatically uncompressed.\n"));
        }
     }
 
     /* make sure our options all make sense otherwise */
-    if(check_rst_flags(rst_flags) == -1) usage();
-
-    config_name = argv[optind++];
-    config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
-    conffile = stralloc2(config_dir, CONFFILE_NAME);
-    if (read_conffile(conffile)) {
-       error("errors processing config file \"%s\"", conffile);
+    if(check_rst_flags(rst_flags) == -1) {
+       usage();
+       /*NOTREACHED*/
     }
-    amfree(conffile);
-
 
-    if((argc - optind) < 1 && !rst_flags->inventory_log){
-       fprintf(stderr, "Not enough arguments\n\n");
+    if (rst_flags->inventory_log) {
+        minimum_arguments = 1;
+    } else {
+        minimum_arguments = 2;
+    }
+    if(argc - optind < minimum_arguments) {
        usage();
+       /*NOTREACHED*/
     }
 
-#define ARG_GET_HOST 0
-#define ARG_GET_DISK 1
-#define ARG_GET_DATE 2
-#define ARG_GET_LEVL 3
-
-    arg_state = ARG_GET_HOST;
-    while(optind < argc) {
-        switch(arg_state) {
-        case ARG_GET_HOST:
-            /*
-             * New host/disk/date/level set, so allocate a match_list.
-             */
-            me = alloc(sizeof(*me));
-            me->hostname = argv[optind++];
-            me->diskname = "";
-            me->datestamp = "";
-            me->level = "";
-            me->next = match_list;
-            match_list = me;
-            if(me->hostname[0] != '\0'
-               && (errstr=validate_regexp(me->hostname)) != NULL) {
-                fprintf(stderr, "%s: bad hostname regex \"%s\": %s\n",
-                        get_pname(), me->hostname, errstr);
-                usage();
-            }
-            arg_state = ARG_GET_DISK;
-            break;
-        case ARG_GET_DISK:
-            me->diskname = argv[optind++];
-            if(me->diskname[0] != '\0'
-               && (errstr=validate_regexp(me->diskname)) != NULL) {
-                fprintf(stderr, "%s: bad diskname regex \"%s\": %s\n",
-                        get_pname(), me->diskname, errstr);
-                usage();
-            }
-            arg_state = ARG_GET_DATE;
-            break;
-        case ARG_GET_DATE:
-            me->datestamp = argv[optind++];
-            if(me->datestamp[0] != '\0'
-               && (errstr=validate_regexp(me->datestamp)) != NULL) {
-                fprintf(stderr, "%s: bad datestamp regex \"%s\": %s\n",
-                        get_pname(), me->datestamp, errstr);
-                usage();
-            }
-            arg_state = ARG_GET_LEVL;
-            break;
-        case ARG_GET_LEVL:
-            me->level = argv[optind++];
-            if(me->level[0] != '\0'
-               && (errstr=validate_regexp(me->level)) != NULL) {
-                fprintf(stderr, "%s: bad level regex \"%s\": %s\n",
-                        get_pname(), me->level, errstr);
-                usage();
-            }
-        }
-    }
+    config_init(CONFIG_INIT_EXPLICIT_NAME, argv[optind++]);
+    apply_config_overwrites(cfg_ovr);
+
+    conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
+    read_diskfile(conf_diskfile, &diskq);
+    amfree(conf_diskfile);
 
-    /* XXX I don't think this can happen */
-    if(match_list == NULL && !rst_flags->inventory_log) {
-        match_list = alloc(sizeof(*match_list));
-        match_list->hostname = "";
-        match_list->diskname = "";
-        match_list->datestamp = "";
-        match_list->level = "";
-        match_list->next = NULL;
+    if (config_errors(NULL) >= CFGERR_WARNINGS) {
+       config_print_errors();
+       if (config_errors(NULL) >= CFGERR_ERRORS) {
+           g_critical(_("errors processing config file"));
+       }
     }
 
+    check_running_as(RUNNING_AS_DUMPUSER);
+
+    dbrename(get_config_name(), DBG_SUBDIR_SERVER);
+
+    dumpspecs = cmdline_parse_dumpspecs(argc - optind, argv + optind,
+                                       CMDLINE_PARSE_DATESTAMP |
+                                       CMDLINE_PARSE_LEVEL |
+                                       CMDLINE_EMPTY_TO_WILDCARD);
+
     /*
      * We've been told explicitly to go and search through the tapes the hard
      * way.
      */
     if(rst_flags->inventory_log){
-       fprintf(stderr, "Beginning tape-by-tape search.\n");
-       search_tapes(stderr, 1, NULL, match_list, rst_flags, NULL);
+       g_fprintf(stderr, _("Beginning tape-by-tape search.\n"));
+       search_tapes(stderr, stdin, rst_flags->alt_tapedev == NULL,
+                     NULL, dumpspecs, rst_flags, NULL);
        exit(0);
     }
 
 
     /* Decide what tapes we'll need */
-    needed_tapes = list_needed_tapes(match_list);
-    
+    needed_tapes = list_needed_tapes(dumpspecs,
+                                    rst_flags->pipe_to_fd == STDOUT_FILENO,
+                                    &diskq);
+
     parent_pid = getpid();
     atexit(cleanup);
     get_lock = lock_logfile(); /* config is loaded, should be ok here */
-    search_tapes(NULL, 1, needed_tapes, match_list, rst_flags, NULL);
+    if(get_lock == 0) {
+       char *process_name = get_master_process(rst_conf_logfile);
+       error(_("%s exists: %s is already running, or you must run amcleanup"), rst_conf_logfile, process_name);
+    }
+    log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
+    search_tapes(NULL, stdin, rst_flags->alt_tapedev == NULL,
+                 needed_tapes, dumpspecs, rst_flags, NULL);
     cleanup();
 
-    free_match_list(match_list);
+    dumpspec_list_free(dumpspecs);
 
     if(rst_flags->inline_assemble || rst_flags->delay_assemble)
        flush_open_outputs(1, NULL);
     else flush_open_outputs(0, NULL);
 
+    free_disklist(&diskq);
     free_rst_flags(rst_flags);
 
     return(0);
@@ -478,8 +458,10 @@ char **argv;
 static void
 cleanup(void)
 {
-    if(parent_pid == getpid()) {
-       if(get_lock) unlink(rst_conf_logfile);
+    if (parent_pid == getpid()) {
+       if (get_lock) {
+           log_add(L_INFO, "pid-done %ld\n", (long)getpid());
+           unlink(rst_conf_logfile);
+       }
     }
 }
-