1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1999 University of Maryland at College Park
4  * Copyright (c) 2007-2013 Zmanda, Inc.  All Rights Reserved.
5  * All Rights Reserved.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of U.M. not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  U.M. makes no representations about the
14  * suitability of this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  *
17  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Authors: the Amanda Development Team.  Its members are listed in a
25  * file named AUTHORS, in the root directory of this distribution.
26  */
27 /*
28  * $Id: sendbackup.c,v 1.88 2006/07/25 18:27:56 martinea Exp $
29  *
30  * common code for the sendbackup-* programs.
31  */
32 
33 #include "amanda.h"
34 #include "match.h"
35 #include "sendbackup.h"
36 #include "clock.h"
37 #include "pipespawn.h"
38 #include "amfeatures.h"
39 #include "arglist.h"
40 #include "getfsent.h"
41 #include "conffile.h"
42 #include "amandates.h"
43 #include "stream.h"
44 
45 #define sendbackup_debug(i, ...) do {	\
46 	if ((i) <= debug_sendbackup) {	\
47 	    dbprintf(__VA_LIST__);	\
48 	}				\
49 } while (0)
50 
51 #define TIMEOUT 30
52 
53 pid_t comppid = (pid_t)-1;
54 pid_t dumppid = (pid_t)-1;
55 pid_t tarpid = (pid_t)-1;
56 pid_t encpid = (pid_t)-1;
57 pid_t indexpid = (pid_t)-1;
58 pid_t application_api_pid = (pid_t)-1;
59 char *errorstr = NULL;
60 
61 int datafd;
62 int mesgfd;
63 int indexfd;
64 
65 g_option_t *g_options = NULL;
66 
67 long dump_size = -1;
68 
69 backup_program_t *program = NULL;
70 dle_t *gdle = NULL;
71 
72 static am_feature_t *our_features = NULL;
73 static char *our_feature_string = NULL;
74 static char *amandad_auth = NULL;
75 
76 /* local functions */
77 int main(int argc, char **argv);
78 char *childstr(pid_t pid);
79 int check_status(pid_t pid, amwait_t w, int mesgfd);
80 
81 pid_t pipefork(void (*func)(void), char *fname, int *stdinfd,
82 		int stdoutfd, int stderrfd);
83 int check_result(int mesgfd);
84 void parse_backup_messages(dle_t *dle, int mesgin);
85 static void process_dumpline(char *str);
86 static void save_fd(int *, int);
87 void application_api_info_tapeheader(int mesgfd, char *prog, dle_t *dle);
88 
89 int fdprintf(int fd, char *format, ...) G_GNUC_PRINTF(2, 3);
90 
91 int
fdprintf(int fd,char * format,...)92 fdprintf(
93     int   fd,
94     char *format,
95     ...)
96 {
97     va_list  argp;
98     char    *s;
99     int      r;
100 
101     arglist_start(argp, format);
102     s = g_strdup_vprintf(format, argp);
103     arglist_end(argp);
104 
105     r = full_write(fd, s, strlen(s));
106     amfree(s);
107     return r;
108 }
109 
110 int
main(int argc,char ** argv)111 main(
112     int		argc,
113     char **	argv)
114 {
115     int interactive = 0;
116     int level = 0;
117     int mesgpipe[2];
118     dle_t *dle = NULL;
119     char *dumpdate, *stroptions;
120     char *qdisk = NULL;
121     char *qamdevice = NULL;
122     char *line = NULL;
123     char *err_extra = NULL;
124     char *s;
125     int i;
126     int ch;
127     GSList *errlist;
128     FILE   *mesgstream;
129     am_level_t *alevel;
130 
131     if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) {
132 	printf("sendbackup-%s\n", VERSION);
133 	return (0);
134     }
135 
136     /* initialize */
137     /*
138      * Configure program for internationalization:
139      *   1) Only set the message locale for now.
140      *   2) Set textdomain for all amanda related programs to "amanda"
141      *      We don't want to be forced to support dozens of message catalogs.
142      */
143     setlocale(LC_MESSAGES, "C");
144     textdomain("amanda");
145 
146     safe_fd(DATA_FD_OFFSET, DATA_FD_COUNT*2);
147     openbsd_fd_inform();
148 
149     safe_cd();
150 
151     set_pname("sendbackup");
152 
153     /* Don't die when child closes pipe */
154     signal(SIGPIPE, SIG_IGN);
155 
156     /* Don't die when interrupt received */
157     signal(SIGINT, SIG_IGN);
158 
159     if(argc > 1 && strcmp(argv[1],"-t") == 0) {
160 	interactive = 1;
161 	argc--;
162 	argv++;
163     } else {
164 	interactive = 0;
165     }
166 
167     add_amanda_log_handler(amanda_log_stderr);
168     add_amanda_log_handler(amanda_log_syslog);
169     dbopen(DBG_SUBDIR_CLIENT);
170     startclock();
171     dbprintf(_("Version %s\n"), VERSION);
172 
173     if(argc > 2 && strcmp(argv[1], "amandad") == 0) {
174 	amandad_auth = stralloc(argv[2]);
175     }
176 
177     our_features = am_init_feature_set();
178     our_feature_string = am_feature_to_string(our_features);
179 
180     config_init(CONFIG_INIT_CLIENT, NULL);
181     /* (check for config errors comes later) */
182 
183     check_running_as(RUNNING_AS_CLIENT_LOGIN);
184 
185     if(interactive) {
186 	/*
187 	 * In interactive (debug) mode, the backup data is sent to
188 	 * /dev/null and none of the network connections back to driver
189 	 * programs on the tape host are set up.  The index service is
190 	 * run and goes to stdout.
191 	 */
192 	g_fprintf(stderr, _("%s: running in interactive test mode\n"), get_pname());
193 	fflush(stderr);
194     }
195 
196     qdisk = NULL;
197     dumpdate = NULL;
198     stroptions = NULL;
199 
200     for(; (line = agets(stdin)) != NULL; free(line)) {
201 	if (line[0] == '\0')
202 	    continue;
203 	if(interactive) {
204 	    g_fprintf(stderr, "%s> ", get_pname());
205 	    fflush(stderr);
206 	}
207 	if(strncmp_const(line, "OPTIONS ") == 0) {
208 	    g_options = parse_g_options(line+8, 1);
209 	    if(!g_options->hostname) {
210 		g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
211 		gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
212 		g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
213 	    }
214 
215 	    if (g_options->config) {
216 		/* overlay this configuration on the existing (nameless) configuration */
217 		config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
218 			    g_options->config);
219 
220 		dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
221 	    }
222 
223 	    /* check for any config errors now */
224 	    if (config_errors(&errlist) >= CFGERR_ERRORS) {
225 		char *errstr = config_errors_to_error_string(errlist);
226 		g_printf("%s\n", errstr);
227 		dbclose();
228 		return 1;
229 	    }
230 
231 	    if (am_has_feature(g_options->features, fe_req_xml)) {
232 		break;
233 	    }
234 	    continue;
235 	}
236 
237 	if (dle && dle->program != NULL) {
238 	    err_extra = _("multiple requests");
239 	    goto err;
240 	}
241 
242 	dbprintf(_("  sendbackup req: <%s>\n"), line);
243 	dle = alloc_dle();
244 
245 	s = line;
246 	ch = *s++;
247 
248 	skip_whitespace(s, ch);			/* find the program name */
249 	if(ch == '\0') {
250 	    err_extra = _("no program name");
251 	    goto err;				/* no program name */
252 	}
253 	dle->program = s - 1;
254 	skip_non_whitespace(s, ch);
255 	s[-1] = '\0';
256 
257         if (strcmp(dle->program, "APPLICATION")==0) {
258             dle->program_is_application_api=1;
259             skip_whitespace(s, ch);             /* find dumper name */
260             if (ch == '\0') {
261                 goto err;                       /* no program */
262             }
263             dle->program = s - 1;
264             skip_non_whitespace(s, ch);
265             s[-1] = '\0';
266         }
267 	dle->program = stralloc(dle->program);
268 
269 	skip_whitespace(s, ch);			/* find the disk name */
270 	if(ch == '\0') {
271 	    err_extra = _("no disk name");
272 	    goto err;				/* no disk name */
273 	}
274 
275 	amfree(qdisk);
276 	qdisk = s - 1;
277 	ch = *qdisk;
278 	skip_quoted_string(s, ch);
279 	s[-1] = '\0';
280 	qdisk = stralloc(qdisk);
281 	dle->disk = unquote_string(qdisk);
282 
283 	skip_whitespace(s, ch);			/* find the device or level */
284 	if (ch == '\0') {
285 	    err_extra = _("bad level");
286 	    goto err;
287 	}
288 
289 	if(!isdigit((int)s[-1])) {
290 	    amfree(qamdevice);
291 	    qamdevice = s - 1;
292 	    ch = *qamdevice;
293 	    skip_quoted_string(s, ch);
294 	    s[-1] = '\0';
295 	    qamdevice = stralloc(qamdevice);
296 	    dle->device = unquote_string(qamdevice);
297 	    skip_whitespace(s, ch);		/* find level number */
298 	}
299 	else {
300 	    dle->device = stralloc(dle->disk);
301 	    qamdevice = stralloc(qdisk);
302 	}
303 						/* find the level number */
304 	if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
305 	    err_extra = _("bad level");
306 	    goto err;				/* bad level */
307 	}
308 	skip_integer(s, ch);
309 	alevel = g_new0(am_level_t, 1);
310 	alevel->level = level;
311 	dle->levellist = g_slist_append(dle->levellist, alevel);
312 
313 	skip_whitespace(s, ch);			/* find the dump date */
314 	if(ch == '\0') {
315 	    err_extra = _("no dumpdate");
316 	    goto err;				/* no dumpdate */
317 	}
318 	amfree(dumpdate);
319 	dumpdate = s - 1;
320 	skip_non_whitespace(s, ch);
321 	s[-1] = '\0';
322 	dumpdate = stralloc(dumpdate);
323 
324 	skip_whitespace(s, ch);			/* find the options keyword */
325 	if(ch == '\0') {
326 	    err_extra = _("no options");
327 	    goto err;				/* no options */
328 	}
329 	if(strncmp_const_skip(s - 1, "OPTIONS ", s, ch) != 0) {
330 	    err_extra = _("no OPTIONS keyword");
331 	    goto err;				/* no options */
332 	}
333 	skip_whitespace(s, ch);			/* find the options string */
334 	if(ch == '\0') {
335 	    err_extra = _("bad options string");
336 	    goto err;				/* no options */
337 	}
338 	amfree(stroptions);
339 	stroptions = stralloc(s - 1);
340     }
341     amfree(line);
342     if (g_options == NULL) {
343 	g_printf(_("ERROR [Missing OPTIONS line in sendbackup input]\n"));
344 	error(_("Missing OPTIONS line in sendbackup input\n"));
345 	/*NOTREACHED*/
346     }
347 
348     if (am_has_feature(g_options->features, fe_req_xml)) {
349 	char *errmsg = NULL;
350 
351 	dle = amxml_parse_node_FILE(stdin, &errmsg);
352 	if (errmsg) {
353 	    err_extra = errmsg;
354 	    goto err;
355 	}
356 	if (!dle) {
357 	    err_extra = _("One DLE required");
358 	    goto err;
359 	} else if (dle->next) {
360 	    err_extra = _("Only one DLE allowed");
361 	    goto err;
362 	}
363 
364 	qdisk = quote_string(dle->disk);
365 	if (dle->device == NULL)
366 	    dle->device = stralloc(dle->disk);
367 	qamdevice = quote_string(dle->device);
368 	dumpdate = stralloc("NODATE");
369 	stroptions = stralloc("");
370     } else {
371 	parse_options(stroptions, dle, g_options->features, 0);
372     }
373     gdle = dle;
374 
375     if (dle->program   == NULL ||
376 	dle->disk      == NULL ||
377 	dle->device    == NULL ||
378 	dle->levellist == NULL ||
379 	dumpdate       == NULL) {
380 	err_extra = _("no valid sendbackup request");
381 	goto err;
382     }
383 
384     if (g_slist_length(dle->levellist) != 1) {
385 	err_extra = _("Too many level");
386 	goto err;
387     }
388 
389     alevel = (am_level_t *)dle->levellist->data;
390     level = alevel->level;
391     dbprintf(_("  Parsed request as: program `%s'\n"), dle->program);
392     dbprintf(_("                     disk `%s'\n"), qdisk);
393     dbprintf(_("                     device `%s'\n"), qamdevice);
394     dbprintf(_("                     level %d\n"), level);
395     dbprintf(_("                     since %s\n"), dumpdate);
396     dbprintf(_("                     options `%s'\n"), stroptions);
397     dbprintf(_("                     datapath `%s'\n"),
398 			    data_path_to_string(dle->data_path));
399 
400     if (dle->program_is_application_api==1) {
401 	/* check that the application_api exist */
402     } else {
403 	for(i = 0; programs[i]; i++) {
404 	    if (strcmp(programs[i]->name, dle->program) == 0) {
405 		break;
406 	    }
407 	}
408 	if (programs[i] == NULL) {
409 	    dbprintf(_("ERROR [%s: unknown program %s]\n"), get_pname(),
410 		     dle->program);
411 	    error(_("ERROR [%s: unknown program %s]"), get_pname(),
412 		  dle->program);
413 	    /*NOTREACHED*/
414 	}
415 	program = programs[i];
416     }
417 
418     if(!interactive) {
419 	datafd = DATA_FD_OFFSET + 0;
420 	mesgfd = DATA_FD_OFFSET + 2;
421 	indexfd = DATA_FD_OFFSET + 4;
422     }
423     if (!dle->create_index)
424 	indexfd = -1;
425 
426     if (dle->auth && amandad_auth) {
427 	if(strcasecmp(dle->auth, amandad_auth) != 0) {
428 	    g_printf(_("ERROR [client configured for auth=%s while server requested '%s']\n"),
429 		   amandad_auth, dle->auth);
430 	    exit(-1);
431 	}
432     }
433 
434     if (dle->kencrypt) {
435 	g_printf("KENCRYPT\n");
436     }
437 
438     g_printf(_("CONNECT DATA %d MESG %d INDEX %d\n"),
439 	   DATA_FD_OFFSET, DATA_FD_OFFSET+1,
440 	   indexfd == -1 ? -1 : DATA_FD_OFFSET+2);
441     g_printf(_("OPTIONS "));
442     if(am_has_feature(g_options->features, fe_rep_options_features)) {
443 	g_printf("features=%s;", our_feature_string);
444     }
445     if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
446 	g_printf("hostname=%s;", g_options->hostname);
447     }
448     if (!am_has_feature(g_options->features, fe_rep_options_features) &&
449 	!am_has_feature(g_options->features, fe_rep_options_hostname)) {
450 	g_printf(";");
451     }
452     g_printf("\n");
453     fflush(stdout);
454     if (freopen("/dev/null", "w", stdout) == NULL) {
455 	dbprintf(_("Error redirecting stdout to /dev/null: %s\n"),
456 		 strerror(errno));
457         exit(1);
458     }
459 
460     if(interactive) {
461       if((datafd = open("/dev/null", O_RDWR)) < 0) {
462 	error(_("ERROR [open of /dev/null for debug data stream: %s]\n"),
463 		strerror(errno));
464 	/*NOTREACHED*/
465       }
466       mesgfd = 2;
467       indexfd = 1;
468     }
469 
470     if(!interactive) {
471       if(datafd == -1 || mesgfd == -1 || (dle->create_index && indexfd == -1)) {
472         dbclose();
473         exit(1);
474       }
475     }
476 
477     if (merge_dles_properties(dle, 1) == 0) {
478 	g_debug("merge_dles_properties failed");
479 	exit(1);
480     }
481     mesgstream = fdopen(mesgfd,"w");
482     run_client_scripts(EXECUTE_ON_PRE_DLE_BACKUP, g_options, dle, mesgstream);
483     fflush(mesgstream);
484 
485     if (dle->program_is_application_api==1) {
486 	guint j;
487 	char *cmd=NULL;
488 	GPtrArray *argv_ptr;
489 	char levelstr[20];
490 	backup_support_option_t *bsu;
491 	char *compopt = NULL;
492 	char *encryptopt = skip_argument;
493 	int compout, dumpout;
494 	GSList    *scriptlist;
495 	script_t  *script;
496 	time_t     cur_dumptime;
497 	int        result;
498 	GPtrArray *errarray;
499 	int        errfd[2];
500 	FILE      *dumperr;
501 
502 	/*  apply client-side encryption here */
503 	if ( dle->encrypt == ENCRYPT_CUST ) {
504 	    encpid = pipespawn(dle->clnt_encrypt, STDIN_PIPE, 0,
505 			       &compout, &datafd, &mesgfd,
506 			       dle->clnt_encrypt, encryptopt, NULL);
507 	    dbprintf(_("encrypt: pid %ld: %s\n"), (long)encpid, dle->clnt_encrypt);
508 	} else {
509 	    compout = datafd;
510 	    encpid = -1;
511 	}
512 
513 	/*  now do the client-side compression */
514 	if(dle->compress == COMP_FAST || dle->compress == COMP_BEST) {
515 	    compopt = skip_argument;
516 #if defined(COMPRESS_BEST_OPT) && defined(COMPRESS_FAST_OPT)
517 	    if(dle->compress == COMP_BEST) {
518 		compopt = COMPRESS_BEST_OPT;
519 	    } else {
520 		compopt = COMPRESS_FAST_OPT;
521 	    }
522 #endif
523 	    comppid = pipespawn(COMPRESS_PATH, STDIN_PIPE, 0,
524 				&dumpout, &compout, &mesgfd,
525 				COMPRESS_PATH, compopt, NULL);
526 	    if(compopt != skip_argument) {
527 		dbprintf(_("compress pid %ld: %s %s\n"),
528 			 (long)comppid, COMPRESS_PATH, compopt);
529 	    } else {
530 		dbprintf(_("compress pid %ld: %s\n"), (long)comppid, COMPRESS_PATH);
531 	    }
532 	} else if (dle->compress == COMP_CUST) {
533 	    compopt = skip_argument;
534 	    comppid = pipespawn(dle->compprog, STDIN_PIPE, 0,
535 				&dumpout, &compout, &mesgfd,
536 				dle->compprog, compopt, NULL);
537 	    if(compopt != skip_argument) {
538 		dbprintf(_("pid %ld: %s %s\n"),
539 			 (long)comppid, dle->compprog, compopt);
540 	    } else {
541 		dbprintf(_("pid %ld: %s\n"), (long)comppid, dle->compprog);
542 	    }
543 	} else {
544 	    dumpout = compout;
545 	    comppid = -1;
546 	}
547 
548 	cur_dumptime = time(0);
549 	bsu = backup_support_option(dle->program, g_options, dle->disk,
550 				    dle->device, &errarray);
551 	if (!bsu) {
552 	    char  *errmsg;
553 	    char  *qerrmsg;
554 	    guint  i;
555 	    for (i=0; i < errarray->len; i++) {
556 		errmsg = g_ptr_array_index(errarray, i);
557 		qerrmsg = quote_string(errmsg);
558 		fdprintf(mesgfd,
559 			  _("sendbackup: error [Application '%s': %s]\n"),
560 			  dle->program, errmsg);
561 		dbprintf("ERROR %s\n",qerrmsg);
562 		amfree(qerrmsg);
563 	    }
564 	    if (i == 0) { /* no errarray */
565 		errmsg = vstrallocf(_("Can't execute application '%s'"),
566 				    dle->program);
567 		qerrmsg = quote_string(errmsg);
568 		fdprintf(mesgfd, _("sendbackup: error [%s]\n"), errmsg);
569 		dbprintf(_("ERROR %s\n"), qerrmsg);
570 		amfree(qerrmsg);
571 		amfree(errmsg);
572 	    }
573 	    return 0;
574 	}
575 
576 	if (pipe(errfd) < 0) {
577 	    char  *errmsg;
578 	    char  *qerrmsg;
579 	    errmsg = vstrallocf(_("Application '%s': can't create pipe"),
580 				    dle->program);
581 	    qerrmsg = quote_string(errmsg);
582 	    fdprintf(mesgfd, _("sendbackup: error [%s]\n"), errmsg);
583 	    dbprintf(_("ERROR %s\n"), qerrmsg);
584 	    amfree(qerrmsg);
585 	    amfree(errmsg);
586 	    return 0;
587 	}
588 
589 	switch(application_api_pid=fork()) {
590 	case 0:
591 	    application_api_info_tapeheader(mesgfd, dle->program, dle);
592 
593 	    /* find directt-tcp address from indirect direct-tcp */
594 	    if (dle->data_path == DATA_PATH_DIRECTTCP &&
595 		bsu->data_path_set & DATA_PATH_DIRECTTCP &&
596 		strncmp(dle->directtcp_list->data, "255.255.255.255:", 16) == 0) {
597 		char *indirect_tcp;
598 		char *str_port;
599 		in_port_t port;
600 		int fd;
601 		char buffer[32770];
602 		int size;
603 		char *s, *s1;
604 
605 		indirect_tcp = g_strdup(dle->directtcp_list->data);
606 		g_debug("indirecttcp: %s", indirect_tcp);
607 		g_slist_free(dle->directtcp_list);
608 		dle->directtcp_list = NULL;
609 		str_port = strchr(indirect_tcp, ':');
610 		str_port++;
611 		port = atoi(str_port);
612 		fd = stream_client("localhost", port, 32768, 32768, NULL, 0);
613 		if (fd <= 0) {
614 		    g_debug("Failed to connect to indirect-direct-tcp port: %s",
615 			    strerror(errno));
616 		    exit(1);
617 		}
618 		size = full_read(fd, buffer, 32768);
619 		if (size <= 0) {
620 		    g_debug("Failed to read from indirect-direct-tcp port: %s",
621 			    strerror(errno));
622 		    close(fd);
623 		    exit(1);
624 		}
625 		close(fd);
626 		buffer[size++] = ' ';
627 		buffer[size] = '\0';
628 		s1 = buffer;
629 		while ((s = strchr(s1, ' ')) != NULL) {
630 		    *s++ = '\0';
631 		    g_debug("directtcp: %s", s1);
632 		    dle->directtcp_list = g_slist_append(dle->directtcp_list, g_strdup(s1));
633 		    s1 = s;
634 		}
635 		amfree(indirect_tcp);
636 	    }
637 
638 	    argv_ptr = g_ptr_array_new();
639 	    cmd = vstralloc(APPLICATION_DIR, "/", dle->program, NULL);
640 	    g_ptr_array_add(argv_ptr, stralloc(dle->program));
641 	    g_ptr_array_add(argv_ptr, stralloc("backup"));
642 	    if (bsu->message_line == 1) {
643 		g_ptr_array_add(argv_ptr, stralloc("--message"));
644 		g_ptr_array_add(argv_ptr, stralloc("line"));
645 	    }
646 	    if (g_options->config && bsu->config == 1) {
647 		g_ptr_array_add(argv_ptr, stralloc("--config"));
648 		g_ptr_array_add(argv_ptr, stralloc(g_options->config));
649 	    }
650 	    if (g_options->hostname && bsu->host == 1) {
651 		g_ptr_array_add(argv_ptr, stralloc("--host"));
652 		g_ptr_array_add(argv_ptr, stralloc(g_options->hostname));
653 	    }
654 	    if (dle->disk && bsu->disk == 1) {
655 		g_ptr_array_add(argv_ptr, stralloc("--disk"));
656 		g_ptr_array_add(argv_ptr, stralloc(dle->disk));
657 	    }
658 	    g_ptr_array_add(argv_ptr, stralloc("--device"));
659 	    g_ptr_array_add(argv_ptr, stralloc(dle->device));
660 	    if (level <= bsu->max_level) {
661 		g_ptr_array_add(argv_ptr, stralloc("--level"));
662 		g_snprintf(levelstr,19,"%d",level);
663 		g_ptr_array_add(argv_ptr, stralloc(levelstr));
664 	    }
665 	    if (indexfd != -1 && bsu->index_line == 1) {
666 		g_ptr_array_add(argv_ptr, stralloc("--index"));
667 		g_ptr_array_add(argv_ptr, stralloc("line"));
668 	    }
669 	    if (dle->record && bsu->record == 1) {
670 		g_ptr_array_add(argv_ptr, stralloc("--record"));
671 	    }
672 	    application_property_add_to_argv(argv_ptr, dle, bsu,
673 					     g_options->features);
674 
675 	    for (scriptlist = dle->scriptlist; scriptlist != NULL;
676 		 scriptlist = scriptlist->next) {
677 		script = (script_t *)scriptlist->data;
678 		if (script->result && script->result->proplist) {
679 		    property_add_to_argv(argv_ptr, script->result->proplist);
680 		}
681 	    }
682 
683 	    g_ptr_array_add(argv_ptr, NULL);
684 	    dbprintf(_("%s: running \"%s\n"), get_pname(), cmd);
685 	    for (j = 1; j < argv_ptr->len - 1; j++)
686 		dbprintf(" %s\n", (char *)g_ptr_array_index(argv_ptr,j));
687 	    dbprintf(_("\"\n"));
688 	    if(dup2(dumpout, 1) == -1) {
689 		error(_("Can't dup2: %s"),strerror(errno));
690 		/*NOTREACHED*/
691 	    }
692 	    if (dup2(errfd[1], 2) == -1) {
693 		error(_("Can't dup2: %s"),strerror(errno));
694 		/*NOTREACHED*/
695 	    }
696 	    if(dup2(mesgfd, 3) == -1) {
697 		error(_("Can't dup2: %s"),strerror(errno));
698 		/*NOTREACHED*/
699 	    }
700 	    if(indexfd > 0) {
701 		if(dup2(indexfd, 4) == -1) {
702 		    error(_("Can't dup2: %s"),strerror(errno));
703 		    /*NOTREACHED*/
704 		}
705 		fcntl(indexfd, F_SETFD, 0);
706 	    }
707 	    if (indexfd != 0) {
708 		safe_fd(3, 2);
709 	    } else {
710 		safe_fd(3, 1);
711 	    }
712 	    execve(cmd, (char **)argv_ptr->pdata, safe_env());
713 	    exit(1);
714 	    break;
715 
716 	default:
717 	    break;
718 	case -1:
719 	    error(_("%s: fork returned: %s"), get_pname(), strerror(errno));
720 	}
721 
722 	close(errfd[1]);
723 	dumperr = fdopen(errfd[0],"r");
724 	if (!dumperr) {
725 	    error(_("Can't fdopen: %s"), strerror(errno));
726 	    /*NOTREACHED*/
727 	}
728 
729 	result = 0;
730 	while ((line = agets(dumperr)) != NULL) {
731 	    if (strlen(line) > 0) {
732 		fdprintf(mesgfd, "sendbackup: error [%s]\n", line);
733 		dbprintf("error: %s\n", line);
734 		result = 1;
735 	    }
736 	    amfree(line);
737 	}
738 
739 	result |= check_result(mesgfd);
740 	if (result == 0) {
741 	    char *amandates_file;
742 
743 	    amandates_file = getconf_str(CNF_AMANDATES);
744 	    if(start_amandates(amandates_file, 1)) {
745 		amandates_updateone(dle->disk, level, cur_dumptime);
746 		finish_amandates();
747 		free_amandates();
748 	    } else {
749 		if (GPOINTER_TO_INT(dle->estimatelist->data) == ES_CALCSIZE &&
750 		    bsu->calcsize) {
751 		    error(_("error [opening %s for writing: %s]"),
752 			  amandates_file, strerror(errno));
753 		} else {
754 		    g_debug(_("non-fatal error opening '%s' for writing: %s]"),
755 			    amandates_file, strerror(errno));
756 		}
757 	    }
758 	}
759 	amfree(bsu);
760      } else {
761 	if(!interactive) {
762 	    /* redirect stderr */
763 	    if(dup2(mesgfd, 2) == -1) {
764 		dbprintf(_("Error redirecting stderr to fd %d: %s\n"),
765 			 mesgfd, strerror(errno));
766 		dbclose();
767 		exit(1);
768 	    }
769 	}
770 
771 	if(pipe(mesgpipe) == -1) {
772 	    s = strerror(errno);
773 	    dbprintf(_("error [opening mesg pipe: %s]\n"), s);
774 	    error(_("error [opening mesg pipe: %s]"), s);
775 	}
776 
777 	program->start_backup(dle, g_options->hostname,
778 			      datafd, mesgpipe[1], indexfd);
779 	dbprintf(_("Started backup\n"));
780 	parse_backup_messages(dle, mesgpipe[0]);
781 	dbprintf(_("Parsed backup messages\n"));
782     }
783 
784     run_client_scripts(EXECUTE_ON_POST_DLE_BACKUP, g_options, dle, mesgstream);
785     fflush(mesgstream);
786 
787     amfree(qdisk);
788     amfree(qamdevice);
789     amfree(dumpdate);
790     amfree(stroptions);
791     amfree(our_feature_string);
792     am_release_feature_set(our_features);
793     our_features = NULL;
794     free_g_options(g_options);
795 
796     dbclose();
797 
798     return 0;
799 
800  err:
801     if (err_extra) {
802 	g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET '%s'\n"), err_extra);
803 	dbprintf(_("REQ packet is bogus: %s\n"), err_extra);
804     } else {
805 	g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET\n"));
806 	dbprintf(_("REQ packet is bogus\n"));
807     }
808 
809     amfree(qdisk);
810     amfree(qamdevice);
811     amfree(dumpdate);
812     amfree(stroptions);
813     amfree(our_feature_string);
814 
815     dbclose();
816     return 1;
817 }
818 
819 
820 /*
821  * Returns a string for a child process.  Checks the saved dump and
822  * compress pids to see which it is.
823  */
824 
825 char *
childstr(pid_t pid)826 childstr(
827     pid_t pid)
828 {
829     if(pid == dumppid) return program->backup_name;
830     if(pid == comppid) return "compress";
831     if(pid == encpid) return "encrypt";
832     if(pid == indexpid) return "index";
833     if(pid == application_api_pid) {
834 	if (!gdle) {
835 	    dbprintf("gdle == NULL\n");
836 	    return "gdle == NULL";
837 	}
838 	return gdle->program;
839     }
840     return "unknown";
841 }
842 
843 
844 /*
845  * Determine if the child return status really indicates an error.
846  * If so, add the error message to the error string; more than one
847  * child can have an error.
848  */
849 
850 int
check_status(pid_t pid,amwait_t w,int mesgfd)851 check_status(
852     pid_t	pid,
853     amwait_t	w,
854     int		mesgfd)
855 {
856     char *thiserr = NULL;
857     char *str, *strX;
858     int ret, sig, rc;
859 
860     str = childstr(pid);
861 
862     if(WIFSIGNALED(w)) {
863 	ret = 0;
864 	rc = sig = WTERMSIG(w);
865     } else {
866 	sig = 0;
867 	rc = ret = WEXITSTATUS(w);
868     }
869 
870     if(pid == indexpid) {
871 	/*
872 	 * Treat an index failure (other than signal) as a "STRANGE"
873 	 * rather than an error so the dump goes ahead and gets processed
874 	 * but the failure is noted.
875 	 */
876 	if(ret != 0) {
877 	    fdprintf(mesgfd, _("? index %s returned %d\n"), str, ret);
878 	    rc = 0;
879 	}
880 	indexpid = -1;
881 	strX = "index";
882     } else if(pid == comppid) {
883 	/*
884 	 * compress returns 2 sometimes, but it is ok.
885 	 */
886 #ifndef HAVE_GZIP
887 	if(ret == 2) {
888 	    rc = 0;
889 	}
890 #endif
891 	comppid = -1;
892 	strX = "compress";
893     } else if(pid == dumppid && tarpid == -1) {
894         /*
895 	 * Ultrix dump returns 1 sometimes, but it is ok.
896 	 */
897 #ifdef DUMP_RETURNS_1
898         if(ret == 1) {
899 	    rc = 0;
900 	}
901 #endif
902 	dumppid = -1;
903 	strX = "dump";
904     } else if(pid == tarpid) {
905 	if (ret == 1) {
906 	    rc = 0;
907 	}
908 	/*
909 	 * tar bitches about active filesystems, but we do not care.
910 	 */
911 #ifdef IGNORE_TAR_ERRORS
912         if(ret == 2) {
913 	    rc = 0;
914 	}
915 #endif
916 	dumppid = tarpid = -1;
917 	strX = "dump";
918     } else if(pid == application_api_pid) {
919 	strX = "Application";
920     } else {
921 	strX = "unknown";
922     }
923 
924     if(rc == 0) {
925 	return 0;				/* normal exit */
926     }
927 
928     if(ret == 0) {
929 	thiserr = vstrallocf(_("%s (%d) %s got signal %d"), strX, (int)pid, str,
930 			     sig);
931     } else {
932 	thiserr = vstrallocf(_("%s (%d) %s returned %d"), strX, (int)pid, str, ret);
933     }
934 
935     fdprintf(mesgfd, "? %s\n", thiserr);
936 
937     if(errorstr) {
938 	errorstr =  newvstrallocf(errorstr, "%s, %s", errorstr, thiserr);
939 	amfree(thiserr);
940     } else {
941 	errorstr = thiserr;
942 	thiserr = NULL;
943     }
944     return 1;
945 }
946 
947 
948 /*
949  *Send header info to the message file.
950  */
951 void
info_tapeheader(dle_t * dle)952 info_tapeheader(
953     dle_t *dle)
954 {
955     g_fprintf(stderr, "%s: info BACKUP=%s\n", get_pname(), program->backup_name);
956 
957     g_fprintf(stderr, "%s: info RECOVER_CMD=", get_pname());
958     if (dle->compress == COMP_FAST || dle->compress == COMP_BEST)
959 	g_fprintf(stderr, "%s %s |", UNCOMPRESS_PATH,
960 #ifdef UNCOMPRESS_OPT
961 		UNCOMPRESS_OPT
962 #else
963 		""
964 #endif
965 		);
966 
967     g_fprintf(stderr, "%s -xpGf - ...\n", program->restore_name);
968 
969     if (dle->compress == COMP_FAST || dle->compress == COMP_BEST)
970 	g_fprintf(stderr, "%s: info COMPRESS_SUFFIX=%s\n",
971 			get_pname(), COMPRESS_SUFFIX);
972 
973     g_fprintf(stderr, "%s: info end\n", get_pname());
974 }
975 
976 void
application_api_info_tapeheader(int mesgfd,char * prog,dle_t * dle)977 application_api_info_tapeheader(
978     int       mesgfd,
979     char     *prog,
980     dle_t *dle)
981 {
982     char line[1024];
983 
984     g_snprintf(line, 1024, "%s: info BACKUP=APPLICATION\n", get_pname());
985     if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
986 	dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
987 	return;
988     }
989 
990     g_snprintf(line, 1024, "%s: info APPLICATION=%s\n", get_pname(), prog);
991     if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
992 	dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
993 	return;
994     }
995 
996     g_snprintf(line, 1024, "%s: info RECOVER_CMD=", get_pname());
997     if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
998 	dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
999 	return;
1000     }
1001 
1002     if (dle->compress == COMP_FAST || dle->compress == COMP_BEST) {
1003 	g_snprintf(line, 1024, "%s %s |", UNCOMPRESS_PATH,
1004 #ifdef UNCOMPRESS_OPT
1005 		 UNCOMPRESS_OPT
1006 #else
1007 		 ""
1008 #endif
1009 		 );
1010 	if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
1011 	    dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
1012 	    return;
1013 	}
1014     }
1015     g_snprintf(line, 1024, "%s/%s restore [./file-to-restore]+\n",
1016 	       APPLICATION_DIR, prog);
1017     if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
1018 	dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
1019 	return;
1020     }
1021 
1022     if (dle->compress) {
1023 	g_snprintf(line, 1024, "%s: info COMPRESS_SUFFIX=%s\n",
1024 		 get_pname(), COMPRESS_SUFFIX);
1025 	if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
1026 	    dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
1027 	    return;
1028 	}
1029     }
1030 
1031     g_snprintf(line, 1024, "%s: info end\n", get_pname());
1032     if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
1033 	dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
1034 	return;
1035     }
1036 }
1037 
1038 pid_t
pipefork(void (* func)(void),char * fname,int * stdinfd,int stdoutfd,int stderrfd)1039 pipefork(
1040     void	(*func)(void),
1041     char *	fname,
1042     int *	stdinfd,
1043     int		stdoutfd,
1044     int		stderrfd)
1045 {
1046     int inpipe[2];
1047     pid_t pid;
1048 
1049     dbprintf(_("Forking function %s in pipeline\n"), fname);
1050 
1051     if(pipe(inpipe) == -1) {
1052 	error(_("error [open pipe to %s: %s]"), fname, strerror(errno));
1053 	/*NOTREACHED*/
1054     }
1055 
1056     switch(pid = fork()) {
1057     case -1:
1058 	error(_("error [fork %s: %s]"), fname, strerror(errno));
1059 	/*NOTREACHED*/
1060     default:	/* parent process */
1061 	aclose(inpipe[0]);	/* close input side of pipe */
1062 	*stdinfd = inpipe[1];
1063 	break;
1064     case 0:		/* child process */
1065 	aclose(inpipe[1]);	/* close output side of pipe */
1066 
1067 	if(dup2(inpipe[0], 0) == -1) {
1068 	    error(_("error [fork %s: dup2(%d, in): %s]"),
1069 		  fname, inpipe[0], strerror(errno));
1070 	    /*NOTRACHED*/
1071 	}
1072 	if(dup2(stdoutfd, 1) == -1) {
1073 	    error(_("error [fork %s: dup2(%d, out): %s]"),
1074 		  fname, stdoutfd, strerror(errno));
1075 	    /*NOTRACHED*/
1076 	}
1077 	if(dup2(stderrfd, 2) == -1) {
1078 	    error(_("error [fork %s: dup2(%d, err): %s]"),
1079 		  fname, stderrfd, strerror(errno));
1080 	    /*NOTRACHED*/
1081 	}
1082 
1083 	func();
1084 	exit(0);
1085 	/*NOTREACHED*/
1086     }
1087     return pid;
1088 }
1089 
1090 int
check_result(int mesgfd)1091 check_result(
1092     int mesgfd)
1093 {
1094     int goterror;
1095     pid_t wpid;
1096     amwait_t retstat;
1097     int process_alive = 1;
1098     int count = 0;
1099 
1100     goterror = 0;
1101 
1102     while (process_alive && count < 6) {
1103 	process_alive = 0;
1104 	if (indexpid != -1) {
1105 	    if ((wpid = waitpid(indexpid, &retstat, WNOHANG)) > 0) {
1106 		if (check_status(wpid, retstat, mesgfd)) goterror = 1;
1107 	    } else if (wpid == 0) {
1108 		process_alive = 1;
1109 	    }
1110 	}
1111 	if (comppid != -1) {
1112 	    if ((wpid = waitpid(comppid, &retstat, WNOHANG)) > 0) {
1113 		if (check_status(wpid, retstat, mesgfd)) goterror = 1;
1114 	    } else if (wpid == 0) {
1115 		process_alive = 1;
1116 	    }
1117 	}
1118 	if (dumppid != -1) {
1119 	    if ((wpid = waitpid(dumppid, &retstat, WNOHANG)) > 0) {
1120 		if (check_status(wpid, retstat, mesgfd)) goterror = 1;
1121 	    } else if (wpid == 0) {
1122 		process_alive = 1;
1123 	    }
1124 	}
1125 	if (tarpid != -1) {
1126 	    if ((wpid = waitpid(tarpid, &retstat, WNOHANG)) > 0) {
1127 		if (check_status(wpid, retstat, mesgfd)) goterror = 1;
1128 	    } else if (wpid == 0) {
1129 		process_alive = 1;
1130 	    }
1131 	}
1132 	if (application_api_pid != -1) {
1133 	    if ((wpid = waitpid(application_api_pid, &retstat, WNOHANG)) > 0) {
1134 		if (check_status(wpid, retstat, mesgfd)) goterror = 1;
1135 	    } else if (wpid == 0) {
1136 		process_alive = 1;
1137 	    }
1138 	}
1139 
1140 	while ((wpid = waitpid(-1, &retstat, WNOHANG)) > 0) {
1141 	    if (check_status(wpid, retstat, mesgfd)) goterror = 1;
1142 	}
1143 	if (wpid == 0)
1144 	    process_alive = 1;
1145 
1146 	if (process_alive) {
1147 	    sleep(1);
1148 	    count++;
1149 	}
1150     }
1151 
1152     if (dumppid == -1 && tarpid != -1)
1153 	dumppid = tarpid;
1154     if (dumppid == -1 && application_api_pid != -1)
1155 	dumppid = application_api_pid;
1156 
1157     if (dumppid != -1) {
1158 	dbprintf(_("Sending SIGHUP to dump process %d\n"),
1159 		  (int)dumppid);
1160 	if(dumppid != -1) {
1161 	    if(kill(dumppid, SIGHUP) == -1) {
1162 		dbprintf(_("Can't send SIGHUP to %d: %s\n"),
1163 			  (int)dumppid,
1164 			  strerror(errno));
1165 	    }
1166 	}
1167 	sleep(5);
1168 	while((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
1169 	    if(check_status(wpid, retstat, mesgfd)) goterror = 1;
1170 	}
1171     }
1172     if (dumppid != -1) {
1173 	dbprintf(_("Sending SIGKILL to dump process %d\n"),
1174 		  (int)dumppid);
1175 	if(dumppid != -1) {
1176 	    if(kill(dumppid, SIGKILL) == -1) {
1177 		dbprintf(_("Can't send SIGKILL to %d: %s\n"),
1178 			  (int)dumppid,
1179 			  strerror(errno));
1180 	    }
1181 	}
1182 	sleep(5);
1183 	while((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
1184 	    if(check_status(wpid, retstat, mesgfd)) goterror = 1;
1185 	}
1186     }
1187 
1188     return goterror;
1189 }
1190 
1191 void
parse_backup_messages(dle_t * dle,int mesgin)1192 parse_backup_messages(
1193     dle_t      *dle,
1194     int		mesgin)
1195 {
1196     int goterror;
1197     char *line;
1198 
1199     amfree(errorstr);
1200 
1201     for(; (line = areads(mesgin)) != NULL; free(line)) {
1202 	process_dumpline(line);
1203     }
1204 
1205     if(errno) {
1206 	error(_("error [read mesg pipe: %s]"), strerror(errno));
1207 	/*NOTREACHED*/
1208     }
1209 
1210     goterror = check_result(mesgfd);
1211 
1212     if(errorstr) {
1213 	error(_("error [%s]"), errorstr);
1214 	/*NOTREACHED*/
1215     } else if(dump_size == -1) {
1216 	error(_("error [no backup size line]"));
1217 	/*NOTREACHED*/
1218     }
1219 
1220     program->end_backup(dle, goterror);
1221 
1222     fdprintf(mesgfd, _("%s: size %ld\n"), get_pname(), dump_size);
1223     fdprintf(mesgfd, _("%s: end\n"), get_pname());
1224 }
1225 
1226 
1227 static void
process_dumpline(char * str)1228 process_dumpline(
1229     char *	str)
1230 {
1231     amregex_t *rp;
1232     char *type;
1233     char startchr;
1234 
1235     for(rp = program->re_table; rp->regex != NULL; rp++) {
1236 	if(match(rp->regex, str)) {
1237 	    break;
1238 	}
1239     }
1240     if(rp->typ == DMP_SIZE) {
1241 	dump_size = (long)((the_num(str, rp->field)* rp->scale+1023.0)/1024.0);
1242     }
1243     switch(rp->typ) {
1244     case DMP_NORMAL:
1245 	type = "normal";
1246 	startchr = '|';
1247 	break;
1248     case DMP_STRANGE:
1249 	type = "strange";
1250 	startchr = '?';
1251 	break;
1252     case DMP_SIZE:
1253 	type = "size";
1254 	startchr = '|';
1255 	break;
1256     case DMP_ERROR:
1257 	type = "error";
1258 	startchr = '?';
1259 	break;
1260     default:
1261 	/*
1262 	 * Should never get here.
1263 	 */
1264 	type = "unknown";
1265 	startchr = '!';
1266 	break;
1267     }
1268     dbprintf("%3d: %7s(%c): %s\n",
1269 	      rp->srcline,
1270 	      type,
1271 	      startchr,
1272 	      str);
1273     fdprintf(mesgfd, "%c %s\n", startchr, str);
1274 }
1275 
1276 
1277 /*
1278  * start_index.  Creates an index file from the output of dump/tar.
1279  * It arranges that input is the fd to be written by the dump process.
1280  * If createindex is not enabled, it does nothing.  If it is not, a
1281  * new process will be created that tees input both to a pipe whose
1282  * read fd is dup2'ed input and to a program that outputs an index
1283  * file to `index'.
1284  *
1285  * make sure that the chat from restore doesn't go to stderr cause
1286  * this goes back to amanda which doesn't expect to see it
1287  * (2>/dev/null should do it)
1288  *
1289  * Originally by Alan M. McIvor, 13 April 1996
1290  *
1291  * Adapted by Alexandre Oliva, 1 May 1997
1292  *
1293  * This program owes a lot to tee.c from GNU sh-utils and dumptee.c
1294  * from the DeeJay backup package.
1295  */
1296 
1297 static void
save_fd(int * fd,int min)1298 save_fd(
1299     int *	fd,
1300     int		min)
1301 {
1302   int origfd = *fd;
1303 
1304   while (*fd >= 0 && *fd < min) {
1305     int newfd = dup(*fd);
1306     if (newfd == -1)
1307       dbprintf(_("Unable to save file descriptor [%s]\n"), strerror(errno));
1308     *fd = newfd;
1309   }
1310   if (origfd != *fd)
1311     dbprintf(_("Dupped file descriptor %i to %i\n"), origfd, *fd);
1312 }
1313 
1314 void
start_index(int createindex,int input,int mesg,int index,char * cmd)1315 start_index(
1316     int		createindex,
1317     int		input,
1318     int		mesg,
1319     int		index,
1320     char *	cmd)
1321 {
1322   int pipefd[2];
1323   FILE *pipe_fp;
1324   int exitcode;
1325 
1326   if (!createindex)
1327     return;
1328 
1329   if (pipe(pipefd) != 0) {
1330     error(_("creating index pipe: %s"), strerror(errno));
1331     /*NOTREACHED*/
1332   }
1333 
1334   switch(indexpid = fork()) {
1335   case -1:
1336     error(_("forking index tee process: %s"), strerror(errno));
1337     /*NOTREACHED*/
1338 
1339   default:
1340     aclose(pipefd[0]);
1341     if (dup2(pipefd[1], input) == -1) {
1342       error(_("dup'ping index tee output: %s"), strerror(errno));
1343       /*NOTREACHED*/
1344     }
1345     aclose(pipefd[1]);
1346     return;
1347 
1348   case 0:
1349     break;
1350   }
1351 
1352   /* now in a child process */
1353   save_fd(&pipefd[0], 4);
1354   save_fd(&index, 4);
1355   save_fd(&mesg, 4);
1356   save_fd(&input, 4);
1357   dup2(pipefd[0], 0);
1358   dup2(index, 1);
1359   dup2(mesg, 2);
1360   dup2(input, 3);
1361   for(index = 4; index < (int)FD_SETSIZE; index++) {
1362     if (index != dbfd()) {
1363       close(index);
1364     }
1365   }
1366 
1367   if ((pipe_fp = popen(cmd, "w")) == NULL) {
1368     error(_("couldn't start index creator [%s]"), strerror(errno));
1369     /*NOTREACHED*/
1370   }
1371 
1372   dbprintf(_("Started index creator: \"%s\"\n"), cmd);
1373   while(1) {
1374     char buffer[BUFSIZ], *ptr;
1375     ssize_t bytes_read;
1376     size_t bytes_written;
1377     size_t just_written;
1378 
1379     do {
1380 	bytes_read = read(0, buffer, SIZEOF(buffer));
1381     } while ((bytes_read < 0) && ((errno == EINTR) || (errno == EAGAIN)));
1382 
1383     if (bytes_read < 0) {
1384       error(_("index tee cannot read [%s]"), strerror(errno));
1385       /*NOTREACHED*/
1386     }
1387 
1388     if (bytes_read == 0)
1389       break; /* finished */
1390 
1391     /* write the stuff to the subprocess */
1392     ptr = buffer;
1393     bytes_written = 0;
1394     just_written = full_write(fileno(pipe_fp), ptr, (size_t)bytes_read);
1395     if (just_written < (size_t)bytes_read) {
1396 	/*
1397 	 * just as we waited for write() to complete.
1398 	 */
1399 	if (errno != EPIPE) {
1400 	    dbprintf(_("Index tee cannot write to index creator [%s]\n"),
1401 			    strerror(errno));
1402 	}
1403     } else {
1404 	bytes_written += just_written;
1405 	ptr += just_written;
1406     }
1407 
1408     /* write the stuff to stdout, ensuring none lost when interrupt
1409        occurs */
1410     ptr = buffer;
1411     bytes_written = 0;
1412     just_written = full_write(3, ptr, bytes_read);
1413     if (just_written < (size_t)bytes_read) {
1414 	error(_("index tee cannot write [%s]"), strerror(errno));
1415 	/*NOTREACHED*/
1416     } else {
1417 	bytes_written += just_written;
1418 	ptr += just_written;
1419     }
1420   }
1421 
1422   aclose(pipefd[1]);
1423 
1424   /* finished */
1425   /* check the exit code of the pipe and moan if not 0 */
1426   if ((exitcode = pclose(pipe_fp)) != 0) {
1427     char *exitstr = str_exit_status("Index pipe", exitcode);
1428     dbprintf("%s\n", exitstr);
1429     amfree(exitstr);
1430   } else {
1431     dbprintf(_("Index created successfully\n"));
1432   }
1433   pipe_fp = NULL;
1434 
1435   exit(exitcode);
1436 }
1437 
1438 extern backup_program_t dump_program, gnutar_program;
1439 
1440 backup_program_t *programs[] = {
1441   &dump_program, &gnutar_program, NULL
1442 };
1443