/*
* Copyright (c) 1998,1999,2000
* Traakan, Inc., Los Altos, CA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Project: NDMJOB
* Ident: $Id: $
*
* Description:
*
*/
#include "ndmagents.h"
#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
int
ndmca_media_load_first (struct ndm_session *sess)
{
sess->control_acb.cur_media_ix = 0;
return ndmca_media_load_current (sess);
}
/*
* TODO: It would be nice that if a media entry has a problem
* during load_current() to just skip over it and proceed
* to the next one. It will be really annoying to have a
* long running backup terminate because of a write-protect
* or label-check error when there are perfectly good
* tapes available.
*/
int
ndmca_media_load_next (struct ndm_session *sess)
{
int n_media = sess->control_acb.job.media_tab.n_media;
if (sess->control_acb.cur_media_ix+1 >= n_media) {
ndmalogf (sess, 0, 0, "Out of tapes");
return -1;
}
sess->control_acb.cur_media_ix++;
return ndmca_media_load_current (sess);
}
int
ndmca_media_unload_last (struct ndm_session *sess)
{
return ndmca_media_unload_current(sess);
}
int
ndmca_media_change (struct ndm_session *sess)
{
int rc;
rc = ndmca_media_unload_current(sess);
if (rc) return rc;
rc = ndmca_media_load_next (sess);
if (rc) return rc;
return 0;
}
int
ndmca_media_load_seek (struct ndm_session *sess, unsigned long long pos)
{
struct ndm_control_agent *ca = &sess->control_acb;
struct ndm_job_param * job = &ca->job;
int n_media = job->media_tab.n_media;
struct ndmmedia * me;
int i;
for (i = 0; i < n_media; i++) {
me = &job->media_tab.media[i];
if (me->begin_offset <= pos && pos < me->end_offset)
break;
}
if (i >= n_media) {
ndmalogf (sess, 0, 0, "Seek to unspecified media");
return -1;
}
ca->cur_media_ix = i;
return ndmca_media_load_current (sess);
}
int
ndmca_media_load_current (struct ndm_session *sess)
{
struct ndm_control_agent *ca = &sess->control_acb;
struct ndm_job_param * job = &ca->job;
struct ndmmedia * me = &job->media_tab.media[ca->cur_media_ix];
int rc;
unsigned count;
if (job->have_robot) {
rc = ndmca_robot_load (sess, me->slot_addr);
if (rc) return rc;
}
me->media_used = 1;
rc = ndmca_media_open_tape (sess);
/*
* TODO: it would be nice to discriminate this and
* set flags accordingly. For example,
* indicate a write-protect tape.
*/
if (rc) {
me->media_open_error = 1;
/* if use_eject, this won't work */
if (job->have_robot) {
/* best-effort unload the robot */
ndmca_robot_unload (sess, me->slot_addr);
}
return rc;
}
ca->media_is_loaded = 1;
rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_REW, 1, 0);
if (rc) {
me->media_io_error = 1;
goto close_and_unload;
}
if (ca->is_label_op) {
if (ca->tape_mode == NDMP9_TAPE_RDWR_MODE)
me->media_written = 1; /* most likely */
return 0; /* ready to go */
}
if (me->valid_label) {
rc = ndmca_media_check_label (sess, 'm', me->label);
if (rc) {
if (rc == -1) {
me->label_io_error = 1;
} else if (rc == -2) {
me->label_read = 1;
me->label_mismatch = 1;
}
goto close_and_unload;
}
me->label_read = 1;
rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_REW, 1, 0);
if (rc) {
me->media_io_error = 1;
}
}
if (!me->valid_filemark) { /* synthetic */
me->valid_filemark = 1;
if (me->valid_label)
me->file_mark_offset = 1;
else
me->file_mark_offset = 0;
}
count = me->file_mark_offset;
if (count > 0) {
rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_FSF, count, 0);
if (rc) {
me->fmark_error = 1;
ndmca_media_mtio_tape (sess, NDMP9_MTIO_REW, 1, 0);
goto close_and_unload;
}
}
if (ca->tape_mode == NDMP9_TAPE_RDWR_MODE)
me->media_written = 1; /* most likely */
return 0;
close_and_unload:
me->media_io_error = 1;
ndmca_media_unload_best_effort (sess);
return rc;
}
int
ndmca_media_unload_current (struct ndm_session *sess)
{
struct ndm_control_agent *ca = &sess->control_acb;
struct ndm_job_param * job = &ca->job;
struct ndmmedia * me = &job->media_tab.media[ca->cur_media_ix];
int rc;
if (!ca->media_is_loaded)
return 0;
rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_REW, 1, 0);
if (rc) return rc;
if (ca->job.use_eject) {
rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_OFF, 1, 0);
if (rc) return rc;
}
rc = ndmca_media_close_tape (sess);
if (rc) return rc;
if (job->have_robot) {
rc = ndmca_robot_unload (sess, me->slot_addr);
if (rc) return rc;
}
ca->media_is_loaded = 0;
return 0;
}
int
ndmca_media_unload_best_effort (struct ndm_session *sess)
{
struct ndm_control_agent *ca = &sess->control_acb;
struct ndm_job_param * job = &ca->job;
struct ndmmedia * me = &job->media_tab.media[ca->cur_media_ix];
int errors = 0;
int rc;
if (!ca->media_is_loaded)
return 0;
rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_REW, 1, 0);
if (rc) errors++;
if (ca->job.use_eject) {
rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_OFF, 1, 0);
if (rc) errors++;
}
rc = ndmca_media_close_tape (sess);
if (rc) errors++;
if (job->have_robot) {
rc = ndmca_robot_unload (sess, me->slot_addr);
if (rc) errors++;
}
ca->media_is_loaded = 0;
return errors ? -1 : 0;
}
int
ndmca_media_open_tape (struct ndm_session *sess)
{
struct ndm_control_agent *ca = &sess->control_acb;
int rc;
unsigned int t;
ndmalogf (sess, 0, 1, "Opening tape drive %s %s",
ca->job.tape_device,
(ca->tape_mode == NDMP9_TAPE_RDWR_MODE)
? "read/write" : "read-only");
rc = -1;
for (t = 0; t <= ca->job.tape_timeout; t += 10) {
if (t > 0) {
ndmalogf (sess, 0, 1,
"Pausing ten seconds before retry (%d/%d)",
t, ca->job.tape_timeout);
sleep (10);
}
rc = ndmca_tape_open(sess);
if (rc == 0) break;
}
if (rc) {
/* should interpret the error */
ndmalogf (sess, 0, 0, "failed open tape drive %s %s",
ca->job.tape_device,
(ca->tape_mode == NDMP9_TAPE_RDWR_MODE)
? "read/write" : "read-only");
}
return rc;
}
int
ndmca_media_close_tape (struct ndm_session *sess)
{
struct ndm_control_agent *ca = &sess->control_acb;
int rc;
ndmalogf (sess, 0, 2, "Closing tape drive %s", ca->job.tape_device);
rc = ndmca_tape_close (sess);
return 0;
}
int
ndmca_media_mtio_tape (struct ndm_session *sess,
ndmp9_tape_mtio_op op, u_long count, u_long *resid)
{
int rc;
if (op == NDMP9_MTIO_REW) {
ndmalogf (sess, 0, 1, "Commanding tape drive to rewind");
} else if (op == NDMP9_MTIO_OFF) {
ndmalogf (sess, 0, 1,
"Commanding tape drive to eject (go offline)");
} else {
ndmalogf (sess, 0, 2, "Commanding tape drive to %s %d times",
ndmp9_tape_mtio_op_to_str (op),
count);
}
rc = ndmca_tape_mtio (sess, op, count, resid);
return rc;
}
int
ndmca_media_write_filemarks (struct ndm_session *sess)
{
int rc;
rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_EOF, 2, 0);
return rc;
}
/*
* Returns: 'm' for tape (media) label
* 'V' for volume label
* '?' for unknown content
* -1 for some kind of error
*/
int
ndmca_media_read_label (struct ndm_session *sess, char labbuf[])
{
char tape_read_buf[512];
int rc;
char * p;
char * q;
ndmalogf (sess, 0, 2, "Reading label");
*labbuf = 0;
rc = ndmca_tape_read (sess, tape_read_buf, 512);
if (rc == 0) {
p = tape_read_buf;
if (strncmp (p, "##ndmjob -m ", 12) == 0) {
p += 12;
rc = 'm';
} else if (strncmp (p, "##ndmjob -V ", 12) == 0) {
p += 12;
rc = 'V';
} else {
rc = '?';
p = 0;
}
if (p) {
q = labbuf;
while (*p && *p != '\n'
&& q < &labbuf[NDMMEDIA_LABEL_MAX-1]) {
*q++ = *p++;
}
*q = 0;
}
} else {
rc = -1;
}
return rc;
}
/*
* type is either 'm' or 'V', see above
*/
int
ndmca_media_write_label (struct ndm_session *sess, int type, char labbuf[])
{
int rc;
char buf[512];
char * p;
ndmalogf (sess, 0, 1, "Writing tape label '%s' type=%c", labbuf, type);
for (p = buf; p < &buf[512]; p++) *p = '#';
for (p = buf+63; p < &buf[512]; p += 64) *p = '\n';
sprintf (buf, "##ndmjob -%c %s", type, labbuf);
for (p = buf; *p; p++) continue;
*p++ = '\n';
rc = ndmca_tape_write (sess, buf, 512);
return rc;
}
/*
* type is either 'm' or 'V', see above
*/
int
ndmca_media_check_label (struct ndm_session *sess, int type, char labbuf[])
{
int rc;
char mylabbuf[NDMMEDIA_LABEL_MAX];
ndmalogf (sess, 0, 1, "Checking tape label, expect '%s'", labbuf);
rc = ndmca_media_read_label (sess, mylabbuf);
if (rc < 0) {
ndmalogf (sess, 0, 0, "Label read error");
return -1;
}
if (rc != type || strcmp (labbuf, mylabbuf) != 0) {
ndmalogf (sess, 0, 0,
"Label mismatch, expected -%c'%s', got -%c'%s'",
type, labbuf, rc, mylabbuf);
return -2;
}
return 0;
}
int
ndmca_media_verify (struct ndm_session *sess)
{
struct ndm_control_agent *ca = &sess->control_acb;
struct ndm_job_param * job = &ca->job;
int rc;
if (job->have_robot)
return 0; /* not much we can do in advance */
rc = ndmca_robot_verify_media (sess);
if (rc == 0)
return rc;
ndmca_media_tattle (sess);
return -1;
}
int
ndmca_media_tattle (struct ndm_session *sess)
{
struct ndm_control_agent *ca = &sess->control_acb;
struct ndm_job_param * job = &ca->job;
int i, line, nline;
for (i = 0; i < job->media_tab.n_media; i++) {
struct ndmmedia * me = &job->media_tab.media[i];
char buf[80];
nline = ndmmedia_pp (me, 0, buf);
ndmalogf (sess, 0, 1, "media #%d %s", i+1, buf);
for (line = 1; line < nline; line++) {
nline = ndmmedia_pp (me, line, buf);
ndmalogf (sess, 0, 2, " %s", buf);
}
}
return 0;
}
/*
* Determine the current byte offset in the current
* tape file.
*/
unsigned long long
ndmca_media_capture_tape_offset (struct ndm_session *sess)
{
struct ndm_control_agent *ca = &sess->control_acb;
struct ndm_job_param * job = &ca->job;
int rc;
unsigned long long off;
rc = ndmca_tape_get_state(sess);
if (rc) return NDMP_LENGTH_INFINITY; /* invalid? */
if (!ca->tape_state.blockno.valid)
return NDMP_LENGTH_INFINITY; /* invalid? */
off = ca->tape_state.blockno.value;
off *= job->record_size;
return off;
}
int
ndmca_media_capture_mover_window (struct ndm_session *sess)
{
struct ndm_control_agent *ca = &sess->control_acb;
struct ndmlog * ixlog = &ca->job.index_log;
struct ndm_job_param * job = &ca->job;
struct ndmmedia * me = &job->media_tab.media[ca->cur_media_ix];
ndmp9_mover_state ms = ca->mover_state.state;
ndmp9_mover_pause_reason pr = ca->mover_state.pause_reason;
char buf[100];
unsigned long long wlen;
if (ms == NDMP9_MOVER_STATE_PAUSED) {
if (pr == NDMP9_MOVER_PAUSE_SEEK) {
/* end-of-window */
} else if (pr == NDMP9_MOVER_PAUSE_EOM) {
me->media_eom = 1; /* tape full */
} else if (pr == NDMP9_MOVER_PAUSE_EOF) {
me->media_eof = 1;
} else if (pr == NDMP9_MOVER_PAUSE_MEDIA_ERROR) {
me->media_io_error = 1;
} else {
/* what */
}
} else if (ms == NDMP9_MOVER_STATE_HALTED) {
/* if tape_mode == READ, this may not actually be the window */
/* TODO: should STATE_LISTEN be considered? */
} else {
ndmalogf (sess, 0, 1,
"Warning: capturing offset w/o quiescent mover");
}
wlen = ca->mover_state.record_num;
wlen *= job->record_size;
wlen -= job->last_w_offset; /* want the size of this image */
me->valid_n_bytes = 1;
me->nb_determined = 1;
me->n_bytes = wlen;
ndmmedia_pp (me, 0, buf);
ndmlogf (ixlog, "CM", 0, "%02d %s", ca->cur_media_ix+1, buf);
return 0;
}
int
ndmca_media_calculate_windows (struct ndm_session *sess)
{
return 0;
}
int
ndmca_media_calculate_offsets (struct ndm_session *sess)
{
struct ndm_control_agent *ca = &sess->control_acb;
struct ndm_job_param * job = &ca->job;
int n_media = job->media_tab.n_media;
struct ndmmedia * me;
int i;
unsigned long long offset = 0;
for (i = 0; i < n_media; i++) {
me = &job->media_tab.media[i];
me->begin_offset = offset;
if (me->valid_n_bytes) {
offset += me->n_bytes;
me->end_offset = offset;
} else {
me->n_bytes = NDMP_LENGTH_INFINITY;
me->end_offset = NDMP_LENGTH_INFINITY;
/* offset unchanged */
}
}
return 0;
}
int
ndmca_media_set_window_current (struct ndm_session *sess)
{
struct ndm_control_agent *ca = &sess->control_acb;
struct ndm_job_param * job = &ca->job;
struct ndmmedia * me = &job->media_tab.media[ca->cur_media_ix];
int rc;
rc = ndmca_mover_set_window (sess, me->begin_offset, me->n_bytes);
if (rc == 0)
job->last_w_offset = me->begin_offset;
return rc;
}
#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */