Imported Upstream version 3.1.0
[debian/amanda] / ndmp-src / ndma_ctrl_media.c
1 /*
2  * Copyright (c) 1998,1999,2000
3  *      Traakan, Inc., Los Altos, CA
4  *      All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice unmodified, this list of conditions, and the following
11  *    disclaimer.
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.
15  *
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
26  * SUCH DAMAGE.
27  */
28
29 /*
30  * Project:  NDMJOB
31  * Ident:    $Id: $
32  *
33  * Description:
34  *
35  */
36
37
38 #include "ndmagents.h"
39
40
41 #ifndef NDMOS_OPTION_NO_CONTROL_AGENT
42
43
44 int
45 ndmca_media_load_first (struct ndm_session *sess)
46 {
47         sess->control_acb.cur_media_ix = 0;
48         return ndmca_media_load_current (sess);
49 }
50
51 /*
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
57  *       tapes available.
58  */
59 int
60 ndmca_media_load_next (struct ndm_session *sess)
61 {
62         int             n_media =  sess->control_acb.job.media_tab.n_media;
63
64         if (sess->control_acb.cur_media_ix+1 >= n_media) {
65                 ndmalogf (sess, 0, 0, "Out of tapes");
66                 return -1;
67         }
68         sess->control_acb.cur_media_ix++;
69         return ndmca_media_load_current (sess);
70 }
71
72 int
73 ndmca_media_unload_last (struct ndm_session *sess)
74 {
75         return ndmca_media_unload_current(sess);
76 }
77
78 int
79 ndmca_media_change (struct ndm_session *sess)
80 {
81         int             rc;
82
83         rc = ndmca_media_unload_current(sess);
84         if (rc) return rc;
85
86         rc = ndmca_media_load_next (sess);
87         if (rc) return rc;
88
89         return 0;
90 }
91
92 int
93 ndmca_media_load_seek (struct ndm_session *sess, unsigned long long pos)
94 {
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;
98         struct ndmmedia *       me;
99         int                     i;
100
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)
104                         break;
105         }
106
107         if (i >= n_media) {
108                 ndmalogf (sess, 0, 0, "Seek to unspecified media");
109                 return -1;
110         }
111
112         ca->cur_media_ix = i;
113         return ndmca_media_load_current (sess);
114 }
115
116
117
118
119
120
121 int
122 ndmca_media_load_current (struct ndm_session *sess)
123 {
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];
127         int                     rc;
128         unsigned                count;
129
130         if (job->have_robot) {
131                 rc = ndmca_robot_load (sess, me->slot_addr);
132                 if (rc) return rc;
133         }
134
135         me->media_used = 1;
136
137         rc = ndmca_media_open_tape (sess);
138         /*
139          * TODO: it would be nice to discriminate this and
140          *       set flags accordingly. For example,
141          *       indicate a write-protect tape.
142          */
143         if (rc) {
144                 me->media_open_error = 1;
145
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);
150                 }
151                 return rc;
152         }
153
154         ca->media_is_loaded = 1;
155
156         rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_REW, 1, 0);
157         if (rc) {
158                 me->media_io_error = 1;
159                 goto close_and_unload;
160         }
161
162         if (ca->is_label_op) {
163                 if (ca->tape_mode == NDMP9_TAPE_RDWR_MODE)
164                         me->media_written = 1;  /* most likely */
165
166                 return 0;               /* ready to go */
167         }
168
169         if (me->valid_label) {
170                 rc = ndmca_media_check_label (sess, 'm', me->label);
171                 if (rc) {
172                         if (rc == -1) {
173                                 me->label_io_error = 1;
174                         } else if (rc == -2) {
175                                 me->label_read = 1;
176                                 me->label_mismatch = 1;
177                         }
178                         goto close_and_unload;
179                 }
180                 me->label_read = 1;
181
182                 rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_REW, 1, 0);
183                 if (rc) {
184                         me->media_io_error = 1;
185                 }
186         }
187
188         if (!me->valid_filemark) {              /* synthetic */
189                 me->valid_filemark = 1;
190                 if (me->valid_label)
191                         me->file_mark_offset = 1;
192                 else
193                         me->file_mark_offset = 0;
194         }
195
196         count = me->file_mark_offset;
197
198         if (count > 0) {
199                 rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_FSF, count, 0);
200                 if (rc) {
201                         me->fmark_error = 1;
202                         ndmca_media_mtio_tape (sess, NDMP9_MTIO_REW, 1, 0);
203                         goto close_and_unload;
204                 }
205         }
206
207         if (ca->tape_mode == NDMP9_TAPE_RDWR_MODE)
208                 me->media_written = 1;  /* most likely */
209
210         return 0;
211
212   close_and_unload:
213         me->media_io_error = 1;
214         ndmca_media_unload_best_effort (sess);
215         return rc;
216 }
217
218 int
219 ndmca_media_unload_current (struct ndm_session *sess)
220 {
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];
224         int                     rc;
225
226         if (!ca->media_is_loaded)
227                 return 0;
228
229         rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_REW, 1, 0);
230         if (rc) return rc;
231
232         if (ca->job.use_eject) {
233                 rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_OFF, 1, 0);
234                 if (rc) return rc;
235         }
236
237         rc = ndmca_media_close_tape (sess);
238         if (rc) return rc;
239
240         if (job->have_robot) {
241                 rc = ndmca_robot_unload (sess, me->slot_addr);
242                 if (rc) return rc;
243         }
244
245         ca->media_is_loaded = 0;
246
247         return 0;
248 }
249
250 int
251 ndmca_media_unload_best_effort (struct ndm_session *sess)
252 {
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];
256         int                     errors = 0;
257         int                     rc;
258
259         if (!ca->media_is_loaded)
260                 return 0;
261
262         rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_REW, 1, 0);
263         if (rc) errors++;
264
265         if (ca->job.use_eject) {
266                 rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_OFF, 1, 0);
267                 if (rc) errors++;
268         }
269
270         rc = ndmca_media_close_tape (sess);
271         if (rc) errors++;
272
273         if (job->have_robot) {
274                 rc = ndmca_robot_unload (sess, me->slot_addr);
275                 if (rc) errors++;
276         }
277
278         ca->media_is_loaded = 0;
279
280         return errors ? -1 : 0;
281 }
282
283
284
285
286
287
288 int
289 ndmca_media_open_tape (struct ndm_session *sess)
290 {
291         struct ndm_control_agent *ca = &sess->control_acb;
292         int                     rc;
293         unsigned int            t;
294
295         ndmalogf (sess, 0, 1, "Opening tape drive %s %s",
296                         ca->job.tape_device,
297                         (ca->tape_mode == NDMP9_TAPE_RDWR_MODE)
298                                 ? "read/write" : "read-only");
299
300         rc = -1;
301         for (t = 0; t <= ca->job.tape_timeout; t += 10) {
302                 if (t > 0) {
303                         ndmalogf (sess, 0, 1,
304                                 "Pausing ten seconds before retry (%d/%d)",
305                                 t, ca->job.tape_timeout);
306                         sleep (10);
307                 }
308                 rc = ndmca_tape_open(sess);
309                 if (rc == 0) break;
310         }
311
312         if (rc) {
313                 /* should interpret the error */
314                 ndmalogf (sess, 0, 0, "failed open tape drive %s %s",
315                         ca->job.tape_device,
316                         (ca->tape_mode == NDMP9_TAPE_RDWR_MODE)
317                                 ? "read/write" : "read-only");
318         }
319
320         return rc;
321 }
322
323 int
324 ndmca_media_close_tape (struct ndm_session *sess)
325 {
326         struct ndm_control_agent *ca = &sess->control_acb;
327         int                     rc;
328
329         ndmalogf (sess, 0, 2, "Closing tape drive %s", ca->job.tape_device);
330
331         rc = ndmca_tape_close (sess);
332
333         return 0;
334 }
335
336 int
337 ndmca_media_mtio_tape (struct ndm_session *sess,
338   ndmp9_tape_mtio_op op, u_long count, u_long *resid)
339 {
340         int                     rc;
341
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)");
347         } else {
348                 ndmalogf (sess, 0, 2, "Commanding tape drive to %s %d times",
349                         ndmp9_tape_mtio_op_to_str (op),
350                         count);
351         }
352
353         rc = ndmca_tape_mtio (sess, op, count, resid);
354
355         return rc;
356 }
357
358 int
359 ndmca_media_write_filemarks (struct ndm_session *sess)
360 {
361         int             rc;
362
363         rc = ndmca_media_mtio_tape (sess, NDMP9_MTIO_EOF, 2, 0);
364
365         return rc;
366 }
367
368
369
370
371
372 /*
373  * Returns: 'm' for tape (media) label
374  *          'V' for volume label
375  *          '?' for unknown content
376  *          -1  for some kind of error
377  */
378
379 int
380 ndmca_media_read_label (struct ndm_session *sess, char labbuf[])
381 {
382         char            tape_read_buf[512];
383         int             rc;
384         char *          p;
385         char *          q;
386
387         ndmalogf (sess, 0, 2, "Reading label");
388
389         *labbuf = 0;
390
391         rc = ndmca_tape_read (sess, tape_read_buf, 512);
392
393         if (rc == 0) {
394                 p = tape_read_buf;
395                 if (strncmp (p, "##ndmjob -m ", 12) == 0) {
396                         p += 12;
397                         rc = 'm';
398                 } else if (strncmp (p, "##ndmjob -V ", 12) == 0) {
399                         p += 12;
400                         rc = 'V';
401                 } else {
402                         rc = '?';
403                         p = 0;
404                 }
405                 if (p) {
406                         q = labbuf;
407                         while (*p && *p != '\n'
408                          && q < &labbuf[NDMMEDIA_LABEL_MAX-1]) {
409                                 *q++ = *p++;
410                         }
411                         *q = 0;
412                 }
413         } else {
414                 rc = -1;
415         }
416
417         return rc;
418 }
419
420 /*
421  * type is either 'm' or 'V', see above
422  */
423 int
424 ndmca_media_write_label (struct ndm_session *sess, int type, char labbuf[])
425 {
426         int             rc;
427         char            buf[512];
428         char *          p;
429
430         ndmalogf (sess, 0, 1, "Writing tape label '%s' type=%c", labbuf, type);
431
432         for (p = buf; p < &buf[512]; p++) *p = '#';
433         for (p = buf+63; p < &buf[512]; p += 64) *p = '\n';
434
435         sprintf (buf, "##ndmjob -%c %s", type, labbuf);
436         for (p = buf; *p; p++) continue;
437         *p++ = '\n';
438
439         rc = ndmca_tape_write (sess, buf, 512);
440
441         return rc;
442 }
443
444 /*
445  * type is either 'm' or 'V', see above
446  */
447 int
448 ndmca_media_check_label (struct ndm_session *sess, int type, char labbuf[])
449 {
450         int             rc;
451         char            mylabbuf[NDMMEDIA_LABEL_MAX];
452
453         ndmalogf (sess, 0, 1, "Checking tape label, expect '%s'", labbuf);
454
455         rc = ndmca_media_read_label (sess, mylabbuf);
456         if (rc < 0) {
457                 ndmalogf (sess, 0, 0, "Label read error");
458                 return -1;
459         }
460
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);
465                 return -2;
466         }
467
468         return 0;
469 }
470
471
472
473
474
475
476
477
478
479 int
480 ndmca_media_verify (struct ndm_session *sess)
481 {
482         struct ndm_control_agent *ca = &sess->control_acb;
483         struct ndm_job_param *  job = &ca->job;
484         int                     rc;
485
486         if (job->have_robot)
487                 return 0;               /* not much we can do in advance */
488
489         rc = ndmca_robot_verify_media (sess);
490         if (rc == 0)
491                 return rc;
492
493         ndmca_media_tattle (sess);
494
495         return -1;
496 }
497
498 int
499 ndmca_media_tattle (struct ndm_session *sess)
500 {
501         struct ndm_control_agent *ca = &sess->control_acb;
502         struct ndm_job_param *  job = &ca->job;
503         int                     i, line, nline;
504
505         for (i = 0; i < job->media_tab.n_media; i++) {
506                 struct ndmmedia *       me = &job->media_tab.media[i];
507                 char                    buf[80];
508
509                 nline = ndmmedia_pp (me, 0, buf);
510                 ndmalogf (sess, 0, 1, "media #%d %s", i+1, buf);
511
512                 for (line = 1; line < nline; line++) {
513                         nline = ndmmedia_pp (me, line, buf);
514                         ndmalogf (sess, 0, 2, "         %s", buf);
515                 }
516         }
517         return 0;
518 }
519
520
521 /*
522  * Determine the current byte offset in the current
523  * tape file.
524  */
525
526 unsigned long long
527 ndmca_media_capture_tape_offset (struct ndm_session *sess)
528 {
529         struct ndm_control_agent *ca = &sess->control_acb;
530         struct ndm_job_param *  job = &ca->job;
531         int                     rc;
532         unsigned long long      off;
533
534         rc = ndmca_tape_get_state(sess);
535         if (rc) return NDMP_LENGTH_INFINITY;    /* invalid? */
536
537         if (!ca->tape_state.blockno.valid)
538                 return NDMP_LENGTH_INFINITY;    /* invalid? */
539
540         off = ca->tape_state.blockno.value;
541         off *= job->record_size;
542
543         return off;
544 }
545
546 int
547 ndmca_media_capture_mover_window (struct ndm_session *sess)
548 {
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;
555         char                    buf[100];
556         unsigned long long      wlen;
557
558         if (ms == NDMP9_MOVER_STATE_PAUSED) {
559                 if (pr == NDMP9_MOVER_PAUSE_SEEK) {
560                         /* end-of-window */
561                 } else if (pr == NDMP9_MOVER_PAUSE_EOM) {
562                         me->media_eom = 1;      /* tape full */
563                 } else if (pr == NDMP9_MOVER_PAUSE_EOF) {
564                         me->media_eof = 1;
565                 } else if (pr == NDMP9_MOVER_PAUSE_MEDIA_ERROR) {
566                         me->media_io_error = 1;
567                 } else {
568                         /* what */
569                 }
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? */
573         } else {
574                 ndmalogf (sess, 0, 1,
575                         "Warning: capturing offset w/o quiescent mover");
576         }
577
578         wlen = ca->mover_state.record_num;
579         wlen *= job->record_size;
580         wlen -= job->last_w_offset;     /* want the size of this image */
581
582         me->valid_n_bytes = 1;
583         me->nb_determined = 1;
584         me->n_bytes = wlen;
585
586         ndmmedia_pp (me, 0, buf);
587         ndmlogf (ixlog, "CM", 0, "%02d %s", ca->cur_media_ix+1, buf);
588
589         return 0;
590 }
591
592 int
593 ndmca_media_calculate_windows (struct ndm_session *sess)
594 {
595         return 0;
596 }
597
598 int
599 ndmca_media_calculate_offsets (struct ndm_session *sess)
600 {
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;
605         int                     i;
606         unsigned long long      offset = 0;
607
608         for (i = 0; i < n_media; i++) {
609                 me = &job->media_tab.media[i];
610
611                 me->begin_offset = offset;
612                 if (me->valid_n_bytes) {
613                         offset += me->n_bytes;
614                         me->end_offset = offset;
615                 } else {
616                         me->n_bytes = NDMP_LENGTH_INFINITY;
617                         me->end_offset = NDMP_LENGTH_INFINITY;
618                         /* offset unchanged */
619                 }
620         }
621
622         return 0;
623 }
624
625 int
626 ndmca_media_set_window_current (struct ndm_session *sess)
627 {
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];
631         int                     rc;
632
633         rc = ndmca_mover_set_window (sess, me->begin_offset, me->n_bytes);
634         if (rc == 0)
635             job->last_w_offset = me->begin_offset;
636         return rc;
637 }
638 #endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */