/*
 * Load medium in device
 *
 * Returns: true  on success
 *          false on failure
 */
bool generic_tape_device::load_dev()
{
#ifdef MTLOAD
   struct mtop mt_com;
#endif

   if (m_fd < 0) {
      dev_errno = EBADF;
      Mmsg0(errmsg, _("Bad call to load_dev. Device not open\n"));
      Emsg0(M_FATAL, 0, errmsg);
      return false;
   }

#ifndef MTLOAD
   Dmsg0(200, "stored: MTLOAD command not available\n");
   berrno be;
   dev_errno = ENOTTY;           /* function not available */
   Mmsg2(errmsg, _("ioctl MTLOAD error on %s. ERR=%s.\n"), prt_name, be.bstrerror());
   return false;
#else
   block_num = file = 0;
   file_size = 0;
   file_addr = 0;
   mt_com.mt_op = MTLOAD;
   mt_com.mt_count = 1;
   if (d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0) {
      berrno be;
      dev_errno = errno;
      Mmsg2(errmsg, _("ioctl MTLOAD error on %s. ERR=%s.\n"), prt_name, be.bstrerror());
      return false;
   }

   return true;
#endif
}
Exemple #2
0
bool gfapi_device::d_truncate(DCR *dcr)
{
   struct stat st;

   if (m_gfd) {
      if (glfs_ftruncate(m_gfd, 0) != 0) {
         berrno be;

         Mmsg2(errmsg, _("Unable to truncate device %s. ERR=%s\n"),
               print_name(), be.bstrerror());
         Emsg0(M_FATAL, 0, errmsg);
         return false;
      }

      /*
       * Check for a successful glfs_truncate() and issue work-around when truncation doesn't work.
       *
       * 1. close file
       * 2. delete file
       * 3. open new file with same mode
       * 4. change ownership to original
       */
      if (glfs_fstat(m_gfd, &st) != 0) {
         berrno be;

         Mmsg2(errmsg, _("Unable to stat device %s. ERR=%s\n"), print_name(), be.bstrerror());
         return false;
      }

      if (st.st_size != 0) {             /* glfs_truncate() didn't work */
         glfs_close(m_gfd);
         glfs_unlink(m_glfs, getVolCatName());

         set_mode(CREATE_READ_WRITE);

         /*
          * Recreate the file -- of course, empty
          */
         m_gfd = glfs_creat(m_glfs, getVolCatName(), oflags, st.st_mode);
         if (!m_gfd) {
            berrno be;

            dev_errno = errno;
            Mmsg2(errmsg, _("Could not reopen: %s, ERR=%s\n"), getVolCatName(), be.bstrerror());
            Emsg0(M_FATAL, 0, errmsg);

            return false;
         }

         /*
          * Reset proper owner
          */
         glfs_chown(m_glfs, getVolCatName(), st.st_uid, st.st_gid);
      }
   }

   return true;
}
Exemple #3
0
/*
 * Set the position of the device -- only for files and DVD
 *   For other devices, there is no generic way to do it.
 *  Returns: true  on succes
 *           false on error
 */
bool DEVICE::update_pos(DCR *dcr)
{
   boffset_t pos;
   bool ok = true;

   if (!is_open()) {
      dev_errno = EBADF;
      Mmsg0(errmsg, _("Bad device call. Device not open\n"));
      Emsg1(M_FATAL, 0, "%s", errmsg);
      return false;
   }

   if (is_file()) {
      file = 0;
      file_addr = 0;
      pos = lseek(dcr, (boffset_t)0, SEEK_CUR);
      if (pos < 0) {
         berrno be;
         dev_errno = errno;
         Pmsg1(000, _("Seek error: ERR=%s\n"), be.bstrerror());
         Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
               print_name(), be.bstrerror());
         ok = false;
      } else {
         file_addr = pos;
         block_num = (uint32_t)pos;
         file = (uint32_t)(pos >> 32);
      }
   }
   return ok;
}
Exemple #4
0
/*
 * Reposition the device to file, block
 *
 * Returns: false on failure
 *          true  on success
 */
bool DEVICE::reposition(DCR *dcr, uint32_t rfile, uint32_t rblock)
{
   if (!is_open()) {
      dev_errno = EBADF;
      Mmsg0(errmsg, _("Bad call to reposition. Device not open\n"));
      Emsg0(M_FATAL, 0, errmsg);
      return false;
   }

   if (is_fifo() || is_vtl()) {
      return true;
   }

   boffset_t pos = (((boffset_t)rfile) << 32) | rblock;
   Dmsg1(100, "===== lseek to %d\n", (int)pos);
   if (lseek(dcr, pos, SEEK_SET) == (boffset_t)-1) {
      berrno be;
      dev_errno = errno;
      Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"), print_name(), be.bstrerror());
      return false;
   }
   file = rfile;
   block_num = rblock;
   file_addr = pos;
   return true;
}
Exemple #5
0
/*
 * Rewind the device.
 *
 * Returns: true  on success
 *          false on failure
 */
bool DEVICE::rewind(DCR *dcr)
{
   Dmsg3(400, "rewind res=%d fd=%d %s\n", num_reserved(), m_fd, print_name());

   /*
    * Remove EOF/EOT flags
    */
   clear_bit(ST_EOT, state);
   clear_bit(ST_EOF, state);
   clear_bit(ST_WEOT, state);

   block_num = file = 0;
   file_size = 0;
   file_addr = 0;

   if (m_fd < 0) {
      return false;
   }

   if (is_fifo() || is_vtl()) {
      return true;
   }

   if (lseek(dcr, (boffset_t)0, SEEK_SET) < 0) {
      berrno be;
      dev_errno = errno;
      Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"), print_name(), be.bstrerror());
      return false;
   }

   return true;
}
/*
 * Backward space a record
 *
 * Returns:  false on failure
 *           true  on success
 */
bool generic_tape_device::bsr(int num)
{
   struct mtop mt_com;
   int status;

   if (!is_open()) {
      dev_errno = EBADF;
      Mmsg0(errmsg, _("Bad call to bsr_dev. Device not open\n"));
      Emsg0(M_FATAL, 0, errmsg);
      return false;
   }

   if (!has_cap(CAP_BSR)) {
      Mmsg1(errmsg, _("ioctl MTBSR not permitted on %s.\n"), prt_name);
      return false;
   }

   Dmsg0(100, "bsr_dev\n");
   block_num -= num;
   clear_eof();
   clear_eot();
   mt_com.mt_op = MTBSR;
   mt_com.mt_count = num;

   status = d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
   if (status < 0) {
      berrno be;

      clrerror(mt_com.mt_op);
      Mmsg2(errmsg, _("ioctl MTBSR error on %s. ERR=%s.\n"), prt_name, be.bstrerror());
   }

   return status == 0;
}
/*
 * Backward space a file
 *
 * Returns: false on failure
 *          true  on success
 */
bool generic_tape_device::bsf(int num)
{
   struct mtop mt_com;
   int status;

   if (!is_open()) {
      dev_errno = EBADF;
      Mmsg0(errmsg, _("Bad call to bsf. Device not open\n"));
      Emsg0(M_FATAL, 0, errmsg);
      return false;
   }

   Dmsg0(100, "bsf\n");
   clear_eot();
   clear_eof();
   file -= num;
   file_addr = 0;
   file_size = 0;
   mt_com.mt_op = MTBSF;
   mt_com.mt_count = num;

   status = d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
   if (status < 0) {
      berrno be;

      clrerror(mt_com.mt_op);
      Mmsg2(errmsg, _("ioctl MTBSF error on %s. ERR=%s.\n"), prt_name, be.bstrerror());
   }

   return status == 0;
}
Exemple #8
0
/*
 * Open a device.
 */
void DEVICE::open_device(DCR *dcr, int omode)
{
   POOL_MEM archive_name(PM_FNAME);

   get_autochanger_loaded_slot(dcr);

   /*
    * Handle opening of File Archive (not a tape)
    */
   pm_strcpy(archive_name, dev_name);

   /*
    * If this is a virtual autochanger (i.e. changer_res != NULL) we simply use
    * the device name, assuming it has been appropriately setup by the "autochanger".
    */
   if (!device->changer_res || device->changer_command[0] == 0) {
      if (VolCatInfo.VolCatName[0] == 0) {
         Mmsg(errmsg, _("Could not open file device %s. No Volume name given.\n"),
            print_name());
         clear_opened();
         return;
      }

      if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) {
         pm_strcat(archive_name, "/");
      }
      pm_strcat(archive_name, getVolCatName());
   }

   mount(dcr, 1);                     /* do mount if required */

   open_mode = omode;
   set_mode(omode);

   /*
    * If creating file, give 0640 permissions
    */
   Dmsg3(100, "open disk: mode=%s open(%s, 0x%x, 0640)\n", mode_to_str(omode),
         archive_name.c_str(), oflags);

   if ((m_fd = d_open(archive_name.c_str(), oflags, 0640)) < 0) {
      berrno be;
      dev_errno = errno;
      Mmsg2(errmsg, _("Could not open: %s, ERR=%s\n"), archive_name.c_str(),
            be.bstrerror());
      Dmsg1(100, "open failed: %s", errmsg);
   }

   if (m_fd >= 0) {
      dev_errno = 0;
      file = 0;
      file_addr = 0;
   }

   Dmsg1(100, "open dev: disk fd=%d opened\n", m_fd);
}
Exemple #9
0
/*
 * Close the device
 */
