Imported Upstream version 2.6.0p1
[debian/amanda] / restore-src / amfetchdump.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: amfetchdump.c,v 1.16 2006/08/24 01:57:15 paddy_s Exp $
28  *
29  * retrieves specific dumps from a set of amanda tapes
30  */
31
32 #include "amanda.h"
33 #include "fileheader.h"
34 #include "util.h"
35 #include "restore.h"
36 #include "diskfile.h"
37 #include "tapefile.h"
38 #include "find.h"
39 #include "changer.h"
40 #include "logfile.h"
41 #include "cmdline.h"
42
43 #define CREAT_MODE      0640
44
45 extern char *rst_conf_logfile;
46 extern char *config_dir;
47 int get_lock = 0;
48
49 typedef struct needed_tapes_s {
50     char *label;
51     int isafile;
52     find_result_t *files;
53     struct needed_tapes_s *next;
54     struct needed_tapes_s *prev;
55 } needed_tape_t;
56
57 /* local functions */
58
59 tapelist_t *list_needed_tapes(GSList *dumpspecs, int only_one);
60 void usage(void);
61 int main(int argc, char **argv);
62
63 /* exit routine */
64 static pid_t parent_pid = -1;
65 static void cleanup(void);
66
67
68 /*
69  * Print usage message and terminate.
70  */
71
72 void
73 usage(void)
74 {
75     g_fprintf(stderr, _("Usage: amfetchdump [options] config hostname [diskname [datestamp [level [hostname [diskname [datestamp [level ... ]]]]]]] [-o configoption]*\n\n"));
76     g_fprintf(stderr, _("Goes and grabs a dump from tape, moving tapes around and assembling parts as\n"));
77     g_fprintf(stderr, _("necessary.  Files are restored to the current directory, unless otherwise\nspecified.\n\n"));
78     g_fprintf(stderr, _("  -p Pipe exactly *one* complete dumpfile to stdout, instead of to disk.\n"));
79     g_fprintf(stderr, _("  -O <output dir> Restore files to this directory.\n"));
80     g_fprintf(stderr, _("  -d <device> Force restoration from a particular tape device.\n"));
81     g_fprintf(stderr, _("  -c Compress output, fastest method available.\n"));
82     g_fprintf(stderr, _("  -C Compress output, best filesize method available.\n"));
83     g_fprintf(stderr, _("  -l Leave dumps (un)compressed, whichever way they were originally on tape.\n"));
84     g_fprintf(stderr, _("  -a Assume all tapes are available via changer, do not prompt for initial load.\n"));
85     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"));
86     g_fprintf(stderr, _("  -w Wait to put split dumps together until all chunks have been restored.\n"));
87     g_fprintf(stderr, _("  -n Do not reassemble split dumpfiles.\n"));
88     g_fprintf(stderr, _("  -k Skip the rewind/label read when reading a new tape.\n"));
89     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"));
90     g_fprintf(stderr, _("  -b <blocksize> Force a particular block size (default is 32kb).\n"));
91     exit(1);
92 }
93
94 /*
95  * Build the list of tapes we'll be wanting, and include data about the
96  * files we want from said tapes while we're at it (the whole find_result
97  * should do fine)
98  */
99 tapelist_t *
100 list_needed_tapes(
101     GSList *    dumpspecs,
102     int         only_one)
103 {
104     needed_tape_t *needed_tapes = NULL, *curtape = NULL;
105     disklist_t diskqp;
106     find_result_t *alldumps = NULL;
107     find_result_t *curmatch = NULL;
108     find_result_t *matches = NULL;
109     tapelist_t *tapes = NULL;
110     int numtapes = 0;
111     char *conf_diskfile, *conf_tapelist;
112
113     /* For disks and tape lists */
114     conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
115     if(read_diskfile(conf_diskfile, &diskqp) != 0) {
116         error(_("could not load disklist \"%s\""), conf_diskfile);
117         /*NOTREACHED*/
118     }
119     amfree(conf_diskfile);
120
121     conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
122     if(read_tapelist(conf_tapelist)) {
123         error(_("could not load tapelist \"%s\""), conf_tapelist);
124         /*NOTREACHED*/
125     }
126     amfree(conf_tapelist);
127
128     /* Grab a find_output_t of all logged dumps */
129     alldumps = find_dump(&diskqp);
130     free_disklist(&diskqp);
131     if(alldumps == NULL){
132         g_fprintf(stderr, _("No dump records found\n"));
133         exit(1);
134     }
135     
136     /* Compare all known dumps to our match list, note what we'll need */
137     matches = dumps_match_dumpspecs(alldumps, dumpspecs, 1);
138     sort_find_result("Dhklp", &matches);
139     for(curmatch = matches; curmatch; curmatch = curmatch->next) {
140         int havetape = 0;
141         int have_part = 0;
142
143         /* keep only first dump if only_one */
144         if (only_one &&
145             curmatch != matches &&
146             (strcmp(curmatch->hostname, matches->hostname) ||
147              strcmp(curmatch->diskname, matches->diskname) ||
148              strcmp(curmatch->timestamp, matches->timestamp) ||
149              curmatch->level != matches->level)) {
150             continue;
151         }
152         if(strcmp("OK", curmatch->status)){
153             g_fprintf(stderr,_("Dump %s %s %s %d had status '%s', skipping\n"),
154                              curmatch->timestamp, curmatch->hostname,
155                              curmatch->diskname, curmatch->level,
156                              curmatch->status);
157             continue;
158         }
159         /* check if we already have that part */
160         for(curtape = needed_tapes; curtape; curtape = curtape->next) {
161             find_result_t *rsttemp = NULL;
162             for(rsttemp = curtape->files;
163                 rsttemp;
164                 rsttemp=rsttemp->next) {
165                 if (!strcmp(rsttemp->partnum, curmatch->partnum) &&
166                     !strcmp(rsttemp->hostname, curmatch->hostname) &&
167                     !strcmp(rsttemp->diskname, curmatch->diskname) &&
168                     !strcmp(rsttemp->timestamp, curmatch->timestamp) &&
169                     rsttemp->level == curmatch->level) {
170                     have_part = 1;
171                 }
172             }
173         }
174         if (have_part)
175             continue;
176
177         for(curtape = needed_tapes; curtape; curtape = curtape->next) {
178             if (!strcmp(curtape->label, curmatch->label)) {
179                 find_result_t *rsttemp = NULL;
180                 find_result_t *rstfile;
181                 int keep = 1;
182
183                 havetape = 1;
184
185                 for(rsttemp = curtape->files;
186                             rsttemp;
187                             rsttemp=rsttemp->next){
188                     if(curmatch->filenum == rsttemp->filenum){
189                         g_fprintf(stderr, _("Seeing multiple entries for tape "
190                                    "%s file %lld, using most recent\n"),
191                                     curtape->label,
192                                     (long long)curmatch->filenum);
193                         keep = 0;
194                     }
195                 }
196                 if(!keep){
197                     break;
198                 }
199
200                 rstfile = alloc(SIZEOF(find_result_t));
201                 memcpy(rstfile, curmatch, SIZEOF(find_result_t));
202                 rstfile->next = curtape->files;
203
204                 if (curmatch->filenum < 1)
205                     curtape->isafile = 1;
206                 else curtape->isafile = 0;
207                 curtape->files = rstfile;
208                 break;
209             }
210         }
211         if (!havetape) {
212             find_result_t *rstfile = alloc(SIZEOF(find_result_t));
213             needed_tape_t *newtape = alloc(SIZEOF(needed_tape_t));
214             memcpy(rstfile, curmatch, SIZEOF(find_result_t));
215             rstfile->next = NULL;
216             newtape->files = rstfile;
217             if(curmatch->filenum < 1) newtape->isafile = 1;
218             else newtape->isafile = 0;
219             newtape->label = curmatch->label;
220             if (needed_tapes){
221                 needed_tapes->prev->next = newtape;
222                 newtape->prev = needed_tapes->prev;
223                 needed_tapes->prev = newtape;
224             } else {
225                 needed_tapes = newtape;
226                 needed_tapes->prev = needed_tapes;
227             }
228             newtape->next = NULL;
229             numtapes++;
230         } /* if(!havetape) */
231
232     } /* for(curmatch = matches ... */
233
234     if(numtapes == 0){
235       g_fprintf(stderr, _("No matching dumps found\n"));
236       exit(1);
237       /* NOTREACHED */
238     }
239
240     /* stick that list in a structure that librestore will understand */
241     for(curtape = needed_tapes; curtape; curtape = curtape->next) {
242         find_result_t *curfind = NULL;
243         for(curfind = curtape->files; curfind; curfind = curfind->next) {
244             tapes = append_to_tapelist(tapes, curtape->label,
245                                        curfind->filenum, -1, curtape->isafile);
246         }
247     }
248
249     g_fprintf(stderr, _("%d tape(s) needed for restoration\n"), numtapes);
250     return(tapes);
251 }
252
253
254 /*
255  * Parses command line, then loops through all files on tape, restoring
256  * files that match the command line criteria.
257  */
258
259 int
260 main(
261     int         argc,
262     char **     argv)
263 {
264     extern int optind;
265     int opt;
266     GSList *dumpspecs = NULL;
267     int fd;
268     tapelist_t *needed_tapes = NULL;
269     char *e;
270     rst_flags_t *rst_flags;
271     int minimum_arguments;
272     config_overwrites_t *cfg_ovr = NULL;
273
274     /*
275      * Configure program for internationalization:
276      *   1) Only set the message locale for now.
277      *   2) Set textdomain for all amanda related programs to "amanda"
278      *      We don't want to be forced to support dozens of message catalogs.
279      */  
280     setlocale(LC_MESSAGES, "C");
281     textdomain("amanda"); 
282
283     for(fd = 3; fd < (int)FD_SETSIZE; fd++) {
284         /*
285          * Make sure nobody spoofs us with a lot of extra open files
286          * that would cause a successful open to get a very high file
287          * descriptor, which in turn might be used as an index into
288          * an array (e.g. an fd_set).
289          */
290         close(fd);
291     }
292
293     set_pname("amfetchdump");
294
295     /* Don't die when child closes pipe */
296     signal(SIGPIPE, SIG_IGN);
297
298     dbopen(DBG_SUBDIR_SERVER);
299
300     erroutput_type = ERR_INTERACTIVE;
301     error_exit_status = 2;
302
303     rst_flags = new_rst_flags();
304     rst_flags->wait_tape_prompt = 1;
305
306     /* handle options */
307     cfg_ovr = new_config_overwrites(argc/2);
308     while( (opt = getopt(argc, argv, "alht:scCpb:nwi:d:O:o:")) != -1) {
309         switch(opt) {
310         case 'b':
311             rst_flags->blocksize = (ssize_t)strtol(optarg, &e, 10);
312             if(*e == 'k' || *e == 'K') {
313                 rst_flags->blocksize *= 1024;
314             } else if(*e == 'm' || *e == 'M') {
315                 rst_flags->blocksize *= 1024 * 1024;
316             } else if(*e != '\0') {
317                 error(_("invalid blocksize value \"%s\""), optarg);
318                 /*NOTREACHED*/
319             }
320             if(rst_flags->blocksize < DISK_BLOCK_BYTES) {
321                 error(_("minimum block size is %dk"), DISK_BLOCK_BYTES / 1024);
322                 /*NOTREACHED*/
323             }
324             break;
325         case 'c': rst_flags->compress = 1; break;
326         case 'O': rst_flags->restore_dir = stralloc(optarg) ; break;
327         case 'd': rst_flags->alt_tapedev = stralloc(optarg) ; break;
328         case 'C':
329             rst_flags->compress = 1;
330             rst_flags->comp_type = COMPRESS_BEST_OPT;
331             break;
332         case 'p': rst_flags->pipe_to_fd = STDOUT_FILENO; break;
333         case 's': rst_flags->fsf = (off_t)0; break;
334         case 'l': rst_flags->leave_comp = 1; break;
335         case 'i': rst_flags->inventory_log = stralloc(optarg); break;
336         case 'n': rst_flags->inline_assemble = 0; break;
337         case 'w': rst_flags->delay_assemble = 1; break;
338         case 'a': rst_flags->wait_tape_prompt = 0; break;
339         case 'h': rst_flags->headers = 1; break;
340         case 'o': add_config_overwrite_opt(cfg_ovr, optarg); break;
341         default:
342             usage();
343             /*NOTREACHED*/
344         }
345     }
346
347     /* Check some flags that affect inventorying */
348     if(rst_flags->inventory_log){
349         if(rst_flags->inline_assemble) rst_flags->delay_assemble = 1;
350         rst_flags->inline_assemble = 0;
351         rst_flags->leave_comp = 1;
352         if(rst_flags->compress){
353             error(_("Cannot force compression when doing inventory/search"));
354             /*NOTREACHED*/
355         }
356         g_fprintf(stderr, _("Doing inventory/search, dumps will not be uncompressed or assembled on-the-fly.\n"));
357     }
358     else{
359         if(rst_flags->delay_assemble){
360             g_fprintf(stderr, _("Using -w, split dumpfiles will *not* be automatically uncompressed.\n"));
361         }
362     }
363
364     /* make sure our options all make sense otherwise */
365     if(check_rst_flags(rst_flags) == -1) {
366         usage();
367         /*NOTREACHED*/
368     }
369
370     if (rst_flags->inventory_log) {
371         minimum_arguments = 1;
372     } else {
373         minimum_arguments = 2;
374     }
375  
376     if(argc - optind < minimum_arguments) {
377         usage();
378         /*NOTREACHED*/
379     }
380
381     config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_FATAL, argv[optind++]);
382     apply_config_overwrites(cfg_ovr);
383
384     check_running_as(RUNNING_AS_DUMPUSER);
385
386     dbrename(config_name, DBG_SUBDIR_SERVER);
387
388     dumpspecs = cmdline_parse_dumpspecs(argc - optind, argv + optind,
389                                         CMDLINE_PARSE_DATESTAMP |
390                                         CMDLINE_PARSE_LEVEL |
391                                         CMDLINE_EMPTY_TO_WILDCARD);
392
393     /*
394      * We've been told explicitly to go and search through the tapes the hard
395      * way.
396      */
397     if(rst_flags->inventory_log){
398         g_fprintf(stderr, _("Beginning tape-by-tape search.\n"));
399         search_tapes(stderr, stdin, rst_flags->alt_tapedev == NULL,
400                      NULL, dumpspecs, rst_flags, NULL);
401         exit(0);
402     }
403
404
405     /* Decide what tapes we'll need */
406     needed_tapes = list_needed_tapes(dumpspecs,
407                                      rst_flags->pipe_to_fd == STDOUT_FILENO);
408
409     parent_pid = getpid();
410     atexit(cleanup);
411     get_lock = lock_logfile(); /* config is loaded, should be ok here */
412     if(get_lock == 0) {
413         error(_("%s exists: amdump or amflush is already running, or you must run amcleanup"), rst_conf_logfile);
414     }
415     search_tapes(NULL, stdin, rst_flags->alt_tapedev == NULL,
416                  needed_tapes, dumpspecs, rst_flags, NULL);
417     cleanup();
418
419     dumpspec_list_free(dumpspecs);
420
421     if(rst_flags->inline_assemble || rst_flags->delay_assemble)
422         flush_open_outputs(1, NULL);
423     else flush_open_outputs(0, NULL);
424
425     free_rst_flags(rst_flags);
426
427     return(0);
428 }
429
430 static void
431 cleanup(void)
432 {
433     if(parent_pid == getpid()) {
434         if(get_lock) unlink(rst_conf_logfile);
435     }
436 }