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