bool DEVICE::close()
{
   bool ok = true;

   Dmsg4(40, "close_dev vol=%s fd=%d dev=%p dev=%s\n",
      VolHdr.VolumeName, m_fd, this, print_name());
   offline_or_rewind();

   if (!is_open()) {
      Dmsg2(200, "device %s already closed vol=%s\n", print_name(),
         VolHdr.VolumeName);
      return true;                    /* already closed */
   }

   switch (dev_type) {
   case B_VTL_DEV:
   case B_VTAPE_DEV:
   case B_TAPE_DEV:
      unlock_door();
      /* Fall through wanted */
   default:
      if (d_close(m_fd) != 0) {
         berrno be;
         dev_errno = errno;
         Mmsg2(errmsg, _("Error closing device %s. ERR=%s.\n"),
               print_name(), be.bstrerror());
         ok = false;
      }
      break; 
   }

   unmount(1);                       /* do unmount if required */
 
   /* Clean up device packet so it can be reused */
   clear_opened();

   /*
    * Be careful not to clear items needed by the DVD driver
    *    when it is closing a single part.
    */
   state &= ~(ST_LABEL|ST_READ|ST_APPEND|ST_EOT|ST_WEOT|ST_EOF|
              ST_NOSPACE|ST_MOUNTED|ST_MEDIA|ST_SHORT);
   label_type = B_BACULA_LABEL;
   file = block_num = 0;
   file_size = 0;
   file_addr = 0;
   EndFile = EndBlock = 0;
   openmode = 0;
   clear_volhdr();
   memset(&VolCatInfo, 0, sizeof(VolCatInfo));
   if (tid) {
      stop_thread_timer(tid);
      tid = 0;
   }
   return ok;
}
Exemple #10
0
/*
 * Get a non-pooled connection used when either sql pooling is
 * runtime disabled or at compile time. Or when we run out of
 * pooled connections and need more database connections.
 */
B_DB *db_sql_get_non_pooled_connection(JCR *jcr,
                                       const char *db_drivername,
                                       const char *db_name,
                                       const char *db_user,
                                       const char *db_password,
                                       const char *db_address,
                                       int db_port,
                                       const char *db_socket,
                                       bool mult_db_connections,
                                       bool disable_batch_insert,
                                       bool need_private)
{
   B_DB *mdb;

#if defined(HAVE_DYNAMIC_CATS_BACKENDS)
   Dmsg2(100, "db_sql_get_non_pooled_connection allocating 1 new non pooled database connection to database %s, backend type %s\n",
         db_name, db_drivername);
#else
   Dmsg1(100, "db_sql_get_non_pooled_connection allocating 1 new non pooled database connection to database %s\n",
         db_name);
#endif
   mdb = db_init_database(jcr,
                          db_drivername,
                          db_name,
                          db_user,
                          db_password,
                          db_address,
                          db_port,
                          db_socket,
                          mult_db_connections,
                          disable_batch_insert,
                          need_private);
   if (mdb == NULL) {
      return NULL;
   }

   if (!db_open_database(jcr, mdb)) {
      Mmsg2(mdb->errmsg, _("Could not open database \"%s\": ERR=%s\n"), db_name, db_strerror(mdb));
      Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
      db_close_database(jcr, mdb);
      return NULL;
   }

   return mdb;
}
/*
 * Write data to a volume using libdroplet.
 */
ssize_t object_store_device::d_write(int fd, const void *buffer, size_t count)
{
   if (m_vfd) {
      dpl_status_t status;

      status = dpl_pwrite(m_vfd, (char *)buffer, count, m_offset);
      switch (status) {
      case DPL_SUCCESS:
         m_offset += count;
         return count;
      default:
         Mmsg2(errmsg, _("Failed to write %s using dpl_write(): ERR=%s.\n"),
               getVolCatName(), dpl_status_str(status));
         return droplet_errno_to_system_errno(status);
      }
   } else {
      errno = EBADF;
      return -1;
   }
}
Exemple #12
0
/*
 * Find the most recent successful real end time for a job given.
 *
 *  RealEndTime is returned in etime
 *  Job name is returned in job (MAX_NAME_LENGTH)
 *
 * Returns: false on failure
 *          true  on success, jr is unchanged, but etime and job are set
 */
bool BDB::bdb_find_last_job_end_time(JCR *jcr, JOB_DBR *jr, POOLMEM **etime, 
          char *job)
{
   SQL_ROW row;
   char ed1[50], ed2[50];
   char esc_name[MAX_ESCAPE_NAME_LENGTH];

   bdb_lock();
   bdb_escape_string(jcr, esc_name, jr->Name, strlen(jr->Name));
   pm_strcpy(etime, "0000-00-00 00:00:00");   /* default */
   job[0] = 0;

   Mmsg(cmd,
        "SELECT RealEndTime, Job FROM Job WHERE JobStatus IN ('T','W') AND Type='%c' AND "
        "Level IN ('%c','%c','%c') AND Name='%s' AND ClientId=%s AND FileSetId=%s "
        "ORDER BY RealEndTime DESC LIMIT 1", jr->JobType, 
        L_FULL, L_DIFFERENTIAL, L_INCREMENTAL, esc_name, 
        edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2));

   if (!QueryDB(jcr, cmd)) {
      Mmsg2(&errmsg, _("Query error for end time request: ERR=%s\nCMD=%s\n"),
         sql_strerror(), cmd);
      goto bail_out;
   }
   if ((row = sql_fetch_row()) == NULL) {
      sql_free_result();
      Mmsg(errmsg, _("No prior backup Job record found.\n"));
      goto bail_out;
   }
   Dmsg1(100, "Got end time: %s\n", row[0]);
   pm_strcpy(etime, row[0]);
   bstrncpy(job, row[1], MAX_NAME_LENGTH);

   sql_free_result();
   bdb_unlock();
   return true;

bail_out:
   bdb_unlock();
   return false;
}
Exemple #13
0
/*
 * Position device to end of medium (end of data)
 *
 * Returns: true  on succes
 *          false on error
 */
bool DEVICE::eod(DCR *dcr)
{
   boffset_t pos;

   if (m_fd < 0) {
      dev_errno = EBADF;
      Mmsg1(errmsg, _("Bad call to eod. Device %s not open\n"), print_name());
      return false;
   }

   if (is_vtl()) {
      return true;
   }

   Dmsg0(100, "Enter eod\n");
   if (at_eot()) {
      return true;
   }

   clear_eof();         /* remove EOF flag */

   block_num = file = 0;
   file_size = 0;
   file_addr = 0;

   pos = lseek(dcr, (boffset_t)0, SEEK_END);
   Dmsg1(200, "====== Seek to %lld\n", pos);

   if (pos >= 0) {
      update_pos(dcr);
      set_eot();
      return true;
   }

   dev_errno = errno;
   berrno be;
   Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"), print_name(), be.bstrerror());
   Dmsg0(100, errmsg);

   return false;
}
Exemple #14
0
/*
 * Find the last job start time for the specified JobLevel
 *
 *  StartTime is returned in stime
 *  Job name is returned in job (MAX_NAME_LENGTH)
 *
 * Returns: false on failure
 *          true  on success, jr is unchanged, but stime and job are set
 */
bool db_find_last_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr,
                                 POOLMEM **stime, char *job, int JobLevel)
{
   bool retval = false;
   SQL_ROW row;
   char ed1[50], ed2[50];
   char esc_name[MAX_ESCAPE_NAME_LENGTH];

   db_lock(mdb);
   mdb->db_escape_string(jcr, esc_name, jr->Name, strlen(jr->Name));
   pm_strcpy(stime, "0000-00-00 00:00:00");   /* default */
   job[0] = 0;

   Mmsg(mdb->cmd,
"SELECT StartTime, Job FROM Job WHERE JobStatus IN ('T','W') AND Type='%c' AND "
"Level='%c' AND Name='%s' AND ClientId=%s AND FileSetId=%s "
"ORDER BY StartTime DESC LIMIT 1",
      jr->JobType, JobLevel, esc_name,
      edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2));
   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
      Mmsg2(&mdb->errmsg, _("Query error for start time request: ERR=%s\nCMD=%s\n"),
         sql_strerror(mdb), mdb->cmd);
      goto bail_out;
   }
   if ((row = sql_fetch_row(mdb)) == NULL) {
      sql_free_result(mdb);
      Mmsg(mdb->errmsg, _("No prior Full backup Job record found.\n"));
      goto bail_out;
   }
   Dmsg1(100, "Got start time: %s\n", row[0]);
   pm_strcpy(stime, row[0]);
   bstrncpy(job, row[1], MAX_NAME_LENGTH);

   sql_free_result(mdb);
   retval = true;

bail_out:
   db_unlock(mdb);
   return retval;
}
/*
 * Write an end of file on the device
 *
 * Returns: true on success
 *          false on failure
 */
