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