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