2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998, 2000 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: amrecover.c,v 1.29.4.7.4.6.2.7.2.1 2005/09/20 21:31:52 jrjackson Exp $
29 * an interactive program for recovering backed-up files
34 #ifdef HAVE_NETINET_IN_SYSTM_H
35 #include <netinet/in_systm.h>
37 #include <netinet/in.h>
38 #ifdef HAVE_NETINET_IP_H
39 #include <netinet/ip.h>
42 #include "amfeatures.h"
43 #include "amrecover.h"
47 #if defined(KRB4_SECURITY)
48 #include "krb4-security.h"
52 #ifdef HAVE_LIBREADLINE
53 # ifdef HAVE_READLINE_READLINE_H
54 # include <readline/readline.h>
55 # ifdef HAVE_READLINE_HISTORY_H
56 # include <readline/history.h>
59 # ifdef HAVE_READLINE_H
60 # include <readline.h>
61 # ifdef HAVE_HISTORY_H
65 # undef HAVE_LIBREADLINE
70 extern int process_line P((char *line));
71 int guess_disk P((char *cwd, size_t cwd_len, char **dn_guess, char **mpt_guess));
73 #define USAGE "Usage: amrecover [[-C] <config>] [-s <index-server>] [-t <tape-server>] [-d <tape-device>]\n"
76 char *server_name = NULL;
78 char *server_line = NULL;
79 char *dump_datestamp = NULL; /* date we are restoring */
80 char *dump_hostname; /* which machine we are restoring */
81 char *disk_name = NULL; /* disk we are restoring */
82 char *mount_point = NULL; /* where disk was mounted */
83 char *disk_path = NULL; /* path relative to mount point */
84 char dump_date[STR_SIZE]; /* date on which we are restoring */
85 int quit_prog; /* set when time to exit parser */
86 char *tape_server_name = NULL;
87 int tape_server_socket;
88 char *tape_device_name = NULL;
89 am_feature_t *our_features = NULL;
90 am_feature_t *their_features = NULL;
93 #ifndef HAVE_LIBREADLINE
95 * simple readline() replacements
103 fflush(stdout); fflush(stderr);
107 #define add_history(x) /* add_history((x)) */
111 /* gets a "line" from server and put in server_line */
112 /* server_line is terminated with \0, \r\n is striped */
113 /* returns -1 if error */
122 if((part = areads(server_socket)) == NULL) {
123 int save_errno = errno;
126 fputs(server_line, stderr); /* show the last line read */
129 if(save_errno != 0) {
130 fprintf(stderr, "%s: Error reading line from server: %s\n",
132 strerror(save_errno));
134 fprintf(stderr, "%s: Unexpected end of file, check amindexd*debug on server %s\n",
144 strappend(line, part);
150 if((len = strlen(line)) > 0 && line[len-1] == '\r') {
152 server_line = newstralloc(server_line, line);
157 * Hmmm. We got a "line" from areads(), which means it saw
158 * a '\n' (or EOF, etc), but there was not a '\r' before it.
159 * Put a '\n' back in the buffer and loop for more.
161 strappend(line, "\n");
166 /* get reply from server and print to screen */
167 /* handle multi-line reply */
168 /* return -1 if error */
169 /* return code returned by server always occupies first 3 bytes of global
170 variable server_line */
171 int grab_reply (show)
175 if (get_line() == -1) {
178 if(show) puts(server_line);
179 } while (server_line[3] == '-');
180 if(show) fflush(stdout);
186 /* get 1 line of reply */
187 /* returns -1 if error, 0 if last (or only) line, 1 if more to follow */
188 int get_reply_line ()
190 if (get_line() == -1)
192 return server_line[3] == '-';
196 /* returns pointer to returned line */
204 /* returns 0 if server returned an error code (ie code starting with 5)
205 and non-zero otherwise */
208 return server_line[0] != '5';
212 int send_command(cmd)
220 * NOTE: this routine is called from sigint_handler, so we must be
221 * **very** careful about what we do since there is no way to know
222 * our state at the time the interrupt happened. For instance,
223 * do not use any stdio routines here.
225 for (l = 0, n = strlen(cmd); l < n; l += s)
226 if ((s = write(server_socket, cmd + l, n - l)) < 0) {
227 perror("amrecover: Error writing to server");
231 for (l = 0, n = strlen(end); l < n; l += s)
232 if ((s = write(server_socket, end + l, n - l)) < 0) {
233 perror("amrecover: Error writing to server");
240 /* send a command to the server, get reply and print to screen */
244 if (send_command(cmd) == -1) return -1;
245 if (grab_reply(1) == -1) return -1;
250 /* same as converse() but reply not echoed to stdout */
254 if (send_command(cmd) == -1) return -1;
255 if (grab_reply(0) == -1) return -1;
260 /* basic interrupt handler for when user presses ^C */
261 /* Bale out, letting server know before doing so */
262 void sigint_handler(signum)
266 * NOTE: we must be **very** careful about what we do here since there
267 * is no way to know our state at the time the interrupt happened.
268 * For instance, do not use any stdio routines here or in any called
269 * routines. Also, use _exit() instead of exit() to make sure stdio
270 * buffer flushing is not attempted.
272 if (extract_restore_child_pid != -1)
273 (void)kill(extract_restore_child_pid, SIGKILL);
274 extract_restore_child_pid = -1;
276 (void)send_command("QUIT");
281 void clean_pathname(s)
287 /* remove "/" at end of path */
288 if(length>1 && s[length-1]=='/')
291 /* change "/." to "/" */
292 if(strcmp(s,"/.")==0)
295 /* remove "/." at end of path */
296 if(strcmp(&(s[length-2]),"/.")==0)
301 /* try and guess the disk the user is currently on.
302 Return -1 if error, 0 if disk not local, 1 if disk local,
303 2 if disk local but can't guess name */
304 /* do this by looking for the longest mount point which matches the
306 int guess_disk (cwd, cwd_len, dn_guess, mpt_guess)
307 char *cwd, **dn_guess, **mpt_guess;
310 size_t longest_match = 0;
311 size_t current_length;
314 generic_fsent_t fsent;
316 char *disk_try = NULL;
318 *dn_guess = *mpt_guess = NULL;
320 if (getcwd(cwd, cwd_len) == NULL)
322 cwd_length = strlen(cwd);
323 dbprintf(("guess_disk: %d: \"%s\"\n", cwd_length, cwd));
325 if (open_fstab() == 0)
328 while (get_fstab_nextentry(&fsent))
330 current_length = fsent.mntdir ? strlen(fsent.mntdir) : (size_t)0;
331 dbprintf(("guess_disk: %d: %d: \"%s\": \"%s\"\n",
334 fsent.mntdir ? fsent.mntdir : "(mntdir null)",
335 fsent.fsname ? fsent.fsname : "(fsname null)"));
336 if ((current_length > longest_match)
337 && (current_length <= cwd_length)
338 && (strncmp(fsent.mntdir, cwd, current_length) == 0))
340 longest_match = current_length;
342 *mpt_guess = stralloc(fsent.mntdir);
343 if(strncmp(fsent.fsname,DEV_PREFIX,(strlen(DEV_PREFIX))))
345 fsname = newstralloc(fsname, fsent.fsname);
349 fsname = newstralloc(fsname,fsent.fsname+strlen(DEV_PREFIX));
351 local_disk = is_local_fstype(&fsent);
352 dbprintf(("guess_disk: local_disk = %d, fsname = \"%s\"\n",
359 if (longest_match == 0) {
362 return -1; /* ? at least / should match */
371 /* have mount point now */
372 /* disk name may be specified by mount point (logical name) or
373 device name, have to determine */
374 printf("Trying disk %s ...\n", *mpt_guess);
375 disk_try = stralloc2("DISK ", *mpt_guess); /* try logical name */
376 if (exchange(disk_try) == -1)
381 *dn_guess = stralloc(*mpt_guess); /* logical is okay */
385 printf("Trying disk %s ...\n", fsname);
386 disk_try = stralloc2("DISK ", fsname); /* try device name */
387 if (exchange(disk_try) == -1)
392 *dn_guess = stralloc(fsname); /* dev name is okay */
397 /* neither is okay */
407 (void)converse("QUIT");
410 char *localhost = NULL;
420 char *lineread = NULL;
421 struct sigaction act, oact;
424 char cwd[STR_SIZE], *dn_guess = NULL, *mpt_guess = NULL;
430 set_pname("amrecover");
433 #ifndef IGNORE_UID_CHECK
434 if (geteuid() != 0) {
435 erroutput_type |= ERR_SYSLOG;
436 error("amrecover must be run by root");
440 localhost = alloc(MAX_HOSTNAME_LENGTH+1);
441 if (gethostname(localhost, MAX_HOSTNAME_LENGTH) != 0) {
442 error("cannot determine local host name\n");
444 localhost[MAX_HOSTNAME_LENGTH] = '\0';
446 config = newstralloc(config, DEFAULT_CONFIG);
447 server_name = newstralloc(server_name, DEFAULT_SERVER);
448 #ifdef DEFAULT_TAPE_SERVER
449 tape_server_name = newstralloc(tape_server_name, DEFAULT_TAPE_SERVER);
451 amfree(tape_server_name);
453 if (argc > 1 && argv[1][0] != '-')
456 * If the first argument is not an option flag, then we assume
457 * it is a configuration name to match the syntax of the other
462 new_argv = (char **) alloc ((argc + 1 + 1) * sizeof (*new_argv));
463 new_argv[0] = argv[0];
465 for (i = 1; i < argc; i++)
467 new_argv[i + 1] = argv[i];
469 new_argv[i + 1] = NULL;
473 while ((i = getopt(argc, argv, "C:s:t:d:U")) != EOF)
478 config = newstralloc(config, optarg);
482 server_name = newstralloc(server_name, optarg);
486 tape_server_name = newstralloc(tape_server_name, optarg);
490 tape_device_name = newstralloc(tape_device_name, optarg);
501 (void)fprintf(stderr, USAGE);
510 /* set up signal handler */
511 act.sa_handler = sigint_handler;
512 sigemptyset(&act.sa_mask);
514 if (sigaction(SIGINT, &act, &oact) != 0)
516 error("error setting signal handler: %s", strerror(errno));
519 service_name = stralloc2("amandaidx", SERVICE_SUFFIX);
521 printf("AMRECOVER Version %s. Contacting server on %s ...\n",
522 version(), server_name);
523 if ((sp = getservbyname(service_name, "tcp")) == NULL)
525 error("%s/tcp unknown protocol", service_name);
527 amfree(service_name);
528 server_socket = stream_client_privileged(server_name,
533 if (server_socket < 0)
535 error("cannot connect to %s: %s", server_name, strerror(errno));
537 if (my_port >= IPPORT_RESERVED)
539 error("did not get a reserved port: %d", my_port);
544 * We may need root privilege again later for a reserved port to
545 * the tape server, so we will drop down now but might have to
552 /* get server's banner */
553 if (grab_reply(1) == -1)
558 aclose(server_socket);
562 /* do the security thing */
563 #if defined(KRB4_SECURITY)
564 #if 0 /* not yet implemented */
567 line = get_krb_security();
572 line = get_bsd_security();
574 if (converse(line) == -1)
578 memset(line, '\0', strlen(line));
581 /* try to get the features form the server */
583 char *our_feature_string = NULL;
584 char *their_feature_string = NULL;
586 our_features = am_init_feature_set();
587 our_feature_string = am_feature_to_string(our_features);
588 line = stralloc2("FEATURES ", our_feature_string);
589 if(exchange(line) == 0) {
590 their_feature_string = stralloc(server_line+13);
591 their_features = am_string_to_feature(their_feature_string);
594 their_features = am_set_default_feature_set();
596 amfree(our_feature_string);
597 amfree(their_feature_string);
601 /* set the date of extraction to be today */
603 strftime(dump_date, sizeof(dump_date), "%Y-%m-%d", localtime(&timer));
604 printf("Setting restore date to today (%s)\n", dump_date);
605 line = stralloc2("DATE ", dump_date);
606 if (converse(line) == -1)
610 line = stralloc2("SCNF ", config);
611 if (converse(line) == -1)
617 /* set host we are restoring to this host by default */
618 amfree(dump_hostname);
622 /* get a starting disk and directory based on where
624 switch (guess_disk(cwd, sizeof(cwd), &dn_guess, &mpt_guess))
627 /* okay, got a guess. Set disk accordingly */
628 printf("$CWD '%s' is on disk '%s' mounted at '%s'.\n",
629 cwd, dn_guess, mpt_guess);
630 set_disk(dn_guess, mpt_guess);
632 if (server_happy() && strcmp(cwd, mpt_guess) != 0)
633 printf("WARNING: not on root of selected filesystem, check man-page!\n");
639 printf("$CWD '%s' is on a network mounted disk\n",
641 printf("so you must 'sethost' to the server\n");
642 /* fake an unhappy server */
643 server_line[0] = '5';
649 printf("Can't determine disk and mount point from $CWD '%s'\n", cwd);
650 /* fake an unhappy server */
651 server_line[0] = '5';
660 if ((lineread = readline("amrecover> ")) == NULL) {
665 if (lineread[0] != '\0')
667 add_history(lineread);
668 process_line(lineread); /* act on line's content */
671 } while (!quit_prog);
675 aclose(server_socket);