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