bool generic_tape_device::weof(int num)
{
   struct mtop mt_com;
   int status;
   Dmsg1(129, "=== weof_dev=%s\n", prt_name);

   if (!is_open()) {
      dev_errno = EBADF;
      Mmsg0(errmsg, _("Bad call to weof_dev. Device not open\n"));
      Emsg0(M_FATAL, 0, errmsg);
      return false;
   }
   file_size = 0;

   if (!can_append()) {
      Mmsg0(errmsg, _("Attempt to WEOF on non-appendable Volume\n"));
      Emsg0(M_FATAL, 0, errmsg);
      return false;
   }

   clear_eof();
   clear_eot();
   mt_com.mt_op = MTWEOF;
   mt_com.mt_count = num;
   status = d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
   if (status == 0) {
      block_num = 0;
      file += num;
      file_addr = 0;
   } else {
      berrno be;

      clrerror(mt_com.mt_op);
      if (status == -1) {
         Mmsg2(errmsg, _("ioctl MTWEOF error on %s. ERR=%s.\n"), prt_name, be.bstrerror());
       }
   }

   return status == 0;
}
/*
 * Rewind device and put it offline
 *
 * Returns: true  on success
 *          false on failure
 */
bool generic_tape_device::offline()
{
   struct mtop mt_com;

   state &= ~(ST_APPENDREADY | ST_READREADY | ST_EOT | ST_EOF | ST_WEOT);  /* remove EOF/EOT flags */
   block_num = file = 0;
   file_size = 0;
   file_addr = 0;
   unlock_door();
   mt_com.mt_op = MTOFFL;
   mt_com.mt_count = 1;

   if (d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0) {
      berrno be;
      dev_errno = errno;
      Mmsg2(errmsg, _("ioctl MTOFFL error on %s. ERR=%s.\n"), prt_name, be.bstrerror());
      return false;
   }
   Dmsg1(100, "Offlined device %s\n", prt_name);

   return true;
}
/*
 * Read data from a volume using libdroplet.
 */
ssize_t object_store_device::d_read(int fd, void *buffer, size_t count)
{
   if (m_vfd) {
      unsigned int buflen;
      dpl_status_t status;

      buflen = count;
      status = dpl_pread(m_vfd, count, m_offset, (char **)&buffer, &buflen);

      switch (status) {
      case DPL_SUCCESS:
         m_offset += buflen;
         return buflen;
      default:
         Mmsg2(errmsg, _("Failed to read %s using dpl_read(): ERR=%s.\n"),
               getVolCatName(), dpl_status_str(status));
         return droplet_errno_to_system_errno(status);
      }
   } else {
      errno = EBADF;
      return -1;
   }
}
Exemple #18
0
/*
 * Find job start time if JobId specified, otherwise
 * find last Job start time Incremental and Differential saves.
 *
 *  StartTime is returned in stime
 *  Job name is returned in job (MAX_NAME_LENGTH)
 *
 * Returns: 0 on failure
 *          1 on success, jr is unchanged, but stime and job are set
 */
bool db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime, char *job)
{
   bool retval = false;
   SQL_ROW row;
   char ed1[50], ed2[50];
   char esc_name[MAX_ESCAPE_NAME_LENGTH];

   db_lock(mdb);
   mdb->db_escape_string(jcr, esc_name, jr->Name, strlen(jr->Name));
   pm_strcpy(stime, "0000-00-00 00:00:00");   /* default */
   job[0] = 0;

   /* If no Id given, we must find corresponding job */
   if (jr->JobId == 0) {
      /* Differential is since last Full backup */
      Mmsg(mdb->cmd,
"SELECT StartTime, Job FROM Job WHERE JobStatus IN ('T','W') AND Type='%c' AND "
"Level='%c' AND Name='%s' AND ClientId=%s AND FileSetId=%s "
"ORDER BY StartTime DESC LIMIT 1",
           jr->JobType, L_FULL, esc_name,
           edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2));

      if (jr->JobLevel == L_DIFFERENTIAL) {
         /* SQL cmd for Differential backup already edited above */

      /* Incremental is since last Full, Incremental, or Differential */
      } else if (jr->JobLevel == L_INCREMENTAL) {
         /*
          * For an Incremental job, we must first ensure
          *  that a Full backup was done (cmd edited above)
          *  then we do a second look to find the most recent
          *  backup
          */
         if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
            Mmsg2(&mdb->errmsg, _("Query error for start time request: ERR=%s\nCMD=%s\n"),
               sql_strerror(mdb), mdb->cmd);
            goto bail_out;
         }
         if ((row = sql_fetch_row(mdb)) == NULL) {
            sql_free_result(mdb);
            Mmsg(mdb->errmsg, _("No prior Full backup Job record found.\n"));
            goto bail_out;
         }
         sql_free_result(mdb);
         /* Now edit SQL command for Incremental Job */
         Mmsg(mdb->cmd,
"SELECT StartTime, Job FROM Job WHERE JobStatus IN ('T','W') AND Type='%c' AND "
"Level IN ('%c','%c','%c') AND Name='%s' AND ClientId=%s "
"AND FileSetId=%s ORDER BY StartTime DESC LIMIT 1",
            jr->JobType, L_INCREMENTAL, L_DIFFERENTIAL, L_FULL, esc_name,
            edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2));
      } else {
         Mmsg1(mdb->errmsg, _("Unknown level=%d\n"), jr->JobLevel);
         goto bail_out;
      }
   } else {
      Dmsg1(100, "Submitting: %s\n", mdb->cmd);
      Mmsg(mdb->cmd, "SELECT StartTime, Job FROM Job WHERE Job.JobId=%s",
           edit_int64(jr->JobId, ed1));
   }

   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
      pm_strcpy(stime, "");                   /* set EOS */
      Mmsg2(&mdb->errmsg, _("Query error for start time request: ERR=%s\nCMD=%s\n"),
         sql_strerror(mdb),  mdb->cmd);
      goto bail_out;
   }

   if ((row = sql_fetch_row(mdb)) == NULL) {
      Mmsg2(&mdb->errmsg, _("No Job record found: ERR=%s\nCMD=%s\n"),
         sql_strerror(mdb),  mdb->cmd);
      sql_free_result(mdb);
      goto bail_out;
   }
   Dmsg2(100, "Got start time: %s, job: %s\n", row[0], row[1]);
   pm_strcpy(stime, row[0]);
   bstrncpy(job, row[1], MAX_NAME_LENGTH);

   sql_free_result(mdb);
   retval = true;

bail_out:
   db_unlock(mdb);
   return retval;
}
Exemple #19
0
/*
 * Find Available Media (Volume) for Pool
 *
 * Find a Volume for a given PoolId, MediaType, and Status.
 * The unwanted_volumes variable lists the VolumeNames which we should skip if any.
 *
 * Returns: 0 on failure
 *          numrows on success
 */
