Example #1
0
/*
 * This routine is meant to be called once the first pass
 *   to ensure that we have a candidate volume to mount.
 *   Otherwise, we ask the sysop to created one.
 */
bool DCR::find_a_volume()  
{
   DCR *dcr = this;
   if (!is_suitable_volume_mounted()) {
      bool have_vol = false;
      /* Do we have a candidate volume? */
      if (dev->vol) {
         bstrncpy(VolumeName, dev->vol->vol_name, sizeof(VolumeName));
         have_vol = dir_get_volume_info(this, GET_VOL_INFO_FOR_WRITE);
      }
      /*
       * Get Director's idea of what tape we should have mounted.
       *    in dcr->VolCatInfo
       */
      if (!have_vol) {
         Dmsg0(200, "Before dir_find_next_appendable_volume.\n");
         while (!dir_find_next_appendable_volume(dcr)) {
            Dmsg0(200, "not dir_find_next\n");
            if (job_canceled(jcr)) {
               unlock_volumes();
               return false;
            }
            unlock_volumes();
            if (!dir_ask_sysop_to_create_appendable_volume(dcr)) {
               return false;
             }
             lock_volumes();
             Dmsg0(150, "Again dir_find_next_append...\n");
         }
      }
   }
   return true;
}
Example #2
0
static void debug_list_volumes(const char *imsg)
{
   VOLRES *vol;
   POOL_MEM msg(PM_MESSAGE);

   lock_volumes();
   foreach_dlist(vol, vol_list) {
      if (vol->dev) {
         Mmsg(msg, "List %s: %s in_use=%d swap=%d on device %s\n", imsg,
              vol->vol_name, vol->is_in_use(), vol->is_swapping(), vol->dev->print_name());
      } else {
         Mmsg(msg, "List %s: %s in_use=%d swap=%d no dev\n", imsg, vol->vol_name,
              vol->is_in_use(), vol->is_swapping());
      }
      Dmsg1(dbglvl, "%s", msg.c_str());
   }

   unlock_volumes();
}
Example #3
0
File: askdir.c Project: AlD/bareos
/**
 * Get info on the next appendable volume in the Director's database
 *
 * Returns: true  on success dcr->VolumeName is volume
 *                reserve_volume() called on Volume name
 *          false on failure dcr->VolumeName[0] == 0
 *                also sets dcr->found_in_use if at least one
 *                in use volume was found.
 *
 *          Volume information returned in dcr
 *
 */
