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