int db_find_next_volume(JCR *jcr, B_DB *mdb, int item, bool InChanger, MEDIA_DBR *mr, const char *unwanted_volumes)
{
   char ed1[50];
   int num_rows = 0;
   SQL_ROW row = NULL;
   bool find_oldest = false;
   bool found_candidate = false;
   char esc_type[MAX_ESCAPE_NAME_LENGTH];
   char esc_status[MAX_ESCAPE_NAME_LENGTH];

   db_lock(mdb);

   mdb->db_escape_string(jcr, esc_type, mr->MediaType, strlen(mr->MediaType));
   mdb->db_escape_string(jcr, esc_status, mr->VolStatus, strlen(mr->VolStatus));

   if (item == -1) {
      find_oldest = true;
      item = 1;
   }

retry_fetch:
   if (find_oldest) {
      /*
       * Find oldest volume(s)
       */
      Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
                     "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
                     "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
                     "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
                     "EndFile,EndBlock,LabelType,LabelDate,StorageId,"
                     "Enabled,LocationId,RecycleCount,InitialWrite,"
                     "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,"
                     "ActionOnPurge,EncryptionKey,MinBlocksize,MaxBlocksize "
                     "FROM Media WHERE PoolId=%s AND MediaType='%s' AND VolStatus IN ('Full',"
                     "'Recycle','Purged','Used','Append') AND Enabled=1 "
                     "ORDER BY LastWritten LIMIT %d",
           edit_int64(mr->PoolId, ed1), esc_type, item);
   } else {
      POOL_MEM changer(PM_FNAME);
      const char *order;

      /*
       * Find next available volume
       */
      if (InChanger) {
         Mmsg(changer, "AND InChanger=1 AND StorageId=%s", edit_int64(mr->StorageId, ed1));
      }

      if (bstrcmp(mr->VolStatus, "Recycle") ||
          bstrcmp(mr->VolStatus, "Purged")) {
         order = "AND Recycle=1 ORDER BY LastWritten ASC,MediaId";  /* take oldest that can be recycled */
      } else {
         order = sql_media_order_most_recently_written[db_get_type_index(mdb)];    /* take most recently written */
      }

      Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
                     "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
                     "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
                     "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
                     "EndFile,EndBlock,LabelType,LabelDate,StorageId,"
                     "Enabled,LocationId,RecycleCount,InitialWrite,"
                     "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,"
                     "ActionOnPurge,EncryptionKey,MinBlocksize,MaxBlocksize "
                     "FROM Media WHERE PoolId=%s AND MediaType='%s' AND Enabled=1 "
                     "AND VolStatus='%s' "
                     "%s "
                     "%s LIMIT %d",
           edit_int64(mr->PoolId, ed1), esc_type,
           esc_status, changer.c_str(), order, item);
   }

   Dmsg1(100, "fnextvol=%s\n", mdb->cmd);
   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
      goto bail_out;
   }

   num_rows = sql_num_rows(mdb);
   if (item > num_rows || item < 1) {
      Dmsg2(050, "item=%d got=%d\n", item, num_rows);
      Mmsg2(&mdb->errmsg, _("Request for Volume item %d greater than max %d or less than 1\n"), item, num_rows);
      num_rows = 0;
      goto bail_out;
   }

   for (int i = 0 ; i < item; i++) {
      if ((row = sql_fetch_row(mdb)) == NULL) {
         Dmsg1(050, "Fail fetch item=%d\n", i);
         Mmsg1(&mdb->errmsg, _("No Volume record found for item %d.\n"), i);
         sql_free_result(mdb);
         num_rows = 0;
         goto bail_out;
      }

      /*
       * See if this is not on the unwanted volumes list.
       */
      if (unwanted_volumes && is_on_unwanted_volumes_list(row[1], unwanted_volumes)) {
         continue;
      }

      /*
       * Return fields in Media Record
       */
      mr->MediaId = str_to_int64(row[0]);
      bstrncpy(mr->VolumeName, (row[1] != NULL) ? row[1] : "", sizeof(mr->VolumeName));
      mr->VolJobs = str_to_int64(row[2]);
      mr->VolFiles = str_to_int64(row[3]);
      mr->VolBlocks = str_to_int64(row[4]);
      mr->VolBytes = str_to_uint64(row[5]);
      mr->VolMounts = str_to_int64(row[6]);
      mr->VolErrors = str_to_int64(row[7]);
      mr->VolWrites = str_to_int64(row[8]);
      mr->MaxVolBytes = str_to_uint64(row[9]);
      mr->VolCapacityBytes = str_to_uint64(row[10]);
      bstrncpy(mr->MediaType, (row[11] != NULL) ? row[11] : "", sizeof(mr->MediaType));
      bstrncpy(mr->VolStatus, (row[12] != NULL) ? row[12] : "", sizeof(mr->VolStatus));
      mr->PoolId = str_to_int64(row[13]);
      mr->VolRetention = str_to_uint64(row[14]);
      mr->VolUseDuration = str_to_uint64(row[15]);
      mr->MaxVolJobs = str_to_int64(row[16]);
      mr->MaxVolFiles = str_to_int64(row[17]);
      mr->Recycle = str_to_int64(row[18]);
      mr->Slot = str_to_int64(row[19]);
      bstrncpy(mr->cFirstWritten, (row[20] != NULL) ? row[20] : "", sizeof(mr->cFirstWritten));
      mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
      bstrncpy(mr->cLastWritten, (row[21] != NULL) ? row[21] : "", sizeof(mr->cLastWritten));
      mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
      mr->InChanger = str_to_uint64(row[22]);
      mr->EndFile = str_to_uint64(row[23]);
      mr->EndBlock = str_to_uint64(row[24]);
      mr->LabelType = str_to_int64(row[25]);
      bstrncpy(mr->cLabelDate, (row[26] != NULL) ? row[26] : "", sizeof(mr->cLabelDate));
      mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate);
      mr->StorageId = str_to_int64(row[27]);
      mr->Enabled = str_to_int64(row[28]);
      mr->LocationId = str_to_int64(row[29]);
      mr->RecycleCount = str_to_int64(row[30]);
      bstrncpy(mr->cInitialWrite, (row[31] != NULL) ? row[31] : "", sizeof(mr->cInitialWrite));
      mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite);
      mr->ScratchPoolId = str_to_int64(row[32]);
      mr->RecyclePoolId = str_to_int64(row[33]);
      mr->VolReadTime = str_to_int64(row[34]);
      mr->VolWriteTime = str_to_int64(row[35]);
      mr->ActionOnPurge = str_to_int64(row[36]);
      bstrncpy(mr->EncrKey, (row[37] != NULL) ? row[37] : "", sizeof(mr->EncrKey));
      mr->MinBlocksize = str_to_int32(row[38]);
      mr->MaxBlocksize = str_to_int32(row[39]);

      sql_free_result(mdb);
      found_candidate = true;
      break;
   }

   if (!found_candidate && find_oldest) {
      item++;
      goto retry_fetch;
   }

bail_out:
   db_unlock(mdb);
   Dmsg1(050, "Rtn numrows=%d\n", num_rows);

   return num_rows;
}
Exemple #20
0
void set_os_device_parameters(DCR *dcr)
{
   DEVICE *dev = dcr->dev;

   if (strcmp(dev->dev_name, "/dev/null") == 0) {
      return;                            /* no use trying to set /dev/null */
   }

#if defined(HAVE_LINUX_OS) || defined(HAVE_WIN32)
   struct mtop mt_com;

   Dmsg0(100, "In set_os_device_parameters\n");
#if defined(MTSETBLK)
   if (dev->min_block_size == dev->max_block_size &&
       dev->min_block_size == 0) {    /* variable block mode */
      mt_com.mt_op = MTSETBLK;
      mt_com.mt_count = 0;
      Dmsg0(100, "Set block size to zero\n");
      if (dev->d_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
         dev->clrerror(MTSETBLK);
      }
   }
#endif
#if defined(MTSETDRVBUFFER)
   if (getuid() == 0) {          /* Only root can do this */
      mt_com.mt_op = MTSETDRVBUFFER;
      mt_com.mt_count = MT_ST_CLEARBOOLEANS;
      if (!dev->has_cap(CAP_TWOEOF)) {
         mt_com.mt_count |= MT_ST_TWO_FM;
      }
      if (dev->has_cap(CAP_EOM)) {
         mt_com.mt_count |= MT_ST_FAST_MTEOM;
      }
      Dmsg0(100, "MTSETDRVBUFFER\n");
      if (dev->d_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
         dev->clrerror(MTSETDRVBUFFER);
      }
   }
#endif
   return;
#endif

#ifdef HAVE_NETBSD_OS
   struct mtop mt_com;
   if (dev->min_block_size == dev->max_block_size &&
       dev->min_block_size == 0) {    /* variable block mode */
      mt_com.mt_op = MTSETBSIZ;
      mt_com.mt_count = 0;
      if (dev->d_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
         dev->clrerror(MTSETBSIZ);
      }
      /* Get notified at logical end of tape */
      mt_com.mt_op = MTEWARN;
      mt_com.mt_count = 1;
      if (dev->d_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
         dev->clrerror(MTEWARN);
      }
   }
   return;
#endif

#if HAVE_FREEBSD_OS || HAVE_OPENBSD_OS
   struct mtop mt_com;
   if (dev->min_block_size == dev->max_block_size &&
       dev->min_block_size == 0) {    /* variable block mode */
      mt_com.mt_op = MTSETBSIZ;
      mt_com.mt_count = 0;
      if (dev->d_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
         dev->clrerror(MTSETBSIZ);
      }
   }
#if defined(MTIOCSETEOTMODEL)
   uint32_t neof;
   if (dev->has_cap(CAP_TWOEOF)) {
      neof = 2;
   } else {
      neof = 1;
   }
   if (dev->d_ioctl(dev->fd(), MTIOCSETEOTMODEL, (caddr_t)&neof) < 0) {
      berrno be;
      dev->dev_errno = errno;         /* save errno */
      Mmsg2(dev->errmsg, _("Unable to set eotmodel on device %s: ERR=%s\n"),
            dev->print_name(), be.bstrerror(dev->dev_errno));
      Jmsg(dcr->jcr, M_FATAL, 0, dev->errmsg);
   }
#endif
   return;
#endif

#ifdef HAVE_SUN_OS
   struct mtop mt_com;
   if (dev->min_block_size == dev->max_block_size &&
       dev->min_block_size == 0) {    /* variable block mode */
      mt_com.mt_op = MTSRSZ;
      mt_com.mt_count = 0;
      if (dev->d_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
         dev->clrerror(MTSRSZ);
      }
   }
   return;
#endif
}
Exemple #21
0
/*
 * We read an ANSI label and compare the Volume name. We require
 * a VOL1 record of 80 characters followed by a HDR1 record containing
 * BACULA.DATA in the filename field. We then read up to 3 more 
 * header records (they are not required) and an EOF, at which
 * point, all is good.
 *
 * Returns:
 *    VOL_OK            Volume name OK
 *    VOL_NO_LABEL      No ANSI label on Volume
 *    VOL_IO_ERROR      I/O error on read
 *    VOL_NAME_ERROR    Wrong name in VOL1 record
 *    VOL_LABEL_ERROR   Probably an ANSI label, but something wrong
 *      
 */ 