bool dir_find_next_appendable_volume(DCR *dcr)
{
    JCR *jcr = dcr->jcr;
    BSOCK *dir = jcr->dir_bsock;
    bool rtn;
    char lastVolume[MAX_NAME_LENGTH];

    Dmsg2(dbglvl, "dir_find_next_appendable_volume: reserved=%d Vol=%s\n",
       dcr->is_reserved(), dcr->VolumeName);

    /*
     * Try the twenty oldest or most available volumes.  Note,
     *   the most available could already be mounted on another
     *   drive, so we continue looking for a not in use Volume.
     */
    lock_volumes();
    P(vol_info_mutex);
    dcr->clear_found_in_use();
    lastVolume[0] = 0;
    for (int vol_index=1;  vol_index < 20; vol_index++) {
       bash_spaces(dcr->media_type);
       bash_spaces(dcr->pool_name);
       dir->fsend(Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
       unbash_spaces(dcr->media_type);
       unbash_spaces(dcr->pool_name);
       Dmsg1(dbglvl, ">dird %s", dir->msg);
       if (do_get_volume_info(dcr)) {
          /* Give up if we get the same volume name twice */
          if (lastVolume[0] && bstrcmp(lastVolume, dcr->VolumeName)) {
             Dmsg1(dbglvl, "Got same vol = %s\n", lastVolume);
             break;
          }
          bstrncpy(lastVolume, dcr->VolumeName, sizeof(lastVolume));
          if (dcr->can_i_write_volume()) {
             Dmsg1(dbglvl, "Call reserve_volume for write. Vol=%s\n", dcr->VolumeName);
             if (reserve_volume(dcr, dcr->VolumeName) == NULL) {
                Dmsg2(dbglvl, "Could not reserve volume %s on %s\n", dcr->VolumeName,
                    dcr->dev->print_name());
                continue;
             }
             Dmsg1(dbglvl, "dir_find_next_appendable_volume return true. vol=%s\n",
                dcr->VolumeName);
             rtn = true;
             goto get_out;
          } else {
             Dmsg1(dbglvl, "Volume %s is in use.\n", dcr->VolumeName);
             /* If volume is not usable, it is in use by someone else */
             dcr->set_found_in_use();
             continue;
          }
       }
       Dmsg2(dbglvl, "No vol. index %d return false. dev=%s\n", vol_index,
          dcr->dev->print_name());
       break;
    }
    rtn = false;
    dcr->VolumeName[0] = 0;

get_out:
    V(vol_info_mutex);
    unlock_volumes();
    return rtn;
}
Example #4
0
/*
 * This job is done, so release the device. From a Unix standpoint,
 *  the device remains open.
 *
 * Note, if we were spooling, we may enter with the device blocked.
 *   We unblock at the end, only if it was us who blocked the
 *   device.
 *
 */
bool release_device(DCR *dcr)
{
   utime_t now;
   JCR *jcr = dcr->jcr;
   DEVICE *dev = dcr->dev;
   bool ok = true;
   char tbuf[100];
   int was_blocked = BST_NOT_BLOCKED;

   /*
    * Capture job statistics now that we are done using this device.
    */
   now = (utime_t)time(NULL);
   update_job_statistics(jcr, now);

   dev->Lock();
   if (!dev->is_blocked()) {
      block_device(dev, BST_RELEASING);
   } else {
      was_blocked = dev->blocked();
      dev->set_blocked(BST_RELEASING);
   }
   lock_volumes();
   Dmsg2(100, "release_device device %s is %s\n", dev->print_name(), dev->is_tape() ? "tape" : "disk");

   /*
    * If device is reserved, job never started, so release the reserve here
    */
   dcr->clear_reserved();

   if (dev->can_read()) {
      VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
      dev->clear_read();              /* clear read bit */
      Dmsg2(150, "dir_update_vol_info. label=%d Vol=%s\n",
         dev->is_labeled(), vol->VolCatName);
      if (dev->is_labeled() && vol->VolCatName[0] != 0) {
         dcr->dir_update_volume_info(false, false); /* send Volume info to Director */
         remove_read_volume(jcr, dcr->VolumeName);
         volume_unused(dcr);
      }
   } else if (dev->num_writers > 0) {
      /*
       * Note if WEOT is set, we are at the end of the tape and may not be positioned correctly,
       * so the job_media_record and update_vol_info have already been done,
       * which means we skip them here.
       */
      dev->num_writers--;
      Dmsg1(100, "There are %d writers in release_device\n", dev->num_writers);
      if (dev->is_labeled()) {
         Dmsg2(200, "dir_create_jobmedia. Release vol=%s dev=%s\n",
               dev->getVolCatName(), dev->print_name());
         if (!dev->at_weot() && !dcr->dir_create_jobmedia_record(false)) {
            Jmsg2(jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
               dcr->getVolCatName(), jcr->Job);
         }

         /*
          * If no more writers, and no errors, and wrote something, write an EOF
          */
         if (!dev->num_writers && dev->can_write() && dev->block_num > 0) {
            dev->weof(1);
            write_ansi_ibm_labels(dcr, ANSI_EOF_LABEL, dev->VolHdr.VolumeName);
         }
         if (!dev->at_weot()) {
            dev->VolCatInfo.VolCatFiles = dev->file;   /* set number of files */

            /*
             * Note! do volume update before close, which zaps VolCatInfo
             */
            dcr->dir_update_volume_info(false, false); /* send Volume info to Director */
            Dmsg2(200, "dir_update_vol_info. Release vol=%s dev=%s\n",
                  dev->getVolCatName(), dev->print_name());
         }
         if (dev->num_writers == 0) {         /* if not being used */
            volume_unused(dcr);               /*  we obviously are not using the volume */
         }
      }

   } else {
      /*
       * If we reach here, it is most likely because the job has failed,
       * since the device is not in read mode and there are no writers.
       * It was probably reserved.
       */
      volume_unused(dcr);
   }

   Dmsg3(100, "%d writers, %d reserve, dev=%s\n", dev->num_writers, dev->num_reserved(), dev->print_name());

   /*
    * If no writers, close if file or !CAP_ALWAYS_OPEN
    */
   if (dev->num_writers == 0 && (!dev->is_tape() || !dev->has_cap(CAP_ALWAYSOPEN))) {
      dev->close(dcr);
      free_volume(dev);
   }

   unlock_volumes();

   /*
    * Fire off Alert command and include any output
    */
   if (!job_canceled(jcr)) {
      if (!dcr->device->drive_tapealert_enabled && dcr->device->alert_command) {
         int status = 1;
         POOLMEM *alert, *line;
         BPIPE *bpipe;

         alert = get_pool_memory(PM_FNAME);
         line = get_pool_memory(PM_FNAME);

         alert = edit_device_codes(dcr, alert, dcr->device->alert_command, "");

         /*
          * Wait maximum 5 minutes
          */
         bpipe = open_bpipe(alert, 60 * 5, "r");
         if (bpipe) {
            while (bfgets(line, bpipe->rfd)) {
               Jmsg(jcr, M_ALERT, 0, _("Alert: %s"), line);
            }
            status = close_bpipe(bpipe);
         } else {
            status = errno;
         }
         if (status != 0) {
            berrno be;
            Jmsg(jcr, M_ALERT, 0, _("3997 Bad alert command: %s: ERR=%s.\n"), alert, be.bstrerror(status));
         }

         Dmsg1(400, "alert status=%d\n", status);
         free_pool_memory(alert);
         free_pool_memory(line);
      } else {
         /*
          * If all reservations are cleared for this device raise an event that SD plugins can register to.
          */
         if (dev->num_reserved() == 0) {
            generate_plugin_event(jcr, bsdEventDeviceReleased, dcr);
         }
      }
   }

   pthread_cond_broadcast(&dev->wait_next_vol);
   Dmsg2(100, "JobId=%u broadcast wait_device_release at %s\n",
         (uint32_t)jcr->JobId, bstrftimes(tbuf, sizeof(tbuf), (utime_t)time(NULL)));
   release_device_cond();

   /*
    * If we are the thread that blocked the device, then unblock it
    */
   if (pthread_equal(dev->no_wait_id, pthread_self())) {
      dev->dunblock(true);
   } else {
      /*
       * Otherwise, reset the prior block status and unlock
       */
      dev->set_blocked(was_blocked);
      dev->Unlock();
   }

   if (dcr->keep_dcr) {
      detach_dcr_from_dev(dcr);
   } else {
      free_dcr(dcr);
   }

   Dmsg2(100, "Device %s released by JobId=%u\n", dev->print_name(), (uint32_t)jcr->JobId);

   return ok;
}
Example #5
0
/*
 * If release is set, we rewind the current volume,
 * which we no longer want, and ask the user (console)
 * to mount the next volume.
 *
 *  Continue trying until we get it, and then ensure
 *  that we can write on it.
 *
 * This routine returns a 0 only if it is REALLY
 *  impossible to get the requested Volume.
 *
 */
bool DCR::mount_next_write_volume()
{
   int retry = 0;
   bool ask = false, recycle, autochanger;
   bool do_find = true;
   int mode;
   DCR *dcr = this;

   Dmsg2(150, "Enter mount_next_volume(release=%d) dev=%s\n", dev->must_unload(),
      dev->print_name());

   init_device_wait_timers(dcr);
   lock_volumes();
   
   /*
    * 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:
   Dmsg1(150, "mount_next_vol retry=%d\n", retry);
   /* Ignore retry if this is poll request */
   if (!dev->poll && retry++ > 4) {
      /* Last ditch effort before giving up, force operator to respond */
      VolCatInfo.Slot = 0;
      unlock_volumes();
      if (!dir_ask_sysop_to_mount_volume(dcr, ST_APPEND)) {
         Jmsg(jcr, M_FATAL, 0, _("Too many errors trying to mount device %s.\n"),
              dev->print_name());
         goto no_lock_bail_out;
      }
      lock_volumes();
      Dmsg1(150, "Continue after dir_ask_sysop_to_mount. must_load=%d\n", dev->must_load());
   }
   if (job_canceled(jcr)) {
      Jmsg(jcr, M_FATAL, 0, _("Job %d canceled.\n"), jcr->JobId);
      goto bail_out;
   }
   recycle = false;

   if (retry >= 2) {
      do_find = false; 
   }

   if (dev->must_unload()) {
      ask = true;                     /* ask operator to mount tape */
      do_find = true;                 /* re-find a volume after unload */
   }
   do_unload();
   do_swapping(true /*is_writing*/);
   do_load(true /*is_writing*/);

   if (do_find && !find_a_volume()) {
      goto no_lock_bail_out;
   }

   if (job_canceled(jcr)) {
      goto bail_out;
   }
   Dmsg3(150, "After find_next_append. Vol=%s Slot=%d Parts=%d\n",
         VolCatInfo.VolCatName, VolCatInfo.Slot, VolCatInfo.VolCatParts);
   
   /*
    * Get next volume and ready it for append
    * This code ensures that the device is ready for
    * writing. We start from the assumption that there
    * may not be a tape mounted.
    *
    * If the device is a file, we create the output
    * file. If it is a tape, we check the volume name
    * and move the tape to the end of data.
    *
    */
   if (autoload_device(dcr, true/*writing*/, NULL) > 0) {
      autochanger = true;
      ask = false;
   } else {
      autochanger = false;
      VolCatInfo.Slot = 0;
      ask = retry >= 2;
   }
   Dmsg1(150, "autoload_dev returns %d\n", autochanger);
   /*
    * If we autochanged to correct Volume or (we have not just
    *   released the Volume AND we can automount) we go ahead
    *   and read the label. If there is no tape in the drive,
    *   we will fail, recurse and ask the operator the next time.
    */
   if (!dev->must_unload() && dev->is_tape() && dev->has_cap(CAP_AUTOMOUNT)) {
      Dmsg0(250, "(1)Ask=0\n");
      ask = false;                 /* don't ask SYSOP this time */
   }
   /* Don't ask if not removable */
   if (!dev->is_removable()) {
      Dmsg0(250, "(2)Ask=0\n");
      ask = false;
   }
   Dmsg2(250, "Ask=%d autochanger=%d\n", ask, autochanger);

   if (ask) {
      unlock_volumes();
      if (!dir_ask_sysop_to_mount_volume(dcr, ST_APPEND)) {
         Dmsg0(150, "Error return ask_sysop ...\n");
         goto no_lock_bail_out;
      }
      lock_volumes();
   }
   if (job_canceled(jcr)) {
      goto bail_out;
   }
   Dmsg3(150, "want vol=%s devvol=%s dev=%s\n", VolumeName, 
      dev->VolHdr.VolumeName, dev->print_name());

   if (dev->poll && dev->has_cap(CAP_CLOSEONPOLL)) {
      dev->close();
   }

   /* Ensure the device is open */
   if (dev->has_cap(CAP_STREAM)) {
      mode = OPEN_WRITE_ONLY;
   } else {
      mode = OPEN_READ_WRITE;
   }
   /* Try autolabel if enabled */
   if (dev->open(dcr, mode) < 0) {
      try_autolabel(false);      /* try to create a new volume label */
   }
   while (dev->open(dcr, mode) < 0) {
      Dmsg1(150, "open_device failed: ERR=%s\n", dev->bstrerror());
      if ((dev->is_file() && dev->is_removable()) || dev->is_dvd()) {
         bool ok = true;
         Dmsg0(150, "call scan_dir_for_vol\n");
         if (dev->is_dvd()) {
            if (!dev->mount(0)) {
               ok = false;
            }
         }
         if (ok && dev->scan_dir_for_volume(dcr)) {
            if (dev->open(dcr, mode) >= 0) {
               break;                    /* got a valid volume */
            }
         }
         if (ok && dev->is_dvd()) {
            dev->unmount(0);
         }
      }
      if (try_autolabel(false) == try_read_vol) {
         break;                       /* created a new volume label */
      }
      Dmsg0(50, "set_unload\n");
      dev->set_unload();              /* force ask sysop */
      ask = true;
      Dmsg0(150, "goto mount_next_vol\n");
      goto mount_next_vol;
   }

   /*
    * Now check the volume label to make sure we have the right tape mounted
    */
read_volume:
   switch (check_volume_label(ask, autochanger)) {
   case check_next_vol:
      Dmsg0(50, "set_unload\n");
      dev->set_unload();                 /* want a different Volume */
      Dmsg0(150, "goto mount_next_vol\n");
      goto mount_next_vol;
   case check_read_vol:
      goto read_volume;
   case check_error:
      goto bail_out;
   case check_ok:
      break;
   }

   /*
    * See if we have a fresh tape or a tape with data.
    *
    * Note, if the LabelType is PRE_LABEL, it was labeled
    *  but never written. If so, rewrite the label but set as
    *  VOL_LABEL.  We rewind and return the label (reconstructed)
    *  in the block so that in the case of a new tape, data can
    *  be appended just after the block label.  If we are writing
    *  a second volume, the calling routine will write the label
    *  before writing the overflow block.
    *
    *  If the tape is marked as Recycle, we rewrite the label.
    */
   recycle = strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0;
   if (dev->VolHdr.LabelType == PRE_LABEL || recycle) {
      if (!rewrite_volume_label(dcr, recycle)) {
         mark_volume_in_error();
         goto mount_next_vol;
      }
   } else {
      /*
       * OK, at this point, we have a valid Bacula label, but
       * we need to position to the end of the volume, since we are
       * just now putting it into append mode.
       */
      Dmsg0(200, "Device previously written, moving to end of data\n");
      Jmsg(jcr, M_INFO, 0, _("Volume \"%s\" previously written, moving to end of data.\n"),
         VolumeName);

      if (!dev->eod(dcr)) {
         Jmsg(jcr, M_ERROR, 0, _("Unable to position to end of data on device %s: ERR=%s\n"),
            dev->print_name(), dev->bstrerror());
         mark_volume_in_error();
         goto mount_next_vol;
      }
      if (!is_eod_valid()) {
         Dmsg0(150, "goto mount_next_vol\n");
         goto mount_next_vol;
      }

      dev->VolCatInfo.VolCatMounts++;      /* Update mounts */
      Dmsg1(150, "update volinfo mounts=%d\n", dev->VolCatInfo.VolCatMounts);
      if (!dir_update_volume_info(dcr, false, false)) {
         goto bail_out;
      }
      
      /* Return an empty block */
      empty_block(block);             /* we used it for reading so set for write */
   }
   dev->set_append();
   Dmsg1(150, "set APPEND, normal return from mount_next_write_volume. dev=%s\n",
      dev->print_name());

   unlock_volumes();
   return true;

bail_out:
   unlock_volumes();

no_lock_bail_out:
   return false;
}
Example #6
0
/**
 * Get info on the next appendable volume in the Director's database
 *
 * Returns: true  on success dcr->VolumeName is volume
 *                reserve_volume() called on Volume name
 *          false on failure dcr->VolumeName[0] == 0
 *                also sets dcr->found_in_use if at least one
 *                in use volume was found.
 *
 * Volume information returned in dcr
 */
bool SD_DCR::dir_find_next_appendable_volume()
{
    bool retval;
    BSOCK *dir = jcr->dir_bsock;
    POOL_MEM unwanted_volumes(PM_MESSAGE);

    Dmsg2(dbglvl, "dir_find_next_appendable_volume: reserved=%d Vol=%s\n", is_reserved(), VolumeName);

    /*
     * Try the twenty oldest or most available volumes. Note,
     * the most available could already be mounted on another
     * drive, so we continue looking for a not in use Volume.
     */
    lock_volumes();
    P(vol_info_mutex);
    clear_found_in_use();

    pm_strcpy(unwanted_volumes, "");
    for (int vol_index = 1; vol_index < 20; vol_index++) {
       bash_spaces(media_type);
       bash_spaces(pool_name);
       bash_spaces(unwanted_volumes.c_str());
       dir->fsend(Find_media, jcr->Job, vol_index, pool_name, media_type, unwanted_volumes.c_str());
       unbash_spaces(media_type);
       unbash_spaces(pool_name);
       unbash_spaces(unwanted_volumes.c_str());
       Dmsg1(dbglvl, ">dird %s", dir->msg);

       if (do_get_volume_info(this)) {
          if (vol_index == 1) {
             pm_strcpy(unwanted_volumes, VolumeName);
          } else {
             pm_strcat(unwanted_volumes, ",");
             pm_strcat(unwanted_volumes, VolumeName);
          }

          if (can_i_write_volume()) {
             Dmsg1(dbglvl, "Call reserve_volume for write. Vol=%s\n", VolumeName);
             if (reserve_volume(this, VolumeName) == NULL) {
                Dmsg2(dbglvl, "Could not reserve volume %s on %s\n", VolumeName, dev->print_name());
                continue;
             }
             Dmsg1(dbglvl, "dir_find_next_appendable_volume return true. vol=%s\n", VolumeName);
             retval = true;
             goto get_out;
          } else {
             Dmsg1(dbglvl, "Volume %s is in use.\n", VolumeName);

             /*
              * If volume is not usable, it is in use by someone else
              */
             set_found_in_use();
             continue;
          }
       }
       Dmsg2(dbglvl, "No vol. index %d return false. dev=%s\n", vol_index, dev->print_name());
       break;
    }
    retval = false;
    VolumeName[0] = 0;

get_out:
    V(vol_info_mutex);
    unlock_volumes();

    return retval;
}