PageRenderTime 79ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/amanda/tags/amanda252p1/restore-src/amfetchdump.c

#
C | 533 lines | 382 code | 61 blank | 90 comment | 91 complexity | 4c252e5cc55f84c80b92a8a11bde0463 MD5 | raw file
  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. #include "amanda.h"
  32. #include "tapeio.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. #define CREAT_MODE 0640
  42. extern char *rst_conf_logfile;
  43. extern char *config_dir;
  44. int get_lock = 0;
  45. typedef struct needed_tapes_s {
  46. char *label;
  47. int isafile;
  48. find_result_t *files;
  49. struct needed_tapes_s *next;
  50. struct needed_tapes_s *prev;
  51. } needed_tape_t;
  52. /* local functions */
  53. void errexit(void);
  54. tapelist_t *list_needed_tapes(match_list_t *match_list);
  55. void usage(void);
  56. int main(int argc, char **argv);
  57. /* exit routine */
  58. static pid_t parent_pid = -1;
  59. static void cleanup(void);
  60. /*
  61. * Do exit(2) after an error, rather than exit(1).
  62. */
  63. void
  64. errexit(void)
  65. {
  66. exit(2);
  67. }
  68. /*
  69. * Print usage message and terminate.
  70. */
  71. void
  72. usage(void)
  73. {
  74. fprintf(stderr, "Usage: amfetchdump [options] config hostname [diskname [datestamp [level [hostname [diskname [datestamp [level ... ]]]]]]] [-o configoption]*\n\n");
  75. fprintf(stderr, "Goes and grabs a dump from tape, moving tapes around and assembling parts as\n");
  76. fprintf(stderr, "necessary. Files are restored to the current directory, unless otherwise\nspecified.\n\n");
  77. fprintf(stderr, " -p Pipe exactly *one* complete dumpfile to stdout, instead of to disk.\n");
  78. fprintf(stderr, " -O <output dir> Restore files to this directory.\n");
  79. fprintf(stderr, " -d <device> Force restoration from a particular tape device.\n");
  80. fprintf(stderr, " -c Compress output, fastest method available.\n");
  81. fprintf(stderr, " -C Compress output, best filesize method available.\n");
  82. fprintf(stderr, " -l Leave dumps (un)compressed, whichever way they were originally on tape.\n");
  83. fprintf(stderr, " -a Assume all tapes are available via changer, do not prompt for initial load.\n");
  84. 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");
  85. fprintf(stderr, " -w Wait to put split dumps together until all chunks have been restored.\n");
  86. fprintf(stderr, " -n Do not reassemble split dumpfiles.\n");
  87. fprintf(stderr, " -k Skip the rewind/label read when reading a new tape.\n");
  88. 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");
  89. fprintf(stderr, " -b <blocksize> Force a particular block size (default is 32kb).\n");
  90. exit(1);
  91. }
  92. /*
  93. * Build the list of tapes we'll be wanting, and include data about the
  94. * files we want from said tapes while we're at it (the whole find_result
  95. * should do fine)
  96. */
  97. tapelist_t *
  98. list_needed_tapes(
  99. match_list_t * match_list)
  100. {
  101. needed_tape_t *needed_tapes = NULL, *curtape = NULL;
  102. disklist_t diskqp;
  103. match_list_t *me = NULL;
  104. find_result_t *alldumps = NULL;
  105. tapelist_t *tapes = NULL;
  106. int numtapes = 0;
  107. char *conf_diskfile, *conf_tapelist;
  108. /* For disks and tape lists */
  109. conf_diskfile = getconf_str(CNF_DISKFILE);
  110. conf_tapelist = getconf_str(CNF_TAPELIST);
  111. if (*conf_diskfile == '/') {
  112. conf_diskfile = stralloc(conf_diskfile);
  113. } else {
  114. conf_diskfile = stralloc2(config_dir, conf_diskfile);
  115. }
  116. if(read_diskfile(conf_diskfile, &diskqp) != 0) {
  117. error("could not load disklist \"%s\"", conf_diskfile);
  118. /*NOTREACHED*/
  119. }
  120. if (*conf_tapelist == '/') {
  121. conf_tapelist = stralloc(conf_tapelist);
  122. } else {
  123. conf_tapelist = stralloc2(config_dir, conf_tapelist);
  124. }
  125. if(read_tapelist(conf_tapelist)) {
  126. error("could not load tapelist \"%s\"", conf_tapelist);
  127. /*NOTREACHED*/
  128. }
  129. amfree(conf_diskfile);
  130. amfree(conf_tapelist);
  131. /* Grab a find_output_t of all logged dumps */
  132. alldumps = find_dump(1, &diskqp);
  133. free_disklist(&diskqp);
  134. if(alldumps == NULL){
  135. fprintf(stderr, "No dump records found\n");
  136. exit(1);
  137. }
  138. /* Compare all known dumps to our match list, note what we'll need */
  139. for(me = match_list; me; me = me->next) {
  140. find_result_t *curmatch = NULL;
  141. find_result_t *matches = NULL;
  142. matches = dumps_match(alldumps, me->hostname, me->diskname,
  143. me->datestamp, me->level, 1);
  144. sort_find_result("Dhklp", &matches);
  145. for(curmatch = matches; curmatch; curmatch = curmatch->next){
  146. int havetape = 0;
  147. if(strcmp("OK", curmatch->status)){
  148. fprintf(stderr,"Dump %s %s %s %d had status '%s', skipping\n",
  149. curmatch->timestamp, curmatch->hostname,
  150. curmatch->diskname, curmatch->level,
  151. curmatch->status);
  152. continue;
  153. }
  154. for(curtape = needed_tapes; curtape; curtape = curtape->next) {
  155. if(!strcmp(curtape->label, curmatch->label)){
  156. find_result_t *rsttemp = NULL;
  157. find_result_t *rstfile = alloc(SIZEOF(find_result_t));
  158. int keep = 1;
  159. memcpy(rstfile, curmatch, SIZEOF(find_result_t));
  160. havetape = 1;
  161. for(rsttemp = curtape->files;
  162. rsttemp;
  163. rsttemp=rsttemp->next){
  164. if(rstfile->filenum == rsttemp->filenum){
  165. fprintf(stderr, "Seeing multiple entries for tape "
  166. "%s file " OFF_T_FMT ", using most recent\n",
  167. curtape->label,
  168. (OFF_T_FMT_TYPE)rstfile->filenum);
  169. keep = 0;
  170. }
  171. }
  172. if(!keep){
  173. amfree(rstfile);
  174. break;
  175. }
  176. rstfile->next = curtape->files;
  177. if(curmatch->filenum < 1) curtape->isafile = 1;
  178. else curtape->isafile = 0;
  179. curtape->files = rstfile;
  180. break;
  181. }
  182. }
  183. if(!havetape){
  184. find_result_t *rstfile = alloc(SIZEOF(find_result_t));
  185. needed_tape_t *newtape =
  186. alloc(SIZEOF(needed_tape_t));
  187. memcpy(rstfile, curmatch, SIZEOF(find_result_t));
  188. rstfile->next = NULL;
  189. newtape->files = rstfile;
  190. if(curmatch->filenum < 1) newtape->isafile = 1;
  191. else newtape->isafile = 0;
  192. newtape->label = curmatch->label;
  193. if(needed_tapes){
  194. needed_tapes->prev->next = newtape;
  195. newtape->prev = needed_tapes->prev;
  196. needed_tapes->prev = newtape;
  197. }
  198. else{
  199. needed_tapes = newtape;
  200. needed_tapes->prev = needed_tapes;
  201. }
  202. newtape->next = NULL;
  203. numtapes++;
  204. #if 0
  205. // free_find_result(rstfile);
  206. #endif
  207. } /* if(!havetape) */
  208. } /* for(curmatch = matches ... */
  209. } /* for(me = match_list ... */
  210. if(numtapes == 0){
  211. fprintf(stderr, "No matching dumps found\n");
  212. exit(1);
  213. /* NOTREACHED */
  214. }
  215. /* stick that list in a structure that librestore will understand */
  216. for(curtape = needed_tapes; curtape; curtape = curtape->next) {
  217. find_result_t *curfind = NULL;
  218. for(curfind = curtape->files; curfind; curfind = curfind->next) {
  219. tapes = append_to_tapelist(tapes, curtape->label,
  220. curfind->filenum, curtape->isafile);
  221. }
  222. }
  223. fprintf(stderr, "%d tape(s) needed for restoration\n", numtapes);
  224. return(tapes);
  225. }
  226. /*
  227. * Parses command line, then loops through all files on tape, restoring
  228. * files that match the command line criteria.
  229. */
  230. int
  231. main(
  232. int argc,
  233. char ** argv)
  234. {
  235. extern int optind;
  236. int opt;
  237. char *errstr;
  238. match_list_t *match_list = NULL;
  239. match_list_t *me = NULL;
  240. int fd;
  241. char *config_name = NULL;
  242. char *conffile = NULL;
  243. tapelist_t *needed_tapes = NULL;
  244. char *e;
  245. int arg_state;
  246. rst_flags_t *rst_flags;
  247. #ifdef FORCE_USERID
  248. struct passwd *pwent;
  249. #endif
  250. int new_argc, my_argc;
  251. char **new_argv, **my_argv;
  252. for(fd = 3; fd < (int)FD_SETSIZE; fd++) {
  253. /*
  254. * Make sure nobody spoofs us with a lot of extra open files
  255. * that would cause an open we do to get a very high file
  256. * descriptor, which in turn might be used as an index into
  257. * an array (e.g. an fd_set).
  258. */
  259. close(fd);
  260. }
  261. set_pname("amfetchdump");
  262. dbopen(DBG_SUBDIR_SERVER);
  263. #ifdef FORCE_USERID
  264. /* we'd rather not run as root */
  265. if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
  266. client_uid = pwent->pw_uid;
  267. client_gid = pwent->pw_gid;
  268. endpwent();
  269. }
  270. if(geteuid() == 0) {
  271. if(client_uid == (uid_t) -1) {
  272. error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
  273. /*NOTREACHED*/
  274. }
  275. /*@ignore@*/
  276. initgroups(CLIENT_LOGIN, client_gid);
  277. /*@end@*/
  278. setgid(client_gid);
  279. setuid(client_uid);
  280. }
  281. #endif /* FORCE_USERID */
  282. /* Don't die when child closes pipe */
  283. signal(SIGPIPE, SIG_IGN);
  284. erroutput_type = ERR_INTERACTIVE;
  285. onerror(errexit);
  286. parse_conf(argc, argv, &new_argc, &new_argv);
  287. my_argc = new_argc;
  288. my_argv = new_argv;
  289. if(my_argc <= 1) {
  290. usage();
  291. /*NOTREACHED*/
  292. }
  293. rst_flags = new_rst_flags();
  294. rst_flags->wait_tape_prompt = 1;
  295. /* handle options */
  296. while( (opt = getopt(my_argc, my_argv, "alht:scCpb:nwi:d:O:")) != -1) {
  297. switch(opt) {
  298. case 'b':
  299. rst_flags->blocksize = (ssize_t)strtol(optarg, &e, 10);
  300. if(*e == 'k' || *e == 'K') {
  301. rst_flags->blocksize *= 1024;
  302. } else if(*e == 'm' || *e == 'M') {
  303. rst_flags->blocksize *= 1024 * 1024;
  304. } else if(*e != '\0') {
  305. error("invalid blocksize value \"%s\"", optarg);
  306. /*NOTREACHED*/
  307. }
  308. if(rst_flags->blocksize < DISK_BLOCK_BYTES) {
  309. error("minimum block size is %dk", DISK_BLOCK_BYTES / 1024);
  310. /*NOTREACHED*/
  311. }
  312. break;
  313. case 'c': rst_flags->compress = 1; break;
  314. case 'O': rst_flags->restore_dir = stralloc(optarg) ; break;
  315. case 'd': rst_flags->alt_tapedev = stralloc(optarg) ; break;
  316. case 'C':
  317. rst_flags->compress = 1;
  318. rst_flags->comp_type = COMPRESS_BEST_OPT;
  319. break;
  320. case 'p': rst_flags->pipe_to_fd = fileno(stdout); break;
  321. case 's': rst_flags->fsf = (off_t)0; break;
  322. case 'l': rst_flags->leave_comp = 1; break;
  323. case 'i': rst_flags->inventory_log = stralloc(optarg); break;
  324. case 'n': rst_flags->inline_assemble = 0; break;
  325. case 'w': rst_flags->delay_assemble = 1; break;
  326. case 'a': rst_flags->wait_tape_prompt = 0; break;
  327. case 'h': rst_flags->headers = 1; break;
  328. default:
  329. usage();
  330. /*NOTREACHED*/
  331. }
  332. }
  333. /* Check some flags that affect inventorying */
  334. if(rst_flags->inventory_log){
  335. if(rst_flags->inline_assemble) rst_flags->delay_assemble = 1;
  336. rst_flags->inline_assemble = 0;
  337. rst_flags->leave_comp = 1;
  338. if(rst_flags->compress){
  339. error("Cannot force compression when doing inventory/search");
  340. /*NOTREACHED*/
  341. }
  342. fprintf(stderr, "Doing inventory/search, dumps will not be uncompressed or assembled on-the-fly.\n");
  343. }
  344. else{
  345. if(rst_flags->delay_assemble){
  346. fprintf(stderr, "Using -w, split dumpfiles will *not* be automatically uncompressed.\n");
  347. }
  348. }
  349. /* make sure our options all make sense otherwise */
  350. if(check_rst_flags(rst_flags) == -1) {
  351. usage();
  352. /*NOTREACHED*/
  353. }
  354. config_name = my_argv[optind++];
  355. config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
  356. conffile = stralloc2(config_dir, CONFFILE_NAME);
  357. if (read_conffile(conffile)) {
  358. error("errors processing config file \"%s\"", conffile);
  359. /*NOTREACHED*/
  360. }
  361. amfree(conffile);
  362. dbrename(config_name, DBG_SUBDIR_SERVER);
  363. if((my_argc - optind) < 1 && !rst_flags->inventory_log){
  364. fprintf(stderr, "Not enough arguments\n\n");
  365. usage();
  366. /*NOTREACHED*/
  367. }
  368. #define ARG_GET_HOST 0
  369. #define ARG_GET_DISK 1
  370. #define ARG_GET_DATE 2
  371. #define ARG_GET_LEVL 3
  372. arg_state = ARG_GET_HOST;
  373. while(optind < my_argc) {
  374. switch(arg_state) {
  375. case ARG_GET_HOST:
  376. /*
  377. * New host/disk/date/level set, so allocate a match_list.
  378. */
  379. me = alloc(SIZEOF(*me));
  380. me->hostname = my_argv[optind++];
  381. me->diskname = "";
  382. me->datestamp = "";
  383. me->level = "";
  384. me->next = match_list;
  385. match_list = me;
  386. if(me->hostname[0] != '\0'
  387. && (errstr=validate_regexp(me->hostname)) != NULL) {
  388. fprintf(stderr, "%s: bad hostname regex \"%s\": %s\n",
  389. get_pname(), me->hostname, errstr);
  390. usage();
  391. /*NOTREACHED*/
  392. }
  393. arg_state = ARG_GET_DISK;
  394. break;
  395. case ARG_GET_DISK:
  396. me->diskname = my_argv[optind++];
  397. if(me->diskname[0] != '\0'
  398. && (errstr=validate_regexp(me->diskname)) != NULL) {
  399. fprintf(stderr, "%s: bad diskname regex \"%s\": %s\n",
  400. get_pname(), me->diskname, errstr);
  401. usage();
  402. /*NOTREACHED*/
  403. }
  404. arg_state = ARG_GET_DATE;
  405. break;
  406. case ARG_GET_DATE:
  407. me->datestamp = my_argv[optind++];
  408. if(me->datestamp[0] != '\0'
  409. && (errstr=validate_regexp(me->datestamp)) != NULL) {
  410. fprintf(stderr, "%s: bad datestamp regex \"%s\": %s\n",
  411. get_pname(), me->datestamp, errstr);
  412. usage();
  413. /*NOTREACHED*/
  414. }
  415. arg_state = ARG_GET_LEVL;
  416. break;
  417. case ARG_GET_LEVL:
  418. me->level = my_argv[optind++];
  419. if(me->level[0] != '\0'
  420. && (errstr=validate_regexp(me->level)) != NULL) {
  421. fprintf(stderr, "%s: bad level regex \"%s\": %s\n",
  422. get_pname(), me->level, errstr);
  423. usage();
  424. /*NOTREACHED*/
  425. }
  426. arg_state = ARG_GET_HOST;
  427. break;
  428. }
  429. }
  430. /* XXX I don't think this can happen */
  431. if(match_list == NULL && !rst_flags->inventory_log) {
  432. match_list = alloc(SIZEOF(*match_list));
  433. match_list->hostname = "";
  434. match_list->diskname = "";
  435. match_list->datestamp = "";
  436. match_list->level = "";
  437. match_list->next = NULL;
  438. }
  439. /*
  440. * We've been told explicitly to go and search through the tapes the hard
  441. * way.
  442. */
  443. if(rst_flags->inventory_log){
  444. fprintf(stderr, "Beginning tape-by-tape search.\n");
  445. search_tapes(stderr, stdin, 1, NULL, match_list, rst_flags, NULL);
  446. exit(0);
  447. }
  448. /* Decide what tapes we'll need */
  449. needed_tapes = list_needed_tapes(match_list);
  450. parent_pid = getpid();
  451. atexit(cleanup);
  452. get_lock = lock_logfile(); /* config is loaded, should be ok here */
  453. if(get_lock == 0) {
  454. error("%s exists: amdump or amflush is already running, or you must run amcleanup", rst_conf_logfile);
  455. }
  456. search_tapes(NULL, stdin, 1, needed_tapes, match_list, rst_flags, NULL);
  457. cleanup();
  458. free_match_list(match_list);
  459. if(rst_flags->inline_assemble || rst_flags->delay_assemble)
  460. flush_open_outputs(1, NULL);
  461. else flush_open_outputs(0, NULL);
  462. free_rst_flags(rst_flags);
  463. free_new_argv(new_argc, new_argv);
  464. return(0);
  465. }
  466. static void
  467. cleanup(void)
  468. {
  469. if(parent_pid == getpid()) {
  470. if(get_lock) unlink(rst_conf_logfile);
  471. }
  472. }