int read_ansi_ibm_label(DCR *dcr) 
{
   DEVICE * volatile dev = dcr->dev;
   JCR *jcr = dcr->jcr;
   char label[80];                    /* tape label */
   int stat, i;
   char *VolName = dcr->VolumeName;
   bool ok = false;

   /*
    * Read VOL1, HDR1, HDR2 labels, but ignore the data
    *  If tape read the following EOF mark, on disk do
    *  not read.
    */
   Dmsg0(100, "Read ansi label.\n");
   if (!dev->is_tape()) {
      return VOL_OK;
   }

   dev->label_type = B_BACULA_LABEL;  /* assume Bacula label */

   /* Read a maximum of 5 records VOL1, HDR1, ... HDR4 */
   for (i=0; i < 6; i++) {
      do {
         stat = dev->read(label, sizeof(label));
      } while (stat == -1 && errno == EINTR);
      if (stat < 0) {
         berrno be;
         dev->clrerror(-1);
         Dmsg1(100, "Read device got: ERR=%s\n", be.bstrerror());
         Mmsg2(jcr->errmsg, _("Read error on device %s in ANSI label. ERR=%s\n"),
            dev->dev_name, be.bstrerror());
         Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
         dev->VolCatInfo.VolCatErrors++;
         return VOL_IO_ERROR;
      }
      if (stat == 0) {
         if (dev->at_eof()) {
            dev->set_eot();           /* second eof, set eot bit */
            Dmsg0(100, "EOM on ANSI label\n");
            Mmsg0(jcr->errmsg, _("Insane! End of tape while reading ANSI label.\n"));
            return VOL_LABEL_ERROR;   /* at EOM this shouldn't happen */
         } else {
            dev->set_ateof();        /* set eof state */
         }
      }
      switch (i) {
      case 0:                         /* Want VOL1 label */
         if (stat == 80) {
            if (strncmp("VOL1", label, 4) == 0) {
               ok = true;
               dev->label_type = B_ANSI_LABEL;
               Dmsg0(100, "Got ANSI VOL1 label\n");
            } else {
               /* Try EBCDIC */
               ebcdic_to_ascii(label, label, sizeof(label));
               if (strncmp("VOL1", label, 4) == 0) {
                  ok = true;;
                  dev->label_type = B_IBM_LABEL;
                  Dmsg0(100, "Found IBM label.\n");
                  Dmsg0(100, "Got IBM VOL1 label\n");
               }
            }       
         }
         if (!ok) {
            Dmsg0(100, "No VOL1 label\n");
            Mmsg0(jcr->errmsg, _("No VOL1 label while reading ANSI/IBM label.\n"));
            return VOL_NO_LABEL;   /* No ANSI label */
         }


         /* Compare Volume Names allow special wild card */
         if (VolName && *VolName && *VolName != '*') { 
            if (!same_label_names(VolName, &label[4])) {
               char *p = &label[4];
               char *q;  

               free_volume(dev);
               /* Store new Volume name */
               q = dev->VolHdr.VolumeName;
               for (int i=0; *p != ' ' && i < 6; i++) {
                  *q++ = *p++;
               }
               *q = 0;
               Dmsg0(100, "Call reserve_volume\n");
               /* ***FIXME***  why is this reserve_volume() needed???? KES */
               reserve_volume(dcr, dev->VolHdr.VolumeName);
               dev = dcr->dev;            /* may have changed in reserve_volume */
               Dmsg2(100, "Wanted ANSI Vol %s got %6s\n", VolName, dev->VolHdr.VolumeName);
               Mmsg2(jcr->errmsg, _("Wanted ANSI Volume \"%s\" got \"%s\"\n"), VolName, dev->VolHdr.VolumeName);
               return VOL_NAME_ERROR;
            }
         }
         break;
      case 1:
         if (dev->label_type == B_IBM_LABEL) {
            ebcdic_to_ascii(label, label, sizeof(label));
         }
         if (stat != 80 || strncmp("HDR1", label, 4) != 0) {
            Dmsg0(100, "No HDR1 label\n");
            Mmsg0(jcr->errmsg, _("No HDR1 label while reading ANSI label.\n"));
            return VOL_LABEL_ERROR;
         }
         if (strncmp("BACULA.DATA", &label[4], 11) != 0) {
            Dmsg1(100, "HD1 not Bacula label. Wanted  BACULA.DATA got %11s\n",
               &label[4]);
            Mmsg1(jcr->errmsg, _("ANSI/IBM Volume \"%s\" does not belong to Bacula.\n"),
               dev->VolHdr.VolumeName);
            return VOL_NAME_ERROR;     /* Not a Bacula label */
         }
         Dmsg0(100, "Got HDR1 label\n");
         break;
      case 2:
         if (dev->label_type == B_IBM_LABEL) {
            ebcdic_to_ascii(label, label, sizeof(label));
         }
         if (stat != 80 || strncmp("HDR2", label, 4) != 0) {
            Dmsg0(100, "No HDR2 label\n");
            Mmsg0(jcr->errmsg, _("No HDR2 label while reading ANSI/IBM label.\n"));
            return VOL_LABEL_ERROR;
         }
         Dmsg0(100, "Got ANSI HDR2 label\n");
         break;
      default:
         if (stat == 0) {
            Dmsg0(100, "ANSI label OK\n");
            return VOL_OK;
         }
         if (dev->label_type == B_IBM_LABEL) {
            ebcdic_to_ascii(label, label, sizeof(label));
         }
         if (stat != 80 || strncmp("HDR", label, 3) != 0) {
            Dmsg0(100, "Unknown or bad ANSI/IBM label record.\n");
            Mmsg0(jcr->errmsg, _("Unknown or bad ANSI/IBM label record.\n"));
            return VOL_LABEL_ERROR;
         }
         Dmsg0(100, "Got HDR label\n");
         break;
      }
   }
   Dmsg0(100, "Too many records in ANSI/IBM label.\n");
   Mmsg0(jcr->errmsg, _("Too many records in while reading ANSI/IBM label.\n"));
   return VOL_LABEL_ERROR;
}  
bool object_store_device::d_truncate(DCR *dcr)
{
   /*
    * libdroplet doesn't have a truncate function so unlink the volume and create a new empty one.
    */
   if (m_vfd) {
      dpl_status_t status;
      dpl_vfile_flag_t dpl_flags;
      dpl_option_t dpl_options;

      status = dpl_close(m_vfd);
      switch (status) {
      case DPL_SUCCESS:
         m_vfd = NULL;
         break;
      default:
         Mmsg2(errmsg, _("Failed to close %s using dpl_close(): ERR=%s.\n"),
               getVolCatName(), dpl_status_str(status));
         return false;
      }

      status = dpl_unlink(m_ctx, getVolCatName());
      switch (status) {
      case DPL_SUCCESS:
         break;
      default:
         Mmsg2(errmsg, _("Failed to unlink %s using dpl_unlink(): ERR=%s.\n"),
               getVolCatName(), dpl_status_str(status));
         return false;
      }

      /*
       * Create some options for libdroplet.
       *
       * DPL_OPTION_NOALLOC - we provide the buffer to copy the data into
       *                      no need to let the library allocate memory we
       *                      need to free after copying the data.
       */
      memset(&dpl_options, 0, sizeof(dpl_options));
      dpl_options.mask |= DPL_OPTION_NOALLOC;

      dpl_flags = DPL_VFILE_FLAG_CREAT | DPL_VFILE_FLAG_RDWR;
      status = dpl_open(m_ctx, /* context */
                        getVolCatName(), /* locator */
                        dpl_flags, /* flags */
                        &dpl_options, /* options */
                        NULL, /* condition */
                        NULL, /* metadata */
                        NULL, /* sysmd */
                        NULL, /* query_params */
                        NULL, /* stream_status */
                        &m_vfd);

      switch (status) {
      case DPL_SUCCESS:
         break;
      default:
         Mmsg2(errmsg, _("Failed to open %s using dpl_open(): ERR=%s.\n"),
               getVolCatName(), dpl_status_str(status));
         return false;
      }
   }

   return true;
}
Exemple #23
0
/*
 * Close the device.
 */
