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