2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998, 2000 University of Maryland at College Park
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
26 /* $Id: taper.c,v 1.47.2.14.4.8.2.17.2.4 2005/10/11 11:10:20 martinea Exp $
28 * moves files from holding disk to tape, or from a socket to tape
42 #include "amfeatures.h"
43 #include "fileheader.h"
44 #include "server_util.h"
49 static int vtbl_no = -1;
51 static int offset = 0;
52 static char *datestr = NULL;
53 static char start_datestr[20];
56 struct tm backup_time;
57 struct tm *tape_timep = &tape_time;
58 typedef struct vtbl_lbls {
62 static vtbl_lbls vtbl_entry[MAX_VOLUMES];
63 #endif /* HAVE_LIBVTBLC */
65 * XXX update stat collection/printing
66 * XXX advance to next tape first in next_tape
67 * XXX label is being read twice?
70 /* NBUFS replaced by conf_tapebufs */
71 /* #define NBUFS 20 */
74 /* This is now the number of empties, not full bufs */
77 #define CONNECT_TIMEOUT 2*60
85 typedef struct buffer_s {
91 #define nextbuf(p) ((p) == buftable+conf_tapebufs-1? buftable : (p)+1)
92 #define prevbuf(p) ((p) == buftable? buftable+conf_tapebufs-1 : (p)-1)
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));
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));
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));
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));
120 * ========================================================================
133 char *buffers = NULL;
134 buffer_t *buftable = NULL;
136 char *procname = "parent";
138 char *taper_datestamp = NULL;
143 char *tapedev = NULL;
144 char *tapetype = NULL;
145 tapetype_t *tt = NULL;
147 long tt_blocksize_kb;
150 static unsigned long malloc_hist_1, malloc_size_1;
151 static unsigned long malloc_hist_2, malloc_size_2;
153 am_feature_t *their_features = NULL;
155 int runtapes, cur_tape, have_changer, tapedays;
156 char *labelstr, *conf_tapelist;
159 int first_seg, last_seg;
160 #endif /* HAVE_LIBVTBLC */
163 * ========================================================================
167 int main(main_argc, main_argv)
171 int p2c[2], c2p[2]; /* parent-to-child, child-to-parent pipes */
183 malloc_size_1 = malloc_inuse(&malloc_hist_1);
185 fprintf(stderr, "%s: pid %ld executable %s version %s\n",
186 get_pname(), (long) getpid(), main_argv[0], version());
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);
195 char my_cwd[STR_SIZE];
197 if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) {
198 error("cannot determine current working directory");
200 config_dir = stralloc2(my_cwd, "/");
201 if ((config_name = strrchr(my_cwd, '/')) != NULL) {
202 config_name = stralloc(config_name + 1);
208 /* print prompts and debug messages if running interactive */
210 interactive = (main_argc > 1 && strcmp(main_argv[1],"-t") == 0);
212 erroutput_type = ERR_INTERACTIVE;
214 erroutput_type = ERR_AMANDALOG;
215 set_logerror(logerror);
218 conffile = stralloc2(config_dir, CONFFILE_NAME);
219 if(read_conffile(conffile)) {
220 error("errors processing config file \"%s\"", conffile);
224 conf_tapelist = getconf_str(CNF_TAPELIST);
225 if (*conf_tapelist == '/') {
226 conf_tapelist = stralloc(conf_tapelist);
228 conf_tapelist = stralloc2(config_dir, conf_tapelist);
230 if(read_tapelist(conf_tapelist)) {
231 error("could not load tapelist \"%s\"", conf_tapelist);
234 tapedev = getconf_str(CNF_TAPEDEV);
235 tapetype = getconf_str(CNF_TAPETYPE);
236 tt = lookup_tapetype(tapetype);
238 rawtapedev = getconf_str(CNF_RAWTAPEDEV);
239 #endif /* HAVE_LIBVTBLC */
240 tapedays = getconf_int(CNF_TAPECYCLE);
241 labelstr = getconf_str(CNF_LABELSTR);
243 runtapes = getconf_int(CNF_RUNTAPES);
246 conf_tapebufs = getconf_int(CNF_TAPEBUFS);
248 tt_blocksize_kb = tt->blocksize;
249 tt_blocksize = tt_blocksize_kb * 1024;
250 tt_file_pad = tt->file_pad;
253 fprintf(stderr,"taper: running in interactive test mode\n");
257 /* create read/write syncronization pipes */
259 if(pipe(p2c) || pipe(c2p))
260 error("creating sync pipes: %s", strerror(errno));
262 /* create shared memory segment */
264 #if defined(HAVE_GETPAGESIZE)
265 page_size = getpagesize();
266 fprintf(stderr, "%s: page size is %d\n", get_pname(), page_size);
269 fprintf(stderr, "%s: getpagesize() not available, using %d\n",
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) {
277 size += conf_tapebufs * buffer_size;
278 size += conf_tapebufs * sizeof(buffer_t);
279 if((buffers = attach_buffers(size)) != NULL) {
282 log_add(L_INFO, "attach_buffers: (%d tapebuf%s: %d bytes) %s",
284 (conf_tapebufs == 1) ? "" : "s",
289 if(buffers == NULL) {
290 error("cannot allocate shared memory");
292 i = (buffers - (char *)0) & (page_size - 1); /* page boundary offset */
294 first_buffer = buffers + page_size - i;
295 fprintf(stderr, "%s: shared memory at %p, first buffer at %p\n",
300 first_buffer = buffers;
302 buftable = (buffer_t *)(first_buffer + conf_tapebufs * buffer_size);
303 memset(buftable, 0, conf_tapebufs * sizeof(buffer_t));
304 if(conf_tapebufs < 10) {
306 } else if(conf_tapebufs < 100) {
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",
318 fprintf(stderr, "%s: buffer structures at %p for %d bytes\n",
321 (int)(conf_tapebufs * sizeof(buffer_t)));
323 /* fork off child writer process, parent becomes reader process */
325 switch(writerpid = fork()) {
327 error("fork: %s", strerror(errno));
333 tape_writer_side(p2c[0], c2p[1]);
334 error("tape writer terminated unexpectedly");
336 default: /* parent */
340 file_reader_side(c2p[0], p2c[1]);
341 error("file reader terminated unexpectedly");
350 * ========================================================================
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));
361 void file_reader_side(rdpipe, wrpipe)
365 struct cmdargs cmdargs;
367 char *filename = NULL;
368 char *hostname = NULL;
369 char *diskname = NULL;
371 char *datestamp = NULL;
374 int level, fd, data_port, data_socket, wpid;
375 struct stat stat_file;
380 syncpipe_init(rdpipe, wrpipe);
382 /* must get START_TAPER before beginning */
385 cmd = getcmd(&cmdargs);
386 total_wait = stopclock();
388 if(cmd != START_TAPER || cmdargs.argc != 2) {
389 error("error [file_reader_side cmd %d argc %d]", cmd, cmdargs.argc);
392 /* pass start command on to tape writer */
394 taper_datestamp = newstralloc(taper_datestamp, cmdargs.argv[2]);
398 syncpipe_putstr(taper_datestamp);
400 /* get result of start command */
402 tok = syncpipe_get();
405 putresult(TAPER_OK, "\n");
407 /* start is logged in writer */
410 /* no tape, bail out */
411 result = syncpipe_getstr();
412 q = squotef("[%s]", result ? result : "(null)");
413 putresult(TAPE_ERROR, "%s\n", q);
415 log_add(L_ERROR,"no-tape [%s]", result);
417 syncpipe_put('e'); /* ACK error */
420 error("expected 'S' or 'E' for START-TAPER, got '%c'", tok);
423 /* process further commands */
427 cmd = getcmd(&cmdargs);
428 if(cmd != QUIT && !tape_started) {
429 error("error [file_reader_side cmd %d without tape ready]", cmd);
431 total_wait = timesadd(total_wait, stopclock());
444 cmdargs.argc++; /* true count of args */
447 if(a >= cmdargs.argc) {
448 error("error [taper PORT-WRITE: not enough args: handle]");
450 handle = newstralloc(handle, cmdargs.argv[a++]);
452 if(a >= cmdargs.argc) {
453 error("error [taper PORT-WRITE: not enough args: hostname]");
455 hostname = newstralloc(hostname, cmdargs.argv[a++]);
457 if(a >= cmdargs.argc) {
458 error("error [taper PORT-WRITE: not enough args: features]");
460 am_release_feature_set(their_features);
461 their_features = am_string_to_feature(cmdargs.argv[a++]);
463 if(a >= cmdargs.argc) {
464 error("error [taper PORT-WRITE: not enough args: diskname]");
466 diskname = newstralloc(diskname, cmdargs.argv[a++]);
468 if(a >= cmdargs.argc) {
469 error("error [taper PORT-WRITE: not enough args: level]");
471 level = atoi(cmdargs.argv[a++]);
473 if(a >= cmdargs.argc) {
474 error("error [taper PORT-WRITE: not enough args: datestamp]");
476 datestamp = newstralloc(datestamp, cmdargs.argv[a++]);
478 if(a != cmdargs.argc) {
479 error("error [taper file_reader_side PORT-WRITE: too many args: %d != %d]",
484 data_socket = stream_server(&data_port,
487 if(data_socket < 0) {
490 m = vstralloc("[port create failure: ",
495 putresult(TAPE_ERROR, "%s %s\n", handle, q);
499 putresult(PORT, "%d\n", data_port);
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);
508 read_file(fd, handle, hostname, diskname, datestamp, level, 1);
523 cmdargs.argc++; /* true count of args */
526 if(a >= cmdargs.argc) {
527 error("error [taper FILE-WRITE: not enough args: handle]");
529 handle = newstralloc(handle, cmdargs.argv[a++]);
531 if(a >= cmdargs.argc) {
532 error("error [taper FILE-WRITE: not enough args: filename]");
534 filename = newstralloc(filename, cmdargs.argv[a++]);
536 if(a >= cmdargs.argc) {
537 error("error [taper FILE-WRITE: not enough args: hostname]");
539 hostname = newstralloc(hostname, cmdargs.argv[a++]);
541 if(a >= cmdargs.argc) {
542 error("error [taper FILE-WRITE: not enough args: features]");
544 am_release_feature_set(their_features);
545 their_features = am_string_to_feature(cmdargs.argv[a++]);
547 if(a >= cmdargs.argc) {
548 error("error [taper FILE-WRITE: not enough args: diskname]");
550 diskname = newstralloc(diskname, cmdargs.argv[a++]);
552 if(a >= cmdargs.argc) {
553 error("error [taper FILE-WRITE: not enough args: level]");
555 level = atoi(cmdargs.argv[a++]);
557 if(a >= cmdargs.argc) {
558 error("error [taper FILE-WRITE: not enough args: datestamp]");
560 datestamp = newstralloc(datestamp, cmdargs.argv[a++]);
562 if(a != cmdargs.argc) {
563 error("error [taper file_reader_side FILE-WRITE: too many args: %d != %d]",
567 if(stat(filename,&stat_file)!=0) {
568 q = squotef("[%s]", strerror(errno));
569 putresult(TAPE_ERROR, "%s %s\n", handle, q);
572 if((fd = open(filename, O_RDONLY)) == -1) {
573 q = squotef("[%s]", strerror(errno));
574 putresult(TAPE_ERROR, "%s %s\n", handle, q);
577 read_file(fd, handle, hostname, diskname, datestamp, level, 0);
581 putresult(QUITTING, "\n");
582 fprintf(stderr,"taper: DONE [idle wait: %s secs]\n",
583 walltime_str(total_wait));
585 syncpipe_put('Q'); /* tell writer we're exiting gracefully */
588 if((wpid = wait(NULL)) != writerpid) {
590 "taper: writer wait returned %d instead of %d: %s\n",
591 wpid, writerpid, strerror(errno));
595 detach_buffers(buffers);
600 amfree(changer_resultstr);
605 malloc_size_2 = malloc_inuse(&malloc_hist_2);
607 if(malloc_size_1 != malloc_size_2) {
608 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
614 if(cmdargs.argc >= 1) {
615 q = squote(cmdargs.argv[1]);
616 } else if(cmdargs.argc >= 0) {
617 q = squote(cmdargs.argv[0]);
619 q = stralloc("(no input?)");
621 putresult(BAD_COMMAND, "%s\n", q);
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++);
643 if(i == j) fprintf(stderr, " %d:", i);
644 else fprintf(stderr, " %d-%d:", i, j);
646 case FULL: fputc('F', stderr); break;
647 case FILLING: fputc('f', stderr); break;
648 case EMPTY: fputc('E', stderr); break;
650 fprintf(stderr, "%ld", v);
663 char bt[NUM_STR_SIZE];
664 char status[NUM_STR_SIZE + 1];
669 ap_snprintf(bt, sizeof(bt), "%d", (int)(bp-buftable));
672 case FULL: ap_snprintf(status, sizeof(status), "F%d", bp->size);
674 case FILLING: status[0] = 'f'; status[1] = '\0'; break;
675 case EMPTY: status[0] = 'E'; status[1] = '\0'; break;
677 ap_snprintf(status, sizeof(status), "%ld", bp->status);
681 str = vstralloc("taper: ", pn, ": [buf ", bt, ":=", status, "]", NULL);
687 void read_file(fd, handle, hostname, diskname, datestamp, level, port_flag)
688 int fd, level, port_flag;
689 char *handle, *hostname, *diskname, *datestamp;
693 int rc, err, opening, closing, bufnum, need_closing;
696 char *strclosing = NULL;
705 static char desc[45];
706 static char vol_date[20];
707 static char vol_label[45];
708 #endif /* HAVE_LIBVTBLC */
720 fprintf(stderr, "taper: r: start file\n");
724 for(bp = buftable; bp < buftable + conf_tapebufs; bp++) {
729 if(interactive || bufdebug) dumpstatus(bp);
731 /* tell writer to open tape */
735 syncpipe_putstr(datestamp);
736 syncpipe_putstr(hostname);
737 syncpipe_putstr(diskname);
738 syncpipe_putint(level);
742 /* read file in loop */
745 tok = syncpipe_get();
755 bufnum = syncpipe_getint();
758 fprintf(stderr, "taper: r: got R%d\n", bufnum);
769 if(closing) break; /* ignore extra read tokens */
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",
776 if(bufnum != bp-buftable) {
777 fprintf(stderr, " my buf %d but writer buf %d\n",
778 (int)(bp-buftable), bufnum);
781 fprintf(stderr,"buf %d state %s (%ld) instead of EMPTY\n",
783 bp->status == FILLING? "FILLING" :
784 bp->status == FULL? "FULL" : "EMPTY!?!?",
789 dumpbufs("taper: after 1 sec");
790 if(bp->status == EMPTY)
791 fprintf(stderr, "taper: result now correct!\n");
794 errstr = newstralloc(errstr,
795 "[fatal buffer mismanagement bug]");
797 putresult(TRYAGAIN, "%s %s\n", handle, q);
799 log_add(L_INFO, "retrying %s:%s.%d on new tape due to: %s",
800 hostname, diskname, level, errstr);
802 syncpipe_put('X'); /* X == buffer snafu, bail */
804 tok = syncpipe_get();
806 bufnum = syncpipe_getint();
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) {
818 strclosing = newvstralloc(strclosing,"Can't read data: ",NULL);
821 if(rc < buflen) { /* switch to next file */
823 struct stat stat_file;
827 if(file.cont_filename[0] == '\0') { /* no more file */
830 } else if(stat(file.cont_filename, &stat_file) != 0) {
833 strclosing = newvstralloc(strclosing,"can't stat: ",file.cont_filename,NULL);
834 } else if((fd = open(file.cont_filename,O_RDONLY)) == -1) {
837 strclosing = newvstralloc(strclosing,"can't open: ",file.cont_filename,NULL);
838 } else if((fd != save_fd) && dup2(fd, save_fd) == -1) {
841 strclosing = newvstralloc(strclosing,"can't dup2: ",file.cont_filename,NULL);
847 bp1.size = DISK_BLOCK_BYTES;
848 bp1.buffer = malloc(DISK_BLOCK_BYTES);
855 rc1 = taper_fill_buffer(fd, &bp1, DISK_BLOCK_BYTES);
858 err = (rc1 < 0) ? errno : 0;
860 strclosing = newvstralloc(strclosing,
861 "Can't read header: ",
865 parse_file_header(bp1.buffer, &file, rc1);
868 bp1.buffer = bp->buffer + rc;
870 rc1 = taper_fill_buffer(fd, &bp1, tt_blocksize - rc);
872 err = (rc1 < 0) ? errno : 0;
875 strclosing = newvstralloc(strclosing,
890 if(header_read == 0) {
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);
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 */
905 amfree(cont_filename);
908 filesize += am_round(rc, 1024) / 1024;
910 if(interactive || bufdebug) dumpstatus(bp);
912 fprintf(stderr,"taper: r: put W%d\n",(int)(bp-buftable));
916 syncpipe_putint(bp-buftable);
919 if(need_closing && rc <= 0) {
929 syncpipe_put('e'); /* ACK error */
932 str = syncpipe_getstr();
933 errstr = newvstralloc(errstr, "[", str ? str : "(null)", "]", NULL);
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);
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);
954 str = syncpipe_getstr();
955 label = newstralloc(label, str ? str : "(null)");
957 str = syncpipe_getstr();
958 filenum = atoi(str ? str : "-9876"); /* ??? */
960 fprintf(stderr, "taper: reader-side: got label %s filenum %d\n",
965 runtime = stopclock();
968 errstr = newvstralloc(errstr,
969 "[input: ", strclosing, ": ",
970 strerror(err), "]", NULL);
974 errstr = newvstralloc(errstr,
975 "[input: ", strerror(err), "]",
978 putresult(TAPE_ERROR, "%s %s\n", handle, q);
980 log_add(L_FAIL, "%s %s %s %d %s",
981 hostname, diskname, datestamp, level, errstr);
982 str = syncpipe_getstr(); /* reap stats */
985 char kb_str[NUM_STR_SIZE];
986 char kps_str[NUM_STR_SIZE];
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),
998 " ", str ? str : "(null)",
1003 putresult(DONE, "%s %s %d %s\n",
1004 handle, label, filenum, q);
1006 log_add(L_SUCCESS, "%s %s %s %d %s",
1007 hostname, diskname, datestamp, level, errstr);
1008 #ifdef HAVE_LIBVTBLC
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)
1016 memset(desc, '\0', 45);
1018 strncpy(desc, hostname, 20);
1020 if ((len = strlen(hostname)) <= 20) {
1021 memset(desc + len, ' ', 1);
1025 memset(desc + 20, ' ', 1);
1029 strncpy(desc + offset, diskname, 20);
1031 if ((len = strlen(diskname)) <= 20) {
1032 memset(desc + offset + len, ' ', 1);
1033 offset = offset + len + 1;
1036 memset(desc + offset + 20, ' ', 1);
1037 offset = offset + 21;
1040 sprintf(desc + offset, "%i", level);
1042 strncpy(vol_label, desc, 44);
1043 fprintf(stderr, "taper: added vtbl label string %i: \"%s\"\n",
1044 filenum, vol_label);
1047 /* pass label string on to tape writer */
1049 syncpipe_putint(filenum);
1050 syncpipe_putstr(vol_label);
1053 * reformat datestamp for later use with set_date from vtblc
1055 strptime(datestamp, "%Y%m%d", &backup_time);
1056 strftime(vol_date, 20, "%T %D", &backup_time);
1058 "taper: reformatted vtbl date string: \"%s\"->\"%s\"\n",
1062 /* pass date string on to tape writer */
1064 syncpipe_putint(filenum);
1065 syncpipe_putstr(vol_date);
1067 #endif /* HAVE_LIBVTBLC */
1077 int taper_fill_buffer(fd, bp, buflen)
1085 curptr = bp->buffer;
1090 cnt = read(fd, curptr, spaceleft);
1093 if(interactive) fputs("r0", stderr);
1095 case -1: /* error on read, punt */
1096 if(interactive) fputs("rE", stderr);
1104 } while(spaceleft > 0);
1106 if(interactive) fputs("R", stderr);
1113 * ========================================================================
1117 times_t idlewait, rdwait, wrwait, fmwait;
1119 double total_tape_used;
1122 void write_file P((void));
1123 int write_buffer P((buffer_t *bp));
1125 void tape_writer_side(getp, putp)
1136 #ifdef HAVE_LIBVTBLC
1139 #endif /* HAVE_LIBVTBLC */
1141 procname = "writer";
1142 syncpipe_init(getp, putp);
1145 idlewait = times_zero;
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);
1156 case 'S': /* start-tape */
1158 error("writer: multiple start requests");
1160 str = syncpipe_getstr();
1161 if(!first_tape(str ? str : "bad-datestamp")) {
1163 tapefd_close(tape_fd);
1167 syncpipe_putstr(errstr);
1168 /* wait for reader to acknowledge error */
1170 tok = syncpipe_get();
1172 error("writer: got '%c' unexpectedly after error", tok);
1174 } while(tok != 'e');
1183 case 'O': /* open-output */
1184 datestamp = syncpipe_getstr();
1185 tapefd_setinfo_datestamp(tape_fd, datestamp);
1187 hostname = syncpipe_getstr();
1188 tapefd_setinfo_host(tape_fd, hostname);
1190 diskname = syncpipe_getstr();
1191 tapefd_setinfo_disk(tape_fd, diskname);
1193 level = syncpipe_getint();
1194 tapefd_setinfo_level(tape_fd, level);
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",
1204 strncpy(vtbl_entry[vtbl_no].label, vol_label, 45);
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",
1212 strncpy(vtbl_entry[vtbl_no].date, vol_date, 20);
1214 #endif /* HAVE_LIBVTBLC */
1217 end_tape(0); /* XXX check results of end tape ?? */
1219 detach_buffers(buffers);
1220 amfree(taper_datestamp);
1223 amfree(changer_resultstr);
1226 amfree(config_name);
1228 malloc_size_2 = malloc_inuse(&malloc_hist_2);
1230 if(malloc_size_1 != malloc_size_2) {
1231 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
1245 int full_buffers, i, bufnum;
1247 char number[NUM_STR_SIZE];
1248 char *rdwait_str, *wrwait_str, *fmwait_str;
1250 rdwait = wrwait = times_zero;
1258 fprintf(stderr, "taper: w: start file\n");
1263 * Tell the reader that the tape is open, and give it all the buffers.
1266 for(i = 0; i < conf_tapebufs; i++) {
1268 fprintf(stderr, "taper: w: put R%d\n", i);
1271 syncpipe_put('R'); syncpipe_putint(i);
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.
1281 if(!write_filemark())
1283 fmwait = stopclock();
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.
1299 if(interactive) fputs("[WS]", stderr);
1301 while(full_buffers < conf_tapebufs - THRESHOLD) {
1302 tok = syncpipe_get();
1303 if(tok != 'W') break;
1304 bufnum = syncpipe_getint();
1306 fprintf(stderr,"taper: w: got W%d\n",bufnum);
1311 rdwait = timesadd(rdwait, stopclock());
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.
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;
1327 if(!write_buffer(bp)) goto tape_error;
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
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.
1345 while(tok == 'W' && bp->status == FULL) {
1346 tok = syncpipe_get();
1348 bufnum = syncpipe_getint();
1350 fprintf(stderr,"taper: w: got W%d\n",bufnum);
1353 if(bufnum != bp-buftable) {
1355 "taper: tape-writer: my buf %d reader buf %d\n",
1356 (int)(bp-buftable), bufnum);
1359 syncpipe_putstr("writer-side buffer mismatch");
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;
1366 if(!write_buffer(bp)) goto tape_error;
1372 goto reader_buffer_snafu;
1374 error("writer-side not expecting token: %c", tok);
1376 } while(tok == 'W');
1378 /* got close signal from reader, acknowledge it */
1381 goto reader_buffer_snafu;
1386 /* tell reader the tape and file number */
1388 syncpipe_putstr(label);
1389 ap_snprintf(number, sizeof(number), "%d", filenum);
1390 syncpipe_putstr(number);
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,
1398 " writers ", number,
1399 " rdwait ", rdwait_str,
1400 " wrwait ", wrwait_str,
1401 " filemark ", fmwait_str,
1407 syncpipe_putstr(errstr);
1409 /* XXX go to next tape if past tape size? */
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);
1420 /* wait for reader to acknowledge error */
1422 tok = syncpipe_get();
1423 if(tok != 'W' && tok != 'C' && tok != 'e')
1424 error("writer: got '%c' unexpectedly after error", tok);
1426 syncpipe_getint(); /* eat buffer number */
1427 } while(tok != 'e');
1430 reader_buffer_snafu:
1435 int write_buffer(bp)
1440 if(bp->status != FULL) {
1441 /* XXX buffer management snafu */
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;
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
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);
1462 wrwait = timesadd(wrwait, stopclock());
1464 total_tape_used += (double)rc;
1466 if(interactive || bufdebug) dumpstatus(bp);
1467 if(interactive) fputs("W", stderr);
1470 fprintf(stderr, "taper: w: put R%d\n", (int)(bp-buftable));
1473 syncpipe_put('R'); syncpipe_putint(bp-buftable);
1476 errstr = newvstralloc(errstr,
1478 (rc != -1) ? "short write" : strerror(errno),
1480 wrwait = timesadd(wrwait, stopclock());
1481 if(interactive) fputs("[WE]", stderr);
1488 * ========================================================================
1489 * SHARED-MEMORY BUFFER SUBSYSTEM
1497 char *attach_buffers(size)
1502 shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0700);
1507 result = (char *)shmat(shmid, (SHM_ARG_TYPE *)NULL, 0);
1509 if(result == (char *)-1) {
1510 int save_errno = errno;
1514 error("shmat: %s", strerror(errno));
1521 void detach_buffers(bufp)
1524 if(shmdt((SHM_ARG_TYPE *)bufp) == -1) {
1525 error("shmdt: %s", strerror(errno));
1529 void destroy_buffers()
1531 if(shmid == -1) return; /* nothing to destroy */
1532 if(shmctl(shmid, IPC_RMID, NULL) == -1) {
1533 error("shmctl: %s", strerror(errno));
1540 #ifdef HAVE_SYS_MMAN_H
1541 #include <sys/mman.h>
1545 # ifdef MAP_ANONYMOUS /* OSF/1-style */
1546 # define MAP_ANON MAP_ANONYMOUS
1547 # else /* SunOS4-style */
1549 # define ZERO_FILE "/dev/zero"
1554 unsigned int saved_size;
1556 char *attach_buffers(size)
1562 shmfd = open(ZERO_FILE, O_RDWR);
1564 error("attach_buffers: could not open %s: %s",
1571 shmbuf = (char *) mmap((void *) 0,
1573 PROT_READ|PROT_WRITE,
1574 MAP_ANON|MAP_SHARED,
1580 void detach_buffers(bufp)
1583 if(munmap((void *)bufp, saved_size) == -1) {
1584 error("detach_buffers: munmap: %s", strerror(errno));
1590 void destroy_buffers()
1595 error: must define either HAVE_SYSVSHM or HAVE_MMAP!
1602 * ========================================================================
1603 * SYNC-PIPE SUBSYSTEM
1607 int getpipe, putpipe;
1609 void syncpipe_init(rd, wr)
1621 rc = read(getpipe, buf, sizeof(buf));
1622 if(rc == 0) /* EOF */
1623 error("syncpipe_get: %c: unexpected EOF", *procname);
1625 error("syncpipe_get: %c: %s", *procname, strerror(errno));
1627 if(bufdebug && *buf != 'R' && *buf != 'W') {
1628 fprintf(stderr,"taper: %c: getc %c\n",*procname,*buf);
1635 int syncpipe_getint()
1639 int len = sizeof(i);
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");
1653 char *syncpipe_getstr()
1660 if((len = syncpipe_getint()) <= 0) {
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");
1677 void syncpipe_put(chi)
1684 if(bufdebug && chi != 'R' && chi != 'W') {
1685 fprintf(stderr,"taper: %c: putc %c\n",*procname,chi);
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));
1696 void syncpipe_putint(i)
1700 char *item = (char *)&i;
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));
1709 void syncpipe_putstr(item)
1714 n = strlen(item)+1; /* send '\0' as well */
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));
1725 * ========================================================================
1726 * TAPE MANIPULATION SUBSYSTEM
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));
1738 char *conf_tapelist_old = NULL;
1739 char *olddatestamp = NULL;
1742 static int first_call = 1;
1746 if ((tapedev = taper_scan()) == NULL) {
1747 errstr = newstralloc(errstr, changer_resultstr);
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"
1760 if((result = tapefd_rdlabel(tape_fd, &olddatestamp, &label)) != NULL) {
1761 amfree(olddatestamp);
1762 errstr = newstralloc(errstr, result);
1765 if(tapefd_rewind(tape_fd) == -1) {
1768 tapefd_close(tape_fd);
1772 #endif /* !HAVE_LINUX_ZFTAPE_H */
1773 if((result = tape_rdlabel(tapedev, &olddatestamp, &label)) != NULL) {
1774 amfree(olddatestamp);
1775 errstr = newstralloc(errstr, result);
1779 fprintf(stderr, "taper: read label `%s' date `%s'\n", label, olddatestamp);
1781 amfree(olddatestamp);
1783 /* check against tape list */
1784 if (strcmp(label, FAKE_LABEL) != 0) {
1785 tp = lookup_tapelabel(label);
1787 errstr = newvstralloc(errstr,
1789 " match labelstr but it not listed in the tapelist file",
1793 else if(tp != NULL && !reusable_tape(tp)) {
1794 errstr = newvstralloc(errstr,
1795 "cannot overwrite active tape ", label,
1800 if(!match(labelstr, label)) {
1801 errstr = newvstralloc(errstr,
1803 " doesn\'t match labelstr \"", labelstr, "\"",
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");
1814 errstr = newstralloc2(errstr,
1815 "writing label: ", strerror(errno));
1820 tapefd_setinfo_length(tape_fd, tt->length);
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);
1830 fprintf(stderr, "taper: wrote label `%s' date `%s'\n", label, taper_datestamp);
1833 #ifdef HAVE_LIBVTBLC
1834 /* store time for the first volume entry */
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);
1840 #endif /* HAVE_LIBVTBLC */
1842 /* write tape list */
1844 /* XXX add cur_tape number to tape list structure */
1845 if (strcmp(label, FAKE_LABEL) != 0) {
1848 conf_tapelist_old = stralloc2(conf_tapelist, ".yesterday");
1850 char cur_str[NUM_STR_SIZE];
1852 ap_snprintf(cur_str, sizeof(cur_str), "%d", cur_tape - 1);
1853 conf_tapelist_old = vstralloc(conf_tapelist,
1854 ".today.", cur_str, NULL);
1856 if(write_tapelist(conf_tapelist_old)) {
1857 error("could not write tapelist: %s", strerror(errno));
1859 amfree(conf_tapelist_old);
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));
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) {
1872 log_add(L_WARNING, "tapedev is %s, dumps will be thrown away", tapedev);
1875 total_tape_used=0.0;
1881 int first_tape(new_datestamp)
1882 char *new_datestamp;
1884 if((have_changer = changer_init()) < 0) {
1885 error("changer initialization failed: %s", strerror(errno));
1889 taper_datestamp = newstralloc(taper_datestamp, new_datestamp);
1898 int next_tape(writerror)
1901 end_tape(writerror);
1903 if(++cur_tape >= runtapes)
1915 int end_tape(writerror)
1922 log_add(L_INFO, "tape %s kb %ld fm %d %s",
1924 (long) ((total_tape_used+1023.0) / 1024.0),
1926 writerror? errstr : "[OK]");
1928 fprintf(stderr, "taper: writing end marker. [%s %s kb %ld fm %d]\n",
1930 writerror? "ERR" : "OK",
1931 (long) ((total_tape_used+1023.0) / 1024.0),
1935 if(! write_filemark()) {
1940 result = tapefd_wrendmark(tape_fd, taper_datestamp, tt_blocksize);
1941 if(result != NULL) {
1942 errstr = newstralloc(errstr, result);
1949 #ifdef HAVE_LINUX_ZFTAPE_H
1950 if (tape_fd >= 0 && is_zftape(tapedev) == 1) {
1951 /* rewind the tape */
1953 if(tapefd_rewind(tape_fd) == -1 ) {
1954 errstr = newstralloc2(errstr, "rewinding tape: ", strerror(errno));
1958 /* close the tape */
1960 if(tapefd_close(tape_fd) == -1) {
1961 errstr = newstralloc2(errstr, "closing tape: ", strerror(errno));
1967 #ifdef HAVE_LIBVTBLC
1968 /* update volume table */
1969 fprintf(stderr, "taper: updating volume table ...\n");
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");
1977 errstr = newstralloc2(errstr,
1978 "updating volume table: ",
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: ",
1993 /* set volume label and date for first entry */
1995 if(set_label(label, volumes, num_volumes, vtbl_no)){
1996 errstr = newstralloc2(errstr,
1997 "setting label for entry 1: ",
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: ",
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",
2014 vtbl_entry[vtbl_no].label,
2015 vtbl_entry[vtbl_no].date);
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: ",
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: ",
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: ",
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: ",
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: ",
2061 fprintf(stderr, "taper: updating volume table: done.\n");
2063 #endif /* HAVE_LIBVTBLC */
2065 #endif /* !HAVE_LINUX_ZFTAPE_H */
2067 /* close the tape and let the OS write the final filemarks */
2071 if(tape_fd >= 0 && tapefd_close(tape_fd) == -1 && ! writerror) {
2072 errstr = newstralloc2(errstr, "closing tape: ", strerror(errno));
2082 int write_filemark()
2084 if(tapefd_weof(tape_fd, 1) == -1) {
2085 errstr = newstralloc2(errstr, "writing filemark: ", strerror(errno));
2094 * ========================================================================
2098 int nslots, backwards, found, got_match, tapedays;
2099 char *first_match_label = NULL, *first_match = NULL, *found_device = NULL;
2100 char *searchlabel, *labelstr;
2103 int scan_init(rc, ns, bk)
2107 fprintf(stderr, "%s: could not get changer info: %s\n",
2108 get_pname(), changer_resultstr);
2118 int taperscan_slot(rc, slotstr, device)
2124 char *scan_datestamp = NULL;
2127 fprintf(stderr, "%s: fatal slot %s: %s\n",
2128 get_pname(), slotstr, changer_resultstr);
2133 fprintf(stderr, "%s: slot %s: %s\n", get_pname(),
2134 slotstr, changer_resultstr);
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);
2146 /* got an amanda tape */
2147 fprintf(stderr, "%s: slot %s: date %-8s label %s",
2148 get_pname(), slotstr, scan_datestamp, label);
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");
2157 found_device = newstralloc(found_device, device);
2161 else if(!match(labelstr, label)) {
2162 fprintf(stderr, " (no match)\n");
2166 /* not an exact label match, but a labelstr match */
2167 /* check against tape list */
2168 tp = lookup_tapelabel(label);
2170 fprintf(stderr, "(not in tapelist)\n");
2173 else if(!reusable_tape(tp)) {
2174 fprintf(stderr, " (active tape)\n");
2177 else if(got_match == 0 && tp->datestamp == 0) {
2179 first_match = newstralloc(first_match, slotstr);
2180 first_match_label = newstralloc(first_match_label, label);
2181 fprintf(stderr, " (new tape)\n");
2184 found_device = newstralloc(found_device, device);
2187 else if(got_match) {
2188 fprintf(stderr, " (labelstr match)\n");
2193 first_match = newstralloc(first_match, slotstr);
2194 first_match_label = newstralloc(first_match_label, label);
2195 fprintf(stderr, " (first labelstr match)\n");
2197 if(!backwards || !searchlabel) {
2199 found_device = newstralloc(found_device, device);
2211 char *outslot = NULL;
2213 if((tp = lookup_last_reusable_tape(0)) == NULL)
2216 searchlabel = tp->label;
2221 if (searchlabel != NULL)
2222 changer_find(scan_init, taperscan_slot, searchlabel);
2224 changer_scan(scan_init, taperscan_slot);
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) {
2238 changer_resultstr = newvstralloc(changer_resultstr,
2239 "label ", searchlabel,
2240 " or new tape not found in rack",
2243 changer_resultstr = newstralloc(changer_resultstr,
2244 "new tape not found in rack");
2249 outslot = found_device;
2250 found_device = NULL; /* forget about our copy */
2253 amfree(found_device);