--- /dev/null
+
+From bacula-users-admin@lists.sourceforge.net Wed Aug 13 19:22:21 2003
+From: Robert L Mathews <lists@tigertech.com>
+To: <bacula-users@lists.sourceforge.net>
+Content-Type: text/plain; charset="US-ASCII"
+Message-Id: <20030813170422.E48403FC65F@fry.tigertech.net>
+Subject: [Bacula-users] Making Backups Run Every Other Week
+Sender: bacula-users-admin@lists.sourceforge.net
+Date: Wed, 13 Aug 2003 10:04:23 -0700
+
+In case anyone is interested, here's a tip I came up with.
+
+My backup policy is such that I need backups to run every other week. I
+have two separate "offsite" tape pools, and a backup is made to each of
+them on alternating weeks.
+
+Bacula's scheduler currently doesn't handle "every two weeks", and using
+something like "the first and third weeks for backup A, and the second
+and fourth weeks for backup B" means there will be no backup done on the
+fifth week if the month contains one. Scheduling a backup for the fifth
+week doesn't help; it means that the same backup would sometimes run
+twice in a row, which ruins the alternating week scheme.
+
+I first thought of poking around the code to make the scheduler support
+"every two weeks", and I someday may still do so. However, I found an
+easier way to do this is in the meantime: with a RunBeforeJob line.
+
+What I do is schedule both jobs to run every single week. Then the job
+that runs my "Offsite Backup A" has this line:
+
+ RunBeforeJob = "/etc/bacula/two_week_script 'July 6 2003'"
+
+And the job with my "Offsite Backup B" has this one:
+
+ RunBeforeJob = "/etc/bacula/two_week_script 'July 13 2003'"
+
+And two_week_script is the following Perl script:
+
+----------------
+
+#!/usr/bin/perl -w
+
+use strict;
+use constant SECONDS_IN_WEEK => 86400 * 7;
+use constant SECONDS_IN_TWO_WEEKS => SECONDS_IN_WEEK * 2;
+
+# Calculate the elapsed seconds since the starting date,
+# which must be in a format that /bin/date can understand
+# Note that this relies on the GNU "%s" date extension
+my $start_date = shift;
+$start_date = `/bin/date -d '$start_date' +'%s'`;
+chomp $start_date;
+my $time_since_start_date = time - $start_date;
+
+# Now take those seconds modulo the number of seconds in
+# two weeks. If we're in the second half of the two week
+# period, exit with an error to stop the Bacula job. If
+# we're in the first half, the script will terminate normally
+# and the Bacula job will run.
+if ($time_since_start_date % SECONDS_IN_TWO_WEEKS
+ >= SECONDS_IN_WEEK)
+{
+ exit 1;
+}
+
+----------------
+
+The result is that the script cancels the job that should not be run that
+week, while allowing the other job to continue.
+
+This idea could be trivially changed to support running every three
+weeks, every two months, every prime number of days, etc.
+
+Anyway, just a tip in case anyone else needs to schedule things in a way
+that the scheduler doesn't currently support. It's pretty obvious that
+this is the right way to do it now, but I puzzled over it for a little
+while before coming up with this.
+
+--
+Robert L Mathews, Tiger Technologies
+
+
+
+-------------------------------------------------------
+This SF.Net email sponsored by: Free pre-built ASP.NET sites including
+Data Reports, E-commerce, Portals, and Forums are available now.
+Download today and enter to win an XBOX or Visual Studio .NET.
+http://aspnet.click-url.com/go/psa00100003ave/direct;at.aspnet_072303_01/01
+_______________________________________________
+Bacula-users mailing list
+Bacula-users@lists.sourceforge.net
+https://lists.sourceforge.net/lists/listinfo/bacula-users
- Figure out how to use ssh or stunnel to protect Bacula communications.
For 1.32:
+- Edit the Client/Storage name into authentication failure messages.
- Implement job in VerifyToCatalog
- Implement migrate
- Implement List Volume Job=xxx or List scheduled volumes or Status Director
- Add user configurable timeout for connecting to SD.
- Unsaved Flag in Job record (use JobMissingFiles).
- Base Flag in Job record.
-
&jcr->VolSessionId, &jcr->VolSessionTime,
sd_auth_key) != 5) {
pm_strcpy(&jcr->errmsg, dir->msg);
- bnet_fsend(dir, BADjob);
Jmsg(jcr, M_FATAL, 0, _("Bad Job Command: %s"), jcr->errmsg);
+ bnet_fsend(dir, BADjob);
free_pool_memory(sd_auth_key);
return 0;
}
set_jcr_job_status(jcr, JS_ErrorTerminated);
return 0;
}
- strcpy(sd->msg, bootstrap);
+ pm_strcpy(&sd->msg, bootstrap);
sd->msglen = strlen(sd->msg);
bnet_send(sd);
while (fgets(buf, sizeof(buf), bs)) {
char * encode_mode (mode_t mode, char *buf);
int do_shell_expansion (char *name, int name_len);
void jobstatus_to_ascii (int JobStatus, char *msg, int maxlen);
-void pm_strcat (POOLMEM **pm, char *str);
-void pm_strcpy (POOLMEM **pm, char *str);
+int pm_strcat (POOLMEM **pm, char *str);
+int pm_strcpy (POOLMEM **pm, char *str);
int run_program (char *prog, int wait, POOLMEM *results);
char * job_type_to_str (int type);
char * job_status_to_str (int stat);
/*
* Concatenate a string (str) onto a pool memory buffer pm
*/
-void pm_strcat(POOLMEM **pm, char *str)
+in pm_strcat(POOLMEM **pm, char *str)
{
int pmlen = strlen(*pm);
int len = strlen(str) + 1;
*pm = check_pool_memory_size(*pm, pmlen + len);
memcpy(*pm+pmlen, str, len);
+ return pmlen + len - 1;
}
/*
* Copy a string (str) into a pool memory buffer pm
*/
-void pm_strcpy(POOLMEM **pm, char *str)
+int pm_strcpy(POOLMEM **pm, char *str)
{
int len = strlen(str) + 1;
*pm = check_pool_memory_size(*pm, len);
memcpy(*pm, str, len);
+ return len - 1;
}
ssize_t stat = 0;
uint32_t wlen; /* length to write */
int hit_max1, hit_max2;
- int ok;
+ bool ok;
#ifdef NO_TAPE_WRITE_TEST
empty_block(block);
dev->VolCatInfo.VolCatWrites++;
Dmsg1(300, "Write block of %u bytes\n", wlen);
- if ((uint32_t)(stat=write(dev->fd, block->buf, (size_t)wlen)) != wlen) {
+ stat = write(dev->fd, block->buf, (size_t)wlen);
+ if (stat != (ssize_t)wlen) {
/* We should check for errno == ENOSPC, BUT many
* devices simply report EIO when the volume is full.
* With a little more thought we may be able to check
* systems (FreeBSD like), do the clrerror() only after
* the weof_dev() call.
*/
-#ifndef MTIOCERRSTAT
clrerror_dev(dev, -1);
-#else
- dev->dev_errno = errno; /* save errno */
-#endif
if (dev->dev_errno == 0) {
dev->dev_errno = ENOSPC; /* out of space */
}
dev->dev_name, wlen, stat);
}
- Dmsg4(10, "=== Write error. size=%u rtn=%d errno=%d: ERR=%s\n",
- wlen, stat, dev->dev_errno, strerror(dev->dev_errno));
+ Dmsg6(100, "=== Write error. size=%u rtn=%d dev_blk=%d blk_blk=%d errno=%d: ERR=%s\n",
+ wlen, stat, dev->block_num, block->BlockNumber, dev->dev_errno, strerror(dev->dev_errno));
block->write_failed = true;
- weof_dev(dev, 1); /* end the tape */
- weof_dev(dev, 1); /* write second eof */
-#ifdef MTIOCERRSTAT
- clrerror_dev(dev, -1);
-#endif
+ if (weof_dev(dev, 2) != 0) { /* end the tape */
+ Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
+ }
dev->state |= (ST_EOF | ST_EOT | ST_WEOT);
- ok = TRUE;
+ ok = true;
#define CHECK_LAST_BLOCK
#ifdef CHECK_LAST_BLOCK
/*
/* Now back up over what we wrote and read the last block */
if (bsf_dev(dev, 1) != 0 || bsf_dev(dev, 1) != 0) {
- ok = FALSE;
+ ok = false;
Jmsg(jcr, M_ERROR, 0, _("Backspace file at EOT failed. ERR=%s\n"), strerror(dev->dev_errno));
}
/* Backspace over record */
if (ok && bsr_dev(dev, 1) != 0) {
- ok = FALSE;
+ ok = false;
Jmsg(jcr, M_ERROR, 0, _("Backspace record at EOT failed. ERR=%s\n"), strerror(dev->dev_errno));
/*
* On FreeBSD systems, if the user got here, it is likely that his/her
static void scan_blocks();
static void set_volume_name(char *VolName, int volnum);
static void rawfill_cmd();
+static void bfill_cmd();
/* Static variables */
while ((ch = getopt(argc, argv, "b:c:d:sv?")) != -1) {
switch (ch) {
- case 'b': /* bootstrap file */
- bsr = parse_bsr(NULL, optarg);
-// dump_bsr(bsr);
- break;
+ case 'b': /* bootstrap file */
+ bsr = parse_bsr(NULL, optarg);
+// dump_bsr(bsr);
+ break;
- case 'c': /* specify config file */
- if (configfile != NULL) {
- free(configfile);
- }
- configfile = bstrdup(optarg);
- break;
+ case 'c': /* specify config file */
+ if (configfile != NULL) {
+ free(configfile);
+ }
+ configfile = bstrdup(optarg);
+ break;
- case 'd': /* set debug level */
- debug_level = atoi(optarg);
- if (debug_level <= 0) {
- debug_level = 1;
- }
- break;
+ case 'd': /* set debug level */
+ debug_level = atoi(optarg);
+ if (debug_level <= 0) {
+ debug_level = 1;
+ }
+ break;
- case 's':
- signals = FALSE;
- break;
+ case 's':
+ signals = FALSE;
+ break;
- case 'v':
- verbose++;
- break;
+ case 'v':
+ verbose++;
+ break;
- case '?':
- default:
- helpcmd();
- exit(0);
+ case '?':
+ default:
+ helpcmd();
+ exit(0);
}
}
blocks++;
tot_blocks++;
bytes += block->block_len;
- Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
- block->BlockNumber, block->block_len, block->BlockVer,
+ Dmsg6(100, "Blk_blk=%u dev_blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
+ block->BlockNumber, dev->block_num, block->block_len, block->BlockVer,
block->VolSessionId, block->VolSessionTime);
if (verbose == 1) {
DEV_RECORD *rec = new_record();
read_record_from_block(block, rec);
- Pmsg7(-1, "Block: %u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n",
- block->BlockNumber, block->block_len,
+ Pmsg8(-1, "Blk_block: %u dev_blk=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n",
+ block->BlockNumber, dev->block_num, block->block_len,
FI_to_ascii(rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len);
rec->remainder = 0;
*/
jcr->VolFirstIndex = 0;
time(&jcr->run_time); /* start counting time for rates */
- Pmsg0(-1, "Begin writing records to first tape ...\n");
+ Pmsg0(-1, "Begin writing Bacula records to first tape ...\n");
+ Pmsg1(-1, "Block num = %d\n", dev->block_num);
for (file_index = 0; ok && !job_canceled(jcr); ) {
rec.VolSessionId = jcr->VolSessionId;
rec.VolSessionTime = jcr->VolSessionTime;
/* Write block to tape */
if (!flush_block(block, 1)) {
- return;
+ break;
}
/* Every 5000 blocks (approx 322MB) report where we are.
now = 1;
}
kbs = (double)dev->VolCatInfo.VolCatBytes / (1000.0 * (double)now);
- Pmsg3(-1, "Wrote block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber,
+ Pmsg4(-1, "Wrote block=%u, blk_num=%d VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber,
+ dev->block_num,
edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), (float)kbs);
}
/* Every 15000 blocks (approx 1GB) write an EOF.
free_block(block);
free_memory(rec.data);
- Pmsg0(-1, _("\n\nDone filling tape. Now beginning re-read of tape ...\n"));
dump_block(last_block, _("Last block written to tape.\n"));
+ Pmsg0(-1, _("\n\nDone filling tape. Now beginning re-read of tape ...\n"));
+
unfillcmd();
}
this_file = dev->file;
this_block_num = dev->block_num;
if (!write_block_to_dev(jcr, dev, block)) {
- Pmsg0(000, strerror_dev(dev));
Pmsg3(000, "Block not written: FileIndex=%u Block=%u Size=%u\n",
(unsigned)file_index, block->BlockNumber, block->block_len);
Pmsg2(000, "last_block_num=%u this_block_num=%d\n", last_block_num,
memset(rec->data, i & 0xFF, i);
rec->data_len = i;
rewindcmd();
- Pmsg1(0, "Begin writing %d blocks to tape ...\n", count);
+ Pmsg1(0, "Begin writing %d Bacula blocks to tape ...\n", count);
for (i=0; i < count; i++) {
if (i % 100 == 0) {
printf("+");
+ fflush(stdout);
}
if (!write_record_to_block(block, rec)) {
Pmsg0(0, _("Error writing record to block.\n"));
}
+/*
+ * Fill a tape using raw write() command
+ */
static void rawfill_cmd()
{
DEV_BLOCK *block;
return;
}
p = (uint32_t *)block->buf;
- Pmsg1(0, "Begin writing blocks of %u bytes.\n", block->buf_len);
+ Pmsg1(0, "Begin writing raw blocks of %u bytes.\n", block->buf_len);
for ( ;; ) {
*p = block_num;
stat = write(dev->fd, block->buf, block->buf_len);
if (stat == (int)block->buf_len) {
if ((block_num++ % 100) == 0) {
printf("+");
+ fflush(stdout);
}
continue;
}
}
my_errno = errno;
printf("\n");
- weofcmd();
printf("Write failed at block %u. stat=%d ERR=%s\n", block_num, stat,
strerror(my_errno));
+ weofcmd();
free_block(block);
+}
+
+/*
+ * Fill a tape using raw write() command
+ */
+static void bfill_cmd()
+{
+ DEV_BLOCK *block;
+ uint32_t block_num = 0;
+ uint32_t *p;
+ int my_errno;
+ int fd;
+
+ block = new_block(dev);
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd) {
+ read(fd, block->buf, block->buf_len);
+ } else {
+ Pmsg0(0, "Cannot open /dev/urandom.\n");
+ free_block(block);
+ return;
+ }
+ p = (uint32_t *)block->buf;
+ Pmsg1(0, "Begin writing Bacula blocks of %u bytes.\n", block->buf_len);
+ for ( ;; ) {
+ *p = block_num;
+ block->binbuf = block->buf_len;
+ block->bufp = block->buf + block->binbuf;
+ if (!write_block_to_dev(jcr, dev, block)) {
+ break;
+ }
+ if ((block_num++ % 100) == 0) {
+ printf("+");
+ fflush(stdout);
+ }
+ }
+ my_errno = errno;
+ printf("\n");
+ printf("Write failed at block %u.\n", block_num);
+ weofcmd();
+ free_block(block);
}
+
struct cmdstruct { char *key; void (*func)(); char *help; };
static struct cmdstruct commands[] = {
{"bsf", bsfcmd, "backspace file"},
{"bsr", bsrcmd, "backspace record"},
+ {"bfill", bfill_cmd, "fill tape using Bacula writes"},
{"cap", capcmd, "list device capabilities"},
{"clear", clearcmd, "clear tape errors"},
{"eod", eodcmd, "go to end of Bacula data for append"},