bool DEVICE::close(DCR *dcr)
{
   bool retval = true;
   int status;
   Dmsg1(100, "close_dev %s\n", print_name());

   if (!is_open()) {
      Dmsg2(100, "device %s already closed vol=%s\n", print_name(), VolHdr.VolumeName);
      goto bail_out;                  /* already closed */
   }

   if (!norewindonclose) {
      offline_or_rewind();
   }

   switch (dev_type) {
   case B_VTL_DEV:
   case B_TAPE_DEV:
      unlock_door();
      /*
       * Fall through wanted
       */
   default:
      status = d_close(m_fd);
      if (status < 0) {
         berrno be;

         Mmsg2(errmsg, _("Unable to close device %s. ERR=%s\n"),
               print_name(), be.bstrerror());
         dev_errno = errno;
         retval = false;
      }
      break;
   }

   unmount(dcr, 1);                   /* do unmount if required */

   /*
    * Clean up device packet so it can be reused.
    */
   clear_opened();

   clear_bit(ST_LABEL, state);
   clear_bit(ST_READREADY, state);
   clear_bit(ST_APPENDREADY, state);
   clear_bit(ST_EOT, state);
   clear_bit(ST_WEOT, state);
   clear_bit(ST_EOF, state);
   clear_bit(ST_MOUNTED, state);
   clear_bit(ST_MEDIA, state);
   clear_bit(ST_SHORT, state);

   label_type = B_BAREOS_LABEL;
   file = block_num = 0;
   file_size = 0;
   file_addr = 0;
   EndFile = EndBlock = 0;
   open_mode = 0;
   clear_volhdr();
   memset(&VolCatInfo, 0, sizeof(VolCatInfo));
   if (tid) {
      stop_thread_timer(tid);
      tid = 0;
   }

   /*
    * We closed the device so let any plugin know we did.
    */
   if (dcr) {
      generate_plugin_event(dcr->jcr, bsdEventDeviceClose, dcr);
   }

bail_out:
   return retval;
}
Exemple #24
0
/* Update the free space on the device */
bool DEVICE::update_freespace() 
{
   POOL_MEM ocmd(PM_FNAME);
   POOLMEM* results;
   char* icmd;
   int timeout;
   uint64_t free;
   char ed1[50];
   bool ok = false;
   int status;

   if (!is_dvd() || is_freespace_ok()) {
      return true;
   }
   
   /* The device must be mounted in order to dvd-freespace to work */
   mount(1);
   
   Dsm_check(400);
   icmd = device->free_space_command;
   
   if (!icmd) {
      free_space = 0;
      free_space_errno = 0;
      clear_freespace_ok();              /* No valid freespace */
      clear_media();
      Dmsg2(29, "ERROR: update_free_space_dev: free_space=%s, free_space_errno=%d (!icmd)\n", 
            edit_uint64(free_space, ed1), free_space_errno);
      Mmsg(errmsg, _("No FreeSpace command defined.\n"));
      return false;
   }
   
   edit_mount_codes(ocmd, icmd);
   
   Dmsg1(29, "update_freespace: cmd=%s\n", ocmd.c_str());

   results = get_pool_memory(PM_MESSAGE);
   
   /* Try at most 3 times to get the free space on the device. This should perhaps be configurable. */
   timeout = 3;
   
   while (1) {
      berrno be;
      Dmsg1(20, "Run freespace prog=%s\n", ocmd.c_str());
      status = run_program_full_output(ocmd.c_str(), max_open_wait/2, results);
      Dmsg2(500, "Freespace status=%d result=%s\n", status, results);
      if (status == 0) {
         free = str_to_int64(results);
         Dmsg1(400, "Free space program run: Freespace=%s\n", results);
         if (free >= 0) {
            free_space = free;
            free_space_errno = 0;
            set_freespace_ok();     /* have valid freespace */
            set_media();
            Mmsg(errmsg, "");
            ok = true;
            break;
         }
      }
      free_space = 0;
      free_space_errno = EPIPE;
      clear_freespace_ok();         /* no valid freespace */
      Mmsg2(errmsg, _("Cannot run free space command. Results=%s ERR=%s\n"), 
            results, be.bstrerror(status));
      
      if (--timeout > 0) {
         Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
            "free_space_errno=%d ERR=%s\n", print_name(), 
               edit_uint64(free_space, ed1), free_space_errno, 
               errmsg);
         bmicrosleep(1, 0);
         continue;
      }

      dev_errno = free_space_errno;
      Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
         "free_space_errno=%d ERR=%s\n",
            print_name(), edit_uint64(free_space, ed1),
            free_space_errno, errmsg);
      break;
   }
   
   free_pool_memory(results);
   Dmsg4(29, "leave update_freespace: free_space=%s freespace_ok=%d free_space_errno=%d have_media=%d\n", 
      edit_uint64(free_space, ed1), !!is_freespace_ok(), free_space_errno, !!have_media());
   Dsm_check(400);
   return ok;
}
/*
 * Open a volume using libdroplet.
 */
int object_store_device::d_open(const char *pathname, int flags, int mode)
{
   dpl_status_t status;
   dpl_vfile_flag_t dpl_flags;
   dpl_option_t dpl_options;

#if 1
   Mmsg1(errmsg, _("Object Storage devices are not yet supported, please disable %s\n"), dev_name);
   return -1;
#endif

   /*
    * Initialize the droplet library when its not done previously.
    */
   P(mutex);
   if (droplet_reference_count == 0) {
      status = dpl_init();
      if (status != DPL_SUCCESS) {
         V(mutex);
         return -1;
      }

      dpl_set_log_func(object_store_logfunc);
      droplet_reference_count++;
   }
   V(mutex);

   if (!m_object_configstring) {
      int len;
      char *bp, *next_option;
      bool done;

      if (!dev_options) {
         Mmsg0(errmsg, _("No device options configured\n"));
         Emsg0(M_FATAL, 0, errmsg);
         return -1;
      }

      m_object_configstring = bstrdup(dev_options);

      bp = m_object_configstring;
      while (bp) {
         next_option = strchr(bp, ',');
         if (next_option) {
            *next_option++ = '\0';
         }

         done = false;
         for (int i = 0; !done && device_options[i].name; i++) {
            /*
             * Try to find a matching device option.
             */
            if (bstrncasecmp(bp, device_options[i].name, device_options[i].compare_size)) {
               switch (device_options[i].type) {
               case argument_profile:
                  m_profile = bp + device_options[i].compare_size;
                  done = true;
                  break;
               case argument_bucket:
                  m_object_bucketname = bp + device_options[i].compare_size;
                  done = true;
                  break;
               default:
                  break;
               }
            }
         }

         if (!done) {
            Mmsg1(errmsg, _("Unable to parse device option: %s\n"), bp);
            Emsg0(M_FATAL, 0, errmsg);
            goto bail_out;
         }

         bp = next_option;
      }

      if (!m_profile) {
         Mmsg0(errmsg, _("No droplet profile configured\n"));
         Emsg0(M_FATAL, 0, errmsg);
         goto bail_out;
      }

      /*
       * Strip any .profile prefix from the libdroplet profile name.
       */
      len = strlen(m_profile);
      if (len > 8 && bstrcasecmp(m_profile + (len - 8), ".profile")) {
         m_profile[len - 8] = '\0';
      }
   }

   /*
    * See if we need to setup a new context for this device.
    */
   if (!m_ctx) {
      char *bp;

      /*
       * See if this is a path.
       */
      bp = strrchr(m_object_configstring, '/');
      if (!bp) {
         /*
          * Only a profile name.
          */
         m_ctx = dpl_ctx_new(NULL, m_object_configstring);
      } else {
         if (bp == m_object_configstring) {
            /*
             * Profile in root of filesystem
             */
            m_ctx = dpl_ctx_new("/", bp + 1);
         } else {
            /*
             * Profile somewhere else.
             */
            *bp++ = '\0';
            m_ctx = dpl_ctx_new(m_object_configstring, bp);
         }
      }

      /*
       * If we failed to allocate a new context fail the open.
       */
      if (!m_ctx) {
         Mmsg1(errmsg, _("Failed to create a new context using config %s\n"), dev_options);
         return -1;
      }

      /*
       * Login if that is needed for this backend.
       */
      status = dpl_login(m_ctx);
      switch (status) {
      case DPL_SUCCESS:
         break;
      case DPL_ENOTSUPP:
         /*
          * Backend doesn't support login which is fine.
          */
         break;
      default:
         Mmsg2(errmsg, _("Failed to login for voume %s using dpl_login(): ERR=%s.\n"),
               getVolCatName(), dpl_status_str(status));
         return -1;
      }

      /*
       * If a bucketname was defined set it in the context.
       */
      if (m_object_bucketname) {
         m_ctx->cur_bucket = m_object_bucketname;
      }
   }

   /*
    * See if we don't have a file open already.
    */
   if (m_vfd) {
      dpl_close(m_vfd);
      m_vfd = NULL;
   }

   /*
    * Create some options for libdroplet.
    *
    * DPL_OPTION_NOALLOC - we provide the buffer to copy the data into
    *                      no need to let the library allocate memory we
    *                      need to free after copying the data.
    */
   memset(&dpl_options, 0, sizeof(dpl_options));
   dpl_options.mask |= DPL_OPTION_NOALLOC;

   if (flags & O_CREAT) {
      dpl_flags = DPL_VFILE_FLAG_CREAT | DPL_VFILE_FLAG_RDWR;
      status = dpl_open(m_ctx, /* context */
                        getVolCatName(), /* locator */
                        dpl_flags, /* flags */
                        &dpl_options, /* options */
                        NULL, /* condition */
                        NULL, /* metadata */
                        NULL, /* sysmd */
                        NULL, /* query_params */
                        NULL, /* stream_status */
                        &m_vfd);
   } else {
      dpl_flags = DPL_VFILE_FLAG_RDWR;
      status = dpl_open(m_ctx, /* context */
                        getVolCatName(), /* locator */
                        dpl_flags, /* flags */
                        &dpl_options, /* options */
                        NULL, /* condition */
                        NULL, /* metadata */
                        NULL, /* sysmd */
                        NULL, /* query_params */
                        NULL, /* stream_status */
                        &m_vfd);
   }

   switch (status) {
   case DPL_SUCCESS:
      m_offset = 0;
      return 0;
   default:
      Mmsg2(errmsg, _("Failed to open %s using dpl_open(): ERR=%s.\n"),
            getVolCatName(), dpl_status_str(status));
      m_vfd = NULL;
      return droplet_errno_to_system_errno(status);
   }

bail_out:
   return -1;
}
Exemple #26
0
/*
 * Open the next part file.
 *  - Close the fd
 *  - Increment part number 
 *  - Reopen the device
 */
