Kern's ToDo List
- 22 December 2003
+ 09 January 2004
Documentation to do: (any release a little bit at a time)
- Document running a test version.
- Add counter variable test.
- Document ln -sf /usr/lib/libncurses.so /usr/lib/libtermcap.so
and install the esound-devĀ package for compiling Console on SuSE.
+ This should read 'Document LDFLAGS="-L/usr/lib/termcap" ... '
- Add an example of using a FIFO in dirdconf.wml
- Add an item to the FAQ about running jobs in different timezones.
- Add some examples of job editing codes.
- Add subsections to the Disaster Recovery index section.
For 1.33
-- Look at code in recycle_oldes_purged_volume() recycle.c. Why not
- let SQL do ORDER BY LastWritten ASC?
-- Look at find_next_volume() algorithm. Currently, it selects:
- +---------+------------+---------------------+-----------+
- | MediaId | VolumeName | LastWritten | VolBytes |
- +---------+------------+---------------------+-----------+
- | 3 | Test13 | 0000-00-00 00:00:00 | 1 |
- | 4 | Test14 | 0000-00-00 00:00:00 | 1 |
- | 1 | test11 | 2003-12-03 18:39:55 | 4,004,926 |
- | 2 | test12 | 2004-01-04 15:25:56 | 2,078,691 |
- +---------+------------+---------------------+-----------+
- but perhaps it should fill already used Volumes first, and use
- Append volumes before Purged, or Recycled, ...
+- Add level to estimate command.
+- Check time/dates printed during restore when using Win32 API.
+- Possibly remove the "|| ap == NULL" on lines 123 and 207 of lib/var.c,
+ which creates compile problems on alpha systems.
+ var.c:123: no match for `va_list & == long int'
+- Check "restore" 3 (JobId), then it asks for Storage resource. Does
+ it verify that the correct volume is chosen?
- Volume "add"ed to Pool gets recycled in first use. VolBytes=0
- Get rid of 0 dates in LastWritten, ...
- Make Bacula "poll a drive".
resources, like Level? If so, I think I'd make it an optional directive
in Job, Client, and Pool, with precedence such that Job overrides Client
which in turn overrides Pool.
+- Print a message when a job starts if the conf file is not current.
- Finish work on conio.c
- To pass Include 1 or two letter commands
I Name Include name - first record
gnome-console is built, but the binary and .conf are not being installed.
- Permit Bacula and apcupsd donations (not done for apcupsd).
- Fix Ctl-C crashing the Console (readline?).
+- Look at code in recycle_oldes_purged_volume() recycle.c. Why not
+ let SQL do ORDER BY LastWritten ASC?
+- Look at find_next_volume() algorithm. Currently, it selects:
+ +---------+------------+---------------------+-----------+
+ | MediaId | VolumeName | LastWritten | VolBytes |
+ +---------+------------+---------------------+-----------+
+ | 3 | Test13 | 0000-00-00 00:00:00 | 1 |
+ | 4 | Test14 | 0000-00-00 00:00:00 | 1 |
+ | 1 | test11 | 2003-12-03 18:39:55 | 4,004,926 |
+ | 2 | test12 | 2004-01-04 15:25:56 | 2,078,691 |
+ +---------+------------+---------------------+-----------+
+ but perhaps it should fill already used Volumes first, and use
+ Append volumes before Purged, or Recycled, ...
msg_type = M_ERROR; /* Generate error message */
if (jcr->store_bsock) {
bnet_sig(jcr->store_bsock, BNET_TERMINATE);
- pthread_cancel(jcr->SD_msg_chan);
+ if (jcr->SD_msg_chan) {
+ pthread_cancel(jcr->SD_msg_chan);
+ }
}
break;
case JS_Canceled:
term_msg = _("Backup Canceled");
if (jcr->store_bsock) {
bnet_sig(jcr->store_bsock, BNET_TERMINATE);
- pthread_cancel(jcr->SD_msg_chan);
+ if (jcr->SD_msg_chan) {
+ pthread_cancel(jcr->SD_msg_chan);
+ }
}
break;
default:
P(jcr->mutex);
jcr->sd_msg_thread_done = true;
pthread_cond_broadcast(&jcr->term_wait); /* wakeup any waiting threads */
+ jcr->SD_msg_chan = 0;
V(jcr->mutex);
free_jcr(jcr); /* release jcr */
}
msg_type = M_ERROR; /* Generate error message */
if (jcr->store_bsock) {
bnet_sig(jcr->store_bsock, BNET_TERMINATE);
- pthread_cancel(jcr->SD_msg_chan);
+ if (jcr->SD_msg_chan) {
+ pthread_cancel(jcr->SD_msg_chan);
+ }
}
break;
case JS_Canceled:
term_msg = _("Restore Canceled");
if (jcr->store_bsock) {
bnet_sig(jcr->store_bsock, BNET_TERMINATE);
- pthread_cancel(jcr->SD_msg_chan);
+ if (jcr->SD_msg_chan) {
+ pthread_cancel(jcr->SD_msg_chan);
+ }
}
break;
default:
static char OK_create[] = "1000 OK CreateJobMedia\n";
/* Forward referenced functions */
-static int wait_for_sysop(JCR *jcr, DEVICE *dev, int wait_sec);
+static int wait_for_sysop(JCR *jcr, DEVICE *dev);
/*
* Send current JobStatus to Director
int dir_update_volume_info(JCR *jcr, DEVICE *dev, int label)
{
BSOCK *dir = jcr->dir_bsock;
- time_t EndTime = time(NULL);
+ time_t LastWritten = time(NULL);
char ed1[50], ed2[50], ed3[50], ed4[50];
VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
vol->VolCatMounts, vol->VolCatErrors,
vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
- EndTime, vol->VolCatStatus, vol->Slot, label,
+ LastWritten, vol->VolCatStatus, vol->Slot, label,
vol->InChanger,
edit_uint64(vol->VolReadTime, ed3),
edit_uint64(vol->VolWriteTime, ed4) );
}
+
/*
- * Request to mount next Volume, which Volume not specified
+ * Request the sysop to create an appendable volume
*
* Entered with device blocked.
* Leaves with device blocked.
* actually be mounted. The calling routine must read it and
* verify the label.
*/
-int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev)
+int dir_ask_sysop_to_create_appendable_volume(JCR *jcr, DEVICE *dev)
{
int stat = 0, jstat;
- /* ******FIXME******* put these on config variable */
- int min_wait = 60 * 60;
- int max_wait = 24 * 60 * 60;
- int max_num_wait = 9; /* 5 waits =~ 1 day, then 1 day at a time */
-
- int wait_sec;
- int num_wait = 0;
+ bool unmounted;
- Dmsg0(130, "enter dir_ask_sysop_to_mount_next_volume\n");
+ Dmsg0(130, "enter dir_ask_sysop_to_create_appendable_volume\n");
ASSERT(dev->dev_blocked);
- wait_sec = min_wait;
for ( ;; ) {
if (job_canceled(jcr)) {
- Mmsg(&dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
+ Mmsg(&dev->errmsg,
+ _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
jcr->Job, jcr->dev_name);
Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
return 0;
}
if (dir_find_next_appendable_volume(jcr)) { /* get suggested volume */
jstat = JS_WaitMount;
+ unmounted = (dev->dev_blocked == BST_UNMOUNTED) ||
+ (dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
/*
* If we have a valid volume name and we are not
* removable media, return now, or if we have a
* Slot for an autochanger, otherwise wait
* for the operator to mount the media.
*/
- if ((jcr->VolumeName[0] && !dev_cap(dev, CAP_REM) && dev_cap(dev, CAP_LABEL)) ||
- (jcr->VolumeName[0] && jcr->VolCatInfo.Slot)) {
+ if (!unmounted && ((jcr->VolumeName[0] && !dev_cap(dev, CAP_REM) &&
+ dev_cap(dev, CAP_LABEL)) ||
+ (jcr->VolumeName[0] && jcr->VolCatInfo.Slot))) {
Dmsg0(100, "Return 1 from mount without wait.\n");
return 1;
}
jcr->VolumeName, jcr->dev_name, jcr->Job);
} else {
jstat = JS_WaitMedia;
- Jmsg(jcr, M_MOUNT, 0, _(
+ if (!dev->poll) {
+ Jmsg(jcr, M_MOUNT, 0, _(
"Job %s waiting. Cannot find any appendable volumes.\n\
Please use the \"label\" command to create a new Volume for:\n\
Storage: %s\n\
Media type: %s\n\
Pool: %s\n"),
- jcr->Job,
- jcr->dev_name,
- jcr->media_type,
- jcr->pool_name);
+ jcr->Job,
+ jcr->dev_name,
+ jcr->media_type,
+ jcr->pool_name);
+ }
}
jcr->JobStatus = jstat;
dir_send_job_status(jcr);
- stat = wait_for_sysop(jcr, dev, wait_sec);
+ stat = wait_for_sysop(jcr, dev);
+ if (dev->poll) {
+ Dmsg1(200, "Poll timeout in create append vol on device %s\n", dev_name(dev));
+ continue;
+ }
if (stat == ETIMEDOUT) {
- wait_sec *= 2; /* double wait time */
- if (wait_sec > max_wait) { /* but not longer than maxtime */
- wait_sec = max_wait;
- }
- num_wait++;
- if (num_wait >= max_num_wait) {
+ if (!double_dev_wait_time(dev)) {
Mmsg(&dev->errmsg, _("Gave up waiting to mount Storage Device \"%s\" for Job %s\n"),
- jcr->dev_name, jcr->Job);
+ dev_name(dev), jcr->Job);
Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
Dmsg1(190, "Gave up waiting on device %s\n", dev_name(dev));
return 0; /* exceeded maximum waits */
}
Dmsg1(190, "Someone woke me for device %s\n", dev_name(dev));
- /* Restart wait counters */
- wait_sec = min_wait;
- num_wait = 0;
/* If no VolumeName, and cannot get one, try again */
if (jcr->VolumeName[0] == 0 && !job_canceled(jcr) &&
!dir_find_next_appendable_volume(jcr)) {
Jmsg(jcr, M_MOUNT, 0, _(
"Someone woke me up, but I cannot find any appendable\n\
volumes for Job=%s.\n"), jcr->Job);
+ /* Restart wait counters after user interaction */
+ init_dev_wait_timers(dev);
continue;
}
+ unmounted = (dev->dev_blocked == BST_UNMOUNTED) ||
+ (dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
+ if (unmounted) {
+ continue; /* continue to wait */
+ }
+
+ /*
+ * Device mounted, we have a volume, break and return
+ */
break;
}
set_jcr_job_status(jcr, JS_Running);
dir_send_job_status(jcr);
- Dmsg0(130, "leave dir_ask_sysop_to_mount_next_volume\n");
+ Dmsg0(130, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
return 1;
}
int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
{
int stat = 0;
- /* ******FIXME******* put these on config variable */
- int min_wait = 60 * 60;
- int max_wait = 24 * 60 * 60;
- int max_num_wait = 9; /* 5 waits =~ 1 day, then 1 day at a time */
- int wait_sec;
- int num_wait = 0;
char *msg;
- Dmsg0(130, "enter dir_ask_sysop_to_mount_next_volume\n");
+ Dmsg0(130, "enter dir_ask_sysop_to_mount_volume\n");
if (!jcr->VolumeName[0]) {
Mmsg0(&dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
return 0;
}
ASSERT(dev->dev_blocked);
- wait_sec = min_wait;
for ( ;; ) {
if (job_canceled(jcr)) {
Mmsg(&dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
jcr->Job, jcr->dev_name);
return 0;
}
- msg = _("Please mount");
- Jmsg(jcr, M_MOUNT, 0, _("%s Volume \"%s\" on Storage Device \"%s\" for Job %s\n"),
- msg, jcr->VolumeName, jcr->dev_name, jcr->Job);
- Dmsg3(190, "Mount %s on %s for Job %s\n",
- jcr->VolumeName, jcr->dev_name, jcr->Job);
+
+ /*
+ * If we have a valid volume name and we are not
+ * removable media, return now, or if we have a
+ * Slot for an autochanger, otherwise wait
+ * for the operator to mount the media.
+ */
+ if ((jcr->VolumeName[0] && !dev_cap(dev, CAP_REM) && dev_cap(dev, CAP_LABEL)) ||
+ (jcr->VolumeName[0] && jcr->VolCatInfo.Slot)) {
+ Dmsg0(100, "Return 1 from mount without wait.\n");
+ return 1;
+ }
+
+ if (!dev->poll) {
+ msg = _("Please mount");
+ Jmsg(jcr, M_MOUNT, 0, _("%s Volume \"%s\" on Storage Device \"%s\" for Job %s\n"),
+ msg, jcr->VolumeName, jcr->dev_name, jcr->Job);
+ Dmsg3(190, "Mount %s on %s for Job %s\n",
+ jcr->VolumeName, jcr->dev_name, jcr->Job);
+ }
jcr->JobStatus = JS_WaitMount;
dir_send_job_status(jcr);
- stat = wait_for_sysop(jcr, dev, wait_sec); /* wait on device */
+ stat = wait_for_sysop(jcr, dev); /* wait on device */
+ if (dev->poll) {
+ Dmsg1(200, "Poll timeout in mount vol on device %s\n", dev_name(dev));
+ Dmsg1(200, "Blocked=%d\n", dev->dev_blocked);
+ return 1;
+ }
if (stat == ETIMEDOUT) {
- wait_sec *= 2; /* double wait time */
- if (wait_sec > max_wait) { /* but not longer than maxtime */
- wait_sec = max_wait;
- }
- num_wait++;
- if (num_wait >= max_num_wait) {
+ if (!double_dev_wait_time(dev)) {
Mmsg(&dev->errmsg, _("Gave up waiting to mount Storage Device \"%s\" for Job %s\n"),
- jcr->dev_name, jcr->Job);
+ dev_name(dev), jcr->Job);
Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
Dmsg1(190, "Gave up waiting on device %s\n", dev_name(dev));
return 0; /* exceeded maximum waits */
strerror(stat));
}
Dmsg1(190, "Someone woke me for device %s\n", dev_name(dev));
-
- /* Restart wait counters */
- wait_sec = min_wait;
- num_wait = 0;
break;
}
set_jcr_job_status(jcr, JS_Running);
dir_send_job_status(jcr);
- Dmsg0(130, "leave dir_ask_sysop_to_mount_next_volume\n");
+ Dmsg0(130, "leave dir_ask_sysop_to_mount_volume\n");
return 1;
}
/*
* Wait for SysOp to mount a tape
*/
-static int wait_for_sysop(JCR *jcr, DEVICE *dev, int wait_sec)
+static int wait_for_sysop(JCR *jcr, DEVICE *dev)
{
struct timeval tv;
struct timezone tz;
struct timespec timeout;
- int dev_blocked;
- time_t start = time(NULL);
time_t last_heartbeat = 0;
+ time_t first_start = time(NULL);
int stat = 0;
int add_wait;
+ bool unmounted;
+ P(dev->mutex);
+ unmounted = (dev->dev_blocked == BST_UNMOUNTED) ||
+ (dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
+
+ dev->poll = false;
/*
- * Wait requested time (wait_sec). However, we also wake up every
+ * Wait requested time (dev->rem_wait_sec). However, we also wake up every
* HB_TIME seconds and send a heartbeat to the FD and the Director
* to keep stateful firewalls from closing them down while waiting
* for the operator.
*/
- add_wait = wait_sec;
+ add_wait = dev->rem_wait_sec;
if (me->heartbeat_interval && add_wait > me->heartbeat_interval) {
add_wait = me->heartbeat_interval;
}
+ if (!unmounted && dev->vol_poll_interval && add_wait > dev->vol_poll_interval) {
+ add_wait = dev->vol_poll_interval;
+ }
gettimeofday(&tv, &tz);
timeout.tv_nsec = tv.tv_usec * 1000;
timeout.tv_sec = tv.tv_sec + add_wait;
- P(dev->mutex);
- dev_blocked = dev->dev_blocked;
- dev->dev_blocked = BST_WAITING_FOR_SYSOP; /* indicate waiting for mount */
+ if (!unmounted) {
+ dev->dev_prev_blocked = dev->dev_blocked;
+ dev->dev_blocked = BST_WAITING_FOR_SYSOP; /* indicate waiting for mount */
+ }
for ( ; !job_canceled(jcr); ) {
- time_t now;
+ time_t now, start;
Dmsg3(100, "I'm going to sleep on device %s. HB=%d wait=%d\n", dev_name(dev),
- (int)me->heartbeat_interval, wait_sec);
+ (int)me->heartbeat_interval, dev->wait_sec);
+ start = time(NULL);
stat = pthread_cond_timedwait(&dev->wait_next_vol, &dev->mutex, &timeout);
Dmsg1(100, "Wokeup from sleep on device stat=%d\n", stat);
now = time(NULL);
+ dev->rem_wait_sec -= (now - start);
/* Note, this always triggers the first time. We want that. */
if (me->heartbeat_interval) {
}
}
- if (dev->dev_blocked == BST_MOUNT) { /* mount request ? */
- stat = 0;
- break;
- }
+ /*
+ * Check if user unmounted the device while we were waiting
+ */
+ unmounted = (dev->dev_blocked == BST_UNMOUNTED) ||
+ (dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
if (stat != ETIMEDOUT) { /* we blocked the device */
break; /* on error return */
}
- if (now - start >= wait_sec) { /* on exceeding wait time return */
+ if (dev->rem_wait_sec <= 0) { /* on exceeding wait time return */
Dmsg0(100, "Exceed wait time.\n");
break;
}
- add_wait = wait_sec - (now - start);
+
+ if (!unmounted && dev->vol_poll_interval &&
+ (now - first_start >= dev->vol_poll_interval)) {
+ Dmsg1(200, "In wait blocked=%d\n", dev->dev_blocked);
+ dev->poll = true;
+ break;
+ }
+ /*
+ * Check if user mounted the device while we were waiting
+ */
+ if (dev->dev_blocked == BST_MOUNT) { /* mount request ? */
+ stat = 0;
+ break;
+ }
+
+ add_wait = dev->wait_sec - (now - start);
+ if (add_wait < 0) {
+ add_wait = 0;
+ }
if (me->heartbeat_interval && add_wait > me->heartbeat_interval) {
add_wait = me->heartbeat_interval;
}
Dmsg1(100, "Additional wait %d sec.\n", add_wait);
}
- dev->dev_blocked = dev_blocked; /* restore entry state */
+ if (!unmounted) {
+ dev->dev_blocked = dev->dev_prev_blocked; /* restore entry state */
+ }
V(dev->mutex);
return stat;
}
int dir_find_next_appendable_volume(JCR *jcr) { return 1;}
int dir_update_volume_info(JCR *jcr, DEVICE *dev, int relabel) { return 1; }
int dir_create_jobmedia_record(JCR *jcr) { return 1; }
-int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
+int dir_ask_sysop_to_create_appendable_volume(JCR *jcr, DEVICE *dev) { 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_find_next_appendable_volume(JCR *jcr) { return 1;}
int dir_update_volume_info(JCR *jcr, DEVICE *dev, int relabel) { return 1; }
int dir_create_jobmedia_record(JCR *jcr) { return 1; }
-int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
+int dir_ask_sysop_to_create_appendable_volume(JCR *jcr, DEVICE *dev) { return 1; }
int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
int dir_send_job_status(JCR *jcr) {return 1;}
block->write_failed = true;
if (weof_dev(dev, 1) != 0) { /* end tape */
Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
+ dev->VolCatInfo.VolCatErrors++;
}
/* Don't do update after second EOF or file count will be wrong */
Dmsg0(100, "dir_update_volume_info\n");
dir_update_volume_info(jcr, dev, 0);
if (dev_cap(dev, CAP_TWOEOF) && weof_dev(dev, 1) != 0) { /* write eof */
Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
+ dev->VolCatInfo.VolCatErrors++;
}
dev->state |= (ST_EOF | ST_EOT | ST_WEOT);
return 0;
if (weof_dev(dev, 1) != 0) { /* write eof */
Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
block->write_failed = true;
+ dev->VolCatInfo.VolCatErrors++;
dev->state |= (ST_EOF | ST_EOT | ST_WEOT);
Dmsg0(100, "dir_update_volume_info\n");
dev->VolCatInfo.VolCatFiles = dev->file;
block->write_failed = true;
if (weof_dev(dev, 1) != 0) { /* end the tape */
+ dev->VolCatInfo.VolCatErrors++;
Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
}
Dmsg0(100, "dir_update_volume_info\n");
dev->VolCatInfo.VolCatFiles = dev->file;
dir_update_volume_info(jcr, dev, 0);
if (dev_cap(dev, CAP_TWOEOF) && weof_dev(dev, 1) != 0) { /* end the tape */
+ dev->VolCatInfo.VolCatErrors++;
Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
}
dev->state |= (ST_EOF | ST_EOT | ST_WEOT);
int dir_find_next_appendable_volume(JCR *jcr) { return 1;}
int dir_update_volume_info(JCR *jcr, DEVICE *dev, int relabel) { return 1; }
int dir_create_jobmedia_record(JCR *jcr) { return 1; }
-int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
+int dir_ask_sysop_to_create_appendable_volume(JCR *jcr, DEVICE *dev) { 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_find_next_appendable_volume(JCR *jcr) { return 1;}
int dir_update_volume_info(JCR *jcr, DEVICE *dev, int relabel) { return 1; }
int dir_create_jobmedia_record(JCR *jcr) { return 1; }
-int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
+int dir_ask_sysop_to_create_appendable_volume(JCR *jcr, DEVICE *dev) { return 1; }
int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
int dir_send_job_status(JCR *jcr) {return 1;}
return 1;
}
-int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev)
+int dir_ask_sysop_to_create_appendable_volume(JCR *jcr, DEVICE *dev)
{
/* Close device so user can use autochanger if desired */
if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
dev->max_rewind_wait = device->max_rewind_wait;
dev->max_open_wait = device->max_open_wait;
dev->max_open_vols = device->max_open_vols;
+ dev->vol_poll_interval = device->vol_poll_interval;
+ /* Sanity check */
+ if (dev->vol_poll_interval && dev->vol_poll_interval < 60) {
+ dev->vol_poll_interval = 60;
+ }
dev->device = device;
if (tape) {
}
return jcr->next_dev;
}
+
+/*
+ * This routine initializes the device wait timers
+ */
+void init_dev_wait_timers(DEVICE *dev)
+{
+ /* ******FIXME******* put these on config variables */
+ dev->min_wait = 60 * 60;
+ dev->max_wait = 24 * 60 * 60;
+ dev->max_num_wait = 9; /* 5 waits =~ 1 day, then 1 day at a time */
+ dev->wait_sec = dev->min_wait;
+ dev->rem_wait_sec = dev->wait_sec;
+ dev->num_wait = 0;
+ dev->poll = false;
+ dev->BadVolName[0] = 0;
+}
+
+/*
+ * Returns: true if time doubled
+ * false if max time expired
+ */
+bool double_dev_wait_time(DEVICE *dev)
+{
+ dev->wait_sec *= 2; /* double wait time */
+ if (dev->wait_sec > dev->max_wait) { /* but not longer than maxtime */
+ dev->wait_sec = dev->max_wait;
+ }
+ dev->num_wait++;
+ dev->rem_wait_sec = dev->wait_sec;
+ if (dev->num_wait >= dev->max_num_wait) {
+ return false;
+ }
+ return true;
+}
typedef struct s_steal_lock {
pthread_t no_wait_id; /* id of no wait thread */
int dev_blocked; /* state */
+ int dev_prev_blocked; /* previous blocked state */
} bsteal_lock_t;
struct DEVRES; /* Device resource defined in stored_conf.h */
pthread_cond_t wait_next_vol; /* wait for tape to be mounted */
pthread_t no_wait_id; /* this thread must not wait */
int dev_blocked; /* set if we must wait (i.e. change tape) */
+ int dev_prev_blocked; /* previous blocked state */
int num_waiting; /* number of threads waiting */
int num_writers; /* number of writing threads */
int use_count; /* usage count on this device */
uint32_t max_rewind_wait; /* max secs to allow for rewind */
uint32_t max_open_wait; /* max secs to allow for open */
uint32_t max_open_vols; /* max simultaneous open volumes */
+ utime_t vol_poll_interval; /* interval between polling Vol mount */
DEVRES *device; /* pointer to Device Resource */
btimer_id tid; /* timer id */
VOLUME_CAT_INFO VolCatInfo; /* Volume Catalog Information */
VOLUME_LABEL VolHdr; /* Actual volume label */
+ /* Device wait times ***FIXME*** look at durations */
+ char BadVolName[MAX_NAME_LENGTH]; /* Last wrong Volume mounted */
+ bool poll; /* set to poll Volume */
+ int min_wait;
+ int max_wait;
+ int max_num_wait;
+ int wait_sec;
+ int rem_wait_sec;
+ int num_wait;
};
Dmsg4(100, "steal lock. old=%d new=%d from %s:%d\n", dev->dev_blocked, state,
file, line);
hold->dev_blocked = dev->dev_blocked;
+ hold->dev_prev_blocked = dev->dev_prev_blocked;
hold->no_wait_id = dev->no_wait_id;
dev->dev_blocked = state;
dev->no_wait_id = pthread_self();
dev->dev_blocked, hold->dev_blocked, file, line);
P(dev->mutex);
dev->dev_blocked = hold->dev_blocked;
+ dev->dev_prev_blocked = hold->dev_prev_blocked;
dev->no_wait_id = hold->no_wait_id;
}
P(dev->mutex); /* Use P to avoid indefinite block */
if (!(dev->state & ST_OPENED)) {
Dmsg0(90, "Device already unmounted\n");
- bnet_fsend(dir, _("3901 Device %s is already unmounted.\n"), dev_name(dev));
+ bnet_fsend(dir, _("3901 Device \"%s\" is already unmounted.\n"), dev_name(dev));
} else if (dev->dev_blocked == BST_WAITING_FOR_SYSOP) {
Dmsg2(90, "%d waiter dev_block=%d. doing unmount\n", dev->num_waiting,
offline_or_rewind_dev(dev);
force_close_dev(dev);
dev->dev_blocked = BST_UNMOUNTED_WAITING_FOR_SYSOP;
- bnet_fsend(dir, _("3001 Device %s unmounted.\n"), dev_name(dev));
+ bnet_fsend(dir, _("3001 Device \"%s\" unmounted.\n"), dev_name(dev));
} else if (dev->dev_blocked == BST_DOING_ACQUIRE) {
- bnet_fsend(dir, _("3902 Device %s is busy in acquire.\n"), dev_name(dev));
+ bnet_fsend(dir, _("3902 Device \"%s\" is busy in acquire.\n"), dev_name(dev));
} else if (dev->dev_blocked == BST_WRITING_LABEL) {
- bnet_fsend(dir, _("3903 Device %s is being labeled.\n"), dev_name(dev));
+ bnet_fsend(dir, _("3903 Device \"%s\" is being labeled.\n"), dev_name(dev));
} else if (dev_state(dev, ST_READ) || dev->num_writers) {
if (dev_state(dev, ST_READ)) {
Dmsg0(90, "Device in read mode\n");
- bnet_fsend(dir, _("3904 Device %s is busy with 1 reader.\n"), dev_name(dev));
+ bnet_fsend(dir, _("3904 Device \"%s\" is busy reading.\n"), dev_name(dev));
} else {
Dmsg1(90, "Device busy with %d writers\n", dev->num_writers);
bnet_fsend(dir, _("3905 Device %s is busy with %d writer(s).\n"),
dev = jcr->device->dev;
P(dev->mutex); /* Use P to avoid indefinite block */
- if (!(dev->state & ST_OPENED)) {
+ if (!dev_state(dev, ST_OPENED)) {
if (open_dev(dev, NULL, READ_WRITE) < 0) {
bnet_fsend(dir, _("3994 Connot open device: %s\n"), strerror_dev(dev));
} else {
block = new_block(dev);
/* Ensure that the device is open -- autoload_device() closes it */
- for ( ; !(dev->state & ST_OPENED); ) {
+ for ( ; !dev_state(dev, ST_OPENED); ) {
if (open_dev(dev, jcr->VolumeName, READ_WRITE) < 0) {
- bnet_fsend(dir, _("3910 Unable to open device %s. ERR=%s\n"),
+ bnet_fsend(dir, _("3910 Unable to open device \"%s\". ERR=%s\n"),
dev_name(dev), strerror_dev(dev));
goto bail_out;
}
dev->state &= ~ST_LABEL; /* force read of label */
switch (read_dev_volume_label(jcr, dev, block)) {
case VOL_OK:
- bnet_fsend(dir, _("3001 Volume=%s Slot=%d\n"), dev->VolHdr.VolName, Slot);
+ bnet_fsend(dir, _("3001 Volume=\"%s\" Slot=%d\n"), dev->VolHdr.VolName, Slot);
Dmsg1(100, "Volume: %s\n", dev->VolHdr.VolName);
break;
default:
#include "bacula.h" /* pull in global headers */
#include "stored.h" /* pull in Storage Deamon headers */
-/* Forward referenced routines */
-static void mark_volume_in_error(JCR *jcr, DEVICE *dev);
-
-
/*
* If release is set, we rewind the current volume,
* which we no longer want, and ask the user (console)
Dmsg0(100, "Enter mount_next_volume()\n");
+ init_dev_wait_timers(dev);
+
/*
* Attempt to mount the next volume. If something non-fatal goes
* wrong, we come back here to re-try (new op messages, re-read
* Volume, ...)
*/
mount_next_vol:
- if (retry++ > 5) {
+ /* Ignore retry if this is poll request */
+ if (!dev->poll && retry++ > 8) {
Jmsg(jcr, M_FATAL, 0, _("Too many errors trying to mount device %s.\n"),
dev_name(dev));
return 0;
* in jcr->VolCatInfo
*/
Dmsg0(100, "Before dir_find_next\n");
- if (!dir_find_next_appendable_volume(jcr)) {
+ while (!dir_find_next_appendable_volume(jcr)) {
Dmsg0(100, "not dir_find_next\n");
- if (!dir_ask_sysop_to_mount_next_volume(jcr, dev)) {
+ if (!dir_ask_sysop_to_create_appendable_volume(jcr, dev)) {
return 0;
}
}
Dmsg2(100, "Ask=%d autochanger=%d\n", ask, autochanger);
release = true; /* release next time if we "recurse" */
- if (ask && !dir_ask_sysop_to_mount_next_volume(jcr, dev)) {
+ if (ask && !dir_ask_sysop_to_mount_volume(jcr, dev)) {
Dmsg0(100, "Error return ask_sysop ...\n");
return 0; /* error return */
}
VOLUME_CAT_INFO VolCatInfo;
Dmsg1(100, "Vol NAME Error Name=%s\n", jcr->VolumeName);
+ /* If polling and got a previous bad name, ignore it */
+ if (dev->poll && strcmp(dev->BadVolName, dev->VolHdr.VolName) == 0) {
+ ask = true;
+ Dmsg1(200, "Vol Name error supress due to poll. Name=%s\n",
+ jcr->VolumeName);
+ goto mount_next_vol;
+ }
/*
* OK, we got a different volume mounted. First save the
* requested Volume info (jcr) structure, then query if
/* Check if this is a valid Volume in the pool */
pm_strcpy(&jcr->VolumeName, dev->VolHdr.VolName);
if (!dir_get_volume_info(jcr, GET_VOL_INFO_FOR_WRITE)) {
+ bstrncpy(dev->BadVolName, dev->VolHdr.VolName, sizeof(dev->BadVolName));
Jmsg(jcr, M_WARNING, 0, _("Director wanted Volume \"%s\".\n"
" Current Volume \"%s\" not acceptable because:\n"
" %s"),
case VOL_NO_MEDIA:
default:
/* Send error message */
- Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
+ if (!dev->poll) {
+ Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
+ } else {
+ Dmsg1(200, "Msg suppressed by poll: %s\n", jcr->errmsg);
+ }
ask = true;
goto mount_next_vol;
}
return 1;
}
-static void mark_volume_in_error(JCR *jcr, DEVICE *dev)
+/*
+ * Mark volume in error in catalog
+ */
+void mark_volume_in_error(JCR *jcr, DEVICE *dev)
{
Jmsg(jcr, M_INFO, 0, _("Marking Volume \"%s\" in Error in Catalog.\n"),
jcr->VolumeName);
int dir_get_volume_info(JCR *jcr, enum get_vol_info_rw);
int dir_find_next_appendable_volume(JCR *jcr);
int dir_update_volume_info(JCR *jcr, DEVICE *dev, int label);
-int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev);
+int dir_ask_sysop_to_create_appendable_volume(JCR *jcr, DEVICE *dev);
int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev);
int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec);
int dir_send_job_status(JCR *jcr);
int dev_can_write(DEVICE *dev);
int offline_or_rewind_dev(DEVICE *dev);
int reposition_dev(DEVICE *dev, uint32_t file, uint32_t block);
+void init_dev_wait_timers(DEVICE *dev);
+bool double_dev_wait_time(DEVICE *dev);
/* Get info about device */
/* From device.c */
int open_device(DEVICE *dev);
int fixup_device_block_write_error(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-void _lock_device(char *file, int line, DEVICE *dev);
-void _unlock_device(char *file, int line, DEVICE *dev);
-void _block_device(char *file, int line, DEVICE *dev, int state);
-void _unblock_device(char *file, int line, DEVICE *dev);
-void _steal_device_lock(char *file, int line, DEVICE *dev, bsteal_lock_t *hold, int state);
-void _give_back_device_lock(char *file, int line, DEVICE *dev, bsteal_lock_t *hold);
-void set_new_volume_parameters(JCR *jcr, DEVICE *dev);
-void set_new_file_parameters(JCR *jcr, DEVICE *dev);
-int device_is_unmounted(DEVICE *dev);
+void _lock_device(char *file, int line, DEVICE *dev);
+void _unlock_device(char *file, int line, DEVICE *dev);
+void _block_device(char *file, int line, DEVICE *dev, int state);
+void _unblock_device(char *file, int line, DEVICE *dev);
+void _steal_device_lock(char *file, int line, DEVICE *dev, bsteal_lock_t *hold, int state);
+void _give_back_device_lock(char *file, int line, DEVICE *dev, bsteal_lock_t *hold);
+void set_new_volume_parameters(JCR *jcr, DEVICE *dev);
+void set_new_file_parameters(JCR *jcr, DEVICE *dev);
+int device_is_unmounted(DEVICE *dev);
/* From dircmd.c */
void *connection_request(void *arg);
int mount_next_write_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release);
int mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
void release_volume(JCR *jcr, DEVICE *dev);
+void mark_volume_in_error(JCR *jcr, DEVICE *dev);
/* From autochanger.c */
int autoload_device(JCR *jcr, DEVICE *dev, int writing, BSOCK *dir);
void create_vol_list(JCR *jcr);
/* From record.c */
-char *FI_to_ascii(int fi);
-char *stream_to_ascii(int stream, int fi);
-int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
-int can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
-int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec);
+char *FI_to_ascii(int fi);
+char *stream_to_ascii(int stream, int fi);
+int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
+int can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
+int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec);
DEV_RECORD *new_record();
-void free_record(DEV_RECORD *rec);
+void free_record(DEV_RECORD *rec);
/* From read_record.c */
int read_records(JCR *jcr, DEVICE *dev,
* List devices
*/
LockRes();
- for (device=NULL; (device=(DEVRES *)GetNextRes(R_DEVICE, (RES *)device)); ) {
+ foreach_res(device, R_DEVICE) {
for (dev=device->dev; dev; dev=dev->next) {
- if (dev->state & ST_OPENED) {
- if (dev->state & ST_LABEL) {
- bnet_fsend(user, _("Device %s is mounted with Volume \"%s\"\n"),
+ if (dev_state(dev, ST_OPENED)) {
+ if (dev_state(dev, ST_LABEL)) {
+ bnet_fsend(user, _("Device \"%s\" is mounted with Volume \"%s\"\n"),
dev_name(dev), dev->VolHdr.VolName);
} else {
- bnet_fsend(user, _("Device %s open but no Bacula volume is mounted.\n"), dev_name(dev));
+ bnet_fsend(user, _("Device \"%s\" open but no Bacula volume is mounted.\n"), dev_name(dev));
}
send_blocked_status(jcr, dev);
- if (dev->state & ST_APPEND) {
+ if (dev_state(dev, ST_APPEND)) {
bpb = dev->VolCatInfo.VolCatBlocks;
if (bpb <= 0) {
bpb = 1;
edit_uint64_with_commas(dev->block_num, b2));
} else {
- bnet_fsend(user, _("Device %s is not open.\n"), dev_name(dev));
+ bnet_fsend(user, _("Device \"%s\" is not open.\n"), dev_name(dev));
send_blocked_status(jcr, dev);
}
}
job_type_to_str(jcr->JobType), jcr->Job);
}
if (jcr->device) {
- bnet_fsend(user, _("%s %s job %s using Volume \"%s\" on device %s\n"),
+ bnet_fsend(user, _("%s %s job %s using Volume \"%s\" on device \"%s\"\n"),
job_level_to_str(jcr->JobLevel),
job_type_to_str(jcr->JobType),
jcr->Job,
{"maximumchangerwait", store_pint, ITEM(res_dev.max_changer_wait), 0, ITEM_DEFAULT, 5 * 60},
{"maximumopenwait", store_pint, ITEM(res_dev.max_open_wait), 0, ITEM_DEFAULT, 5 * 60},
{"maximumopenvolumes", store_pint, ITEM(res_dev.max_open_vols), 0, ITEM_DEFAULT, 1},
+ {"volumepollinterval", store_time, ITEM(res_dev.vol_poll_interval), 0, 0, 0},
{"offlineonunmount", store_yesno, ITEM(res_dev.cap_bits), CAP_OFFLINEUNMOUNT, ITEM_DEFAULT, 0},
{"maximumrewindwait", store_pint, ITEM(res_dev.max_rewind_wait), 0, ITEM_DEFAULT, 5 * 60},
{"minimumblocksize", store_pint, ITEM(res_dev.min_block_size), 0, 0, 0},
uint32_t min_block_size; /* min block size */
uint32_t max_block_size; /* max block size */
uint32_t max_volume_jobs; /* max jobs to put on one volume */
+ utime_t vol_poll_interval; /* interval between polling volume during mount */
int64_t max_volume_files; /* max files to put on one volume */
int64_t max_volume_size; /* max bytes to put on one volume */
int64_t max_file_size; /* max file size in bytes */
#undef VERSION
#define VERSION "1.33"
#define VSTRING "1"
-#define BDATE "06 Jan 2004"
-#define LSMDATE "06Jan04"
+#define BDATE "10 Jan 2004"
+#define LSMDATE "10Jan04"
/* Debug flags */
#undef DEBUG