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