int dvd_open_next_part(DCR *dcr)
{
   DEVICE *dev = dcr->dev;

   Dmsg6(29, "Enter: == open_next_part part=%d npart=%d dev=%s vol=%s mode=%d file_addr=%d\n", 
      dev->part, dev->num_dvd_parts, dev->print_name(),
         dev->getVolCatName(), dev->openmode, dev->file_addr);
   if (!dev->is_dvd()) {
      Dmsg1(100, "Device %s is not dvd!!!!\n", dev->print_name()); 
      return -1;
   }
   
   /* When appending, do not open a new part if the current is empty */
   if (dev->can_append() && (dev->part > dev->num_dvd_parts) && 
       (dev->part_size == 0)) {
      Dmsg0(29, "open_next_part exited immediately (dev->part_size == 0).\n");
      return dev->fd();
   }

   dev->close_part(dcr);               /* close current part */
   
   /*
    * If we have a spooled part open, write it to the
    *  DVD before opening the next part.
    */
   if (dev->is_part_spooled()) {
      Dmsg2(100, "Before open next write previous. part=%d num_parts=%d\n",
         dev->part, dev->num_dvd_parts);
      if (!dvd_write_part(dcr)) {
         Dmsg0(29, "Error in dvd_write part.\n");
         return -1;
      }
   }
     
   dev->part_start += dev->part_size;
   dev->part++;
   Dmsg2(29, "Inc part=%d num_dvd_parts=%d\n", dev->part, dev->num_dvd_parts);

   /* Are we working on a part past what is written in the DVD? */
   if (dev->num_dvd_parts < dev->part) {
      POOL_MEM archive_name(PM_FNAME);
      struct stat buf;
      /* 
       * First check what is on DVD.  If our part is there, we
       *   are in trouble, so bail out.
       * NB: This is however not a problem if we are writing the first part.
       * It simply means that we are over writing an existing volume...
       */
      if (dev->num_dvd_parts > 0) {
         make_mounted_dvd_filename(dev, archive_name);   /* makes dvd name */
         Dmsg1(100, "Check if part on DVD: %s\n", archive_name.c_str());
         if (stat(archive_name.c_str(), &buf) == 0) {
            /* bad news bail out */
            dev->set_part_spooled(false);
            Mmsg1(&dev->errmsg, _("Next Volume part already exists on DVD. Cannot continue: %s\n"),
               archive_name.c_str());
            return -1;
         }
      }

#ifdef neeeded
      Dmsg2(400, "num_dvd_parts=%d part=%d\n", dev->num_dvd_parts, dev->part);
      make_spooled_dvd_filename(dev, archive_name);   /* makes spool name */
      
      /* Check if the next part exists in spool directory . */
      Dmsg1(100, "Check if part on spool: %s\n", archive_name.c_str());
      if ((stat(archive_name.c_str(), &buf) == 0) || (errno != ENOENT)) {
         Dmsg1(29, "======= Part %s is in the way, deleting it...\n", archive_name.c_str());
         /* Then try to unlink it */
         if (unlink(archive_name.c_str()) < 0) {
            berrno be;
            dev->set_part_spooled(false);
            dev->dev_errno = errno;
            Mmsg2(dev->errmsg, _("open_next_part can't unlink existing part %s, ERR=%s\n"), 
                   archive_name.c_str(), be.bstrerror());
            return -1;
         }
      }
#endif
   }

   Dmsg2(400, "Call dev->open(vol=%s, mode=%d)\n", dcr->getVolCatName(), 
         dev->openmode);

   /* Open next part.  Note, this sets part_size for part opened. */
   if (dev->open(dcr, OPEN_READ_ONLY) < 0) {
      return -1;
   } 
   dev->set_labeled();                   /* all next parts are "labeled" */
   
   return dev->fd();
}
bool win32_file_device::d_truncate(DCR *dcr)
{
   struct stat st;

   if (ftruncate(m_fd, 0) != 0) {
      berrno be;

      Mmsg2(errmsg, _("Unable to truncate device %s. ERR=%s\n"), print_name(), be.bstrerror());
      return false;
   }

   /*
    * Check for a successful ftruncate() and issue a work-around for devices
    * (mostly cheap NAS) that don't support truncation.
    * Workaround supplied by Martin Schmid as a solution to bug #1011.
    * 1. close file
    * 2. delete file
    * 3. open new file with same mode
    * 4. change ownership to original
    */
   if (fstat(m_fd, &st) != 0) {
      berrno be;

      Mmsg2(errmsg, _("Unable to stat device %s. ERR=%s\n"), print_name(), be.bstrerror());
      return false;
   }

   if (st.st_size != 0) {             /* ftruncate() didn't work */
      POOL_MEM archive_name(PM_FNAME);

      pm_strcpy(archive_name, dev_name);
      if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) {
         pm_strcat(archive_name, "/");
      }
      pm_strcat(archive_name, dcr->VolumeName);

      Mmsg2(errmsg, _("Device %s doesn't support ftruncate(). Recreating file %s.\n"),
            print_name(), archive_name.c_str());

      /*
       * Close file and blow it away
       */
      ::close(m_fd);
      ::unlink(archive_name.c_str());

      /*
       * Recreate the file -- of course, empty
       */
      oflags = O_CREAT | O_RDWR | O_BINARY;
      if ((m_fd = ::open(archive_name.c_str(), oflags, st.st_mode)) < 0) {
         berrno be;

         dev_errno = errno;
         Mmsg2(errmsg, _("Could not reopen: %s, ERR=%s\n"), archive_name.c_str(), be.bstrerror());
         Dmsg1(100, "reopen failed: %s", errmsg);
         Emsg0(M_FATAL, 0, errmsg);
         return false;
      }

      /*
       * Reset proper owner
       */
      chown(archive_name.c_str(), st.st_uid, st.st_gid);
   }

   return true;
}
Exemple #28
0
/* Update the free space on the device */
bool DEVICE::update_freespace()
{
   POOL_MEM ocmd(PM_FNAME);
   POOLMEM* results;
   char* icmd;
   char* p;
   uint64_t free, total;
   char ed1[50];
   bool ok = false;
   int status;
   berrno be;

   if (!is_file()) {
      Mmsg(errmsg, "");
      return true;
   }

   /* The device must be mounted in order for freespace to work */
   if (requires_mount()) {
      mount(1);
   }

   if (get_os_device_freespace()) {
      Dmsg4(20, "get_os_device_freespace: free_space=%s freespace_ok=%d free_space_errno=%d have_media=%d\n",
         edit_uint64(free_space, ed1), !!is_freespace_ok(), free_space_errno, !!have_media());
      return true;
   }

   icmd = device->free_space_command;

   if (!icmd) {
      set_freespace(0, 0, 0, false);
      Dmsg2(20, "ERROR: update_free_space_dev: free_space=%s, free_space_errno=%d (!icmd)\n",
            edit_uint64(free_space, ed1), free_space_errno);
      Mmsg(errmsg, _("No FreeSpace command defined.\n"));
      return false;
   }

   edit_mount_codes(ocmd, icmd);

   Dmsg1(20, "update_freespace: cmd=%s\n", ocmd.c_str());

   results = get_pool_memory(PM_MESSAGE);

   Dmsg1(20, "Run freespace prog=%s\n", ocmd.c_str());
   status = run_program_full_output(ocmd.c_str(), max_open_wait/2, results);
   Dmsg2(20, "Freespace status=%d result=%s\n", status, results);
   /* Should report "1223232 12323232\n"  "free  total\n" */
   if (status == 0) {
      free = str_to_int64(results) * 1024;
      p = results;

      if (skip_nonspaces(&p)) {
         total = str_to_int64(p) * 1024;

      } else {
         total = 0;
      }

      Dmsg1(400, "Free space program run: Freespace=%s\n", results);
      if (free >= 0) {
         set_freespace(free, total, 0, true); /* have valid freespace */
         Mmsg(errmsg, "");
         ok = true;
      }
   } else {
      set_freespace(0, 0, EPIPE, false); /* no valid freespace */
      Mmsg2(errmsg, _("Cannot run free space command. Results=%s ERR=%s\n"),
            results, be.bstrerror(status));

      dev_errno = free_space_errno;
      Dmsg4(20, "Cannot get free space on device %s. free_space=%s, "
         "free_space_errno=%d ERR=%s\n",
            print_name(), edit_uint64(free_space, ed1),
            free_space_errno, errmsg);
   }
   free_pool_memory(results);
   Dmsg4(20, "leave update_freespace: free_space=%s freespace_ok=%d free_space_errno=%d have_media=%d\n",
      edit_uint64(free_space, ed1), !!is_freespace_ok(), free_space_errno, !!have_media());
   return ok;
}
Exemple #29
0
/*
 * Find Available Media (Volume) for Pool
 *
 * Find a Volume for a given PoolId, MediaType, and Status.
 *
 * Returns: 0 on failure
 *          numrows on success
 */
