2 * Copyright (c) 1998,1999,2000
3 * Traakan, Inc., Los Altos, CA
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice unmodified, this list of conditions, and the following
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 #include "ndmagents.h"
41 #ifndef NDMOS_OPTION_NO_CONTROL_AGENT
45 ndmca_media_load_first (struct ndm_session *sess)
47 sess->control_acb.cur_media_ix = 0;
48 return ndmca_media_load_current (sess);
52 * TODO: It would be nice that if a media entry has a problem
53 * during load_current() to just skip over it and proceed
54 * to the next one. It will be really annoying to have a
55 * long running backup terminate because of a write-protect
56 * or label-check error when there are perfectly good
60 ndmca_media_load_next (struct ndm_session *sess)
62 int n_media = sess->control_acb.job.media_tab.n_media;
64 if (sess->control_acb.cur_media_ix+1 >= n_media) {
65 ndmalogf (sess, 0, 0, "Out of tapes");
68 sess->control_acb.cur_media_ix++;
69 return ndmca_media_load_current (sess);
73 ndmca_media_unload_last (struct ndm_session *sess)
75 return ndmca_media_unload_current(sess);
79 ndmca_media_change (struct ndm_session *sess)
83 rc = ndmca_media_unload_current(sess);
86 rc = ndmca_media_load_next (sess);
93 ndmca_media_load_seek (struct ndm_session *sess, unsigned long long pos)
95 struct ndm_control_agent *ca = &sess->control_acb;
96 struct ndm_job_param * job = &ca->job;
97 int n_media = job->media_tab.n_media;
101 for (i = 0; i < n_media; i++) {
102 me = &job->media_tab.media[i];
103 if (me->begin_offset <= pos && pos < me->end_offset)
108 ndmalogf (sess, 0, 0, "Seek to unspecified media");
112 ca->cur_media_ix = i;
113 return ndmca_media_load_current (sess);
122 ndmca_media_load_current (struct ndm_session *sess)
124 struct ndm_control_agent *ca = &sess->control_acb;
125 struct ndm_job_param * job = &ca->job;
126 struct ndmmedia * me = &job->media_tab.media[ca->cur_media_ix];
130 if (job->have_robot) {
131 rc = ndmca_robot_load (sess, me->slot_addr);
137 rc = ndmca_media_open_tape (sess);
139 * TODO: it would be nice to discriminate this and
140 * set flags accordingly. For example,
141 * indicate a write-protect tape.
144 me->media_open_error = 1;
146 /* if use_eject, this won't work */
147 if (job->have_robot) {
148 /* best-effort unload the robot */
149 ndmca_robot_unload (sess, me->slot_addr);
154 ca->media_is_loaded = 1;
156 rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_REW, 1, 0);
158 me->media_io_error = 1;
159 goto close_and_unload;
162 if (ca->is_label_op) {
163 if (ca->tape_mode == NDMP9_TAPE_RDWR_MODE)
164 me->media_written = 1; /* most likely */
166 return 0; /* ready to go */
169 if (me->valid_label) {
170 rc = ndmca_media_check_label (sess, 'm', me->label);
173 me->label_io_error = 1;
174 } else if (rc == -2) {
176 me->label_mismatch = 1;
178 goto close_and_unload;
182 rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_REW, 1, 0);
184 me->media_io_error = 1;
188 if (!me->valid_filemark) { /* synthetic */
189 me->valid_filemark = 1;
191 me->file_mark_offset = 1;
193 me->file_mark_offset = 0;
196 count = me->file_mark_offset;
199 rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_FSF, count, 0);
202 ndmca_media_mtio_tape (sess, NDMP9_MTIO_REW, 1, 0);
203 goto close_and_unload;
207 if (ca->tape_mode == NDMP9_TAPE_RDWR_MODE)
208 me->media_written = 1; /* most likely */
213 me->media_io_error = 1;
214 ndmca_media_unload_best_effort (sess);
219 ndmca_media_unload_current (struct ndm_session *sess)
221 struct ndm_control_agent *ca = &sess->control_acb;
222 struct ndm_job_param * job = &ca->job;
223 struct ndmmedia * me = &job->media_tab.media[ca->cur_media_ix];
226 if (!ca->media_is_loaded)
229 rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_REW, 1, 0);
232 if (ca->job.use_eject) {
233 rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_OFF, 1, 0);
237 rc = ndmca_media_close_tape (sess);
240 if (job->have_robot) {
241 rc = ndmca_robot_unload (sess, me->slot_addr);
245 ca->media_is_loaded = 0;
251 ndmca_media_unload_best_effort (struct ndm_session *sess)
253 struct ndm_control_agent *ca = &sess->control_acb;
254 struct ndm_job_param * job = &ca->job;
255 struct ndmmedia * me = &job->media_tab.media[ca->cur_media_ix];
259 if (!ca->media_is_loaded)
262 rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_REW, 1, 0);
265 if (ca->job.use_eject) {
266 rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_OFF, 1, 0);
270 rc = ndmca_media_close_tape (sess);
273 if (job->have_robot) {
274 rc = ndmca_robot_unload (sess, me->slot_addr);
278 ca->media_is_loaded = 0;
280 return errors ? -1 : 0;
289 ndmca_media_open_tape (struct ndm_session *sess)
291 struct ndm_control_agent *ca = &sess->control_acb;
295 ndmalogf (sess, 0, 1, "Opening tape drive %s %s",
297 (ca->tape_mode == NDMP9_TAPE_RDWR_MODE)
298 ? "read/write" : "read-only");
301 for (t = 0; t <= ca->job.tape_timeout; t += 10) {
303 ndmalogf (sess, 0, 1,
304 "Pausing ten seconds before retry (%d/%d)",
305 t, ca->job.tape_timeout);
308 rc = ndmca_tape_open(sess);
313 /* should interpret the error */
314 ndmalogf (sess, 0, 0, "failed open tape drive %s %s",
316 (ca->tape_mode == NDMP9_TAPE_RDWR_MODE)
317 ? "read/write" : "read-only");
324 ndmca_media_close_tape (struct ndm_session *sess)
326 struct ndm_control_agent *ca = &sess->control_acb;
329 ndmalogf (sess, 0, 2, "Closing tape drive %s", ca->job.tape_device);
331 rc = ndmca_tape_close (sess);
337 ndmca_media_mtio_tape (struct ndm_session *sess,
338 ndmp9_tape_mtio_op op, u_long count, u_long *resid)
342 if (op == NDMP9_MTIO_REW) {
343 ndmalogf (sess, 0, 1, "Commanding tape drive to rewind");
344 } else if (op == NDMP9_MTIO_OFF) {
345 ndmalogf (sess, 0, 1,
346 "Commanding tape drive to eject (go offline)");
348 ndmalogf (sess, 0, 2, "Commanding tape drive to %s %d times",
349 ndmp9_tape_mtio_op_to_str (op),
353 rc = ndmca_tape_mtio (sess, op, count, resid);
359 ndmca_media_write_filemarks (struct ndm_session *sess)
363 rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_EOF, 2, 0);
373 * Returns: 'm' for tape (media) label
374 * 'V' for volume label
375 * '?' for unknown content
376 * -1 for some kind of error
380 ndmca_media_read_label (struct ndm_session *sess, char labbuf[])
382 char tape_read_buf[512];
387 ndmalogf (sess, 0, 2, "Reading label");
391 rc = ndmca_tape_read (sess, tape_read_buf, 512);
395 if (strncmp (p, "##ndmjob -m ", 12) == 0) {
398 } else if (strncmp (p, "##ndmjob -V ", 12) == 0) {
407 while (*p && *p != '\n'
408 && q < &labbuf[NDMMEDIA_LABEL_MAX-1]) {
421 * type is either 'm' or 'V', see above
424 ndmca_media_write_label (struct ndm_session *sess, int type, char labbuf[])
430 ndmalogf (sess, 0, 1, "Writing tape label '%s' type=%c", labbuf, type);
432 for (p = buf; p < &buf[512]; p++) *p = '#';
433 for (p = buf+63; p < &buf[512]; p += 64) *p = '\n';
435 sprintf (buf, "##ndmjob -%c %s", type, labbuf);
436 for (p = buf; *p; p++) continue;
439 rc = ndmca_tape_write (sess, buf, 512);
445 * type is either 'm' or 'V', see above
448 ndmca_media_check_label (struct ndm_session *sess, int type, char labbuf[])
451 char mylabbuf[NDMMEDIA_LABEL_MAX];
453 ndmalogf (sess, 0, 1, "Checking tape label, expect '%s'", labbuf);
455 rc = ndmca_media_read_label (sess, mylabbuf);
457 ndmalogf (sess, 0, 0, "Label read error");
461 if (rc != type || strcmp (labbuf, mylabbuf) != 0) {
462 ndmalogf (sess, 0, 0,
463 "Label mismatch, expected -%c'%s', got -%c'%s'",
464 type, labbuf, rc, mylabbuf);
480 ndmca_media_verify (struct ndm_session *sess)
482 struct ndm_control_agent *ca = &sess->control_acb;
483 struct ndm_job_param * job = &ca->job;
487 return 0; /* not much we can do in advance */
489 rc = ndmca_robot_verify_media (sess);
493 ndmca_media_tattle (sess);
499 ndmca_media_tattle (struct ndm_session *sess)
501 struct ndm_control_agent *ca = &sess->control_acb;
502 struct ndm_job_param * job = &ca->job;
505 for (i = 0; i < job->media_tab.n_media; i++) {
506 struct ndmmedia * me = &job->media_tab.media[i];
509 nline = ndmmedia_pp (me, 0, buf);
510 ndmalogf (sess, 0, 1, "media #%d %s", i+1, buf);
512 for (line = 1; line < nline; line++) {
513 nline = ndmmedia_pp (me, line, buf);
514 ndmalogf (sess, 0, 2, " %s", buf);
522 * Determine the current byte offset in the current
527 ndmca_media_capture_tape_offset (struct ndm_session *sess)
529 struct ndm_control_agent *ca = &sess->control_acb;
530 struct ndm_job_param * job = &ca->job;
532 unsigned long long off;
534 rc = ndmca_tape_get_state(sess);
535 if (rc) return NDMP_LENGTH_INFINITY; /* invalid? */
537 if (!ca->tape_state.blockno.valid)
538 return NDMP_LENGTH_INFINITY; /* invalid? */
540 off = ca->tape_state.blockno.value;
541 off *= job->record_size;
547 ndmca_media_capture_mover_window (struct ndm_session *sess)
549 struct ndm_control_agent *ca = &sess->control_acb;
550 struct ndmlog * ixlog = &ca->job.index_log;
551 struct ndm_job_param * job = &ca->job;
552 struct ndmmedia * me = &job->media_tab.media[ca->cur_media_ix];
553 ndmp9_mover_state ms = ca->mover_state.state;
554 ndmp9_mover_pause_reason pr = ca->mover_state.pause_reason;
556 unsigned long long wlen;
558 if (ms == NDMP9_MOVER_STATE_PAUSED) {
559 if (pr == NDMP9_MOVER_PAUSE_SEEK) {
561 } else if (pr == NDMP9_MOVER_PAUSE_EOM) {
562 me->media_eom = 1; /* tape full */
563 } else if (pr == NDMP9_MOVER_PAUSE_EOF) {
565 } else if (pr == NDMP9_MOVER_PAUSE_MEDIA_ERROR) {
566 me->media_io_error = 1;
570 } else if (ms == NDMP9_MOVER_STATE_HALTED) {
571 /* if tape_mode == READ, this may not actually be the window */
572 /* TODO: should STATE_LISTEN be considered? */
574 ndmalogf (sess, 0, 1,
575 "Warning: capturing offset w/o quiescent mover");
578 wlen = ca->mover_state.record_num;
579 wlen *= job->record_size;
580 wlen -= job->last_w_offset; /* want the size of this image */
582 me->valid_n_bytes = 1;
583 me->nb_determined = 1;
586 ndmmedia_pp (me, 0, buf);
587 ndmlogf (ixlog, "CM", 0, "%02d %s", ca->cur_media_ix+1, buf);
593 ndmca_media_calculate_windows (struct ndm_session *sess)
599 ndmca_media_calculate_offsets (struct ndm_session *sess)
601 struct ndm_control_agent *ca = &sess->control_acb;
602 struct ndm_job_param * job = &ca->job;
603 int n_media = job->media_tab.n_media;
604 struct ndmmedia * me;
606 unsigned long long offset = 0;
608 for (i = 0; i < n_media; i++) {
609 me = &job->media_tab.media[i];
611 me->begin_offset = offset;
612 if (me->valid_n_bytes) {
613 offset += me->n_bytes;
614 me->end_offset = offset;
616 me->n_bytes = NDMP_LENGTH_INFINITY;
617 me->end_offset = NDMP_LENGTH_INFINITY;
618 /* offset unchanged */
626 ndmca_media_set_window_current (struct ndm_session *sess)
628 struct ndm_control_agent *ca = &sess->control_acb;
629 struct ndm_job_param * job = &ca->job;
630 struct ndmmedia * me = &job->media_tab.media[ca->cur_media_ix];
633 rc = ndmca_mover_set_window (sess, me->begin_offset, me->n_bytes);
635 job->last_w_offset = me->begin_offset;
638 #endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */