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