737d4f89a0965ee529ee93135128abd838ad6f1c
[debian/amanda] / client-src / sendbackup.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1999 University of Maryland at College Park
4  * All Rights Reserved.
5  *
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.
15  *
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.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /* 
27  * $Id: sendbackup.c,v 1.88.2.3 2006/12/12 14:56:38 martinea Exp $
28  *
29  * common code for the sendbackup-* programs.
30  */
31
32 #include "amanda.h"
33 #include "sendbackup.h"
34 #include "clock.h"
35 #include "pipespawn.h"
36 #include "amfeatures.h"
37 #include "amandad.h"
38 #include "arglist.h"
39 #include "getfsent.h"
40 #include "version.h"
41 #include "clientconf.h"
42
43 #define TIMEOUT 30
44
45 pid_t comppid = (pid_t)-1;
46 pid_t dumppid = (pid_t)-1;
47 pid_t tarpid = (pid_t)-1;
48 pid_t encpid = (pid_t)-1;
49 pid_t indexpid = (pid_t)-1;
50 char *errorstr = NULL;
51
52 int datafd;
53 int mesgfd;
54 int indexfd;
55
56 option_t *options;
57 g_option_t *g_options = NULL;
58
59 long dump_size = -1;
60
61 backup_program_t *program = NULL;
62
63 static am_feature_t *our_features = NULL;
64 static char *our_feature_string = NULL;
65 static char *amandad_auth = NULL;
66
67 /* local functions */
68 int main(int argc, char **argv);
69 char *optionstr(option_t *options);
70 char *childstr(pid_t pid);
71 int check_status(pid_t pid, amwait_t w);
72
73 pid_t pipefork(void (*func)(void), char *fname, int *stdinfd,
74                 int stdoutfd, int stderrfd);
75 void parse_backup_messages(int mesgin);
76 static void process_dumpline(char *str);
77 static void save_fd(int *, int);
78
79 double first_num(char *str);
80
81
82
83 char *
84 optionstr(
85     option_t *  options)
86 {
87     static char *optstr = NULL;
88     char *compress_opt;
89     char *encrypt_opt;
90     char *decrypt_opt;
91     char *record_opt = "";
92     char *index_opt = "";
93     char *auth_opt;
94     char *exclude_file_opt;
95     char *exclude_list_opt;
96     char *exc = NULL;
97     sle_t *excl;
98
99     if(options->compress == COMPR_BEST)
100         compress_opt = stralloc("compress-best;");
101     else if(options->compress == COMPR_FAST)
102         compress_opt = stralloc("compress-fast;");
103     else if(options->compress == COMPR_SERVER_BEST)
104         compress_opt = stralloc("srvcomp-best;");
105     else if(options->compress == COMPR_SERVER_FAST)
106         compress_opt = stralloc("srvcomp-fast;");
107     else if(options->compress == COMPR_SERVER_CUST)
108         compress_opt = vstralloc("srvcomp-cust=", options->srvcompprog, ";", NULL);
109     else if(options->compress == COMPR_CUST)
110         compress_opt = vstralloc("comp-cust=", options->clntcompprog, ";", NULL);
111     else
112         compress_opt = stralloc("");
113     
114     if(options->encrypt == ENCRYPT_CUST) {
115       encrypt_opt = vstralloc("encrypt-cust=", options->clnt_encrypt, ";", NULL);
116       if (options->clnt_decrypt_opt)
117         decrypt_opt = vstralloc("client-decrypt-option=", options->clnt_decrypt_opt, ";", NULL);
118       else
119         decrypt_opt = stralloc("");
120     }
121     else if(options->encrypt == ENCRYPT_SERV_CUST) {
122       encrypt_opt = vstralloc("encrypt-serv-cust=", options->srv_encrypt, ";", NULL);
123       if(options->srv_decrypt_opt)
124         decrypt_opt = vstralloc("server-decrypt-option=", options->srv_decrypt_opt, ";", NULL);
125       else
126         decrypt_opt = stralloc("");
127     }
128     else {
129         encrypt_opt = stralloc("");
130         decrypt_opt = stralloc("");
131     }
132
133     if(options->no_record) record_opt = "no-record;";
134     if(options->auth) auth_opt = vstralloc("auth=", options->auth, ";", NULL);
135         else auth_opt = stralloc("");
136     if(options->createindex) index_opt = "index;";
137
138     exclude_file_opt = stralloc("");
139     if(options->exclude_file) {
140         for(excl = options->exclude_file->first; excl != NULL; excl=excl->next){
141             exc = newvstralloc(exc, "exclude-file=", excl->name, ";", NULL);
142             strappend(exclude_file_opt, exc);
143         }
144     }
145     exclude_list_opt = stralloc("");
146     if(options->exclude_list) {
147         for(excl = options->exclude_list->first; excl != NULL; excl=excl->next){
148             exc = newvstralloc(exc, "exclude-list=", excl->name, ";", NULL);
149             strappend(exclude_list_opt, exc);
150         }
151     }
152     amfree(exc);
153     optstr = newvstralloc(optstr,
154                           compress_opt,
155                           encrypt_opt,
156                           decrypt_opt,
157                           record_opt,
158                           index_opt,
159                           auth_opt,
160                           exclude_file_opt,
161                           exclude_list_opt,
162                           NULL);
163     amfree(compress_opt);
164     amfree(encrypt_opt);
165     amfree(decrypt_opt);
166     amfree(auth_opt);
167     amfree(exclude_file_opt);
168     amfree(exclude_list_opt);
169     return optstr;
170 }
171
172
173 int
174 main(
175     int         argc,
176     char **     argv)
177 {
178     int interactive = 0;
179     int level = 0;
180     int mesgpipe[2];
181     char *prog, *dumpdate, *stroptions;
182     char *disk = NULL;
183     char *qdisk = NULL;
184     char *amdevice = NULL;
185     char *qamdevice = NULL;
186     char *line = NULL;
187     char *err_extra = NULL;
188     char *s;
189     char *conffile;
190     int i;
191     int ch;
192     unsigned long malloc_hist_1, malloc_size_1;
193     unsigned long malloc_hist_2, malloc_size_2;
194
195     /* initialize */
196
197     safe_fd(DATA_FD_OFFSET, DATA_FD_COUNT*2);
198
199     safe_cd();
200
201     set_pname("sendbackup");
202
203     /* Don't die when child closes pipe */
204     signal(SIGPIPE, SIG_IGN);
205
206     /* Don't die when interrupt received */
207     signal(SIGINT, SIG_IGN);
208
209     malloc_size_1 = malloc_inuse(&malloc_hist_1);
210
211     if(argc > 1 && strcmp(argv[1],"-t") == 0) {
212         interactive = 1;
213         argc--;
214         argv++;
215     } else {
216         interactive = 0;
217     }
218
219     erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
220     dbopen(DBG_SUBDIR_CLIENT);
221     startclock();
222     dbprintf(("%s: version %s\n", get_pname(), version()));
223
224     if(argc > 2 && strcmp(argv[1], "amandad") == 0) {
225         amandad_auth = stralloc(argv[2]);
226     }
227
228     our_features = am_init_feature_set();
229     our_feature_string = am_feature_to_string(our_features);
230
231     conffile = vstralloc(CONFIG_DIR, "/", "amanda-client.conf", NULL);
232     if (read_clientconf(conffile) > 0) {
233         error("error reading conffile: %s", conffile);
234         /*NOTREACHED*/
235     }
236     amfree(conffile);
237
238     if(interactive) {
239         /*
240          * In interactive (debug) mode, the backup data is sent to
241          * /dev/null and none of the network connections back to driver
242          * programs on the tape host are set up.  The index service is
243          * run and goes to stdout.
244          */
245         fprintf(stderr, "%s: running in interactive test mode\n", get_pname());
246         fflush(stderr);
247     }
248
249     prog = NULL;
250     disk = NULL;
251     qdisk = NULL;
252     amdevice = NULL;
253     dumpdate = NULL;
254     stroptions = NULL;
255
256     for(; (line = agets(stdin)) != NULL; free(line)) {
257         if (line[0] == '\0')
258             continue;
259         if(interactive) {
260             fprintf(stderr, "%s> ", get_pname());
261             fflush(stderr);
262         }
263 #define sc "OPTIONS "
264         if(strncmp(line, sc, SIZEOF(sc)-1) == 0) {
265 #undef sc
266             g_options = parse_g_options(line+8, 1);
267             if(!g_options->hostname) {
268                 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
269                 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
270                 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
271             }
272
273             if (g_options->config) {
274                 conffile = vstralloc(CONFIG_DIR, "/", g_options->config, "/",
275                                      "amanda-client.conf", NULL);
276                 if (read_clientconf(conffile) > 0) {
277                     error("error reading conffile: %s", conffile);
278                     /*NOTREACHED*/
279                 }
280                 amfree(conffile);
281
282                 dbrename(g_options->config, DBG_SUBDIR_CLIENT);
283             }
284             continue;
285         }
286
287         if (prog != NULL) {
288             err_extra = "multiple requests";
289             goto err;
290         }
291
292         dbprintf(("  sendbackup req: <%s>\n", line));
293         s = line;
294         ch = *s++;
295
296         skip_whitespace(s, ch);                 /* find the program name */
297         if(ch == '\0') {
298             err_extra = "no program name";
299             goto err;                           /* no program name */
300         }
301         prog = s - 1;
302         skip_non_whitespace(s, ch);
303         s[-1] = '\0';
304         prog = stralloc(prog);
305
306         skip_whitespace(s, ch);                 /* find the disk name */
307         if(ch == '\0') {
308             err_extra = "no disk name";
309             goto err;                           /* no disk name */
310         }
311
312         amfree(disk);
313         amfree(qdisk);
314         qdisk = s - 1;
315         ch = *qdisk;
316         skip_quoted_string(s, ch);
317         s[-1] = '\0';
318         qdisk = stralloc(qdisk);
319         disk = unquote_string(qdisk);
320
321         skip_whitespace(s, ch);                 /* find the device or level */
322         if (ch == '\0') {
323             err_extra = "bad level";
324             goto err;
325         }
326
327         if(!isdigit((int)s[-1])) {
328             amfree(amdevice);
329             amfree(qamdevice);
330             amdevice = s - 1;
331             skip_non_whitespace(s, ch);
332             s[-1] = '\0';
333             amdevice = stralloc(amdevice);
334             skip_whitespace(s, ch);             /* find level number */
335         }
336         else {
337             amdevice = stralloc(disk);
338         }
339         qamdevice = quote_string(amdevice);
340                                                 /* find the level number */
341         if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
342             err_extra = "bad level";
343             goto err;                           /* bad level */
344         }
345         skip_integer(s, ch);
346
347         skip_whitespace(s, ch);                 /* find the dump date */
348         if(ch == '\0') {
349             err_extra = "no dumpdate";
350             goto err;                           /* no dumpdate */
351         }
352         amfree(dumpdate);
353         dumpdate = s - 1;
354         skip_non_whitespace(s, ch);
355         s[-1] = '\0';
356         dumpdate = stralloc(dumpdate);
357
358         skip_whitespace(s, ch);                 /* find the options keyword */
359         if(ch == '\0') {
360             err_extra = "no options";
361             goto err;                           /* no options */
362         }
363 #define sc "OPTIONS "
364         if(strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
365             err_extra = "no OPTIONS keyword";
366             goto err;                           /* no options */
367         }
368         s += SIZEOF(sc)-1;
369         ch = s[-1];
370 #undef sc
371         skip_whitespace(s, ch);                 /* find the options string */
372         if(ch == '\0') {
373             err_extra = "bad options string";
374             goto err;                           /* no options */
375         }
376         amfree(stroptions);
377         stroptions = stralloc(s - 1);
378     }
379     amfree(line);
380
381     if (prog       == NULL ||
382         disk       == NULL ||
383         amdevice   == NULL ||
384         dumpdate   == NULL ||
385         stroptions == NULL) {
386         err_extra = "no valid sendbackup request";
387         goto err;
388     }
389         
390     dbprintf(("  parsed request as: program `%s'\n", prog));
391     dbprintf(("                     disk `%s'\n", qdisk));
392     dbprintf(("                     device `%s'\n", qamdevice));
393     dbprintf(("                     level %d\n", level));
394     dbprintf(("                     since %s\n", dumpdate));
395     dbprintf(("                     options `%s'\n", stroptions));
396
397     for(i = 0; programs[i]; i++) {
398         if (strcmp(programs[i]->name, prog) == 0) {
399             break;
400         }
401     }
402     if (programs[i] == NULL) {
403         error("ERROR [%s: unknown program %s]", get_pname(), prog);
404         /*NOTREACHED*/
405     }
406     program = programs[i];
407
408     options = parse_options(stroptions, disk, amdevice, g_options->features, 0);
409
410     if(!interactive) {
411         datafd = DATA_FD_OFFSET + 0;
412         mesgfd = DATA_FD_OFFSET + 2;
413         indexfd = DATA_FD_OFFSET + 4;
414     }
415     if (!options->createindex)
416         indexfd = -1;
417
418     if(options->auth && amandad_auth) {
419         if(strcasecmp(options->auth, amandad_auth) != 0) {
420             printf("ERROR [client configured for auth=%s while server requested '%s']\n",
421                    amandad_auth, options->auth);
422             exit(-1);
423         }
424     }
425
426     printf("CONNECT DATA %d MESG %d INDEX %d\n",
427            DATA_FD_OFFSET, DATA_FD_OFFSET+1,
428            indexfd == -1 ? -1 : DATA_FD_OFFSET+2);
429     printf("OPTIONS ");
430     if(am_has_feature(g_options->features, fe_rep_options_features)) {
431         printf("features=%s;", our_feature_string);
432     }
433     if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
434         printf("hostname=%s;", g_options->hostname);
435     }
436     if(am_has_feature(g_options->features, fe_rep_options_sendbackup_options)) {
437         printf("%s", optionstr(options));
438     }
439     printf("\n");
440     fflush(stdout);
441     if (freopen("/dev/null", "w", stdout) == NULL) {
442         dbprintf(("%s: error redirecting stdout to /dev/null: %s\n",
443             debug_prefix_time(NULL), strerror(errno)));
444         exit(1);
445     }
446
447     if(interactive) {
448       if((datafd = open("/dev/null", O_RDWR)) < 0) {
449         s = strerror(errno);
450         error("ERROR [%s: open of /dev/null for debug data stream: %s]\n",
451                   get_pname(), s);
452         /*NOTREACHED*/
453       }
454       mesgfd = 2;
455       indexfd = 1;
456     }
457
458     if(!interactive) {
459       if(datafd == -1 || mesgfd == -1 || (options->createindex && indexfd == -1)) {
460         dbclose();
461         exit(1);
462       }
463     }
464
465     if(!interactive) {
466       /* redirect stderr */
467       if(dup2(mesgfd, 2) == -1) {
468           dbprintf(("%s: error redirecting stderr to fd %d: %s\n",
469               debug_prefix_time(NULL), mesgfd, strerror(errno)));
470           dbclose();
471           exit(1);
472       }
473     }
474
475     if(pipe(mesgpipe) == -1) {
476       error("error [opening mesg pipe: %s]", strerror(errno));
477       /*NOTREACHED*/
478     }
479
480     program->start_backup(g_options->hostname, disk, amdevice, level, dumpdate, datafd, mesgpipe[1],
481                           indexfd);
482     dbprintf(("%s: started backup\n", debug_prefix_time(NULL)));
483     parse_backup_messages(mesgpipe[0]);
484     dbprintf(("%s: parsed backup messages\n", debug_prefix_time(NULL)));
485
486     amfree(prog);
487     amfree(disk);
488     amfree(qdisk);
489     amfree(amdevice);
490     amfree(qamdevice);
491     amfree(dumpdate);
492     amfree(stroptions);
493     amfree(our_feature_string);
494     am_release_feature_set(our_features);
495     our_features = NULL;
496     am_release_feature_set(g_options->features);
497     g_options->features = NULL;
498     amfree(g_options->hostname);
499     amfree(g_options->str);
500     amfree(g_options);
501
502     dbclose();
503
504     malloc_size_2 = malloc_inuse(&malloc_hist_2);
505
506     if(malloc_size_1 != malloc_size_2) {
507         malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
508     }
509
510     return 0;
511
512  err:
513     printf("FORMAT ERROR IN REQUEST PACKET\n");
514     dbprintf(("%s: REQ packet is bogus%s%s\n",
515               debug_prefix_time(NULL),
516               err_extra ? ": " : "",
517               err_extra ? err_extra : ""));
518     dbclose();
519     return 1;
520 }
521
522
523 /*
524  * Returns a string for a child process.  Checks the saved dump and
525  * compress pids to see which it is.
526  */
527
528 char *
529 childstr(
530     pid_t pid)
531 {
532     if(pid == dumppid) return program->backup_name;
533     if(pid == comppid) return "compress";
534     if(pid == encpid) return "encrypt";
535     if(pid == indexpid) return "index";
536     return "unknown";
537 }
538
539
540 /*
541  * Determine if the child return status really indicates an error.
542  * If so, add the error message to the error string; more than one
543  * child can have an error.
544  */
545
546 int
547 check_status(
548     pid_t       pid,
549     amwait_t    w)
550 {
551     char *thiserr = NULL;
552     char *str, *strX;
553     int ret, sig, rc;
554     char number[NUM_STR_SIZE];
555     char numberpid[NUM_STR_SIZE];
556
557     str = childstr(pid);
558
559     if(WIFSIGNALED(w)) {
560         ret = 0;
561         rc = sig = WTERMSIG(w);
562     } else {
563         sig = 0;
564         rc = ret = WEXITSTATUS(w);
565     }
566
567     if(pid == indexpid) {
568         /*
569          * Treat an index failure (other than signal) as a "STRANGE"
570          * rather than an error so the dump goes ahead and gets processed
571          * but the failure is noted.
572          */
573         if(ret != 0) {
574             fprintf(stderr, "? index %s returned %d\n", str, ret);
575             rc = 0;
576         }
577         indexpid = -1;
578         strX = "index ";
579     } else if(pid == comppid) {
580         /*
581          * compress returns 2 sometimes, but it is ok.
582          */
583 #ifndef HAVE_GZIP
584         if(ret == 2) {
585             rc = 0;
586         }
587 #endif
588         comppid = -1;
589         strX = "compress ";
590     } else if(pid == dumppid && tarpid == -1) {
591         /*
592          * Ultrix dump returns 1 sometimes, but it is ok.
593          */
594 #ifdef DUMP_RETURNS_1
595         if(ret == 1) {
596             rc = 0;
597         }
598 #endif
599         dumppid = -1;
600         strX = "dump ";
601     } else if(pid == tarpid) {
602         if (ret == 1) {
603             rc = 0;
604         }
605         /*
606          * tar bitches about active filesystems, but we do not care.
607          */
608 #ifdef IGNORE_TAR_ERRORS
609         if(ret == 2) {
610             rc = 0;
611         }
612 #endif
613         dumppid = tarpid = -1;
614         strX = "dump ";
615     } else {
616         strX = "unknown ";
617     }
618
619     if(rc == 0) {
620         return 0;                               /* normal exit */
621     }
622
623     snprintf(numberpid, SIZEOF(number), "%d", (int)pid);
624     if(ret == 0) {
625         snprintf(number, SIZEOF(number), "%d", sig);
626         thiserr = vstralloc(strX, "(", numberpid, ") ", str, " got signal ", number, NULL);
627     } else {
628         snprintf(number, SIZEOF(number), "%d", ret);
629         thiserr = vstralloc(strX, "(", numberpid, ") ", str, " returned ", number, NULL);
630     }
631
632     if(errorstr) {
633         strappend(errorstr, ", ");
634         strappend(errorstr, thiserr);
635         amfree(thiserr);
636     } else {
637         errorstr = thiserr;
638         thiserr = NULL;
639     }
640     return 1;
641 }
642
643
644 /*
645  *Send header info to the message file.
646  */
647 void
648 info_tapeheader(void)
649 {
650     fprintf(stderr, "%s: info BACKUP=%s\n", get_pname(), program->backup_name);
651
652     fprintf(stderr, "%s: info RECOVER_CMD=", get_pname());
653     if (options->compress == COMPR_FAST || options->compress == COMPR_BEST)
654         fprintf(stderr, "%s %s |", UNCOMPRESS_PATH,
655 #ifdef UNCOMPRESS_OPT
656                 UNCOMPRESS_OPT
657 #else
658                 ""
659 #endif
660                 );
661
662     fprintf(stderr, "%s -f - ...\n", program->restore_name);
663
664     if (options->compress == COMPR_FAST || options->compress == COMPR_BEST)
665         fprintf(stderr, "%s: info COMPRESS_SUFFIX=%s\n",
666                         get_pname(), COMPRESS_SUFFIX);
667
668     fprintf(stderr, "%s: info end\n", get_pname());
669 }
670
671 pid_t
672 pipefork(
673     void        (*func)(void),
674     char *      fname,
675     int *       stdinfd,
676     int         stdoutfd,
677     int         stderrfd)
678 {
679     int inpipe[2];
680     pid_t pid;
681
682     dbprintf(("%s: forking function %s in pipeline\n",
683         debug_prefix_time(NULL), fname));
684
685     if(pipe(inpipe) == -1) {
686         error("error [open pipe to %s: %s]", fname, strerror(errno));
687         /*NOTREACHED*/
688     }
689
690     switch(pid = fork()) {
691     case -1:
692         error("error [fork %s: %s]", fname, strerror(errno));
693         /*NOTREACHED*/
694     default:    /* parent process */
695         aclose(inpipe[0]);      /* close input side of pipe */
696         *stdinfd = inpipe[1];
697         break;
698     case 0:             /* child process */
699         aclose(inpipe[1]);      /* close output side of pipe */
700
701         if(dup2(inpipe[0], 0) == -1) {
702             error("error [fork %s: dup2(%d, in): %s]",
703                   fname, inpipe[0], strerror(errno));
704             /*NOTRACHED*/
705         }
706         if(dup2(stdoutfd, 1) == -1) {
707             error("error [fork %s: dup2(%d, out): %s]",
708                   fname, stdoutfd, strerror(errno));
709             /*NOTRACHED*/
710         }
711         if(dup2(stderrfd, 2) == -1) {
712             error("error [fork %s: dup2(%d, err): %s]",
713                   fname, stderrfd, strerror(errno));
714             /*NOTRACHED*/
715         }
716
717         func();
718         exit(0);
719         /*NOTREACHED*/
720     }
721     return pid;
722 }
723
724 void
725 parse_backup_messages(
726     int         mesgin)
727 {
728     int goterror;
729     pid_t wpid;
730     amwait_t retstat;
731     char *line;
732
733     goterror = 0;
734     amfree(errorstr);
735
736     for(; (line = areads(mesgin)) != NULL; free(line)) {
737         process_dumpline(line);
738     }
739
740     if(errno) {
741         error("error [read mesg pipe: %s]", strerror(errno));
742         /*NOTREACHED*/
743     }
744
745     while((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
746         if(check_status(wpid, retstat)) goterror = 1;
747     }
748
749     if (dumppid != -1) {
750         sleep(5);
751         while((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
752             if(check_status(wpid, retstat)) goterror = 1;
753         }
754     }
755     if (dumppid != -1) {
756         dbprintf(("%s: Sending SIGHUP to dump process %d\n",
757                   debug_prefix_time(NULL), (int)dumppid));
758         if(dumppid != -1) {
759             if(kill(dumppid, SIGHUP) == -1) {
760                 dbprintf(("%s: Can't send SIGHUP to %d: %s\n",
761                           debug_prefix_time(NULL), (int)dumppid,
762                           strerror(errno)));
763             }
764         }
765         sleep(5);
766         while((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
767             if(check_status(wpid, retstat)) goterror = 1;
768         }
769     }
770     if (dumppid != -1) {
771         dbprintf(("%s: Sending SIGKILL to dump process %d\n",
772                   debug_prefix_time(NULL), (int)dumppid));
773         if(dumppid != -1) {
774             if(kill(dumppid, SIGKILL) == -1) {
775                 dbprintf(("%s: Can't send SIGKILL to %d: %s\n",
776                           debug_prefix_time(NULL), (int)dumppid,
777                           strerror(errno)));
778             }
779         }
780         sleep(5);
781         while((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
782             if(check_status(wpid, retstat)) goterror = 1;
783         }
784     }
785
786     if(errorstr) {
787         error("error [%s]", errorstr);
788         /*NOTREACHED*/
789     } else if(dump_size == -1) {
790         error("error [no backup size line]");
791         /*NOTREACHED*/
792     }
793
794     program->end_backup(goterror);
795
796     fprintf(stderr, "%s: size %ld\n", get_pname(), dump_size);
797     fprintf(stderr, "%s: end\n", get_pname());
798 }
799
800
801 /*
802  * Returns the value of the first integer in a string.
803  */
804
805 double
806 first_num(
807     char *      str)
808 {
809     char *num;
810     int ch;
811     double d;
812
813     ch = *str++;
814     while(ch && !isdigit(ch)) ch = *str++;
815     num = str - 1;
816     while(isdigit(ch) || ch == '.') ch = *str++;
817     str[-1] = '\0';
818     d = atof(num);
819     str[-1] = (char)ch;
820     return d;
821 }
822
823
824 static void
825 process_dumpline(
826     char *      str)
827 {
828     amregex_t *rp;
829     char *type;
830     char startchr;
831
832     for(rp = program->re_table; rp->regex != NULL; rp++) {
833         if(match(rp->regex, str)) {
834             break;
835         }
836     }
837     if(rp->typ == DMP_SIZE) {
838         dump_size = (long)((first_num(str) * rp->scale + 1023.0)/1024.0);
839     }
840     switch(rp->typ) {
841     case DMP_NORMAL:
842         type = "normal";
843         startchr = '|';
844         break;
845     case DMP_STRANGE:
846         type = "strange";
847         startchr = '?';
848         break;
849     case DMP_SIZE:
850         type = "size";
851         startchr = '|';
852         break;
853     case DMP_ERROR:
854         type = "error";
855         startchr = '?';
856         break;
857     default:
858         /*
859          * Should never get here.
860          */
861         type = "unknown";
862         startchr = '!';
863         break;
864     }
865     dbprintf(("%s: %3d: %7s(%c): %s\n",
866               debug_prefix_time(NULL),
867               rp->srcline,
868               type,
869               startchr,
870               str));
871     fprintf(stderr, "%c %s\n", startchr, str);
872 }
873
874
875 /*
876  * start_index.  Creates an index file from the output of dump/tar.
877  * It arranges that input is the fd to be written by the dump process.
878  * If createindex is not enabled, it does nothing.  If it is not, a
879  * new process will be created that tees input both to a pipe whose
880  * read fd is dup2'ed input and to a program that outputs an index
881  * file to `index'.
882  *
883  * make sure that the chat from restore doesn't go to stderr cause
884  * this goes back to amanda which doesn't expect to see it
885  * (2>/dev/null should do it)
886  *
887  * Originally by Alan M. McIvor, 13 April 1996
888  *
889  * Adapted by Alexandre Oliva, 1 May 1997
890  *
891  * This program owes a lot to tee.c from GNU sh-utils and dumptee.c
892  * from the DeeJay backup package.
893  */
894
895 static void
896 save_fd(
897     int *       fd,
898     int         min)
899 {
900   int origfd = *fd;
901
902   while (*fd >= 0 && *fd < min) {
903     int newfd = dup(*fd);
904     if (newfd == -1)
905       dbprintf(("%s: unable to save file descriptor [%s]\n",
906         debug_prefix_time(NULL), strerror(errno)));
907     *fd = newfd;
908   }
909   if (origfd != *fd)
910     dbprintf(("%s: dupped file descriptor %i to %i\n",
911       debug_prefix_time(NULL), origfd, *fd));
912 }
913
914 void
915 start_index(
916     int         createindex,
917     int         input,
918     int         mesg,
919     int         index,
920     char *      cmd)
921 {
922   int pipefd[2];
923   FILE *pipe_fp;
924   int exitcode;
925
926   if (!createindex)
927     return;
928
929   if (pipe(pipefd) != 0) {
930     error("creating index pipe: %s", strerror(errno));
931     /*NOTREACHED*/
932   }
933
934   switch(indexpid = fork()) {
935   case -1:
936     error("forking index tee process: %s", strerror(errno));
937     /*NOTREACHED*/
938
939   default:
940     aclose(pipefd[0]);
941     if (dup2(pipefd[1], input) == -1) {
942       error("dup'ping index tee output: %s", strerror(errno));
943       /*NOTREACHED*/
944     }
945     aclose(pipefd[1]);
946     return;
947
948   case 0:
949     break;
950   }
951
952   /* now in a child process */
953   save_fd(&pipefd[0], 4);
954   save_fd(&index, 4);
955   save_fd(&mesg, 4);
956   save_fd(&input, 4);
957   dup2(pipefd[0], 0);
958   dup2(index, 1);
959   dup2(mesg, 2);
960   dup2(input, 3);
961   for(index = 4; index < FD_SETSIZE; index++) {
962     if (index != dbfd()) {
963       close(index);
964     }
965   }
966
967   if ((pipe_fp = popen(cmd, "w")) == NULL) {
968     error("couldn't start index creator [%s]", strerror(errno));
969     /*NOTREACHED*/
970   }
971
972   dbprintf(("%s: started index creator: \"%s\"\n",
973     debug_prefix_time(NULL), cmd));
974   while(1) {
975     char buffer[BUFSIZ], *ptr;
976     ssize_t bytes_read;
977     size_t bytes_written;
978     ssize_t just_written;
979
980     do {
981         bytes_read = read(0, buffer, SIZEOF(buffer));
982     } while ((bytes_read < 0) && ((errno == EINTR) || (errno == EAGAIN)));
983
984     if (bytes_read < 0) {
985       error("index tee cannot read [%s]", strerror(errno));
986       /*NOTREACHED*/
987     }
988
989     if (bytes_read == 0)
990       break; /* finished */
991
992     /* write the stuff to the subprocess */
993     ptr = buffer;
994     bytes_written = 0;
995     just_written = fullwrite(fileno(pipe_fp), ptr, (size_t)bytes_read);
996     if (just_written < 0) {
997         /* 
998          * just as we waited for write() to complete.
999          */
1000         if (errno != EPIPE) {
1001             dbprintf(("%s: index tee cannot write to index creator [%s]\n",
1002                             debug_prefix_time(NULL), strerror(errno)));
1003         }
1004     } else {
1005         bytes_written += just_written;
1006         ptr += just_written;
1007     }
1008
1009     /* write the stuff to stdout, ensuring none lost when interrupt
1010        occurs */
1011     ptr = buffer;
1012     bytes_written = 0;
1013     just_written = fullwrite(3, ptr, (size_t)bytes_read);
1014     if (just_written < 0) {
1015         error("index tee cannot write [%s]", strerror(errno));
1016         /*NOTREACHED*/
1017     } else {
1018         bytes_written += just_written;
1019         ptr += just_written;
1020     }
1021   }
1022
1023   aclose(pipefd[1]);
1024
1025   /* finished */
1026   /* check the exit code of the pipe and moan if not 0 */
1027   if ((exitcode = pclose(pipe_fp)) != 0) {
1028     dbprintf(("%s: index pipe returned %d\n",
1029       debug_prefix_time(NULL), exitcode));
1030   } else {
1031     dbprintf(("%s: index created successfully\n", debug_prefix_time(NULL)));
1032   }
1033   pipe_fp = NULL;
1034
1035   exit(exitcode);
1036 }
1037
1038 extern backup_program_t dump_program, gnutar_program;
1039
1040 backup_program_t *programs[] = {
1041   &dump_program, &gnutar_program, NULL
1042 };