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"
47 #include "server_util.h"
49 #define amidxtaped_debug(i,x) do { \
50 if ((i) <= debug_amidxtaped) { \
57 static char *pgm = "amidxtaped"; /* in case argv[0] is not set */
59 extern char *rst_conf_logfile;
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(_("read error: %s\n"), strerror(errno));
95 dbprintf(_("EOF reached\n"));
98 dbprintf(_("s: unprocessed input:\n"));
100 dbprintf("%s\n", line);
110 strappend(line, part);
116 if((len = strlen(line)) > 0 && line[len-1] == '\r') {
117 line[len-1] = '\0'; /* zap the '\r' */
121 * Hmmm. We got a "line" from agets(), which means it saw
122 * a '\n' (or EOF, etc), but there was not a '\r' before it.
123 * Put a '\n' back in the buffer and loop for more.
125 strappend(line, "\n");
127 dbprintf("> %s\n", line);
131 /* get a line from client - line terminated by \r\n */
136 static char *line = NULL;
137 static size_t line_size = 0;
143 if(line == NULL) { /* first time only, allocate initial buffer */
144 s = line = alloc(128);
148 nb = read(fd, &c, 1);
151 if ((nb <= 0) && ((errno == EINTR) || (errno == EAGAIN))) {
152 /* Keep looping if failure is temporary */
155 dbprintf(_("%s: Control pipe read error - %s\n"),
156 pgm, strerror(errno));
160 if(len >= line_size-1) { /* increase buffer size */
162 line = realloc(line, line_size);
164 error(_("Memory reallocation failure"));
171 if(len > 0 && *(s-1) == '\r') { /* remove '\r' */
187 check_security_buffer(
191 struct sockaddr_in addr;
195 dbprintf(_("check_security_buffer(buffer='%s')\n"), buffer);
198 if (getpeername(0, (struct sockaddr *)&addr, &i) == -1) {
199 error(_("getpeername: %s"), strerror(errno));
202 if ((addr.sin_family != (sa_family_t)AF_INET)
203 || (ntohs(addr.sin_port) == 20)) {
204 error(_("connection rejected from %s family %d port %d"),
205 inet_ntoa(addr.sin_addr), addr.sin_family, htons(addr.sin_port));
209 /* do the security thing */
213 skip_whitespace(s, ch);
215 error(_("cannot parse SECURITY line"));
219 skip_non_whitespace(s, ch);
221 if (strcmp(fp, "SECURITY") != 0) {
222 error(_("cannot parse SECURITY line"));
225 skip_whitespace(s, ch);
226 if (!check_security((sockaddr_union *)&addr, s-1, 0, &errstr)) {
227 error(_("security check failed: %s"), errstr);
239 in_port_t data_port = (in_port_t)-1;
240 socklen_t_equiv socklen;
241 struct sockaddr_in addr;
243 tapelist_t *tapes = NULL;
244 char *their_feature_string = NULL;
245 rst_flags_t *rst_flags;
248 char *re_config = NULL;
256 * Configure program for internationalization:
257 * 1) Only set the message locale for now.
258 * 2) Set textdomain for all amanda related programs to "amanda"
259 * We don't want to be forced to support dozens of message catalogs.
261 setlocale(LC_MESSAGES, "C");
262 textdomain("amanda");
264 safe_fd(DATA_FD_OFFSET, 4);
267 /* Don't die when child closes pipe */
268 signal(SIGPIPE, SIG_IGN);
270 rst_flags = new_rst_flags();
271 rst_flags->mask_splits = 1; /* for older clients */
272 rst_flags->amidxtaped = 1;
273 our_features = am_init_feature_set();
274 their_features = am_set_default_feature_set();
277 * When called via inetd, it is not uncommon to forget to put the
278 * argv[0] value on the config line. On some systems (e.g. Solaris)
279 * this causes argv and/or argv[0] to be NULL, so we have to be
280 * careful getting our name.
282 if (argc >= 1 && argv != NULL && argv[0] != NULL) {
283 if((pgm = strrchr(argv[0], '/')) != NULL) {
292 if(argv && argv[1] && strcmp(argv[1], "amandad") == 0) {
295 amandad_auth = argv[2];
303 /* close stderr first so that debug file becomes it - amrestore
304 chats to stderr, which we don't want going to client */
305 /* if no debug file, ship to bit bucket */
306 (void)close(STDERR_FILENO);
307 dbopen(DBG_SUBDIR_SERVER);
309 dbprintf(_("%s: version %s\n"), pgm, version());
310 debug_dup_stderr_to_debug();
312 if (! (argc >= 1 && argv != NULL && argv[0] != NULL)) {
313 dbprintf(_("WARNING: argv[0] not defined: check inetd.conf\n"));
316 if(from_amandad == 0) {
317 socklen = SIZEOF(addr);
318 if (getpeername(0, (struct sockaddr *)&addr, &socklen) == -1) {
319 error(_("getpeername: %s"), strerror(errno));
322 if ((addr.sin_family != (sa_family_t)AF_INET)
323 || (ntohs(addr.sin_port) == 20)) {
324 error(_("connection rejected from %s family %d port %d"),
325 inet_ntoa(addr.sin_addr), addr.sin_family,
326 htons(addr.sin_port));
330 /* do the security thing */
335 buf = stralloc(get_client_line(cmdin));
336 check_security_buffer(buf);
339 ctlfdout = DATA_FD_OFFSET + 0;
340 ctlfdin = DATA_FD_OFFSET + 1;
341 datafdout = DATA_FD_OFFSET + 2;
342 close(DATA_FD_OFFSET +3);
344 /* read the REQ packet */
345 for(; (line = agets(stdin)) != NULL; free(line)) {
346 if(strncmp_const(line, "OPTIONS ") == 0) {
348 error(_("ERROR recover program sent multiple OPTIONS"));
349 g_options = parse_g_options(line+8, 1);
350 if(!g_options->hostname) {
351 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
352 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
353 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
359 if(amandad_auth && g_options->auth) {
360 if(strcasecmp(amandad_auth, g_options->auth) != 0) {
361 g_printf(_("ERROR recover program ask for auth=%s while amidxtaped is configured for '%s'\n"),
362 g_options->auth, amandad_auth);
363 error(_("ERROR recover program ask for auth=%s while amidxtaped is configured for '%s'"),
364 g_options->auth, amandad_auth);
368 /* send the REP packet */
369 g_printf("CONNECT CTL %d DATA %d\n", DATA_FD_OFFSET, DATA_FD_OFFSET+1);
374 cmdout = fdopen(ctlfdout, "a");
376 error(_("amidxtaped: Can't fdopen(ctlfdout): %s"), strerror(errno));
379 cmdin = fdopen(ctlfdin, "r");
381 error(_("amidxtaped: Can't fdopen(ctlfdin): %s"), strerror(errno));
386 ds = dumpspec_new(NULL, NULL, NULL, NULL);
387 for (re_end = 0; re_end == 0; ) {
390 buf = stralloc(get_client_line(cmdin));
392 if(strncmp_const_skip(buf, "LABEL=", s, ch) == 0) {
393 tapes = unmarshal_tapelist_str(s);
395 else if(strncmp_const_skip(buf, "FSF=", s, ch) == 0) {
396 rst_flags->fsf = OFF_T_ATOI(s);
398 else if(strncmp_const_skip(buf, "HEADER", s, ch) == 0) {
399 rst_flags->headers = 1;
401 else if(strncmp_const_skip(buf, "FEATURES=", s, ch) == 0) {
402 char *our_feature_string = NULL;
403 their_feature_string = stralloc(s);
404 am_release_feature_set(their_features);
405 their_features = am_string_to_feature(their_feature_string);
406 amfree(their_feature_string);
407 our_feature_string = am_feature_to_string(our_features);
408 if(from_amandad == 1)
409 g_fprintf(cmdout,"FEATURES=%s\r\n", our_feature_string);
411 g_fprintf(cmdout,"%s", our_feature_string);
413 amfree(our_feature_string);
415 else if(strncmp_const_skip(buf, "DEVICE=", s, ch) == 0) {
416 rst_flags->alt_tapedev= stralloc(s);
418 else if(strncmp_const_skip(buf, "HOST=", s, ch) == 0) {
420 dbprintf(_("WARNING: HOST appeared twice in client request.\n"));
423 ds->host = stralloc(s);
425 else if(strncmp_const_skip(buf, "DISK=", s, ch) == 0) {
427 dbprintf(_("WARNING: DISK appeared twice in client request.\n"));
430 ds->disk = stralloc(s);
432 else if(strncmp_const_skip(buf, "DATESTAMP=", s, ch) == 0) {
434 dbprintf(_("WARNING: DATESTAMP appeared twice in client request.\n"));
435 amfree(ds->datestamp);
437 ds->datestamp = stralloc(s);
439 else if(strncmp_const(buf, "END") == 0) {
442 else if(strncmp_const_skip(buf, "CONFIG=", s, ch) == 0) {
443 re_config = stralloc(s);
444 if(strlen(re_config) == 0)
447 else if(buf[0] != '\0' && buf[0] >= '0' && buf[0] <= '9') {
454 config_init(CONFIG_INIT_EXPLICIT_NAME, re_config);
455 dbrename(re_config, DBG_SUBDIR_SERVER);
457 config_init(0, NULL);
460 if (config_errors(NULL) >= CFGERR_ERRORS) {
461 g_critical(_("errors processing config file"));
464 check_running_as(RUNNING_AS_DUMPUSER_PREFERRED);
467 (!rst_flags->alt_tapedev ||
468 (re_config && ( strcmp(rst_flags->alt_tapedev,
469 getconf_str(CNF_AMRECOVER_CHANGER)) == 0 ||
470 strcmp(rst_flags->alt_tapedev,
471 getconf_str(CNF_TPCHANGER)) == 0 ) ) ) ) {
472 /* We need certain options, if restoring from more than one tape */
473 if(tapes->next && !am_has_feature(their_features, fe_recover_splits)) {
474 error(_("Client must support split dumps to restore requested data."));
477 dbprintf(_("Restoring from changer, checking labels\n"));
478 rst_flags->check_labels = 1;
482 /* build the dumpspec list from our single dumpspec */
483 dumpspecs = g_slist_append(NULL, (gpointer)ds);
486 if(!tapes && rst_flags->alt_tapedev){
487 dbprintf(_("Looks like we're restoring from a holding file...\n"));
488 tapes = unmarshal_tapelist_str(rst_flags->alt_tapedev);
490 amfree(rst_flags->alt_tapedev);
491 rst_flags->alt_tapedev = NULL;
495 tapedev = getconf_str(CNF_TAPEDEV);
496 /* If we'll be stepping on the tape server's devices, lock them. */
498 (use_changer || (rst_flags->alt_tapedev && tapedev &&
499 strcmp(rst_flags->alt_tapedev, tapedev) == 0) ) ) {
500 dbprintf(_("Locking devices\n"));
501 parent_pid = getpid();
503 get_lock = lock_logfile();
506 log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
508 /* Init the tape changer */
509 if(tapes && use_changer && changer_init() == 0) {
510 dbprintf(_("No changer available\n"));
513 /* Read the default block size from the tape type */
514 if(re_config && (conf_tapetype = getconf_str(CNF_TAPETYPE)) != NULL) {
515 tape = lookup_tapetype(conf_tapetype);
516 rst_flags->blocksize = tapetype_get_blocksize(tape) * 1024;
519 if(rst_flags->fsf && re_config &&
520 getconf_boolean(CNF_AMRECOVER_DO_FSF) == 0) {
521 rst_flags->fsf = (off_t)0;
524 if (!use_changer && re_config &&
525 getconf_boolean(CNF_AMRECOVER_CHECK_LABEL) == 0) {
526 rst_flags->check_labels = 0;
529 /* establish a distinct data connection for dumpfile data */
530 if(am_has_feature(their_features, fe_recover_splits)) {
531 if(from_amandad == 1) {
532 rst_flags->pipe_to_fd = datafdout;
538 dbprintf(_("Client understands split dumpfiles\n"));
540 if((data_sock = stream_server(AF_INET, &data_port, STREAM_BUFSIZE,
541 STREAM_BUFSIZE, 0)) < 0){
542 error(_("could not create data socket: %s"), strerror(errno));
545 dbprintf(_("Local port %d set aside for data\n"), data_port);
547 /* tell client where to connect */
548 g_printf(_("CONNECT %hu\n"), (unsigned short)data_port);
551 if((data_fd = stream_accept(data_sock, TIMEOUT, STREAM_BUFSIZE,
552 STREAM_BUFSIZE)) < 0){
553 error(_("stream_accept failed for client data connection: %s\n"),
558 buf = get_client_line_fd(data_fd);
560 check_security_buffer(buf);
561 rst_flags->pipe_to_fd = data_fd;
565 rst_flags->pipe_to_fd = fileno(stdout);
568 dbprintf(_("Sending output to file descriptor %d\n"), rst_flags->pipe_to_fd);
571 tapedev = getconf_str(CNF_TAPEDEV);
574 (use_changer || (rst_flags->alt_tapedev && tapedev &&
575 strcmp(rst_flags->alt_tapedev, tapedev) == 0) ) ) {
576 char *process_name = get_master_process(rst_conf_logfile);
577 send_message(cmdout, rst_flags, their_features,
578 _("%s exists: %s is already running, "
579 "or you must run amcleanup"),
580 rst_conf_logfile, process_name);
581 error(_("%s exists: %s is already running, "
582 "or you must run amcleanup"),
583 rst_conf_logfile, process_name);
586 /* make sure our restore flags aren't crazy */
587 if (check_rst_flags(rst_flags) == -1) {
588 if (rst_flags->pipe_to_fd != -1)
589 aclose(rst_flags->pipe_to_fd);
590 send_message(cmdout, rst_flags, their_features,
591 _("restore flags are crazy"));
595 /* actual restoration */
596 search_tapes(cmdout, cmdin, use_changer, tapes, dumpspecs, rst_flags,
598 dbprintf(_("Restoration finished\n"));
601 if(rst_flags->pipe_to_fd != -1) aclose(rst_flags->pipe_to_fd);
602 free_tapelist(tapes);
604 am_release_feature_set(their_features);
606 amfree(rst_flags->alt_tapedev);
608 dumpspec_list_free(dumpspecs);
617 if (parent_pid == getpid()) {
619 log_add(L_INFO, "pid-done %ld\n", (long)getpid());
620 unlink(rst_conf_logfile);