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