From: Kern Sibbald Date: Wed, 9 Oct 2002 13:27:23 +0000 (+0000) Subject: btape updates + documentation -- see kes09Oct02 X-Git-Tag: Release-1.26~3 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=c71cea19ab440d3a10815042a38cc9121c6061f2;p=bacula%2Fbacula btape updates + documentation -- see kes09Oct02 git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@168 91ce42f0-d328-0410-95d8-f526ca767f89 --- diff --git a/bacula/ChangeLog b/bacula/ChangeLog index 638ccc19bd..3840e1136b 100644 --- a/bacula/ChangeLog +++ b/bacula/ChangeLog @@ -1,5 +1,27 @@ -2002-xx-xx Version 1.26 (04Oct02) +2002-xx-xx Version 1.26 (08Oct02) +General: from kes09Oct02 +- More documentation. +- Implemented new fill command in btape that permits filling + a tape and then reading it back to ensure that it works + with Bacula. + +Changes submitted this submission: +- Added ReadBytes to JCR, which contains Job bytes read by + FD, JobBytes contains compressed output of FD. +- Modified FD to pass back JobStatus, ReadBytes, JobBytes, ... + for backup jobs. This is upward compatible. +- Modified backup termination status report to contain the + compression ratio 100 * (1 - compressed-bytes/uncompressed-bytes) + This will always be zero if no software compression was + done, or if you are using a version 1.25 or older FD. +- Pickup Job termination status of FD in backup, so now + the termination status represents the state of all three + daemons. +- Implemented new fill command in btape that permits filling + a tape and then reading it back to ensure that it works + with Bacula. + General: From kes06Oct02 - Implemented first major cut of bscan -- program to scan a tape and recreate a Bacula catalog. diff --git a/bacula/kernstodo b/bacula/kernstodo index 602bb7201a..96ebaa9e75 100644 --- a/bacula/kernstodo +++ b/bacula/kernstodo @@ -28,8 +28,6 @@ From Chuck: ======= - Figure out why my Catalog size keeps growing. -- Document bscan. -- Document Restore. - Make SD disallow writing on Volume with fewer files than in the catalog. @@ -554,3 +552,6 @@ Longer term to do: Done: (see kernsdone for more) +- Document bscan. +- Document Restore. + diff --git a/bacula/src/dird/backup.c b/bacula/src/dird/backup.c index 09917fbe72..decff941a9 100644 --- a/bacula/src/dird/backup.c +++ b/bacula/src/dird/backup.c @@ -47,9 +47,11 @@ static char storaddr[] = "storage address=%s port=%d\n"; static char levelcmd[] = "level = %s%s\n"; /* Responses received from File daemon */ -static char OKbackup[] = "2000 OK backup\n"; -static char OKstore[] = "2000 OK storage\n"; -static char OKlevel[] = "2000 OK level\n"; +static char OKbackup[] = "2000 OK backup\n"; +static char OKstore[] = "2000 OK storage\n"; +static char OKlevel[] = "2000 OK level\n"; +static char EndBackup[] = "2801 End Backup Job TermCode=%d JobFiles=%u ReadBytes=%" lld " JobBytes=%" lld "\n"; + /* Forward referenced functions */ static void backup_cleanup(JCR *jcr, int TermCode, char *since); @@ -257,19 +259,23 @@ bail_out: } /* - * NOTE! This is no longer really needed as the Storage - * daemon now passes this information directly - * back to us. + * Here we wait for the File daemon to signal termination, + * then we wait for the Storage daemon. When both + * are done, we return the job status. */ static int wait_for_job_termination(JCR *jcr) { int32_t n = 0; BSOCK *fd = jcr->file_bsock; + int fd_ok = FALSE; jcr->JobStatus = JS_WaitFD; /* Wait for Client to terminate */ while ((n = bget_msg(fd, 0)) > 0 && !job_cancelled(jcr)) { - /* get and discard Client output */ + if (sscanf(fd->msg, EndBackup, &jcr->JobStatus, &jcr->JobFiles, + &jcr->ReadBytes, &jcr->JobBytes) == 4) { + fd_ok = TRUE; + } } bnet_sig(fd, BNET_TERMINATE); /* tell Client we are terminating */ if (n < 0) { @@ -279,7 +285,11 @@ static int wait_for_job_termination(JCR *jcr) wait_for_storage_daemon_termination(jcr); - if (n < 0) { + /* Return the first error status we find FD or SD */ + if (fd_ok && jcr->JobStatus != JS_Terminated) { + return jcr->JobStatus; + } + if (!fd_ok || n < 0) { return JS_ErrorTerminated; } return jcr->SDJobStatus; @@ -296,7 +306,7 @@ static void backup_cleanup(JCR *jcr, int TermCode, char *since) char *term_msg; int msg_type; MEDIA_DBR mr; - double kbps; + double kbps, compression; btime_t RunTime; Dmsg0(100, "Enter backup_cleanup()\n"); @@ -354,6 +364,12 @@ static void backup_cleanup(JCR *jcr, int TermCode, char *since) jcr->VolumeName[0] = 0; /* none */ } + if (jcr->ReadBytes == 0) { + compression = 0.0; + } else { + compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes); + } + Jmsg(jcr, msg_type, 0, _("%s\n\ JobId: %d\n\ Job: %s\n\ @@ -365,6 +381,7 @@ End time: %s\n\ Files Written: %s\n\ Bytes Written: %s\n\ Rate: %.1f KB/s\n\ +Software Compression: %.1f %%\n\ Volume names(s): %s\n\ Volume Session Id: %d\n\ Volume Session Time: %d\n\ @@ -381,6 +398,7 @@ Termination: %s\n\n"), edit_uint64_with_commas(jcr->jr.JobFiles, ec1), edit_uint64_with_commas(jcr->jr.JobBytes, ec2), (float)kbps, + (float)compression, jcr->VolumeName, jcr->VolSessionId, jcr->VolSessionTime, diff --git a/bacula/src/dird/msgchan.c b/bacula/src/dird/msgchan.c index 54303a5d20..70176ff7fe 100644 --- a/bacula/src/dird/msgchan.c +++ b/bacula/src/dird/msgchan.c @@ -256,4 +256,5 @@ void wait_for_storage_daemon_termination(JCR *jcr) pthread_cond_timedwait(&jcr->term_wait, &jcr->mutex, &timeout); } V(jcr->mutex); + jcr->JobStatus = jcr->SDJobStatus; } diff --git a/bacula/src/dird/restore.c b/bacula/src/dird/restore.c index 8414843c52..974bde208e 100644 --- a/bacula/src/dird/restore.c +++ b/bacula/src/dird/restore.c @@ -53,7 +53,7 @@ static char OKrestore[] = "2000 OK restore\n"; static char OKstore[] = "2000 OK storage\n"; static char OKsession[] = "2000 OK session\n"; static char OKbootstrap[] = "2000 OK bootstrap\n"; -static char EndJob[] = "2800 End Job TermCode=%d JobFiles=%u JobBytes=%" lld "\n"; +static char EndRestore[] = "2800 End Job TermCode=%d JobFiles=%u JobBytes=%" lld "\n"; /* Forward referenced functions */ static void restore_cleanup(JCR *jcr, int status); @@ -244,7 +244,7 @@ int do_restore(JCR *jcr) Dmsg0(20, "wait for job termination\n"); while (bget_msg(fd, 0) > 0) { Dmsg1(100, "dirdmsg); - if (sscanf(fd->msg, EndJob, &jcr->JobStatus, &jcr->JobFiles, + if (sscanf(fd->msg, EndRestore, &jcr->JobStatus, &jcr->JobFiles, &jcr->JobBytes) == 3) { ok = TRUE; } diff --git a/bacula/src/filed/backup.c b/bacula/src/filed/backup.c index 5c1667cce6..b91a6911f9 100644 --- a/bacula/src/filed/backup.c +++ b/bacula/src/filed/backup.c @@ -264,6 +264,7 @@ static int save_file(FF_PKT *ff_pkt, void *ijcr) msgsave = sd->msg; while ((sd->msglen=read(fid, sd->msg, jcr->buf_size)) > 0) { + jcr->ReadBytes += sd->msglen; /* count bytes read */ if (ff_pkt->flags & FO_MD5) { MD5Update(&md5c, (unsigned char *)(sd->msg), sd->msglen); gotMD5 = 1; @@ -296,7 +297,7 @@ static int save_file(FF_PKT *ff_pkt, void *ijcr) } Dmsg1(130, "Send data to FD len=%d\n", sd->msglen); #endif - jcr->JobBytes += sd->msglen; + jcr->JobBytes += sd->msglen; /* count compressed bytes saved */ sd->msg = msgsave; /* restore read buffer */ continue; } @@ -308,7 +309,7 @@ static int save_file(FF_PKT *ff_pkt, void *ijcr) } Dmsg1(130, "Send data to FD len=%d\n", sd->msglen); #endif - jcr->JobBytes += sd->msglen; + jcr->JobBytes += sd->msglen; /* count bytes saved */ } /* end while */ if (sd->msglen < 0) { diff --git a/bacula/src/filed/job.c b/bacula/src/filed/job.c index be261e2061..cfd78f82bd 100644 --- a/bacula/src/filed/job.c +++ b/bacula/src/filed/job.c @@ -108,7 +108,8 @@ static char OKstore[] = "2000 OK storage\n"; static char OKjob[] = "2000 OK Job\n"; static char OKsetdebug[] = "2000 OK setdebug=%d\n"; static char BADjob[] = "2901 Bad Job\n"; -static char EndJob[] = "2800 End Job TermCode=%d JobFiles=%u JobBytes=%" lld "\n"; +static char EndRestore[] = "2800 End Job TermCode=%d JobFiles=%u JobBytes=%" lld "\n"; +static char EndBackup[] = "2801 End Backup Job TermCode=%d JobFiles=%u ReadBytes=%" lld " JobBytes=%" lld "\n"; /* Responses received from Storage Daemon */ static char OK_end[] = "3000 OK end\n"; @@ -582,6 +583,8 @@ cleanup: bnet_sig(sd, BNET_TERMINATE); } + bnet_fsend(dir, EndBackup, jcr->JobStatus, jcr->JobFiles, jcr->ReadBytes, jcr->JobBytes); + /* Inform Director that we are done */ bnet_sig(dir, BNET_TERMINATE); @@ -699,7 +702,7 @@ static int restore_cmd(JCR *jcr) /* Inform Storage daemon that we are done */ bnet_sig(sd, BNET_TERMINATE); - bnet_fsend(dir, EndJob, jcr->JobStatus, jcr->num_files_examined, jcr->JobBytes); + bnet_fsend(dir, EndRestore, jcr->JobStatus, jcr->num_files_examined, jcr->JobBytes); /* Inform Director that we are done */ bnet_sig(dir, BNET_TERMINATE); diff --git a/bacula/src/jcr.h b/bacula/src/jcr.h index fd5c472363..9533105b8a 100644 --- a/bacula/src/jcr.h +++ b/bacula/src/jcr.h @@ -94,6 +94,7 @@ struct s_jcr { uint32_t JobFiles; /* Number of files written, this job */ uint32_t JobErrors; /* */ uint64_t JobBytes; /* Number of bytes processed this job */ + uint64_t ReadBytes; /* Bytes read -- before compression */ uint32_t Errors; /* Number of non-fatal errors */ int JobStatus; /* ready, running, blocked, terminated */ int JobType; /* backup, restore, verify ... */ diff --git a/bacula/src/stored/Makefile.in b/bacula/src/stored/Makefile.in index 56b285d7df..0b7a996eb3 100644 --- a/bacula/src/stored/Makefile.in +++ b/bacula/src/stored/Makefile.in @@ -17,7 +17,7 @@ DEBUG=@DEBUG@ first_rule: all dummy: -# +# bacula-sd SVRSRCS = stored.c acquire.c append.c askdir.c authenticate.c \ block.c dev.c \ device.c dircmd.c fd_cmds.c fdmsg.c job.c \ @@ -29,31 +29,39 @@ SVROBJS = stored.o acquire.o append.o askdir.o authenticate.o \ label.o match_bsr.o mount.o parse_bsr.o \ read.o record.o stored_conf.o -# bpool is deprecated -#POOLSRCS = bpool.c block.c dev.c device.c askdir.c label.c \ -# record.c stored_conf.c -#POOLOBJS = bpool.o block.o dev.o device.o askdir.o label.o \ -# record.o stored_conf.o - -# -TAPESRCS = btape.c block.c dev.c device.c askdir.c label.c \ - acquire.c mount.c record.c stored_conf.c -TAPEOBJS = btape.o block.o dev.o device.o askdir.o label.o \ - acquire.o mount.o record.o stored_conf.o +# btape +TAPESRCS = btape.c block.c butil.c dev.c device.c label.c \ + acquire.c mount.c record.c read_record.c \ + stored_conf.c match_bsr.c parse_bsr.o +TAPEOBJS = btape.o block.o butil.o dev.o device.o label.o \ + acquire.o mount.o record.o read_record.o \ + stored_conf.o match_bsr.o parse_bsr.o +# bls BLSOBJS = bls.o block.o device.o dev.o label.o match_bsr.o \ acquire.o mount.o parse_bsr.o record.o butil.o \ read_record.o +# bextract BEXTOBJS = bextract.o block.o device.o dev.o label.o record.o \ acquire.o mount.o match_bsr.o parse_bsr.o butil.o \ read_record.o +# bscan SCNOBJS = bscan.o block.o device.o dev.o label.o \ acquire.o mount.o record.o match_bsr.o parse_bsr.o \ butil.o read_record.o +# bpool is deprecated +#POOLSRCS = bpool.c block.c dev.c device.c askdir.c label.c \ +# record.c stored_conf.c +#POOLOBJS = bpool.o block.o dev.o device.o askdir.o label.o \ +# record.o stored_conf.o + + + + # these are the objects that are changed by the .configure process EXTRAOBJS = @OBJLIST@ diff --git a/bacula/src/stored/bextract.c b/bacula/src/stored/bextract.c index 00dd57a987..5bab15516b 100644 --- a/bacula/src/stored/bextract.c +++ b/bacula/src/stored/bextract.c @@ -82,6 +82,7 @@ int main (int argc, char *argv[]) char line[1000]; int got_inc = FALSE; + working_directory = "/tmp"; my_name_is(argc, argv, "bextract"); init_msg(NULL, NULL); /* setup message handler */ diff --git a/bacula/src/stored/bls.c b/bacula/src/stored/bls.c index 61da289ae2..3cac57d2f7 100644 --- a/bacula/src/stored/bls.c +++ b/bacula/src/stored/bls.c @@ -78,6 +78,7 @@ int main (int argc, char *argv[]) FILE *fd; char line[1000]; + working_directory = "/tmp"; my_name_is(argc, argv, "bls"); init_msg(NULL, NULL); /* initialize message handler */ @@ -392,7 +393,7 @@ int dir_send_job_status(JCR *jcr) {return 1;} int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev) { - fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ", + fprintf(stderr, "Mount Volume \"%s\" on device %s and press return when ready: ", jcr->VolumeName, dev_name(dev)); getchar(); return 1; diff --git a/bacula/src/stored/btape.c b/bacula/src/stored/btape.c index 91e624bc23..8c0df2384d 100644 --- a/bacula/src/stored/btape.c +++ b/bacula/src/stored/btape.c @@ -59,7 +59,11 @@ static void clearcmd(); static void wrcmd(); static void eodcmd(); static int find_device_res(); - +static void fillcmd(); +static void unfillcmd(); +static int flush_block(DEV_BLOCK *block); +static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec); +static int my_mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); #define CONFIG_FILE "stored.conf" @@ -67,8 +71,20 @@ static char *configfile; static char cmd[1000]; static int signals = TRUE; static int default_tape = FALSE; +static int ok; +static int stop; +static uint64_t vol_size; +static uint64_t VolBytes; +static time_t now; +static double kbs; +static long file_index; +static int verbose = 0; +static int end_of_tape = 0; +static uint32_t LastBlock = 0; + +static char *VolumeName = NULL; -static JCR *jcr; +static JCR *jcr = NULL; static void usage(); @@ -77,6 +93,27 @@ int get_cmd(char *prompt); static void my_free_jcr(JCR *jcr) { + if (jcr->pool_name) { + free_pool_memory(jcr->pool_name); + jcr->pool_name = NULL; + } + if (jcr->pool_type) { + free_pool_memory(jcr->pool_type); + jcr->pool_type = NULL; + } + if (jcr->job_name) { + free_pool_memory(jcr->job_name); + jcr->job_name = NULL; + } + if (jcr->client_name) { + free_pool_memory(jcr->client_name); + jcr->client_name = NULL; + } + if (jcr->fileset_name) { + free_pool_memory(jcr->fileset_name); + jcr->fileset_name = NULL; + } + return; } @@ -113,7 +150,11 @@ int main(int argc, char *argv[]) printf("Tape block granularity is %d bytes.\n", TAPE_BSIZE); - while ((ch = getopt(argc, argv, "c:d:st?")) != -1) { + working_directory = "/tmp"; + my_name_is(argc, argv, "btape"); + init_msg(NULL, NULL); + + while ((ch = getopt(argc, argv, "c:d:stv?")) != -1) { switch (ch) { case 'c': /* specify config file */ if (configfile != NULL) { @@ -129,12 +170,16 @@ int main(int argc, char *argv[]) } break; + case 's': + signals = FALSE; + break; + case 't': default_tape = TRUE; break; - case 's': - signals = FALSE; + case 'v': + verbose++; break; case '?': @@ -148,8 +193,6 @@ int main(int argc, char *argv[]) argv += optind; - my_name_is(argc, argv, "btape"); - init_msg(NULL, NULL); if (signals) { init_signals(terminate_btape); @@ -188,10 +231,26 @@ int main(int argc, char *argv[]) } } + /* Setup a "dummy" JCR that should work for most everything */ jcr = new_jcr(sizeof(JCR), my_free_jcr); jcr->VolSessionId = 1; jcr->VolSessionTime = (uint32_t)time(NULL); jcr->NumVolumes = 1; + jcr->pool_name = get_pool_memory(PM_FNAME); + strcpy(jcr->pool_name, "Default"); + jcr->pool_type = get_pool_memory(PM_FNAME); + strcpy(jcr->pool_type, "Backup"); + jcr->job_name = get_pool_memory(PM_FNAME); + strcpy(jcr->job_name, "Dummy.Job.Name"); + jcr->client_name = get_pool_memory(PM_FNAME); + strcpy(jcr->client_name, "Dummy.Client.Name"); + strcpy(jcr->Job, "Dummy.Job"); + jcr->fileset_name = get_pool_memory(PM_FNAME); + strcpy(jcr->fileset_name, "Dummy.fileset.name"); + jcr->JobId = 1; + jcr->JobType = JT_BACKUP; + jcr->JobLevel = L_FULL; + jcr->JobStatus = JS_Terminated; Dmsg0(200, "Do tape commands\n"); @@ -287,8 +346,12 @@ static void labelcmd() return; } - if (!get_cmd("Enter Volume Name: ")) { - return; + if (VolumeName) { + strcpy(cmd, VolumeName); + } else { + if (!get_cmd("Enter Volume Name: ")) { + return; + } } if (!(dev->state & ST_OPENED)) { @@ -609,7 +672,8 @@ I'm going to write one record in file 0,\n\ rewindcmd(); Pmsg0(0, "Now moving to end of media.\n"); eodcmd(); - Pmsg2(0, "\nWe should be in file 3. I am at file %d. This is %s\n\n", + Pmsg2(0, "End Append files test.\n\ +We should be in file 3. I am at file %d. This is %s\n\n", dev->file, dev->file == 3 ? "correct!" : "NOT correct!!!!"); Pmsg0(0, "\nNow I am going to attempt to append to the tape.\n"); @@ -618,14 +682,15 @@ I'm going to write one record in file 0,\n\ // weofcmd(); rewindcmd(); scancmd(); - Pmsg0(0, "The above scan should have four files of:\n\ + Pmsg0(0, "End Append to the tape test.\n\ +The above scan should have four files of:\n\ One record, two records, three records, and one record respectively.\n\n"); - Pmsg0(0, "Append block test.\n\n\ + Pmsg0(0, "Append block test.\n\ I'm going to write a block, an EOF, rewind, go to EOM,\n\ -then backspace over the EOF and attempt to append a\ -second block in the first file.\n\n"); +then backspace over the EOF and attempt to append a second\n\ +block in the first file.\n\n"); rewindcmd(); wrcmd(); weofcmd(); @@ -748,6 +813,7 @@ static void scancmd() Pmsg0(0, "No device: Use device command.\n"); return; } + blocks = block_size = tot_blocks = 0; bytes = 0; if (dev->state & ST_EOT) { @@ -826,6 +892,319 @@ static void statcmd() } +/* + * First we label the tape, then we fill + * it with data get a new tape and write a few blocks. + */ +static void fillcmd() +{ + DEV_RECORD rec; + DEV_BLOCK *block; + char ec1[50]; + char *p; + + if (!dev) { + Pmsg0(0, "No device: Use device command.\n"); + return; + } + + ok = TRUE; + stop = FALSE; + + Pmsg0(000, "\n\ +This command simulates Bacula writing to a tape.\n\ +It command requires two blank tapes, which it\n\ +will label and write. It will print a status approximately\n\ +every 322 MB, and write an EOF every 3.2 GB. When the first tape\n\ +fills, it will ask for a second, and after writing a few \n\ +blocks, it will stop. Then it will begin re-reading the\n\ +This may take a long time. I.e. hours! ...\n\n"); + + get_cmd("Do you wish to continue? (y/n): "); + if (cmd[0] != 'y') { + Pmsg0(000, "Command aborted.\n"); + return; + } + + VolumeName = "TestVolume1"; + labelcmd(); + VolumeName = NULL; + + + Dmsg1(20, "Begin append device=%s\n", dev_name(dev)); + + block = new_block(dev); + + /* + * Acquire output device for writing. Note, after acquiring a + * device, we MUST release it, which is done at the end of this + * subroutine. + */ + Dmsg0(100, "just before acquire_device\n"); + if (!acquire_device_for_append(jcr, dev, block)) { + jcr->JobStatus = JS_Cancelled; + free_block(block); + return; + } + + Dmsg0(100, "Just after acquire_device_for_append\n"); + /* + * Write Begin Session Record + */ + if (!write_session_label(jcr, block, SOS_LABEL)) { + jcr->JobStatus = JS_Cancelled; + Jmsg1(jcr, M_FATAL, 0, _("Write session label failed. ERR=%s\n"), + strerror_dev(dev)); + ok = FALSE; + } + + memset(&rec, 0, sizeof(rec)); + rec.data = get_memory(100000); /* max record size */ + /* + * Fill write buffer with random data + */ +#define REC_SIZE 32768 + p = rec.data; + for (int i=0; i < REC_SIZE; ) { + makeSessionKey(p, NULL, 0); + p += 16; + i += 16; + } + rec.data_len = REC_SIZE; + + /* + * Get Data from File daemon, write to device + */ + jcr->VolFirstFile = 0; + time(&jcr->run_time); /* start counting time for rates */ + for (file_index = 0; ok && !job_cancelled(jcr); ) { + uint64_t *lp; + rec.VolSessionId = jcr->VolSessionId; + rec.VolSessionTime = jcr->VolSessionTime; + rec.FileIndex = ++file_index; + rec.Stream = STREAM_FILE_DATA; + /* Write file_index at beginning of buffer */ + lp = (uint64_t *)rec.data; + *lp = (uint64_t)file_index; + + Dmsg4(250, "before writ_rec FI=%d SessId=%d Strm=%s len=%d\n", + rec.FileIndex, rec.VolSessionId, stream_to_ascii(rec.Stream), + rec.data_len); + + if (!write_record_to_block(block, &rec)) { + Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len, + rec.remainder); + if (!flush_block(block)) { + return; + } + + /* Every 5000 blocks (approx 322MB) report where we are. + */ + if ((block->BlockNumber % 5000) == 0) { + now = time(NULL); + now -= jcr->run_time; + if (now <= 0) { + now = 1; + } + kbs = (double)dev->VolCatInfo.VolCatBytes / (1000 * now); + Pmsg3(000, "Wrote block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber, + edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), (float)kbs); + } + /* Every 50000 blocks (approx 3.2MB) write an eof. + */ + if ((block->BlockNumber % 50000) == 0) { + Pmsg0(000, "Flush block, write EOF\n"); + flush_block(block); + weof_dev(dev, 1); + /* The weof resets the block number */ + } + + if (block->BlockNumber > 10 && stop) { /* get out */ + break; + } + } + if (!ok) { + Pmsg0(000, "Not OK\n"); + break; + } + jcr->JobBytes += rec.data_len; /* increment bytes this job */ + Dmsg4(190, "write_record FI=%s SessId=%d Strm=%s len=%d\n", + FI_to_ascii(rec.FileIndex), rec.VolSessionId, + stream_to_ascii(rec.Stream), rec.data_len); + } + Dmsg0(000, "Write_end_session_label()\n"); + /* Create Job status for end of session label */ + if (!job_cancelled(jcr) && ok) { + jcr->JobStatus = JS_Terminated; + } else if (!ok) { + jcr->JobStatus = JS_ErrorTerminated; + } + if (!write_session_label(jcr, block, EOS_LABEL)) { + Pmsg1(000, _("Error writting end session label. ERR=%s\n"), strerror_dev(dev)); + ok = FALSE; + } + /* Write out final block of this session */ + if (!write_block_to_device(jcr, dev, block)) { + Pmsg0(000, "Set ok=FALSE after write_block_to_device.\n"); + ok = FALSE; + } + + /* Release the device */ + if (!release_device(jcr, dev)) { + Pmsg0(000, "Error in release_device\n"); + ok = FALSE; + } + + free_block(block); + Pmsg0(000, "Done with fill command. Now beginning re-read of tapes...\n"); + + unfillcmd(); +} + +/* + * Read two tapes written by the "fill" command and ensure + * that the data is valid. + */ +static void unfillcmd() +{ + DEV_BLOCK *block; + + VolBytes = 0; + LastBlock = 0; + block = new_block(dev); + + dev->capabilities |= CAP_ANONVOLS; /* allow reading any volume */ + dev->capabilities &= ~CAP_LABEL; /* don't label anything here */ + + end_of_tape = 0; + get_cmd("Mount first of two tapes. Press enter when ready: "); + + pm_strcpy(&jcr->VolumeName, "TestVolume1"); + close_dev(dev); + dev->state &= ~ST_READ; + if (!acquire_device_for_read(jcr, dev, block)) { + Pmsg0(000, dev->errmsg); + return; + } + + time(&jcr->run_time); /* start counting time for rates */ + read_records(jcr, dev, record_cb, my_mount_next_read_volume); + free_block(block); + + Pmsg0(000, "Done with unfillcmd.\n"); +} + + +static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec) +{ + SESSION_LABEL label; + if (rec->FileIndex < 0) { + if (verbose > 1) { + dump_label_record(dev, rec, 1); + } + switch (rec->FileIndex) { + case PRE_LABEL: + Pmsg0(000, "Volume is prelabeled. This tape cannot be scanned.\n"); + return; + case VOL_LABEL: + unser_volume_label(dev, rec); + Pmsg2(000, "VOL_LABEL: block=%u vol=%s\n", block->BlockNumber, + dev->VolHdr.VolName); + break; + case SOS_LABEL: + unser_session_label(&label, rec); + Pmsg1(000, "SOS_LABEL: JobId=%u\n", label.JobId); + break; + case EOS_LABEL: + unser_session_label(&label, rec); + Pmsg2(000, "EOS_LABEL: block=%u JobId=%u\n", block->BlockNumber, + label.JobId); + break; + case EOM_LABEL: + Pmsg0(000, "EOM_LABEL:\n"); + break; + case EOT_LABEL: /* end of all tapes */ + char ec1[50]; + + if (LastBlock != block->BlockNumber) { + VolBytes += block->block_len; + } + LastBlock = block->BlockNumber; + now = time(NULL); + now -= jcr->run_time; + if (now <= 0) { + now = 1; + } + kbs = (double)VolBytes / (1000 * now); + Pmsg3(000, "Read block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber, + edit_uint64_with_commas(VolBytes, ec1), (float)kbs); + + Pmsg0(000, "End of all tapes.\n"); + + break; + default: + break; + } + return; + } + if (LastBlock != block->BlockNumber) { + VolBytes += block->block_len; + } + if ((block->BlockNumber != LastBlock) && (block->BlockNumber % 50000) == 0) { + char ec1[50]; + now = time(NULL); + now -= jcr->run_time; + if (now <= 0) { + now = 1; + } + kbs = (double)VolBytes / (1000 * now); + Pmsg3(000, "Read block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber, + edit_uint64_with_commas(VolBytes, ec1), (float)kbs); + } + LastBlock = block->BlockNumber; + if (end_of_tape) { + Pmsg1(000, "End of all blocks. Block=%u\n", block->BlockNumber); + } +} + + + +/* + * Write current block to tape regardless of whether or + * not it is full. If the tape fills, attempt to + * acquire another tape. + */ +static int flush_block(DEV_BLOCK *block) +{ + char ec1[50]; + lock_device(dev); + if (!write_block_to_dev(dev, block)) { + Pmsg2(000, "Doing fixup device error. FileIndex=%u Block=%u\n", + (unsigned)file_index, block->BlockNumber); + now = time(NULL); + now -= jcr->run_time; + if (now <= 0) { + now = 1; + } + kbs = (double)dev->VolCatInfo.VolCatBytes / (1000 * now); + vol_size = dev->VolCatInfo.VolCatBytes; + Pmsg2(000, "End of tape. VolumeCapacity=%s. Write rate = %.1f KB/s\n", + edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), kbs); + if (!fixup_device_block_write_error(jcr, dev, block)) { + Pmsg1(000, _("Cannot fixup device error. %s\n"), strerror_dev(dev)); + ok = FALSE; + unlock_device(dev); + return 0; + } + Pmsg1(000, "Changed tapes. Block=%u\n", block->BlockNumber); + stop = 1; + unlock_device(dev); + return 1; /* write one more block to next tape then stop */ + } + unlock_device(dev); + return 1; +} + struct cmdstruct { char *key; void (*func)(); char *help; }; static struct cmdstruct commands[] = { @@ -837,6 +1216,8 @@ static struct cmdstruct commands[] = { {"eod", eodcmd, "go to end of Bacula data for append"}, {"test", testcmd, "General test Bacula tape functions"}, {"eom", eomcmd, "go to the physical end of medium"}, + {"fill", fillcmd, "fill tape, write onto second volume"}, + {"unfill", unfillcmd, "read filled tape"}, {"fsf", fsfcmd, "forward space a file"}, {"fsr", fsrcmd, "forward space a record"}, {"help", helpcmd, "print this command"}, @@ -932,3 +1313,64 @@ get_cmd(char *prompt) quit = 1; return 0; } + +/* Dummies to replace askdir.c */ +int dir_get_volume_info(JCR *jcr, int writing) { return 1;} +int dir_find_next_appendable_volume(JCR *jcr) { return 1;} +int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; } +int dir_create_jobmedia_record(JCR *jcr) { return 1; } +int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;} +int dir_send_job_status(JCR *jcr) {return 1;} + + +int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev) +{ + fprintf(stderr, "Mount Volume \"%s\" on device %s and press return when ready: ", + jcr->VolumeName, dev_name(dev)); + getchar(); + return 1; +} + +int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) +{ + fprintf(stderr, "Mount next Volume on device %s and press return when ready: ", + dev_name(dev)); + getchar(); + VolumeName = "TestVolume2"; + labelcmd(); + VolumeName = NULL; + return 1; +} + +static int my_mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) +{ + char ec1[50]; + + Pmsg1(000, "End of Volume \"%s\"\n", jcr->VolumeName); + + if (LastBlock != block->BlockNumber) { + VolBytes += block->block_len; + } + LastBlock = block->BlockNumber; + now = time(NULL); + now -= jcr->run_time; + if (now <= 0) { + now = 1; + } + kbs = (double)VolBytes / (1000 * now); + Pmsg3(000, "Read block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber, + edit_uint64_with_commas(VolBytes, ec1), (float)kbs); + + if (strcmp(jcr->VolumeName, "TestVolume2") == 0) { + end_of_tape = 1; + return 0; + } + pm_strcpy(&jcr->VolumeName, "TestVolume2"); + close_dev(dev); + dev->state &= ~ST_READ; + if (!acquire_device_for_read(jcr, dev, block)) { + Pmsg2(0, "Cannot open Dev=%s, Vol=%s\n", dev_name(dev), jcr->VolumeName); + return 0; + } + return 1; /* next volume mounted */ +} diff --git a/bacula/src/stored/label.c b/bacula/src/stored/label.c index bf7281b198..92a34507ff 100644 --- a/bacula/src/stored/label.c +++ b/bacula/src/stored/label.c @@ -453,15 +453,16 @@ void create_session_label(JCR *jcr, DEV_RECORD *rec, int label) ser_btime(get_current_btime()); ser_float64(0); } else { - get_current_time(&dt); - ser_float64(dt.julian_day_number); - ser_float64(dt.julian_day_fraction); + get_current_time(&dt); + ser_float64(dt.julian_day_number); + ser_float64(dt.julian_day_fraction); } ser_string(jcr->pool_name); ser_string(jcr->pool_type); ser_string(jcr->job_name); /* base Job name */ ser_string(jcr->client_name); + /* Added in VerNum 10 */ ser_string(jcr->Job); /* Unique name of this Job */ ser_string(jcr->fileset_name); @@ -476,6 +477,7 @@ void create_session_label(JCR *jcr, DEV_RECORD *rec, int label) ser_uint32(jcr->StartFile); ser_uint32(jcr->EndFile); ser_uint32(jcr->JobErrors); + /* Added in VerNum 11 */ ser_uint32(jcr->JobStatus); } diff --git a/bacula/src/version.h b/bacula/src/version.h index c2761e8a41..ef79285da5 100644 --- a/bacula/src/version.h +++ b/bacula/src/version.h @@ -1,8 +1,8 @@ /* */ #define VERSION "1.26" #define VSTRING "1" -#define DATE "4 October 2002" -#define LSMDATE "04Oct02" +#define DATE "8 October 2002" +#define LSMDATE "08Oct02" /* Debug flags */ #define DEBUG 1