- Make sure that the restore options don't permit "seeing" other
Client's job data.
- Restore of a raw drive should not try to check the volume size.
+- Lock tape drive door when open()
+- Make release unload any autochanger.
}
pm_strcpy(jcr->VolumeName, mr->VolumeName);
generate_job_event(jcr, "VolumePurged");
- if (ua->jcr) {
+ /* Send message to Job report, if it is a *real* job */
+ if (jcr && jcr->JobId > 0) {
Jmsg1(jcr, M_INFO, 0, _("All records pruned from Volume \"%s\"; marking it \"Purged\"\n"),
mr->VolumeName);
}
rx->pnl = 0;
}
- Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
+ Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname);
}
static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
{
int sec, bps;
char *msg, b1[32], b2[32], b3[32], b4[32];
- int found, len;
+ int len;
+ bool found = false;
JCR *njcr;
char dt[MAX_TIME_LENGTH];
msg = (char *)get_pool_memory(PM_MESSAGE);
- found = 0;
len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s %s\n"),
my_name, VERSION, BDATE, VSS, HOST_OS, DISTNAME, DISTVER);
sendit(msg, len, arg);
sendit(msg, len, arg);
}
- found = 1;
+ found = true;
if (njcr->store_bsock) {
len = Mmsg(msg, " SDReadSeqNo=%" lld " fd=%d\n",
njcr->store_bsock->read_seqno, njcr->store_bsock->fd);
}
endeach_jcr(njcr);
+ if (!found) {
+ len = Mmsg(msg, _("No Jobs running.\n"));
+ sendit(msg, len, arg);
+ }
sendit(_("====\n"), 5, arg);
list_terminated_jobs(sendit, arg);
vol.InChanger = InChanger; /* bool in structure */
unbash_spaces(vol.VolCatName);
bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName));
- memcpy(&dcr->VolCatInfo, &vol, sizeof(dcr->VolCatInfo));
+ dcr->VolCatInfo = vol; /* structure assignment */
Dmsg2(300, "do_reqest_vol_info return true slot=%d Volume=%s\n",
vol.Slot, vol.VolCatName);
BSOCK *dir = jcr->dir_bsock;
DEVICE *dev = dcr->dev;
time_t LastWritten = time(NULL);
- char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
+ char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
int InChanger;
POOL_MEM VolumeName;
}
Dmsg1(420, "get_volume_info(): %s", dir->msg);
/* Update dev Volume info in case something changed (e.g. expired) */
- memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
+ dev->VolCatInfo = dcr->VolCatInfo;
return true;
}
dev->VolCatInfo.VolCatBytes += block->block_len;
dev->VolCatInfo.VolCatBlocks++;
if (dev->VolCatInfo.VolFirstWritten == 0) {
- dev->VolCatInfo.VolFirstWritten = time(NULL); /* Set first written time */
+ dev->VolCatInfo.VolFirstWritten = (utime_t)time(NULL); /* Set first written time */
}
dev->EndBlock = dev->block_num;
dev->EndFile = dev->file;
static void clearcmd();
static void wrcmd();
static void rrcmd();
+static void rbcmd();
static void eodcmd();
static void fillcmd();
static void qfillcmd();
}
}
+/*
+ * Read a Bacula block from the tape
+ */
+static void rbcmd()
+{
+ dev->open(dcr, OPEN_READ_ONLY);
+ read_block_from_dev(dcr, NO_BLOCK_NUMBER_CHECK);
+}
/*
* Write a Bacula block to the tape
DEV_RECORD *rec = dcr->rec;
int i;
+ open_the_device();
sm_check(__FILE__, __LINE__, false);
empty_block(block);
if (verbose > 1) {
{NT_("weof"), weofcmd, _("write an EOF on the tape")},
{NT_("wr"), wrcmd, _("write a single Bacula block")},
{NT_("rr"), rrcmd, _("read a single record")},
+ {NT_("rb"), rbcmd, _("read a single Bacula block")},
{NT_("qfill"), qfillcmd, _("quick fill command")}
};
#define comsize (sizeof(commands)/sizeof(struct cmdstruct))
/* Set open timer */
tid = start_thread_timer(pthread_self(), timeout);
}
- /* If busy retry each second for max_open_wait seconds */
Dmsg2(100, "Try open %s mode=%s\n", print_name(), mode_to_str(omode));
- /* Use system open() */
#if defined(HAVE_WIN32)
/* Windows Code */
if ((fd = tape_open(dev_name, mode)) < 0) {
dev_errno = errno;
}
+
#else
+
/* UNIX Code */
-
+ /* If busy retry each second for max_open_wait seconds */
for ( ;; ) {
/* Try non-blocking open */
fd = ::open(dev_name, mode+O_NONBLOCK);
print_name(), omode, mode, errno, be.strerror());
} else {
/* Tape open, now rewind it */
+ Dmsg0(050, "Rewind after open\n");
mt_com.mt_op = MTREW;
mt_com.mt_count = 1;
- if (tape_ioctl(fd, MTIOCTOP, (char *)&mt_com) < 0) {
+ if (ioctl(fd, MTIOCTOP, (char *)&mt_com) < 0) {
berrno be;
dev_errno = errno; /* set error status from rewind */
::close(fd);
Dmsg5(050, "Open error on %s omode=%d mode=%x errno=%d: ERR=%s\n",
print_name(), omode, mode, errno, be.strerror());
break;
- } else {
- dev_errno = 0;
}
+ dev_errno = 0;
+ lock_door();
set_os_device_parameters(this); /* do system dependent stuff */
- break; /* Successfully opened and rewound */
+ break; /* Successfully opened and rewound */
}
}
bmicrosleep(5, 0);
* catalog in dcr->VolCatInfo, and thus we refresh the dev->VolCatInfo
* copy here, when opening.
*/
- memcpy(&VolCatInfo, &dcr->VolCatInfo, sizeof(VolCatInfo));
+ VolCatInfo = dcr->VolCatInfo; /* structure assignment */
Dmsg1(100, "Volume=%s\n", VolCatInfo.VolCatName);
if (VolCatInfo.VolCatName[0] == 0) {
block_num = file = 0;
file_size = 0;
file_addr = 0;
-#ifdef MTUNLOCK
- mt_com.mt_op = MTUNLOCK;
- mt_com.mt_count = 1;
- tape_ioctl(fd, MTIOCTOP, (char *)&mt_com);
-#endif
+ unlock_door();
mt_com.mt_op = MTOFFL;
mt_com.mt_count = 1;
if (tape_ioctl(fd, MTIOCTOP, (char *)&mt_com) < 0) {
return stat == 0;
}
+void DEVICE::lock_door()
+{
+#ifdef MTLOCK
+ struct mtop mt_com;
+ mt_com.mt_op = MTLOCK;
+ mt_com.mt_count = 1;
+ tape_ioctl(fd, MTIOCTOP, (char *)&mt_com);
+#endif
+}
+
+void DEVICE::unlock_door()
+{
+#ifdef MTUNLOCK
+ struct mtop mt_com;
+ mt_com.mt_op = MTUNLOCK;
+ mt_com.mt_count = 1;
+ tape_ioctl(fd, MTIOCTOP, (char *)&mt_com);
+#endif
+}
+
+
/*
* Reposition the device to file, block
* Returns: false on failure
offline();
}
- if (is_open()) {
- if (is_tape()) {
- tape_close(fd);
- } else {
- ::close(fd);
- }
- } else {
+ if (!is_open()) {
Dmsg2(100, "device %s already closed vol=%s\n", print_name(),
VolHdr.VolumeName);
return; /* already closed */
}
+ switch (dev_type) {
+ case B_TAPE_DEV:
+ tape_close(fd);
+ unlock_door();
+ break;
+ default:
+ ::close(fd);
+ }
+
/* Clean up device packet so it can be reused */
clear_opened();
state &= ~(ST_LABEL|ST_READ|ST_APPEND|ST_EOT|ST_WEOT|ST_EOF);
file_size = 0;
file_addr = 0;
EndFile = EndBlock = 0;
+ openmode = 0;
Slot = -1; /* unknown slot */
free_volume(this);
memset(&VolCatInfo, 0, sizeof(VolCatInfo));
stop_thread_timer(tid);
tid = 0;
}
- openmode = 0;
}
/*
VOLUME_CAT_INFO saveVolCatInfo; /* Volume Catalog Information */
- memcpy(&saveVolHdr, &VolHdr, sizeof(saveVolHdr));
- memcpy(&saveVolCatInfo, &VolCatInfo, sizeof(saveVolCatInfo));
+ saveVolHdr = VolHdr; /* structure assignment */
+ saveVolCatInfo = VolCatInfo; /* structure assignment */
close(); /* close current part */
- memcpy(&VolHdr, &saveVolHdr, sizeof(VolHdr));
- memcpy(&VolCatInfo, &saveVolCatInfo, sizeof(VolCatInfo));
- memcpy(&dcr->VolCatInfo, &saveVolCatInfo, sizeof(dcr->VolCatInfo));
+ VolHdr = saveVolHdr; /* structure assignment */
+ VolCatInfo = saveVolCatInfo; /* structure assignment */
+ dcr->VolCatInfo = saveVolCatInfo; /* structure assignment */
}
off_t DEVICE::lseek(DCR *dcr, off_t offset, int whence)
#if defined(HAVE_LINUX_OS) || defined(HAVE_WIN32)
struct mtop mt_com;
-#if defined(MTRESET)
- mt_com.mt_op = MTRESET;
- mt_com.mt_count = 0;
- if (tape_ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
- dev->clrerror(MTRESET);
- }
-#endif
+ Dmsg0(050, "In set_os_device_parameters\n");
#if defined(MTSETBLK)
if (dev->min_block_size == dev->max_block_size &&
dev->min_block_size == 0) { /* variable block mode */
mt_com.mt_op = MTSETBLK;
mt_com.mt_count = 0;
+ Dmsg0(050, "Set block size to zero\n");
if (tape_ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
dev->clrerror(MTSETBLK);
}
- Dmsg0(100, "Set block size to 0\n");
}
#endif
#if defined(MTSETDRVBUFFER)
if (dev->has_cap(CAP_EOM)) {
mt_com.mt_count |= MT_ST_FAST_MTEOM;
}
+ Dmsg0(050, "MTSETDRVBUFFER\n");
if (tape_ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
dev->clrerror(MTSETDRVBUFFER);
}
static bool dev_get_os_pos(DEVICE *dev, struct mtget *mt_stat)
{
+ Dmsg0(050, "dev_get_os_pos\n");
return dev->has_cap(CAP_MTIOCGET) &&
tape_ioctl(dev->fd, MTIOCGET, (char *)mt_stat) == 0 &&
mt_stat->mt_fileno >= 0;
uint32_t EndBlock; /* Last block number */
int32_t LabelType; /* Bacula/ANSI/IBM */
int32_t Slot; /* >0=Slot loaded, 0=nothing, -1=unknown */
- utime_t VolFirstWritten; /* Time of first write */
uint32_t VolCatMaxJobs; /* Maximum Jobs to write to volume */
uint32_t VolCatMaxFiles; /* Maximum files to write to volume */
uint64_t VolCatMaxBytes; /* Max bytes to write to volume */
uint64_t VolCatCapacityBytes; /* capacity estimate */
uint64_t VolReadTime; /* time spent reading */
uint64_t VolWriteTime; /* time spent writing this Volume */
+ utime_t VolFirstWritten; /* Time of first write */
bool InChanger; /* Set if vol in current magazine */
char VolCatStatus[20]; /* Volume status */
char VolCatName[MAX_NAME_LENGTH]; /* Desired volume to mount */
bool fsf(int num); /* in dev.c */
bool bsr(int num); /* in dev.c */
bool weof(int num); /* in dev.c */
+ void lock_door(); /* in dev.c */
+ void unlock_door(); /* in dev.c */
bool scan_dir_for_volume(DCR *dcr); /* in scan.c */
bool reposition(DCR *dcr, uint32_t rfile, uint32_t rblock); /* in dev.c */
void clrerror(int func); /* in dev.c */
P(dev->mutex); /* Use P to avoid indefinite block */
if (!dev->is_open()) {
if (!dev->is_busy()) {
- unload_autochanger(jcr->dcr, -1);
+ unload_autochanger(dcr, -1);
}
if (dev->is_dvd()) {
if (unmount_dvd(dev, 0)) {
} else if (dev->dev_blocked == BST_WAITING_FOR_SYSOP) {
Dmsg2(90, "%d waiter dev_block=%d. doing unmount\n", dev->num_waiting,
dev->dev_blocked);
- if (!unload_autochanger(jcr->dcr, -1)) {
+ if (!unload_autochanger(dcr, -1)) {
dev->close();
}
if (dev->is_dvd() && !unmount_dvd(dev, 0)) {
/* block_device(dev, BST_UNMOUNTED); replace with 2 lines below */
dev->dev_blocked = BST_UNMOUNTED;
dev->no_wait_id = 0;
- if (!unload_autochanger(jcr->dcr, -1)) {
+ if (!unload_autochanger(dcr, -1)) {
dev->close();
}
if (dev->is_dvd() && !unmount_dvd(dev, 0)) {
dev = dcr->dev;
P(dev->mutex); /* Use P to avoid indefinite block */
if (!dev->is_open()) {
+ if (!dev->is_busy()) {
+ unload_autochanger(dcr, -1);
+ }
Dmsg0(90, "Device already released\n");
bnet_fsend(dir, _("3921 Device %s already released.\n"),
dev->print_name());
- } else if (dev->dev_blocked == BST_WAITING_FOR_SYSOP ||
- dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP) {
+ } else if (dev->dev_blocked == BST_WAITING_FOR_SYSOP) {
+ Dmsg2(90, "%d waiter dev_block=%d.\n", dev->num_waiting,
+ dev->dev_blocked);
+ unload_autochanger(dcr, -1);
+ bnet_fsend(dir, _("3922 Device %s waiting for sysop.\n"),
+ dev->print_name());
+
+ } else if (dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP) {
Dmsg2(90, "%d waiter dev_block=%d. doing unmount\n", dev->num_waiting,
dev->dev_blocked);
bnet_fsend(dir, _("3922 Device %s waiting for mount.\n"),
} else if (dev->is_busy()) {
send_dir_busy_message(dir, dev);
} else { /* device not being used */
- Dmsg0(90, "Device not in use, unmounting\n");
- release_volume(jcr->dcr);
+ Dmsg0(90, "Device not in use, releaseing\n");
+ unload_autochanger(dcr, -1);
+ release_volume(dcr);
bnet_fsend(dir, _("3022 Device %s released.\n"),
dev->print_name());
}
switch (vol_label_status) {
case VOL_OK:
Dmsg1(150, "Vol OK name=%s\n", dcr->VolumeName);
- memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
+ dev->VolCatInfo = dcr->VolCatInfo; /* structure assignment */
recycle = strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0;
break; /* got a Volume */
case VOL_NAME_ERROR:
* this volume is really OK. If not, put back the desired
* volume name, mark it not in changer and continue.
*/
- memcpy(&dcrVolCatInfo, &dcr->VolCatInfo, sizeof(dcrVolCatInfo));
- memcpy(&devVolCatInfo, &dev->VolCatInfo, sizeof(devVolCatInfo));
+ dcrVolCatInfo = dcr->VolCatInfo; /* structure assignment */
+ devVolCatInfo = dev->VolCatInfo; /* structure assignment */
/* Check if this is a valid Volume in the pool */
bstrncpy(dcr->VolumeName, dev->VolHdr.VolumeName, sizeof(dcr->VolumeName));
if (!dir_get_volume_info(dcr, GET_VOL_INFO_FOR_WRITE)) {
*/
mark_volume_not_inchanger(dcr);
}
- memcpy(&dev->VolCatInfo, &devVolCatInfo, sizeof(dev->VolCatInfo));
+ dev->VolCatInfo = devVolCatInfo; /* structure assignment */
bstrncpy(dev->BadVolName, dev->VolHdr.VolumeName, sizeof(dev->BadVolName));
Jmsg(jcr, M_WARNING, 0, _("Director wanted Volume \"%s\".\n"
" Current Volume \"%s\" not acceptable because:\n"
jcr->dir_bsock->msg);
ask = true;
/* Restore saved DCR before continuing */
- memcpy(&dcr->VolCatInfo, &dcrVolCatInfo, sizeof(dcr->VolCatInfo));
+ dcr->VolCatInfo = dcrVolCatInfo; /* structure assignment */
goto mount_next_vol;
}
/*
* the Director, so use it.
*/
Dmsg1(150, "want new name=%s\n", dcr->VolumeName);
- memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
+ dev->VolCatInfo = dcr->VolCatInfo; /* structure assignment */
recycle = strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0;
break; /* got a Volume */
/*
}
Dmsg0(150, "dir_update_vol_info. Set Append\n");
/* Copy Director's info into the device info */
- memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
+ dev->VolCatInfo = dcr->VolCatInfo; /* structure assignment */
if (!dir_update_volume_info(dcr, true)) { /* indicate tape labeled */
return try_error;
}
DEVICE *dev = dcr->dev;
Jmsg(dcr->jcr, M_INFO, 0, _("Marking Volume \"%s\" in Error in Catalog.\n"),
dcr->VolumeName);
- memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
+ dev->VolCatInfo = dcr->VolCatInfo; /* structure assignment */
bstrncpy(dev->VolCatInfo.VolCatStatus, "Error", sizeof(dev->VolCatInfo.VolCatStatus));
Dmsg0(150, "dir_update_vol_info. Set Error.\n");
dir_update_volume_info(dcr, false);
Jmsg(jcr, M_ERROR, 0, _("Autochanger Volume \"%s\" not found in slot %d.\n"
" Setting InChanger to zero in catalog.\n"),
dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.Slot);
- memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
+ dev->VolCatInfo = dcr->VolCatInfo; /* structure assignment */
dcr->VolCatInfo.InChanger = false;
dev->VolCatInfo.InChanger = false;
Dmsg0(400, "update vol info in mount\n");
* this volume is really OK. If not, put back the desired
* volume name, mark it not in changer and continue.
*/
- memcpy(&dcrVolCatInfo, &dcr->VolCatInfo, sizeof(dcrVolCatInfo));
- memcpy(&devVolCatInfo, &VolCatInfo, sizeof(devVolCatInfo));
+ dcrVolCatInfo = dcr->VolCatInfo; /* structure assignment */
+ devVolCatInfo = VolCatInfo; /* structure assignment */
/* Check if this is a valid Volume in the pool */
bstrncpy(dcr->VolumeName, result->d_name, sizeof(dcr->VolumeName));
if (!dir_get_volume_info(dcr, GET_VOL_INFO_FOR_WRITE)) {
/* This was not the volume we expected, but it is OK with
* the Director, so use it.
*/
- memcpy(&VolCatInfo, &dcr->VolCatInfo, sizeof(VolCatInfo));
+ VolCatInfo = dcr->VolCatInfo; /* structure assignment */
found = true;
break; /* got a Volume */
}
dev->pool_name[0]?dev->pool_name:"*unknown*");
sendit(msg, len, arg);
} else {
- len = Mmsg(msg, _("Device %s open but no Bacula volume is mounted.\n"),
+ len = Mmsg(msg, _("Device %s open but no Bacula volume is currently mounted.\n"),
dev->print_name());
sendit(msg, len, arg);
}
}
endeach_jcr(jcr);
+ if (!found) {
+ len = Mmsg(msg, _("No Jobs running.\n"));
+ sendit(msg, len, arg);
+ }
sendit("====\n", 5, arg);
free_pool_memory(msg);
OPENSSL_INC = @OPENSSL_INC@
OPENSSL_LIBS = @OPENSSL_LIBS@
-FINDOBJS = testfind.o dird_conf.o inc_conf.o run_conf.o
+FINDOBJS = testfind.o ../dird/dird_conf.o ../dird/inc_conf.o ../dird/run_conf.o
# these are the objects that are changed by the .configure process
EXTRAOBJS = @OBJLIST@
#undef VERSION
#define VERSION "1.39.23"
-#define BDATE "26 September 2006"
-#define LSMDATE "26Sep06"
+#define BDATE "28 September 2006"
+#define LSMDATE "28Sep06"
#define BYEAR "2006" /* year for copyright messages in progs */
/* Debug flags */
Technical notes on version 1.39
General:
+28Sep06
+kes Print the Volume purged message only for real jobs to keep
+ from cluttering up the daemon messages.
+kes Lock the tape drive door while Bacula is using the device.
+kes Add back the 'No Jobs running' message to FD and SD status because
+ my regression script depends on it, and I'm too lazy to change the
+ scripts.
+kes Start using structure assignments (not too happy about it ...).
+kes Fix a bug in the tools Makefile that broke on Solaris.
+kes Unload any autochanger drive during a 'release' command.
26Sep06
kes Enhance error message when restoring without bootstrap file.
kes Check restored size only for regular files.
kes Fix logic error in handling error return from mtx-changer
script.
kes Make status from SD aware of -1 (unknown) Slot status.
-kes At Eric's suggestion make both the "slots" and "drive" commands
+kes At Eric's suggestion make both the 'slots' and 'drive' commands
to the Storage daemon work even if the drive is busy.
kes Make two separate Win32 menu links for starting bconsole and
wx-console in winbacula.nsi
kes Integrate multiple console/director patch from
Carsten Paeth calle@calle.in-berlin.de
ebl Add character substitution in Job/JobDefs WriteBootStrap.
- You can use now 'WriteBootStrap = "/path/%c_%n.bsr"'
+ You can use now 'WriteBootStrap = '/path/%c_%n.bsr''
kes Apply patch supplied in bug #656 to pass priority field
in the run dialog to the Director in gnome console
kes Restore DCR after VOL_NAME_ERROR in mount.c. Hopefully
and forcing a 6 second wait.
===================== Warning =============================
- Removed "Accept Any Volume" directive.
+ Removed 'Accept Any Volume' directive.
===========================================================
kes Major cleanup and simplification of regress using shell functions
kes Rework a few of the zlib changes so that they build properly.
21May06
-kes Move DIR "run" command to SD before starting the message thread
+kes Move DIR 'run' command to SD before starting the message thread
as suggested by Cristopher Hull to avoid a race deadlock from
two threads using the bsock structure.
kes Modify LICENSE to correct some problems pointed out by Debian.
volume so that it can handle multiple returns from the wait
code.
kes Modify the wait code to permit multiple returns.
-kes Return a zero when "autochanger drives" is called and
+kes Return a zero when 'autochanger drives' is called and
it is not an autochanger.
kes Make rewind_dev() a method taking a DCR as an argument.
This permits closing and reopening the drive if the
from find_one.c as it can generate too many warning messages.
kes Modify most restore error messages to be queued so that they
appear at the end of the job rather than mixted with the restore
- listing where they could be "lost".
+ listing where they could be 'lost'.
06Dec05
kes Reset timeout values before select() per patch from
Frank Sweetser for problems with non-blocking sockets.
kes Landon merged his data encription changes into the HEAD
kes Apply days keyword patch from Alexander.Bergolth at wu-wien.ac.at
If this patch is applied, the number of days can be specified with
- "list nextvol days=xx"
+ 'list nextvol days=xx'
or
- "status dir days=xx"
+ 'status dir days=xx'
My use case is to be able to preview the next scheduled job (and the
next tape to be used) on fridays if there are no scheduled jobs during
the weekend.