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.
26 /* $Id: amidxtaped.c,v 1.73 2006/07/25 19:06:46 martinea Exp $
28 * This daemon extracts a dump image off a tape for amrecover and
29 * returns it over the network. It basically, reads a number of
30 * arguments from stdin (it is invoked via inet), one per line,
31 * preceeded by the number of them, and forms them into an argv
32 * structure, then execs amrestore
44 #include "amfeatures.h"
48 #define amidxtaped_debug(i,x) do { \
49 if ((i) <= debug_amidxtaped) { \
56 static char *pgm = "amidxtaped"; /* in case argv[0] is not set */
58 extern char *rst_conf_logfile;
59 extern char *config_dir;
61 static int get_lock = 0;
62 static int from_amandad;
64 static am_feature_t *our_features = NULL;
65 static am_feature_t *their_features = NULL;
66 static g_option_t *g_options = NULL;
67 static int ctlfdin, ctlfdout, datafdout;
68 static char *amandad_auth = NULL;
69 static FILE *cmdin, *cmdout;
71 static char *get_client_line(FILE *in);
72 static void check_security_buffer(char *);
73 static char *get_client_line_fd(int);
76 static pid_t parent_pid = -1;
77 static void cleanup(void);
79 int main(int argc, char **argv);
81 /* get a line from client - line terminated by \r\n */
83 get_client_line(FILE *in)
85 static char *line = NULL;
91 if((part = agets(in)) == NULL) {
93 dbprintf(("%s: read error: %s\n",
94 debug_prefix_time(NULL), strerror(errno)));
96 dbprintf(("%s: EOF reached\n", debug_prefix_time(NULL)));
99 dbprintf(("%s: unprocessed input:\n", debug_prefix_time(NULL)));
100 dbprintf(("-----\n"));
101 dbprintf(("%s\n", line));
102 dbprintf(("-----\n"));
111 strappend(line, part);
117 if((len = strlen(line)) > 0 && line[len-1] == '\r') {
118 line[len-1] = '\0'; /* zap the '\r' */
122 * Hmmm. We got a "line" from agets(), which means it saw
123 * a '\n' (or EOF, etc), but there was not a '\r' before it.
124 * Put a '\n' back in the buffer and loop for more.
126 strappend(line, "\n");
128 dbprintf(("%s: > %s\n", debug_prefix_time(NULL), line));
132 /* get a line from client - line terminated by \r\n */
137 static char *line = NULL;
138 static size_t line_size = 0;
144 if(line == NULL) { /* first time only, allocate initial buffer */
145 s = line = alloc(128);
149 nb = read(fd, &c, 1);
152 if ((nb <= 0) && ((errno == EINTR) || (errno == EAGAIN))) {
153 /* Keep looping if failure is temporary */
156 dbprintf(("%s: Control pipe read error - %s\n",
157 pgm, strerror(errno)));
161 if(len >= line_size-1) { /* increase buffer size */
163 line = realloc(line, line_size);
165 error("Memory reallocation failure");
172 if(len > 0 && *(s-1) == '\r') { /* remove '\r' */
188 check_security_buffer(
192 struct sockaddr_in addr;
196 dbprintf(("%s: check_security_buffer(buffer='%s')\n",
197 debug_prefix_time(NULL), buffer));
200 if (getpeername(0, (struct sockaddr *)&addr, &i) == -1) {
201 error("getpeername: %s", strerror(errno));
204 if ((addr.sin_family != (sa_family_t)AF_INET)
205 || (ntohs(addr.sin_port) == 20)) {
206 error("connection rejected from %s family %d port %d",
207 inet_ntoa(addr.sin_addr), addr.sin_family, htons(addr.sin_port));
211 /* do the security thing */
215 skip_whitespace(s, ch);
217 error("cannot parse SECURITY line");
221 skip_non_whitespace(s, ch);
223 if (strcmp(fp, "SECURITY") != 0) {
224 error("cannot parse SECURITY line");
227 skip_whitespace(s, ch);
228 if (!check_security((struct sockaddr_storage *)&addr, s-1, 0, &errstr)) {
229 error("security check failed: %s", errstr);
241 in_port_t data_port = (in_port_t)-1;
243 struct sockaddr_in addr;
244 match_list_t *match_list;
245 tapelist_t *tapes = NULL;
246 char *their_feature_string = NULL;
247 rst_flags_t *rst_flags;
250 char *re_config = NULL;
259 safe_fd(DATA_FD_OFFSET, 4);
262 /* Don't die when child closes pipe */
263 signal(SIGPIPE, SIG_IGN);
265 rst_flags = new_rst_flags();
266 rst_flags->mask_splits = 1; /* for older clients */
267 rst_flags->amidxtaped = 1;
268 our_features = am_init_feature_set();
269 their_features = am_set_default_feature_set();
272 * When called via inetd, it is not uncommon to forget to put the
273 * argv[0] value on the config line. On some systems (e.g. Solaris)
274 * this causes argv and/or argv[0] to be NULL, so we have to be
275 * careful getting our name.
277 if (argc >= 1 && argv != NULL && argv[0] != NULL) {
278 if((pgm = strrchr(argv[0], '/')) != NULL) {
287 if(argv && argv[1] && strcmp(argv[1], "amandad") == 0) {
290 amandad_auth = argv[2];
299 /* we'd rather not run as root */
302 if(client_uid == (uid_t) -1) {
303 error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
308 initgroups(CLIENT_LOGIN, client_gid);
314 #endif /* FORCE_USERID */
317 /* close stderr first so that debug file becomes it - amrestore
318 chats to stderr, which we don't want going to client */
319 /* if no debug file, ship to bit bucket */
320 (void)close(STDERR_FILENO);
321 dbopen(DBG_SUBDIR_SERVER);
323 dbprintf(("%s: version %s\n", pgm, version()));
325 if(dbfd() != -1 && dbfd() != STDERR_FILENO)
327 if(dup2(dbfd(),STDERR_FILENO) != STDERR_FILENO)
329 perror("amidxtaped can't redirect stderr to the debug file");
330 dbprintf(("%s: can't redirect stderr to the debug file\n",
331 debug_prefix_time(NULL)));
336 if ((i = open("/dev/null", O_WRONLY)) == -1 ||
337 (i != STDERR_FILENO &&
338 (dup2(i, STDERR_FILENO) != STDERR_FILENO ||
340 perror("amidxtaped can't redirect stderr");
345 if (! (argc >= 1 && argv != NULL && argv[0] != NULL)) {
346 dbprintf(("%s: WARNING: argv[0] not defined: check inetd.conf\n",
347 debug_prefix_time(NULL)));
350 if(from_amandad == 0) {
351 socklen = SIZEOF(addr);
352 if (getpeername(0, (struct sockaddr *)&addr, &socklen) == -1) {
353 error("getpeername: %s", strerror(errno));
356 if ((addr.sin_family != (sa_family_t)AF_INET)
357 || (ntohs(addr.sin_port) == 20)) {
358 error("connection rejected from %s family %d port %d",
359 inet_ntoa(addr.sin_addr), addr.sin_family,
360 htons(addr.sin_port));
364 /* do the security thing */
369 buf = stralloc(get_client_line(cmdin));
370 check_security_buffer(buf);
373 ctlfdout = DATA_FD_OFFSET + 0;
374 ctlfdin = DATA_FD_OFFSET + 1;
375 datafdout = DATA_FD_OFFSET + 2;
376 close(DATA_FD_OFFSET +3);
378 /* read the REQ packet */
379 for(; (line = agets(stdin)) != NULL; free(line)) {
380 if(strncmp_const(line, "OPTIONS ") == 0) {
381 g_options = parse_g_options(line+8, 1);
382 if(!g_options->hostname) {
383 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
384 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
385 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
391 if(amandad_auth && g_options->auth) {
392 if(strcasecmp(amandad_auth, g_options->auth) != 0) {
393 printf("ERROR recover program ask for auth=%s while amidxtaped is configured for '%s'\n",
394 g_options->auth, amandad_auth);
395 error("ERROR recover program ask for auth=%s while amidxtaped is configured for '%s'",
396 g_options->auth, amandad_auth);
400 /* send the REP packet */
401 printf("CONNECT CTL %d DATA %d\n", DATA_FD_OFFSET, DATA_FD_OFFSET+1);
406 cmdout = fdopen(ctlfdout, "a");
408 error("amidxtaped: Can't fdopen(ctlfdout): %s", strerror(errno));
411 cmdin = fdopen(ctlfdin, "r");
413 error("amidxtaped: Can't fdopen(ctlfdin): %s", strerror(errno));
418 /* get the number of arguments */
419 match_list = alloc(SIZEOF(match_list_t));
420 match_list->next = NULL;
421 match_list->hostname = "";
422 match_list->datestamp = "";
423 match_list->level = "";
424 match_list->diskname = "";
426 for (re_end = 0; re_end == 0; ) {
429 buf = stralloc(get_client_line(cmdin));
431 if(strncmp_const_skip(buf, "LABEL=", s, ch) == 0) {
432 tapes = unmarshal_tapelist_str(s);
434 else if(strncmp_const_skip(buf, "FSF=", s, ch) == 0) {
435 rst_flags->fsf = OFF_T_ATOI(s);
437 else if(strncmp_const_skip(buf, "HEADER", s, ch) == 0) {
438 rst_flags->headers = 1;
440 else if(strncmp_const_skip(buf, "FEATURES=", s, ch) == 0) {
441 char *our_feature_string = NULL;
442 their_feature_string = stralloc(s);
443 am_release_feature_set(their_features);
444 their_features = am_string_to_feature(their_feature_string);
445 amfree(their_feature_string);
446 our_feature_string = am_feature_to_string(our_features);
447 if(from_amandad == 1)
448 fprintf(cmdout,"FEATURES=%s\r\n", our_feature_string);
450 fprintf(cmdout,"%s", our_feature_string);
452 amfree(our_feature_string);
454 else if(strncmp_const_skip(buf, "DEVICE=", s, ch) == 0) {
455 rst_flags->alt_tapedev= stralloc(s);
457 else if(strncmp_const_skip(buf, "HOST=", s, ch) == 0) {
458 match_list->hostname = stralloc(s);
460 else if(strncmp_const_skip(buf, "DISK=", s, ch) == 0) {
461 match_list->diskname = stralloc(s);
463 else if(strncmp_const_skip(buf, "DATESTAMP=", s, ch) == 0) {
464 match_list->datestamp = stralloc(s);
466 else if(strncmp_const(buf, "END") == 0) {
469 else if(strncmp_const_skip(buf, "CONFIG=", s, ch) == 0) {
470 re_config = stralloc(s);
472 else if(buf[0] != '\0' && buf[0] >= '0' && buf[0] <= '9') {
473 /* XXX does nothing? amrestore_nargs = atoi(buf); */
479 if(!tapes && rst_flags->alt_tapedev){
480 dbprintf(("%s: Looks like we're restoring from a holding file...\n", debug_prefix_time(NULL)));
481 tapes = unmarshal_tapelist_str(rst_flags->alt_tapedev);
483 amfree(rst_flags->alt_tapedev);
484 rst_flags->alt_tapedev = NULL;
489 config_dir = vstralloc(CONFIG_DIR, "/", re_config, "/", NULL);
490 conffile = stralloc2(config_dir, CONFFILE_NAME);
491 if (read_conffile(conffile)) {
492 dbprintf(("%s: config '%s' not found\n",
493 debug_prefix_time(NULL), re_config));
499 dbrename(config_name, DBG_SUBDIR_SERVER);
503 (!rst_flags->alt_tapedev ||
504 (re_config && ( strcmp(rst_flags->alt_tapedev,
505 getconf_str(CNF_AMRECOVER_CHANGER)) == 0 ||
506 strcmp(rst_flags->alt_tapedev,
507 getconf_str(CNF_TPCHANGER)) == 0 ) ) ) ) {
508 /* We need certain options, if restoring from more than one tape */
509 if(tapes->next && !am_has_feature(their_features, fe_recover_splits)) {
510 error("%s: Client must support split dumps to restore requested data.", get_pname());
513 dbprintf(("%s: Restoring from changer, checking labels\n", get_pname()));
514 rst_flags->check_labels = 1;
518 tapedev = getconf_str(CNF_TAPEDEV);
519 /* If we'll be stepping on the tape server's devices, lock them. */
521 (use_changer || (rst_flags->alt_tapedev && tapedev &&
522 strcmp(rst_flags->alt_tapedev, tapedev) == 0) ) ) {
523 dbprintf(("%s: Locking devices\n", get_pname()));
524 parent_pid = getpid();
526 get_lock = lock_logfile();
529 /* Init the tape changer */
530 if(tapes && use_changer && changer_init() == 0) {
531 dbprintf(("%s: No changer available\n", debug_prefix_time(NULL)));
534 /* Read the default block size from the tape type */
535 if(re_config && (conf_tapetype = getconf_str(CNF_TAPETYPE)) != NULL) {
536 tape = lookup_tapetype(conf_tapetype);
537 rst_flags->blocksize = tapetype_get_blocksize(tape) * 1024;
540 if(rst_flags->fsf && re_config &&
541 getconf_boolean(CNF_AMRECOVER_DO_FSF) == 0) {
542 rst_flags->fsf = (off_t)0;
545 if (!use_changer && re_config &&
546 getconf_boolean(CNF_AMRECOVER_CHECK_LABEL) == 0) {
547 rst_flags->check_labels = 0;
550 /* establish a distinct data connection for dumpfile data */
551 if(am_has_feature(their_features, fe_recover_splits)) {
552 if(from_amandad == 1) {
553 rst_flags->pipe_to_fd = datafdout;
559 dbprintf(("%s: Client understands split dumpfiles\n",get_pname()));
561 if((data_sock = stream_server(&data_port, STREAM_BUFSIZE,
562 STREAM_BUFSIZE, 0)) < 0){
563 error("%s: could not create data socket: %s", get_pname(),
567 dbprintf(("%s: Local port %d set aside for data\n", get_pname(), data_port));
569 /* tell client where to connect */
570 printf("CONNECT %hu\n", (unsigned short)data_port);
573 if((data_fd = stream_accept(data_sock, TIMEOUT, STREAM_BUFSIZE,
574 STREAM_BUFSIZE)) < 0){
575 error("stream_accept failed for client data connection: %s\n",
580 buf = get_client_line_fd(data_fd);
582 check_security_buffer(buf);
583 rst_flags->pipe_to_fd = data_fd;
587 rst_flags->pipe_to_fd = fileno(stdout);
590 dbprintf(("%s: Sending output to file descriptor %d\n",
591 get_pname(), rst_flags->pipe_to_fd));
594 tapedev = getconf_str(CNF_TAPEDEV);
597 (use_changer || (rst_flags->alt_tapedev && tapedev &&
598 strcmp(rst_flags->alt_tapedev, tapedev) == 0) ) ) {
599 send_message(cmdout, rst_flags, their_features,
600 "%s exists: amdump or amflush is already running, "
601 "or you must run amcleanup",
603 error("%s exists: amdump or amflush is already running, "
604 "or you must run amcleanup",
608 /* make sure our restore flags aren't crazy */
609 if (check_rst_flags(rst_flags) == -1) {
610 if (rst_flags->pipe_to_fd != -1)
611 aclose(rst_flags->pipe_to_fd);
612 send_message(cmdout, rst_flags, their_features,
613 "restore flags are crazy");
617 /* actual restoration */
618 search_tapes(cmdout, cmdin, use_changer, tapes, match_list, rst_flags,
620 dbprintf(("%s: Restoration finished\n", debug_prefix_time(NULL)));
623 if(rst_flags->pipe_to_fd != -1) aclose(rst_flags->pipe_to_fd);
624 free_tapelist(tapes);
626 am_release_feature_set(their_features);
628 amfree(rst_flags->alt_tapedev);
630 amfree(match_list->hostname);
631 amfree(match_list->diskname);
632 amfree(match_list->datestamp);
643 if(parent_pid == getpid()) {
644 if(get_lock) unlink(rst_conf_logfile);