int BDB::bdb_find_next_volume(JCR *jcr, int item, bool InChanger, MEDIA_DBR *mr)
{
   SQL_ROW row = NULL;
   int numrows;
   const char *order;
   char esc_type[MAX_ESCAPE_NAME_LENGTH];
   char esc_status[MAX_ESCAPE_NAME_LENGTH];
   char ed1[50];

   bdb_lock();
   bdb_escape_string(jcr, esc_type, mr->MediaType, strlen(mr->MediaType));
   bdb_escape_string(jcr, esc_status, mr->VolStatus, strlen(mr->VolStatus));

   if (item == -1) {       /* find oldest volume */
      /* Find oldest volume */
      Mmsg(cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
         "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
         "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
         "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
         "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
         "Enabled,LocationId,RecycleCount,InitialWrite,"
         "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
         "FROM Media WHERE PoolId=%s AND MediaType='%s' AND VolStatus IN ('Full',"
         "'Recycle','Purged','Used','Append') AND Enabled=1 "
         "ORDER BY LastWritten LIMIT 1",
         edit_int64(mr->PoolId, ed1), esc_type);
     item = 1;
   } else {
      POOL_MEM changer(PM_FNAME);
      POOL_MEM voltype(PM_FNAME);
      POOL_MEM exclude(PM_FNAME);
      /* Find next available volume */
      if (InChanger) {
         Mmsg(changer, " AND InChanger=1 AND StorageId=%s ",
                 edit_int64(mr->StorageId, ed1));
      }
      /* Volumes will be automatically excluded from the query, we just take the
       * first one of the list 
       */
      if (mr->exclude_list && *mr->exclude_list) {
         item = 1;
         Mmsg(exclude, " AND MediaId NOT IN (%s) ", mr->exclude_list);
      }
      if (strcmp(mr->VolStatus, "Recycle") == 0 ||
          strcmp(mr->VolStatus, "Purged") == 0) {
         order = "AND Recycle=1 ORDER BY LastWritten ASC,MediaId";  /* take oldest that can be recycled */
      } else {
         order = sql_media_order_most_recently_written[bdb_get_type_index()];    /* take most recently written */
      }
      Mmsg(cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
         "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
         "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
         "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
         "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
         "Enabled,LocationId,RecycleCount,InitialWrite,"
         "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
         "FROM Media WHERE PoolId=%s AND MediaType='%s' AND Enabled=1 "
         "AND VolStatus='%s' "
         "%s "
         "%s "
         "%s "
         "%s LIMIT %d",
         edit_int64(mr->PoolId, ed1), esc_type,
         esc_status,
         voltype.c_str(),
         changer.c_str(), exclude.c_str(), order, item);
   }
   Dmsg1(100, "fnextvol=%s\n", cmd);
   if (!QueryDB(jcr, cmd)) {
      bdb_unlock();
      return 0;
   }

   numrows = sql_num_rows();
   if (item > numrows || item < 1) {
      Dmsg2(050, "item=%d got=%d\n", item, numrows);
      Mmsg2(&errmsg, _("Request for Volume item %d greater than max %d or less than 1\n"),
         item, numrows);
      bdb_unlock();
      return 0;
   }

   /* Note, we previously seeked to the row using:
    *  sql_data_seek(item-1);
    * but this failed on PostgreSQL, so now we loop
    * over all the records.  This should not be too horrible since
    * the maximum Volumes we look at in any case is 20.
    */
   while (item-- > 0) {
      if ((row = sql_fetch_row()) == NULL) {
         Dmsg1(050, "Fail fetch item=%d\n", item+1);
         Mmsg1(&errmsg, _("No Volume record found for item %d.\n"), item);
         sql_free_result();
         bdb_unlock();
         return 0;
      }
   }

   /* Return fields in Media Record */
   mr->MediaId = str_to_int64(row[0]);
   bstrncpy(mr->VolumeName, row[1]!=NULL?row[1]:"", sizeof(mr->VolumeName));
   mr->VolJobs = str_to_int64(row[2]);
   mr->VolFiles = str_to_int64(row[3]);
   mr->VolBlocks = str_to_int64(row[4]);
   mr->VolBytes = str_to_uint64(row[5]);
   mr->VolMounts = str_to_int64(row[6]);
   mr->VolErrors = str_to_int64(row[7]);
   mr->VolWrites = str_to_int64(row[8]);
   mr->MaxVolBytes = str_to_uint64(row[9]);
   mr->VolCapacityBytes = str_to_uint64(row[10]);
   bstrncpy(mr->MediaType, row[11]!=NULL?row[11]:"", sizeof(mr->MediaType));
   bstrncpy(mr->VolStatus, row[12]!=NULL?row[12]:"", sizeof(mr->VolStatus));
   mr->PoolId = str_to_int64(row[13]);
   mr->VolRetention = str_to_uint64(row[14]);
   mr->VolUseDuration = str_to_uint64(row[15]);
   mr->MaxVolJobs = str_to_int64(row[16]);
   mr->MaxVolFiles = str_to_int64(row[17]);
   mr->Recycle = str_to_int64(row[18]);
   mr->Slot = str_to_int64(row[19]);
   bstrncpy(mr->cFirstWritten, row[20]!=NULL?row[20]:"", sizeof(mr->cFirstWritten));
   mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
   bstrncpy(mr->cLastWritten, row[21]!=NULL?row[21]:"", sizeof(mr->cLastWritten));
   mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
   mr->InChanger = str_to_uint64(row[22]);
   mr->EndFile = str_to_uint64(row[23]);
   mr->EndBlock = str_to_uint64(row[24]);
   mr->VolType = str_to_int64(row[25]);   /* formerly VolParts */
   mr->LabelType = str_to_int64(row[26]);
   bstrncpy(mr->cLabelDate, row[27]!=NULL?row[27]:"", sizeof(mr->cLabelDate));
   mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate);
   mr->StorageId = str_to_int64(row[28]);
   mr->Enabled = str_to_int64(row[29]);
   mr->LocationId = str_to_int64(row[30]);
   mr->RecycleCount = str_to_int64(row[31]);
   bstrncpy(mr->cInitialWrite, row[32]!=NULL?row[32]:"", sizeof(mr->cInitialWrite));
   mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite);
   mr->ScratchPoolId = str_to_int64(row[33]);
   mr->RecyclePoolId = str_to_int64(row[34]);
   mr->VolReadTime = str_to_int64(row[35]);
   mr->VolWriteTime = str_to_int64(row[36]);
   mr->ActionOnPurge = str_to_int64(row[37]);

   sql_free_result();

   bdb_unlock();
   Dmsg1(050, "Rtn numrows=%d\n", numrows);
   return numrows;
}
Exemple #30
0
/*
 * Open a fifo device
 */
void win32_fifo_device::open_device(DCR *dcr, int omode)
{
   file_size = 0;
   int timeout = max_open_wait;
   utime_t start_time = time(NULL);

   mount(dcr, 1);                     /* do mount if required */

   Dmsg0(100, "Open dev: device is fifo\n");

   get_autochanger_loaded_slot(dcr);

   open_mode = omode;
   set_mode(omode);

   if (timeout < 1) {
      timeout = 1;
   }
   errno = 0;

   if (timeout) {
      /*
       * Set open timer
       */
      tid = start_thread_timer(dcr->jcr, pthread_self(), timeout);
   }

   Dmsg2(100, "Try open %s mode=%s\n", prt_name, mode_to_str(omode));

   /*
    * If busy retry each second for max_open_wait seconds
    */
   for ( ;; ) {
      /*
       * Try non-blocking open
       */
      m_fd = d_open(dev_name, oflags | O_NONBLOCK, 0);
      if (m_fd < 0) {
         berrno be;
         dev_errno = errno;
         Dmsg5(100, "Open error on %s omode=%d oflags=%x errno=%d: ERR=%s\n",
               prt_name, omode, oflags, errno, be.bstrerror());
      } else {
         d_close(m_fd);
         m_fd = d_open(dev_name, oflags, 0); /* open normally */
         if (m_fd < 0) {
            berrno be;
            dev_errno = errno;
            Dmsg5(100, "Open error on %s omode=%d oflags=%x errno=%d: ERR=%s\n",
                  prt_name, omode, oflags, errno, be.bstrerror());
            break;
         }
         dev_errno = 0;
         lock_door();
         break;                               /* Successfully opened and rewound */
      }
      bmicrosleep(5, 0);

      /*
       * Exceed wait time ?
       */
      if (time(NULL) - start_time >= max_open_wait) {
         break;                       /* yes, get out */
      }
   }

   if (!is_open()) {
      berrno be;
      Mmsg2(errmsg, _("Unable to open device %s: ERR=%s\n"),
            prt_name, be.bstrerror(dev_errno));
      Dmsg1(100, "%s", errmsg);
   }

   /*
    * Stop any open() timer we started
    */
   if (tid) {
      stop_thread_timer(tid);
      tid = 0;
   }

   Dmsg1(100, "open dev: fifo %d opened\n", m_fd);
}