2 * Routines to acquire and release a device for read/write
4 * Kern Sibbald, August MMII
9 Copyright (C) 2002-2005 Kern Sibbald
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License
13 version 2 as ammended with additional clauses defined in the
14 file LICENSE in the main source directory.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 the file LICENSE for additional details.
23 #include "bacula.h" /* pull in global headers */
24 #include "stored.h" /* pull in Storage Deamon headers */
27 * Create a new Device Control Record and attach
28 * it to the device (if this is a real job).
30 DCR *new_dcr(JCR *jcr, DEVICE *dev)
32 if (jcr && jcr->dcr) {
35 DCR *dcr = (DCR *)malloc(sizeof(DCR));
36 memset(dcr, 0, sizeof(DCR));
43 dcr->device = dev->device;
45 dcr->block = new_block(dev);
46 dcr->rec = new_record();
48 dcr->max_job_spool_size = dev->device->max_job_spool_size;
49 /* Attach this dcr only if dev is initialized */
50 if (dev->fd != 0 && jcr && jcr->JobType != JT_SYSTEM) {
51 dev->attached_dcrs->append(dcr); /* attach dcr to device */
52 // jcr->dcrs->append(dcr); /* put dcr in list for Job */
58 * Search the dcrs list for the given dcr. If it is found,
59 * as it should be, then remove it. Also zap the jcr pointer
60 * to the dcr if it is the same one.
63 static void remove_dcr_from_dcrs(DCR *dcr)
69 int num = jcr->dcrs->size();
70 for (i=0; i < num; i++) {
71 ldcr = (DCR *)jcr->dcrs->get(i);
74 if (jcr->dcr == dcr) {
84 * Free up all aspects of the given dcr -- i.e. dechain it,
85 * release allocated memory, zap pointers, ...
87 void free_dcr(DCR *dcr)
90 DEVICE *dev = dcr->dev;
93 * If we reserved the device, we must decrement the
96 if (dcr->reserved_device) {
99 if (dev->num_writers < 0) {
100 Jmsg1(dcr->jcr, M_ERROR, 0, _("Hey! num_writers=%d!!!!\n"), dev->num_writers);
101 dev->num_writers = 0;
102 dcr->reserved_device = false;
107 /* Detach this dcr only if the dev is initialized */
108 if (dev->fd != 0 && jcr && jcr->JobType != JT_SYSTEM) {
109 dev->attached_dcrs->remove(dcr); /* detach dcr from device */
110 // remove_dcr_from_dcrs(dcr); /* remove dcr from jcr list */
113 free_block(dcr->block);
116 free_record(dcr->rec);
119 dcr->jcr->dcr = NULL;
124 /*********************************************************************
125 * Acquire device for reading.
126 * The drive should have previously been reserved by calling
127 * reserve_device_for_read(). We read the Volume label from the block and
128 * leave the block pointers just after the label.
130 * Returns: NULL if failed for any reason
133 DCR *acquire_device_for_read(DCR *dcr)
135 DEVICE *dev = dcr->dev;
138 bool tape_previously_mounted;
139 bool tape_initially_mounted;
141 bool try_autochanger = true;
143 int vol_label_status;
145 dev->block(BST_DOING_ACQUIRE);
147 if (dev->num_writers > 0) {
148 Jmsg2(jcr, M_FATAL, 0, _("Num_writers=%d not zero. Job %d canceled.\n"),
149 dev->num_writers, jcr->JobId);
153 /* Find next Volume, if any */
156 Jmsg(jcr, M_FATAL, 0, _("No volumes specified. Job %d canceled.\n"), jcr->JobId);
160 for (i=1; i<jcr->CurVolume; i++) {
164 goto get_out; /* should not happen */
166 bstrncpy(dcr->VolumeName, vol->VolumeName, sizeof(dcr->VolumeName));
168 init_device_wait_timers(dcr);
170 tape_previously_mounted = dev->can_read() || dev->can_append() ||
172 tape_initially_mounted = tape_previously_mounted;
175 /* Volume info is always needed because of VolParts */
176 Dmsg0(200, "dir_get_volume_info\n");
177 if (!dir_get_volume_info(dcr, GET_VOL_INFO_FOR_READ)) {
178 Jmsg1(jcr, M_WARNING, 0, "%s", jcr->errmsg);
181 dev->num_parts = dcr->VolCatInfo.VolCatParts;
183 for (i=0; i<5; i++) {
184 dev->clear_labeled(); /* force reread of label */
185 if (job_canceled(jcr)) {
186 Mmsg1(dev->errmsg, _("Job %d canceled.\n"), jcr->JobId);
187 goto get_out; /* error return */
190 * This code ensures that the device is ready for
191 * reading. If it is a file, it opens it.
192 * If it is a tape, it checks the volume name
194 for ( ; !dev->is_open(); ) {
195 Dmsg1(120, "bstored: open vol=%s\n", dcr->VolumeName);
196 if (open_dev(dev, dcr->VolumeName, OPEN_READ_ONLY) < 0) {
197 if (dev->dev_errno == EIO) { /* no tape loaded */
198 Jmsg3(jcr, M_WARNING, 0, _("Open device %s Volume \"%s\" failed: ERR=%s\n"),
199 dev->print_name(), dcr->VolumeName, strerror_dev(dev));
203 /* If we have a dvd that requires mount,
204 * we need to try to open the label, so the info can be reported
205 * if a wrong volume has been mounted. */
206 if (dev->is_dvd() && (dcr->VolCatInfo.VolCatParts > 0)) {
210 Jmsg3(jcr, M_FATAL, 0, _("Open device %s Volume \"%s\" failed: ERR=%s\n"),
211 dev->print_name(), dcr->VolumeName, strerror_dev(dev));
214 Dmsg1(129, "open_dev %s OK\n", dev->print_name());
218 vol_label_status = read_dev_volume_label_guess(dcr, 0);
220 vol_label_status = read_dev_volume_label(dcr);
223 Dmsg0(200, "calling read-vol-label\n");
224 switch (vol_label_status) {
227 memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
231 * Send error message generated by read_dev_volume_label()
232 * only we really had a tape mounted. This supresses superfluous
233 * error messages when nothing is mounted.
235 if (tape_previously_mounted) {
236 Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
240 if (tape_initially_mounted) {
241 tape_initially_mounted = false;
246 Jmsg1(jcr, M_WARNING, 0, "%s", jcr->errmsg);
248 tape_previously_mounted = true;
250 /* If the device requires mount, close it, so the device can be ejected.
251 * FIXME: This should perhaps be done for all devices. */
252 if (dev_cap(dev, CAP_REQMOUNT)) {
253 force_close_dev(dev);
256 /* Call autochanger only once unless ask_sysop called */
257 if (try_autochanger) {
259 Dmsg2(200, "calling autoload Vol=%s Slot=%d\n",
260 dcr->VolumeName, dcr->VolCatInfo.Slot);
261 stat = autoload_device(dcr, 0, NULL);
263 try_autochanger = false;
264 continue; /* try reading volume mounted */
268 /* Mount a specific volume and no other */
269 Dmsg0(200, "calling dir_ask_sysop\n");
270 if (!dir_ask_sysop_to_mount_volume(dcr)) {
271 goto get_out; /* error return */
273 try_autochanger = true; /* permit using autochanger again */
274 continue; /* try reading again */
279 Jmsg1(jcr, M_FATAL, 0, _("Too many errors trying to mount device %s.\n"),
286 set_jcr_job_status(jcr, JS_Running);
287 dir_send_job_status(jcr);
288 Jmsg(jcr, M_INFO, 0, _("Ready to read from volume \"%s\" on device %s.\n"),
289 dcr->VolumeName, dev->print_name());
302 * Acquire device for writing. We permit multiple writers.
303 * If this is the first one, we read the label.
305 * Returns: NULL if failed for any reason
307 * Note, normally reserve_device_for_append() is called
308 * before this routine.
310 DCR *acquire_device_for_append(DCR *dcr)
312 bool release = false;
313 bool recycle = false;
314 bool do_mount = false;
315 DEVICE *dev = dcr->dev;
318 init_device_wait_timers(dcr);
320 dev->block(BST_DOING_ACQUIRE);
321 Dmsg1(190, "acquire_append device is %s\n", dev->is_tape()?"tape":"disk");
323 if (dcr->reserved_device) {
324 dev->reserved_device--;
325 dcr->reserved_device = false;
329 * With the reservation system, this should not happen
331 if (dev->can_read()) {
332 Jmsg(jcr, M_FATAL, 0, _("Device %s is busy reading.\n"), dev->print_name());
336 if (dev->can_append()) {
337 Dmsg0(190, "device already in append.\n");
339 * Device already in append mode
341 * Check if we have the right Volume mounted
342 * OK if current volume info OK
343 * OK if next volume matches current volume
344 * otherwise mount desired volume obtained from
345 * dir_find_next_appendable_volume
347 bstrncpy(dcr->VolumeName, dev->VolHdr.VolName, sizeof(dcr->VolumeName));
348 if (!dir_get_volume_info(dcr, GET_VOL_INFO_FOR_WRITE) &&
349 !(dir_find_next_appendable_volume(dcr) &&
350 strcmp(dev->VolHdr.VolName, dcr->VolumeName) == 0)) { /* wrong tape mounted */
351 Dmsg0(190, "Wrong tape mounted.\n");
352 if (dev->num_writers != 0 || dev->reserved_device) {
353 Jmsg(jcr, M_FATAL, 0, _("Device %s is busy writing on another Volume.\n"), dev->print_name());
356 /* Wrong tape mounted, release it, then fall through to get correct one */
357 Dmsg0(190, "Wrong tape mounted, release and try mount.\n");
362 * At this point, the correct tape is already mounted, so
363 * we do not need to do mount_next_write_volume(), unless
364 * we need to recycle the tape.
366 recycle = strcmp(dcr->VolCatInfo.VolCatStatus, "Recycle") == 0;
367 Dmsg1(190, "Correct tape mounted. recycle=%d\n", recycle);
368 if (recycle && dev->num_writers != 0) {
369 Jmsg(jcr, M_FATAL, 0, _("Cannot recycle volume \"%s\""
370 " on device %s because it is in use by another job.\n"),
371 dev->VolHdr.VolName, dev->print_name());
374 if (dev->num_writers == 0) {
375 memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
379 /* Not already in append mode, so mount the device */
380 Dmsg0(190, "Not in append mode, try mount.\n");
381 ASSERT(dev->num_writers == 0);
385 if (do_mount || recycle) {
386 Dmsg0(190, "Do mount_next_write_vol\n");
387 bool mounted = mount_next_write_volume(dcr, release);
389 if (!job_canceled(jcr)) {
390 /* Reduce "noise" -- don't print if job canceled */
391 Jmsg(jcr, M_FATAL, 0, _("Could not ready device %s for append.\n"),
398 dev->num_writers++; /* we are now a writer */
399 if (jcr->NumVolumes == 0) {
405 * If we jump here, it is an error return because
406 * rtn_dev will still be NULL
417 * This job is done, so release the device. From a Unix standpoint,
418 * the device remains open.
421 bool release_device(DCR *dcr)
424 DEVICE *dev = dcr->dev;
428 Dmsg1(100, "release_device device is %s\n", dev->is_tape()?"tape":"disk");
430 /* if device is reserved, job never started, so release the reserve here */
431 if (dcr->reserved_device) {
432 dev->reserved_device--;
433 dcr->reserved_device = false;
436 if (dev->can_read()) {
437 dev->clear_read(); /* clear read bit */
439 /******FIXME**** send read volume usage statistics to director */
441 } else if (dev->num_writers > 0) {
443 * Note if WEOT is set, we are at the end of the tape
444 * and may not be positioned correctly, so the
445 * job_media_record and update_vol_info have already been
446 * done, which means we skip them here.
449 Dmsg1(100, "There are %d writers in release_device\n", dev->num_writers);
450 if (dev->is_labeled()) {
451 Dmsg0(100, "dir_create_jobmedia_record. Release\n");
452 if (!dev->at_weot() && !dir_create_jobmedia_record(dcr)) {
453 Jmsg(jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
454 dcr->VolCatInfo.VolCatName, jcr->Job);
456 /* If no more writers, write an EOF */
457 if (!dev->num_writers && dev->can_write()) {
459 write_ansi_ibm_labels(dcr, ANSI_EOF_LABEL, dev->VolHdr.VolName);
461 if (!dev->at_weot()) {
462 dev->VolCatInfo.VolCatFiles = dev->file; /* set number of files */
463 dev->VolCatInfo.VolCatJobs++; /* increment number of jobs */
464 /* Note! do volume update before close, which zaps VolCatInfo */
465 Dmsg0(100, "dir_update_vol_info. Release0\n");
466 dir_update_volume_info(dcr, false); /* send Volume info to Director */
472 * If we reach here, it is most likely because the job
473 * has failed, since the device is not in read mode and
474 * there are no writers. It was probably reserved.
478 /* If no writers, close if file or !CAP_ALWAYS_OPEN */
479 if (dev->num_writers == 0 && (!dev->is_tape() || !dev_cap(dev, CAP_ALWAYSOPEN))) {
480 offline_or_rewind_dev(dev);
484 /* Fire off Alert command and include any output */
485 if (!job_canceled(jcr) && dcr->device->alert_command) {
489 char line[MAXSTRING];
490 alert = get_pool_memory(PM_FNAME);
491 alert = edit_device_codes(dcr, alert, dcr->device->alert_command, "");
492 bpipe = open_bpipe(alert, 0, "r");
494 while (fgets(line, sizeof(line), bpipe->rfd)) {
495 Jmsg(jcr, M_ALERT, 0, _("Alert: %s"), line);
497 status = close_bpipe(bpipe);
503 Jmsg(jcr, M_ALERT, 0, _("3997 Bad alert command: %s: ERR=%s.\n"),
504 alert, be.strerror(status));
507 Dmsg1(400, "alert status=%d\n", status);
508 free_pool_memory(alert);
513 pthread_cond_broadcast(&wait_device_release);