6afc7c052b0f6b10025c166aefce79b5e193572f
[debian/amanda] / server-src / dumper.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /* $Id: dumper.c,v 1.75.2.14.2.7.2.17.2.4 2005/09/20 21:31:52 jrjackson Exp $
27  *
28  * requests remote amandad processes to dump filesystems
29  */
30 #include "amanda.h"
31 #include "amindex.h"
32 #include "arglist.h"
33 #include "clock.h"
34 #include "conffile.h"
35 #include "logfile.h"
36 #include "protocol.h"
37 #include "stream.h"
38 #include "token.h"
39 #include "version.h"
40 #include "fileheader.h"
41 #include "amfeatures.h"
42 #include "server_util.h"
43 #include "holding.h"
44
45 #ifdef KRB4_SECURITY
46 #include "dumper-krb4.c"
47 #else
48 #define NAUGHTY_BITS_INITIALIZE         /* I'd tell you what these do */
49 #define NAUGHTY_BITS                    /* but then I'd have to kill you */
50 #endif
51
52
53 #ifndef SEEK_SET
54 #define SEEK_SET 0
55 #endif
56
57 #ifndef SEEK_CUR
58 #define SEEK_CUR 1
59 #endif
60
61 #define CONNECT_TIMEOUT 5*60
62 #define MESGBUF_SIZE    4*1024
63
64 #define STARTUP_TIMEOUT 60
65
66 int interactive;
67 char *handle = NULL;
68
69 char databuf[DISK_BLOCK_BYTES];
70 char mesgbuf[MESGBUF_SIZE+1];
71 char *errstr = NULL;
72 char *datain;                           /* where to read in data */
73 char *dataout;                          /* where to write out data */
74 char *datalimit;                        /* end of the data area */
75 int abort_pending;
76 long dumpsize;                          /* total size of dump */
77 long dumpbytes;
78 long origsize;
79 long filesize;                          /* size of current holding disk file */
80 int nb_header_block;
81 static enum { srvcomp_none, srvcomp_fast, srvcomp_best } srvcompress;
82
83 static FILE *errf = NULL;
84 char *filename = NULL;                  /* holding disk base file name */
85 string_t cont_filename;
86 char *hostname = NULL;
87 am_feature_t *their_features = NULL;
88 char *diskname = NULL;
89 char *device = NULL;
90 char *options = NULL;
91 char *progname = NULL;
92 int level;
93 char *dumpdate = NULL;
94 long chunksize;
95 long use;                               /* space remaining in this hold disk */
96 char *datestamp;
97 char *backup_name = NULL;
98 char *recover_cmd = NULL;
99 char *compress_suffix = NULL;
100 int conf_dtimeout;
101
102 dumpfile_t file;
103 int filename_seq;
104 long split_size;                        /* next dumpsize we will split at */
105
106 int datafd = -1;
107 int mesgfd = -1;
108 int indexfd = -1;
109 int amanda_port;
110
111 static am_feature_t *our_features = NULL;
112 static char *our_feature_string = NULL;
113
114 /* local functions */
115 int main P((int main_argc, char **main_argv));
116 int do_dump P((int mesgfd, int datafd, int indexfd, int outfd));
117 void check_options P((char *options));
118 void service_ports_init P((void));
119 int write_tapeheader P((int outfd, dumpfile_t *type));
120 int write_dataptr P((int outf));
121 int update_dataptr P((int *outf, int size));
122 static void process_dumpeof P((void));
123 static void process_dumpline P((char *str));
124 static void add_msg_data P((char *str, int len));
125 static void log_msgout P((logtype_t typ));
126 void sendbackup_response P((proto_t *p, pkt_t *pkt));
127 int startup_dump P((char *hostname, char *disk, char *device, int level,
128                     char *dumpdate, char *progname, char *options));
129
130
131 void check_options(options)
132 char *options;
133 {
134 #ifdef KRB4_SECURITY
135     krb4_auth = strstr(options, "krb4-auth;") != NULL;
136     kencrypt = strstr(options, "kencrypt;") != NULL;
137 #endif
138     if (strstr(options, "srvcomp-best;") != NULL)
139       srvcompress = srvcomp_best;
140     else if (strstr(options, "srvcomp-fast;") != NULL)
141       srvcompress = srvcomp_fast;
142     else
143       srvcompress = srvcomp_none;
144 }
145
146 void service_ports_init()
147 {
148     struct servent *amandad;
149
150     if((amandad = getservbyname(AMANDA_SERVICE_NAME, "udp")) == NULL) {
151         amanda_port = AMANDA_SERVICE_DEFAULT;
152         log_add(L_WARNING, "no %s/udp service, using default port %d",
153                 AMANDA_SERVICE_NAME, AMANDA_SERVICE_DEFAULT);
154     }
155     else
156         amanda_port = ntohs(amandad->s_port);
157
158 #ifdef KRB4_SECURITY
159     if((amandad = getservbyname(KAMANDA_SERVICE_NAME, "udp")) == NULL) {
160         kamanda_port = KAMANDA_SERVICE_DEFAULT;
161         log_add(L_WARNING, "no %s/udp service, using default port %d",
162                 KAMANDA_SERVICE_NAME, KAMANDA_SERVICE_DEFAULT);
163     }
164     else
165         kamanda_port = ntohs(amandad->s_port);
166 #endif
167 }
168
169
170 int main(main_argc, main_argv)
171 int main_argc;
172 char **main_argv;
173 {
174     struct cmdargs cmdargs;
175     cmd_t cmd;
176     int outfd, protocol_port, taper_port, rc;
177     dgram_t *msg;
178     unsigned long malloc_hist_1, malloc_size_1;
179     unsigned long malloc_hist_2, malloc_size_2;
180     char *conffile;
181     char *q = NULL;
182     char *tmp_filename = NULL, *pc;
183     int a;
184
185     safe_fd(-1, 0);
186
187     set_pname("dumper");
188
189     malloc_size_1 = malloc_inuse(&malloc_hist_1);
190
191     erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
192     set_logerror(logerror);
193
194     if (main_argc > 1) {
195         config_name = stralloc(main_argv[1]);
196         config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
197     } else {
198         char my_cwd[STR_SIZE];
199
200         if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) {
201             error("cannot determine current working directory");
202         }
203         config_dir = stralloc2(my_cwd, "/");
204         if ((config_name = strrchr(my_cwd, '/')) != NULL) {
205             config_name = stralloc(config_name + 1);
206         }
207     }
208
209     safe_cd();
210
211     our_features = am_init_feature_set();
212     our_feature_string = am_feature_to_string(our_features);
213
214     conffile = stralloc2(config_dir, CONFFILE_NAME);
215     if(read_conffile(conffile)) {
216         error("errors processing config file \"%s\"", conffile);
217     }
218     amfree(conffile);
219
220     /* set up dgram port first thing */
221
222     msg = dgram_alloc();
223     if(dgram_bind(msg, &protocol_port) == -1)
224         error("could not bind result datagram port: %s", strerror(errno));
225
226     if(geteuid() == 0) {
227         /* set both real and effective uid's to real uid, likewise for gid */
228         setgid(getgid());
229         setuid(getuid());
230     }
231 #ifdef BSD_SECURITY
232     else error("must be run setuid root to communicate correctly");
233 #endif
234
235     fprintf(stderr,
236             "%s: pid %ld executable %s version %s, using port %d\n",
237             get_pname(), (long) getpid(),
238             main_argv[0], version(), protocol_port);
239     fflush(stderr);
240
241     /* now, make sure we are a valid user */
242
243     if(getpwuid(getuid()) == NULL)
244         error("can't get login name for my uid %ld", (long)getuid());
245
246     signal(SIGPIPE, SIG_IGN);
247
248     interactive = isatty(0);
249
250     amfree(datestamp);
251     datestamp = construct_datestamp(NULL);
252     conf_dtimeout = getconf_int(CNF_DTIMEOUT);
253
254     service_ports_init();
255     proto_init(msg->socket, time(0), 16);
256
257     do {
258         cmd = getcmd(&cmdargs);
259
260         switch(cmd) {
261         case QUIT:
262             break;
263         case FILE_DUMP:
264             /*
265              * FILE-DUMP
266              *   handle
267              *   filename
268              *   host
269              *   features
270              *   disk
271              *   device
272              *   level
273              *   dumpdate
274              *   chunksize
275              *   progname
276              *   use
277              *   options
278              */
279             cmdargs.argc++;                     /* true count of args */
280             a = 2;
281
282             if(a >= cmdargs.argc) {
283                 error("error [dumper FILE-DUMP: not enough args: handle]");
284             }
285             handle = newstralloc(handle, cmdargs.argv[a++]);
286
287             if(a >= cmdargs.argc) {
288                 error("error [dumper FILE-DUMP: not enough args: filename]");
289             }
290             filename = newstralloc(filename, cmdargs.argv[a++]);
291
292             if(a >= cmdargs.argc) {
293                 error("error [dumper FILE-DUMP: not enough args: hostname]");
294             }
295             hostname = newstralloc(hostname, cmdargs.argv[a++]);
296
297             if(a >= cmdargs.argc) {
298                 error("error [dumper FILE-DUMP: not enough args: features]");
299             }
300             am_release_feature_set(their_features);
301             their_features = am_string_to_feature(cmdargs.argv[a++]);
302
303             if(a >= cmdargs.argc) {
304                 error("error [dumper FILE-DUMP: not enough args: diskname]");
305             }
306             diskname = newstralloc(diskname, cmdargs.argv[a++]);
307
308             if(a >= cmdargs.argc) {
309                 error("error [dumper FILE-DUMP: not enough args: device]");
310             }
311             device = newstralloc(device, cmdargs.argv[a++]);
312             if(strcmp(device, "NODEVICE") == 0) amfree(device);
313
314             if(a >= cmdargs.argc) {
315                 error("error [dumper FILE-DUMP: not enough args: level]");
316             }
317             level = atoi(cmdargs.argv[a++]);
318
319             if(a >= cmdargs.argc) {
320                 error("error [dumper FILE-DUMP: not enough args: dumpdate]");
321             }
322             dumpdate = newstralloc(dumpdate, cmdargs.argv[a++]);
323
324             if(a >= cmdargs.argc) {
325                 error("error [dumper FILE-DUMP: not enough args: chunksize]");
326             }
327             chunksize = am_floor(atoi(cmdargs.argv[a++]), DISK_BLOCK_KB);
328
329             if(a >= cmdargs.argc) {
330                 error("error [dumper FILE-DUMP: not enough args: progname]");
331             }
332             progname = newstralloc(progname, cmdargs.argv[a++]);
333
334             if(a >= cmdargs.argc) {
335                 error("error [dumper FILE-DUMP: not enough args: use]");
336             }
337             use = am_floor(atoi(cmdargs.argv[a++]), DISK_BLOCK_KB);
338
339             if(a >= cmdargs.argc) {
340                 error("error [dumper FILE-DUMP: not enough args: options]");
341             }
342             options = newstralloc(options, cmdargs.argv[a++]);
343
344             if(a != cmdargs.argc) {
345                 error("error [dumper FILE-DUMP: too many args: %d != %d]",
346                       cmdargs.argc, a);
347             }
348
349             cont_filename[0] = '\0';
350
351             tmp_filename = newvstralloc(tmp_filename, filename, ".tmp", NULL);
352             pc = strrchr(tmp_filename, '/');
353             *pc = '\0';
354             mkholdingdir(tmp_filename);
355             *pc = '/';
356             outfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
357             if(outfd == -1) {
358                 int save_errno = errno;
359                 q = squotef("[main holding file \"%s\": %s]",
360                             tmp_filename, strerror(errno));
361                 if(save_errno == ENOSPC) {
362                     putresult(NO_ROOM, "%s %lu\n", handle, use);
363                     putresult(TRYAGAIN, "%s %s\n", handle, q);
364                 }
365                 else {
366                     putresult(FAILED, "%s %s\n", handle, q);
367                 }
368                 amfree(q);
369                 break;
370             }
371             filename_seq = 1;
372
373             check_options(options);
374
375             rc = startup_dump(hostname,
376                               diskname,
377                               device,
378                               level,
379                               dumpdate,
380                               progname,
381                               options);
382             if(rc) {
383                 q = squote(errstr);
384                 putresult(rc == 2? FAILED : TRYAGAIN, "%s %s\n",
385                           handle, q);
386                 if(rc == 2) {
387                     log_add(L_FAIL, "%s %s %s %d [%s]", hostname, diskname,
388                             datestamp, level, errstr);
389                 }
390                 amfree(q);
391                 /* do need to close if TRY-AGAIN, doesn't hurt otherwise */
392                 if (mesgfd != -1)
393                     aclose(mesgfd);
394                 if (datafd != -1)
395                     aclose(datafd);
396                 if (indexfd != -1)
397                     aclose(indexfd);
398                 if (outfd != -1)
399                     aclose(outfd);
400                 break;
401             }
402
403             abort_pending = 0;
404             split_size = (chunksize>use)?use:chunksize;
405             use -= split_size;
406             if(do_dump(mesgfd, datafd, indexfd, outfd)) {
407             }
408             aclose(mesgfd);
409             aclose(datafd);
410             if (indexfd != -1)
411                 aclose(indexfd);
412             aclose(outfd);
413             if(abort_pending) putresult(ABORT_FINISHED, "%s\n", handle);
414             break;
415
416         case PORT_DUMP:
417             /*
418              * PORT-DUMP
419              *   handle
420              *   port
421              *   host
422              *   features
423              *   disk
424              *   device
425              *   level
426              *   dumpdate
427              *   progname
428              *   options
429              */
430             cmdargs.argc++;                     /* true count of args */
431             a = 2;
432
433             if(a >= cmdargs.argc) {
434                 error("error [dumper PORT-DUMP: not enough args: handle]");
435             }
436             handle = newstralloc(handle, cmdargs.argv[a++]);
437
438             if(a >= cmdargs.argc) {
439                 error("error [dumper PORT-DUMP: not enough args: port]");
440             }
441             taper_port = atoi(cmdargs.argv[a++]);
442
443             if(a >= cmdargs.argc) {
444                 error("error [dumper PORT-DUMP: not enough args: hostname]");
445             }
446             hostname = newstralloc(hostname, cmdargs.argv[a++]);
447
448             if(a >= cmdargs.argc) {
449                 error("error [dumper PORT-DUMP: not enough args: features]");
450             }
451             am_release_feature_set(their_features);
452             their_features = am_string_to_feature(cmdargs.argv[a++]);
453
454             if(a >= cmdargs.argc) {
455                 error("error [dumper PORT-DUMP: not enough args: diskname]");
456             }
457             diskname = newstralloc(diskname, cmdargs.argv[a++]);
458
459             if(a >= cmdargs.argc) {
460                 error("error [dumper PORT-DUMP: not enough args: device]");
461             }
462             device = newstralloc(device, cmdargs.argv[a++]);
463             if(strcmp(device,"NODEVICE") == 0) amfree(device);
464
465             if(a >= cmdargs.argc) {
466                 error("error [dumper PORT-DUMP: not enough args: level]");
467             }
468             level = atoi(cmdargs.argv[a++]);
469
470             if(a >= cmdargs.argc) {
471                 error("error [dumper PORT-DUMP: not enough args: dumpdate]");
472             }
473             dumpdate = newstralloc(dumpdate, cmdargs.argv[a++]);
474
475             if(a >= cmdargs.argc) {
476                 error("error [dumper PORT-DUMP: not enough args: progname]");
477             }
478             progname = newstralloc(progname, cmdargs.argv[a++]);
479
480             if(a >= cmdargs.argc) {
481                 error("error [dumper PORT-DUMP: not enough args: options]");
482             }
483             options = newstralloc(options, cmdargs.argv[a++]);
484
485             if(a != cmdargs.argc) {
486                 error("error [dumper PORT-DUMP: too many args: %d != %d]",
487                       cmdargs.argc, a);
488             }
489
490             filename = newstralloc(filename, "<taper program>");
491             cont_filename[0] = '\0';
492
493             /* connect outf to taper port */
494
495             outfd = stream_client("localhost", taper_port,
496                                   STREAM_BUFSIZE, -1, NULL);
497             if(outfd == -1) {
498                 q = squotef("[taper port open: %s]", strerror(errno));
499                 putresult(FAILED, "%s %s\n", handle, q);
500                 amfree(q);
501                 break;
502             }
503             filename_seq = 1;
504
505             check_options(options);
506
507             rc = startup_dump(hostname,
508                               diskname,
509                               device,
510                               level,
511                               dumpdate,
512                               progname,
513                               options);
514             if(rc) {
515                 q = squote(errstr);
516                 putresult(rc == 2? FAILED : TRYAGAIN, "%s %s\n",
517                           handle, q);
518                 if(rc == 2) {
519                     log_add(L_FAIL, "%s %s %s %d [%s]", hostname, diskname,
520                             datestamp, level, errstr);
521                 }
522                 amfree(q);
523                 /* do need to close if TRY-AGAIN, doesn't hurt otherwise */
524                 if (mesgfd != -1)
525                     aclose(mesgfd);
526                 if (datafd != -1)
527                     aclose(datafd);
528                 if (indexfd != -1)
529                     aclose(indexfd);
530                 if (outfd != -1)
531                     aclose(outfd);
532                 break;
533             }
534
535             abort_pending = 0;
536             split_size = -1;
537             if(do_dump(mesgfd, datafd, indexfd, outfd)) {
538             }
539             aclose(mesgfd);
540             aclose(datafd);
541             if (indexfd != -1)
542                 aclose(indexfd);
543             aclose(outfd);
544             if(abort_pending) putresult(ABORT_FINISHED, "%s\n", handle);
545             break;
546
547         default:
548             if(cmdargs.argc >= 1) {
549                 q = squote(cmdargs.argv[1]);
550             } else if(cmdargs.argc >= 0) {
551                 q = squote(cmdargs.argv[0]);
552             } else {
553                 q = stralloc("(no input?)");
554             }
555             putresult(BAD_COMMAND, "%s\n", q);
556             amfree(q);
557         }
558         while(wait(NULL) != -1);
559     } while(cmd != QUIT);
560
561     amfree(tmp_filename);
562     amfree(errstr);
563     amfree(msg);
564     amfree(datestamp);
565     amfree(backup_name);
566     amfree(recover_cmd);
567     amfree(compress_suffix);
568     amfree(handle);
569     amfree(filename);
570     amfree(hostname);
571     amfree(diskname);
572     amfree(device);
573     amfree(dumpdate);
574     amfree(progname);
575     amfree(options);
576     amfree(config_dir);
577     amfree(config_name);
578     amfree(our_feature_string);
579     am_release_feature_set(our_features);
580     our_features = NULL;
581     am_release_feature_set(their_features);
582     their_features = NULL;
583
584     malloc_size_2 = malloc_inuse(&malloc_hist_2);
585
586     if(malloc_size_1 != malloc_size_2) {
587         malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
588     }
589
590     return 0;
591 }
592
593
594 int write_dataptr(outf)
595 int outf;
596 {
597     int w;
598     int written;
599
600     written = w = 0;
601     while(dataout < datain) {
602         if((w = write(outf, dataout, datain - dataout)) < 0) {
603             break;
604         }
605         dataout += w;
606         written += w;
607     }
608     dumpbytes += written;
609     dumpsize += (dumpbytes / 1024);
610     filesize += (dumpbytes / 1024);
611     dumpbytes %= 1024;
612     if(w < 0) {
613         if(errno != ENOSPC) {
614             errstr = squotef("data write: %s", strerror(errno));
615             return 1;
616         }
617         /*
618          * NO-ROOM is informational only.  Later, RQ_MORE_DISK will be
619          * issued to use another holding disk.
620          */
621         putresult(NO_ROOM, "%s %lu\n", handle, use+split_size-dumpsize);
622         use = 0;                                /* force RQ_MORE_DISK */
623         split_size = dumpsize;
624     }
625     if(dataout == datain) {
626         /*
627          * We flushed the whole buffer so reset to use it all.
628          */
629         dataout = datain = databuf;
630     }
631     return 0;
632 }
633
634 int update_dataptr(p_outfd, size)
635 int *p_outfd, size;
636 /*
637  * Updates the buffer pointer for the input data buffer.  The buffer is
638  * written if it is full or we are at EOF.
639  */
640 {
641     int outfd = *p_outfd;
642     int rc = 0;
643     char *arg_filename = NULL;
644     char *new_filename = NULL;
645     char *tmp_filename = NULL;
646     char *pc;
647     char sequence[NUM_STR_SIZE];
648     int new_outfd = -1;
649     struct cmdargs cmdargs;
650     cmd_t cmd;
651     filetype_t save_type;
652     long left_in_chunk;
653     int a;
654     char *q;
655
656     datain += size;
657
658     while(rc == 0 && ((size == 0 && dataout < datain) || datain >= datalimit)) {
659
660         NAUGHTY_BITS;
661
662         /* We open a new chunkfile if                                    */
663         /*   We have something to write (dataout < datain)               */
664         /*   We have a split_size defined (split_size > 0)               */
665         /*   The current file is already filled (dumpsize >= split_size) */
666
667         while(dataout < datain && split_size > 0 && dumpsize >= split_size) {
668             amfree(new_filename);
669             if(use == 0) {
670                 /*
671                  * Probably no more space on this disk.  Request more.
672                  */
673                 putresult(RQ_MORE_DISK, "%s\n", handle);
674                 cmd = getcmd(&cmdargs);
675                 if(cmd == CONTINUE) {
676                     /*
677                      * CONTINUE
678                      *   serial
679                      *   filename
680                      *   chunksize
681                      *   use
682                      */
683                     cmdargs.argc++;             /* true count of args */
684                     a = 3;
685
686                     if(a >= cmdargs.argc) {
687                         error("error [dumper CONTINUE: not enough args: filename]");
688                     }
689                     arg_filename = newstralloc(arg_filename, cmdargs.argv[a++]);
690
691                     if(a >= cmdargs.argc) {
692                         error("error [dumper CONTINUE: not enough args: chunksize]");
693                     }
694                     chunksize = atoi(cmdargs.argv[a++]);
695                     chunksize = am_floor(chunksize, DISK_BLOCK_KB);
696
697                     if(a >= cmdargs.argc) {
698                         error("error [dumper CONTINUE: not enough args: use]");
699                     }
700                     use = atoi(cmdargs.argv[a++]);
701
702                     if(a != cmdargs.argc) {
703                         error("error [dumper CONTINUE: too many args: %d != %d]",
704                               cmdargs.argc, a);
705                     }
706
707                     if(strcmp(filename, arg_filename) == 0) {
708                         /*
709                          * Same disk, so use what room is left up to the
710                          * next chunk boundary or the amount we were given,
711                          * whichever is less.
712                          */
713                         left_in_chunk = chunksize - filesize;
714                         if(left_in_chunk > use) {
715                             split_size += use;
716                             use = 0;
717                         } else {
718                             split_size += left_in_chunk;
719                             use -= left_in_chunk;
720                         }
721                         if(left_in_chunk > 0) {
722                             /*
723                              * We still have space in this chunk.
724                              */
725                             break;
726                         }
727                     } else {
728                         /*
729                          * Different disk, so use new file.
730                          */
731                         filename = newstralloc(filename, arg_filename);
732                     }
733                 } else if(cmd == ABORT) {
734                     abort_pending = 1;
735                     errstr = newstralloc(errstr, "ERROR");
736                     rc = 1;
737                     goto common_exit;
738                 } else {
739                     if(cmdargs.argc >= 1) {
740                         q = squote(cmdargs.argv[1]);
741                     } else if(cmdargs.argc >= 0) {
742                         q = squote(cmdargs.argv[0]);
743                     } else {
744                         q = stralloc("(no input?)");
745                     }
746                     error("error [bad command after RQ-MORE-DISK: \"%s\"]", q);
747                 }
748             }
749
750             ap_snprintf(sequence, sizeof(sequence), "%d", filename_seq);
751             new_filename = newvstralloc(new_filename,
752                                         filename,
753                                         ".",
754                                         sequence,
755                                         NULL);
756             tmp_filename = newvstralloc(tmp_filename,
757                                         new_filename,
758                                         ".tmp",
759                                         NULL);
760             pc = strrchr(tmp_filename, '/');
761             *pc = '\0';
762             mkholdingdir(tmp_filename);
763             *pc = '/';
764             new_outfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
765             if(new_outfd == -1) {
766                 int save_errno = errno;
767
768                 errstr = squotef("creating chunk holding file \"%s\": %s",
769                                  tmp_filename,
770                                  strerror(save_errno));
771                 if(save_errno == ENOSPC) {
772                     putresult(NO_ROOM, "%s %lu\n",
773                               handle, 
774                               use + split_size - dumpsize);
775                     use = 0;            /* force RQ_MORE_DISK */
776                     split_size = dumpsize;
777                     continue;
778                 }
779                 aclose(outfd);
780                 rc = 1;
781                 goto common_exit;
782             }
783             save_type = file.type;
784             file.type = F_CONT_DUMPFILE;
785             file.cont_filename[0] = '\0';
786             if(write_tapeheader(new_outfd, &file)) {
787                 int save_errno = errno;
788
789                 aclose(new_outfd);
790                 unlink(tmp_filename);
791                 if(save_errno == ENOSPC) {
792                     putresult(NO_ROOM, "%s %lu\n",
793                               handle, 
794                               use + split_size - dumpsize);
795                     use = 0;                    /* force RQ_MORE_DISK */
796                     split_size = dumpsize;
797                     continue;
798                 }
799                 errstr = squotef("write_tapeheader file \"%s\": %s",
800                                  tmp_filename, strerror(errno));
801                 rc = 1;
802                 goto common_exit;
803             }
804             if(lseek(outfd, (off_t)0, SEEK_SET) != 0) {
805                 errstr = squotef("cannot lseek: %s", strerror(errno));
806                 aclose(new_outfd);
807                 unlink(tmp_filename);
808                 rc = 1;
809                 goto common_exit;
810             }
811             strncpy(file.cont_filename, new_filename, 
812                     sizeof(file.cont_filename));
813             file.cont_filename[sizeof(file.cont_filename)-1] = '\0';
814             file.type = save_type;
815             if(write_tapeheader(outfd, &file)) {
816                 errstr = squotef("write_tapeheader file linked to \"%s\": %s",
817                                  tmp_filename, strerror(errno));
818                 aclose(new_outfd);
819                 unlink(tmp_filename);
820                 rc = 1;
821                 goto common_exit;
822             }
823             file.type = F_CONT_DUMPFILE;
824             strncpy(cont_filename, new_filename, sizeof(cont_filename));
825             cont_filename[sizeof(cont_filename)-1] = '\0';
826
827             aclose(outfd);
828             *p_outfd = outfd = new_outfd;
829             new_outfd = -1;
830
831             dumpsize += DISK_BLOCK_KB;
832             filesize = DISK_BLOCK_KB;
833             split_size += (chunksize>use)?use:chunksize;
834             use = (chunksize>use)?0:use-chunksize;
835             nb_header_block++;
836             filename_seq++;
837         }
838         rc = write_dataptr(outfd);
839     }
840
841 common_exit:
842
843     amfree(new_filename);
844     amfree(tmp_filename);
845     amfree(arg_filename);
846     return rc;
847 }
848
849
850 static char *msgbuf = NULL;
851 int got_info_endline;
852 int got_sizeline;
853 int got_endline;
854 int dump_result;
855
856 static void process_dumpeof()
857 {
858     /* process any partial line in msgbuf? !!! */
859     if(msgbuf != NULL) {
860         fprintf(errf,"? %s: error [partial line in msgbuf: %ld bytes]\n",
861                 get_pname(), (long) strlen(msgbuf));
862         fprintf(errf,"? %s: error [partial line in msgbuf: \"%s\"]\n",
863                 get_pname(), msgbuf);
864     }
865     if(!got_sizeline && dump_result < 2) {
866         /* make a note if there isn't already a failure */
867         fprintf(errf,"? %s: strange [missing size line from sendbackup]\n",
868                 get_pname());
869         dump_result = max(dump_result, 2);
870     }
871
872     if(!got_endline && dump_result < 2) {
873         fprintf(errf,"? %s: strange [missing end line from sendbackup]\n",
874                 get_pname());
875         dump_result = max(dump_result, 2);
876     }
877 }
878
879 /* Parse an information line from the client.
880 ** We ignore unknown parameters and only remember the last
881 ** of any duplicates.
882 */
883 static void parse_info_line(str)
884 char *str;
885 {
886     if(strcmp(str, "end") == 0) {
887         got_info_endline = 1;
888         return;
889     }
890
891 #define sc "BACKUP="
892     if(strncmp(str, sc, sizeof(sc)-1) == 0) {
893         backup_name = newstralloc(backup_name, str + sizeof(sc)-1);
894         return;
895     }
896 #undef sc
897
898 #define sc "RECOVER_CMD="
899     if(strncmp(str, sc, sizeof(sc)-1) == 0) {
900         recover_cmd = newstralloc(recover_cmd, str + sizeof(sc)-1);
901         return;
902     }
903 #undef sc
904
905 #define sc "COMPRESS_SUFFIX="
906     if(strncmp(str, sc, sizeof(sc)-1) == 0) {
907         compress_suffix = newstralloc(compress_suffix, str + sizeof(sc)-1);
908         return;
909     }
910 #undef sc
911 }
912
913 static void process_dumpline(str)
914 char *str;
915 {
916     char *s, *fp;
917     int ch;
918
919     s = str;
920     ch = *s++;
921
922     switch(ch) {
923     case '|':
924         /* normal backup output line */
925         break;
926     case '?':
927         /* sendbackup detected something strange */
928         dump_result = max(dump_result, 1);
929         break;
930     case 's':
931         /* a sendbackup line, just check them all since there are only 5 */
932 #define sc "sendbackup: start"
933         if(strncmp(str, sc, sizeof(sc)-1) == 0) {
934             break;
935         }
936 #undef sc
937 #define sc "sendbackup: size"
938         if(strncmp(str, sc, sizeof(sc)-1) == 0) {
939             s += sizeof(sc)-1;
940             ch = s[-1];
941             skip_whitespace(s, ch);
942             if(ch) {
943                 origsize = (long)atof(str + sizeof(sc)-1);
944                 got_sizeline = 1;
945                 break;
946             }
947         }
948 #undef sc
949 #define sc "sendbackup: end"
950         if(strncmp(str, sc, sizeof(sc)-1) == 0) {
951             got_endline = 1;
952             break;
953         }
954 #undef sc
955 #define sc "sendbackup: warning"
956         if(strncmp(str, sc, sizeof(sc)-1) == 0) {
957             dump_result = max(dump_result, 1);
958             break;
959         }
960 #undef sc
961 #define sc "sendbackup: error"
962         if(strncmp(str, sc, sizeof(sc)-1) == 0) {
963             s += sizeof(sc)-1;
964             ch = s[-1];
965 #undef sc
966             got_endline = 1;
967             dump_result = max(dump_result, 2);
968             skip_whitespace(s, ch);
969             if(ch == '\0' || ch != '[') {
970                 errstr = newvstralloc(errstr,
971                                       "bad remote error: ", str,
972                                       NULL);
973             } else {
974                 ch = *s++;
975                 fp = s - 1;
976                 while(ch && ch != ']') ch = *s++;
977                 s[-1] = '\0';
978                 errstr = newstralloc(errstr, fp);
979                 s[-1] = ch;
980             }
981             break;
982         }
983 #define sc "sendbackup: info"
984         if(strncmp(str, sc, sizeof(sc)-1) == 0) {
985             s += sizeof(sc)-1;
986             ch = s[-1];
987             skip_whitespace(s, ch);
988             parse_info_line(s - 1);
989             break;
990         }
991 #undef sc
992         /* else we fall through to bad line */
993     default:
994         fprintf(errf, "??%s", str);
995         dump_result = max(dump_result, 1);
996         return;
997     }
998     fprintf(errf, "%s\n", str);
999 }
1000
1001 static void add_msg_data(str, len)
1002 char *str;
1003 int len;
1004 {
1005     char *t;
1006     char *nl;
1007
1008     while(len > 0) {
1009         if((nl = strchr(str, '\n')) != NULL) {
1010             *nl = '\0';
1011         }
1012         if(msgbuf) {
1013             t = stralloc2(msgbuf, str);
1014             amfree(msgbuf);
1015             msgbuf = t;
1016         } else if(nl == NULL) {
1017             msgbuf = stralloc(str);
1018         } else {
1019             msgbuf = str;
1020         }
1021         if(nl == NULL) break;
1022         process_dumpline(msgbuf);
1023         if(msgbuf != str) free(msgbuf);
1024         msgbuf = NULL;
1025         len -= nl + 1 - str;
1026         str = nl + 1;
1027     }
1028 }
1029
1030
1031 static void log_msgout(typ)
1032 logtype_t typ;
1033 {
1034     char *line = NULL;
1035
1036     fflush(errf);
1037     (void) fseek(errf, 0L, SEEK_SET);
1038     for(; (line = agets(errf)) != NULL; free(line)) {
1039         log_add(typ, "%s", line);
1040     }
1041     afclose(errf);
1042 }
1043
1044 /* ------------- */
1045
1046 void make_tapeheader(file, type)
1047 dumpfile_t *file;
1048 filetype_t type;
1049 {
1050     fh_init(file);
1051     file->type = type;
1052     strncpy(file->datestamp  , datestamp  , sizeof(file->datestamp)-1);
1053     file->datestamp[sizeof(file->datestamp)-1] = '\0';
1054     strncpy(file->name       , hostname   , sizeof(file->name)-1);
1055     file->name[sizeof(file->name)-1] = '\0';
1056     strncpy(file->disk       , diskname   , sizeof(file->disk)-1);
1057     file->disk[sizeof(file->disk)-1] = '\0';
1058     file->dumplevel = level;
1059     strncpy(file->program    , backup_name, sizeof(file->program)-1);
1060     file->program[sizeof(file->program)-1] = '\0';
1061     strncpy(file->recover_cmd, recover_cmd, sizeof(file->recover_cmd)-1);
1062     file->recover_cmd[sizeof(file->recover_cmd)-1] = '\0';
1063     file->blocksize = DISK_BLOCK_BYTES;
1064
1065     if (srvcompress) {
1066         file->compressed=1;
1067         ap_snprintf(file->uncompress_cmd, sizeof(file->uncompress_cmd),
1068                     " %s %s |", UNCOMPRESS_PATH,
1069 #ifdef UNCOMPRESS_OPT
1070                     UNCOMPRESS_OPT
1071 #else
1072                     ""
1073 #endif
1074                     );
1075         strncpy(file->comp_suffix, COMPRESS_SUFFIX,sizeof(file->comp_suffix)-1);
1076         file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
1077     }
1078     else {
1079         file->uncompress_cmd[0] = '\0';
1080         file->compressed=compress_suffix!=NULL;
1081         if(compress_suffix) {
1082             strncpy(file->comp_suffix, compress_suffix,
1083                     sizeof(file->comp_suffix)-1);
1084             file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
1085         } else {
1086             strncpy(file->comp_suffix, "N", sizeof(file->comp_suffix)-1);
1087             file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
1088         }
1089     }
1090     strncpy(file->cont_filename, cont_filename, sizeof(file->cont_filename)-1);
1091     file->cont_filename[sizeof(file->cont_filename)-1] = '\0';
1092 }
1093
1094 /* Send an Amanda dump header to the output file.
1095  * returns true if an error occured, false on success
1096  */
1097
1098 int write_tapeheader(outfd, file)
1099 int outfd;
1100 dumpfile_t *file;
1101 {
1102     char buffer[DISK_BLOCK_BYTES];
1103     int  written;
1104
1105     build_header(buffer, file, sizeof(buffer));
1106
1107     written = fullwrite(outfd, buffer, sizeof(buffer));
1108     if(written == sizeof(buffer)) return 0;
1109     if(written < 0) return written;
1110     errno = ENOSPC;
1111     return -1;
1112 }
1113
1114
1115 int do_dump(mesgfd, datafd, indexfd, outfd)
1116 int mesgfd, datafd, indexfd, outfd;
1117 {
1118     int maxfd, nfound, size1, size2, eof1, eof2;
1119     int rc;
1120     fd_set readset, selectset;
1121     struct timeval timeout;
1122     int outpipe[2];
1123     int header_done;    /* flag - header has been written */
1124     char *indexfile_tmp = NULL;
1125     char *indexfile_real = NULL;
1126     char level_str[NUM_STR_SIZE];
1127     char kb_str[NUM_STR_SIZE];
1128     char kps_str[NUM_STR_SIZE];
1129     char orig_kb_str[NUM_STR_SIZE];
1130     char *fn;
1131     char *q;
1132     times_t runtime;
1133     double dumptime;    /* Time dump took in secs */
1134     int compresspid = -1, indexpid = -1, killerr;
1135     char *errfname = NULL;
1136
1137 #ifndef DUMPER_SOCKET_BUFFERING
1138 #define DUMPER_SOCKET_BUFFERING 0
1139 #endif
1140
1141 #if !defined(SO_RCVBUF) || !defined(SO_RCVLOWAT)
1142 #undef  DUMPER_SOCKET_BUFFERING
1143 #define DUMPER_SOCKET_BUFFERING 0
1144 #endif
1145
1146 #if DUMPER_SOCKET_BUFFERING
1147     int lowat = NETWORK_BLOCK_BYTES;
1148     int recbuf = 0;
1149     int sizeof_recbuf = sizeof(recbuf);
1150     int lowwatset = 0;
1151     int lowwatset_count = 0;
1152 #endif
1153
1154     startclock();
1155
1156     datain = dataout = databuf;
1157     datalimit = databuf + sizeof(databuf);
1158     dumpsize = dumpbytes = origsize = filesize = dump_result = 0;
1159     nb_header_block = 0;
1160     got_info_endline = got_sizeline = got_endline = 0;
1161     header_done = 0;
1162     amfree(backup_name);
1163     amfree(recover_cmd);
1164     amfree(compress_suffix);
1165
1166     ap_snprintf(level_str, sizeof(level_str), "%d", level);
1167     fn = sanitise_filename(diskname);
1168     errfname = newvstralloc(errfname,
1169                             AMANDA_TMPDIR,
1170                             "/", hostname,
1171                             ".", fn,
1172                             ".", level_str,
1173                             ".errout",
1174                             NULL);
1175     amfree(fn);
1176     if((errf = fopen(errfname, "w+")) == NULL) {
1177         errstr = newvstralloc(errstr,
1178                               "errfile open \"", errfname, "\": ",
1179                               strerror(errno),
1180                               NULL);
1181         amfree(errfname);
1182         rc = 2;
1183         goto failed;
1184     }
1185     unlink(errfname);                           /* so it goes away on close */
1186     amfree(errfname);
1187
1188     /* insert pipe in the *READ* side, if server-side compression is desired */
1189     compresspid = -1;
1190     if (srvcompress) {
1191         int tmpfd;
1192
1193         tmpfd = datafd;
1194         pipe(outpipe); /* outpipe[0] is pipe's stdin, outpipe[1] is stdout. */
1195         datafd = outpipe[0];
1196         if(datafd < 0 || datafd >= FD_SETSIZE) {
1197             aclose(outpipe[0]);
1198             aclose(outpipe[1]);
1199             errstr = newstralloc(errstr, "descriptor out of range");
1200             errno = EMFILE;
1201             rc = 2;
1202             goto failed;
1203         }
1204         switch(compresspid=fork()) {
1205         case -1:
1206             errstr = newstralloc2(errstr, "couldn't fork: ", strerror(errno));
1207             rc = 2;
1208             goto failed;
1209         default:
1210             aclose(outpipe[1]);
1211             aclose(tmpfd);
1212             break;
1213         case 0:
1214             aclose(outpipe[0]);
1215             /* child acts on stdin/stdout */
1216             if (dup2(outpipe[1],1) == -1)
1217                 fprintf(stderr, "err dup2 out: %s\n", strerror(errno));
1218             if (dup2(tmpfd, 0) == -1)
1219                 fprintf(stderr, "err dup2 in: %s\n", strerror(errno));
1220             safe_fd(-1, 0);
1221             /* now spawn gzip -1 to take care of the rest */
1222             execlp(COMPRESS_PATH, COMPRESS_PATH,
1223                    (srvcompress == srvcomp_best ? COMPRESS_BEST_OPT
1224                                                 : COMPRESS_FAST_OPT),
1225                    (char *)0);
1226             error("error: couldn't exec %s.\n", COMPRESS_PATH);
1227         }
1228         /* Now the pipe has been inserted. */
1229     }
1230
1231     indexpid = -1;
1232     if (indexfd != -1) {
1233         indexfile_real = getindexfname(hostname, diskname, datestamp, level),
1234         indexfile_tmp = stralloc2(indexfile_real, ".tmp");
1235
1236         if (mkpdir(indexfile_tmp, 02755, (uid_t)-1, (gid_t)-1) == -1) {
1237            errstr = newvstralloc(errstr,
1238                                  "err create ",
1239                                  indexfile_tmp,
1240                                  ": ",
1241                                  strerror(errno),
1242                                  NULL);
1243            amfree(indexfile_real);
1244            amfree(indexfile_tmp);
1245            rc = 2;
1246            goto failed;
1247         }
1248
1249         switch(indexpid=fork()) {
1250         case -1:
1251             errstr = newstralloc2(errstr, "couldn't fork: ", strerror(errno));
1252             rc = 2;
1253             goto failed;
1254         default:
1255             aclose(indexfd);
1256             indexfd = -1;                       /* redundant */
1257             break;
1258         case 0:
1259             if (dup2(indexfd, 0) == -1) {
1260                 error("err dup2 in: %s", strerror(errno));
1261             }
1262             indexfd = open(indexfile_tmp, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1263             if (indexfd == -1)
1264                 error("err open %s: %s", indexfile_tmp, strerror(errno));
1265             if (dup2(indexfd,1) == -1)
1266                 error("err dup2 out: %s", strerror(errno));
1267             safe_fd(-1, 0);
1268             execlp(COMPRESS_PATH, COMPRESS_PATH, COMPRESS_BEST_OPT, (char *)0);
1269             error("error: couldn't exec %s.", COMPRESS_PATH);
1270         }
1271     }
1272
1273     NAUGHTY_BITS_INITIALIZE;
1274
1275     maxfd = max(mesgfd, datafd) + 1;
1276     eof1 = eof2 = 0;
1277
1278     FD_ZERO(&readset);
1279
1280     /* Just process messages for now.  Once we have done the header
1281     ** we will start processing data too.
1282     */
1283     FD_SET(mesgfd, &readset);
1284
1285     if(datafd == -1) eof1 = 1;  /* fake eof on data */
1286
1287 #if DUMPER_SOCKET_BUFFERING
1288
1289 #ifndef EST_PACKET_SIZE
1290 #define EST_PACKET_SIZE 512
1291 #endif
1292 #ifndef EST_MIN_WINDOW
1293 #define EST_MIN_WINDOW  EST_PACKET_SIZE*4 /* leave room for 2k in transit */
1294 #endif
1295
1296     else {
1297         recbuf = STREAM_BUFSIZE;
1298         if (setsockopt(datafd, SOL_SOCKET, SO_RCVBUF,
1299                        (void *) &recbuf, sizeof_recbuf)) {
1300             const int errornumber = errno;
1301             fprintf(stderr, "%s: pid %ld setsockopt(SO_RCVBUF): %s\n",
1302                     get_pname(), (long) getpid(), strerror(errornumber));
1303         }
1304         if (getsockopt(datafd, SOL_SOCKET, SO_RCVBUF,
1305                        (void *) &recbuf, (void *)&sizeof_recbuf)) {
1306             const int errornumber = errno;
1307             fprintf(stderr, "%s: pid %ld getsockopt(SO_RCVBUF): %s\n",
1308                     get_pname(), (long) getpid(), strerror(errornumber));
1309             recbuf = 0;
1310         }
1311
1312         /* leave at least EST_MIN_WINDOW between lowwat and recbuf */
1313         if (recbuf-lowat < EST_MIN_WINDOW)
1314             lowat = recbuf-EST_MIN_WINDOW;
1315
1316         /* if lowwat < ~512, don't bother */
1317         if (lowat < EST_PACKET_SIZE)
1318             recbuf = 0;
1319         fprintf(stderr, "%s: pid %ld receive size is %d, low water is %d\n",
1320                 get_pname(), (long) getpid(), recbuf, lowat);
1321     }
1322 #endif
1323
1324     while(!(eof1 && eof2)) {
1325
1326 #if DUMPER_SOCKET_BUFFERING
1327         /* Set socket buffering */
1328         if (recbuf>0 && !lowwatset) {
1329             if (setsockopt(datafd, SOL_SOCKET, SO_RCVLOWAT,
1330                            (void *) &lowat, sizeof(lowat))) {
1331                 const int errornumber = errno;
1332                 fprintf(stderr,
1333                         "%s: pid %ld setsockopt(SO_RCVLOWAT): %s\n",
1334                         get_pname(), (long) getpid(), strerror(errornumber));
1335             }
1336             lowwatset = 1;
1337             lowwatset_count++;
1338         }
1339 #endif
1340
1341         timeout.tv_sec = conf_dtimeout;
1342         timeout.tv_usec = 0;
1343         memcpy(&selectset, &readset, sizeof(fd_set));
1344
1345         nfound = select(maxfd, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL, &timeout);
1346
1347         /* check for errors or timeout */
1348
1349 #if DUMPER_SOCKET_BUFFERING
1350         if (nfound==0 && lowwatset) {
1351             const int zero = 0;
1352             /* Disable socket buffering and ... */
1353             if (setsockopt(datafd, SOL_SOCKET, SO_RCVLOWAT,
1354                            (void *) &zero, sizeof(zero))) {
1355                 const int errornumber = errno;
1356                 fprintf(stderr,
1357                         "%s: pid %ld setsockopt(SO_RCVLOWAT): %s\n",
1358                         get_pname(), (long) getpid(), strerror(errornumber));
1359             }
1360             lowwatset = 0;
1361
1362             /* ... try once more */
1363             timeout.tv_sec = conf_dtimeout;
1364             timeout.tv_usec = 0;
1365             memcpy(&selectset, &readset, sizeof(fd_set));
1366             nfound = select(maxfd, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL, &timeout);
1367         }
1368 #endif
1369
1370         if(nfound == 0)  {
1371             errstr = newstralloc(errstr, "data timeout");
1372             rc = 2;
1373             goto failed;
1374         }
1375         if(nfound == -1) {
1376             errstr = newstralloc2(errstr, "select: ", strerror(errno));
1377             rc = 2;
1378             goto failed;
1379         }
1380
1381         /* read/write any data */
1382
1383         if(datafd >= 0 && FD_ISSET(datafd, &selectset)) {
1384             size1 = read(datafd, datain, datalimit - datain);
1385             if(size1 < 0) {
1386                 errstr = newstralloc2(errstr, "data read: ", strerror(errno));
1387                 rc = 2;
1388                 goto failed;
1389             }
1390             if(update_dataptr(&outfd, size1)) {
1391                 rc = 2;
1392                 goto failed;
1393             }
1394             if(size1 == 0) {
1395                 eof1 = 1;
1396                 FD_CLR(datafd, &readset);
1397                 aclose(datafd);
1398             }
1399         }
1400
1401         if(mesgfd >= 0 && FD_ISSET(mesgfd, &selectset)) {
1402             size2 = read(mesgfd, mesgbuf, sizeof(mesgbuf)-1);
1403             switch(size2) {
1404             case -1:
1405                 errstr = newstralloc2(errstr, "mesg read: ", strerror(errno));
1406                 rc = 2;
1407                 goto failed;
1408             case 0:
1409                 eof2 = 1;
1410                 process_dumpeof();
1411                 FD_CLR(mesgfd, &readset);
1412                 aclose(mesgfd);
1413                 break;
1414             default:
1415                 mesgbuf[size2] = '\0';
1416                 add_msg_data(mesgbuf, size2);
1417             }
1418
1419             if (got_info_endline && !header_done) { /* time to do the header */
1420                 make_tapeheader(&file, F_DUMPFILE);
1421                 if (write_tapeheader(outfd, &file)) {
1422                     int save_errno = errno;
1423                     errstr = newstralloc2(errstr, "write_tapeheader: ", 
1424                                           strerror(errno));
1425                     if(save_errno == ENOSPC) {
1426                         putresult(NO_ROOM, "%s %lu\n", handle, 
1427                                   use+split_size-dumpsize);
1428                         use = 0; /* force RQ_MORE_DISK */
1429                         split_size = dumpsize;
1430                         rc = 1;
1431                     }
1432                     else {
1433                         rc = 2;
1434                     }
1435                     goto failed;
1436                 }
1437                 dumpsize += DISK_BLOCK_KB;
1438                 filesize += DISK_BLOCK_KB;
1439                 nb_header_block++;
1440                 header_done = 1;
1441                 strncat(cont_filename,filename,sizeof(cont_filename));
1442                 cont_filename[sizeof(cont_filename)-1] = '\0';
1443
1444                 if (datafd != -1)
1445                     FD_SET(datafd, &readset);   /* now we can read the data */
1446             }
1447         }
1448     } /* end while */
1449
1450 #if DUMPER_SOCKET_BUFFERING
1451     if(lowwatset_count > 1) {
1452         fprintf(stderr, "%s: pid %ld low water set %d times\n",
1453                 get_pname(), (long) getpid(), lowwatset_count);
1454     }
1455 #endif
1456
1457     if(dump_result > 1) {
1458         rc = 2;
1459         goto failed;
1460     }
1461
1462     runtime = stopclock();
1463     dumptime = runtime.r.tv_sec + runtime.r.tv_usec/1000000.0;
1464
1465     dumpsize -= (nb_header_block * DISK_BLOCK_KB);/* don't count the header */
1466     if (dumpsize < 0) dumpsize = 0;     /* XXX - maybe this should be fatal? */
1467
1468     ap_snprintf(kb_str, sizeof(kb_str), "%ld", dumpsize);
1469     ap_snprintf(kps_str, sizeof(kps_str),
1470                 "%3.1f",
1471                 dumptime ? dumpsize / dumptime : 0.0);
1472     ap_snprintf(orig_kb_str, sizeof(orig_kb_str), "%ld", origsize);
1473     errstr = newvstralloc(errstr,
1474                           "sec ", walltime_str(runtime),
1475                           " ", "kb ", kb_str,
1476                           " ", "kps ", kps_str,
1477                           " ", "orig-kb ", orig_kb_str,
1478                           NULL);
1479     q = squotef("[%s]", errstr);
1480     putresult(DONE, "%s %ld %ld %ld %s\n", handle, origsize, dumpsize,
1481               (long)(dumptime+0.5), q);
1482     amfree(q);
1483
1484     switch(dump_result) {
1485     case 0:
1486         log_add(L_SUCCESS, "%s %s %s %d [%s]", hostname, diskname, datestamp, level, errstr);
1487
1488         break;
1489
1490     case 1:
1491         log_start_multiline();
1492         log_add(L_STRANGE, "%s %s %d [%s]", hostname, diskname, level, errstr);
1493         log_msgout(L_STRANGE);
1494         log_end_multiline();
1495
1496         break;
1497     }
1498
1499     if(errf) afclose(errf);
1500
1501     if (indexfile_tmp) {
1502         waitpid(indexpid,NULL,0);
1503         if(rename(indexfile_tmp, indexfile_real) != 0) {
1504             log_add(L_WARNING, "could not rename \"%s\" to \"%s\": %s",
1505                     indexfile_tmp, indexfile_real, strerror(errno));
1506         }
1507         amfree(indexfile_tmp);
1508         amfree(indexfile_real);
1509     }
1510
1511     return 0;
1512
1513  failed:
1514
1515 #if DUMPER_SOCKET_BUFFERING
1516     if(lowwatset_count > 1) {
1517         fprintf(stderr, "%s: pid %ld low water set %d times\n",
1518                 get_pname(), (long) getpid(), lowwatset_count);
1519     }
1520 #endif
1521
1522     if(!abort_pending) {
1523         q = squotef("[%s]", errstr);
1524         if(rc==2)
1525             putresult(FAILED, "%s %s\n", handle, q);
1526         else
1527             putresult(TRYAGAIN, "%s %s\n", handle, q);
1528         amfree(q);
1529     }
1530
1531     /* kill all child process */
1532     if(compresspid != -1) {
1533         killerr = kill(compresspid,SIGTERM);
1534         if(killerr == 0) {
1535             fprintf(stderr,"%s: kill compress command\n",get_pname());
1536         }
1537         else if ( killerr == -1 ) {
1538             if(errno != ESRCH)
1539                 fprintf(stderr,"%s: can't kill compress command: %s\n", 
1540                                get_pname(), strerror(errno));
1541         }
1542     }
1543
1544     if(indexpid != -1) {
1545         killerr = kill(indexpid,SIGTERM);
1546         if(killerr == 0) {
1547             fprintf(stderr,"%s: kill index command\n",get_pname());
1548         }
1549         else if ( killerr == -1 ) {
1550             if(errno != ESRCH)
1551                 fprintf(stderr,"%s: can't kill index command: %s\n", 
1552                                get_pname(),strerror(errno));
1553         }
1554     }
1555
1556     if(!abort_pending) {
1557         log_start_multiline();
1558         log_add(L_FAIL, "%s %s %s %d [%s]", hostname, diskname, 
1559                 datestamp, level, errstr);
1560         if (errf) {
1561             log_msgout(L_FAIL);
1562         }
1563         log_end_multiline();
1564     }
1565
1566     if(errf) afclose(errf);
1567
1568     if (indexfile_tmp) {
1569         unlink(indexfile_tmp);
1570         amfree(indexfile_tmp);
1571         amfree(indexfile_real);
1572     }
1573
1574     return rc;
1575 }
1576
1577 /* -------------------- */
1578
1579 char *hostname, *disk;
1580 int response_error;
1581
1582 void sendbackup_response(p, pkt)
1583 proto_t *p;
1584 pkt_t *pkt;
1585 {
1586     int data_port = -1;
1587     int mesg_port = -1;
1588     int index_port = -1;
1589     char *line;
1590     char *fp;
1591     char *s;
1592     char *t;
1593     int ch;
1594     int tch;
1595
1596     am_release_feature_set(their_features);
1597     their_features = NULL;
1598
1599     if(p->state == S_FAILED) {
1600         if(pkt == NULL) {
1601             if(p->prevstate == S_REPWAIT) {
1602                 errstr = newstralloc(errstr, "[reply timeout]");
1603             }
1604             else {
1605                 errstr = newstralloc(errstr, "[request timeout]");
1606             }
1607             response_error = 1;
1608             return;
1609         }
1610     }
1611
1612 #if 0
1613     fprintf(stderr, "got %sresponse:\n----\n%s----\n\n",
1614             (p->state == S_FAILED) ? "NAK " : "", pkt->body);
1615 #endif
1616
1617 #ifdef KRB4_SECURITY
1618     if(krb4_auth && !check_mutual_authenticator(&cred.session, pkt, p)) {
1619         errstr = newstralloc(errstr, "mutual-authentication failed");
1620         response_error = 2;
1621         return;
1622     }
1623 #endif
1624
1625     s = pkt->body;
1626     ch = *s++;
1627     while(ch) {
1628         line = s - 1;
1629         skip_line(s, ch);
1630         if (s[-2] == '\n') {
1631             s[-2] = '\0';
1632         }
1633
1634 #define sc "OPTIONS "
1635         if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1636 #undef sc
1637
1638 #define sc "features="
1639             t = strstr(line, sc);
1640             if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) {
1641                 t += sizeof(sc)-1;
1642 #undef sc
1643                 am_release_feature_set(their_features);
1644                 if((their_features = am_string_to_feature(t)) == NULL) {
1645                     errstr = newvstralloc(errstr,
1646                                           "bad features value: ",
1647                                           line,
1648                                           NULL);
1649                 }
1650             }
1651
1652             continue;
1653         }
1654
1655 #define sc "ERROR "
1656         if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1657             t = line + sizeof(sc)-1;
1658             tch = t[-1];
1659 #undef sc
1660
1661             fp = t - 1;
1662             skip_whitespace(t, tch);
1663             errstr = newvstralloc(errstr,
1664                                   (p->state == S_FAILED) ? "nak error: " : "",
1665                                   fp,
1666                                   NULL);
1667             response_error = ((p->state == S_FAILED) ? 1 : 2);
1668             return;
1669         }
1670
1671 #define sc "CONNECT "
1672         if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1673 #undef sc
1674
1675 #define sc "DATA "
1676             t = strstr(line, sc);
1677             if(t != NULL && isspace((int)t[-1])) {
1678                 t += sizeof(sc)-1;
1679 #undef sc
1680                 data_port = atoi(t);
1681             }
1682
1683 #define sc "MESG "
1684             t = strstr(line, sc);
1685             if(t != NULL && isspace((int)t[-1])) {
1686                 t += sizeof(sc)-1;
1687 #undef sc
1688                 mesg_port = atoi(t);
1689             }
1690
1691 #define sc "INDEX "
1692             t = strstr(line, sc);
1693             if(t != NULL && isspace((int)t[-1])) {
1694                 t += sizeof(sc)-1;
1695 #undef sc
1696                 index_port = atoi(t);
1697             }
1698             continue;
1699         }
1700
1701         errstr = newvstralloc(errstr,
1702                               "unknown response: ",
1703                               line,
1704                               NULL);
1705         response_error = 2;
1706         return;
1707     }
1708
1709     if (data_port == -1 || mesg_port == -1) {
1710         errstr = newvstralloc(errstr, "bad CONNECT response", NULL);
1711         response_error = 2;
1712         return;
1713     }
1714
1715     datafd = stream_client(hostname, data_port, -1, -1, NULL);
1716     if(datafd == -1) {
1717         errstr = newvstralloc(errstr,
1718                               "could not connect to data port: ",
1719                               strerror(errno),
1720                               NULL);
1721         response_error = 1;
1722         return;
1723     }
1724     mesgfd = stream_client(hostname, mesg_port, -1, -1, NULL);
1725     if(mesgfd == -1) {
1726         errstr = newvstralloc(errstr,
1727                               "could not connect to mesg port: ",
1728                               strerror(errno),
1729                               NULL);
1730         aclose(datafd);
1731         datafd = -1;                            /* redundant */
1732         response_error = 1;
1733         return;
1734     }
1735
1736     if (index_port != -1) {
1737         indexfd = stream_client(hostname, index_port, -1, -1, NULL);
1738         if (indexfd == -1) {
1739             errstr = newvstralloc(errstr,
1740                                   "could not connect to index port: ",
1741                                   strerror(errno),
1742                                   NULL);
1743             aclose(datafd);
1744             aclose(mesgfd);
1745             datafd = mesgfd = -1;               /* redundant */
1746             response_error = 1;
1747             return;
1748         }
1749     }
1750
1751     /* everything worked */
1752
1753 #ifdef KRB4_SECURITY
1754     if(krb4_auth && kerberos_handshake(datafd, cred.session) == 0) {
1755         errstr = newstralloc(errstr,
1756                              "mutual authentication in data stream failed");
1757         aclose(datafd);
1758         aclose(mesgfd);
1759         if (indexfd != -1)
1760             aclose(indexfd);
1761         response_error = 1;
1762         return;
1763     }
1764     if(krb4_auth && kerberos_handshake(mesgfd, cred.session) == 0) {
1765         errstr = newstralloc(errstr,
1766                              "mutual authentication in mesg stream failed");
1767         aclose(datafd);
1768         if (indexfd != -1)
1769             aclose(indexfd);
1770         aclose(mesgfd);
1771         response_error = 1;
1772         return;
1773     }
1774 #endif
1775     response_error = 0;
1776     return;
1777 }
1778
1779 int startup_dump(hostname, disk, device, level, dumpdate, progname, options)
1780 char *hostname, *disk, *device, *dumpdate, *progname, *options;
1781 int level;
1782 {
1783     char level_string[NUM_STR_SIZE];
1784     char *req = NULL;
1785     int rc;
1786
1787     int has_features = am_has_feature(their_features, fe_req_options_features);
1788     int has_hostname = am_has_feature(their_features, fe_req_options_hostname);
1789     int has_device   = am_has_feature(their_features, fe_sendbackup_req_device);
1790
1791     ap_snprintf(level_string, sizeof(level_string), "%d", level);
1792     req = vstralloc("SERVICE sendbackup\n",
1793                     "OPTIONS ",
1794                     has_features ? "features=" : "",
1795                     has_features ? our_feature_string : "",
1796                     has_features ? ";" : "",
1797                     has_hostname ? "hostname=" : "",
1798                     has_hostname ? hostname : "",
1799                     has_hostname ? ";" : "",
1800                     "\n",
1801                     progname,
1802                     " ", disk,
1803                     " ", device && has_device ? device : "",
1804                     " ", level_string,
1805                     " ", dumpdate,
1806                     " ", "OPTIONS ", options,
1807                     "\n",
1808                     NULL);
1809
1810     datafd = mesgfd = indexfd = -1;
1811
1812 #ifdef KRB4_SECURITY
1813     if(krb4_auth) {
1814         char rc_str[NUM_STR_SIZE];
1815
1816         rc = make_krb_request(hostname, kamanda_port, req, NULL,
1817                               STARTUP_TIMEOUT, sendbackup_response);
1818         if(!rc) {
1819             char inst[256], realm[256];
1820 #define HOSTNAME_INSTANCE inst
1821             /*
1822              * This repeats a lot of work with make_krb_request, but it's
1823              * ultimately the kerberos library's fault: krb_mk_req calls
1824              * krb_get_cred, but doesn't make the session key available!
1825              * XXX: But admittedly, we could restructure a bit here and
1826              * at least eliminate the duplicate gethostbyname().
1827              */
1828             if(host2krbname(hostname, inst, realm) == 0)
1829                 rc = -1;
1830             else
1831                 rc = krb_get_cred(CLIENT_HOST_PRINCIPLE, CLIENT_HOST_INSTANCE,
1832                                   realm, &cred);
1833             if(rc > 0 ) {
1834                 ap_snprintf(rc_str, sizeof(rc_str), "%d", rc);
1835                 errstr = newvstralloc(errstr,
1836                                       "[host ", hostname,
1837                                       ": krb4 error (krb_get_cred) ",
1838                                       rc_str,
1839                                       ": ", krb_err_txt[rc],
1840                                       NULL);
1841                 amfree(req);
1842                 return 2;
1843             }
1844         }
1845         if(rc > 0) {
1846             ap_snprintf(rc_str, sizeof(rc_str), "%d", rc);
1847             errstr = newvstralloc(errstr,
1848                                   "[host ", hostname,
1849                                   ": krb4 error (make_krb_req) ",
1850                                   rc_str,
1851                                   ": ", krb_err_txt[rc],
1852                                   NULL);
1853             amfree(req);
1854             return 2;
1855         }
1856     } else
1857 #endif
1858         rc = make_request(hostname, amanda_port, req, NULL,
1859                           STARTUP_TIMEOUT, sendbackup_response);
1860
1861     req = NULL;                                 /* do not own this any more */
1862
1863     if(rc) {
1864         errstr = newvstralloc(errstr,
1865                               "[could not resolve name \"", hostname, "\"]",
1866                               NULL);
1867         return 2;
1868     }
1869     run_protocol();
1870     return response_error;
1871 }