Imported Upstream version 2.5.1p3
[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.144.2.7 2007/01/18 12:57:16 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 static time_t raw_time;
65 static struct tm tape_time;
66 static struct tm backup_time;
67 static 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 static off_t splitsize = (off_t)0; /* max size of dumpfile before split (Kb) */
80 static off_t mmap_splitsize = (off_t)0;
81 static char *mmap_filename = NULL;
82 static char *mmap_splitbuf = NULL;
83 static char *mem_splitbuf = NULL;
84 static char *splitbuf = NULL;
85 static off_t mem_splitsize = (off_t)0;
86 static char *splitbuf_wr_ptr = NULL; /* the number of Kb we've written into splitbuf */
87 int orig_holdfile = -1;
88
89 /* NBUFS replaced by conf_tapebufs */
90 /* #define NBUFS                20 */
91 static int conf_tapebufs;
92
93 static off_t maxseek = (off_t)1 << ((SIZEOF(off_t) * 8) - 11);
94
95 static char *holdfile_path = NULL;
96 static char *holdfile_path_thischunk = NULL;
97 static int num_holdfile_chunks = 0;
98 static off_t holdfile_offset_thischunk = (off_t)0;
99 static int mmap_splitbuffer_fd = -1;
100
101 #define MODE_NONE 0
102 #define MODE_FILE_WRITE 1
103 #define MODE_PORT_WRITE 2
104
105 static mode_t mode = MODE_NONE;
106
107 /* This is now the number of empties, not full bufs */
108 #define THRESHOLD       1
109
110 #define CONNECT_TIMEOUT 2*60
111
112 #define EMPTY 1
113 #define FILLING 2
114 #define FULL 3
115
116 typedef struct buffer_s {
117     long status;
118     ssize_t 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(int main_argc, char **main_argv);
127 void file_reader_side(int rdpipe, int wrpipe);
128 void tape_writer_side(int rdpipe, int wrpipe);
129 void put_syncpipe_fault_result(char *handle);
130
131 /* shared-memory routines */
132 char *attach_buffers(size_t size);
133 void detach_buffers(char *bufp);
134 void destroy_buffers(void);
135 #define REMOVE_SHARED_MEMORY() \
136     detach_buffers(buffers); \
137     if (strcmp(procname, "reader") == 0) { \
138         destroy_buffers(); \
139     }
140
141 /* synchronization pipe routines */
142 void syncpipe_init(int rd, int wr);
143 void syncpipe_read_error(ssize_t rc, ssize_t expected);
144 void syncpipe_write_error(ssize_t rc, ssize_t expected);
145 int syncpipe_get(int *intp);
146 int syncpipe_getint(void);
147 char *syncpipe_getstr(void);
148 int syncpipe_put(int ch, int intval);
149 int syncpipe_putint(int i);
150 int syncpipe_putstr(const char *str);
151
152 /* tape manipulation subsystem */
153 int first_tape(char *new_datestamp);
154 int next_tape(int writerr);
155 int end_tape(int writerr);
156 int write_filemark(void);
157
158 /* support crap */
159 int seek_holdfile(int fd, buffer_t *bp, off_t kbytes);
160
161 /* signal handling */
162 static void install_signal_handlers(void);
163 static void signal_handler(int);
164
165 /* exit routine */
166 static void cleanup(void);
167
168 /*
169  * ========================================================================
170  * GLOBAL STATE
171  *
172  */
173 int interactive;
174 pid_t writerpid;
175 times_t total_wait;
176 #ifdef TAPER_DEBUG
177 int bufdebug = 1;
178 #else
179 int bufdebug = 0;
180 #endif
181
182 char *buffers = NULL;
183 buffer_t *buftable = NULL;
184 int err;
185
186 char *procname = "parent";
187
188 char *taper_timestamp = NULL;
189 char *label = NULL;
190 int filenum;
191 char *errstr = NULL;
192 int tape_fd = -1;
193 char *tapedev = NULL;
194 char *tapetype = NULL;
195 tapetype_t *tt = NULL;
196 size_t tt_blocksize;
197 size_t tt_blocksize_kb;
198 size_t buffer_size;
199 int tt_file_pad;
200 static unsigned long malloc_hist_1, malloc_size_1;
201 static unsigned long malloc_hist_2, malloc_size_2;
202 dumpfile_t file;
203 dumpfile_t *save_holdfile = NULL;
204 off_t cur_span_chunkstart = (off_t)0; /* start of current split dump chunk (Kb) */
205 char *holdfile_name;
206 int num_splits = 0;
207 int expected_splits = 0;
208 int num_holdfiles = 0;
209 times_t curdump_rt;
210
211 am_feature_t *their_features = NULL;
212
213 int runtapes, cur_tape, have_changer, tapedays;
214 char *labelstr, *conf_tapelist;
215 #ifdef HAVE_LIBVTBLC
216 char *rawtapedev;
217 int first_seg, last_seg;
218 #endif /* HAVE_LIBVTBLC */
219
220 /*
221  * ========================================================================
222  * MAIN PROGRAM
223  *
224  */
225 int
226 main(
227     int main_argc,
228     char **main_argv)
229 {
230     int p2c[2], c2p[2];         /* parent-to-child, child-to-parent pipes */
231     char *conffile;
232     size_t size;
233     int i;
234     size_t j;
235     size_t page_size;
236     char *first_buffer;
237     int    new_argc,   my_argc;
238     char **new_argv, **my_argv;
239
240     safe_fd(-1, 0);
241
242     set_pname("taper");
243
244     dbopen("server");
245
246     /* Don't die when child closes pipe */
247     signal(SIGPIPE, SIG_IGN);
248
249     malloc_size_1 = malloc_inuse(&malloc_hist_1);
250
251     parse_server_conf(main_argc, main_argv, &new_argc, &new_argv);
252     my_argc = new_argc;
253     my_argv = new_argv;
254
255     fprintf(stderr, "%s: pid %ld executable %s version %s\n",
256             get_pname(), (long) getpid(), my_argv[0], version());
257     dbprintf(("%s: pid %ld executable %s version %s\n",
258             get_pname(), (long) getpid(), my_argv[0], version()));
259     fflush(stderr);
260
261     if (my_argc > 1 && my_argv[1][0] != '-') {
262         config_name = stralloc(my_argv[1]);
263         config_dir = vstralloc(CONFIG_DIR, "/", my_argv[1], "/", NULL);
264         my_argc--;
265         my_argv++;
266     } else {
267         char my_cwd[STR_SIZE];
268
269         if (getcwd(my_cwd, SIZEOF(my_cwd)) == NULL) {
270             error("cannot determine current working directory");
271             /*NOTREACHED*/
272         }
273         config_dir = stralloc2(my_cwd, "/");
274         if ((config_name = strrchr(my_cwd, '/')) != NULL) {
275             config_name = stralloc(config_name + 1);
276         }
277     }
278
279     safe_cd();
280
281     install_signal_handlers();
282     atexit(cleanup);
283
284     /* print prompts and debug messages if running interactive */
285
286     interactive = (my_argc > 1 && strcmp(my_argv[1],"-t") == 0);
287     if (interactive) {
288         erroutput_type = ERR_INTERACTIVE;
289     } else {
290         erroutput_type = ERR_AMANDALOG;
291         set_logerror(logerror);
292     }
293
294     free_new_argv(new_argc, new_argv);
295
296     conffile = stralloc2(config_dir, CONFFILE_NAME);
297     if (read_conffile(conffile)) {
298         error("errors processing config file \"%s\"", conffile);
299         /*NOTREACHED*/
300     }
301     amfree(conffile);
302
303     dbrename(config_name, DBG_SUBDIR_SERVER);
304
305     report_bad_conf_arg();
306
307     conf_tapelist = getconf_str(CNF_TAPELIST);
308     if (*conf_tapelist == '/') {
309         conf_tapelist = stralloc(conf_tapelist);
310     } else {
311         conf_tapelist = stralloc2(config_dir, conf_tapelist);
312     }
313     if (read_tapelist(conf_tapelist)) {
314         error("could not load tapelist \"%s\"", conf_tapelist);
315         /*NOTREACHED*/
316     }
317
318     tapedev     = getconf_str(CNF_TAPEDEV);
319     tapetype    = getconf_str(CNF_TAPETYPE);
320     tt          = lookup_tapetype(tapetype);
321 #ifdef HAVE_LIBVTBLC
322     rawtapedev = stralloc(getconf_str(CNF_RAWTAPEDEV));
323 #endif /* HAVE_LIBVTBLC */
324     tapedays    = getconf_int(CNF_TAPECYCLE);
325     labelstr    = getconf_str(CNF_LABELSTR);
326
327     runtapes    = getconf_int(CNF_RUNTAPES);
328     cur_tape    = 0;
329
330     conf_tapebufs = getconf_int(CNF_TAPEBUFS);
331
332     tt_blocksize_kb = (size_t)tapetype_get_blocksize(tt);
333     tt_blocksize = tt_blocksize_kb * 1024;
334     tt_file_pad = tapetype_get_file_pad(tt);
335
336     if (interactive) {
337         fprintf(stderr,"taper: running in interactive test mode\n");
338         dbprintf(("taper: running in interactive test mode\n"));
339         fflush(stderr);
340     }
341
342     /* create read/write syncronization pipes */
343
344     if (pipe(p2c)) {
345         error("creating sync pipes: %s", strerror(errno));
346         /*NOTREACHED*/
347     }
348     if (pipe(c2p)) {
349         error("creating sync pipes: %s", strerror(errno));
350         /*NOTREACHED*/
351     }
352
353     /* create shared memory segment */
354
355 #if defined(HAVE_GETPAGESIZE)
356     page_size = (size_t)getpagesize();
357     fprintf(stderr, "%s: page size = " SIZE_T_FMT "\n",
358                 get_pname(), (SIZE_T_FMT_TYPE)page_size);
359     dbprintf(("%s: page size = " SIZE_T_FMT "\n", get_pname(),
360                 (SIZE_T_FMT_TYPE)page_size));
361 #else
362     page_size = 1024;
363     fprintf(stderr, "%s: getpagesize() not available, using " SIZE_T_FMT "\n",
364             get_pname(), page_size);
365     dbprintf((stderr, "%s: getpagesize() not available, using " SIZE_T_FMT "\n",
366             get_pname(), page_size));
367 #endif
368     buffer_size = am_round(tt_blocksize, page_size);
369     fprintf(stderr, "%s: buffer size is " SIZE_T_FMT "\n",
370             get_pname(), (SIZE_T_FMT_TYPE)buffer_size);
371     dbprintf(("%s: buffer size is " SIZE_T_FMT "\n",
372             get_pname(), (SIZE_T_FMT_TYPE)buffer_size));
373     while (conf_tapebufs > 0) {
374         size  = page_size;
375         size += conf_tapebufs * buffer_size;
376         size += conf_tapebufs * SIZEOF(buffer_t);
377         if ((buffers = attach_buffers(size)) != NULL) {
378             break;
379         }
380         log_add(L_INFO, "attach_buffers: (%d tapebuf%s: %zu bytes) %s",
381                         conf_tapebufs,
382                         (conf_tapebufs == 1) ? "" : "s",
383                         size,
384                         strerror(errno));
385         conf_tapebufs--;
386     }
387     if (buffers == NULL) {
388         error("cannot allocate shared memory");
389         /*NOTREACHED*/
390     }
391
392     /* page boundary offset */
393     i = (int)((buffers - (char *)0) & (page_size - 1));
394     if (i != 0) {
395         first_buffer = buffers + page_size - i;
396         dbprintf(("%s: shared memory at %p, first buffer at %p\n",
397                 get_pname(),
398                 (void *)buffers,
399                 (void *)first_buffer));
400     } else {
401         first_buffer = buffers;
402     }
403
404     /*LINTED  first_buffer, conf_tapebufs and buffer size are all * pagesize */
405     buftable = (buffer_t *)(first_buffer + (conf_tapebufs * buffer_size));
406     memset(buftable, 0, conf_tapebufs * SIZEOF(buffer_t));
407     if (conf_tapebufs < 10) {
408         j = 1;
409     } else if (conf_tapebufs < 100) {
410         j = 2;
411     } else {
412         j = 3;
413     }
414     for (i = 0; i < conf_tapebufs; i++) {
415         buftable[i].buffer = first_buffer + i * buffer_size;
416         dbprintf(("%s: buffer[%0*d] at %p\n",
417                 get_pname(),
418                 (int)j, i,
419                 (void *)buftable[i].buffer));
420     }
421     dbprintf(("%s: buffer structures at %p for %d bytes\n",
422             get_pname(),
423             (void *)buftable,
424             (int)(conf_tapebufs * SIZEOF(buffer_t))));
425
426     /* fork off child writer process, parent becomes reader process */
427     switch(writerpid = fork()) {
428     case -1:
429         error("fork: %s", strerror(errno));
430         /*NOTREACHED*/
431
432     case 0:     /* child */
433         aclose(p2c[1]);
434         aclose(c2p[0]);
435
436         tape_writer_side(p2c[0], c2p[1]);
437         error("tape writer terminated unexpectedly");
438         /*NOTREACHED*/
439
440     default:    /* parent */
441         aclose(p2c[0]);
442         aclose(c2p[1]);
443
444         file_reader_side(c2p[0], p2c[1]);
445         error("file reader terminated unexpectedly");
446         /*NOTREACHED*/
447     }
448
449     /*NOTREACHED*/
450     return 0;
451 }
452
453
454 /*
455  * ========================================================================
456  * FILE READER SIDE
457  *
458  */
459 int read_file(int fd, char *handle,
460                   char *host, char *disk, char *datestamp, 
461                   int level);
462 ssize_t taper_fill_buffer(int fd, buffer_t *bp, size_t buflen);
463 void dumpbufs(char *str1);
464 void dumpstatus(buffer_t *bp);
465 ssize_t get_next_holding_file(int fd, buffer_t *bp, char **strclosing, size_t rc);
466 int predict_splits(char *filename);
467 void create_split_buffer(char *split_diskbuffer, size_t fallback_splitsize, char *id_string);
468 void free_split_buffer(void);
469
470
471 /*
472  * Create a buffer, either in an mmapped file or in memory, where PORT-WRITE
473  * dumps can buffer the current split chunk in case of retry.
474  */
475 void
476 create_split_buffer(
477     char *split_diskbuffer,
478     size_t fallback_splitsize,
479     char *id_string)
480 {
481     char *buff_err = NULL;
482     off_t offset;
483     char *splitbuffer_path = NULL;
484     
485     /* don't bother if we're not actually splitting */
486     if (splitsize <= (off_t)0) {
487         splitbuf = NULL;
488         splitbuf_wr_ptr = NULL;
489         return;
490     }
491
492 #ifdef HAVE_MMAP
493 #ifdef HAVE_SYS_MMAN_H
494     if (strcmp(split_diskbuffer, "NULL")) {
495         void *nulls = NULL;
496         char *quoted;
497         off_t c;
498
499         splitbuffer_path = vstralloc(split_diskbuffer,
500                                      "/splitdump_buffer",
501                                      NULL);
502         /* different file, munmap the previous */
503         if (mmap_filename && strcmp(mmap_filename, splitbuffer_path) != 0) {
504             dbprintf(("create_split_buffer: new file %s\n", splitbuffer_path));
505             munmap(splitbuf, (size_t)mmap_splitsize);
506             aclose(mmap_splitbuffer_fd);
507             mmap_splitbuf = NULL;
508             amfree(mmap_filename);
509             mmap_splitsize = 0;
510         }
511         if (!mmap_filename) {
512             dbprintf(("create_split_buffer: open file %s\n",
513                       splitbuffer_path));
514             mmap_splitbuffer_fd = open(splitbuffer_path, O_RDWR|O_CREAT, 0600);
515             if (mmap_splitbuffer_fd == -1) {
516                 buff_err = newvstralloc(buff_err, "open of ", 
517                                         splitbuffer_path, "failed (",
518                                         strerror(errno), ")", NULL);
519                 goto fallback;
520             }
521         }
522         offset = lseek(mmap_splitbuffer_fd, (off_t)0, SEEK_END) / 1024;
523         if (offset < splitsize) { /* Increase file size */
524             dbprintf(("create_split_buffer: increase file size of %s to "
525                       OFF_T_FMT "kb\n",
526                       splitbuffer_path, (OFF_T_FMT_TYPE)splitsize));
527             if (mmap_filename) {
528                 dbprintf(("create_split_buffer: munmap old file %s\n",
529                           mmap_filename));
530                 munmap(splitbuf, (size_t)mmap_splitsize);
531                 mmap_splitsize = 0;
532                 mmap_splitbuf = NULL;
533             }
534             nulls = alloc(1024); /* lame */
535             memset(nulls, 0, 1024);
536             for (c = offset; c < splitsize ; c += (off_t)1) {
537                 if (fullwrite(mmap_splitbuffer_fd, nulls, 1024) < 1024) {
538                     buff_err = newvstralloc(buff_err, "write to ",
539                                             splitbuffer_path,
540                                             "failed (", strerror(errno),
541                                             ")", NULL);
542                     c -= 1;
543                     if (c <= (off_t)fallback_splitsize) {
544                         goto fallback;
545                     }
546                     splitsize = c;
547                     break;
548                 }
549             }
550         }
551         amfree(nulls);
552
553         if (mmap_splitsize < splitsize*1024) {
554             mmap_splitsize = splitsize*1024;
555             mmap_filename = stralloc(splitbuffer_path);
556             dbprintf(("create_split_buffer: mmap file %s for " OFF_T_FMT "kb\n",
557                           mmap_filename,(OFF_T_FMT_TYPE)splitsize));
558             mmap_splitbuf = mmap(NULL, (size_t)mmap_splitsize,
559                                  PROT_READ|PROT_WRITE,
560                                  MAP_SHARED, mmap_splitbuffer_fd, (off_t)0);
561             if (mmap_splitbuf == (char*)-1) {
562                 buff_err = newvstralloc(buff_err, "mmap failed (",
563                                         strerror(errno), ")", NULL);
564                 aclose(mmap_splitbuffer_fd);
565                 amfree(mmap_filename);
566                 mmap_splitsize = 0;
567                 mmap_splitbuf = NULL;
568                 goto fallback;
569             }
570         }
571         quoted = quote_string(splitbuffer_path);
572         fprintf(stderr,
573                 "taper: r: buffering " OFF_T_FMT
574                 "kb split chunks in mmapped file %s\n",
575                 (OFF_T_FMT_TYPE)splitsize, quoted);
576         dbprintf(("taper: r: buffering " OFF_T_FMT
577                 "kb split chunks in mmapped file %s\n",
578                 (OFF_T_FMT_TYPE)splitsize, quoted));
579         amfree(splitbuffer_path);
580         amfree(quoted);
581         amfree(buff_err);
582         splitbuf = mmap_splitbuf;
583         splitbuf_wr_ptr = splitbuf;
584         return;
585     } else {
586         buff_err = stralloc("no split_diskbuffer specified");
587     }
588 #else
589     (void)split_diskbuffer;     /* Quite unused parameter warning */
590     buff_err = stralloc("mman.h not available");
591     goto fallback;
592 #endif
593 #else
594     (void)split_diskbuffer;     /* Quite unused parameter warning */
595     buff_err = stralloc("mmap not available");
596     goto fallback;
597 #endif
598
599     /*
600       Buffer split dumps in memory, if we can't use a file.
601     */
602     fallback:
603         amfree(splitbuffer_path);
604         splitsize = (off_t)fallback_splitsize;
605         dbprintf(("create_split_buffer: fallback size " OFF_T_FMT "\n",
606                   (OFF_T_FMT_TYPE)splitsize));
607         log_add(L_INFO,
608                 "%s: using fallback split size of " OFF_T_FMT "kb to buffer %s in-memory",
609                 buff_err, (OFF_T_FMT_TYPE)splitsize, id_string);
610         amfree(buff_err);
611         if (splitsize > mem_splitsize) {
612             amfree(mem_splitbuf);
613             mem_splitbuf = alloc(fallback_splitsize * 1024);
614             mem_splitsize = fallback_splitsize;
615             dbprintf(("create_split_buffer: alloc buffer size " OFF_T_FMT "\n",
616                           (OFF_T_FMT_TYPE)splitsize *1024));
617         }
618         splitbuf = mem_splitbuf;
619         splitbuf_wr_ptr = splitbuf;
620 }
621
622 /*
623  * Free up resources that create_split_buffer eats.
624  */
625 void
626 free_split_buffer(void)
627 {
628     if (mmap_splitbuffer_fd != -1) {
629 #ifdef HAVE_MMAP
630 #ifdef HAVE_SYS_MMAN_H
631         if (mmap_splitbuf != NULL) {
632             munmap(mmap_splitbuf, (size_t)mmap_splitsize);
633             mmap_splitbuf = NULL;
634         }
635 #endif
636 #endif
637         aclose(mmap_splitbuffer_fd);
638         amfree(mmap_filename);
639         mmap_splitsize = 0;
640     }
641     if (mem_splitbuf) {
642         amfree(mem_splitbuf);
643         mem_splitsize = 0;
644     }
645 }
646
647 void
648 put_syncpipe_fault_result(
649     char *      handle)
650 {
651     char *q;
652
653     if (handle == NULL)
654         handle = "<nohandle>";
655
656     q = squotef("[Taper syncpipe fault]");
657     putresult(TAPE_ERROR, "%s %s\n", handle, q);
658     log_add(L_ERROR, "tape-error %s %s", handle, q);
659     amfree(q);
660 }
661
662 void
663 file_reader_side(
664     int rdpipe,
665     int wrpipe)
666 {
667     cmd_t cmd;
668     struct cmdargs cmdargs;
669     char *handle = NULL;
670     char *filename = NULL;
671     char *qfilename = NULL;
672     char *hostname = NULL;
673     char *diskname = NULL;
674     char *qdiskname = NULL;
675     char *result = NULL;
676     char *datestamp = NULL;
677     char *split_diskbuffer = NULL;
678     char *id_string = NULL;
679     int tok;
680     char *q = NULL;
681     int level, fd;
682     in_port_t data_port;
683     int data_socket;
684     pid_t wpid;
685     char level_str[64];
686     struct stat stat_file;
687     int tape_started;
688     int a;
689     size_t fallback_splitsize = 0;
690     int tmpint;
691     char *c, *c1;
692
693     procname = "reader";
694     syncpipe_init(rdpipe, wrpipe);
695
696     /* must get START_TAPER before beginning */
697
698     startclock();
699     cmd = getcmd(&cmdargs);
700     total_wait = stopclock();
701
702     if (cmd != START_TAPER || cmdargs.argc != 2) {
703         error("error [file_reader_side cmd %d argc %d]", cmd, cmdargs.argc);
704         /*NOTREACHED*/
705     }
706
707     /* pass start command on to tape writer */
708
709     taper_timestamp = newstralloc(taper_timestamp, cmdargs.argv[2]);
710
711     if (tapedev == NULL) {
712         if (getconf_str(CNF_TPCHANGER) == NULL) {
713             putresult(TAPE_ERROR, "[No tapedev or tpchanger defined]\n");
714             log_add(L_ERROR, "No tapedev or tpchanger defined");
715             dbprintf(("taper: No tapedev or tpchanger defined\n"));
716             exit(1);
717         }
718     } else {
719         tapedev = stralloc(tapedev);
720     }
721
722     tape_started = 0;
723     if (syncpipe_put('S', 0) == -1) {
724         put_syncpipe_fault_result(NULL);
725     }
726
727     if (syncpipe_putstr(taper_timestamp) == -1) {
728         put_syncpipe_fault_result(NULL);
729     }
730
731     /* get result of start command */
732
733     tok = syncpipe_get(&tmpint);
734     switch(tok) {
735     case -1:
736         put_syncpipe_fault_result(NULL);
737         break;
738
739     case 'S':
740         putresult(TAPER_OK, "\n");
741         tape_started = 1;
742         /* start is logged in writer */
743         break;
744
745     case 'E':
746         /* no tape, bail out */
747         if ((result = syncpipe_getstr()) == NULL) {
748             put_syncpipe_fault_result(NULL);
749         } else {
750             q = squotef("[%s]", result);
751             putresult(TAPE_ERROR, "<nohandle> %s\n", q);
752             amfree(q);
753             log_add(L_ERROR,"no-tape [%s]", "No writable valid tape found");
754             c = c1 = result;
755             while (*c != '\0') {
756                 if (*c == '\n') {
757                     *c = '\0';
758                     log_add(L_WARNING,"%s", c1);
759                     c1 = c+1;
760                 }
761                 c++;
762             }
763             if (strlen(c1) > 1 )
764                 log_add(L_WARNING,"%s", c1);
765             amfree(result);
766             (void)syncpipe_put('e', 0);                 /* ACK error */
767         }
768         break;
769
770     case 'H': /* Syncpipe I/O error */
771         /* No ACK syncpipe is down just exit */
772         put_syncpipe_fault_result(handle);
773         break;
774
775     case 'X':
776         /*
777          * Pipe read error: Communications is severed at least
778          * back to us.  We send a blind 'Q' (quit) and we don't
779          * wait for a response...
780          */
781         syncpipe_put('Q', 0);                   /* ACK error */
782         error("error [communications pipe from writer severed]");
783         /*NOTREACHED*/
784
785     default:
786         q = squotef("[syncpipe sequence fault: Expected 'S' or 'E']");
787         putresult(TAPE_ERROR, "<nohandle> %s\n", q);
788         log_add(L_ERROR, "no-tape %s]", q);
789         amfree(q);
790     }
791
792     /* process further driver commands */
793     while (1) {
794         startclock();
795         cmd = getcmd(&cmdargs);
796         if (cmd != QUIT && !tape_started) {
797             error("error [file_reader_side cmd %d without tape ready]", cmd);
798             /*NOTREACHED*/
799         }
800         total_wait = timesadd(total_wait, stopclock());
801
802         switch(cmd) {
803         case PORT_WRITE:
804             /*
805              * PORT-WRITE
806              *   handle
807              *   hostname
808              *   features
809              *   diskname
810              *   level
811              *   datestamp
812              *   splitsize
813              *   split_diskbuffer
814              */
815             mode = MODE_PORT_WRITE;
816             cmdargs.argc++;                     /* true count of args */
817             a = 2;
818
819             if (a >= cmdargs.argc) {
820                 error("error [taper PORT-WRITE: not enough args: handle]");
821                 /*NOTREACHED*/
822             }
823             handle = newstralloc(handle, cmdargs.argv[a++]);
824
825             if (a >= cmdargs.argc) {
826                 error("error [taper PORT-WRITE: not enough args: hostname]");
827                 /*NOTREACHED*/
828             }
829             hostname = newstralloc(hostname, cmdargs.argv[a++]);
830
831             if (a >= cmdargs.argc) {
832                 error("error [taper PORT-WRITE: not enough args: features]");
833                 /*NOTREACHED*/
834             }
835             am_release_feature_set(their_features);
836             their_features = am_string_to_feature(cmdargs.argv[a++]);
837
838             if (a >= cmdargs.argc) {
839                 error("error [taper PORT-WRITE: not enough args: diskname]");
840                 /*NOTREACHED*/
841             }
842             qdiskname = newstralloc(qdiskname, cmdargs.argv[a++]);
843             if (diskname != NULL)
844                 amfree(diskname);
845             diskname = unquote_string(qdiskname);
846
847             if (a >= cmdargs.argc) {
848                 error("error [taper PORT-WRITE: not enough args: level]");
849                 /*NOTREACHED*/
850             }
851             level = atoi(cmdargs.argv[a++]);
852
853             if (a >= cmdargs.argc) {
854                 error("error [taper PORT-WRITE: not enough args: datestamp]");
855                 /*NOTREACHED*/
856             }
857             datestamp = newstralloc(datestamp, cmdargs.argv[a++]);
858
859             if (a >= cmdargs.argc) {
860                 error("error [taper PORT-WRITE: not enough args: splitsize]");
861                 /*NOTREACHED*/
862             }
863             splitsize = OFF_T_ATOI(cmdargs.argv[a++]);
864
865             if (a >= cmdargs.argc) {
866                 error("error [taper PORT-WRITE: not enough args: split_diskbuffer]");
867                 /*NOTREACHED*/
868             }
869             split_diskbuffer = newstralloc(split_diskbuffer, cmdargs.argv[a++]);
870
871             if (a >= cmdargs.argc) {
872                 error("error [taper PORT-WRITE: not enough args: fallback_splitsize]");
873                 /*NOTREACHED*/
874             }
875             /* Must fit in memory... */
876             fallback_splitsize = (size_t)atoi(cmdargs.argv[a++]);
877
878             if (a != cmdargs.argc) {
879                 error("error [taper file_reader_side PORT-WRITE: too many args: %d != %d]",
880                       cmdargs.argc, a);
881                 /*NOTREACHED*/
882             }
883
884             if (fallback_splitsize < 128 ||
885                 fallback_splitsize > 64 * 1024 * 1024) {
886                 error("error [bad value for fallback_splitsize]");
887                 /*NOTREACHED*/
888             }
889             snprintf(level_str, SIZEOF(level_str), "%d", level);
890             id_string = newvstralloc(id_string, hostname, ":", qdiskname, ".",
891                                      level_str, NULL);
892
893             create_split_buffer(split_diskbuffer, fallback_splitsize, id_string);
894             amfree(id_string);
895
896             data_port = 0;
897             data_socket = stream_server(&data_port, 0, STREAM_BUFSIZE, 0);      
898             if (data_socket < 0) {
899                 char *m;
900
901                 m = vstralloc("[port create failure: ",
902                               strerror(errno),
903                               "]",
904                               NULL);
905                 q = squote(m);
906                 putresult(TAPE_ERROR, "%s %s\n", handle, q);
907                 amfree(m);
908                 amfree(q);
909                 break;
910             }
911             putresult(PORT, "%d\n", data_port);
912
913             if ((fd = stream_accept(data_socket, CONNECT_TIMEOUT,
914                                    0, STREAM_BUFSIZE)) == -1) {
915                 q = squote("[port connect timeout]");
916                 putresult(TAPE_ERROR, "%s %s\n", handle, q);
917                 aclose(data_socket);
918                 amfree(q);
919                 break;
920             }
921             expected_splits = -1;
922
923             while(read_file(fd, handle, hostname, qdiskname, datestamp, level))
924                 (void)fd;  /* Quiet lint */
925
926             aclose(data_socket);
927             break;
928
929         case FILE_WRITE:
930             /*
931              * FILE-WRITE
932              *   handle
933              *   filename
934              *   hostname
935              *   features
936              *   diskname
937              *   level
938              *   datestamp
939              *   splitsize
940              */
941             mode = MODE_FILE_WRITE;
942             cmdargs.argc++;                     /* true count of args */
943             a = 2;
944
945             if (a >= cmdargs.argc) {
946                 error("error [taper FILE-WRITE: not enough args: handle]");
947                 /*NOTREACHED*/
948             }
949             handle = newstralloc(handle, cmdargs.argv[a++]);
950
951             if (a >= cmdargs.argc) {
952                 error("error [taper FILE-WRITE: not enough args: filename]");
953                 /*NOTREACHED*/
954             }
955             qfilename = newstralloc(qfilename, cmdargs.argv[a++]);
956             if (filename != NULL)
957                 amfree(filename);
958             filename = unquote_string(qfilename);
959
960             if (a >= cmdargs.argc) {
961                 error("error [taper FILE-WRITE: not enough args: hostname]");
962                 /*NOTREACHED*/
963             }
964             hostname = newstralloc(hostname, cmdargs.argv[a++]);
965
966             if (a >= cmdargs.argc) {
967                 error("error [taper FILE-WRITE: not enough args: features]");
968                 /*NOTREACHED*/
969             }
970             am_release_feature_set(their_features);
971             their_features = am_string_to_feature(cmdargs.argv[a++]);
972
973             if (a >= cmdargs.argc) {
974                 error("error [taper FILE-WRITE: not enough args: diskname]");
975                 /*NOTREACHED*/
976             }
977             qdiskname = newstralloc(qdiskname, cmdargs.argv[a++]);
978             if (diskname != NULL)
979                 amfree(diskname);
980             diskname = unquote_string(qdiskname);
981
982             if (a >= cmdargs.argc) {
983                 error("error [taper FILE-WRITE: not enough args: level]");
984                 /*NOTREACHED*/
985             }
986             level = atoi(cmdargs.argv[a++]);
987
988             if (a >= cmdargs.argc) {
989                 error("error [taper FILE-WRITE: not enough args: datestamp]");
990                 /*NOTREACHED*/
991             }
992             datestamp = newstralloc(datestamp, cmdargs.argv[a++]);
993
994             if (a >= cmdargs.argc) {
995                 error("error [taper FILE-WRITE: not enough args: splitsize]");
996                 /*NOTREACHED*/
997             }
998             splitsize = OFF_T_ATOI(cmdargs.argv[a++]);
999
1000             if (a != cmdargs.argc) {
1001                 error("error [taper file_reader_side FILE-WRITE: too many args: %d != %d]",
1002                       cmdargs.argc, a);
1003                 /*NOTREACHED*/
1004             }
1005             if (holdfile_name != NULL) {
1006                 filename = newstralloc(filename, holdfile_name);
1007             }
1008
1009             if ((expected_splits = predict_splits(filename)) < 0) {
1010                 break;
1011             }
1012             if (stat(filename, &stat_file)!=0) {
1013                 q = squotef("[%s]", strerror(errno));
1014                 putresult(TAPE_ERROR, "%s %s\n", handle, q);
1015                 amfree(q);
1016                 break;
1017             }
1018             if ((fd = open(filename, O_RDONLY)) == -1) {
1019                 q = squotef("[%s]", strerror(errno));
1020                 putresult(TAPE_ERROR, "%s %s\n", handle, q);
1021                 amfree(q);
1022                 break;
1023             }
1024             holdfile_path = stralloc(filename);
1025             holdfile_path_thischunk = stralloc(filename);
1026             holdfile_offset_thischunk = (off_t)0;
1027
1028             while (read_file(fd,handle,hostname,qdiskname,datestamp,level)) {
1029                 if (splitsize > (off_t)0 && holdfile_path_thischunk)
1030                     filename = newstralloc(filename, holdfile_path_thischunk);
1031                 if ((fd = open(filename, O_RDONLY)) == -1) {
1032                     q = squotef("[%s]", strerror(errno));
1033                     putresult(TAPE_ERROR, "%s %s\n", handle, q);
1034                     amfree(q);
1035                     break;
1036                 }
1037             }
1038             break;
1039
1040         case QUIT:
1041             putresult(QUITTING, "\n");
1042             fprintf(stderr,"taper: DONE [idle wait: %s secs]\n",
1043                     walltime_str(total_wait));
1044             fflush(stderr);
1045             (void)syncpipe_put('Q', 0); /* tell writer we're exiting gracefully */
1046             aclose(wrpipe);
1047
1048             if ((wpid = wait(NULL)) != writerpid) {
1049                 dbprintf(("taper: writer wait returned %u instead of %u: %s\n",
1050                         (unsigned)wpid, (unsigned)writerpid, strerror(errno)));
1051                 fprintf(stderr,
1052                         "taper: writer wait returned %u instead of %u: %s\n",
1053                         (unsigned)wpid, (unsigned)writerpid, strerror(errno));
1054                 fflush(stderr);
1055             }
1056
1057             free_split_buffer();
1058             amfree(datestamp);
1059             clear_tapelist();
1060             free_server_config();
1061             amfree(taper_timestamp);
1062             amfree(label);
1063             amfree(errstr);
1064             amfree(changer_resultstr);
1065             amfree(tapedev);
1066             amfree(filename);
1067             amfree(conf_tapelist);
1068             amfree(config_dir);
1069             amfree(config_name);
1070             amfree(holdfile_name);
1071
1072             malloc_size_2 = malloc_inuse(&malloc_hist_2);
1073
1074             if (malloc_size_1 != malloc_size_2) {
1075                 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
1076             }
1077             exit(0);
1078             /*NOTREACHED*/
1079
1080         default:
1081             if (cmdargs.argc >= 1) {
1082                 q = squote(cmdargs.argv[1]);
1083             } else if (cmdargs.argc >= 0) {
1084                 q = squote(cmdargs.argv[0]);
1085             } else {
1086                 q = stralloc("(no input?)");
1087             }
1088             putresult(BAD_COMMAND, "%s\n", q);
1089             amfree(q);
1090             break;
1091         }
1092     }
1093     /* NOTREACHED */
1094 }
1095
1096 void
1097 dumpbufs(
1098     char *str1)
1099 {
1100     int i,j;
1101     long v;
1102
1103     fprintf(stderr, "%s: state", str1);
1104     for (i = j = 0; i < conf_tapebufs; i = j+1) {
1105         v = buftable[i].status;
1106         for(j = i; j < conf_tapebufs && buftable[j].status == v; j++)
1107             (void)j; /* Quiet lint */
1108         j--;
1109         if (i == j) {
1110             fprintf(stderr, " %d:", i);
1111         } else {
1112             fprintf(stderr, " %d-%d:", i, j);
1113         }
1114         switch(v) {
1115         case FULL:
1116             fputc('F', stderr);
1117             break;
1118
1119         case FILLING:
1120             fputc('f', stderr);
1121             break;
1122
1123         case EMPTY:
1124             fputc('E', stderr);
1125             break;
1126
1127         default:
1128             fprintf(stderr, "%ld", v);
1129             break;
1130         }
1131     }
1132     fputc('\n', stderr);
1133     fflush(stderr);
1134 }
1135
1136 void
1137 dumpstatus(
1138     buffer_t *bp)
1139 {
1140     char pn[2];
1141     char bt[NUM_STR_SIZE];
1142     char status[NUM_STR_SIZE + 1];
1143     char *str = NULL;
1144
1145     pn[0] = procname[0];
1146     pn[1] = '\0';
1147     snprintf(bt, SIZEOF(bt), "%d", (int)(bp-buftable));
1148
1149     switch(bp->status) {
1150     case FULL:
1151         snprintf(status, SIZEOF(status), "F" SIZE_T_FMT,
1152                 (SIZE_T_FMT_TYPE)bp->size);
1153         break;
1154
1155     case FILLING:
1156         snprintf(status, SIZEOF(status), "f");
1157         break;
1158
1159     case EMPTY:
1160         snprintf(status, SIZEOF(status), "E");
1161         break;
1162
1163     default:
1164         snprintf(status, SIZEOF(status), "%ld", bp->status);
1165         break;
1166     }
1167
1168     str = vstralloc("taper: ", pn, ": [buf ", bt, ":=", status, "]", NULL);
1169     dumpbufs(str);
1170     amfree(str);
1171 }
1172
1173 /*
1174   Handle moving to the next chunk of holding file, if any.  Returns -1 for
1175   errors, 0 if there's no more file, or a positive integer for the amount of
1176   stuff read that'll go into 'rc' (XXX That's fugly, maybe that should just
1177   be another global.  What is rc anyway, 'read count?' I keep thinking it
1178   should be 'return code')
1179 */
1180 ssize_t
1181 get_next_holding_file(
1182     int fd,
1183     buffer_t *bp,
1184     char **strclosing,
1185     size_t rc)
1186 {
1187     int save_fd;
1188     ssize_t rc1;
1189     struct stat stat_file;
1190     ssize_t ret = -1;
1191     
1192     save_fd = fd;
1193     close(fd);
1194     
1195     /* see if we're fresh out of file */
1196     if (file.cont_filename[0] == '\0') {
1197         err = 0;
1198         ret = 0;
1199     } else if (stat(file.cont_filename, &stat_file) != 0) {
1200         err = errno;
1201         ret = -1;
1202         *strclosing = newvstralloc(*strclosing, "can't stat: ",
1203                                    file.cont_filename, NULL);
1204     } else if ((fd = open(file.cont_filename,O_RDONLY)) == -1) {
1205         err = errno;
1206         ret = -1;
1207         *strclosing = newvstralloc(*strclosing, "can't open: ",
1208                                    file.cont_filename, NULL);
1209     } else if ((fd != save_fd) && dup2(fd, save_fd) == -1) {
1210         err = errno;
1211         ret = -1;
1212         *strclosing = newvstralloc(*strclosing, "can't dup2: ",
1213                                    file.cont_filename, NULL);
1214     } else {
1215         buffer_t bp1;
1216         char *quoted;
1217
1218         holdfile_path = stralloc(file.cont_filename);
1219         quoted = quote_string(holdfile_path);
1220         fprintf(stderr, "taper: r: switching to next holding chunk '%s'\n",
1221                 quoted); 
1222         amfree(quoted);
1223         num_holdfile_chunks++;
1224         
1225         bp1.status = EMPTY;
1226         bp1.size = DISK_BLOCK_BYTES;
1227         bp1.buffer = alloc(DISK_BLOCK_BYTES);
1228         
1229         if (fd != save_fd) {
1230             close(fd);
1231             fd = save_fd;
1232         }
1233         
1234         rc1 = taper_fill_buffer(fd, &bp1, DISK_BLOCK_BYTES);
1235         if (rc1 <= 0) {
1236             amfree(bp1.buffer);
1237             err = (rc1 < 0) ? errno : 0;
1238             ret = -1;
1239             *strclosing = newvstralloc(*strclosing,
1240                                        "Can't read header: ",
1241                                        file.cont_filename,
1242                                        NULL);
1243         } else {
1244             parse_file_header(bp1.buffer, &file, (size_t)rc1);
1245             
1246             amfree(bp1.buffer);
1247             bp1.buffer = bp->buffer + rc;
1248             
1249             rc1 = taper_fill_buffer(fd, &bp1, (size_t)tt_blocksize - rc);
1250             if (rc1 <= 0) {
1251                 err = (rc1 < 0) ? errno : 0;
1252                 ret = -1;
1253                 if (rc1 < 0) {
1254                     *strclosing = newvstralloc(*strclosing,
1255                                                "Can't read data: ",
1256                                                file.cont_filename,
1257                                                NULL);
1258                 }
1259             } else {
1260                 ret = rc1;
1261                 num_holdfiles++;
1262             }
1263         }
1264     }
1265
1266     return(ret);
1267 }
1268
1269
1270 int
1271 read_file(
1272     int         fd,
1273     char *      handle,
1274     char *      hostname,
1275     char *      qdiskname,
1276     char *      datestamp,
1277     int         level)
1278 {
1279     buffer_t *bp;
1280     int tok;
1281     ssize_t rc;
1282 #ifdef ASSERTIONS
1283     int opening;
1284 #endif
1285     int closing, bufnum, need_closing, nexting;
1286     off_t filesize;
1287     times_t runtime;
1288     char *strclosing = NULL;
1289     char seekerrstr[STR_SIZE];
1290     char *str;
1291     int header_written = 0;
1292     size_t buflen;
1293     dumpfile_t first_file;
1294     dumpfile_t cur_holdfile;
1295     off_t kbytesread = (off_t)0;
1296     int header_read = 0;
1297     char *cur_filename = NULL;
1298     int retry_from_splitbuf = 0;
1299     char *splitbuf_rd_ptr = NULL;
1300     char *q = NULL;
1301
1302 #ifdef HAVE_LIBVTBLC
1303     static char desc[45];
1304     static char vol_date[20];
1305     static char vol_label[45];
1306 #endif /* HAVE_LIBVTBLC */
1307
1308
1309     /* initialize */
1310     memset(&first_file, 0, SIZEOF(first_file));
1311     memset(&cur_holdfile, 0, SIZEOF(cur_holdfile));
1312
1313     filesize = (off_t)0;
1314     closing = 0;
1315     need_closing = 0;
1316     nexting = 0;
1317     err = 0;
1318
1319     /* don't break this if we're still on the same file as a previous init */
1320     if (cur_span_chunkstart <= (off_t)0) {
1321         fh_init(&file);
1322         header_read = 0;
1323     } else if(mode == MODE_FILE_WRITE){
1324         memcpy(&file, save_holdfile, SIZEOF(dumpfile_t));
1325         memcpy(&cur_holdfile, save_holdfile, SIZEOF(dumpfile_t));
1326     }
1327
1328     if (bufdebug) {
1329         fprintf(stderr, "taper: r: start file\n");
1330         fflush(stderr);
1331     }
1332
1333     for (bp = buftable; bp < buftable + conf_tapebufs; bp++) {
1334         bp->status = EMPTY;
1335     }
1336
1337     bp = buftable;
1338     if (interactive || bufdebug)
1339         dumpstatus(bp);
1340
1341     if ((cur_span_chunkstart >= (off_t)0) && (splitsize > (off_t)0)) {
1342         /* We're supposed to start at some later part of the file, not read the
1343            whole thing. "Seek" forward to where we want to be. */
1344         if (label)
1345             putresult(SPLIT_CONTINUE, "%s %s\n", handle, label);
1346         if ((mode == MODE_FILE_WRITE) && (cur_span_chunkstart > (off_t)0)) {
1347             char *quoted = quote_string(holdfile_path_thischunk);
1348             fprintf(stderr, "taper: r: seeking %s to " OFF_T_FMT " kb\n",
1349                             quoted,
1350                             (OFF_T_FMT_TYPE)holdfile_offset_thischunk);
1351             fflush(stderr);
1352
1353             if (holdfile_offset_thischunk > maxseek) {
1354                 snprintf(seekerrstr, SIZEOF(seekerrstr), "Can't seek by "
1355                         OFF_T_FMT " kb (compiled for %d-bit file offsets), "
1356                         "recompile with large file support or "
1357                         "set holdingdisk chunksize to <" OFF_T_FMT " Mb",
1358                         (OFF_T_FMT_TYPE)holdfile_offset_thischunk,
1359                         (int)(sizeof(off_t) * 8),
1360                         (OFF_T_FMT_TYPE)(maxseek/(off_t)1024));
1361                 log_add(L_ERROR, "%s", seekerrstr);
1362                 fprintf(stderr, "taper: r: FATAL: %s\n", seekerrstr);
1363                 fflush(stderr);
1364                 if (syncpipe_put('X', 0) == -1) {
1365                         put_syncpipe_fault_result(handle);
1366                 }
1367                 amfree(quoted);
1368                 return -1;
1369             }
1370             if (lseek(fd, holdfile_offset_thischunk*(off_t)1024, SEEK_SET) == (off_t)-1) {
1371                 fprintf(stderr, "taper: r: FATAL: seek_holdfile lseek error "
1372                         "while seeking into %s by "
1373                         OFF_T_FMT "kb: %s\n", quoted,
1374                         (OFF_T_FMT_TYPE)holdfile_offset_thischunk,
1375                         strerror(errno));
1376                 fflush(stderr);
1377                 if (syncpipe_put('X', 0) == -1) {
1378                         put_syncpipe_fault_result(handle);
1379                 }
1380                 amfree(quoted);
1381                 return -1;
1382             }
1383             amfree(quoted);
1384         } else if (mode == MODE_PORT_WRITE) {
1385             fprintf(stderr, "taper: r: re-reading split dump piece from buffer\n");
1386             fflush(stderr);
1387             retry_from_splitbuf = 1;
1388             splitbuf_rd_ptr = splitbuf;
1389             if (splitbuf_rd_ptr >= splitbuf_wr_ptr)
1390                 retry_from_splitbuf = 0;
1391         }
1392         if (cur_span_chunkstart > (off_t)0)
1393             header_read = 1; /* really initialized in prior run */
1394     }
1395
1396     /* tell writer to open tape */
1397
1398 #ifdef ASSERTIONS
1399     opening = 1;
1400 #endif
1401
1402     if (syncpipe_put('O', 0) == -1) {
1403         put_syncpipe_fault_result(handle);
1404         return -1;
1405     }
1406     if (syncpipe_putstr(datestamp) == -1) {
1407         put_syncpipe_fault_result(handle);
1408         return -1;
1409     }
1410     if (syncpipe_putstr(hostname) == -1) {
1411         put_syncpipe_fault_result(handle);
1412         return -1;
1413     }
1414     if (syncpipe_putstr(qdiskname) == -1) {
1415         put_syncpipe_fault_result(handle);
1416         return -1;
1417     }
1418     if (syncpipe_putint(level) == -1) {
1419         put_syncpipe_fault_result(handle);
1420         return -1;
1421     }
1422
1423     startclock();
1424     
1425     /* read file in loop */
1426     
1427     while (1) {
1428         if ((tok = syncpipe_get(&bufnum)) == -1) {
1429             put_syncpipe_fault_result(handle);
1430             return -1;
1431         }
1432
1433         switch(tok) {
1434         case 'O':
1435 #ifdef ASSERTIONS
1436             assert(opening);
1437             opening = 0;
1438 #endif
1439             err = 0;
1440             break;
1441             
1442         case 'R':
1443             if (bufdebug) {
1444                 fprintf(stderr, "taper: r: got R%d\n", bufnum);
1445                 fflush(stderr);
1446             }
1447             
1448             if (need_closing) {
1449                 if (syncpipe_put('C', 0) == -1) {
1450                     put_syncpipe_fault_result(handle);
1451                     return (-1);
1452                 }
1453                 closing = 1;
1454                 need_closing = 0;
1455                 break;
1456             }
1457             
1458             if (closing)
1459                 break;  /* ignore extra read tokens */
1460             
1461 #ifdef ASSERTIONS
1462             assert(!opening);
1463 #endif
1464             if(bp->status != EMPTY || bufnum != (int)(bp - buftable)) {
1465                 /* XXX this SHOULD NOT HAPPEN.  Famous last words. */
1466                 fprintf(stderr,"taper: panic: buffer mismatch at ofs "
1467                         OFF_T_FMT ":\n", (OFF_T_FMT_TYPE)filesize);
1468                 if(bufnum != (int)(bp - buftable)) {
1469                     fprintf(stderr, "    my buf %d but writer buf %d\n",
1470                             (int)(bp-buftable), bufnum);
1471                 } else {
1472                     fprintf(stderr,"buf %d state %s (%ld) instead of EMPTY\n",
1473                             (int)(bp-buftable),
1474                             bp->status == FILLING? "FILLING" :
1475                             bp->status == FULL? "FULL" : "EMPTY!?!?",
1476                             (long)bp->status);
1477                 }
1478                 dumpbufs("taper");
1479                 sleep(1);
1480                 dumpbufs("taper: after 1 sec");
1481                 if (bp->status == EMPTY)
1482                     fprintf(stderr, "taper: result now correct!\n");
1483                 fflush(stderr);
1484                 
1485                 errstr = newstralloc(errstr,
1486                                      "[fatal buffer mismanagement bug]");
1487                 q = squote(errstr);
1488                 putresult(TRYAGAIN, "%s %s\n", handle, q);
1489                 cur_span_chunkstart = (off_t)0;
1490                 amfree(q);
1491                 log_add(L_INFO, "retrying %s:%s.%d on new tape due to: %s",
1492                         hostname, qdiskname, level, errstr);
1493                 closing = 1;
1494                 if (syncpipe_put('X', 0) == -1) {/* X == buffer snafu, bail */
1495                     put_syncpipe_fault_result(handle);
1496                     return (-1);
1497                 }
1498                 do {
1499                     if ((tok = syncpipe_get(&bufnum)) == -1) {
1500                         put_syncpipe_fault_result(handle);
1501                         return (-1);
1502                     }
1503                 } while (tok != 'x');
1504                 aclose(fd);
1505                 return -1;
1506             } /* end 'if (bf->status != EMPTY || bufnum != (int)(bp-buftable))' */
1507
1508             bp->status = FILLING;
1509             buflen = header_read ? (size_t)tt_blocksize : DISK_BLOCK_BYTES;
1510             if (interactive || bufdebug)
1511                 dumpstatus(bp);
1512             if (header_written == 0 &&
1513                         (header_read == 1 || cur_span_chunkstart > (off_t)0)) {
1514                 /* for split dumpfiles, modify headers for the second - nth
1515                    pieces that signify that they're continuations of the last
1516                    normal one */
1517                 char *cont_filename;
1518                 file.type = F_SPLIT_DUMPFILE;
1519                 file.partnum = num_splits + 1;
1520                 file.totalparts = expected_splits;
1521                 cont_filename = stralloc(file.cont_filename);
1522                 file.cont_filename[0] = '\0';
1523                 build_header(bp->buffer, &file, tt_blocksize);
1524   
1525                 if (cont_filename[0] != '\0') {
1526                   file.type = F_CONT_DUMPFILE;
1527                    strncpy(file.cont_filename, cont_filename,
1528                            SIZEOF(file.cont_filename));
1529                         }
1530                 memcpy(&cur_holdfile, &file, SIZEOF(dumpfile_t));
1531   
1532                 if (interactive || bufdebug)
1533                     dumpstatus(bp);
1534                 bp->size = (ssize_t)tt_blocksize;
1535                 rc = (ssize_t)tt_blocksize;
1536                 header_written = 1;
1537                 amfree(cont_filename);
1538             } else if (retry_from_splitbuf) {
1539                 /* quietly pull dump data from our in-memory cache, and the
1540                    writer side need never know the wiser */
1541                 memcpy(bp->buffer, splitbuf_rd_ptr, tt_blocksize);
1542                 bp->size = (ssize_t)tt_blocksize;
1543                 rc = (ssize_t)tt_blocksize;
1544  
1545                 splitbuf_rd_ptr += tt_blocksize;
1546                 if (splitbuf_rd_ptr >= splitbuf_wr_ptr)
1547                     retry_from_splitbuf = 0;
1548             } else if ((rc = taper_fill_buffer(fd, bp, buflen)) < 0) {
1549                 err = errno;
1550                 closing = 1;
1551                 strclosing = newvstralloc(strclosing,"Can't read data: ",
1552                                           NULL);
1553                 if (syncpipe_put('C', 0) == -1) {
1554                     put_syncpipe_fault_result(handle);
1555                     return (-1);
1556                 }
1557             }
1558   
1559             if (!closing) {
1560                 if (rc < (ssize_t)buflen) { /* switch to next holding file */
1561                     ssize_t ret;
1562
1563                     if (file.cont_filename[0] != '\0') {
1564                         cur_filename = newvstralloc(cur_filename, file.cont_filename, NULL);
1565                     }
1566                     ret = get_next_holding_file(fd, bp, &strclosing, (size_t)rc);
1567                     if (ret <= 0) {
1568                         need_closing = 1;
1569                     } else {
1570                         memcpy(&cur_holdfile, &file, SIZEOF(dumpfile_t));
1571                         rc += ret;
1572                         bp->size = rc;
1573                     }
1574                 }
1575                 if (rc > 0) {
1576                     bp->status = FULL;
1577                     /* rebuild the header block, which might have CONT junk */
1578                     if (header_read == 0) {
1579                         char *cont_filename;
1580                         /* write the "real" filename if the holding-file
1581                            is a partial one */
1582                         parse_file_header(bp->buffer, &file, (size_t)rc);
1583                         parse_file_header(bp->buffer, &first_file, (size_t)rc);
1584                         cont_filename = stralloc(file.cont_filename);
1585                         file.cont_filename[0] = '\0';
1586                         if (splitsize > (off_t)0) {
1587                             file.type = F_SPLIT_DUMPFILE;
1588                             file.partnum = 1;
1589                             file.totalparts = expected_splits;
1590                         }
1591                         file.blocksize = tt_blocksize;
1592                         build_header(bp->buffer, &file, tt_blocksize);
1593  
1594                         file.type = F_CONT_DUMPFILE;
1595  
1596                         /* add CONT_FILENAME back to in-memory header */
1597                         strncpy(file.cont_filename, cont_filename, 
1598                                 SIZEOF(file.cont_filename));
1599                         if (interactive || bufdebug)
1600                             dumpstatus(bp);
1601                         bp->size = (ssize_t)tt_blocksize; /* output a full tape block */
1602                         /* save the header, we'll need it if we jump tapes */
1603                         memcpy(&cur_holdfile, &file, SIZEOF(dumpfile_t));
1604                         header_read = 1;
1605                         header_written = 1;
1606                         amfree(cont_filename);
1607                     } else {
1608                         filesize = kbytesread;
1609                     }
1610
1611                     if (bufdebug) {
1612                         fprintf(stderr,"taper: r: put W%d\n",(int)(bp-buftable));
1613                         fflush(stderr);
1614                     }
1615                     if (syncpipe_put('W', (int)(bp-buftable)) == -1) {
1616                         put_syncpipe_fault_result(handle);
1617                         return (-1);
1618                     }
1619                     bp = nextbuf(bp);
1620                 }
1621
1622                 if (((kbytesread + (off_t)(DISK_BLOCK_BYTES/1024)) >= splitsize)
1623                         && (splitsize > (off_t)0) && !need_closing) {
1624
1625                     if (mode == MODE_PORT_WRITE) {
1626                         splitbuf_wr_ptr = splitbuf;
1627                         splitbuf_rd_ptr = splitbuf;
1628                         memset(splitbuf, 0, SIZEOF(splitbuf));
1629                         retry_from_splitbuf = 0;
1630                     }
1631
1632                     fprintf(stderr,"taper: r: end %s.%s.%s.%d part %d, "
1633                                 "splitting chunk that started at "
1634                                 OFF_T_FMT "kb after " OFF_T_FMT
1635                                 "kb (next chunk will start at "
1636                                 OFF_T_FMT "kb)\n",
1637                                 hostname, qdiskname, datestamp, level,
1638                                 num_splits+1,
1639                                 (OFF_T_FMT_TYPE)cur_span_chunkstart,
1640                                 (OFF_T_FMT_TYPE)kbytesread,
1641                                 (OFF_T_FMT_TYPE)(cur_span_chunkstart+kbytesread));
1642                     fflush(stderr);
1643
1644                     nexting = 1;
1645                     need_closing = 1;
1646                 } /* end '(kbytesread >= splitsize && splitsize > 0)' */
1647                 if (need_closing && rc <= 0) {
1648                     if (syncpipe_put('C', 0) == -1) {
1649                         put_syncpipe_fault_result(handle);
1650                         return (-1);
1651                     }
1652                     need_closing = 0;
1653                     closing = 1;
1654                 }
1655                 kbytesread += (off_t)(rc / 1024);
1656             } /* end the 'if (!closing)' (successful buffer fill) */
1657             break;
1658
1659         case 'T':
1660         case 'E':
1661         case 'H':
1662             if (syncpipe_put('e', 0) == -1) {   /* ACK error */
1663                 put_syncpipe_fault_result(handle);
1664                 return (-1);
1665             }
1666
1667             if ((str = syncpipe_getstr()) == NULL) {
1668                 put_syncpipe_fault_result(handle);
1669                 return (-1);
1670             }
1671             
1672             errstr = newvstralloc(errstr, "[", str, "]", NULL);
1673             amfree(str);
1674
1675             q = squote(errstr);
1676             if (tok == 'T') {
1677                 if (splitsize > (off_t)0) {
1678                     /* we'll be restarting this chunk on the next tape */
1679                     if (mode == MODE_FILE_WRITE) {
1680                       aclose(fd);
1681                     }
1682
1683                     putresult(SPLIT_NEEDNEXT, "%s " OFF_T_FMT "\n", handle,
1684                                 (OFF_T_FMT_TYPE)cur_span_chunkstart);
1685                     log_add(L_INFO, "continuing %s:%s.%d on new tape from "
1686                                 OFF_T_FMT "kb mark: %s",
1687                                 hostname, qdiskname, level,
1688                                 (OFF_T_FMT_TYPE)cur_span_chunkstart, errstr);
1689                     return 1;
1690                 } else {
1691                     /* restart the entire dump (failure propagates to driver) */
1692                     aclose(fd);
1693                     putresult(TRYAGAIN, "%s %s\n", handle, q);
1694                     cur_span_chunkstart = (off_t)0;
1695                     log_add(L_INFO, "retrying %s:%s.%d on new tape due to: %s",
1696                             hostname, qdiskname, level, errstr);
1697                 }
1698             } else {
1699                 aclose(fd);
1700                 putresult(TAPE_ERROR, "%s %s\n", handle, q);
1701                 log_add(L_FAIL, "%s %s %s %d [out of tape]",
1702                         hostname, qdiskname, datestamp, level);
1703                 log_add(L_ERROR,"no-tape [%s]", "No more writable valid tape found");
1704             }
1705             amfree(q);
1706             return 0;
1707
1708         case 'C':
1709 #ifdef ASSERTIONS
1710             assert(!opening);
1711 #endif
1712             assert(closing);
1713
1714             if (nexting) {
1715               cur_span_chunkstart += kbytesread; /* XXX possibly wrong */
1716               if (cur_filename)
1717                 holdfile_name = newvstralloc(holdfile_name, cur_filename,
1718                                              NULL);
1719               else
1720                 amfree(holdfile_name);
1721
1722               kbytesread = (off_t)0;
1723               amfree(cur_filename);
1724             }
1725
1726             if ((str = syncpipe_getstr()) == NULL) {
1727                 put_syncpipe_fault_result(handle);
1728                 return (-1);
1729             }
1730
1731             label = newstralloc(label, str ? str : "(null)");
1732             amfree(str);
1733             if ((str = syncpipe_getstr()) == NULL) {
1734                 put_syncpipe_fault_result(handle);
1735                 return (-1);
1736             }
1737
1738             filenum = atoi(str ? str : "-9876");        /* ??? */
1739             amfree(str);
1740             fprintf(stderr, "taper: reader-side: got label %s filenum %d\n",
1741                     label, filenum);
1742             fflush(stderr);
1743
1744             /* we'll need that file descriptor if we're gonna write more */
1745             if (!nexting) {
1746                 aclose(fd);
1747             }
1748
1749             runtime = stopclock();
1750             if (nexting)
1751                 startclock();
1752             if (err) {
1753                 if (strclosing) {
1754                     errstr = newvstralloc(errstr,
1755                                           "[input: ", strclosing, ": ",
1756                                           strerror(err), "]", NULL);
1757                     amfree(strclosing);
1758                 } else
1759                     errstr = newvstralloc(errstr,
1760                                           "[input: ", strerror(err), "]",
1761                                           NULL);
1762                 q = squote(errstr);
1763                 putresult(TAPE_ERROR, "%s %s\n", handle, q);
1764
1765                 amfree(q);
1766                 if (splitsize != (off_t)0) {
1767                     log_add(L_FAIL, "%s %s %s.%d %d %s", hostname, qdiskname,
1768                                 datestamp, num_splits, level, errstr);
1769                 } else {
1770                     log_add(L_FAIL, "%s %s %s %d %s",
1771                                 hostname, qdiskname, datestamp, level, errstr);
1772                 }
1773                 if ((str = syncpipe_getstr()) == NULL) {        /* reap stats */
1774                     put_syncpipe_fault_result(handle);
1775                     return (-1);
1776                 }
1777                 amfree(str);
1778                 amfree(errstr);
1779             } else {
1780                 char kb_str[NUM_STR_SIZE];
1781                 char kps_str[NUM_STR_SIZE];
1782                 double rt;
1783
1784                 rt = (double)(runtime.r.tv_sec) +
1785                      ((double)(runtime.r.tv_usec) / 1000000.0);
1786                 curdump_rt = timesadd(runtime, curdump_rt);
1787                 snprintf(kb_str, SIZEOF(kb_str), OFF_T_FMT,
1788                         (OFF_T_FMT_TYPE)filesize);
1789                 snprintf(kps_str, SIZEOF(kps_str), "%3.1lf",
1790                                   (isnormal(rt) ? (double)filesize / rt : 0.0));
1791                 if ((str = syncpipe_getstr()) == NULL) {
1792                     put_syncpipe_fault_result(handle);
1793                     return (-1);
1794                 }
1795                 errstr = newvstralloc(errstr,
1796                                       "[sec ", walltime_str(runtime),
1797                                       " kb ", kb_str,
1798                                       " kps ", kps_str,
1799                                       " ", str,
1800                                       "]",
1801                                       NULL);
1802                 if (splitsize == (off_t)0) { /* Ordinary dump */
1803                     q = squote(errstr);
1804 /*@i@*/             if (first_file.is_partial) {
1805                         putresult(PARTIAL, "%s %s %d %s\n",
1806                                   handle, label, filenum, q);
1807                         log_add(L_PARTIAL, "%s %s %s %d %s",
1808                                 hostname, qdiskname, datestamp, level, errstr);
1809                     } else {
1810                         putresult(DONE, "%s %s %d %s\n",
1811                                   handle, label, filenum, q);
1812                         log_add(L_SUCCESS, "%s %s %s %d %s",
1813                                 hostname, qdiskname, datestamp, level, errstr);
1814                     }
1815                     amfree(q);
1816                 } else { /* Chunked dump */
1817                     num_splits++;
1818                     if (mode == MODE_FILE_WRITE) {
1819                         holdfile_path_thischunk = stralloc(holdfile_path);
1820                         holdfile_offset_thischunk = (lseek(fd, (off_t)0, SEEK_CUR))/(off_t)1024;
1821                         if(!save_holdfile){
1822                             save_holdfile = alloc(SIZEOF(dumpfile_t));
1823                         }
1824                         memcpy(save_holdfile, &cur_holdfile,SIZEOF(dumpfile_t));
1825                     }
1826                     log_add(L_CHUNK, "%s %s %s %d %d %s", hostname, qdiskname,
1827                             datestamp, num_splits, level, errstr);
1828                     if (!nexting) { /* split dump complete */
1829                         rt = (double)(curdump_rt.r.tv_sec) +
1830                              ((double)(curdump_rt.r.tv_usec) / 1000000.0);
1831                         snprintf(kb_str, SIZEOF(kb_str), OFF_T_FMT,
1832                                 (OFF_T_FMT_TYPE)(filesize + cur_span_chunkstart));
1833                         snprintf(kps_str, SIZEOF(kps_str), "%3.1lf",
1834                             isnormal(rt) ?
1835                             ((double)(filesize+cur_span_chunkstart)) / rt :
1836                             0.0);
1837                         amfree(errstr);
1838                         errstr = newvstralloc(errstr,
1839                                               "[sec ", walltime_str(curdump_rt),
1840                                               " kb ", kb_str,
1841                                               " kps ", kps_str,
1842                                               " ", str,
1843                                               "]",
1844                                               NULL);
1845                         q = squote(errstr);
1846                         putresult(DONE, "%s %s %d %s\n", handle, label,
1847                                   filenum, q);
1848                         log_add(L_CHUNKSUCCESS, "%s %s %s %d %s",
1849                                 hostname, qdiskname, datestamp, level, errstr);
1850                         amfree(save_holdfile);
1851                         amfree(holdfile_path_thischunk);
1852                         amfree(q);
1853                     }
1854                 }
1855                 amfree(str);
1856
1857                 if (!nexting) {
1858                     num_splits = 0;
1859                     expected_splits = 0;
1860                     amfree(holdfile_name);
1861                     num_holdfiles = 0;
1862                     cur_span_chunkstart = (off_t)0;
1863                     curdump_rt = times_zero;
1864                 }
1865                 amfree(errstr);
1866                 
1867 #ifdef HAVE_LIBVTBLC
1868                 /* 
1869                  *  We have 44 characters available for the label string:
1870                  *  use max 20 characters for hostname
1871                  *      max 20 characters for diskname 
1872                  *             (it could contain a samba share or dos path)
1873                  *           2 for level
1874                  */
1875                 memset(desc, '\0', 45);
1876
1877                 strncpy(desc, hostname, 20);
1878
1879                 if ((len = strlen(hostname)) <= 20) {
1880                     memset(desc + len, ' ', 1);
1881                     offset = len + 1;
1882                 } else {
1883                     memset(desc + 20, ' ', 1);
1884                     offset = 21;
1885                 }
1886
1887                 strncpy(desc + offset, qdiskname, 20);
1888
1889                 if ((len = strlen(qdiskname)) <= 20) {
1890                     memset(desc + offset + len, ' ', 1);
1891                     offset = offset + len + 1;
1892                 } else {
1893                     memset(desc + offset + 20, ' ', 1);
1894                     offset = offset + 21;
1895                 }
1896
1897                 sprintf(desc + offset, "%i", level);
1898
1899                 strncpy(vol_label, desc, 44);
1900                 fprintf(stderr, "taper: added vtbl label string %i: \"%s\"\n",
1901                         filenum, vol_label);
1902                 fflush(stderr);
1903
1904                 /* pass label string on to tape writer */
1905                 if (syncpipe_put('L', filenum) == -1) {
1906                     put_syncpipe_fault_result(handle);
1907                     return (-1);
1908                 }
1909                 if (syncpipe_putstr(vol_label) == -1) {
1910                     put_syncpipe_fault_result(handle);
1911                     return (-1);
1912                 }
1913
1914                 /* 
1915                  * reformat datestamp for later use with set_date from vtblc 
1916                  */
1917                 strptime(datestamp, "%Y%m%d", &backup_time);
1918                 strftime(vol_date, 20, "%T %D", &backup_time);
1919                 fprintf(stderr, 
1920                         "taper: reformatted vtbl date string: \"%s\"->\"%s\"\n",
1921                         datestamp,
1922                         vol_date);
1923
1924                 /* pass date string on to tape writer */                
1925                 if (syncpipe_put('D', filenum) == -1) {
1926                     put_syncpipe_fault_result(handle);
1927                     return (-1);
1928                 }
1929                 if (syncpipe_putstr(vol_date) == -1) {
1930                     put_syncpipe_fault_result(handle);
1931                     return (-1);
1932                 }
1933
1934 #endif /* HAVE_LIBVTBLC */
1935             }
1936             /* reset stuff that assumes we're on a new file */
1937
1938             if (!nexting)
1939                 return 0;
1940
1941 #ifdef ASSERTIONS
1942             opening = 1;
1943 #endif
1944             nexting = 0;
1945             closing = 0;
1946             filesize = (off_t)0;
1947             if (syncpipe_put('O', 0) == -1) {
1948                 put_syncpipe_fault_result(handle);
1949                 return -1;
1950             }
1951             if (syncpipe_putstr(datestamp) == -1) {
1952                 put_syncpipe_fault_result(handle);
1953                 return -1;
1954             }
1955             if (syncpipe_putstr(hostname) == -1) {
1956                 put_syncpipe_fault_result(handle);
1957                 return -1;
1958             }
1959             if (syncpipe_putstr(qdiskname) == -1) {
1960                 put_syncpipe_fault_result(handle);
1961                 return -1;
1962             }
1963             if (syncpipe_putint(level) == -1) {
1964                 put_syncpipe_fault_result(handle);
1965                 return -1;
1966             }
1967             for (bp = buftable; bp < buftable + conf_tapebufs; bp++) {
1968                 bp->status = EMPTY;
1969             }
1970             bp = buftable;
1971             header_written = 0;
1972             break;
1973
1974         case 'X':
1975             /*
1976              * Pipe read error: Communications is severed at least
1977              * back to us.  We send a blind 'Q' (quit) and we don't
1978              * wait for a response...
1979              */
1980             syncpipe_put('Q', 0);                       /* ACK error */
1981             fprintf(stderr, "taper: communications pipe from reader severed\n");
1982             return -1;
1983
1984         default:
1985             q = squotef("[Taper syncpipe protocol error]");
1986             putresult(TAPE_ERROR, "%s %s\n", handle, q);
1987             log_add(L_ERROR, "tape-error %s %s", handle, q);
1988             amfree(q);
1989             return -1;
1990         }
1991     }
1992     return 0;
1993 }
1994
1995 ssize_t
1996 taper_fill_buffer(
1997     int fd,
1998     buffer_t *bp,
1999     size_t buflen)
2000 {
2001     char *curptr;
2002     ssize_t cnt;
2003
2004     curptr = bp->buffer;
2005
2006     cnt = fullread(fd, curptr, buflen);
2007     switch(cnt) {
2008     case 0:     /* eof */
2009         if (interactive)
2010             fputs("r0", stderr);
2011         bp->size = 0;
2012         return (ssize_t)0;
2013         /*NOTREACHED*/
2014
2015     case -1:    /* error on read, punt */
2016         if (interactive)
2017             fputs("rE", stderr);
2018         bp->size = 0;
2019         return -1;
2020         /*NOTREACHED*/
2021
2022     default:
2023         if ((mode == MODE_PORT_WRITE) && (splitsize > (off_t)0)) {
2024             memcpy(splitbuf_wr_ptr, curptr, (size_t)cnt);
2025             splitbuf_wr_ptr += cnt;
2026         }
2027         bp->size = cnt;
2028         break;
2029     }
2030
2031     if (interactive)
2032         fputs("R", stderr);
2033     return ((ssize_t)bp->size);
2034 }
2035
2036 /* Given a dumpfile in holding, determine its size and figure out how many
2037  * times we'd have to split it.
2038  */
2039 int
2040 predict_splits(
2041     char *filename)
2042 {
2043     int splits = 0;
2044     off_t total_kb = (off_t)0;
2045     off_t adj_splitsize = splitsize - (off_t)(DISK_BLOCK_BYTES / 1024);
2046
2047     if (splitsize <= (off_t)0)
2048         return(0);
2049
2050     if (adj_splitsize <= (off_t)0) {
2051       error("Split size must be > " OFF_T_FMT "k",
2052         (OFF_T_FMT_TYPE)(DISK_BLOCK_BYTES/1024));
2053       /*NOTREACHED*/
2054     }
2055
2056     /* should only calculuate this once, not on retries etc */
2057     if (expected_splits != 0)
2058         return(expected_splits);
2059
2060     total_kb = size_holding_files(filename, 1);
2061     
2062     if (total_kb <= (off_t)0) {
2063       fprintf(stderr, "taper: r: " OFF_T_FMT
2064                 " kb holding file makes no sense, not precalculating splits\n",
2065                 (OFF_T_FMT_TYPE)total_kb);
2066       fflush(stderr);
2067       return(0);
2068     }
2069
2070     fprintf(stderr, "taper: r: Total dump size should be " OFF_T_FMT
2071                 "kb, chunk size is " OFF_T_FMT "kb\n",
2072                 (OFF_T_FMT_TYPE)total_kb,
2073                 (OFF_T_FMT_TYPE)splitsize);
2074     fflush(stderr);
2075
2076     splits = (int)(total_kb / adj_splitsize);
2077     if ((splits == 0) || (total_kb % adj_splitsize))
2078         splits++;
2079
2080
2081     fprintf(stderr, "taper: r: Expecting to split into %d parts \n", splits);
2082     fflush(stderr);
2083
2084     return(splits);
2085 }
2086
2087 /*
2088  * ========================================================================
2089  * TAPE WRITER SIDE
2090  *
2091  */
2092 times_t idlewait, rdwait, wrwait, fmwait;
2093 unsigned long total_writes;
2094 off_t total_tape_used;
2095 int total_tape_fm;
2096
2097 void write_file(void);
2098 int write_buffer(buffer_t *bp);
2099
2100 void
2101 tape_writer_side(
2102     int getp,
2103     int putp)
2104 {
2105     int tok;
2106     int tape_started;
2107     char *str;
2108     char *hostname;
2109     char *diskname;
2110     char *datestamp;
2111     int level;
2112     int tmpint;
2113
2114 #ifdef HAVE_LIBVTBLC
2115     char *vol_label;
2116     char *vol_date;
2117 #endif /* HAVE_LIBVTBLC */
2118
2119     procname = "writer";
2120     syncpipe_init(getp, putp);
2121     tape_started = 0;
2122     idlewait = times_zero;
2123     if (tapedev != NULL) {
2124         tapedev = stralloc(tapedev);
2125     }
2126
2127     while (1) {
2128         startclock();
2129         if ((tok = syncpipe_get(&tmpint)) == -1) {
2130             error("writer: Syncpipe failure before start");
2131             /*NOTREACHED*/
2132         }
2133
2134         idlewait = timesadd(idlewait, stopclock());
2135         if (tok != 'S' && tok != 'Q' && !tape_started) {
2136             error("writer: token '%c' before start", tok);
2137             /*NOTREACHED*/
2138         }
2139
2140         switch(tok) {
2141         case 'H':               /* Reader read pipe side is down */
2142             dbprintf(("writer: Communications with reader is down"));
2143             error("writer: Communications with reader is down");
2144             /*NOTREACHED*/
2145             
2146         case 'S':               /* start-tape */
2147             if (tape_started) {
2148                 error("writer: multiple start requests");
2149                 /*NOTREACHED*/
2150             }
2151             if ((str = syncpipe_getstr()) == NULL) {
2152                 error("writer: Syncpipe failure");
2153                 /*NOTREACHED*/
2154             }
2155             if (!first_tape(str ? str : "bad-datestamp")) {
2156                 if (tape_fd >= 0) {
2157                     tapefd_close(tape_fd);
2158                     tape_fd = -1;
2159                 }
2160                 if (syncpipe_put('E', 0) == -1) {
2161                     error("writer: Syncpipe failure passing exit code");
2162                     /*NOTREACHED*/
2163                 }
2164                 if (syncpipe_putstr(errstr) == -1) {
2165                     error("writer: Syncpipe failure passing exit string");
2166                     /*NOTREACHED*/
2167                 }
2168                 /* wait for reader to acknowledge error */
2169                 do {
2170                     if ((tok = syncpipe_get(&tmpint)) == -1) {
2171                         error("writer: Syncpipe failure waiting for error ack");
2172                         /*NOTREACHED*/
2173                     }
2174                     if (tok != 'e') {
2175                         error("writer: got '%c' unexpectedly after error", tok);
2176                         /*NOTREACHED*/
2177                     }
2178                 } while (tok != 'e');
2179             } else {
2180                 if (syncpipe_put('S', 0) == -1) {
2181                     error("writer: syncpipe failure while starting tape");
2182                     /*NOTREACHED*/
2183                 }
2184                 tape_started = 1;
2185             }
2186             amfree(str);
2187             break;
2188
2189         case 'O':               /* open-output */
2190             if ((datestamp = syncpipe_getstr()) == NULL) {
2191                 error("writer: Syncpipe failure during open");
2192                 /*NOTREACHED*/
2193             }
2194             tapefd_setinfo_datestamp(tape_fd, datestamp);
2195             amfree(datestamp);
2196
2197             if ((hostname = syncpipe_getstr()) == NULL) {
2198                 error("writer: Syncpipe failure fetching hostname");
2199                 /*NOTREACHED*/
2200             }
2201             tapefd_setinfo_host(tape_fd, hostname);
2202             amfree(hostname);
2203
2204             if ((diskname = syncpipe_getstr()) == NULL) {
2205                 error("writer: Syncpipe failure fetching diskname");
2206                 /*NOTREACHED*/
2207             }
2208             tapefd_setinfo_disk(tape_fd, diskname);
2209             amfree(diskname);
2210             if ((level = syncpipe_getint()) == -1) {
2211                 error("writer: Syncpipe failure fetching level");
2212                 /*NOTREACHED*/
2213             }
2214             tapefd_setinfo_level(tape_fd, level);
2215             write_file();
2216             break;
2217
2218 #ifdef HAVE_LIBVTBLC
2219         case 'L':               /* read vtbl label */
2220             vtbl_no = tmpint;
2221             if ((vol_label = syncpipe_getstr()) == NULL) {
2222                 error("writer: Syncpipe failure fetching vrbl label");
2223                 /*NOTREACHED*/
2224             }
2225             fprintf(stderr, "taper: read label string \"%s\" from pipe\n", 
2226                     vol_label);
2227             strncpy(vtbl_entry[vtbl_no].label, vol_label, 45);
2228             break;
2229
2230         case 'D':               /* read vtbl date */
2231             vtbl_no = tmpint;
2232             if ((vol_date = syncpipe_getstr()) == NULL) {
2233                 error("writer: Syncpipe failure fetching vrbl date");
2234                 /*NOTREACHED*/
2235             }
2236             fprintf(stderr, "taper: read date string \"%s\" from pipe\n", 
2237                     vol_date);
2238             strncpy(vtbl_entry[vtbl_no].date, vol_date, 20);
2239             break;
2240 #endif /* HAVE_LIBVTBLC */
2241
2242         case 'Q':
2243             end_tape(0);        /* XXX check results of end tape ?? */
2244             clear_tapelist();
2245             free_server_config();
2246             amfree(taper_timestamp);
2247             amfree(label);
2248             amfree(errstr);
2249             amfree(changer_resultstr);
2250             amfree(tapedev);
2251             amfree(conf_tapelist);
2252             amfree(config_dir);
2253             amfree(config_name);
2254
2255             malloc_size_2 = malloc_inuse(&malloc_hist_2);
2256
2257             if (malloc_size_1 != malloc_size_2) {
2258                 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
2259             }
2260             exit(0);
2261             /*NOTREACHED*/
2262
2263         default:
2264             assert(0);
2265         }
2266     }
2267 }
2268
2269 void
2270 write_file(void)
2271 {
2272     buffer_t *bp;
2273     int full_buffers, i, bufnum;
2274     int tok;
2275     char number[NUM_STR_SIZE];
2276     char *rdwait_str, *wrwait_str, *fmwait_str;
2277     int tmpint;
2278
2279     rdwait = wrwait = times_zero;
2280     total_writes = 0;
2281
2282     bp = buftable;
2283     full_buffers = 0;
2284     tok = '?';
2285
2286     if (bufdebug) {
2287         fprintf(stderr, "taper: w: start file\n");
2288         fflush(stderr);
2289     }
2290
2291     /*
2292      * Tell the reader that the tape is open, and give it all the buffers.
2293      */
2294     if (syncpipe_put('O', 0) == -1) {
2295         error("writer: Syncpipe failure starting write sequence");
2296         /*NOTREACHED*/
2297     }
2298     for (i = 0; i < conf_tapebufs; i++) {
2299         if (bufdebug) {
2300             fprintf(stderr, "taper: w: put R%d\n", i);
2301             fflush(stderr);
2302         }
2303         if (syncpipe_put('R', i) == -1) {
2304             error("writer: Syncpipe failure readying write buffers");
2305             /*NOTREACHED*/
2306         }
2307     }
2308
2309     /*
2310      * We write the filemark at the start of the file rather than at the end,
2311      * so that it can proceed in parallel with the reader's initial filling
2312      * up of the buffers.
2313      */
2314
2315     startclock();
2316     if (!write_filemark())
2317         goto tape_error;
2318     fmwait = stopclock();
2319
2320     filenum += 1;
2321
2322     do {
2323
2324         /*
2325          * STOPPED MODE
2326          *
2327          * At the start of the file, or if the input can't keep up with the
2328          * tape, we enter STOPPED mode, which waits for most of the buffers
2329          * to fill up before writing to tape.  This maximizes the amount of
2330          * data written in chunks to the tape drive, minimizing the number
2331          * of starts/stops, which in turn saves tape and time.
2332          */
2333
2334         if (interactive)
2335             fputs("[WS]", stderr);
2336         startclock();
2337         while (full_buffers < conf_tapebufs - THRESHOLD) {
2338             if ((tok = syncpipe_get(&bufnum)) == -1) {
2339                 error("writer: Syncpipe failure during buffer advance");
2340                 /*NOTREACHED*/
2341             }
2342             if (tok != 'W')
2343                 break;
2344             if (bufdebug) {
2345                 fprintf(stderr,"taper: w: got W%d\n",bufnum);
2346                 fflush(stderr);
2347             }
2348             full_buffers++;
2349         }
2350         rdwait = timesadd(rdwait, stopclock());
2351
2352         /*
2353          * STARTING MODE
2354          *
2355          * We start output when sufficient buffers have filled up, or at
2356          * end-of-file, whichever comes first.  Here we drain all the buffers
2357          * that were waited on in STOPPED mode.  If more full buffers come
2358          * in, then we will be STREAMING.
2359          */
2360
2361         while (full_buffers) {
2362             if (tt_file_pad && bp->size < (ssize_t)tt_blocksize) {
2363                 memset(bp->buffer+bp->size, 0, tt_blocksize - bp->size);
2364                 bp->size = (ssize_t)tt_blocksize;
2365             }
2366             if (!write_buffer(bp))
2367                 goto tape_error;
2368             full_buffers--;
2369             bp = nextbuf(bp);
2370         }
2371
2372         /*
2373          * STREAMING MODE
2374          *
2375          * With any luck, the input source is faster than the tape drive.  In
2376          * this case, full buffers will appear in the circular queue faster
2377          * than we can write them, so the next buffer in the queue will always
2378          * be marked FULL by the time we get to it.  If so, we'll stay in
2379          * STREAMING mode.
2380          *
2381          * On the other hand, if we catch up to the input and thus would have
2382          * to wait for buffers to fill, we are then STOPPED again.
2383          */
2384
2385         while (tok == 'W' && bp->status == FULL) {
2386             if ((tok = syncpipe_get(&bufnum)) == -1) {
2387                 error("writer: Syncpipe failure advancing buffer");
2388                 /*NOTREACHED*/
2389             }
2390
2391             if (tok == 'W') {
2392                 if (bufdebug) {
2393                     fprintf(stderr,"taper: w: got W%d\n",bufnum);
2394                     fflush(stderr);
2395                 }
2396                 if(bufnum != (int)(bp - buftable)) {
2397                     fprintf(stderr,
2398                             "taper: tape-writer: my buf %d reader buf %d\n",
2399                             (int)(bp-buftable), bufnum);
2400                     fflush(stderr);
2401                     if (syncpipe_put('E', 0) == -1) { 
2402                         error("writer: Syncpipe failure putting error token");
2403                         /*NOTREACHED*/
2404                     }
2405                     if (syncpipe_putstr("writer-side buffer mismatch") == -1) {
2406                         error("writer: Syncpipe failure putting error messgae");
2407                         /*NOTREACHED*/
2408                     }
2409                     goto error_ack;
2410                 }
2411                 if (tt_file_pad && bp->size < (ssize_t)tt_blocksize) {
2412                     memset(bp->buffer+bp->size, 0, tt_blocksize - bp->size);
2413                     bp->size = (ssize_t)tt_blocksize;
2414                 }
2415                 if (!write_buffer(bp))
2416                     goto tape_error;
2417                 bp = nextbuf(bp);
2418             } else if (tok == 'Q') {
2419                 return;
2420             } else if (tok == 'X') {
2421                 goto reader_buffer_snafu;
2422             } else {
2423                 error("writer-side not expecting token: %c", tok);
2424                 /*NOTREACHED*/
2425             }
2426         }
2427     } while (tok == 'W');
2428
2429     /* got close signal from reader, acknowledge it */
2430
2431     if (tok == 'X')
2432         goto reader_buffer_snafu;
2433
2434     assert(tok == 'C');
2435     if (syncpipe_put('C', 0) == -1) {
2436         error("writer: Syncpipe failure putting close");
2437         /*NOTREACHED*/
2438     }
2439
2440     /* tell reader the tape and file number */
2441
2442     if (syncpipe_putstr(label) == -1) {
2443         error("writer: Syncpipe failure putting label");
2444         /*NOTREACHED*/
2445     }
2446     snprintf(number, SIZEOF(number), "%d", filenum);
2447     if (syncpipe_putstr(number) == -1) {
2448         error("writer: Syncpipe failure putting filenum");
2449         /*NOTREACHED*/
2450     }
2451
2452     snprintf(number, SIZEOF(number), "%lu", total_writes);
2453     rdwait_str = stralloc(walltime_str(rdwait));
2454     wrwait_str = stralloc(walltime_str(wrwait));
2455     fmwait_str = stralloc(walltime_str(fmwait));
2456     errstr = newvstralloc(errstr,
2457                           "{wr:",
2458                           " writers ", number,
2459                           " rdwait ", rdwait_str,
2460                           " wrwait ", wrwait_str,
2461                           " filemark ", fmwait_str,
2462                           "}",
2463                           NULL);
2464     amfree(rdwait_str);
2465     amfree(wrwait_str);
2466     amfree(fmwait_str);
2467     if (syncpipe_putstr(errstr) == -1) {
2468         error("writer: Syncpipe failure putting '%s'", errstr);
2469         /*NOTREACHED*/
2470     }
2471
2472     /* XXX go to next tape if past tape size? */
2473
2474     return;
2475
2476  tape_error:
2477     /* got tape error */
2478     if (next_tape(1)) {
2479         if (syncpipe_put('T', 0) == -1) {   /* next tape in place, try again */
2480             error("writer: Syncpipe failure during tape advance");
2481             /*NOTREACHED*/
2482         }
2483     } else {
2484         if (syncpipe_put('E', 0) == -1) {   /* no more tapes, fail */
2485             error("writer: Syncpipe failure during tape error");
2486             /*NOTREACHED*/
2487         }
2488     }
2489     if (syncpipe_putstr(errstr) == -1) {
2490         error("writer: Syncpipe failure putting '%s'", errstr);
2491         /*NOTREACHED*/
2492     }
2493
2494  error_ack:
2495     /* wait for reader to acknowledge error */
2496     do {
2497         if ((tok = syncpipe_get(&tmpint)) == -1) {
2498             error("writer: syncpipe failure waiting for error ack");
2499             /*NOTREACHED*/
2500         }
2501
2502         if (tok != 'W' && tok != 'C' && tok != 'e') {
2503             error("writer: got '%c' unexpectedly after error", tok);
2504             /*NOTREACHED*/
2505         }
2506     } while (tok != 'e');
2507     return;
2508
2509  reader_buffer_snafu:
2510     if (syncpipe_put('x', 0) == -1) {
2511         error("writer: syncpipe failure putting buffer snafu");
2512         /*NOTREACHED*/
2513     }
2514     return;
2515 }
2516
2517 int
2518 write_buffer(
2519     buffer_t *bp)
2520 {
2521     ssize_t rc;
2522
2523     assert(bp->status == FULL);
2524
2525     startclock();
2526     rc = tapefd_write(tape_fd, bp->buffer, (size_t)bp->size);
2527     if (rc == (ssize_t)bp->size) {
2528 #if defined(NEED_RESETOFS)
2529         static double tape_used_modulus_2gb = 0;
2530
2531         /*
2532          * If the next write will go over the 2 GByte boundary, reset
2533          * the kernel concept of where we are to make sure it does not
2534          * go silly on us.
2535          */
2536         tape_used_modulus_2gb += (double)rc;
2537         if (tape_used_modulus_2gb + (double)rc > (double)0x7fffffff) {
2538             tape_used_modulus_2gb = 0;
2539             tapefd_resetofs(tape_fd);
2540         }
2541 #endif
2542         wrwait = timesadd(wrwait, stopclock());
2543         total_writes += 1;
2544         total_tape_used += (off_t)rc;
2545         bp->status = EMPTY;
2546         if (interactive || bufdebug)
2547             dumpstatus(bp);
2548         if (interactive)
2549             fputs("W", stderr);
2550
2551         if (bufdebug) {
2552             fprintf(stderr, "taper: w: put R%d\n", (int)(bp-buftable));
2553             fflush(stderr);
2554         }
2555         if (syncpipe_put('R', (int)(bp-buftable)) == -1) {
2556             error("writer: Syncpipe failure during advancing write bufffer");
2557             /*NOTREACHED*/
2558         }
2559         return 1;
2560     } else {
2561         errstr = newvstralloc(errstr,
2562                               "writing file: ",
2563                               (rc != -1) ? "short write" : strerror(errno),
2564                               NULL);
2565         wrwait = timesadd(wrwait, stopclock());
2566         if (interactive)
2567             fputs("[WE]", stderr);
2568         return 0;
2569     }
2570 }
2571
2572
2573 static void 
2574 cleanup(void)
2575 {
2576     REMOVE_SHARED_MEMORY(); 
2577 }
2578
2579
2580 /*
2581  * Cleanup shared memory segments 
2582  */
2583 static void 
2584 signal_handler(
2585     int signum)
2586 {
2587     log_add(L_INFO, "Received signal %d", signum);
2588
2589     exit(1);
2590 }
2591
2592
2593 /*
2594  * Installing signal handlers for signal whose default action is 
2595  * process termination so that we can clean up shared memory
2596  * segments
2597  */
2598 static void
2599 install_signal_handlers(void)
2600 {
2601     struct sigaction act;
2602
2603     act.sa_handler = signal_handler;
2604     act.sa_flags = 0;
2605     sigemptyset(&act.sa_mask);
2606
2607     signal(SIGPIPE, SIG_IGN);
2608
2609     if (sigaction(SIGINT, &act, NULL) != 0) {
2610         error("taper: couldn't install SIGINT handler [%s]", strerror(errno));
2611         /*NOTREACHED*/
2612     }
2613
2614     if (sigaction(SIGHUP, &act, NULL) != 0) {
2615         error("taper: couldn't install SIGHUP handler [%s]", strerror(errno));
2616         /*NOTREACHED*/
2617     }
2618    
2619     if (sigaction(SIGTERM, &act, NULL) != 0) {
2620         error("taper: couldn't install SIGTERM handler [%s]", strerror(errno));
2621         /*NOTREACHED*/
2622     }
2623
2624     if (sigaction(SIGUSR1, &act, NULL) != 0) {
2625         error("taper: couldn't install SIGUSR1 handler [%s]", strerror(errno));
2626         /*NOTREACHED*/
2627     }
2628
2629     if (sigaction(SIGUSR2, &act, NULL) != 0) {
2630         error("taper: couldn't install SIGUSR2 handler [%s]", strerror(errno));
2631         /*NOTREACHED*/
2632     }
2633
2634     if (sigaction(SIGALRM, &act, NULL) != 0) {
2635         error("taper: couldn't install SIGALRM handler [%s]", strerror(errno));
2636         /*NOTREACHED*/
2637     }
2638 }
2639
2640
2641 /*
2642  * ========================================================================
2643  * SHARED-MEMORY BUFFER SUBSYSTEM
2644  *
2645  */
2646
2647 #ifdef HAVE_SYSVSHM
2648
2649 int shmid = -1;
2650
2651 char *
2652 attach_buffers(
2653     size_t size)
2654 {
2655     char *result;
2656
2657     shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0700);
2658     if (shmid == -1) {
2659         return NULL;
2660     }
2661
2662     result = (char *)shmat(shmid, (SHM_ARG_TYPE *)NULL, 0);
2663
2664     if (result == (char *)-1) {
2665         int save_errno = errno;
2666
2667         destroy_buffers();
2668         errno = save_errno;
2669         error("shmat: %s", strerror(errno));
2670         /*NOTREACHED*/
2671     }
2672
2673     return result;
2674 }
2675
2676
2677 void
2678 detach_buffers(
2679     char *bufp)
2680 {
2681     if ((bufp != NULL) &&
2682         (shmdt((SHM_ARG_TYPE *)bufp) == -1)) {
2683         error("shmdt: %s", strerror(errno));
2684         /*NOTREACHED*/
2685     }
2686 }
2687
2688 void
2689 destroy_buffers(void)
2690 {
2691     if (shmid == -1)
2692         return; /* nothing to destroy */
2693     if (shmctl(shmid, IPC_RMID, NULL) == -1) {
2694         error("shmctl: %s", strerror(errno));
2695         /*NOTREACHED*/
2696     }
2697 }
2698
2699 #else
2700 #ifdef HAVE_MMAP
2701
2702 #ifdef HAVE_SYS_MMAN_H
2703 #include <sys/mman.h>
2704 #endif
2705
2706 #ifndef MAP_ANON
2707 #  ifdef MAP_ANONYMOUS                  /* OSF/1-style */
2708 #    define MAP_ANON MAP_ANONYMOUS
2709 #  else                                 /* SunOS4-style */
2710 #    define MAP_ANON 0
2711 #    define ZERO_FILE "/dev/zero"
2712 #  endif
2713 #endif
2714
2715 int shmfd = -1;
2716 size_t saved_size;
2717
2718 char *
2719 attach_buffers(
2720     size_t size)
2721 {
2722     char *shmbuf;
2723
2724 #ifdef ZERO_FILE
2725     shmfd = open(ZERO_FILE, O_RDWR);
2726     if (shmfd == -1) {
2727         error("attach_buffers: could not open %s: %s",
2728               ZERO_FILE,
2729               strerror(errno));
2730         /*NOTREACHED*/
2731     }
2732 #endif
2733
2734     saved_size = size;
2735     shmbuf = (char *) mmap((void *) 0,
2736                            size,
2737                            PROT_READ|PROT_WRITE,
2738                            MAP_ANON|MAP_SHARED,
2739                            shmfd, 0);
2740
2741     return shmbuf;
2742 }
2743
2744 void
2745 detach_buffers(
2746     char *bufp)
2747 {
2748     if ((bufp != NULL) && 
2749         (munmap((void *)bufp, saved_size) == -1)) {
2750         error("detach_buffers: munmap: %s", strerror(errno));
2751         /*NOTREACHED*/
2752     }
2753
2754     if (shmfd != -1)
2755         aclose(shmfd);
2756 }
2757
2758 void
2759 destroy_buffers(void)
2760 {
2761 }
2762
2763 #else
2764 #error: must define either HAVE_SYSVSHM or HAVE_MMAP!
2765 #endif
2766 #endif
2767
2768
2769
2770 /*
2771  * ========================================================================
2772  * SYNC-PIPE SUBSYSTEM
2773  *
2774  */
2775
2776 int getpipe, putpipe;
2777
2778 void
2779 syncpipe_init(
2780     int rd,
2781     int wr)
2782 {
2783     getpipe = rd;
2784     putpipe = wr;
2785 }
2786
2787 void
2788 syncpipe_read_error(
2789     ssize_t     rc,
2790     ssize_t     expected)
2791 {
2792     char buf[sizeof(char) + sizeof(int)];
2793
2794     if (rc == 0) {
2795         dbprintf(("syncpipe_get %s halting: Unexpected read EOF\n", procname));
2796         fprintf(stderr, "syncpipe_get %s halting: Unexpected read EOF\n", procname);
2797     } else if (rc < 0) {
2798         dbprintf(("syncpipe_get %s halting: Read error - %s\n",
2799                         procname, strerror(errno)));
2800         fprintf(stderr, "syncpipe_get %s halting: Read error - %s\n",
2801                         procname, strerror(errno));
2802     } else {
2803         dbprintf(("syncpipe_get %s halting: Read "
2804                 SSIZE_T_FMT " bytes short of " SSIZE_T_FMT "\n",
2805                 procname, (SSIZE_T_FMT_TYPE)(rc - expected),
2806                 (SSIZE_T_FMT_TYPE)expected));
2807         fprintf(stderr, "syncpipe_get %s halting: Read "
2808                 SSIZE_T_FMT " bytes short of " SSIZE_T_FMT "\n",
2809                 procname, (SSIZE_T_FMT_TYPE)(rc - expected),
2810                 (SSIZE_T_FMT_TYPE)expected);
2811     }
2812     /* Halt the other side if it's still alive */
2813     buf[0] = 'H';
2814     memset(&buf[1], 0, SIZEOF(int));
2815     if (write(putpipe, buf, SIZEOF(buf)))
2816         return;
2817 }
2818
2819 void
2820 syncpipe_write_error(
2821     ssize_t     rc,
2822     ssize_t     expected)
2823 {
2824     char buf[sizeof(char) + sizeof(int)];
2825
2826     if (rc == 0) {              /* EOF */
2827         dbprintf(("syncpipe %s halting: Write EOF\n", procname));
2828         fprintf(stderr, "syncpipe %s halting: Write EOF\n", procname);
2829     } else if (rc < 0) {
2830         dbprintf(("syncpipe %s halting: Write error - %s\n",
2831                         procname, strerror(errno)));
2832         fprintf(stderr, "syncpipe %s halting: Write error - %s\n",
2833                         procname, strerror(errno));
2834     } else {
2835         dbprintf(("syncpipe %s halting: Write "
2836                         SSIZE_T_FMT " bytes short of " SSIZE_T_FMT "\n",
2837                         procname, (SSIZE_T_FMT_TYPE)(rc - expected),
2838                         (SSIZE_T_FMT_TYPE)expected));
2839         fprintf(stderr, "syncpipe %s halting: Write "
2840                         SSIZE_T_FMT " bytes short of " SSIZE_T_FMT "\n",
2841                         procname, (SSIZE_T_FMT_TYPE)(rc - expected),
2842                         (SSIZE_T_FMT_TYPE)expected);
2843     }
2844     /* Halt the other side if it's still alive */
2845     buf[0] = 'H';
2846     memset(&buf[1], 0, SIZEOF(int));
2847     if (write(putpipe, buf, SIZEOF(buf)))
2848         return;
2849 }
2850
2851 int
2852 syncpipe_get(
2853     int *intp)
2854 {
2855     ssize_t rc;
2856     char buf[SIZEOF(char) + SIZEOF(int)];
2857
2858     memset(buf, 0, sizeof(buf));
2859     rc = fullread(getpipe, buf, SIZEOF(buf));
2860     if (rc != (ssize_t)sizeof(buf)) {
2861         syncpipe_read_error(rc, (ssize_t)sizeof(buf));
2862         return (-1);
2863     }
2864
2865     if (bufdebug && *buf != 'R' && *buf != 'W') {
2866         fprintf(stderr,"taper: %c: getc %c\n", *procname, *buf);
2867         fflush(stderr);
2868     }
2869
2870     memcpy(intp, &buf[1], SIZEOF(int));
2871     return (int)buf[0];
2872 }
2873
2874 int
2875 syncpipe_getint(void)
2876 {
2877     ssize_t rc;
2878     int i = 0;
2879
2880     rc = fullread(getpipe, &i, SIZEOF(i));
2881     if (rc != (ssize_t)sizeof(i)) {
2882         syncpipe_read_error(rc, (ssize_t)sizeof(i));
2883         return (-1);
2884     }
2885
2886     return (i);
2887 }
2888
2889
2890 char *
2891 syncpipe_getstr(void)
2892 {
2893     ssize_t rc;
2894     int len;
2895     char *str;
2896
2897     if ((len = syncpipe_getint()) <= 0) {
2898         fprintf(stderr, "syncpipe %s halting: Protocol error - "
2899                         "Invalid string length (%d)\n", procname, len);
2900         syncpipe_put('H', 0); /* Halt the other side */
2901         exit(1);
2902         /*NOTREACHED*/
2903     }
2904
2905     str = alloc((size_t)len);
2906
2907     rc = fullread(getpipe, str, (size_t)len);
2908     if (rc != (ssize_t)len) {
2909         syncpipe_read_error(rc, (ssize_t)len);
2910         return (NULL);
2911     }
2912     return (str);
2913 }
2914
2915
2916 int
2917 syncpipe_put(
2918     int chi,
2919     int intval)
2920 {
2921     char buf[sizeof(char) + sizeof(int)];
2922     ssize_t     rc;
2923
2924     buf[0] = (char)chi;
2925     memcpy(&buf[1], &intval, SIZEOF(int));
2926     if (bufdebug && buf[0] != 'R' && buf[0] != 'W') {
2927         fprintf(stderr,"taper: %c: putc %c\n",*procname,buf[0]);
2928         fflush(stderr);
2929     }
2930
2931     rc = fullwrite(putpipe, buf, SIZEOF(buf));
2932     if (rc != (ssize_t)sizeof(buf)) {
2933         syncpipe_write_error(rc, (ssize_t)sizeof(buf));
2934         return (-1);
2935     }
2936     return (0);
2937 }
2938
2939 int
2940 syncpipe_putint(
2941     int i)
2942 {
2943     ssize_t     rc;
2944
2945     rc = fullwrite(putpipe, &i, SIZEOF(i));
2946     if (rc != (ssize_t)sizeof(i)) {
2947         syncpipe_write_error(rc, (ssize_t)sizeof(i));
2948         return (-1);
2949         /* NOTREACHED */
2950     }
2951     return (0);
2952 }
2953
2954 int
2955 syncpipe_putstr(
2956     const char *str)
2957 {
2958     ssize_t n, rc;
2959
2960     if(!str)
2961         str = "UNKNOWN syncpipe_putstr STRING";
2962
2963     n = (ssize_t)strlen(str) + 1;                       /* send '\0' as well */
2964     syncpipe_putint((int)n);
2965
2966     rc = fullwrite(putpipe, str, (size_t)n);
2967     if (rc != n) {
2968         syncpipe_write_error(rc, n);
2969         return (-1);
2970     }
2971     return (0);
2972 }
2973 \f
2974 /*
2975  * ========================================================================
2976  * TAPE MANIPULATION SUBSYSTEM
2977  *
2978  */
2979 int label_tape(void);
2980
2981 /* local functions */
2982
2983 /* return 0 on success              */
2984 /* return 1 on error and set errstr */
2985 int
2986 label_tape(void)
2987 {  
2988     char *conf_tapelist_old = NULL;
2989     char *result;
2990     static int first_call = 1;
2991     char *timestamp;
2992     char *error_msg = NULL;
2993     char *s, *r;
2994     int slot = -1;
2995
2996     amfree(label);
2997     amfree(tapedev);
2998     if (taper_scan(NULL, &label, &timestamp, &tapedev, CHAR_taperscan_output_callback, &error_msg) < 0) {
2999         fprintf(stderr, "%s\n", error_msg);
3000         errstr = error_msg;
3001         error_msg = NULL;
3002         amfree(timestamp);
3003         return 0;
3004     }
3005     amfree(timestamp);
3006     if(error_msg) {
3007         s = error_msg; r = NULL;
3008         while((s=strstr(s,"slot "))) { s += 5; r=s; };
3009         if(r) {
3010             slot = atoi(r);
3011         }
3012         amfree(error_msg);
3013     }
3014     if ((tape_fd = tape_open(tapedev, O_WRONLY)) == -1) {
3015         if (errno == EACCES) {
3016             errstr = newstralloc2(errstr,
3017                                  "writing label: tape is write protected or I don't have write permission on ", tapedev);
3018         } else {
3019             errstr = newstralloc2(errstr,
3020                                   "writing label: ", strerror(errno));
3021         }
3022         return 0;
3023     }
3024
3025     tapefd_setinfo_length(tape_fd, tapetype_get_length(tt));
3026
3027     tapefd_setinfo_datestamp(tape_fd, taper_timestamp);
3028     tapefd_setinfo_disk(tape_fd, label);
3029     result = tapefd_wrlabel(tape_fd, taper_timestamp, label, tt_blocksize);
3030     if (result != NULL) {
3031         errstr = newstralloc(errstr, result);
3032         return 0;
3033     }
3034
3035     if(slot > -1) {
3036         fprintf(stderr, "taper: slot: %d wrote label `%s' date `%s'\n", slot,
3037                 label, taper_timestamp);
3038     }
3039     else {
3040         fprintf(stderr, "taper: wrote label `%s' date `%s'\n", label,
3041                 taper_timestamp);
3042     }
3043     fflush(stderr);
3044
3045 #ifdef HAVE_LIBVTBLC
3046     /* store time for the first volume entry */
3047     time(&raw_time);
3048     tape_timep = localtime(&raw_time);
3049     strftime(start_datestr, 20, "%T %D", tape_timep);
3050     fprintf(stderr, "taper: got vtbl start time: %s\n", start_datestr);
3051     fflush(stderr);
3052 #endif /* HAVE_LIBVTBLC */
3053
3054     if (strcmp(label, FAKE_LABEL) != 0) {
3055         if (cur_tape == 0) {
3056             conf_tapelist_old = stralloc2(conf_tapelist, ".yesterday");
3057         } else {
3058             char cur_str[NUM_STR_SIZE];
3059
3060             snprintf(cur_str, SIZEOF(cur_str), "%d", cur_tape - 1);
3061             conf_tapelist_old = vstralloc(conf_tapelist,
3062                                           ".today.", cur_str, NULL);
3063         }
3064
3065         if (write_tapelist(conf_tapelist_old)) {
3066             error("could not write tapelist: %s", strerror(errno));
3067             /*NOTREACHED*/
3068         }
3069         amfree(conf_tapelist_old);
3070
3071         remove_tapelabel(label);
3072         add_tapelabel(taper_timestamp, label);
3073         if (write_tapelist(conf_tapelist)) {
3074             error("could not write tapelist: %s", strerror(errno));
3075             /*NOTREACHED*/
3076         }
3077     }
3078
3079     log_add(L_START, "datestamp %s label %s tape %d",
3080             taper_timestamp, label, cur_tape);
3081     if (first_call && strcmp(label, FAKE_LABEL) == 0) {
3082         first_call = 0;
3083         log_add(L_WARNING, "tapedev is %s, dumps will be thrown away", tapedev);
3084     }
3085
3086     total_tape_used=(off_t)0;
3087     total_tape_fm = 0;
3088
3089     return 1;
3090 }
3091
3092 /* return 0 on error and set errstr */
3093 /* return 1 on success              */
3094 int
3095 first_tape(
3096     char *new_datestamp)
3097 {
3098     if ((have_changer = changer_init()) < 0) {
3099         error("changer initialization failed: %s", strerror(errno));
3100         /*NOTREACHED*/
3101     }
3102     changer_debug = 1;
3103
3104     taper_timestamp = newstralloc(taper_timestamp, new_datestamp);
3105
3106     if (!label_tape())
3107         return 0;
3108
3109     filenum = 0;
3110     return 1;
3111 }
3112
3113 int
3114 next_tape(
3115     int writerror)
3116 {
3117     end_tape(writerror);
3118
3119     if (++cur_tape >= runtapes)
3120         return 0;
3121
3122     if (!label_tape()) {
3123         return 0;
3124     }
3125
3126     filenum = 0;
3127     return 1;
3128 }
3129
3130
3131 int
3132 end_tape(
3133     int writerror)
3134 {
3135     char *result;
3136     int rc = 0;
3137
3138     if (tape_fd >= 0) {
3139         log_add(L_INFO, "tape %s kb " OFF_T_FMT " fm %d %s", 
3140                 label,
3141                 (OFF_T_FMT_TYPE)((total_tape_used+(off_t)1023) / (off_t)1024),
3142                 total_tape_fm,
3143                 writerror? errstr : "[OK]");
3144
3145         fprintf(stderr, "taper: writing end marker. [%s %s kb "
3146                 OFF_T_FMT " fm %d]\n", label,
3147                 writerror? "ERR" : "OK",
3148                 (OFF_T_FMT_TYPE)((total_tape_used+(off_t)1023) / (off_t)1024),
3149                 total_tape_fm);
3150         fflush(stderr);
3151         if (! writerror) {
3152             if (! write_filemark()) {
3153                 rc = 1;
3154                 goto common_exit;
3155             }
3156
3157             result = tapefd_wrendmark(tape_fd, taper_timestamp, tt_blocksize);
3158             if (result != NULL) {
3159                 errstr = newstralloc(errstr, result);
3160                 rc = 1;
3161                 goto common_exit;
3162             }
3163         }
3164     }
3165
3166 #ifdef HAVE_LINUX_ZFTAPE_H
3167     if (tape_fd >= 0 && is_zftape(tapedev) == 1) {
3168         /* rewind the tape */
3169
3170         if (tapefd_rewind(tape_fd) == -1 ) {
3171             errstr = newstralloc2(errstr, "rewinding tape: ", strerror(errno));
3172             rc = 1;
3173             goto common_exit;
3174         }
3175         /* close the tape */
3176
3177         if (tapefd_close(tape_fd) == -1) {
3178             errstr = newstralloc2(errstr, "closing tape: ", strerror(errno));
3179             rc = 1;
3180             goto common_exit;
3181         }
3182         tape_fd = -1;
3183
3184 #ifdef HAVE_LIBVTBLC
3185         /* update volume table */
3186         fprintf(stderr, "taper: updating volume table ...\n");
3187         fflush(stderr);
3188     
3189         if ((tape_fd = raw_tape_open(rawtapedev, O_RDWR)) == -1) {
3190             if (errno == EACCES) {
3191                 errstr = newstralloc(errstr,
3192                                      "updating volume table: tape is write protected");
3193             } else {
3194                 errstr = newstralloc2(errstr,
3195                                       "updating volume table: ", 
3196                                       strerror(errno));
3197             }
3198             rc = 1;
3199             goto common_exit;
3200         }
3201         /* read volume table */
3202         if ((num_volumes = read_vtbl(tape_fd, volumes, vtbl_buffer,
3203                                      &first_seg, &last_seg)) == -1 ) {
3204             errstr = newstralloc2(errstr,
3205                                   "reading volume table: ", 
3206                                   strerror(errno));
3207             rc = 1;
3208             goto common_exit;
3209         }
3210         /* set volume label and date for first entry */
3211         vtbl_no = 0;
3212         if (set_label(label, volumes, num_volumes, vtbl_no)) {
3213             errstr = newstralloc2(errstr,
3214                                   "setting label for entry 1: ",
3215                                   strerror(errno));
3216             rc = 1;
3217             goto common_exit;
3218         }
3219         /* date of start writing this tape */
3220         if (set_date(start_datestr, volumes, num_volumes, vtbl_no)) {
3221             errstr = newstralloc2(errstr,
3222                                   "setting date for entry 1: ", 
3223                                   strerror(errno));
3224             rc = 1;
3225             goto common_exit;
3226         }
3227         /* set volume labels and dates for backup files */
3228         for (vtbl_no = 1; vtbl_no <= num_volumes - 2; vtbl_no++) { 
3229             fprintf(stderr,"taper: label %i: %s, date %s\n", 
3230                     vtbl_no,
3231                     vtbl_entry[vtbl_no].label,
3232                     vtbl_entry[vtbl_no].date);
3233             fflush(stderr);
3234             if (set_label(vtbl_entry[vtbl_no].label, 
3235                          volumes, num_volumes, vtbl_no)) {
3236                 errstr = newstralloc2(errstr,
3237                                       "setting label for entry i: ", 
3238                                       strerror(errno));
3239                 rc = 1;
3240                 goto common_exit;
3241             }
3242             if (set_date(vtbl_entry[vtbl_no].date, 
3243                         volumes, num_volumes, vtbl_no)) {
3244                 errstr = newstralloc2(errstr,
3245                                       "setting date for entry i: ",
3246                                       strerror(errno));
3247                 rc = 1;
3248                 goto common_exit;
3249             }
3250         }
3251         /* set volume label and date for last entry */
3252         vtbl_no = num_volumes - 1;
3253         if (set_label("AMANDA Tape End", volumes, num_volumes, vtbl_no)) {
3254             errstr = newstralloc2(errstr,
3255                                   "setting label for last entry: ", 
3256                                   strerror(errno));
3257             rc = 1;
3258             goto common_exit;
3259         }
3260         datestr = NULL; /* take current time */ 
3261         if (set_date(datestr, volumes, num_volumes, vtbl_no)) {
3262             errstr = newstralloc2(errstr,
3263                                   "setting date for last entry 1: ", 
3264                                   strerror(errno));
3265             rc = 1;
3266             goto common_exit;
3267         }
3268         /* write volume table back */
3269         if (write_vtbl(tape_fd, volumes, vtbl_buffer, num_volumes, first_seg,
3270                        op_mode == trunc)) {
3271             errstr = newstralloc2(errstr,
3272                                   "writing volume table: ", 
3273                                   strerror(errno));
3274             rc = 1;
3275             goto common_exit;
3276         }  
3277
3278         fprintf(stderr, "taper: updating volume table: done.\n");
3279         fflush(stderr);
3280 #endif /* HAVE_LIBVTBLC */
3281     }
3282 #endif /* !HAVE_LINUX_ZFTAPE_H */
3283
3284     /* close the tape and let the OS write the final filemarks */
3285
3286 common_exit:
3287
3288     if (tape_fd >= 0 && tapefd_close(tape_fd) == -1 && ! writerror) {
3289         errstr = newstralloc2(errstr, "closing tape: ", strerror(errno));
3290         rc = 1;
3291     }
3292     tape_fd = -1;
3293     amfree(label);
3294
3295     return rc;
3296 }
3297
3298
3299 int
3300 write_filemark(void)
3301 {
3302     if (tapefd_weof(tape_fd, (off_t)1) == -1) {
3303         errstr = newstralloc2(errstr, "writing filemark: ", strerror(errno));
3304         return 0;
3305     }
3306     total_tape_fm++;
3307     return 1;
3308 }