f403171f2d2867d208ef6bfba0e9400286b799cd
[debian/amanda] / server-src / taper.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998, 2000 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: taper.c,v 1.118.2.1 2006/05/04 21:31:15 martinea Exp $
27  *
28  * moves files from holding disk to tape, or from a socket to tape
29  */
30
31 #include "amanda.h"
32 #include "util.h"
33 #include "conffile.h"
34 #include "tapefile.h"
35 #include "clock.h"
36 #include "stream.h"
37 #include "holding.h"
38 #include "logfile.h"
39 #include "tapeio.h"
40 #include "changer.h"
41 #include "version.h"
42 #include "arglist.h"
43 #include "token.h"
44 #include "amfeatures.h"
45 #include "fileheader.h"
46 #include "server_util.h"
47 #include "taperscan.c"
48
49 #ifdef HAVE_SYS_MMAN_H
50 #include <sys/mman.h>
51 #endif
52
53 #ifdef HAVE_LIBVTBLC
54 #include <vtblc.h>
55 #include <strings.h>
56 #include <math.h>
57
58
59 static int vtbl_no   = -1;
60 static int len       =  0;
61 static int offset    =  0;
62 static char *datestr = NULL;
63 static char start_datestr[20];
64 time_t raw_time;
65 struct tm tape_time;
66 struct tm backup_time;
67 struct tm *tape_timep = &tape_time;
68 typedef struct vtbl_lbls {
69     u_int8_t  label[45];
70     u_int8_t  date[20];
71 } vtbl_lbls;
72 static vtbl_lbls vtbl_entry[MAX_VOLUMES];
73 #endif /* HAVE_LIBVTBLC */
74 /*
75  * XXX update stat collection/printing
76  * XXX advance to next tape first in next_tape
77  * XXX label is being read twice?
78  */
79 long splitsize = 0; /* max size of dumpfile before split (Kb) */
80 char *splitbuf = NULL;
81 char *splitbuf_wr_ptr = NULL; /* the number of Kb we've written into splitbuf */
82 int orig_holdfile = -1;
83
84 /* NBUFS replaced by conf_tapebufs */
85 /* #define NBUFS                20 */
86 int conf_tapebufs;
87
88 off_t maxseek = (off_t)1 << ((sizeof(off_t) * 8) - 11);
89
90 char *holdfile_path = NULL;
91 char *holdfile_path_thischunk = NULL;
92 int num_holdfile_chunks = 0;
93 dumpfile_t holdfile_hdr;
94 dumpfile_t holdfile_hdr_thischunk;
95 off_t holdfile_offset_thischunk = (off_t)0;
96 int splitbuffer_fd = -1;
97 char *splitbuffer_path = NULL;
98
99 #define MODE_NONE 0
100 #define MODE_FILE_WRITE 1
101 #define MODE_PORT_WRITE 2
102
103 int mode = MODE_NONE;
104
105 /* This is now the number of empties, not full bufs */
106 #define THRESHOLD       1
107
108 #define CONNECT_TIMEOUT 2*60
109
110
111
112 #define EMPTY 1
113 #define FILLING 2
114 #define FULL 3
115
116 typedef struct buffer_s {
117     long status;
118     unsigned int size;
119     char *buffer;
120 } buffer_t;
121
122 #define nextbuf(p)    ((p) == buftable+conf_tapebufs-1? buftable : (p)+1)
123 #define prevbuf(p)    ((p) == buftable? buftable+conf_tapebufs-1 : (p)-1)
124
125 /* major modules */
126 int main P((int main_argc, char **main_argv));
127 void file_reader_side P((int rdpipe, int wrpipe));
128 void tape_writer_side P((int rdpipe, int wrpipe));
129
130 /* shared-memory routines */
131 char *attach_buffers P((unsigned int size));
132 void detach_buffers P((char *bufp));
133 void destroy_buffers P((void));
134 #define REMOVE_SHARED_MEMORY() \
135     detach_buffers(buffers); \
136     if (strcmp(procname, "reader") == 0) { \
137         destroy_buffers(); \
138     }
139
140 /* synchronization pipe routines */
141 void syncpipe_init P((int rd, int wr));
142 char syncpipe_get P((int *intp));
143 int  syncpipe_getint P((void));
144 char *syncpipe_getstr P((void));
145 void syncpipe_put P((int ch, int intval));
146 void syncpipe_putint P((int i));
147 void syncpipe_putstr P((const char *str));
148
149 /* tape manipulation subsystem */
150 int first_tape P((char *new_datestamp));
151 int next_tape P((int writerr));
152 int end_tape P((int writerr));
153 int write_filemark P((void));
154
155 /* support crap */
156 int seek_holdfile P((int fd, buffer_t *bp, long kbytes));
157
158 /* signal handling */
159 static void install_signal_handlers P((void));
160 static void signal_handler P((int));
161
162 /* exit routine */
163 static void cleanup P((void));
164
165 /*
166  * ========================================================================
167  * GLOBAL STATE
168  *
169  */
170 int interactive;
171 int writerpid;
172 times_t total_wait;
173 #ifdef TAPER_DEBUG
174 int bufdebug = 1;
175 #else
176 int bufdebug = 0;
177 #endif
178
179 char *buffers = NULL;
180 buffer_t *buftable = NULL;
181 int err;
182
183 char *procname = "parent";
184
185 char *taper_datestamp = NULL;
186 char *label = NULL;
187 int filenum;
188 char *errstr = NULL;
189 int tape_fd = -1;
190 char *tapedev = NULL;
191 char *tapetype = NULL;
192 tapetype_t *tt = NULL;
193 long tt_blocksize;
194 long tt_blocksize_kb;
195 long buffer_size;
196 int tt_file_pad;
197 static unsigned long malloc_hist_1, malloc_size_1;
198 static unsigned long malloc_hist_2, malloc_size_2;
199 dumpfile_t file;
200 dumpfile_t *save_holdfile = NULL;
201 long cur_span_chunkstart = 0; /* start of current split dump chunk (Kb) */
202 char *holdfile_name;
203 int num_splits = 0;
204 int expected_splits = 0;
205 int num_holdfiles = 0;
206 times_t curdump_rt;
207
208 am_feature_t *their_features = NULL;
209
210 int runtapes, cur_tape, have_changer, tapedays;
211 char *labelstr, *conf_tapelist;
212 #ifdef HAVE_LIBVTBLC
213 char *rawtapedev;
214 int first_seg, last_seg;
215 #endif /* HAVE_LIBVTBLC */
216
217 /*
218  * ========================================================================
219  * MAIN PROGRAM
220  *
221  */
222 int main(main_argc, main_argv)
223 int main_argc;
224 char **main_argv;
225 {
226     int p2c[2], c2p[2];         /* parent-to-child, child-to-parent pipes */
227     char *conffile;
228     unsigned int size;
229     int i;
230     int j;
231     int page_size;
232     char *first_buffer;
233
234     safe_fd(-1, 0);
235
236     set_pname("taper");
237
238     /* Don't die when child closes pipe */
239     signal(SIGPIPE, SIG_IGN);
240
241     malloc_size_1 = malloc_inuse(&malloc_hist_1);
242
243     fprintf(stderr, "%s: pid %ld executable %s version %s\n",
244             get_pname(), (long) getpid(), main_argv[0], version());
245     fflush(stderr);
246
247     if (main_argc > 1 && main_argv[1][0] != '-') {
248         config_name = stralloc(main_argv[1]);
249         config_dir = vstralloc(CONFIG_DIR, "/", main_argv[1], "/", NULL);
250         main_argc--;
251         main_argv++;
252     } else {
253         char my_cwd[STR_SIZE];
254
255         if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) {
256             error("cannot determine current working directory");
257         }
258         config_dir = stralloc2(my_cwd, "/");
259         if ((config_name = strrchr(my_cwd, '/')) != NULL) {
260             config_name = stralloc(config_name + 1);
261         }
262     }
263
264     safe_cd();
265
266     install_signal_handlers();
267     atexit(cleanup);
268
269     /* print prompts and debug messages if running interactive */
270
271     interactive = (main_argc > 1 && strcmp(main_argv[1],"-t") == 0);
272     if(interactive) {
273         erroutput_type = ERR_INTERACTIVE;
274     } else {
275         erroutput_type = ERR_AMANDALOG;
276         set_logerror(logerror);
277     }
278
279     conffile = stralloc2(config_dir, CONFFILE_NAME);
280     if(read_conffile(conffile)) {
281         error("errors processing config file \"%s\"", conffile);
282     }
283     amfree(conffile);
284
285     conf_tapelist = getconf_str(CNF_TAPELIST);
286     if (*conf_tapelist == '/') {
287         conf_tapelist = stralloc(conf_tapelist);
288     } else {
289         conf_tapelist = stralloc2(config_dir, conf_tapelist);
290     }
291     if(read_tapelist(conf_tapelist)) {
292         error("could not load tapelist \"%s\"", conf_tapelist);
293     }
294
295     tapedev     = getconf_str(CNF_TAPEDEV);
296     tapetype    = getconf_str(CNF_TAPETYPE);
297     tt          = lookup_tapetype(tapetype);
298 #ifdef HAVE_LIBVTBLC
299     rawtapedev = getconf_str(CNF_RAWTAPEDEV);
300 #endif /* HAVE_LIBVTBLC */
301     tapedays    = getconf_int(CNF_TAPECYCLE);
302     labelstr    = getconf_str(CNF_LABELSTR);
303
304     runtapes    = getconf_int(CNF_RUNTAPES);
305     cur_tape    = 0;
306
307     conf_tapebufs = getconf_int(CNF_TAPEBUFS);
308
309     tt_blocksize_kb = tt->blocksize;
310     tt_blocksize = tt_blocksize_kb * 1024;
311     tt_file_pad = tt->file_pad;
312
313     if(interactive) {
314         fprintf(stderr,"taper: running in interactive test mode\n");
315         fflush(stderr);
316     }
317
318     /* create read/write syncronization pipes */
319
320     if(pipe(p2c) || pipe(c2p))
321         error("creating sync pipes: %s", strerror(errno));
322
323     /* create shared memory segment */
324
325 #if defined(HAVE_GETPAGESIZE)
326     page_size = getpagesize();
327     fprintf(stderr, "%s: page size is %d\n", get_pname(), page_size);
328 #else
329     page_size = 1024;
330     fprintf(stderr, "%s: getpagesize() not available, using %d\n",
331             get_pname(),
332             page_size);
333 #endif
334     buffer_size = am_round(tt_blocksize, page_size);
335     fprintf(stderr, "%s: buffer size is %ld\n", get_pname(), buffer_size);
336     while(conf_tapebufs > 0) {
337         size  = page_size;
338         size += conf_tapebufs * buffer_size;
339         size += conf_tapebufs * sizeof(buffer_t);
340         if((buffers = attach_buffers(size)) != NULL) {
341             break;
342         }
343         log_add(L_INFO, "attach_buffers: (%d tapebuf%s: %d bytes) %s",
344                         conf_tapebufs,
345                         (conf_tapebufs == 1) ? "" : "s",
346                         size,
347                         strerror(errno));
348         conf_tapebufs--;
349     }
350     if(buffers == NULL) {
351         error("cannot allocate shared memory");
352     }
353     i = (buffers - (char *)0) & (page_size - 1);  /* page boundary offset */
354     if(i != 0) {
355         first_buffer = buffers + page_size - i;
356         fprintf(stderr, "%s: shared memory at %p, first buffer at %p\n",
357                 get_pname(),
358                 buffers,
359                 first_buffer);
360     } else {
361         first_buffer = buffers;
362     }
363     buftable = (buffer_t *)(first_buffer + conf_tapebufs * buffer_size);
364     memset(buftable, 0, conf_tapebufs * sizeof(buffer_t));
365     if(conf_tapebufs < 10) {
366         j = 1;
367     } else if(conf_tapebufs < 100) {
368         j = 2;
369     } else {
370         j = 3;
371     }
372     for(i = 0; i < conf_tapebufs; i++) {
373         buftable[i].buffer = first_buffer + i * buffer_size;
374         fprintf(stderr, "%s: buffer[%0*d] at %p\n",
375                 get_pname(),
376                 j, i,
377                 buftable[i].buffer);
378     }
379     fprintf(stderr, "%s: buffer structures at %p for %d bytes\n",
380             get_pname(),
381             buftable,
382             (int)(conf_tapebufs * sizeof(buffer_t)));
383
384     /* fork off child writer process, parent becomes reader process */
385
386     switch(writerpid = fork()) {
387     case -1:
388         error("fork: %s", strerror(errno));
389
390     case 0:     /* child */
391         aclose(p2c[1]);
392         aclose(c2p[0]);
393
394         tape_writer_side(p2c[0], c2p[1]);
395         error("tape writer terminated unexpectedly");
396
397     default:    /* parent */
398         aclose(p2c[0]);
399         aclose(c2p[1]);
400
401         file_reader_side(c2p[0], p2c[1]);
402         error("file reader terminated unexpectedly");
403     }
404
405     /* NOTREACHED */
406     return 0;
407 }
408
409
410 /*
411  * ========================================================================
412  * FILE READER SIDE
413  *
414  */
415 int read_file P((int fd, char *handle,
416                   char *host, char *disk, char *datestamp, 
417                   int level));
418 int taper_fill_buffer P((int fd, buffer_t *bp, int buflen));
419 void dumpbufs P((char *str1));
420 void dumpstatus P((buffer_t *bp));
421 int get_next_holding_file P((int fd, buffer_t *bp, char *strclosing, int rc));
422 int predict_splits P((char *filename));
423 void create_split_buffer P((char *split_diskbuffer, long fallback_splitsize, char *id_string));
424 void free_split_buffer P(());
425
426
427 /*
428  * Create a buffer, either in an mmapped file or in memory, where PORT-WRITE
429  * dumps can buffer the current split chunk in case of retry.
430  */
431 void create_split_buffer(split_diskbuffer, fallback_splitsize, id_string)
432 char *split_diskbuffer;
433 long fallback_splitsize;
434 char *id_string;
435 {
436     char *buff_err = NULL;
437     void *nulls = NULL;
438     int c;
439     
440     /* don't bother if we're not actually splitting */
441     if(splitsize <= 0){
442         splitbuf = NULL;
443         splitbuf_wr_ptr = NULL;
444         return;
445     }
446
447 #ifdef HAVE_MMAP
448 #ifdef HAVE_SYS_MMAN_H
449     if(strcmp(split_diskbuffer, "NULL")){
450         splitbuffer_path = vstralloc(split_diskbuffer,
451                                      "/splitdump_buffer_XXXXXX",
452                                      NULL);
453 #ifdef HAVE_MKSTEMP
454         splitbuffer_fd = mkstemp(splitbuffer_path);
455 #else
456         log_add(L_INFO, "mkstemp not available, using plain open() for split buffer- make sure %s has safe permissions", split_diskbuffer);
457         splitbuffer_fd = open(splitbuffer_path, O_RDWR|O_CREAT, 0600);
458 #endif
459         if(splitbuffer_fd == -1){
460             buff_err = newvstralloc(buff_err, "mkstemp/open of ", 
461                                     splitbuffer_path, "failed (",
462                                     strerror(errno), ")", NULL);
463             goto fallback;
464         }
465         nulls = alloc(1024); /* lame */
466         memset(nulls, 0, 1024);
467         for(c = 0; c < splitsize ; c++) {
468             if(fullwrite(splitbuffer_fd, nulls, 1024) < 1024){
469                 buff_err = newvstralloc(buff_err, "write to ", splitbuffer_path,
470                                         "failed (", strerror(errno), ")", NULL);
471                 free_split_buffer();
472                 goto fallback;
473             }
474         }
475         amfree(nulls);
476
477         splitbuf = mmap(NULL, (size_t)splitsize*1024, PROT_READ|PROT_WRITE,
478                         MAP_SHARED, splitbuffer_fd, (off_t)0);
479         if(splitbuf == (char*)-1){
480             buff_err = newvstralloc(buff_err, "mmap failed (", strerror(errno),
481                                     ")", NULL);
482             free_split_buffer();
483             goto fallback;
484         }
485         fprintf(stderr,
486                 "taper: r: buffering %ldkb split chunks in mmapped file %s\n",
487                 splitsize, splitbuffer_path);
488         splitbuf_wr_ptr = splitbuf;
489         return;
490     }
491     else{
492         buff_err = stralloc("no split_diskbuffer specified");
493     }
494 #else
495     buff_err = stralloc("mman.h not available");
496     goto fallback;
497 #endif
498 #else
499     buff_err = stralloc("mmap not available");
500     goto fallback;
501 #endif
502
503     /*
504       Buffer split dumps in memory, if we can't use a file.
505     */
506     fallback:
507         splitsize = fallback_splitsize;
508         log_add(L_INFO,
509                 "%s: using fallback split size of %dkb to buffer %s in-memory",
510                 buff_err, splitsize, id_string);
511         splitbuf = alloc(splitsize * 1024);
512         splitbuf_wr_ptr = splitbuf;
513 }
514
515 /*
516  * Free up resources that create_split_buffer eats.
517  */
518 void free_split_buffer()
519 {
520     if(splitbuffer_fd != -1){
521 #ifdef HAVE_MMAP
522 #ifdef HAVE_SYS_MMAN_H
523         if(splitbuf != NULL) munmap(splitbuf, splitsize);
524 #endif
525 #endif
526         aclose(splitbuffer_fd);
527         splitbuffer_fd = -1;
528
529         if(unlink(splitbuffer_path) == -1){
530             log_add(L_WARNING, "Failed to unlink %s: %s",
531                     splitbuffer_path, strerror(errno));
532         }
533         amfree(splitbuffer_path);
534         splitbuffer_path = NULL;
535     }
536     else if(splitbuf){
537         amfree(splitbuf);
538         splitbuf = NULL;
539     }
540 }
541
542
543 void file_reader_side(rdpipe, wrpipe)
544 int rdpipe, wrpipe;
545 {
546     cmd_t cmd;
547     struct cmdargs cmdargs;
548     char *handle = NULL;
549     char *filename = NULL;
550     char *hostname = NULL;
551     char *diskname = NULL;
552     char *result = NULL;
553     char *datestamp = NULL;
554     char *split_diskbuffer = NULL;
555     char *id_string = NULL;
556     char tok;
557     char *q = NULL;
558     int level, fd, data_port, data_socket, wpid;
559     char level_str[64];
560     struct stat stat_file;
561     int tape_started;
562     int a;
563     long fallback_splitsize = 0;
564     int tmpint;
565
566     procname = "reader";
567     syncpipe_init(rdpipe, wrpipe);
568
569     /* must get START_TAPER before beginning */
570
571     startclock();
572     cmd = getcmd(&cmdargs);
573     total_wait = stopclock();
574
575     if(cmd != START_TAPER || cmdargs.argc != 2) {
576         error("error [file_reader_side cmd %d argc %d]", cmd, cmdargs.argc);
577     }
578
579     /* pass start command on to tape writer */
580
581     taper_datestamp = newstralloc(taper_datestamp, cmdargs.argv[2]);
582
583     tape_started = 0;
584     syncpipe_put('S', 0);
585     syncpipe_putstr(taper_datestamp);
586
587     /* get result of start command */
588
589     tok = syncpipe_get(&tmpint);
590     switch(tok) {
591     case 'S':
592         putresult(TAPER_OK, "\n");
593         tape_started = 1;
594         /* start is logged in writer */
595         break;
596     case 'E':
597         /* no tape, bail out */
598         result = syncpipe_getstr();
599         q = squotef("[%s]", result ? result : "(null)");
600         putresult(TAPE_ERROR, "%s\n", q);
601         amfree(q);
602         log_add(L_ERROR,"no-tape [%s]", "No writable valid tape found");
603         amfree(result);
604         syncpipe_put('e', 0);                   /* ACK error */
605         break;
606     default:
607         error("expected 'S' or 'E' for START-TAPER, got '%c'", tok);
608     }
609
610     /* process further commands */
611
612     while(1) {
613         startclock();
614         cmd = getcmd(&cmdargs);
615         if(cmd != QUIT && !tape_started) {
616             error("error [file_reader_side cmd %d without tape ready]", cmd);
617         }
618         total_wait = timesadd(total_wait, stopclock());
619
620         switch(cmd) {
621         case PORT_WRITE:
622             /*
623              * PORT-WRITE
624              *   handle
625              *   hostname
626              *   features
627              *   diskname
628              *   level
629              *   datestamp
630              *   splitsize
631              *   split_diskbuffer
632              */
633             mode = MODE_PORT_WRITE;
634             cmdargs.argc++;                     /* true count of args */
635             a = 2;
636
637             if(a >= cmdargs.argc) {
638                 error("error [taper PORT-WRITE: not enough args: handle]");
639             }
640             handle = newstralloc(handle, cmdargs.argv[a++]);
641
642             if(a >= cmdargs.argc) {
643                 error("error [taper PORT-WRITE: not enough args: hostname]");
644             }
645             hostname = newstralloc(hostname, cmdargs.argv[a++]);
646
647             if(a >= cmdargs.argc) {
648                 error("error [taper PORT-WRITE: not enough args: features]");
649             }
650             am_release_feature_set(their_features);
651             their_features = am_string_to_feature(cmdargs.argv[a++]);
652
653             if(a >= cmdargs.argc) {
654                 error("error [taper PORT-WRITE: not enough args: diskname]");
655             }
656             diskname = newstralloc(diskname, cmdargs.argv[a++]);
657
658             if(a >= cmdargs.argc) {
659                 error("error [taper PORT-WRITE: not enough args: level]");
660             }
661             level = atoi(cmdargs.argv[a++]);
662
663             if(a >= cmdargs.argc) {
664                 error("error [taper PORT-WRITE: not enough args: datestamp]");
665             }
666             datestamp = newstralloc(datestamp, cmdargs.argv[a++]);
667
668             if(a >= cmdargs.argc) {
669                 error("error [taper PORT-WRITE: not enough args: splitsize]");
670             }
671             splitsize = atoi(cmdargs.argv[a++]);
672
673             if(a >= cmdargs.argc) {
674                 error("error [taper PORT-WRITE: not enough args: split_diskbuffer]");
675             }
676             split_diskbuffer = newstralloc(split_diskbuffer, cmdargs.argv[a++]);
677
678             if(a >= cmdargs.argc) {
679                 error("error [taper PORT-WRITE: not enough args: fallback_splitsize]");
680             }
681             fallback_splitsize = atoi(cmdargs.argv[a++]);
682
683             if(a != cmdargs.argc) {
684                 error("error [taper file_reader_side PORT-WRITE: too many args: %d != %d]",
685                       cmdargs.argc, a);
686             }
687
688             snprintf(level_str, sizeof(level_str), "%d", level);
689             id_string = newvstralloc(id_string, hostname, ":", diskname, ".",
690                                      level_str, NULL);
691
692             create_split_buffer(split_diskbuffer, fallback_splitsize, id_string);
693             amfree(id_string);
694
695             data_port = 0;
696             data_socket = stream_server(&data_port, -1, STREAM_BUFSIZE);        
697             if(data_socket < 0) {
698                 char *m;
699
700                 m = vstralloc("[port create failure: ",
701                               strerror(errno),
702                               "]",
703                               NULL);
704                 q = squote(m);
705                 putresult(TAPE_ERROR, "%s %s\n", handle, q);
706                 amfree(m);
707                 amfree(q);
708                 break;
709             }
710             putresult(PORT, "%d\n", data_port);
711
712             if((fd = stream_accept(data_socket, CONNECT_TIMEOUT,
713                                    -1, NETWORK_BLOCK_BYTES)) == -1) {
714                 q = squote("[port connect timeout]");
715                 putresult(TAPE_ERROR, "%s %s\n", handle, q);
716                 aclose(data_socket);
717                 amfree(q);
718                 break;
719             }
720             expected_splits = -1;
721
722             while(read_file(fd,handle,hostname,diskname,datestamp,level));
723
724             aclose(data_socket);
725             free_split_buffer();
726             break;
727
728         case FILE_WRITE:
729             /*
730              * FILE-WRITE
731              *   handle
732              *   filename
733              *   hostname
734              *   features
735              *   diskname
736              *   level
737              *   datestamp
738              *   splitsize
739              */
740             mode = MODE_FILE_WRITE;
741             cmdargs.argc++;                     /* true count of args */
742             a = 2;
743
744             if(a >= cmdargs.argc) {
745                 error("error [taper FILE-WRITE: not enough args: handle]");
746             }
747             handle = newstralloc(handle, cmdargs.argv[a++]);
748
749             if(a >= cmdargs.argc) {
750                 error("error [taper FILE-WRITE: not enough args: filename]");
751             }
752             filename = newstralloc(filename, cmdargs.argv[a++]);
753
754             if(a >= cmdargs.argc) {
755                 error("error [taper FILE-WRITE: not enough args: hostname]");
756             }
757             hostname = newstralloc(hostname, cmdargs.argv[a++]);
758
759             if(a >= cmdargs.argc) {
760                 error("error [taper FILE-WRITE: not enough args: features]");
761             }
762             am_release_feature_set(their_features);
763             their_features = am_string_to_feature(cmdargs.argv[a++]);
764
765             if(a >= cmdargs.argc) {
766                 error("error [taper FILE-WRITE: not enough args: diskname]");
767             }
768             diskname = newstralloc(diskname, cmdargs.argv[a++]);
769
770             if(a >= cmdargs.argc) {
771                 error("error [taper FILE-WRITE: not enough args: level]");
772             }
773             level = atoi(cmdargs.argv[a++]);
774
775             if(a >= cmdargs.argc) {
776                 error("error [taper FILE-WRITE: not enough args: datestamp]");
777             }
778             datestamp = newstralloc(datestamp, cmdargs.argv[a++]);
779
780             if(a >= cmdargs.argc) {
781                 error("error [taper FILE-WRITE: not enough args: splitsize]");
782             }
783             splitsize = atoi(cmdargs.argv[a++]);
784
785             if(a != cmdargs.argc) {
786                 error("error [taper file_reader_side FILE-WRITE: too many args: %d != %d]",
787                       cmdargs.argc, a);
788             }
789             if(holdfile_name != NULL) {
790                 filename = newstralloc(filename, holdfile_name);
791             }
792
793             if((expected_splits = predict_splits(filename)) < 0) {
794                 break;
795             }
796             if(stat(filename, &stat_file)!=0) {
797                 q = squotef("[%s]", strerror(errno));
798                 putresult(TAPE_ERROR, "%s %s\n", handle, q);
799                 amfree(q);
800                 break;
801             }
802             if((fd = open(filename, O_RDONLY)) == -1) {
803                 q = squotef("[%s]", strerror(errno));
804                 putresult(TAPE_ERROR, "%s %s\n", handle, q);
805                 amfree(q);
806                 break;
807             }
808             holdfile_path = stralloc(filename);
809             holdfile_path_thischunk = stralloc(filename);
810             holdfile_offset_thischunk = (off_t)0;
811
812             while(read_file(fd,handle,hostname,diskname,datestamp,level)){
813                 if(splitsize > 0 && holdfile_path_thischunk)
814                     filename = newstralloc(filename, holdfile_path_thischunk);
815                 if((fd = open(filename, O_RDONLY)) == -1) {
816                     q = squotef("[%s]", strerror(errno));
817                     putresult(TAPE_ERROR, "%s %s\n", handle, q);
818                     amfree(q);
819                     break;
820                 }
821             }
822
823             break;
824
825         case QUIT:
826             putresult(QUITTING, "\n");
827             fprintf(stderr,"taper: DONE [idle wait: %s secs]\n",
828                     walltime_str(total_wait));
829             fflush(stderr);
830             syncpipe_put('Q', 0);       /* tell writer we're exiting gracefully */
831             aclose(wrpipe);
832
833             if((wpid = wait(NULL)) != writerpid) {
834                 fprintf(stderr,
835                         "taper: writer wait returned %d instead of %d: %s\n",
836                         wpid, writerpid, strerror(errno));
837                 fflush(stderr);
838             }
839
840             if (datestamp != NULL)
841                 amfree(datestamp);
842             amfree(label);
843             amfree(errstr);
844             amfree(changer_resultstr);
845             amfree(tapedev);
846             amfree(conf_tapelist);
847             amfree(filename);
848             amfree(config_dir);
849             amfree(config_name);
850             if(holdfile_name != NULL) amfree(holdfile_name);
851
852             malloc_size_2 = malloc_inuse(&malloc_hist_2);
853
854             if(malloc_size_1 != malloc_size_2) {
855                 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
856             }
857
858             exit(0);
859
860         default:
861             if(cmdargs.argc >= 1) {
862                 q = squote(cmdargs.argv[1]);
863             } else if(cmdargs.argc >= 0) {
864                 q = squote(cmdargs.argv[0]);
865             } else {
866                 q = stralloc("(no input?)");
867             }
868             putresult(BAD_COMMAND, "%s\n", q);
869             amfree(q);
870             break;
871         }
872     }
873     amfree(handle);
874     am_release_feature_set(their_features);
875     amfree(hostname);
876     amfree(diskname);
877     fprintf(stderr, "TAPER AT END OF READER SIDE\n");
878 }
879
880 void dumpbufs(str1)
881 char *str1;
882 {
883     int i,j;
884     long v;
885
886     fprintf(stderr, "%s: state", str1);
887     for(i = j = 0; i < conf_tapebufs; i = j+1) {
888         v = buftable[i].status;
889         for(j = i; j < conf_tapebufs && buftable[j].status == v; j++);
890         j--;
891         if(i == j) fprintf(stderr, " %d:", i);
892         else fprintf(stderr, " %d-%d:", i, j);
893         switch(v) {
894         case FULL:      fputc('F', stderr); break;
895         case FILLING:   fputc('f', stderr); break;
896         case EMPTY:     fputc('E', stderr); break;
897         default:
898             fprintf(stderr, "%ld", v);
899             break;
900         }
901
902     }
903     fputc('\n', stderr);
904     fflush(stderr);
905 }
906
907 void dumpstatus(bp)
908 buffer_t *bp;
909 {
910     char pn[2];
911     char bt[NUM_STR_SIZE];
912     char status[NUM_STR_SIZE + 1];
913     char *str = NULL;
914
915     pn[0] = procname[0];
916     pn[1] = '\0';
917     snprintf(bt, sizeof(bt), "%d", (int)(bp-buftable));
918
919     switch(bp->status) {
920     case FULL:          snprintf(status, sizeof(status), "F%d", bp->size);
921                         break;
922     case FILLING:       status[0] = 'f'; status[1] = '\0'; break;
923     case EMPTY:         status[0] = 'E'; status[1] = '\0'; break;
924     default:
925         snprintf(status, sizeof(status), "%ld", bp->status);
926         break;
927     }
928
929     str = vstralloc("taper: ", pn, ": [buf ", bt, ":=", status, "]", NULL);
930     dumpbufs(str);
931     amfree(str);
932 }
933
934 /*
935   Handle moving to the next chunk of holding file, if any.  Returns -1 for
936   errors, 0 if there's no more file, or a positive integer for the amount of
937   stuff read that'll go into 'rc' (XXX That's fugly, maybe that should just
938   be another global.  What is rc anyway, 'read count?' I keep thinking it
939   should be 'return code')
940 */
941 int get_next_holding_file(fd, bp, strclosing, rc)
942      int fd;
943      buffer_t *bp;
944      char *strclosing;
945 {
946     int save_fd, rc1;
947     struct stat stat_file;
948     int ret = -1;
949     
950     save_fd = fd;
951     close(fd);
952     
953     /* see if we're fresh out of file */
954     if(file.cont_filename[0] == '\0') {
955         err = 0;
956         ret = 0;
957     } else if(stat(file.cont_filename, &stat_file) != 0) {
958         err = errno;
959         ret = -1;
960         strclosing = newvstralloc(strclosing,"can't stat: ",file.cont_filename,NULL);
961     } else if((fd = open(file.cont_filename,O_RDONLY)) == -1) {
962         err = errno;
963         ret = -1;
964         strclosing = newvstralloc(strclosing,"can't open: ",file.cont_filename,NULL);
965     } else if((fd != save_fd) && dup2(fd, save_fd) == -1) {
966         err = errno;
967         ret = -1;
968         strclosing = newvstralloc(strclosing,"can't dup2: ",file.cont_filename,NULL);
969     } else {
970         buffer_t bp1;
971         holdfile_path = stralloc(file.cont_filename);
972         
973         fprintf(stderr, "taper: r: switching to next holding chunk '%s'\n", file.cont_filename); 
974         num_holdfile_chunks++;
975         
976         bp1.status = EMPTY;
977         bp1.size = DISK_BLOCK_BYTES;
978         bp1.buffer = malloc(DISK_BLOCK_BYTES);
979         
980         if(fd != save_fd) {
981             close(fd);
982             fd = save_fd;
983         }
984         
985         rc1 = taper_fill_buffer(fd, &bp1, DISK_BLOCK_BYTES);
986         if(rc1 <= 0) {
987             amfree(bp1.buffer);
988             err = (rc1 < 0) ? errno : 0;
989             ret = -1;
990             strclosing = newvstralloc(strclosing,
991                                       "Can't read header: ",
992                                       file.cont_filename,
993                                       NULL);
994         } else {
995             parse_file_header(bp1.buffer, &file, rc1);
996             
997             amfree(bp1.buffer);
998             bp1.buffer = bp->buffer + rc;
999             
1000             rc1 = taper_fill_buffer(fd, &bp1, tt_blocksize - rc);
1001             if(rc1 <= 0) {
1002                 err = (rc1 < 0) ? errno : 0;
1003                 ret = -1;
1004                 if(rc1 < 0) {
1005                     strclosing = newvstralloc(strclosing,
1006                                               "Can't read data: ",
1007                                               file.cont_filename,
1008                                               NULL);
1009                 }
1010             }
1011             else {
1012                 ret = rc1;
1013                 num_holdfiles++;
1014             }
1015         }
1016     }
1017     
1018     return(ret);
1019 }
1020
1021
1022 int read_file(fd, handle, hostname, diskname, datestamp, level)
1023     int fd, level;
1024     char *handle, *hostname, *diskname, *datestamp;
1025 {
1026     buffer_t *bp;
1027     char tok;
1028     int rc, opening, closing, bufnum, need_closing, nexting;
1029     long filesize;
1030     times_t runtime;
1031     char *strclosing = NULL;
1032     char seekerrstr[STR_SIZE];
1033     char *str;
1034     int header_written = 0;
1035     int buflen;
1036     dumpfile_t first_file;
1037     dumpfile_t cur_holdfile;
1038     long kbytesread = 0;
1039     int header_read = 0;
1040     char *cur_filename = NULL;
1041     int retry_from_splitbuf = 0;
1042     char *splitbuf_rd_ptr = NULL;
1043
1044     char *q = NULL;
1045
1046 #ifdef HAVE_LIBVTBLC
1047     static char desc[45];
1048     static char vol_date[20];
1049     static char vol_label[45];
1050 #endif /* HAVE_LIBVTBLC */
1051
1052
1053     /* initialize */
1054
1055     filesize = 0;
1056     closing = 0;
1057     need_closing = 0;
1058     nexting = 0;
1059     err = 0;
1060
1061     /* don't break this if we're still on the same file as a previous init */
1062     if(cur_span_chunkstart <= 0){
1063     fh_init(&file);
1064       header_read = 0;
1065     }
1066     else if(mode == MODE_FILE_WRITE){
1067       memcpy(&file, save_holdfile, sizeof(dumpfile_t));
1068       memcpy(&cur_holdfile, save_holdfile, sizeof(dumpfile_t));
1069     }
1070
1071     if(bufdebug) {
1072         fprintf(stderr, "taper: r: start file\n");
1073         fflush(stderr);
1074     }
1075
1076     for(bp = buftable; bp < buftable + conf_tapebufs; bp++) {
1077         bp->status = EMPTY;
1078     }
1079
1080     bp = buftable;
1081     if(interactive || bufdebug) dumpstatus(bp);
1082
1083     if(cur_span_chunkstart >= 0 && splitsize > 0){
1084         /* We're supposed to start at some later part of the file, not read the
1085            whole thing. "Seek" forward to where we want to be. */
1086         if(label) putresult(SPLIT_CONTINUE, "%s %s\n", handle, label);
1087         if(mode == MODE_FILE_WRITE && cur_span_chunkstart > 0){
1088             fprintf(stderr, "taper: r: seeking %s to " OFF_T_FMT " kb\n",
1089                             holdfile_path_thischunk, holdfile_offset_thischunk);
1090             fflush(stderr);
1091
1092             if(holdfile_offset_thischunk > maxseek){
1093               snprintf(seekerrstr, sizeof(seekerrstr), "Can't seek by " OFF_T_FMT " kb (compiled for %d-bit file offsets), recompile with large file support or set holdingdisk chunksize to <%ld Mb", holdfile_offset_thischunk, (int)(sizeof(off_t) * 8), (long)(maxseek/1024));
1094               log_add(L_ERROR, "%s", seekerrstr);
1095               fprintf(stderr, "taper: r: FATAL: %s\n", seekerrstr);
1096               fflush(stderr);
1097               syncpipe_put('X', 0);
1098               return -1;
1099             }
1100             if(lseek(fd, holdfile_offset_thischunk*1024, SEEK_SET) == (off_t)-1){
1101               fprintf(stderr, "taper: r: FATAL: seek_holdfile lseek error while seeking into %s by " OFF_T_FMT "kb: %s\n", holdfile_path_thischunk, holdfile_offset_thischunk, strerror(errno));
1102               fflush(stderr);
1103               syncpipe_put('X', 0);
1104               return -1;
1105             }
1106         }
1107         else if(mode == MODE_PORT_WRITE){
1108             fprintf(stderr, "taper: r: re-reading split dump piece from buffer\n");
1109             fflush(stderr);
1110             retry_from_splitbuf = 1;
1111             splitbuf_rd_ptr = splitbuf;
1112             if(splitbuf_rd_ptr >= splitbuf_wr_ptr) retry_from_splitbuf = 0;
1113         }
1114         if(cur_span_chunkstart > 0) header_read = 1; /* really initialized in prior run */
1115     }
1116
1117     /* tell writer to open tape */
1118
1119     opening = 1;
1120     syncpipe_put('O', 0);
1121     syncpipe_putstr(datestamp);
1122     syncpipe_putstr(hostname);
1123     syncpipe_putstr(diskname);
1124     syncpipe_putint(level);
1125
1126     startclock();
1127     
1128     /* read file in loop */
1129     
1130     while(1) {
1131         tok = syncpipe_get(&bufnum);
1132         switch(tok) {
1133             
1134         case 'O':
1135             assert(opening);
1136             opening = 0;
1137             err = 0;
1138             break;
1139             
1140         case 'R':
1141             if(bufdebug) {
1142                 fprintf(stderr, "taper: r: got R%d\n", bufnum);
1143                 fflush(stderr);
1144             }
1145             
1146             if(need_closing) {
1147                 syncpipe_put('C', 0);
1148                 closing = 1;
1149                 need_closing = 0;
1150                 break;
1151             }
1152             
1153             if(closing) break;  /* ignore extra read tokens */
1154             
1155             assert(!opening);
1156             if(bp->status != EMPTY || bufnum != bp-buftable) {
1157                 /* XXX this SHOULD NOT HAPPEN.  Famous last words. */
1158                 fprintf(stderr,"taper: panic: buffer mismatch at ofs %ld:\n",
1159                         filesize);
1160                 if(bufnum != bp-buftable) {
1161                     fprintf(stderr, "    my buf %d but writer buf %d\n",
1162                             (int)(bp-buftable), bufnum);
1163                 }
1164                 else {
1165                     fprintf(stderr,"buf %d state %s (%ld) instead of EMPTY\n",
1166                             (int)(bp-buftable),
1167                             bp->status == FILLING? "FILLING" :
1168                             bp->status == FULL? "FULL" : "EMPTY!?!?",
1169                             (long)bp->status);
1170                 }
1171                 dumpbufs("taper");
1172                 sleep(1);
1173                 dumpbufs("taper: after 1 sec");
1174                 if(bp->status == EMPTY)
1175                     fprintf(stderr, "taper: result now correct!\n");
1176                 fflush(stderr);
1177                 
1178                 errstr = newstralloc(errstr,
1179                                      "[fatal buffer mismanagement bug]");
1180                 q = squote(errstr);
1181                 putresult(TRYAGAIN, "%s %s\n", handle, q);
1182                 cur_span_chunkstart = 0;
1183                 amfree(q);
1184                 log_add(L_INFO, "retrying %s:%s.%d on new tape due to: %s",
1185                         hostname, diskname, level, errstr);
1186                 closing = 1;
1187                 syncpipe_put('X', 0);   /* X == buffer snafu, bail */
1188                 do {
1189                     tok = syncpipe_get(&bufnum);
1190                 } while(tok != 'x');
1191                 aclose(fd);
1192                 return -1;
1193             } /* end 'if (bf->status != EMPTY || bufnum != bp-buftable)' */
1194
1195             bp->status = FILLING;
1196             buflen = header_read ? tt_blocksize : DISK_BLOCK_BYTES;
1197             if(interactive || bufdebug) dumpstatus(bp);
1198             if(header_written == 0 && (header_read == 1 || cur_span_chunkstart > 0)){
1199                 /* for split dumpfiles, modify headers for the second - nth
1200                    pieces that signify that they're continuations of the last
1201                    normal one */
1202                 char *cont_filename;
1203                 file.type = F_SPLIT_DUMPFILE;
1204                 file.partnum = num_splits + 1;
1205                 file.totalparts = expected_splits;
1206                  cont_filename = stralloc(file.cont_filename);
1207                 file.cont_filename[0] = '\0';
1208                 build_header(bp->buffer, &file, tt_blocksize);
1209   
1210                 if(cont_filename[0] != '\0') {
1211                   file.type = F_CONT_DUMPFILE;
1212                    strncpy(file.cont_filename, cont_filename,
1213                            sizeof(file.cont_filename));
1214                         }
1215                 memcpy(&cur_holdfile, &file, sizeof(dumpfile_t));
1216   
1217                 if(interactive || bufdebug) dumpstatus(bp);
1218                 bp->size = tt_blocksize;
1219                 rc = tt_blocksize;
1220                 header_written = 1;
1221                 amfree(cont_filename);
1222                         }
1223             else if(retry_from_splitbuf){
1224                 /* quietly pull dump data from our in-memory cache, and the
1225                    writer side need never know the wiser */
1226                 memcpy(bp->buffer, splitbuf_rd_ptr, tt_blocksize);
1227                 bp->size = tt_blocksize;
1228                 rc = tt_blocksize;
1229  
1230                 splitbuf_rd_ptr += tt_blocksize;
1231                 if(splitbuf_rd_ptr >= splitbuf_wr_ptr) retry_from_splitbuf = 0;
1232             }
1233             else if((rc = taper_fill_buffer(fd, bp, buflen)) < 0) {
1234                 err = errno;
1235                 closing = 1;
1236                 strclosing = newvstralloc(strclosing,"Can't read data: ",NULL);
1237                 syncpipe_put('C', 0);
1238             }
1239   
1240             if(!closing) {
1241                 if(rc < buflen) { /* switch to next holding file */
1242                     int ret;
1243                     if(file.cont_filename[0] != '\0'){
1244                        cur_filename = newvstralloc(cur_filename, file.cont_filename, NULL);
1245                                 }
1246                     ret = get_next_holding_file(fd, bp, strclosing, rc);
1247                     if(ret <= 0){
1248                         need_closing = 1;
1249                             }
1250                             else {
1251                         memcpy(&cur_holdfile, &file, sizeof(dumpfile_t));
1252                         rc += ret;
1253                                 bp->size = rc;
1254                             }
1255                         }
1256                 if(rc > 0) {
1257                     bp->status = FULL;
1258                     /* rebuild the header block, which might have CONT junk */
1259                     if(header_read == 0) {
1260                         char *cont_filename;
1261                         /* write the "real" filename if the holding-file
1262                            is a partial one */
1263                         parse_file_header(bp->buffer, &file, rc);
1264                         parse_file_header(bp->buffer, &first_file, rc);
1265                         cont_filename = stralloc(file.cont_filename);
1266                         file.cont_filename[0] = '\0';
1267                         if(splitsize > 0){
1268                             file.type = F_SPLIT_DUMPFILE;
1269                             file.partnum = 1;
1270                             file.totalparts = expected_splits;
1271                         }
1272                         file.blocksize = tt_blocksize;
1273                         build_header(bp->buffer, &file, tt_blocksize);
1274                         kbytesread += tt_blocksize/1024; /* XXX shady */
1275  
1276                         file.type = F_CONT_DUMPFILE;
1277  
1278                         /* add CONT_FILENAME back to in-memory header */
1279                         strncpy(file.cont_filename, cont_filename, 
1280                                 sizeof(file.cont_filename));
1281                         if(interactive || bufdebug) dumpstatus(bp);
1282                         bp->size = tt_blocksize; /* output a full tape block */
1283                         /* save the header, we'll need it if we jump tapes */
1284                         memcpy(&cur_holdfile, &file, sizeof(dumpfile_t));
1285                         header_read = 1;
1286                         header_written = 1;
1287                         amfree(cont_filename);
1288                     }
1289                     else {
1290                         filesize = kbytesread;
1291                     }
1292
1293                     if(bufdebug) {
1294                         fprintf(stderr,"taper: r: put W%d\n",(int)(bp-buftable));
1295                         fflush(stderr);
1296                     }
1297                     syncpipe_put('W', bp-buftable);
1298                     bp = nextbuf(bp);
1299                 }
1300
1301                 if(kbytesread + DISK_BLOCK_BYTES/1024 >= splitsize && splitsize > 0 && !need_closing){
1302
1303                     if(mode == MODE_PORT_WRITE){
1304                         splitbuf_wr_ptr = splitbuf;
1305                         splitbuf_rd_ptr = splitbuf;
1306                         memset(splitbuf, 0, sizeof(splitbuf));
1307                         retry_from_splitbuf = 0;
1308                     }
1309
1310                     fprintf(stderr,"taper: r: end %s.%s.%s.%d part %d, splitting chunk that started at %ldkb after %ldkb (next chunk will start at %ldkb)\n", hostname, diskname, datestamp, level, num_splits+1, cur_span_chunkstart, kbytesread, cur_span_chunkstart+kbytesread);
1311                     fflush(stderr);
1312
1313                     nexting = 1;
1314                     need_closing = 1;
1315                 } /* end '(kbytesread >= splitsize && splitsize > 0)' */
1316                 if(need_closing && rc <= 0) {
1317                     syncpipe_put('C', 0);
1318                     need_closing = 0;
1319                     closing = 1;
1320                 }
1321                 kbytesread += rc/1024;
1322             } /* end the 'if(!closing)' (successful buffer fill) */
1323             break;
1324
1325         case 'T':
1326         case 'E':
1327             syncpipe_put('e', 0);       /* ACK error */
1328
1329             str = syncpipe_getstr();
1330             errstr = newvstralloc(errstr, "[", str ? str : "(null)", "]", NULL);
1331             amfree(str);
1332
1333             q = squote(errstr);
1334             if(tok == 'T') {
1335                 if(splitsize > 0){
1336                     /* we'll be restarting this chunk on the next tape */
1337                     if(mode == MODE_FILE_WRITE){
1338                       aclose(fd);
1339                     }
1340
1341                     putresult(SPLIT_NEEDNEXT, "%s %ld\n", handle, cur_span_chunkstart);
1342                     log_add(L_INFO, "continuing %s:%s.%d on new tape from %ldkb mark: %s",
1343                             hostname, diskname, level, cur_span_chunkstart, errstr);
1344                     return 1;
1345                 }
1346                 else{
1347                     /* restart the entire dump (failure propagates to driver) */
1348                     aclose(fd);
1349                     putresult(TRYAGAIN, "%s %s\n", handle, q);
1350                     cur_span_chunkstart = 0;
1351                     log_add(L_INFO, "retrying %s:%s.%d on new tape due to: %s",
1352                             hostname, diskname, level, errstr);
1353                 }
1354             } else {
1355                 aclose(fd);
1356                 putresult(TAPE_ERROR, "%s %s\n", handle, q);
1357                 log_add(L_FAIL, "%s %s %s %d [out of tape]",
1358                         hostname, diskname, datestamp, level);
1359                 log_add(L_ERROR,"no-tape [%s]", "No more writable valid tape found");
1360             }
1361             amfree(q);
1362
1363             return 0;
1364
1365         case 'C':
1366             assert(!opening);
1367             assert(closing);
1368
1369             if(nexting){
1370               cur_span_chunkstart += kbytesread; /* XXX possibly wrong */
1371               holdfile_name = newvstralloc(holdfile_name, cur_filename, NULL);
1372
1373               kbytesread = 0;
1374               if(cur_filename != NULL) amfree(cur_filename);
1375             }
1376
1377
1378             str = syncpipe_getstr();
1379             label = newstralloc(label, str ? str : "(null)");
1380             amfree(str);
1381             str = syncpipe_getstr();
1382             filenum = atoi(str ? str : "-9876");        /* ??? */
1383             amfree(str);
1384             fprintf(stderr, "taper: reader-side: got label %s filenum %d\n",
1385                     label, filenum);
1386             fflush(stderr);
1387
1388             /* we'll need that file descriptor if we're gonna write more */
1389             if(!nexting){
1390             aclose(fd);
1391             }
1392
1393             runtime = stopclock();
1394             if(nexting) startclock();
1395             if(err) {
1396                 if(strclosing) {
1397                     errstr = newvstralloc(errstr,
1398                                           "[input: ", strclosing, ": ",
1399                                           strerror(err), "]", NULL);
1400                     amfree(strclosing);
1401                 }
1402                 else
1403                     errstr = newvstralloc(errstr,
1404                                           "[input: ", strerror(err), "]",
1405                                           NULL);
1406                 q = squote(errstr);
1407                 putresult(TAPE_ERROR, "%s %s\n", handle, q);
1408
1409                 amfree(q);
1410                 if(splitsize){
1411                   log_add(L_FAIL, "%s %s %s.%d %d %s", hostname, diskname,
1412                           datestamp, num_splits, level, errstr);
1413                 }
1414                 else{
1415                 log_add(L_FAIL, "%s %s %s %d %s",
1416                         hostname, diskname, datestamp, level, errstr);
1417                 }
1418                 str = syncpipe_getstr();        /* reap stats */
1419                 amfree(str);
1420                 amfree(errstr);
1421             } else {
1422                 char kb_str[NUM_STR_SIZE];
1423                 char kps_str[NUM_STR_SIZE];
1424                 double rt;
1425
1426                 rt = runtime.r.tv_sec+runtime.r.tv_usec/1000000.0;
1427                 curdump_rt = timesadd(runtime, curdump_rt);
1428                 snprintf(kb_str, sizeof(kb_str), "%ld", filesize);
1429                 snprintf(kps_str, sizeof(kps_str), "%3.1f",
1430                                      rt ? filesize / rt : 0.0);
1431                 str = syncpipe_getstr();
1432                 errstr = newvstralloc(errstr,
1433                                       "[sec ", walltime_str(runtime),
1434                                       " kb ", kb_str,
1435                                       " kps ", kps_str,
1436                                       " ", str,
1437                                       "]",
1438                                       NULL);
1439                 q = squote(errstr);
1440                 if (splitsize == 0) { /* Ordinary dump */
1441                     if(first_file.is_partial) {
1442                         putresult(PARTIAL, "%s %s %d %s\n",
1443                                   handle, label, filenum, q);
1444                         log_add(L_PARTIAL, "%s %s %s %d %s",
1445                                 hostname, diskname, datestamp, level, errstr);
1446                     }
1447                     else {
1448                         putresult(DONE, "%s %s %d %s\n",
1449                                   handle, label, filenum, q);
1450                         log_add(L_SUCCESS, "%s %s %s %d %s",
1451                                 hostname, diskname, datestamp, level, errstr);
1452                     }
1453                 } else { /* Chunked dump */
1454                     num_splits++;
1455                     if(mode == MODE_FILE_WRITE){
1456                         holdfile_path_thischunk = stralloc(holdfile_path);
1457                         holdfile_offset_thischunk = (lseek(fd, (off_t)0, SEEK_CUR))/1024;
1458                         if(!save_holdfile){
1459                             save_holdfile = alloc(sizeof(dumpfile_t));
1460                         }
1461                         memcpy(save_holdfile, &cur_holdfile,sizeof(dumpfile_t));
1462                     }
1463                     log_add(L_CHUNK, "%s %s %s %d %d %s", hostname, diskname,
1464                             datestamp, num_splits, level, errstr);
1465                     if(!nexting){ /* split dump complete */
1466                         rt =curdump_rt.r.tv_sec+curdump_rt.r.tv_usec/1000000.0;
1467                         snprintf(kb_str, sizeof(kb_str), "%ld",
1468                                     filesize+cur_span_chunkstart);
1469                         snprintf(kps_str, sizeof(kps_str), "%3.1f",
1470                                     rt ? (filesize+cur_span_chunkstart) / rt : 0.0);
1471                         amfree(errstr);
1472                         errstr = newvstralloc(errstr,
1473                                               "[sec ", walltime_str(curdump_rt),
1474                                               " kb ", kb_str,
1475                                               " kps ", kps_str,
1476                                               " ", str,
1477                                               "]",
1478                                               NULL);
1479                         q = squote(errstr);
1480                         putresult(DONE, "%s %s %d %s\n", handle, label,
1481                                   filenum, q);
1482                         log_add(L_CHUNKSUCCESS, "%s %s %s %d %s",
1483                                 hostname, diskname, datestamp, level, errstr);
1484                         amfree(save_holdfile);
1485                         amfree(holdfile_path_thischunk);
1486                         amfree(q);
1487                     }
1488                 }
1489                 amfree(str);
1490
1491                 if(!nexting){
1492                     num_splits = 0;
1493                     expected_splits = 0;
1494                     amfree(holdfile_name);
1495                     num_holdfiles = 0;
1496                     cur_span_chunkstart = 0;
1497                     curdump_rt = times_zero;
1498                 }
1499                 
1500                 amfree(errstr);
1501                 
1502 #ifdef HAVE_LIBVTBLC
1503                 /* 
1504                  *  We have 44 characters available for the label string:
1505                  *  use max 20 characters for hostname
1506                  *      max 20 characters for diskname 
1507                  *             (it could contain a samba share or dos path)
1508                  *           2 for level
1509                  */
1510                 memset(desc, '\0', 45);
1511
1512                 strncpy(desc, hostname, 20);
1513
1514                 if ((len = strlen(hostname)) <= 20) {
1515                     memset(desc + len, ' ', 1);
1516                     offset = len + 1;
1517                 }
1518                 else{
1519                     memset(desc + 20, ' ', 1);
1520                     offset = 21;
1521                 }
1522
1523                 strncpy(desc + offset, diskname, 20);
1524
1525                 if ((len = strlen(diskname)) <= 20) {
1526                     memset(desc + offset + len, ' ', 1);
1527                     offset = offset + len + 1;
1528                 }
1529                 else{
1530                     memset(desc + offset + 20, ' ', 1);
1531                     offset = offset + 21;
1532                 }
1533
1534                 sprintf(desc + offset, "%i", level);
1535
1536                 strncpy(vol_label, desc, 44);
1537                 fprintf(stderr, "taper: added vtbl label string %i: \"%s\"\n",
1538                         filenum, vol_label);
1539                 fflush(stderr);
1540
1541                 /* pass label string on to tape writer */
1542                 syncpipe_put('L', filenum);
1543                 syncpipe_putstr(vol_label);             
1544
1545                 /* 
1546                  * reformat datestamp for later use with set_date from vtblc 
1547                  */
1548                 strptime(datestamp, "%Y%m%d", &backup_time);
1549                 strftime(vol_date, 20, "%T %D", &backup_time);
1550                 fprintf(stderr, 
1551                         "taper: reformatted vtbl date string: \"%s\"->\"%s\"\n",
1552                         datestamp,
1553                         vol_date);
1554
1555                 /* pass date string on to tape writer */                
1556                 syncpipe_put('D', filenum);
1557                 syncpipe_putstr(vol_date);
1558
1559 #endif /* HAVE_LIBVTBLC */
1560             }
1561             /* reset stuff that assumes we're on a new file */
1562
1563             if(nexting){
1564                 opening = 1;
1565                 nexting = 0;
1566                 closing = 0;
1567                 filesize = 0;
1568                 syncpipe_put('O', 0);
1569                 syncpipe_putstr(datestamp);
1570                 syncpipe_putstr(hostname);
1571                 syncpipe_putstr(diskname);
1572                 syncpipe_putint(level);
1573                 for(bp = buftable; bp < buftable + conf_tapebufs; bp++) {
1574                     bp->status = EMPTY;
1575                 }
1576                 bp = buftable;
1577                 header_written = 0;
1578                 break;
1579             }
1580             else return 0;
1581
1582         default:
1583             assert(0);
1584         }
1585     }
1586
1587     return 0;
1588 }
1589
1590 int taper_fill_buffer(fd, bp, buflen)
1591 int fd;
1592 buffer_t *bp;
1593 int buflen;
1594 {
1595     char *curptr;
1596     int spaceleft, cnt;
1597
1598     curptr = bp->buffer;
1599     bp->size = 0;
1600     spaceleft = buflen;
1601
1602     cnt = fullread(fd, curptr, spaceleft);
1603     switch(cnt) {
1604     case 0:     /* eof */
1605         if(interactive) fputs("r0", stderr);
1606         return bp->size;
1607     case -1:    /* error on read, punt */
1608         if(interactive) fputs("rE", stderr);
1609         return -1;
1610     default:
1611         if(mode == MODE_PORT_WRITE && splitsize > 0){
1612             memcpy(splitbuf_wr_ptr, curptr, (size_t)cnt);
1613             splitbuf_wr_ptr += cnt;
1614         }
1615         spaceleft -= cnt;
1616         curptr += cnt;
1617         bp->size += cnt;
1618     }
1619
1620     if(interactive) fputs("R", stderr);
1621     return bp->size;
1622 }
1623
1624 /* Given a dumpfile in holding, determine its size and figure out how many
1625  * times we'd have to split it.
1626  */
1627 int predict_splits(filename)
1628 char *filename;
1629 {
1630     int splits = 0;
1631     long total_kb = 0;
1632     long adj_splitsize = splitsize - DISK_BLOCK_BYTES/1024;
1633
1634     if(splitsize <= 0) return(0);
1635
1636     if(adj_splitsize <= 0){
1637       error("Split size must be > %ldk", DISK_BLOCK_BYTES/1024);
1638     }
1639
1640     /* should only calculuate this once, not on retries etc */
1641     if(expected_splits != 0) return(expected_splits);
1642
1643     total_kb = size_holding_files(filename, 1);
1644     
1645     if(total_kb <= 0){
1646       fprintf(stderr, "taper: r: %ld kb holding file makes no sense, not precalculating splits\n", total_kb);
1647       fflush(stderr);
1648       return(0);
1649     }
1650
1651     fprintf(stderr, "taper: r: Total dump size should be %ldkb, chunk size is %ldkb\n", total_kb, splitsize);
1652     fflush(stderr);
1653
1654     splits = total_kb/adj_splitsize;
1655     if(total_kb % adj_splitsize) splits++;
1656
1657
1658     fprintf(stderr, "taper: r: Expecting to split into %d parts \n", splits);
1659     fflush(stderr);
1660
1661     return(splits);
1662 }
1663
1664 /*
1665  * ========================================================================
1666  * TAPE WRITER SIDE
1667  *
1668  */
1669 times_t idlewait, rdwait, wrwait, fmwait;
1670 long total_writes;
1671 double total_tape_used;
1672 int total_tape_fm;
1673
1674 void write_file P((void));
1675 int write_buffer P((buffer_t *bp));
1676
1677 void tape_writer_side(getp, putp)
1678 int getp, putp;
1679 {
1680     char tok;
1681     int tape_started;
1682     char *str;
1683     char *hostname;
1684     char *diskname;
1685     char *datestamp;
1686     int level;
1687     int tmpint;
1688
1689 #ifdef HAVE_LIBVTBLC
1690     char *vol_label;
1691     char *vol_date;
1692 #endif /* HAVE_LIBVTBLC */
1693
1694     procname = "writer";
1695     syncpipe_init(getp, putp);
1696
1697     tape_started = 0;
1698     idlewait = times_zero;
1699
1700     while(1) {
1701         startclock();
1702         tok = syncpipe_get(&tmpint);
1703         idlewait = timesadd(idlewait, stopclock());
1704         if(tok != 'S' && tok != 'Q' && !tape_started) {
1705             error("writer: token '%c' before start", tok);
1706         }
1707
1708         switch(tok) {
1709         case 'S':               /* start-tape */
1710             if(tape_started) {
1711                 error("writer: multiple start requests");
1712             }
1713             str = syncpipe_getstr();
1714             if(!first_tape(str ? str : "bad-datestamp")) {
1715                 if(tape_fd >= 0) {
1716                     tapefd_close(tape_fd);
1717                     tape_fd = -1;
1718                 }
1719                 syncpipe_put('E', 0);
1720                 syncpipe_putstr(errstr);
1721                 /* wait for reader to acknowledge error */
1722                 do {
1723                     tok = syncpipe_get(&tmpint);
1724                     if(tok != 'e') {
1725                         error("writer: got '%c' unexpectedly after error", tok);
1726                     }
1727                 } while(tok != 'e');
1728             } else {
1729                 syncpipe_put('S', 0);
1730                 tape_started = 1;
1731             }
1732             amfree(str);
1733
1734             break;
1735
1736         case 'O':               /* open-output */
1737             datestamp = syncpipe_getstr();
1738             tapefd_setinfo_datestamp(tape_fd, datestamp);
1739             amfree(datestamp);
1740             hostname = syncpipe_getstr();
1741             tapefd_setinfo_host(tape_fd, hostname);
1742             amfree(hostname);
1743             diskname = syncpipe_getstr();
1744             tapefd_setinfo_disk(tape_fd, diskname);
1745             amfree(diskname);
1746             level = syncpipe_getint();
1747             tapefd_setinfo_level(tape_fd, level);
1748             write_file();
1749             break;
1750
1751 #ifdef HAVE_LIBVTBLC
1752         case 'L':               /* read vtbl label */
1753             vtbl_no = tmpint;
1754             vol_label = syncpipe_getstr();
1755             fprintf(stderr, "taper: read label string \"%s\" from pipe\n", 
1756                     vol_label);
1757             strncpy(vtbl_entry[vtbl_no].label, vol_label, 45);
1758             break;
1759
1760         case 'D':               /* read vtbl date */
1761             vtbl_no = tmpint;
1762             vol_date = syncpipe_getstr();
1763             fprintf(stderr, "taper: read date string \"%s\" from pipe\n", 
1764                     vol_date);
1765             strncpy(vtbl_entry[vtbl_no].date, vol_date, 20);
1766             break;
1767 #endif /* HAVE_LIBVTBLC */
1768
1769         case 'Q':
1770             end_tape(0);        /* XXX check results of end tape ?? */
1771             clear_tapelist();
1772             amfree(taper_datestamp);
1773             amfree(label);
1774             amfree(errstr);
1775             amfree(changer_resultstr);
1776             amfree(tapedev);
1777             amfree(conf_tapelist);
1778             amfree(config_dir);
1779             amfree(config_name);
1780
1781             malloc_size_2 = malloc_inuse(&malloc_hist_2);
1782
1783             if(malloc_size_1 != malloc_size_2) {
1784                 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
1785             }
1786
1787             exit(0);
1788
1789         default:
1790             assert(0);
1791         }
1792     }
1793 }
1794
1795 void write_file()
1796 {
1797     buffer_t *bp;
1798     int full_buffers, i, bufnum;
1799     char tok;
1800     char number[NUM_STR_SIZE];
1801     char *rdwait_str, *wrwait_str, *fmwait_str;
1802     int tmpint;
1803
1804     rdwait = wrwait = times_zero;
1805     total_writes = 0;
1806
1807     bp = buftable;
1808     full_buffers = 0;
1809     tok = '?';
1810
1811     if(bufdebug) {
1812         fprintf(stderr, "taper: w: start file\n");
1813         fflush(stderr);
1814     }
1815
1816     /*
1817      * Tell the reader that the tape is open, and give it all the buffers.
1818      */
1819     syncpipe_put('O', 0);
1820     for(i = 0; i < conf_tapebufs; i++) {
1821         if(bufdebug) {
1822             fprintf(stderr, "taper: w: put R%d\n", i);
1823             fflush(stderr);
1824         }
1825         syncpipe_put('R', i);
1826     }
1827
1828     /*
1829      * We write the filemark at the start of the file rather than at the end,
1830      * so that it can proceed in parallel with the reader's initial filling
1831      * up of the buffers.
1832      */
1833
1834     startclock();
1835     if(!write_filemark())
1836         goto tape_error;
1837     fmwait = stopclock();
1838
1839     filenum += 1;
1840
1841     do {
1842
1843         /*
1844          * STOPPED MODE
1845          *
1846          * At the start of the file, or if the input can't keep up with the
1847          * tape, we enter STOPPED mode, which waits for most of the buffers
1848          * to fill up before writing to tape.  This maximizes the amount of
1849          * data written in chunks to the tape drive, minimizing the number
1850          * of starts/stops, which in turn saves tape and time.
1851          */
1852
1853         if(interactive) fputs("[WS]", stderr);
1854         startclock();
1855         while(full_buffers < conf_tapebufs - THRESHOLD) {
1856             tok = syncpipe_get(&bufnum);
1857             if(tok != 'W') break;
1858             if(bufdebug) {
1859                 fprintf(stderr,"taper: w: got W%d\n",bufnum);
1860                 fflush(stderr);
1861             }
1862             full_buffers++;
1863         }
1864         rdwait = timesadd(rdwait, stopclock());
1865
1866         /*
1867          * STARTING MODE
1868          *
1869          * We start output when sufficient buffers have filled up, or at
1870          * end-of-file, whichever comes first.  Here we drain all the buffers
1871          * that were waited on in STOPPED mode.  If more full buffers come
1872          * in, then we will be STREAMING.
1873          */
1874
1875         while(full_buffers) {
1876             if(tt_file_pad && bp->size < tt_blocksize) {
1877                 memset(bp->buffer+bp->size, 0, tt_blocksize - bp->size);
1878                 bp->size = tt_blocksize;
1879             }
1880             if(!write_buffer(bp)) goto tape_error;
1881             full_buffers--;
1882             bp = nextbuf(bp);
1883         }
1884
1885         /*
1886          * STREAMING MODE
1887          *
1888          * With any luck, the input source is faster than the tape drive.  In
1889          * this case, full buffers will appear in the circular queue faster
1890          * than we can write them, so the next buffer in the queue will always
1891          * be marked FULL by the time we get to it.  If so, we'll stay in
1892          * STREAMING mode.
1893          *
1894          * On the other hand, if we catch up to the input and thus would have
1895          * to wait for buffers to fill, we are then STOPPED again.
1896          */
1897
1898         while(tok == 'W' && bp->status == FULL) {
1899             tok = syncpipe_get(&bufnum);
1900             if(tok == 'W') {
1901                 if(bufdebug) {
1902                     fprintf(stderr,"taper: w: got W%d\n",bufnum);
1903                     fflush(stderr);
1904                 }
1905                 if(bufnum != bp-buftable) {
1906                     fprintf(stderr,
1907                             "taper: tape-writer: my buf %d reader buf %d\n",
1908                             (int)(bp-buftable), bufnum);
1909                     fflush(stderr);
1910                     syncpipe_put('E', 0);
1911                     syncpipe_putstr("writer-side buffer mismatch");
1912                     goto error_ack;
1913                 }
1914                 if(tt_file_pad && bp->size < tt_blocksize) {
1915                     memset(bp->buffer+bp->size, 0, tt_blocksize - bp->size);
1916                     bp->size = tt_blocksize;
1917                 }
1918                 if(!write_buffer(bp)) goto tape_error;
1919                 bp = nextbuf(bp);
1920             }
1921             else if(tok == 'Q')
1922                 return;
1923             else if(tok == 'X')
1924                 goto reader_buffer_snafu;
1925             else
1926                 error("writer-side not expecting token: %c", tok);
1927         }
1928     } while(tok == 'W');
1929
1930     /* got close signal from reader, acknowledge it */
1931
1932     if(tok == 'X')
1933         goto reader_buffer_snafu;
1934
1935     assert(tok == 'C');
1936     syncpipe_put('C', 0);
1937
1938     /* tell reader the tape and file number */
1939
1940     syncpipe_putstr(label);
1941     snprintf(number, sizeof(number), "%d", filenum);
1942     syncpipe_putstr(number);
1943
1944     snprintf(number, sizeof(number), "%ld", total_writes);
1945     rdwait_str = stralloc(walltime_str(rdwait));
1946     wrwait_str = stralloc(walltime_str(wrwait));
1947     fmwait_str = stralloc(walltime_str(fmwait));
1948     errstr = newvstralloc(errstr,
1949                           "{wr:",
1950                           " writers ", number,
1951                           " rdwait ", rdwait_str,
1952                           " wrwait ", wrwait_str,
1953                           " filemark ", fmwait_str,
1954                           "}",
1955                           NULL);
1956     amfree(rdwait_str);
1957     amfree(wrwait_str);
1958     amfree(fmwait_str);
1959     syncpipe_putstr(errstr);
1960
1961     /* XXX go to next tape if past tape size? */
1962
1963     return;
1964
1965  tape_error:
1966     /* got tape error */
1967     if(next_tape(1)) syncpipe_put('T', 0);      /* next tape in place, try again */
1968     else syncpipe_put('E', 0);          /* no more tapes, fail */
1969     syncpipe_putstr(errstr);
1970
1971  error_ack:
1972     /* wait for reader to acknowledge error */
1973     do {
1974         tok = syncpipe_get(&tmpint);
1975         if(tok != 'W' && tok != 'C' && tok != 'e')
1976             error("writer: got '%c' unexpectedly after error", tok);
1977     } while(tok != 'e');
1978     return;
1979
1980  reader_buffer_snafu:
1981     syncpipe_put('x', 0);
1982     return;
1983 }
1984
1985 int write_buffer(bp)
1986 buffer_t *bp;
1987 {
1988     int rc;
1989
1990     assert(bp->status == FULL);
1991
1992     startclock();
1993     rc = tapefd_write(tape_fd, bp->buffer, bp->size);
1994     if(rc == bp->size) {
1995 #if defined(NEED_RESETOFS)
1996         static double tape_used_modulus_2gb = 0;
1997
1998         /*
1999          * If the next write will go over the 2 GByte boundary, reset
2000          * the kernel concept of where we are to make sure it does not
2001          * go silly on us.
2002          */
2003         tape_used_modulus_2gb += (double)rc;
2004         if(tape_used_modulus_2gb + (double)rc > (double)0x7fffffff) {
2005             tape_used_modulus_2gb = 0;
2006             tapefd_resetofs(tape_fd);
2007         }
2008 #endif
2009         wrwait = timesadd(wrwait, stopclock());
2010         total_writes += 1;
2011         total_tape_used += (double)rc;
2012         bp->status = EMPTY;
2013         if(interactive || bufdebug) dumpstatus(bp);
2014         if(interactive) fputs("W", stderr);
2015
2016         if(bufdebug) {
2017             fprintf(stderr, "taper: w: put R%d\n", (int)(bp-buftable));
2018             fflush(stderr);
2019         }
2020         syncpipe_put('R', bp-buftable);
2021         return 1;
2022     } else {
2023         errstr = newvstralloc(errstr,
2024                               "writing file: ",
2025                               (rc != -1) ? "short write" : strerror(errno),
2026                               NULL);
2027         wrwait = timesadd(wrwait, stopclock());
2028         if(interactive) fputs("[WE]", stderr);
2029         return 0;
2030     }
2031 }
2032
2033
2034 static void 
2035 cleanup(void)
2036 {
2037     REMOVE_SHARED_MEMORY(); 
2038 }
2039
2040
2041 /*
2042  * Cleanup shared memory segments 
2043  */
2044 static void 
2045 signal_handler(int signum)
2046 {
2047     log_add(L_INFO, "Received signal %d", signum);
2048
2049     exit(1);
2050 }
2051
2052
2053 /*
2054  * Installing signal handlers for signal whose default action is 
2055  * process termination so that we can clean up shared memory
2056  * segments
2057  */
2058 static void
2059 install_signal_handlers(void) 
2060 {
2061     struct sigaction act;
2062
2063     act.sa_handler = signal_handler;
2064     act.sa_flags = 0;
2065     sigemptyset(&act.sa_mask);
2066
2067     signal(SIGPIPE, SIG_IGN);
2068
2069     if (sigaction(SIGINT, &act, NULL) != 0) {
2070         error("taper: couldn't install SIGINT handler [%s]", strerror(errno));
2071     }
2072
2073     if (sigaction(SIGHUP, &act, NULL) != 0) {
2074         error("taper: couldn't install SIGHUP handler [%s]", strerror(errno));
2075     }
2076    
2077     if (sigaction(SIGTERM, &act, NULL) != 0) {
2078         error("taper: couldn't install SIGTERM handler [%s]", strerror(errno));
2079     }
2080
2081     if (sigaction(SIGUSR1, &act, NULL) != 0) {
2082         error("taper: couldn't install SIGUSR1 handler [%s]", strerror(errno));
2083     }
2084
2085     if (sigaction(SIGUSR2, &act, NULL) != 0) {
2086         error("taper: couldn't install SIGUSR2 handler [%s]", strerror(errno));
2087     }
2088
2089     if (sigaction(SIGALRM, &act, NULL) != 0) {
2090         error("taper: couldn't install SIGALRM handler [%s]", strerror(errno));
2091     }
2092 }
2093
2094
2095 /*
2096  * ========================================================================
2097  * SHARED-MEMORY BUFFER SUBSYSTEM
2098  *
2099  */
2100
2101 #ifdef HAVE_SYSVSHM
2102
2103 int shmid = -1;
2104
2105 char *attach_buffers(size)
2106     unsigned int size;
2107 {
2108     char *result;
2109
2110     shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0700);
2111     if(shmid == -1) {
2112         return NULL;
2113     }
2114
2115     result = (char *)shmat(shmid, (SHM_ARG_TYPE *)NULL, 0);
2116
2117     if(result == (char *)-1) {
2118         int save_errno = errno;
2119
2120         destroy_buffers();
2121         errno = save_errno;
2122         error("shmat: %s", strerror(errno));
2123     }
2124
2125     return result;
2126 }
2127
2128
2129 void detach_buffers(bufp)
2130     char *bufp;
2131 {
2132     if ((bufp != NULL) &&
2133         (shmdt((SHM_ARG_TYPE *)bufp) == -1)) {
2134         error("shmdt: %s", strerror(errno));
2135     }
2136 }
2137
2138 void destroy_buffers()
2139 {
2140     if(shmid == -1) return;     /* nothing to destroy */
2141     if(shmctl(shmid, IPC_RMID, NULL) == -1) {
2142         error("shmctl: %s", strerror(errno));
2143     }
2144 }
2145
2146 #else
2147 #ifdef HAVE_MMAP
2148
2149 #ifdef HAVE_SYS_MMAN_H
2150 #include <sys/mman.h>
2151 #endif
2152
2153 #ifndef MAP_ANON
2154 #  ifdef MAP_ANONYMOUS                  /* OSF/1-style */
2155 #    define MAP_ANON MAP_ANONYMOUS
2156 #  else                                 /* SunOS4-style */
2157 #    define MAP_ANON 0
2158 #    define ZERO_FILE "/dev/zero"
2159 #  endif
2160 #endif
2161
2162 int shmfd = -1;
2163 unsigned int saved_size;
2164
2165 char *attach_buffers(size)
2166     unsigned int size;
2167 {
2168     char *shmbuf;
2169
2170 #ifdef ZERO_FILE
2171     shmfd = open(ZERO_FILE, O_RDWR);
2172     if(shmfd == -1) {
2173         error("attach_buffers: could not open %s: %s",
2174               ZERO_FILE,
2175               strerror(errno));
2176     }
2177 #endif
2178
2179     saved_size = size;
2180     shmbuf = (char *) mmap((void *) 0,
2181                            size,
2182                            PROT_READ|PROT_WRITE,
2183                            MAP_ANON|MAP_SHARED,
2184                            shmfd, 0);
2185
2186     return shmbuf;
2187 }
2188
2189 void detach_buffers(bufp)
2190 char *bufp;
2191 {
2192     if ((bufp != NULL) && 
2193         (munmap((void *)bufp, saved_size) == -1)) {
2194         error("detach_buffers: munmap: %s", strerror(errno));
2195     }
2196
2197     if (shmfd != -1)
2198         aclose(shmfd);
2199 }
2200
2201 void destroy_buffers()
2202 {
2203 }
2204
2205 #else
2206 #error: must define either HAVE_SYSVSHM or HAVE_MMAP!
2207 #endif
2208 #endif
2209
2210
2211
2212 /*
2213  * ========================================================================
2214  * SYNC-PIPE SUBSYSTEM
2215  *
2216  */
2217
2218 int getpipe, putpipe;
2219
2220 void syncpipe_init(rd, wr)
2221 int rd, wr;
2222 {
2223     getpipe = rd;
2224     putpipe = wr;
2225 }
2226
2227 char syncpipe_get(intp)
2228 int *intp;
2229 {
2230     int rc;
2231     char buf[sizeof(char) + sizeof(int)];
2232
2233     rc = fullread(getpipe, buf, sizeof(buf));
2234     if(rc == 0)         /* EOF */
2235         error("syncpipe_get: %c: unexpected EOF", *procname);
2236     else if(rc < 0)
2237         error("syncpipe_get: %c: %s", *procname, strerror(errno));
2238     else if(rc != sizeof(buf))
2239         error("syncpipe_get: %s", "short read");
2240
2241     if(bufdebug && *buf != 'R' && *buf != 'W') {
2242         fprintf(stderr,"taper: %c: getc %c\n",*procname,*buf);
2243         fflush(stderr);
2244     }
2245
2246     memcpy(intp, &buf[1], sizeof(int));
2247     return buf[0];
2248 }
2249
2250 int syncpipe_getint()
2251 {
2252     int rc, i;
2253
2254     if ((rc = fullread(getpipe, &i, sizeof(i))) != sizeof(i))
2255         error("syncpipe_getint: %s", rc < 0 ? strerror(errno) : "short read");
2256
2257     return (i);
2258 }
2259
2260
2261 char *syncpipe_getstr()
2262 {
2263     int rc, len;
2264     char *str;
2265
2266     if((len = syncpipe_getint()) <= 0) {
2267         error("syncpipe_getstr: Protocol error - Invalid length (%d)", len);
2268         /* NOTREACHED */
2269     }
2270
2271     str = alloc(len);
2272
2273     if ((rc = fullread(getpipe, str, len)) != len) {
2274         error("syncpipe_getstr: %s", rc < 0 ? strerror(errno) : "short read");
2275         /* NOTREACHED */
2276     }
2277
2278     return (str);
2279 }
2280
2281
2282 void syncpipe_put(chi, intval)
2283 int chi;
2284 int intval;
2285 {
2286     char buf[sizeof(char) + sizeof(int)];
2287
2288     buf[0] = (char)chi;
2289     memcpy(&buf[1], &intval, sizeof(int));
2290     if(bufdebug && buf[0] != 'R' && buf[0] != 'W') {
2291         fprintf(stderr,"taper: %c: putc %c\n",*procname,buf[0]);
2292         fflush(stderr);
2293     }
2294
2295     if (fullwrite(putpipe, buf, sizeof(buf)) < 0)
2296         error("syncpipe_put: %s", strerror(errno));
2297 }
2298
2299 void syncpipe_putint(i)
2300 int i;
2301 {
2302
2303     if (fullwrite(putpipe, &i, sizeof(i)) < 0)
2304         error("syncpipe_putint: %s", strerror(errno));
2305 }
2306
2307 void syncpipe_putstr(str)
2308 const char *str;
2309 {
2310     int n;
2311
2312     n = strlen(str)+1;                          /* send '\0' as well */
2313     syncpipe_putint(n);
2314     if (fullwrite(putpipe, str, n) < 0)
2315         error("syncpipe_putstr: %s", strerror(errno));
2316 }
2317
2318 \f
2319 /*
2320  * ========================================================================
2321  * TAPE MANIPULATION SUBSYSTEM
2322  *
2323  */
2324
2325 /* local functions */
2326 int label_tape P((void));
2327
2328 int label_tape()
2329 {  
2330     char *conf_tapelist_old = NULL;
2331     char *result;
2332     static int first_call = 1;
2333     char *timestamp;
2334     char *error_msg;
2335     char *s, *r;
2336     int slot = -1;
2337
2338     if (taper_scan(NULL, &label, &timestamp, &error_msg, &tapedev) < 0) {
2339         fprintf(stderr, "%s\n", error_msg);
2340         errstr = newstralloc(errstr, error_msg);
2341         amfree(error_msg);
2342         amfree(timestamp);
2343         return 0;
2344     }
2345     if(error_msg) {
2346         s = error_msg; r = NULL;
2347         while((s=strstr(s,"slot "))) { s += 5; r=s; };
2348         if(r) {
2349             slot = atoi(r);
2350         }
2351     }
2352     if((tape_fd = tape_open(tapedev, O_WRONLY)) == -1) {
2353         if(errno == EACCES) {
2354             errstr = newstralloc(errstr,
2355                                  "writing label: tape is write protected");
2356         } else {
2357             errstr = newstralloc2(errstr,
2358                                   "writing label: ", strerror(errno));
2359         }
2360         return 0;
2361     }
2362
2363     tapefd_setinfo_length(tape_fd, tt->length);
2364
2365     tapefd_setinfo_datestamp(tape_fd, taper_datestamp);
2366     tapefd_setinfo_disk(tape_fd, label);
2367     result = tapefd_wrlabel(tape_fd, taper_datestamp, label, tt_blocksize);
2368     if(result != NULL) {
2369         errstr = newstralloc(errstr, result);
2370         return 0;
2371     }
2372
2373     if(slot > -1) {
2374         fprintf(stderr, "taper: slot: %d wrote label `%s' date `%s'\n", slot,
2375                 label, taper_datestamp);
2376     }
2377     else {
2378         fprintf(stderr, "taper: wrote label `%s' date `%s'\n", label,
2379                 taper_datestamp);
2380     }
2381     fflush(stderr);
2382
2383 #ifdef HAVE_LIBVTBLC
2384     /* store time for the first volume entry */
2385     time(&raw_time);
2386     tape_timep = localtime(&raw_time);
2387     strftime(start_datestr, 20, "%T %D", tape_timep);
2388     fprintf(stderr, "taper: got vtbl start time: %s\n", start_datestr);
2389     fflush(stderr);
2390 #endif /* HAVE_LIBVTBLC */
2391
2392     if (strcmp(label, FAKE_LABEL) != 0) {
2393
2394         if(cur_tape == 0) {
2395             conf_tapelist_old = stralloc2(conf_tapelist, ".yesterday");
2396         } else {
2397             char cur_str[NUM_STR_SIZE];
2398
2399             snprintf(cur_str, sizeof(cur_str), "%d", cur_tape - 1);
2400             conf_tapelist_old = vstralloc(conf_tapelist,
2401                                           ".today.", cur_str, NULL);
2402         }
2403         if(write_tapelist(conf_tapelist_old)) {
2404             error("could not write tapelist: %s", strerror(errno));
2405         }
2406         amfree(conf_tapelist_old);
2407
2408         remove_tapelabel(label);
2409         add_tapelabel(atoi(taper_datestamp), label);
2410         if(write_tapelist(conf_tapelist)) {
2411             error("could not write tapelist: %s", strerror(errno));
2412         }
2413     }
2414
2415     log_add(L_START, "datestamp %s label %s tape %d",
2416             taper_datestamp, label, cur_tape);
2417     if (first_call && strcmp(label, FAKE_LABEL) == 0) {
2418         first_call = 0;
2419         log_add(L_WARNING, "tapedev is %s, dumps will be thrown away", tapedev);
2420     }
2421
2422     total_tape_used=0.0;
2423     total_tape_fm = 0;
2424
2425     return 1;
2426 }
2427
2428 int first_tape(new_datestamp)
2429 char *new_datestamp;
2430 {
2431     if((have_changer = changer_init()) < 0) {
2432         error("changer initialization failed: %s", strerror(errno));
2433     }
2434     changer_debug = 1;
2435
2436     taper_datestamp = newstralloc(taper_datestamp, new_datestamp);
2437
2438     if(!label_tape())
2439         return 0;
2440
2441     filenum = 0;
2442     return 1;
2443 }
2444
2445 int next_tape(writerror)
2446 int writerror;
2447 {
2448     end_tape(writerror);
2449
2450     if(++cur_tape >= runtapes)
2451         return 0;
2452
2453     if(!label_tape()) {
2454         return 0;
2455     }
2456
2457     filenum = 0;
2458     return 1;
2459 }
2460
2461
2462 int end_tape(writerror)
2463 int writerror;
2464 {
2465     char *result;
2466     int rc = 0;
2467
2468     if(tape_fd >= 0) {
2469         log_add(L_INFO, "tape %s kb %ld fm %d %s", 
2470                 label,
2471                 (long) ((total_tape_used+1023.0) / 1024.0),
2472                 total_tape_fm,
2473                 writerror? errstr : "[OK]");
2474
2475         fprintf(stderr, "taper: writing end marker. [%s %s kb %ld fm %d]\n",
2476                 label,
2477                 writerror? "ERR" : "OK",
2478                 (long) ((total_tape_used+1023.0) / 1024.0),
2479                 total_tape_fm);
2480         fflush(stderr);
2481         if(! writerror) {
2482             if(! write_filemark()) {
2483                 rc = 1;
2484                 goto common_exit;
2485             }
2486
2487             result = tapefd_wrendmark(tape_fd, taper_datestamp, tt_blocksize);
2488             if(result != NULL) {
2489                 errstr = newstralloc(errstr, result);
2490                 rc = 1;
2491                 goto common_exit;
2492             }
2493         }
2494     }
2495
2496 #ifdef HAVE_LINUX_ZFTAPE_H
2497     if (tape_fd >= 0 && is_zftape(tapedev) == 1) {
2498         /* rewind the tape */
2499
2500         if(tapefd_rewind(tape_fd) == -1 ) {
2501             errstr = newstralloc2(errstr, "rewinding tape: ", strerror(errno));
2502             rc = 1;
2503             goto common_exit;
2504         }
2505         /* close the tape */
2506
2507         if(tapefd_close(tape_fd) == -1) {
2508             errstr = newstralloc2(errstr, "closing tape: ", strerror(errno));
2509             rc = 1;
2510             goto common_exit;
2511         }
2512         tape_fd = -1;
2513
2514 #ifdef HAVE_LIBVTBLC
2515         /* update volume table */
2516         fprintf(stderr, "taper: updating volume table ...\n");
2517         fflush(stderr);
2518     
2519         if ((tape_fd = raw_tape_open(rawtapedev, O_RDWR)) == -1) {
2520             if(errno == EACCES) {
2521                 errstr = newstralloc(errstr,
2522                                      "updating volume table: tape is write protected");
2523             } else {
2524                 errstr = newstralloc2(errstr,
2525                                       "updating volume table: ", 
2526                                       strerror(errno));
2527             }
2528             rc = 1;
2529             goto common_exit;
2530         }
2531         /* read volume table */
2532         if ((num_volumes = read_vtbl(tape_fd, volumes, vtbl_buffer,
2533                                      &first_seg, &last_seg)) == -1 ) {
2534             errstr = newstralloc2(errstr,
2535                                   "reading volume table: ", 
2536                                   strerror(errno));
2537             rc = 1;
2538             goto common_exit;
2539         }
2540         /* set volume label and date for first entry */
2541         vtbl_no = 0;
2542         if(set_label(label, volumes, num_volumes, vtbl_no)){
2543             errstr = newstralloc2(errstr,
2544                                   "setting label for entry 1: ",
2545                                   strerror(errno));
2546             rc = 1;
2547             goto common_exit;
2548         }
2549         /* date of start writing this tape */
2550         if (set_date(start_datestr, volumes, num_volumes, vtbl_no)){
2551             errstr = newstralloc2(errstr,
2552                                   "setting date for entry 1: ", 
2553                                   strerror(errno));
2554             rc = 1;
2555             goto common_exit;
2556         }
2557         /* set volume labels and dates for backup files */
2558         for (vtbl_no = 1; vtbl_no <= num_volumes - 2; vtbl_no++){ 
2559             fprintf(stderr,"taper: label %i: %s, date %s\n", 
2560                     vtbl_no,
2561                     vtbl_entry[vtbl_no].label,
2562                     vtbl_entry[vtbl_no].date);
2563             fflush(stderr);
2564             if(set_label(vtbl_entry[vtbl_no].label, 
2565                          volumes, num_volumes, vtbl_no)){
2566                 errstr = newstralloc2(errstr,
2567                                       "setting label for entry i: ", 
2568                                       strerror(errno));
2569                 rc = 1;
2570                 goto common_exit;
2571             }
2572             if(set_date(vtbl_entry[vtbl_no].date, 
2573                         volumes, num_volumes, vtbl_no)){
2574                 errstr = newstralloc2(errstr,
2575                                       "setting date for entry i: ",
2576                                       strerror(errno));
2577                 rc = 1;
2578                 goto common_exit;
2579             }
2580         }
2581         /* set volume label and date for last entry */
2582         vtbl_no = num_volumes - 1;
2583         if(set_label("AMANDA Tape End", volumes, num_volumes, vtbl_no)){
2584             errstr = newstralloc2(errstr,
2585                                   "setting label for last entry: ", 
2586                                   strerror(errno));
2587             rc = 1;
2588             goto common_exit;
2589         }
2590         datestr = NULL; /* take current time */ 
2591         if (set_date(datestr, volumes, num_volumes, vtbl_no)){
2592             errstr = newstralloc2(errstr,
2593                                   "setting date for last entry 1: ", 
2594                                   strerror(errno));
2595             rc = 1;
2596             goto common_exit;
2597         }
2598         /* write volume table back */
2599         if (write_vtbl(tape_fd, volumes, vtbl_buffer, num_volumes, first_seg,
2600                        op_mode == trunc)) {
2601             errstr = newstralloc2(errstr,
2602                                   "writing volume table: ", 
2603                                   strerror(errno));
2604             rc = 1;
2605             goto common_exit;
2606         }  
2607
2608         fprintf(stderr, "taper: updating volume table: done.\n");
2609         fflush(stderr);
2610 #endif /* HAVE_LIBVTBLC */
2611     }
2612 #endif /* !HAVE_LINUX_ZFTAPE_H */
2613
2614     /* close the tape and let the OS write the final filemarks */
2615
2616 common_exit:
2617
2618     if(tape_fd >= 0 && tapefd_close(tape_fd) == -1 && ! writerror) {
2619         errstr = newstralloc2(errstr, "closing tape: ", strerror(errno));
2620         rc = 1;
2621     }
2622     tape_fd = -1;
2623     amfree(label);
2624
2625     return rc;
2626 }
2627
2628
2629 int write_filemark()
2630 {
2631     if(tapefd_weof(tape_fd, 1) == -1) {
2632         errstr = newstralloc2(errstr, "writing filemark: ", strerror(errno));
2633         return 0;
2634     }
2635     total_tape_fm++;
2636     return 1;
2637 }
2638
2639