Imported Upstream version 2.4.4p3
[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 2003/10/30 18:09:27 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         while(size > 0 && split_size > 0 && dumpsize >= split_size) {
672             amfree(new_filename);
673             if(use == 0) {
674                 /*
675                  * Probably no more space on this disk.  Request more.
676                  */
677                 putresult(RQ_MORE_DISK, "%s\n", handle);
678                 cmd = getcmd(&cmdargs);
679                 if(cmd == CONTINUE) {
680                     /*
681                      * CONTINUE
682                      *   filename
683                      *   chunksize
684                      *   use
685                      */
686                     cmdargs.argc++;             /* true count of args */
687                     a = 2;
688
689                     if(a >= cmdargs.argc) {
690                         error("error [dumper CONTINUE: not enough args: filename]");
691                     }
692                     arg_filename = newstralloc(arg_filename, cmdargs.argv[a++]);
693
694                     if(a >= cmdargs.argc) {
695                         error("error [dumper CONTINUE: not enough args: chunksize]");
696                     }
697                     chunksize = atoi(cmdargs.argv[a++]);
698                     chunksize = am_floor(chunksize, DISK_BLOCK_KB);
699
700                     if(a >= cmdargs.argc) {
701                         error("error [dumper CONTINUE: not enough args: use]");
702                     }
703                     use = atoi(cmdargs.argv[a++]);
704
705                     if(a != cmdargs.argc) {
706                         error("error [dumper CONTINUE: too many args: %d != %d]",
707                               cmdargs.argc, a);
708                     }
709
710                     if(strcmp(filename, arg_filename) == 0) {
711                         /*
712                          * Same disk, so use what room is left up to the
713                          * next chunk boundary or the amount we were given,
714                          * whichever is less.
715                          */
716                         left_in_chunk = chunksize - filesize;
717                         if(left_in_chunk > use) {
718                             split_size += use;
719                             use = 0;
720                         } else {
721                             split_size += left_in_chunk;
722                             use -= left_in_chunk;
723                         }
724                         if(left_in_chunk > 0) {
725                             /*
726                              * We still have space in this chunk.
727                              */
728                             break;
729                         }
730                     } else {
731                         /*
732                          * Different disk, so use new file.
733                          */
734                         filename = newstralloc(filename, arg_filename);
735                     }
736                 } else if(cmd == ABORT) {
737                     abort_pending = 1;
738                     errstr = newstralloc(errstr, "ERROR");
739                     rc = 1;
740                     goto common_exit;
741                 } else {
742                     if(cmdargs.argc >= 1) {
743                         q = squote(cmdargs.argv[1]);
744                     } else if(cmdargs.argc >= 0) {
745                         q = squote(cmdargs.argv[0]);
746                     } else {
747                         q = stralloc("(no input?)");
748                     }
749                     error("error [bad command after RQ-MORE-DISK: \"%s\"]", q);
750                 }
751             }
752
753             ap_snprintf(sequence, sizeof(sequence), "%d", filename_seq);
754             new_filename = newvstralloc(new_filename,
755                                         filename,
756                                         ".",
757                                         sequence,
758                                         NULL);
759             tmp_filename = newvstralloc(tmp_filename,
760                                         new_filename,
761                                         ".tmp",
762                                         NULL);
763             pc = strrchr(tmp_filename, '/');
764             *pc = '\0';
765             mkholdingdir(tmp_filename);
766             *pc = '/';
767             new_outfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
768             if(new_outfd == -1) {
769                 int save_errno = errno;
770
771                 errstr = squotef("creating chunk holding file \"%s\": %s",
772                                  tmp_filename,
773                                  strerror(save_errno));
774                 if(save_errno == ENOSPC) {
775                     putresult(NO_ROOM, "%s %lu\n",
776                               handle, 
777                               use + split_size - dumpsize);
778                     use = 0;            /* force RQ_MORE_DISK */
779                     split_size = dumpsize;
780                     continue;
781                 }
782                 aclose(outfd);
783                 rc = 1;
784                 goto common_exit;
785             }
786             save_type = file.type;
787             file.type = F_CONT_DUMPFILE;
788             file.cont_filename[0] = '\0';
789             if(write_tapeheader(new_outfd, &file)) {
790                 int save_errno = errno;
791
792                 aclose(new_outfd);
793                 unlink(tmp_filename);
794                 if(save_errno == ENOSPC) {
795                     putresult(NO_ROOM, "%s %lu\n",
796                               handle, 
797                               use + split_size - dumpsize);
798                     use = 0;                    /* force RQ_MORE_DISK */
799                     split_size = dumpsize;
800                     continue;
801                 }
802                 errstr = squotef("write_tapeheader file \"%s\": %s",
803                                  tmp_filename, strerror(errno));
804                 rc = 1;
805                 goto common_exit;
806             }
807             if(lseek(outfd, (off_t)0, SEEK_SET) != 0) {
808                 errstr = squotef("cannot lseek: %s", strerror(errno));
809                 aclose(new_outfd);
810                 unlink(tmp_filename);
811                 rc = 1;
812                 goto common_exit;
813             }
814             strncpy(file.cont_filename, new_filename, 
815                     sizeof(file.cont_filename));
816             file.cont_filename[sizeof(file.cont_filename)-1] = '\0';
817             file.type = save_type;
818             if(write_tapeheader(outfd, &file)) {
819                 errstr = squotef("write_tapeheader file linked to \"%s\": %s",
820                                  tmp_filename, strerror(errno));
821                 aclose(new_outfd);
822                 unlink(tmp_filename);
823                 rc = 1;
824                 goto common_exit;
825             }
826             file.type = F_CONT_DUMPFILE;
827             strncpy(cont_filename, new_filename, sizeof(cont_filename));
828             cont_filename[sizeof(cont_filename)-1] = '\0';
829
830             aclose(outfd);
831             *p_outfd = outfd = new_outfd;
832             new_outfd = -1;
833
834             dumpsize += DISK_BLOCK_KB;
835             filesize = DISK_BLOCK_KB;
836             split_size += (chunksize>use)?use:chunksize;
837             use = (chunksize>use)?0:use-chunksize;
838             nb_header_block++;
839             filename_seq++;
840         }
841         rc = write_dataptr(outfd);
842     }
843
844 common_exit:
845
846     amfree(new_filename);
847     amfree(tmp_filename);
848     amfree(arg_filename);
849     return rc;
850 }
851
852
853 static char *msgbuf = NULL;
854 int got_info_endline;
855 int got_sizeline;
856 int got_endline;
857 int dump_result;
858
859 static void process_dumpeof()
860 {
861     /* process any partial line in msgbuf? !!! */
862     if(msgbuf != NULL) {
863         fprintf(errf,"? %s: error [partial line in msgbuf: %ld bytes]\n",
864                 get_pname(), (long) strlen(msgbuf));
865         fprintf(errf,"? %s: error [partial line in msgbuf: \"%s\"]\n",
866                 get_pname(), msgbuf);
867     }
868     if(!got_sizeline && dump_result < 2) {
869         /* make a note if there isn't already a failure */
870         fprintf(errf,"? %s: strange [missing size line from sendbackup]\n",
871                 get_pname());
872         dump_result = max(dump_result, 2);
873     }
874
875     if(!got_endline && dump_result < 2) {
876         fprintf(errf,"? %s: strange [missing end line from sendbackup]\n",
877                 get_pname());
878         dump_result = max(dump_result, 2);
879     }
880 }
881
882 /* Parse an information line from the client.
883 ** We ignore unknown parameters and only remember the last
884 ** of any duplicates.
885 */
886 static void parse_info_line(str)
887 char *str;
888 {
889     if(strcmp(str, "end") == 0) {
890         got_info_endline = 1;
891         return;
892     }
893
894 #define sc "BACKUP="
895     if(strncmp(str, sc, sizeof(sc)-1) == 0) {
896         backup_name = newstralloc(backup_name, str + sizeof(sc)-1);
897         return;
898     }
899 #undef sc
900
901 #define sc "RECOVER_CMD="
902     if(strncmp(str, sc, sizeof(sc)-1) == 0) {
903         recover_cmd = newstralloc(recover_cmd, str + sizeof(sc)-1);
904         return;
905     }
906 #undef sc
907
908 #define sc "COMPRESS_SUFFIX="
909     if(strncmp(str, sc, sizeof(sc)-1) == 0) {
910         compress_suffix = newstralloc(compress_suffix, str + sizeof(sc)-1);
911         return;
912     }
913 #undef sc
914 }
915
916 static void process_dumpline(str)
917 char *str;
918 {
919     char *s, *fp;
920     int ch;
921
922     s = str;
923     ch = *s++;
924
925     switch(ch) {
926     case '|':
927         /* normal backup output line */
928         break;
929     case '?':
930         /* sendbackup detected something strange */
931         dump_result = max(dump_result, 1);
932         break;
933     case 's':
934         /* a sendbackup line, just check them all since there are only 5 */
935 #define sc "sendbackup: start"
936         if(strncmp(str, sc, sizeof(sc)-1) == 0) {
937             break;
938         }
939 #undef sc
940 #define sc "sendbackup: size"
941         if(strncmp(str, sc, sizeof(sc)-1) == 0) {
942             s += sizeof(sc)-1;
943             ch = s[-1];
944             skip_whitespace(s, ch);
945             if(ch) {
946                 origsize = (long)atof(str + sizeof(sc)-1);
947                 got_sizeline = 1;
948                 break;
949             }
950         }
951 #undef sc
952 #define sc "sendbackup: end"
953         if(strncmp(str, sc, sizeof(sc)-1) == 0) {
954             got_endline = 1;
955             break;
956         }
957 #undef sc
958 #define sc "sendbackup: error"
959         if(strncmp(str, sc, sizeof(sc)-1) == 0) {
960             s += sizeof(sc)-1;
961             ch = s[-1];
962 #undef sc
963             got_endline = 1;
964             dump_result = max(dump_result, 2);
965             skip_whitespace(s, ch);
966             if(ch == '\0' || ch != '[') {
967                 errstr = newvstralloc(errstr,
968                                       "bad remote error: ", str,
969                                       NULL);
970             } else {
971                 ch = *s++;
972                 fp = s - 1;
973                 while(ch && ch != ']') ch = *s++;
974                 s[-1] = '\0';
975                 errstr = newstralloc(errstr, fp);
976                 s[-1] = ch;
977             }
978             break;
979         }
980 #define sc "sendbackup: info"
981         if(strncmp(str, sc, sizeof(sc)-1) == 0) {
982             s += sizeof(sc)-1;
983             ch = s[-1];
984             skip_whitespace(s, ch);
985             parse_info_line(s - 1);
986             break;
987         }
988 #undef sc
989         /* else we fall through to bad line */
990     default:
991         fprintf(errf, "??%s", str);
992         dump_result = max(dump_result, 1);
993         return;
994     }
995     fprintf(errf, "%s\n", str);
996 }
997
998 static void add_msg_data(str, len)
999 char *str;
1000 int len;
1001 {
1002     char *t;
1003     char *nl;
1004
1005     while(len > 0) {
1006         if((nl = strchr(str, '\n')) != NULL) {
1007             *nl = '\0';
1008         }
1009         if(msgbuf) {
1010             t = stralloc2(msgbuf, str);
1011             amfree(msgbuf);
1012             msgbuf = t;
1013         } else if(nl == NULL) {
1014             msgbuf = stralloc(str);
1015         } else {
1016             msgbuf = str;
1017         }
1018         if(nl == NULL) break;
1019         process_dumpline(msgbuf);
1020         if(msgbuf != str) free(msgbuf);
1021         msgbuf = NULL;
1022         len -= nl + 1 - str;
1023         str = nl + 1;
1024     }
1025 }
1026
1027
1028 static void log_msgout(typ)
1029 logtype_t typ;
1030 {
1031     char *line = NULL;
1032
1033     fflush(errf);
1034     (void) fseek(errf, 0L, SEEK_SET);
1035     for(; (line = agets(errf)) != NULL; free(line)) {
1036         log_add(typ, "%s", line);
1037     }
1038     afclose(errf);
1039 }
1040
1041 /* ------------- */
1042
1043 void make_tapeheader(file, type)
1044 dumpfile_t *file;
1045 filetype_t type;
1046 {
1047     fh_init(file);
1048     file->type = type;
1049     strncpy(file->datestamp  , datestamp  , sizeof(file->datestamp)-1);
1050     file->datestamp[sizeof(file->datestamp)-1] = '\0';
1051     strncpy(file->name       , hostname   , sizeof(file->name)-1);
1052     file->name[sizeof(file->name)-1] = '\0';
1053     strncpy(file->disk       , diskname   , sizeof(file->disk)-1);
1054     file->disk[sizeof(file->disk)-1] = '\0';
1055     file->dumplevel = level;
1056     strncpy(file->program    , backup_name, sizeof(file->program)-1);
1057     file->program[sizeof(file->program)-1] = '\0';
1058     strncpy(file->recover_cmd, recover_cmd, sizeof(file->recover_cmd)-1);
1059     file->recover_cmd[sizeof(file->recover_cmd)-1] = '\0';
1060     file->blocksize = DISK_BLOCK_BYTES;
1061
1062     if (srvcompress) {
1063         file->compressed=1;
1064         ap_snprintf(file->uncompress_cmd, sizeof(file->uncompress_cmd),
1065                     " %s %s |", UNCOMPRESS_PATH,
1066 #ifdef UNCOMPRESS_OPT
1067                     UNCOMPRESS_OPT
1068 #else
1069                     ""
1070 #endif
1071                     );
1072         strncpy(file->comp_suffix, COMPRESS_SUFFIX,sizeof(file->comp_suffix)-1);
1073         file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
1074     }
1075     else {
1076         file->uncompress_cmd[0] = '\0';
1077         file->compressed=compress_suffix!=NULL;
1078         if(compress_suffix) {
1079             strncpy(file->comp_suffix, compress_suffix,
1080                     sizeof(file->comp_suffix)-1);
1081             file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
1082         } else {
1083             strncpy(file->comp_suffix, "N", sizeof(file->comp_suffix)-1);
1084             file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
1085         }
1086     }
1087     strncpy(file->cont_filename, cont_filename, sizeof(file->cont_filename)-1);
1088     file->cont_filename[sizeof(file->cont_filename)-1] = '\0';
1089 }
1090
1091 /* Send an Amanda dump header to the output file.
1092  * returns true if an error occured, false on success
1093  */
1094
1095 int write_tapeheader(outfd, file)
1096 int outfd;
1097 dumpfile_t *file;
1098 {
1099     char buffer[DISK_BLOCK_BYTES];
1100     int  written;
1101
1102     build_header(buffer, file, sizeof(buffer));
1103
1104     written = fullwrite(outfd, buffer, sizeof(buffer));
1105     if(written == sizeof(buffer)) return 0;
1106     if(written < 0) return written;
1107     errno = ENOSPC;
1108     return -1;
1109 }
1110
1111
1112 int do_dump(mesgfd, datafd, indexfd, outfd)
1113 int mesgfd, datafd, indexfd, outfd;
1114 {
1115     int maxfd, nfound, size1, size2, eof1, eof2;
1116     int rc;
1117     fd_set readset, selectset;
1118     struct timeval timeout;
1119     int outpipe[2];
1120     int header_done;    /* flag - header has been written */
1121     char *indexfile_tmp = NULL;
1122     char *indexfile_real = NULL;
1123     char level_str[NUM_STR_SIZE];
1124     char kb_str[NUM_STR_SIZE];
1125     char kps_str[NUM_STR_SIZE];
1126     char orig_kb_str[NUM_STR_SIZE];
1127     char *fn;
1128     char *q;
1129     times_t runtime;
1130     double dumptime;    /* Time dump took in secs */
1131     int compresspid = -1, indexpid = -1, killerr;
1132     char *errfname = NULL;
1133
1134 #ifndef DUMPER_SOCKET_BUFFERING
1135 #define DUMPER_SOCKET_BUFFERING 0
1136 #endif
1137
1138 #if !defined(SO_RCVBUF) || !defined(SO_RCVLOWAT)
1139 #undef  DUMPER_SOCKET_BUFFERING
1140 #define DUMPER_SOCKET_BUFFERING 0
1141 #endif
1142
1143 #if DUMPER_SOCKET_BUFFERING
1144     int lowat = NETWORK_BLOCK_BYTES;
1145     int recbuf = 0;
1146     int sizeof_recbuf = sizeof(recbuf);
1147     int lowwatset = 0;
1148     int lowwatset_count = 0;
1149 #endif
1150
1151     startclock();
1152
1153     datain = dataout = databuf;
1154     datalimit = databuf + sizeof(databuf);
1155     dumpsize = dumpbytes = origsize = filesize = dump_result = 0;
1156     nb_header_block = 0;
1157     got_info_endline = got_sizeline = got_endline = 0;
1158     header_done = 0;
1159     amfree(backup_name);
1160     amfree(recover_cmd);
1161     amfree(compress_suffix);
1162
1163     ap_snprintf(level_str, sizeof(level_str), "%d", level);
1164     fn = sanitise_filename(diskname);
1165     errfname = newvstralloc(errfname,
1166                             AMANDA_TMPDIR,
1167                             "/", hostname,
1168                             ".", fn,
1169                             ".", level_str,
1170                             ".errout",
1171                             NULL);
1172     amfree(fn);
1173     if((errf = fopen(errfname, "w+")) == NULL) {
1174         errstr = newvstralloc(errstr,
1175                               "errfile open \"", errfname, "\": ",
1176                               strerror(errno),
1177                               NULL);
1178         amfree(errfname);
1179         rc = 2;
1180         goto failed;
1181     }
1182     unlink(errfname);                           /* so it goes away on close */
1183     amfree(errfname);
1184
1185     /* insert pipe in the *READ* side, if server-side compression is desired */
1186     compresspid = -1;
1187     if (srvcompress) {
1188         int tmpfd;
1189
1190         tmpfd = datafd;
1191         pipe(outpipe); /* outpipe[0] is pipe's stdin, outpipe[1] is stdout. */
1192         datafd = outpipe[0];
1193         if(datafd < 0 || datafd >= FD_SETSIZE) {
1194             aclose(outpipe[0]);
1195             aclose(outpipe[1]);
1196             errstr = newstralloc(errstr, "descriptor out of range");
1197             errno = EMFILE;
1198             rc = 2;
1199             goto failed;
1200         }
1201         switch(compresspid=fork()) {
1202         case -1:
1203             errstr = newstralloc2(errstr, "couldn't fork: ", strerror(errno));
1204             rc = 2;
1205             goto failed;
1206         default:
1207             aclose(outpipe[1]);
1208             aclose(tmpfd);
1209             break;
1210         case 0:
1211             aclose(outpipe[0]);
1212             /* child acts on stdin/stdout */
1213             if (dup2(outpipe[1],1) == -1)
1214                 fprintf(stderr, "err dup2 out: %s\n", strerror(errno));
1215             if (dup2(tmpfd, 0) == -1)
1216                 fprintf(stderr, "err dup2 in: %s\n", strerror(errno));
1217             for(tmpfd = 3; tmpfd <= FD_SETSIZE; ++tmpfd) {
1218                 close(tmpfd);
1219             }
1220             /* now spawn gzip -1 to take care of the rest */
1221             execlp(COMPRESS_PATH, COMPRESS_PATH,
1222                    (srvcompress == srvcomp_best ? COMPRESS_BEST_OPT
1223                                                 : COMPRESS_FAST_OPT),
1224                    (char *)0);
1225             error("error: couldn't exec %s.\n", COMPRESS_PATH);
1226         }
1227         /* Now the pipe has been inserted. */
1228     }
1229
1230     indexpid = -1;
1231     if (indexfd != -1) {
1232         int tmpfd;
1233
1234         indexfile_real = getindexfname(hostname, diskname, datestamp, level),
1235         indexfile_tmp = stralloc2(indexfile_real, ".tmp");
1236
1237         if (mkpdir(indexfile_tmp, 02755, (uid_t)-1, (gid_t)-1) == -1) {
1238            errstr = newvstralloc(errstr,
1239                                  "err create ",
1240                                  indexfile_tmp,
1241                                  ": ",
1242                                  strerror(errno),
1243                                  NULL);
1244            amfree(indexfile_real);
1245            amfree(indexfile_tmp);
1246            rc = 2;
1247            goto failed;
1248         }
1249
1250         switch(indexpid=fork()) {
1251         case -1:
1252             errstr = newstralloc2(errstr, "couldn't fork: ", strerror(errno));
1253             rc = 2;
1254             goto failed;
1255         default:
1256             aclose(indexfd);
1257             indexfd = -1;                       /* redundant */
1258             break;
1259         case 0:
1260             if (dup2(indexfd, 0) == -1) {
1261                 error("err dup2 in: %s", strerror(errno));
1262             }
1263             indexfd = open(indexfile_tmp, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1264             if (indexfd == -1)
1265                 error("err open %s: %s", indexfile_tmp, strerror(errno));
1266             if (dup2(indexfd,1) == -1)
1267                 error("err dup2 out: %s", strerror(errno));
1268             for(tmpfd = 3; tmpfd <= FD_SETSIZE; ++tmpfd) {
1269                 close(tmpfd);
1270             }
1271             execlp(COMPRESS_PATH, COMPRESS_PATH, COMPRESS_BEST_OPT, (char *)0);
1272             error("error: couldn't exec %s.", COMPRESS_PATH);
1273         }
1274     }
1275
1276     NAUGHTY_BITS_INITIALIZE;
1277
1278     maxfd = max(mesgfd, datafd) + 1;
1279     eof1 = eof2 = 0;
1280
1281     FD_ZERO(&readset);
1282
1283     /* Just process messages for now.  Once we have done the header
1284     ** we will start processing data too.
1285     */
1286     FD_SET(mesgfd, &readset);
1287
1288     if(datafd == -1) eof1 = 1;  /* fake eof on data */
1289
1290 #if DUMPER_SOCKET_BUFFERING
1291
1292 #ifndef EST_PACKET_SIZE
1293 #define EST_PACKET_SIZE 512
1294 #endif
1295 #ifndef EST_MIN_WINDOW
1296 #define EST_MIN_WINDOW  EST_PACKET_SIZE*4 /* leave room for 2k in transit */
1297 #endif
1298
1299     else {
1300         recbuf = STREAM_BUFSIZE;
1301         if (setsockopt(datafd, SOL_SOCKET, SO_RCVBUF,
1302                        (void *) &recbuf, sizeof_recbuf)) {
1303             const int errornumber = errno;
1304             fprintf(stderr, "%s: pid %ld setsockopt(SO_RCVBUF): %s\n",
1305                     get_pname(), (long) getpid(), strerror(errornumber));
1306         }
1307         if (getsockopt(datafd, SOL_SOCKET, SO_RCVBUF,
1308                        (void *) &recbuf, (void *)&sizeof_recbuf)) {
1309             const int errornumber = errno;
1310             fprintf(stderr, "%s: pid %ld getsockopt(SO_RCVBUF): %s\n",
1311                     get_pname(), (long) getpid(), strerror(errornumber));
1312             recbuf = 0;
1313         }
1314
1315         /* leave at least EST_MIN_WINDOW between lowwat and recbuf */
1316         if (recbuf-lowat < EST_MIN_WINDOW)
1317             lowat = recbuf-EST_MIN_WINDOW;
1318
1319         /* if lowwat < ~512, don't bother */
1320         if (lowat < EST_PACKET_SIZE)
1321             recbuf = 0;
1322         fprintf(stderr, "%s: pid %ld receive size is %d, low water is %d\n",
1323                 get_pname(), (long) getpid(), recbuf, lowat);
1324     }
1325 #endif
1326
1327     while(!(eof1 && eof2)) {
1328
1329 #if DUMPER_SOCKET_BUFFERING
1330         /* Set socket buffering */
1331         if (recbuf>0 && !lowwatset) {
1332             if (setsockopt(datafd, SOL_SOCKET, SO_RCVLOWAT,
1333                            (void *) &lowat, sizeof(lowat))) {
1334                 const int errornumber = errno;
1335                 fprintf(stderr,
1336                         "%s: pid %ld setsockopt(SO_RCVLOWAT): %s\n",
1337                         get_pname(), (long) getpid(), strerror(errornumber));
1338             }
1339             lowwatset = 1;
1340             lowwatset_count++;
1341         }
1342 #endif
1343
1344         timeout.tv_sec = conf_dtimeout;
1345         timeout.tv_usec = 0;
1346         memcpy(&selectset, &readset, sizeof(fd_set));
1347
1348         nfound = select(maxfd, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL, &timeout);
1349
1350         /* check for errors or timeout */
1351
1352 #if DUMPER_SOCKET_BUFFERING
1353         if (nfound==0 && lowwatset) {
1354             const int zero = 0;
1355             /* Disable socket buffering and ... */
1356             if (setsockopt(datafd, SOL_SOCKET, SO_RCVLOWAT,
1357                            (void *) &zero, sizeof(zero))) {
1358                 const int errornumber = errno;
1359                 fprintf(stderr,
1360                         "%s: pid %ld setsockopt(SO_RCVLOWAT): %s\n",
1361                         get_pname(), (long) getpid(), strerror(errornumber));
1362             }
1363             lowwatset = 0;
1364
1365             /* ... try once more */
1366             timeout.tv_sec = conf_dtimeout;
1367             timeout.tv_usec = 0;
1368             memcpy(&selectset, &readset, sizeof(fd_set));
1369             nfound = select(maxfd, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL, &timeout);
1370         }
1371 #endif
1372
1373         if(nfound == 0)  {
1374             errstr = newstralloc(errstr, "data timeout");
1375             rc = 2;
1376             goto failed;
1377         }
1378         if(nfound == -1) {
1379             errstr = newstralloc2(errstr, "select: ", strerror(errno));
1380             rc = 2;
1381             goto failed;
1382         }
1383
1384         /* read/write any data */
1385
1386         if(datafd >= 0 && FD_ISSET(datafd, &selectset)) {
1387             size1 = read(datafd, datain, datalimit - datain);
1388             if(size1 < 0) {
1389                 errstr = newstralloc2(errstr, "data read: ", strerror(errno));
1390                 rc = 2;
1391                 goto failed;
1392             }
1393             if(update_dataptr(&outfd, size1)) {
1394                 rc = 2;
1395                 goto failed;
1396             }
1397             if(size1 == 0) {
1398                 eof1 = 1;
1399                 FD_CLR(datafd, &readset);
1400                 aclose(datafd);
1401             }
1402         }
1403
1404         if(mesgfd >= 0 && FD_ISSET(mesgfd, &selectset)) {
1405             size2 = read(mesgfd, mesgbuf, sizeof(mesgbuf)-1);
1406             switch(size2) {
1407             case -1:
1408                 errstr = newstralloc2(errstr, "mesg read: ", strerror(errno));
1409                 rc = 2;
1410                 goto failed;
1411             case 0:
1412                 eof2 = 1;
1413                 process_dumpeof();
1414                 FD_CLR(mesgfd, &readset);
1415                 aclose(mesgfd);
1416                 break;
1417             default:
1418                 mesgbuf[size2] = '\0';
1419                 add_msg_data(mesgbuf, size2);
1420             }
1421
1422             if (got_info_endline && !header_done) { /* time to do the header */
1423                 make_tapeheader(&file, F_DUMPFILE);
1424                 if (write_tapeheader(outfd, &file)) {
1425                     int save_errno = errno;
1426                     errstr = newstralloc2(errstr, "write_tapeheader: ", 
1427                                           strerror(errno));
1428                     if(save_errno == ENOSPC) {
1429                         putresult(NO_ROOM, "%s %lu\n", handle, 
1430                                   use+split_size-dumpsize);
1431                         use = 0; /* force RQ_MORE_DISK */
1432                         split_size = dumpsize;
1433                         rc = 1;
1434                     }
1435                     else {
1436                         rc = 2;
1437                     }
1438                     goto failed;
1439                 }
1440                 dumpsize += DISK_BLOCK_KB;
1441                 filesize += DISK_BLOCK_KB;
1442                 nb_header_block++;
1443                 header_done = 1;
1444                 strncat(cont_filename,filename,sizeof(cont_filename));
1445                 cont_filename[sizeof(cont_filename)-1] = '\0';
1446
1447                 if (datafd != -1)
1448                     FD_SET(datafd, &readset);   /* now we can read the data */
1449             }
1450         }
1451     } /* end while */
1452
1453 #if DUMPER_SOCKET_BUFFERING
1454     if(lowwatset_count > 1) {
1455         fprintf(stderr, "%s: pid %ld low water set %d times\n",
1456                 get_pname(), (long) getpid(), lowwatset_count);
1457     }
1458 #endif
1459
1460     if(dump_result > 1) {
1461         rc = 2;
1462         goto failed;
1463     }
1464
1465     runtime = stopclock();
1466     dumptime = runtime.r.tv_sec + runtime.r.tv_usec/1000000.0;
1467
1468     dumpsize -= (nb_header_block * DISK_BLOCK_KB);/* don't count the header */
1469     if (dumpsize < 0) dumpsize = 0;     /* XXX - maybe this should be fatal? */
1470
1471     ap_snprintf(kb_str, sizeof(kb_str), "%ld", dumpsize);
1472     ap_snprintf(kps_str, sizeof(kps_str),
1473                 "%3.1f",
1474                 dumptime ? dumpsize / dumptime : 0.0);
1475     ap_snprintf(orig_kb_str, sizeof(orig_kb_str), "%ld", origsize);
1476     errstr = newvstralloc(errstr,
1477                           "sec ", walltime_str(runtime),
1478                           " ", "kb ", kb_str,
1479                           " ", "kps ", kps_str,
1480                           " ", "orig-kb ", orig_kb_str,
1481                           NULL);
1482     q = squotef("[%s]", errstr);
1483     putresult(DONE, "%s %ld %ld %ld %s\n", handle, origsize, dumpsize,
1484               (long)(dumptime+0.5), q);
1485     amfree(q);
1486
1487     switch(dump_result) {
1488     case 0:
1489         log_add(L_SUCCESS, "%s %s %s %d [%s]", hostname, diskname, datestamp, level, errstr);
1490
1491         break;
1492
1493     case 1:
1494         log_start_multiline();
1495         log_add(L_STRANGE, "%s %s %d [%s]", hostname, diskname, level, errstr);
1496         log_msgout(L_STRANGE);
1497         log_end_multiline();
1498
1499         break;
1500     }
1501
1502     if(errf) afclose(errf);
1503
1504     if (indexfile_tmp) {
1505         waitpid(indexpid,NULL,0);
1506         if(rename(indexfile_tmp, indexfile_real) != 0) {
1507             log_add(L_WARNING, "could not rename \"%s\" to \"%s\": %s",
1508                     indexfile_tmp, indexfile_real, strerror(errno));
1509         }
1510         amfree(indexfile_tmp);
1511         amfree(indexfile_real);
1512     }
1513
1514     return 0;
1515
1516  failed:
1517
1518 #if DUMPER_SOCKET_BUFFERING
1519     if(lowwatset_count > 1) {
1520         fprintf(stderr, "%s: pid %ld low water set %d times\n",
1521                 get_pname(), (long) getpid(), lowwatset_count);
1522     }
1523 #endif
1524
1525     if(!abort_pending) {
1526         q = squotef("[%s]", errstr);
1527         if(rc==2)
1528             putresult(FAILED, "%s %s\n", handle, q);
1529         else
1530             putresult(TRYAGAIN, "%s %s\n", handle, q);
1531         amfree(q);
1532     }
1533
1534     /* kill all child process */
1535     if(compresspid != -1) {
1536         killerr = kill(compresspid,SIGTERM);
1537         if(killerr == 0) {
1538             fprintf(stderr,"%s: kill compress command\n",get_pname());
1539         }
1540         else if ( killerr == -1 ) {
1541             if(errno != ESRCH)
1542                 fprintf(stderr,"%s: can't kill compress command: %s\n", 
1543                                get_pname(), strerror(errno));
1544         }
1545     }
1546
1547     if(indexpid != -1) {
1548         killerr = kill(indexpid,SIGTERM);
1549         if(killerr == 0) {
1550             fprintf(stderr,"%s: kill index command\n",get_pname());
1551         }
1552         else if ( killerr == -1 ) {
1553             if(errno != ESRCH)
1554                 fprintf(stderr,"%s: can't kill index command: %s\n", 
1555                                get_pname(),strerror(errno));
1556         }
1557     }
1558
1559     if(!abort_pending) {
1560         log_start_multiline();
1561         log_add(L_FAIL, "%s %s %s %d [%s]", hostname, diskname, 
1562                 datestamp, level, errstr);
1563         if (errf) {
1564             log_msgout(L_FAIL);
1565         }
1566         log_end_multiline();
1567     }
1568
1569     if(errf) afclose(errf);
1570
1571     if (indexfile_tmp) {
1572         unlink(indexfile_tmp);
1573         amfree(indexfile_tmp);
1574         amfree(indexfile_real);
1575     }
1576
1577     return rc;
1578 }
1579
1580 /* -------------------- */
1581
1582 char *hostname, *disk;
1583 int response_error;
1584
1585 void sendbackup_response(p, pkt)
1586 proto_t *p;
1587 pkt_t *pkt;
1588 {
1589     int data_port = -1;
1590     int mesg_port = -1;
1591     int index_port = -1;
1592     char *line;
1593     char *fp;
1594     char *s;
1595     char *t;
1596     int ch;
1597     int tch;
1598
1599     am_release_feature_set(their_features);
1600     their_features = NULL;
1601
1602     if(p->state == S_FAILED) {
1603         if(pkt == NULL) {
1604             if(p->prevstate == S_REPWAIT) {
1605                 errstr = newstralloc(errstr, "[reply timeout]");
1606             }
1607             else {
1608                 errstr = newstralloc(errstr, "[request timeout]");
1609             }
1610             response_error = 1;
1611             return;
1612         }
1613     }
1614
1615 #if 0
1616     fprintf(stderr, "got %sresponse:\n----\n%s----\n\n",
1617             (p->state == S_FAILED) ? "NAK " : "", pkt->body);
1618 #endif
1619
1620 #ifdef KRB4_SECURITY
1621     if(krb4_auth && !check_mutual_authenticator(&cred.session, pkt, p)) {
1622         errstr = newstralloc(errstr, "mutual-authentication failed");
1623         response_error = 2;
1624         return;
1625     }
1626 #endif
1627
1628     s = pkt->body;
1629     ch = *s++;
1630     while(ch) {
1631         line = s - 1;
1632         skip_line(s, ch);
1633         if (s[-2] == '\n') {
1634             s[-2] = '\0';
1635         }
1636
1637 #define sc "OPTIONS "
1638         if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1639 #undef sc
1640
1641 #define sc "features="
1642             t = strstr(line, sc);
1643             if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) {
1644                 t += sizeof(sc)-1;
1645 #undef sc
1646                 am_release_feature_set(their_features);
1647                 if((their_features = am_string_to_feature(t)) == NULL) {
1648                     errstr = newvstralloc(errstr,
1649                                           "bad features value: ",
1650                                           line,
1651                                           NULL);
1652                 }
1653             }
1654
1655             continue;
1656         }
1657
1658 #define sc "ERROR "
1659         if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1660             t = line + sizeof(sc)-1;
1661             tch = t[-1];
1662 #undef sc
1663
1664             fp = t - 1;
1665             skip_whitespace(t, tch);
1666             errstr = newvstralloc(errstr,
1667                                   (p->state == S_FAILED) ? "nak error: " : "",
1668                                   fp,
1669                                   NULL);
1670             response_error = ((p->state == S_FAILED) ? 1 : 2);
1671             return;
1672         }
1673
1674 #define sc "CONNECT "
1675         if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1676 #undef sc
1677
1678 #define sc "DATA "
1679             t = strstr(line, sc);
1680             if(t != NULL && isspace((int)t[-1])) {
1681                 t += sizeof(sc)-1;
1682 #undef sc
1683                 data_port = atoi(t);
1684             }
1685
1686 #define sc "MESG "
1687             t = strstr(line, sc);
1688             if(t != NULL && isspace((int)t[-1])) {
1689                 t += sizeof(sc)-1;
1690 #undef sc
1691                 mesg_port = atoi(t);
1692             }
1693
1694 #define sc "INDEX "
1695             t = strstr(line, sc);
1696             if(t != NULL && isspace((int)t[-1])) {
1697                 t += sizeof(sc)-1;
1698 #undef sc
1699                 index_port = atoi(t);
1700             }
1701             continue;
1702         }
1703
1704         errstr = newvstralloc(errstr,
1705                               "unknown response: ",
1706                               line,
1707                               NULL);
1708         response_error = 2;
1709         return;
1710     }
1711
1712     if (data_port == -1 || mesg_port == -1) {
1713         errstr = newvstralloc(errstr, "bad CONNECT response", NULL);
1714         response_error = 2;
1715         return;
1716     }
1717
1718     datafd = stream_client(hostname, data_port, -1, -1, NULL);
1719     if(datafd == -1) {
1720         errstr = newvstralloc(errstr,
1721                               "could not connect to data port: ",
1722                               strerror(errno),
1723                               NULL);
1724         response_error = 1;
1725         return;
1726     }
1727     mesgfd = stream_client(hostname, mesg_port, -1, -1, NULL);
1728     if(mesgfd == -1) {
1729         errstr = newvstralloc(errstr,
1730                               "could not connect to mesg port: ",
1731                               strerror(errno),
1732                               NULL);
1733         aclose(datafd);
1734         datafd = -1;                            /* redundant */
1735         response_error = 1;
1736         return;
1737     }
1738
1739     if (index_port != -1) {
1740         indexfd = stream_client(hostname, index_port, -1, -1, NULL);
1741         if (indexfd == -1) {
1742             errstr = newvstralloc(errstr,
1743                                   "could not connect to index port: ",
1744                                   strerror(errno),
1745                                   NULL);
1746             aclose(datafd);
1747             aclose(mesgfd);
1748             datafd = mesgfd = -1;               /* redundant */
1749             response_error = 1;
1750             return;
1751         }
1752     }
1753
1754     /* everything worked */
1755
1756 #ifdef KRB4_SECURITY
1757     if(krb4_auth && kerberos_handshake(datafd, cred.session) == 0) {
1758         errstr = newstralloc(errstr,
1759                              "mutual authentication in data stream failed");
1760         aclose(datafd);
1761         aclose(mesgfd);
1762         if (indexfd != -1)
1763             aclose(indexfd);
1764         response_error = 1;
1765         return;
1766     }
1767     if(krb4_auth && kerberos_handshake(mesgfd, cred.session) == 0) {
1768         errstr = newstralloc(errstr,
1769                              "mutual authentication in mesg stream failed");
1770         aclose(datafd);
1771         if (indexfd != -1)
1772             aclose(indexfd);
1773         aclose(mesgfd);
1774         response_error = 1;
1775         return;
1776     }
1777 #endif
1778     response_error = 0;
1779     return;
1780 }
1781
1782 int startup_dump(hostname, disk, device, level, dumpdate, progname, options)
1783 char *hostname, *disk, *device, *dumpdate, *progname, *options;
1784 int level;
1785 {
1786     char level_string[NUM_STR_SIZE];
1787     char *req = NULL;
1788     int rc;
1789
1790     int has_features = am_has_feature(their_features, fe_req_options_features);
1791     int has_hostname = am_has_feature(their_features, fe_req_options_hostname);
1792     int has_device   = am_has_feature(their_features, fe_sendbackup_req_device);
1793
1794     ap_snprintf(level_string, sizeof(level_string), "%d", level);
1795     req = vstralloc("SERVICE sendbackup\n",
1796                     "OPTIONS ",
1797                     has_features ? "features=" : "",
1798                     has_features ? our_feature_string : "",
1799                     has_features ? ";" : "",
1800                     has_hostname ? "hostname=" : "",
1801                     has_hostname ? hostname : "",
1802                     has_hostname ? ";" : "",
1803                     "\n",
1804                     progname,
1805                     " ", disk,
1806                     " ", device && has_device ? device : "",
1807                     " ", level_string,
1808                     " ", dumpdate,
1809                     " ", "OPTIONS ", options,
1810                     "\n",
1811                     NULL);
1812
1813     datafd = mesgfd = indexfd = -1;
1814
1815 #ifdef KRB4_SECURITY
1816     if(krb4_auth) {
1817         char rc_str[NUM_STR_SIZE];
1818
1819         rc = make_krb_request(hostname, kamanda_port, req, NULL,
1820                               STARTUP_TIMEOUT, sendbackup_response);
1821         if(!rc) {
1822             char inst[256], realm[256];
1823 #define HOSTNAME_INSTANCE inst
1824             /*
1825              * This repeats a lot of work with make_krb_request, but it's
1826              * ultimately the kerberos library's fault: krb_mk_req calls
1827              * krb_get_cred, but doesn't make the session key available!
1828              * XXX: But admittedly, we could restructure a bit here and
1829              * at least eliminate the duplicate gethostbyname().
1830              */
1831             if(host2krbname(hostname, inst, realm) == 0)
1832                 rc = -1;
1833             else
1834                 rc = krb_get_cred(CLIENT_HOST_PRINCIPLE, CLIENT_HOST_INSTANCE,
1835                                   realm, &cred);
1836             if(rc > 0 ) {
1837                 ap_snprintf(rc_str, sizeof(rc_str), "%d", rc);
1838                 errstr = newvstralloc(errstr,
1839                                       "[host ", hostname,
1840                                       ": krb4 error (krb_get_cred) ",
1841                                       rc_str,
1842                                       ": ", krb_err_txt[rc],
1843                                       NULL);
1844                 amfree(req);
1845                 return 2;
1846             }
1847         }
1848         if(rc > 0) {
1849             ap_snprintf(rc_str, sizeof(rc_str), "%d", rc);
1850             errstr = newvstralloc(errstr,
1851                                   "[host ", hostname,
1852                                   ": krb4 error (make_krb_req) ",
1853                                   rc_str,
1854                                   ": ", krb_err_txt[rc],
1855                                   NULL);
1856             amfree(req);
1857             return 2;
1858         }
1859     } else
1860 #endif
1861         rc = make_request(hostname, amanda_port, req, NULL,
1862                           STARTUP_TIMEOUT, sendbackup_response);
1863
1864     req = NULL;                                 /* do not own this any more */
1865
1866     if(rc) {
1867         errstr = newvstralloc(errstr,
1868                               "[could not resolve name \"", hostname, "\"]",
1869                               NULL);
1870         return 2;
1871     }
1872     run_protocol();
1873     return response_error;
1874 }