2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: amfetchdump.c,v 1.16 2006/08/24 01:57:15 paddy_s Exp $
29 * retrieves specific dumps from a set of amanda tapes
33 #include "fileheader.h"
43 #define CREAT_MODE 0640
45 extern char *rst_conf_logfile;
46 extern char *config_dir;
49 typedef struct needed_tapes_s {
53 struct needed_tapes_s *next;
54 struct needed_tapes_s *prev;
59 tapelist_t *list_needed_tapes(GSList *dumpspecs);
61 int main(int argc, char **argv);
64 static pid_t parent_pid = -1;
65 static void cleanup(void);
69 * Print usage message and terminate.
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"));
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
103 needed_tape_t *needed_tapes = NULL, *curtape = NULL;
105 dumpspec_t *ds = NULL;
106 find_result_t *alldumps = NULL;
107 tapelist_t *tapes = NULL;
109 char *conf_diskfile, *conf_tapelist;
111 /* For disks and tape lists */
112 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
113 if(read_diskfile(conf_diskfile, &diskqp) != 0) {
114 error(_("could not load disklist \"%s\""), conf_diskfile);
117 amfree(conf_diskfile);
119 conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
120 if(read_tapelist(conf_tapelist)) {
121 error(_("could not load tapelist \"%s\""), conf_tapelist);
124 amfree(conf_tapelist);
126 /* Grab a find_output_t of all logged dumps */
127 alldumps = find_dump(&diskqp);
128 free_disklist(&diskqp);
129 if(alldumps == NULL){
130 g_fprintf(stderr, _("No dump records found\n"));
134 /* Compare all known dumps to our match list, note what we'll need */
136 find_result_t *curmatch = NULL;
137 find_result_t *matches = NULL;
138 ds = (dumpspec_t *)dumpspecs->data;
140 matches = dumps_match(alldumps, ds->host, ds->disk,
141 ds->datestamp, ds->level, 1);
142 sort_find_result("Dhklp", &matches);
143 for(curmatch = matches; curmatch; curmatch = curmatch->next){
146 if(strcmp("OK", curmatch->status)){
147 g_fprintf(stderr,_("Dump %s %s %s %d had status '%s', skipping\n"),
148 curmatch->timestamp, curmatch->hostname,
149 curmatch->diskname, curmatch->level,
153 /* check if we already have that part */
154 for(curtape = needed_tapes; curtape; curtape = curtape->next) {
155 find_result_t *rsttemp = NULL;
156 for(rsttemp = curtape->files;
158 rsttemp=rsttemp->next) {
159 if (strcmp(rsttemp->partnum, curmatch->partnum) == 0)
165 for(curtape = needed_tapes; curtape; curtape = curtape->next) {
166 if(!strcmp(curtape->label, curmatch->label)){
167 find_result_t *rsttemp = NULL;
168 find_result_t *rstfile = alloc(SIZEOF(find_result_t));
171 memcpy(rstfile, curmatch, SIZEOF(find_result_t));
175 for(rsttemp = curtape->files;
177 rsttemp=rsttemp->next){
178 if(rstfile->filenum == rsttemp->filenum){
179 g_fprintf(stderr, _("Seeing multiple entries for tape "
180 "%s file %lld, using most recent\n"),
182 (long long)rstfile->filenum);
190 rstfile->next = curtape->files;
192 if(curmatch->filenum < 1) curtape->isafile = 1;
193 else curtape->isafile = 0;
194 curtape->files = rstfile;
199 find_result_t *rstfile = alloc(SIZEOF(find_result_t));
200 needed_tape_t *newtape =
201 alloc(SIZEOF(needed_tape_t));
202 memcpy(rstfile, curmatch, SIZEOF(find_result_t));
203 rstfile->next = NULL;
204 newtape->files = rstfile;
205 if(curmatch->filenum < 1) newtape->isafile = 1;
206 else newtape->isafile = 0;
207 newtape->label = curmatch->label;
209 needed_tapes->prev->next = newtape;
210 newtape->prev = needed_tapes->prev;
211 needed_tapes->prev = newtape;
214 needed_tapes = newtape;
215 needed_tapes->prev = needed_tapes;
217 newtape->next = NULL;
220 // free_find_result(rstfile);
222 } /* if(!havetape) */
224 } /* for(curmatch = matches ... */
225 dumpspecs = dumpspecs->next;
226 } /* while (dumpspecs) */
229 g_fprintf(stderr, _("No matching dumps found\n"));
234 /* stick that list in a structure that librestore will understand */
235 for(curtape = needed_tapes; curtape; curtape = curtape->next) {
236 find_result_t *curfind = NULL;
237 for(curfind = curtape->files; curfind; curfind = curfind->next) {
238 tapes = append_to_tapelist(tapes, curtape->label,
239 curfind->filenum, -1, curtape->isafile);
243 g_fprintf(stderr, _("%d tape(s) needed for restoration\n"), numtapes);
249 * Parses command line, then loops through all files on tape, restoring
250 * files that match the command line criteria.
260 GSList *dumpspecs = NULL;
262 tapelist_t *needed_tapes = NULL;
264 rst_flags_t *rst_flags;
265 int minimum_arguments;
266 config_overwrites_t *cfg_ovr = NULL;
269 * Configure program for internationalization:
270 * 1) Only set the message locale for now.
271 * 2) Set textdomain for all amanda related programs to "amanda"
272 * We don't want to be forced to support dozens of message catalogs.
274 setlocale(LC_MESSAGES, "C");
275 textdomain("amanda");
277 for(fd = 3; fd < (int)FD_SETSIZE; fd++) {
279 * Make sure nobody spoofs us with a lot of extra open files
280 * that would cause a successful open to get a very high file
281 * descriptor, which in turn might be used as an index into
282 * an array (e.g. an fd_set).
287 set_pname("amfetchdump");
289 /* Don't die when child closes pipe */
290 signal(SIGPIPE, SIG_IGN);
292 dbopen(DBG_SUBDIR_SERVER);
294 erroutput_type = ERR_INTERACTIVE;
295 error_exit_status = 2;
297 rst_flags = new_rst_flags();
298 rst_flags->wait_tape_prompt = 1;
301 cfg_ovr = new_config_overwrites(argc/2);
302 while( (opt = getopt(argc, argv, "alht:scCpb:nwi:d:O:o:")) != -1) {
305 rst_flags->blocksize = (ssize_t)strtol(optarg, &e, 10);
306 if(*e == 'k' || *e == 'K') {
307 rst_flags->blocksize *= 1024;
308 } else if(*e == 'm' || *e == 'M') {
309 rst_flags->blocksize *= 1024 * 1024;
310 } else if(*e != '\0') {
311 error(_("invalid blocksize value \"%s\""), optarg);
314 if(rst_flags->blocksize < DISK_BLOCK_BYTES) {
315 error(_("minimum block size is %dk"), DISK_BLOCK_BYTES / 1024);
319 case 'c': rst_flags->compress = 1; break;
320 case 'O': rst_flags->restore_dir = stralloc(optarg) ; break;
321 case 'd': rst_flags->alt_tapedev = stralloc(optarg) ; break;
323 rst_flags->compress = 1;
324 rst_flags->comp_type = COMPRESS_BEST_OPT;
326 case 'p': rst_flags->pipe_to_fd = STDOUT_FILENO; break;
327 case 's': rst_flags->fsf = (off_t)0; break;
328 case 'l': rst_flags->leave_comp = 1; break;
329 case 'i': rst_flags->inventory_log = stralloc(optarg); break;
330 case 'n': rst_flags->inline_assemble = 0; break;
331 case 'w': rst_flags->delay_assemble = 1; break;
332 case 'a': rst_flags->wait_tape_prompt = 0; break;
333 case 'h': rst_flags->headers = 1; break;
334 case 'o': add_config_overwrite_opt(cfg_ovr, optarg); break;
341 /* Check some flags that affect inventorying */
342 if(rst_flags->inventory_log){
343 if(rst_flags->inline_assemble) rst_flags->delay_assemble = 1;
344 rst_flags->inline_assemble = 0;
345 rst_flags->leave_comp = 1;
346 if(rst_flags->compress){
347 error(_("Cannot force compression when doing inventory/search"));
350 g_fprintf(stderr, _("Doing inventory/search, dumps will not be uncompressed or assembled on-the-fly.\n"));
353 if(rst_flags->delay_assemble){
354 g_fprintf(stderr, _("Using -w, split dumpfiles will *not* be automatically uncompressed.\n"));
358 /* make sure our options all make sense otherwise */
359 if(check_rst_flags(rst_flags) == -1) {
364 if (rst_flags->inventory_log) {
365 minimum_arguments = 1;
367 minimum_arguments = 2;
370 if(argc - optind < minimum_arguments) {
375 config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_FATAL, argv[optind++]);
376 apply_config_overwrites(cfg_ovr);
378 check_running_as(RUNNING_AS_DUMPUSER);
380 dbrename(config_name, DBG_SUBDIR_SERVER);
382 dumpspecs = cmdline_parse_dumpspecs(argc - optind, argv + optind,
383 CMDLINE_PARSE_DATESTAMP |
384 CMDLINE_PARSE_LEVEL |
385 CMDLINE_EMPTY_TO_WILDCARD);
388 * We've been told explicitly to go and search through the tapes the hard
391 if(rst_flags->inventory_log){
392 g_fprintf(stderr, _("Beginning tape-by-tape search.\n"));
393 search_tapes(stderr, stdin, rst_flags->alt_tapedev == NULL,
394 NULL, dumpspecs, rst_flags, NULL);
399 /* Decide what tapes we'll need */
400 needed_tapes = list_needed_tapes(dumpspecs);
402 parent_pid = getpid();
404 get_lock = lock_logfile(); /* config is loaded, should be ok here */
406 error(_("%s exists: amdump or amflush is already running, or you must run amcleanup"), rst_conf_logfile);
408 search_tapes(NULL, stdin, rst_flags->alt_tapedev == NULL,
409 needed_tapes, dumpspecs, rst_flags, NULL);
412 dumpspec_list_free(dumpspecs);
414 if(rst_flags->inline_assemble || rst_flags->delay_assemble)
415 flush_open_outputs(1, NULL);
416 else flush_open_outputs(0, NULL);
418 free_rst_flags(rst_flags);
426 if(parent_pid == getpid()) {
427 if(get_lock) unlink(rst_conf_logfile);