Пример #1
0
/*
 * Do a backup of the specified FileSet
 *
 *  Returns:  false on failure
 *            true  on success
 */
bool do_backup(JCR *jcr)
{
   int stat;
   int tls_need = BNET_TLS_NONE;
   BSOCK   *fd;
   STORE *store;
   char ed1[100];

   if (jcr->get_JobLevel() == L_VIRTUAL_FULL) {
      return do_vbackup(jcr);
   }

   /* Print Job Start message */
   Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
        edit_uint64(jcr->JobId, ed1), jcr->Job);

   set_jcr_job_status(jcr, JS_Running);
   Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
   if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
      Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
      return false;
   }

   /*
    * Open a message channel connection with the Storage
    * daemon. This is to let him know that our client
    * will be contacting him for a backup  session.
    *
    */
   Dmsg0(110, "Open connection with storage daemon\n");
   set_jcr_job_status(jcr, JS_WaitSD);
   /*
    * Start conversation with Storage daemon
    */
   if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
      return false;
   }
   /*
    * Now start a job with the Storage daemon
    */
   if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
      return false;
   }

   /*
    * Start the job prior to starting the message thread below
    * to avoid two threads from using the BSOCK structure at
    * the same time.
    */
   if (!bnet_fsend(jcr->store_bsock, "run")) {
      return false;
   }

   /*
    * Now start a Storage daemon message thread.  Note,
    *   this thread is used to provide the catalog services
    *   for the backup job, including inserting the attributes
    *   into the catalog.  See catalog_update() in catreq.c
    */
   if (!start_storage_daemon_message_thread(jcr)) {
      return false;
   }
   Dmsg0(150, "Storage daemon connection OK\n");

   set_jcr_job_status(jcr, JS_WaitFD);
   if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
      goto bail_out;
   }

   set_jcr_job_status(jcr, JS_Running);
   fd = jcr->file_bsock;

   if (!send_include_list(jcr)) {
      goto bail_out;
   }

   if (!send_exclude_list(jcr)) {
      goto bail_out;
   }

   if (!send_level_command(jcr)) {
      goto bail_out;
   }

   /*
    * send Storage daemon address to the File daemon
    */
   store = jcr->wstore;
   if (store->SDDport == 0) {
      store->SDDport = store->SDport;
   }

   /* TLS Requirement */
   if (store->tls_enable) {
      if (store->tls_require) {
         tls_need = BNET_TLS_REQUIRED;
      } else {
         tls_need = BNET_TLS_OK;
      }
   }

   fd->fsend(storaddr, store->address, store->SDDport, tls_need);
   if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
      goto bail_out;
   }

   if (!send_runscripts_commands(jcr)) {
      goto bail_out;
   }

   /*    
    * We re-update the job start record so that the start
    *  time is set after the run before job.  This avoids 
    *  that any files created by the run before job will
    *  be saved twice.  They will be backed up in the current
    *  job, but not in the next one unless they are changed.
    *  Without this, they will be backed up in this job and
    *  in the next job run because in that case, their date 
    *   is after the start of this run.
    */
   jcr->start_time = time(NULL);
   jcr->jr.StartTime = jcr->start_time;
   if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
      Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
   }

   /*
    * If backup is in accurate mode, we send the list of
    * all files to FD.
    */
   if (!send_accurate_current_files(jcr)) {
      goto bail_out;
   }

   /* Send backup command */
   fd->fsend(backupcmd);
   if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
      goto bail_out;
   }

   /* Pickup Job termination data */
   stat = wait_for_job_termination(jcr);
   db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
   if (stat == JS_Terminated) {
      backup_cleanup(jcr, stat);
      return true;
   }     
   return false;

/* Come here only after starting SD thread */
bail_out:
   set_jcr_job_status(jcr, JS_ErrorTerminated);
   Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
   /* Cancel SD */
   wait_for_job_termination(jcr, FDConnectTimeout);
   Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
   return false;
}
Пример #2
0
/*
 * Do a verification of the specified files against the Catlaog
 *
 *  Returns:  false on failure
 *            true  on success
 */
bool do_verify(JCR *jcr)
{
   const char *level;
   BSOCK   *fd;
   int stat;
   char ed1[100];
   JOB_DBR jr;
   JobId_t verify_jobid = 0;
   const char *Name;

   free_wstorage(jcr);                   /* we don't write */

   memset(&jcr->previous_jr, 0, sizeof(jcr->previous_jr));

   /*
    * Find JobId of last job that ran. Note, we do this when
    *   the job actually starts running, not at schedule time,
    *   so that we find the last job that terminated before
    *   this job runs rather than before it is scheduled. This
    *   permits scheduling a Backup and Verify at the same time,
    *   but with the Verify at a lower priority.
    *
    *   For VERIFY_CATALOG we want the JobId of the last INIT.
    *   For VERIFY_VOLUME_TO_CATALOG, we want the JobId of the
    *       last backup Job.
    */
   if (jcr->get_JobLevel() == L_VERIFY_CATALOG ||
       jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG ||
       jcr->get_JobLevel() == L_VERIFY_DISK_TO_CATALOG) {
      memcpy(&jr, &jcr->jr, sizeof(jr));
      if (jcr->verify_job &&
          (jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG ||
           jcr->get_JobLevel() == L_VERIFY_DISK_TO_CATALOG)) {
         Name = jcr->verify_job->name();  
      } else {
         Name = NULL;
      }
      Dmsg1(100, "find last jobid for: %s\n", NPRT(Name));
      if (!db_find_last_jobid(jcr, jcr->db, Name, &jr)) {
         if (jcr->get_JobLevel() == L_VERIFY_CATALOG) {
            Jmsg(jcr, M_FATAL, 0, _(
                 "Unable to find JobId of previous InitCatalog Job.\n"
                 "Please run a Verify with Level=InitCatalog before\n"
                 "running the current Job.\n"));
          } else {
            Jmsg(jcr, M_FATAL, 0, _(
                 "Unable to find JobId of previous Job for this client.\n"));
         }
         return false;
      }
      verify_jobid = jr.JobId;
      Dmsg1(100, "Last full jobid=%d\n", verify_jobid);
   }
   /*
    * Now get the job record for the previous backup that interests
    *   us. We use the verify_jobid that we found above.
    */
   if (jcr->get_JobLevel() == L_VERIFY_CATALOG ||
       jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG ||
       jcr->get_JobLevel() == L_VERIFY_DISK_TO_CATALOG) {
      jcr->previous_jr.JobId = verify_jobid;
      if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
         Jmsg(jcr, M_FATAL, 0, _("Could not get job record for previous Job. ERR=%s"),
              db_strerror(jcr->db));
         return false;
      }
      if (!(jcr->previous_jr.JobStatus == JS_Terminated ||
            jcr->previous_jr.JobStatus == JS_Warnings)) {
         Jmsg(jcr, M_FATAL, 0, _("Last Job %d did not terminate normally. JobStatus=%c\n"),
            verify_jobid, jcr->previous_jr.JobStatus);
         return false;
      }
      Jmsg(jcr, M_INFO, 0, _("Verifying against JobId=%d Job=%s\n"),
         jcr->previous_jr.JobId, jcr->previous_jr.Job);
   }

   /*
    * If we are verifying a Volume, we need the Storage
    *   daemon, so open a connection, otherwise, just
    *   create a dummy authorization key (passed to
    *   File daemon but not used).
    */
   if (jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
      if (!create_restore_bootstrap_file(jcr)) {
         return false;
      }
   } else {
      jcr->sd_auth_key = bstrdup("dummy");    /* dummy Storage daemon key */
   }

   if (jcr->get_JobLevel() == L_VERIFY_DISK_TO_CATALOG && jcr->verify_job) {
      jcr->fileset = jcr->verify_job->fileset;
   }
   Dmsg2(100, "ClientId=%u JobLevel=%c\n", jcr->previous_jr.ClientId, jcr->get_JobLevel());

   if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
      Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
      return false;
   }

   /* Print Job Start message */
   Jmsg(jcr, M_INFO, 0, _("Start Verify JobId=%s Level=%s Job=%s\n"),
      edit_uint64(jcr->JobId, ed1), level_to_str(jcr->get_JobLevel()), jcr->Job);

   if (jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
      /*
       * Start conversation with Storage daemon
       */
      set_jcr_job_status(jcr, JS_Blocked);
      if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
         return false;
      }
      /*
       * Now start a job with the Storage daemon
       */
      if (!start_storage_daemon_job(jcr, jcr->rstorage, NULL)) {
         return false;
      }
      if (!jcr->store_bsock->fsend("run")) {
         return false;
      }
      /*
       * Now start a Storage daemon message thread
       */
      if (!start_storage_daemon_message_thread(jcr)) {
         return false;
      }
      Dmsg0(50, "Storage daemon connection OK\n");

   }
   /*
    * OK, now connect to the File daemon
    *  and ask him for the files.
    */
   set_jcr_job_status(jcr, JS_Blocked);
   if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
      goto bail_out;
   }

   set_jcr_job_status(jcr, JS_Running);
   fd = jcr->file_bsock;


   Dmsg0(30, ">filed: Send include list\n");
   if (!send_include_list(jcr)) {
      goto bail_out;
   }

   Dmsg0(30, ">filed: Send exclude list\n");
   if (!send_exclude_list(jcr)) {
      goto bail_out;
   }

   /*
    * Send Level command to File daemon, as well
    *   as the Storage address if appropriate.
    */
   switch (jcr->get_JobLevel()) {
   case L_VERIFY_INIT:
      level = "init";
      break;
   case L_VERIFY_CATALOG:
      level = "catalog";
      break;
   case L_VERIFY_VOLUME_TO_CATALOG:
      /*
       * send Storage daemon address to the File daemon
       */
      if (jcr->rstore->SDDport == 0) {
         jcr->rstore->SDDport = jcr->rstore->SDport;
      }
      bnet_fsend(fd, storaddr, jcr->rstore->address, jcr->rstore->SDDport);
      if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
         goto bail_out;
      }

      /*
       * Send the bootstrap file -- what Volumes/files to restore
       */
      if (!send_bootstrap_file(jcr, fd) ||
          !response(jcr, fd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
         goto bail_out;
      }

      if (!jcr->RestoreBootstrap) {
         Jmsg0(jcr, M_FATAL, 0, _("Deprecated feature ... use bootstrap.\n"));
         goto bail_out;
      }

      level = "volume";
      break;
   case L_VERIFY_DATA:
      level = "data";
      break;
   case L_VERIFY_DISK_TO_CATALOG:
      level="disk_to_catalog";
      break;
   default:
      Jmsg2(jcr, M_FATAL, 0, _("Unimplemented Verify level %d(%c)\n"), jcr->get_JobLevel(),
         jcr->get_JobLevel());
      goto bail_out;
   }

   if (!send_runscripts_commands(jcr)) {
      goto bail_out;
   }

   /*
    * Send verify command/level to File daemon
    */
   fd->fsend(verifycmd, level);
   if (!response(jcr, fd, OKverify, "Verify", DISPLAY_ERROR)) {
      goto bail_out;
   }

   /*
    * Now get data back from File daemon and
    *  compare it to the catalog or store it in the
    *  catalog depending on the run type.
    */
   /* Compare to catalog */
   switch (jcr->get_JobLevel()) {
   case L_VERIFY_CATALOG:
      Dmsg0(10, "Verify level=catalog\n");
      jcr->sd_msg_thread_done = true;   /* no SD msg thread, so it is done */
      jcr->SDJobStatus = JS_Terminated;
      get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId);
      break;

   case L_VERIFY_VOLUME_TO_CATALOG:
      Dmsg0(10, "Verify level=volume\n");
      get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId);
      break;

   case L_VERIFY_DISK_TO_CATALOG:
      Dmsg0(10, "Verify level=disk_to_catalog\n");
      jcr->sd_msg_thread_done = true;   /* no SD msg thread, so it is done */
      jcr->SDJobStatus = JS_Terminated;
      get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId);
      break;

   case L_VERIFY_INIT:
      /* Build catalog */
      Dmsg0(10, "Verify level=init\n");
      jcr->sd_msg_thread_done = true;   /* no SD msg thread, so it is done */
      jcr->SDJobStatus = JS_Terminated;
      get_attributes_and_put_in_catalog(jcr);
      db_end_transaction(jcr, jcr->db);   /* terminate any open transaction */
      db_write_batch_file_records(jcr);
      break;

   default:
      Jmsg1(jcr, M_FATAL, 0, _("Unimplemented verify level %d\n"), jcr->get_JobLevel());
      goto bail_out;
   }

   stat = wait_for_job_termination(jcr);
   verify_cleanup(jcr, stat);
   return true;

bail_out:
   return false;
}
Пример #3
0
int apcsmart_ups_entry_point(UPSINFO *ups, int command, void *data)
{
   int retries = 5;                /* Number of retries if reason is NA (see below) */
   char ans[20];

   switch (command) {
   case DEVICE_CMD_SET_DUMB_MODE:
      /* Set dumb mode for a smart UPS */
      write(ups->fd, "R", 1);      /* enter dumb mode */
      *ans = 0;
      getline(ans, sizeof(ans), ups);
      printf("Going dumb: %s\n", ans);
      break;

   case DEVICE_CMD_GET_SELFTEST_MSG:
      /* Results of last self test */
      if (ups->UPS_Cap[CI_ST_STAT]) {
         ups->testresult = decode_testresult(
            smart_poll(ups->UPS_Cmd[CI_ST_STAT], ups));
      }
      break;

   case DEVICE_CMD_CHECK_SELFTEST:

      Dmsg0(80, "Checking self test.\n");
      /*
       * XXX
       *
       * One day we will do this test inside the driver and not as an
       * entry point.
       */
      /* Reason for last transfer to batteries */
      if (ups->UPS_Cap[CI_WHY_BATT]) {
         ups->lastxfer = XFER_NA;
         while (ups->lastxfer == XFER_NA && retries--) {
            ups->lastxfer = decode_lastxfer(
               smart_poll(ups->UPS_Cmd[CI_WHY_BATT], ups));
            if (ups->lastxfer == XFER_NA) {
               Dmsg0(80, "Transfer reason still not available.\n");
               if (retries > 0)
                  sleep(2);           /* debounce */

               /*
                * Be careful because if we go out of here without
                * knowing the reason of transfer (i.e. the reason
                * is "NA", apcupsd will think this is a power failure
                * even if it is a self test. Not much of a problem
                * as this should not happen.
                * We allow 5 retries for reading reason from UPS before
                * giving up.
                */
            } else if (ups->lastxfer == XFER_SELFTEST) {
               ups->SelfTest = time(NULL);
               Dmsg1(80, "Self Test time: %s", ctime(&ups->SelfTest));
            } 
         }
      }
      break;

   default:
      return FAILURE;
      break;
   }

   return SUCCESS;
}
Пример #4
0
void b_free_jcr(const char *file, int line, JCR *jcr)
{
   struct s_last_job *je;

   Dmsg3(dbglvl, "Enter free_jcr jid=%u from %s:%d\n", jcr->JobId, file, line);

#else

void free_jcr(JCR *jcr)
{
   struct s_last_job *je;

   Dmsg3(dbglvl, "Enter free_jcr jid=%u use_count=%d Job=%s\n",
         jcr->JobId, jcr->use_count(), jcr->Job);

#endif

   lock_jcr_chain();
   jcr->dec_use_count();              /* decrement use count */
   if (jcr->use_count() < 0) {
      Jmsg2(jcr, M_ERROR, 0, _("JCR use_count=%d JobId=%d\n"),
         jcr->use_count(), jcr->JobId);
   }
   if (jcr->JobId > 0) {
      Dmsg3(dbglvl, "Dec free_jcr jid=%u use_count=%d Job=%s\n",
         jcr->JobId, jcr->use_count(), jcr->Job);
   }
   if (jcr->use_count() > 0) {          /* if in use */
      unlock_jcr_chain();
      return;
   }
   if (jcr->JobId > 0) {
      Dmsg3(dbglvl, "remove jcr jid=%u use_count=%d Job=%s\n",
            jcr->JobId, jcr->use_count(), jcr->Job);
   }
   remove_jcr(jcr);                   /* remove Jcr from chain */
   unlock_jcr_chain();

   dequeue_messages(jcr);
   close_msg(jcr);                    /* close messages for this job */
   job_end_pop(jcr);                  /* pop and call hooked routines */

   Dmsg1(dbglvl, "End job=%d\n", jcr->JobId);

   /* Keep some statistics */
   switch (jcr->getJobType()) {
   case JT_BACKUP:
   case JT_VERIFY:
   case JT_RESTORE:
   case JT_MIGRATE:
   case JT_COPY:
   case JT_ADMIN:
      /* Keep list of last jobs, but not Console where JobId==0 */
      if (jcr->JobId > 0) {
         lock_last_jobs_list();
         num_jobs_run++;
         je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
         memset(je, 0, sizeof(struct s_last_job));  /* zero in case unset fields */
         je->Errors = jcr->JobErrors;
         je->JobType = jcr->getJobType();
         je->JobId = jcr->JobId;
         je->VolSessionId = jcr->VolSessionId;
         je->VolSessionTime = jcr->VolSessionTime;
         bstrncpy(je->Job, jcr->Job, sizeof(je->Job));
         je->JobFiles = jcr->JobFiles;
         je->JobBytes = jcr->JobBytes;
         je->JobStatus = jcr->JobStatus;
         je->JobLevel = jcr->getJobLevel();
         je->start_time = jcr->start_time;
         je->end_time = time(NULL);

         if (!last_jobs) {
            init_last_jobs_list();
         }
         last_jobs->append(je);
         if (last_jobs->size() > max_last_jobs) {
            je = (struct s_last_job *)last_jobs->first();
            last_jobs->remove(je);
            free(je);
         }
         unlock_last_jobs_list();
      }
      break;
   default:
      break;
   }

   if (jcr->daemon_free_jcr) {
      jcr->daemon_free_jcr(jcr);      /* call daemon free routine */
   }

   free_common_jcr(jcr);
   Dmsg0(dbglvl, "Exit free_jcr\n");
}

/*
 * Remove jcr from thread specific data, but
 *   but make sure it is us who are attached.
 */
void remove_jcr_from_tsd(JCR *jcr)
{
   JCR *tjcr = get_jcr_from_tsd();
   if (tjcr == jcr) {
      set_jcr_in_tsd(INVALID_JCR);
   }
}
Пример #5
0
/*
 * Release resources allocated during backup.
 *
 */
void verify_cleanup(JCR *jcr, int TermCode)
{
   char sdt[50], edt[50];
   char ec1[30], ec2[30];
   char term_code[100], fd_term_msg[100], sd_term_msg[100];
   const char *term_msg;
   int msg_type;
   JobId_t JobId;
   const char *Name;

// Dmsg1(100, "Enter verify_cleanup() TermCod=%d\n", TermCode);

   Dmsg3(900, "JobLevel=%c Expected=%u JobFiles=%u\n", jcr->get_JobLevel(),
      jcr->ExpectedFiles, jcr->JobFiles);
   if (jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG &&
       jcr->ExpectedFiles != jcr->JobFiles) {
      TermCode = JS_ErrorTerminated;
   }

   JobId = jcr->jr.JobId;

   update_job_end(jcr, TermCode);

   if (job_canceled(jcr)) {
      cancel_storage_daemon_job(jcr);
   }

   if (jcr->unlink_bsr && jcr->RestoreBootstrap) {
      unlink(jcr->RestoreBootstrap);
      jcr->unlink_bsr = false;
   }

   msg_type = M_INFO;                 /* by default INFO message */
   switch (TermCode) {
   case JS_Terminated:
      term_msg = _("Verify OK");
      break;
   case JS_FatalError:
   case JS_ErrorTerminated:
      term_msg = _("*** Verify Error ***");
      msg_type = M_ERROR;          /* Generate error message */
      break;
   case JS_Error:
      term_msg = _("Verify warnings");
      break;
   case JS_Canceled:
      term_msg = _("Verify Canceled");
      break;
   case JS_Differences:
      term_msg = _("Verify Differences");
      break;
   default:
      term_msg = term_code;
      bsnprintf(term_code, sizeof(term_code),
                _("Inappropriate term code: %d %c\n"), TermCode, TermCode);
      break;
   }
   bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
   bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
   if (jcr->verify_job) {
      Name = jcr->verify_job->hdr.name;
   } else {
      Name = "";
   }

   jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
   if (jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
      jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
      Jmsg(jcr, msg_type, 0, _("%s %s %s (%s): %s\n"
"  Build OS:               %s %s %s\n"
"  JobId:                  %d\n"
"  Job:                    %s\n"
"  FileSet:                %s\n"
"  Verify Level:           %s\n"
"  Client:                 %s\n"
"  Verify JobId:           %d\n"
"  Verify Job:             %s\n"
"  Start time:             %s\n"
"  End time:               %s\n"
"  Files Expected:         %s\n"
"  Files Examined:         %s\n"
"  Non-fatal FD errors:    %d\n"
"  FD termination status:  %s\n"
"  SD termination status:  %s\n"
"  Termination:            %s\n\n"),
           BACULA, my_name, VERSION, LSMDATE, edt,
           HOST_OS, DISTNAME, DISTVER,
           jcr->jr.JobId,
           jcr->jr.Job,
           jcr->fileset->hdr.name,
           level_to_str(jcr->get_JobLevel()),
           jcr->client->hdr.name,
           jcr->previous_jr.JobId,
           Name,
           sdt,
           edt,
           edit_uint64_with_commas(jcr->ExpectedFiles, ec1),
           edit_uint64_with_commas(jcr->JobFiles, ec2),
           jcr->JobErrors,
           fd_term_msg,
           sd_term_msg,
           term_msg);
   } else {
      Jmsg(jcr, msg_type, 0, _("%s %s %s (%s): %s\n"
"  Build:                  %s %s %s\n"
"  JobId:                  %d\n"
"  Job:                    %s\n"
"  FileSet:                %s\n"
"  Verify Level:           %s\n"
"  Client:                 %s\n"
"  Verify JobId:           %d\n"
"  Verify Job:             %s\n"
"  Start time:             %s\n"
"  End time:               %s\n"
"  Files Examined:         %s\n"
"  Non-fatal FD errors:    %d\n"
"  FD termination status:  %s\n"
"  Termination:            %s\n\n"),
           BACULA, my_name, VERSION, LSMDATE, edt,
           HOST_OS, DISTNAME, DISTVER,
           jcr->jr.JobId,
           jcr->jr.Job,
           jcr->fileset->hdr.name,
           level_to_str(jcr->get_JobLevel()),
           jcr->client->name(),
           jcr->previous_jr.JobId,
           Name,
           sdt,
           edt,
           edit_uint64_with_commas(jcr->JobFiles, ec1),
           jcr->JobErrors,
           fd_term_msg,
           term_msg);
   }
   Dmsg0(100, "Leave verify_cleanup()\n");
}
Пример #6
0
/*
 * Do a verification of the specified files against the Catlaog
 *
 *  Returns:  false on failure
 *            true  on success
 */
bool do_verify(JCR *jcr)
{
   int JobLevel;
   const char *level;
   BSOCK *fd = NULL;
   BSOCK *sd = NULL;
   int status;
   char ed1[100];
   JOB_DBR jr;
   JobId_t verify_jobid = 0;
   const char *Name;

   free_wstorage(jcr);                   /* we don't write */

   memset(&jcr->previous_jr, 0, sizeof(jcr->previous_jr));

   /*
    * Find JobId of last job that ran. Note, we do this when
    *   the job actually starts running, not at schedule time,
    *   so that we find the last job that terminated before
    *   this job runs rather than before it is scheduled. This
    *   permits scheduling a Backup and Verify at the same time,
    *   but with the Verify at a lower priority.
    *
    *   For VERIFY_CATALOG we want the JobId of the last INIT.
    *   For VERIFY_VOLUME_TO_CATALOG, we want the JobId of the
    *       last backup Job.
    */
   JobLevel = jcr->getJobLevel();
   switch (JobLevel) {
   case L_VERIFY_CATALOG:
   case L_VERIFY_VOLUME_TO_CATALOG:
   case L_VERIFY_DISK_TO_CATALOG:
      memcpy(&jr, &jcr->jr, sizeof(jr));
      if (jcr->res.verify_job &&
         (JobLevel == L_VERIFY_VOLUME_TO_CATALOG ||
          JobLevel == L_VERIFY_DISK_TO_CATALOG)) {
         Name = jcr->res.verify_job->name();
      } else {
         Name = NULL;
      }
      Dmsg1(100, "find last jobid for: %s\n", NPRT(Name));

      /*
       * See if user supplied a jobid= as run argument or from menu
       */
      if (jcr->VerifyJobId) {
         verify_jobid = jcr->VerifyJobId;
         Dmsg1(100, "Supplied jobid=%d\n", verify_jobid);

      } else {
         if (!db_find_last_jobid(jcr, jcr->db, Name, &jr)) {
            if (JobLevel == L_VERIFY_CATALOG) {
               Jmsg(jcr, M_FATAL, 0, _(
                       "Unable to find JobId of previous InitCatalog Job.\n"
                       "Please run a Verify with Level=InitCatalog before\n"
                       "running the current Job.\n"));
            } else {
               Jmsg(jcr, M_FATAL, 0, _(
                       "Unable to find JobId of previous Job for this client.\n"));
            }
            return false;
         }
         verify_jobid = jr.JobId;
      }
      Dmsg1(100, "Last full jobid=%d\n", verify_jobid);

      /*
       * Now get the job record for the previous backup that interests
       *   us. We use the verify_jobid that we found above.
       */
      jcr->previous_jr.JobId = verify_jobid;
      if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
         Jmsg(jcr, M_FATAL, 0, _("Could not get job record for previous Job. ERR=%s"),
              db_strerror(jcr->db));
         return false;
      }
      if (!(jcr->previous_jr.JobStatus == JS_Terminated ||
            jcr->previous_jr.JobStatus == JS_Warnings)) {
         Jmsg(jcr, M_FATAL, 0, _("Last Job %d did not terminate normally. JobStatus=%c\n"),
            verify_jobid, jcr->previous_jr.JobStatus);
         return false;
      }
      Jmsg(jcr, M_INFO, 0, _("Verifying against JobId=%d Job=%s\n"),
         jcr->previous_jr.JobId, jcr->previous_jr.Job);
   }

   /*
    * If we are verifying a Volume, we need the Storage
    *   daemon, so open a connection, otherwise, just
    *   create a dummy authorization key (passed to
    *   File daemon but not used).
    */
   switch (JobLevel) {
   case L_VERIFY_VOLUME_TO_CATALOG:
      /*
       * Note: negative status is an error, zero status, means
       *  no files were backed up, so skip calling SD and
       *  client.
       */
      status = create_restore_bootstrap_file(jcr);
      if (status < 0) {                      /* error */
         return false;
      } else if (status == 0) {              /* No files, nothing to do */
         verify_cleanup(jcr, JS_Terminated); /* clean up */
         return true;                      /* get out */
      }

      if (jcr->res.verify_job) {
         jcr->res.fileset = jcr->res.verify_job->fileset;
      }
      break;
   default:
      jcr->sd_auth_key = bstrdup("dummy");    /* dummy Storage daemon key */
      break;
   }

   Dmsg2(100, "ClientId=%u JobLevel=%c\n", jcr->previous_jr.ClientId, JobLevel);

   if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
      Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
      return false;
   }

   /*
    * Print Job Start message
    */
   Jmsg(jcr, M_INFO, 0, _("Start Verify JobId=%s Level=%s Job=%s\n"),
      edit_uint64(jcr->JobId, ed1), level_to_str(JobLevel), jcr->Job);

   switch (JobLevel) {
   case L_VERIFY_VOLUME_TO_CATALOG:
      /*
       * Start conversation with Storage daemon
       */
      jcr->setJobStatus(JS_Blocked);
      if (!connect_to_storage_daemon(jcr, 10, me->SDConnectTimeout, true)) {
         return false;
      }
      sd = jcr->store_bsock;

      /*
       * Now start a job with the Storage daemon
       */
      if (!start_storage_daemon_job(jcr, jcr->res.rstorage, NULL, /* send_bsr */ true)) {
         return false;
      }

      jcr->passive_client = jcr->res.client->passive;
      if (!jcr->passive_client) {
         /*
          * Start the Job in the SD.
          */
         if (!sd->fsend("run")) {
            return false;
         }

         /*
          * Now start a Storage daemon message thread
          */
         if (!start_storage_daemon_message_thread(jcr)) {
            return false;
         }
         Dmsg0(50, "Storage daemon connection OK\n");
      }

      /*
       * OK, now connect to the File daemon and ask him for the files.
       */
      jcr->setJobStatus(JS_Blocked);
      if (!connect_to_file_daemon(jcr, 10, me->FDConnectTimeout, true)) {
         goto bail_out;
      }
      send_job_info(jcr);
      fd = jcr->file_bsock;

      /*
       * Check if the file daemon supports passive client mode.
       */
      if (jcr->passive_client && jcr->FDVersion < FD_VERSION_51) {
         Jmsg(jcr, M_FATAL, 0,
               _("Client \"%s\" doesn't support passive client mode. "
                 "Please upgrade your client or disable compat mode.\n"),
              jcr->res.client->name());
         goto bail_out;
      }
      break;
   default:
      /*
       * OK, now connect to the File daemon and ask him for the files.
       */
      jcr->setJobStatus(JS_Blocked);
      if (!connect_to_file_daemon(jcr, 10, me->FDConnectTimeout, true)) {
         goto bail_out;
      }
      send_job_info(jcr);
      fd = jcr->file_bsock;
      break;
   }

   jcr->setJobStatus(JS_Running);

   Dmsg0(30, ">filed: Send include list\n");
   if (!send_include_list(jcr)) {
      goto bail_out;
   }

   Dmsg0(30, ">filed: Send exclude list\n");
   if (!send_exclude_list(jcr)) {
      goto bail_out;
   }

   /*
    * Send Level command to File daemon, as well as the Storage address if appropriate.
    */
   switch (JobLevel) {
   case L_VERIFY_INIT:
      level = "init";
      break;
   case L_VERIFY_CATALOG:
      level = "catalog";
      break;
   case L_VERIFY_VOLUME_TO_CATALOG:
      if (!jcr->RestoreBootstrap) {
         Jmsg0(jcr, M_FATAL, 0, _("Deprecated feature ... use bootstrap.\n"));
         goto bail_out;
      }

      if (!jcr->passive_client) {
         int tls_need = BNET_TLS_NONE;
         STORERES *store = jcr->res.rstore;

         /*
          * Send Storage daemon address to the File daemon
          */
         if (store->SDDport == 0) {
            store->SDDport = store->SDport;
         }

         /*
          * TLS Requirement
          */
         if (store->tls.enable) {
            if (store->tls.require) {
               tls_need = BNET_TLS_REQUIRED;
            } else {
               tls_need = BNET_TLS_OK;
            }
         }

         fd->fsend(storaddrcmd, store->address, store->SDDport, tls_need, jcr->sd_auth_key);
         if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
            goto bail_out;
         }
      } else {
         int tls_need = BNET_TLS_NONE;
         CLIENTRES *client = jcr->res.client;

         /*
          * TLS Requirement
          */
         if (client->tls.enable) {
            if (client->tls.require) {
               tls_need = BNET_TLS_REQUIRED;
            } else {
               tls_need = BNET_TLS_OK;
            }
         }

         /*
          * Tell the SD to connect to the FD.
          */
         sd->fsend(passiveclientcmd, client->address, client->FDport, tls_need);
         if (!response(jcr, sd, OKpassiveclient, "Passive client", DISPLAY_ERROR)) {
            goto bail_out;
         }

         /*
          * Start the Job in the SD.
          */
         if (!sd->fsend("run")) {
            goto bail_out;
         }

         /*
          * Now start a Storage daemon message thread
          */
         if (!start_storage_daemon_message_thread(jcr)) {
            goto bail_out;
         }
         Dmsg0(50, "Storage daemon connection OK\n");
      }

      level = "volume";
      break;
   case L_VERIFY_DATA:
      level = "data";
      break;
   case L_VERIFY_DISK_TO_CATALOG:
      level="disk_to_catalog";
      break;
   default:
      Jmsg2(jcr, M_FATAL, 0, _("Unimplemented Verify level %d(%c)\n"), JobLevel, JobLevel);
      goto bail_out;
   }

   if (!send_runscripts_commands(jcr)) {
      goto bail_out;
   }

   /*
    * Send verify command/level to File daemon
    */
   fd->fsend(verifycmd, level);
   if (!response(jcr, fd, OKverify, "Verify", DISPLAY_ERROR)) {
      goto bail_out;
   }

   /*
    * Now get data back from File daemon and
    *  compare it to the catalog or store it in the
    *  catalog depending on the run type.
    */
   switch (JobLevel) {
   case L_VERIFY_CATALOG:
      /*
       * Verify from catalog
       */
      Dmsg0(10, "Verify level=catalog\n");
      jcr->sd_msg_thread_done = true;   /* no SD msg thread, so it is done */
      jcr->SDJobStatus = JS_Terminated;
      get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId);
      break;
   case L_VERIFY_VOLUME_TO_CATALOG:
      /*
       * Verify Volume to catalog entries
       */
      Dmsg0(10, "Verify level=volume\n");
      get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId);
      break;
   case L_VERIFY_DISK_TO_CATALOG:
      /*
       * Verify Disk attributes to catalog
       */
      Dmsg0(10, "Verify level=disk_to_catalog\n");
      jcr->sd_msg_thread_done = true;   /* no SD msg thread, so it is done */
      jcr->SDJobStatus = JS_Terminated;
      get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId);
      break;
   case L_VERIFY_INIT:
      /*
       * Build catalog
       */
      Dmsg0(10, "Verify level=init\n");
      jcr->sd_msg_thread_done = true;   /* no SD msg thread, so it is done */
      jcr->SDJobStatus = JS_Terminated;
      get_attributes_and_put_in_catalog(jcr);
      db_end_transaction(jcr, jcr->db);   /* terminate any open transaction */
      db_write_batch_file_records(jcr);
      break;
   default:
      Jmsg1(jcr, M_FATAL, 0, _("Unimplemented verify level %d\n"), JobLevel);
      goto bail_out;
   }

   status = wait_for_job_termination(jcr);
   verify_cleanup(jcr, status);
   return true;

bail_out:
   if (jcr->file_bsock) {
      jcr->file_bsock->signal(BNET_TERMINATE);
      jcr->file_bsock->close();
      delete jcr->file_bsock;
      jcr->file_bsock = NULL;
   }

   return false;
}
Пример #7
0
int vtape::tape_op(struct mtop *mt_com)
{
   int result=0;
   int count = mt_com->mt_count;

   if (!online) {
      errno = ENOMEDIUM;
      return -1;
   }

   switch (mt_com->mt_op)
   {
   case MTRESET:
   case MTNOP:
   case MTSETDRVBUFFER:
      break;

   default:
   case MTRAS1:
   case MTRAS2:
   case MTRAS3:
   case MTSETDENSITY:
      errno = ENOTTY;
      result = -1;
      break;

   case MTFSF:                  /* Forward space over mt_count filemarks. */
      do {
         result = fsf();
      } while (--count > 0 && result == 0);
      break;

   case MTBSF:                  /* Backward space over mt_count filemarks. */
      do {
         result = bsf();
      } while (--count > 0 && result == 0);
      break;

   case MTFSR:      /* Forward space over mt_count records (tape blocks). */
/*
    file number = 1
    block number = 0

    file number = 1
    block number = 1

    mt: /dev/lto2: Erreur d'entree/sortie

    file number = 2
    block number = 0
*/
      /* tester si on se trouve a la fin du fichier */
      result = fsr(mt_com->mt_count);
      break;

   case MTBSR:      /* Backward space over mt_count records (tape blocks). */
      result = bsr(mt_com->mt_count);
      break;

   case MTWEOF:                 /* Write mt_count filemarks. */
      do {
         result = weof();
      } while (result == 0 && --count > 0);
      break;

   case MTREW:                  /* Rewind. */
      Dmsg0(dbglevel, "rewind vtape\n");
      check_eof();
      atEOF = atEOD = false;
      atBOT = true;
      current_file = 0;
      current_block = 0;
      lseek(fd, 0, SEEK_SET);
      result = !read_fm(VT_READ_EOF);
      break;

   case MTOFFL:                 /* put tape offline */
      result = offline();
      break;

   case MTRETEN:                /* Re-tension tape. */
      result = 0;
      break;

   case MTBSFM:                 /* not used by bareos */
      errno = EIO;
      result = -1;
      break;

   case MTFSFM:                 /* not used by bareos */
      errno = EIO;
      result = -1;
      break;

   case MTEOM:/* Go to the end of the recorded media (for appending files). */
      while (next_FM) {
         lseek(fd, next_FM, SEEK_SET);
         if (read_fm(VT_READ_EOF)) {
            current_file++;
         }
      }
      boffset_t l;
      while (::read(fd, &l, sizeof(l)) > 0) {
         if (l) {
            lseek(fd, l, SEEK_CUR);
         } else {
            ASSERT(0);
         }
         Dmsg0(dbglevel, "skip 1 block\n");
      }
      current_block = -1;
      atEOF = false;
      atEOD = true;

/*
   file number = 3
   block number = -1
*/
      /* Can be at EOM */
      break;

   case MTERASE:                /* not used by bareos */
      atEOD = true;
      atEOF = false;
      atEOT = false;

      current_file = 0;
      current_block = -1;
      lseek(fd, 0, SEEK_SET);
      read_fm(VT_READ_EOF);
      truncate_file();
      break;

   case MTSETBLK:
      break;

   case MTSEEK:
      break;

   case MTTELL:
      break;

   case MTFSS:
      break;

   case MTBSS:
      break;

   case MTWSM:
      break;

   case MTLOCK:
      break;

   case MTUNLOCK:
      break;

   case MTLOAD:
      break;

   case MTUNLOAD:
      break;

   case MTCOMPRESSION:
      break;

   case MTSETPART:
      break;

   case MTMKPART:
      break;
   }

   return result == 0 ? 0 : -1;
}
Пример #8
0
/*
 * Acquire device for writing. We permit multiple writers.
 *  If this is the first one, we read the label.
 *
 *  Returns: NULL if failed for any reason
 *           dcr if successful.
 *   Note, normally reserve_device_for_append() is called
 *   before this routine.
 */
DCR *acquire_device_for_append(DCR *dcr)
{
   DEVICE *dev = dcr->dev;
   JCR *jcr = dcr->jcr;
   bool ok = false;
   bool have_vol = false;

   Enter(200);
   init_device_wait_timers(dcr);

   dev->Lock_acquire();             /* only one job at a time */
   dev->Lock();
   Dmsg1(100, "acquire_append device is %s\n", dev->is_tape()?"tape":
        (dev->is_dvd()?"DVD":"disk"));
   /*
    * With the reservation system, this should not happen
    */
   if (dev->can_read()) {
      Mmsg2(jcr->errmsg, "Want to append but %s device %s is busy reading.\n",
         dev->print_type(), dev->print_name());
      Jmsg(jcr, M_FATAL, 0, jcr->errmsg);
      Dmsg0(50, jcr->errmsg);
      goto get_out;
   }

   dev->clear_unload();

   /*
    * have_vol defines whether or not mount_next_write_volume should
    *   ask the Director again about what Volume to use.
    */
   if (dev->can_append() && dcr->is_suitable_volume_mounted() &&
       strcmp(dcr->VolCatInfo.VolCatStatus, "Recycle") != 0) {
      Dmsg0(190, "device already in append.\n");
      /*
       * At this point, the correct tape is already mounted, so
       *   we do not need to do mount_next_write_volume(), unless
       *   we need to recycle the tape.
       */
       if (dev->num_writers == 0) {
          dev->VolCatInfo = dcr->VolCatInfo;   /* structure assignment */
       }
       have_vol = dcr->is_tape_position_ok();
   }

   if (!have_vol) {
      dev->rLock(true);
      block_device(dev, BST_DOING_ACQUIRE);
      dev->Unlock();
      Dmsg1(190, "jid=%u Do mount_next_write_vol\n", (uint32_t)jcr->JobId);
      if (!dcr->mount_next_write_volume()) {
         if (!job_canceled(jcr)) {
            /* Reduce "noise" -- don't print if job canceled */
            Mmsg2(jcr->errmsg, _("Could not ready %s device %s for append.\n"),
               dev->print_type(), dev->print_name());
            Jmsg(jcr, M_FATAL, 0, jcr->errmsg);
            Dmsg0(50, jcr->errmsg);
         }
         dev->Lock();
         unblock_device(dev);
         goto get_out;
      }
      Dmsg2(190, "Output pos=%u:%u\n", dcr->dev->file, dcr->dev->block_num);
      dev->Lock();
      unblock_device(dev);
   }

   if (generate_plugin_event(jcr, bsdEventDeviceOpen, dcr) != bRC_OK) {
      Mmsg0(jcr->errmsg,  _("generate_plugin_event(bsdEventDeviceOpen) Failed\n"));
      Jmsg(jcr, M_FATAL, 0, jcr->errmsg);
      Dmsg0(50, jcr->errmsg);
      goto get_out;
   }

   dev->num_writers++;                /* we are now a writer */
   if (jcr->NumWriteVolumes == 0) {
      jcr->NumWriteVolumes = 1;
   }
   dev->VolCatInfo.VolCatJobs++;              /* increment number of jobs on vol */
   Dmsg4(100, "=== nwriters=%d nres=%d vcatjob=%d dev=%s\n",
      dev->num_writers, dev->num_reserved(), dev->VolCatInfo.VolCatJobs,
      dev->print_name());
   ok = dir_update_volume_info(dcr, false, false); /* send Volume info to Director */

get_out:
   /* Don't plugin close here, we might have multiple writers */
   dcr->clear_reserved();
   dev->Unlock();
   dev->Unlock_acquire();
   Leave(200);
   return ok ? dcr : NULL;
}
Пример #9
0
/*********************************************************************
 * Acquire device for reading.
 *  The drive should have previously been reserved by calling
 *  reserve_device_for_read(). We read the Volume label from the block and
 *  leave the block pointers just after the label.
 *
 *  Returns: false if failed for any reason
 *           true  if successful
 */
bool acquire_device_for_read(DCR *dcr)
{
   DEVICE *dev;
   JCR *jcr = dcr->jcr;
   bool ok = false;
   bool tape_previously_mounted;
   VOL_LIST *vol;
   bool try_autochanger = true;
   int i;
   int vol_label_status;
   int retry = 0;

   Enter(rdbglvl);
   dev = dcr->dev;
   dev->Lock_read_acquire();
   Dmsg2(rdbglvl, "dcr=%p dev=%p\n", dcr, dcr->dev);
   Dmsg2(rdbglvl, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);
   dev->dblock(BST_DOING_ACQUIRE);

   if (dev->num_writers > 0) {
      Jmsg2(jcr, M_FATAL, 0, _("Acquire read: num_writers=%d not zero. Job %d canceled.\n"),
         dev->num_writers, jcr->JobId);
      goto get_out;
   }

   /* Find next Volume, if any */
   vol = jcr->VolList;
   if (!vol) {
      char ed1[50];
      Jmsg(jcr, M_FATAL, 0, _("No volumes specified for reading. Job %s canceled.\n"),
         edit_int64(jcr->JobId, ed1));
      goto get_out;
   }
   jcr->CurReadVolume++;
   for (i=1; i<jcr->CurReadVolume; i++) {
      vol = vol->next;
   }
   if (!vol) {
      Jmsg(jcr, M_FATAL, 0, _("Logic error: no next volume to read. Numvol=%d Curvol=%d\n"),
         jcr->NumReadVolumes, jcr->CurReadVolume);
      goto get_out;                   /* should not happen */
   }
   set_dcr_from_vol(dcr, vol);

   if (generate_plugin_event(jcr, bsdEventDeviceOpen, dcr) != bRC_OK) {
      Jmsg(jcr, M_FATAL, 0, _("generate_plugin_event(bsdEventDeviceOpen) Failed\n"));
      goto get_out;
   }

   Dmsg2(rdbglvl, "Want Vol=%s Slot=%d\n", vol->VolumeName, vol->Slot);

   /*
    * If the MediaType requested for this volume is not the
    *  same as the current drive, we attempt to find the same
    *  device that was used to write the orginal volume.  If
    *  found, we switch to using that device.
    *
    *  N.B. A lot of routines rely on the dcr pointer not changing
    *    read_records.c even has multiple dcrs cached, so we take care
    *    here to release all important parts of the dcr and re-acquire
    *    them such as the block pointer (size may change), but we do
    *    not release the dcr.
    */
   Dmsg2(rdbglvl, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);
   if (dcr->media_type[0] && strcmp(dcr->media_type, dev->device->media_type) != 0) {
      RCTX rctx;
      DIRSTORE *store;
      int stat;

      Jmsg4(jcr, M_INFO, 0, _("Changing read device. Want Media Type=\"%s\" have=\"%s\"\n"
                              "  %s device=%s\n"),
            dcr->media_type, dev->device->media_type, dev->print_type(),
            dev->print_name());
      Dmsg4(rdbglvl, "Changing read device. Want Media Type=\"%s\" have=\"%s\"\n"
                              "  %s device=%s\n",
            dcr->media_type, dev->device->media_type,
            dev->print_type(), dev->print_name());

      generate_plugin_event(jcr, bsdEventDeviceClose, dcr);

      dev->dunblock(DEV_UNLOCKED);

      lock_reservations();
      memset(&rctx, 0, sizeof(RCTX));
      rctx.jcr = jcr;
      jcr->read_dcr = dcr;
      jcr->reserve_msgs = New(alist(10, not_owned_by_alist));
      rctx.any_drive = true;
      rctx.device_name = vol->device;
      store = new DIRSTORE;
      memset(store, 0, sizeof(DIRSTORE));
      store->name[0] = 0; /* No storage name */
      bstrncpy(store->media_type, vol->MediaType, sizeof(store->media_type));
      bstrncpy(store->pool_name, dcr->pool_name, sizeof(store->pool_name));
      bstrncpy(store->pool_type, dcr->pool_type, sizeof(store->pool_type));
      store->append = false;
      rctx.store = store;
      clean_device(dcr);                     /* clean up the dcr */

      /*
       * Search for a new device
       */
      stat = search_res_for_device(rctx);
      release_reserve_messages(jcr);         /* release queued messages */
      unlock_reservations();

      if (stat == 1) { /* found new device to use */
         /*
          * Switching devices, so acquire lock on new device,
          *   then release the old one.
          */
         dcr->dev->Lock_read_acquire();      /* lock new one */
         dev->Unlock_read_acquire();         /* release old one */
         dev = dcr->dev;                     /* get new device pointer */
         dev->dblock(BST_DOING_ACQUIRE);

         dcr->VolumeName[0] = 0;
         Jmsg(jcr, M_INFO, 0, _("Media Type change.  New read %s device %s chosen.\n"),
            dev->print_type(), dev->print_name());
         Dmsg2(50, "Media Type change.  New read %s device %s chosen.\n",
            dev->print_type(), dev->print_name());
         if (generate_plugin_event(jcr, bsdEventDeviceOpen, dcr) != bRC_OK) {
            Jmsg(jcr, M_FATAL, 0, _("generate_plugin_event(bsdEventDeviceOpen) Failed\n"));
            goto get_out;
         }
         bstrncpy(dcr->VolumeName, vol->VolumeName, sizeof(dcr->VolumeName));
         dcr->setVolCatName(vol->VolumeName);
         bstrncpy(dcr->media_type, vol->MediaType, sizeof(dcr->media_type));
         dcr->VolCatInfo.Slot = vol->Slot;
         dcr->VolCatInfo.InChanger = vol->Slot > 0;
         bstrncpy(dcr->pool_name, store->pool_name, sizeof(dcr->pool_name));
         bstrncpy(dcr->pool_type, store->pool_type, sizeof(dcr->pool_type));
      } else {
         /* error */
         Jmsg1(jcr, M_FATAL, 0, _("No suitable device found to read Volume \"%s\"\n"),
            vol->VolumeName);
         Dmsg1(rdbglvl, "No suitable device found to read Volume \"%s\"\n", vol->VolumeName);
         goto get_out;
      }
   }
   Dmsg2(rdbglvl, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);

   dev->clear_unload();

   if (dev->vol && dev->vol->is_swapping()) {
      dev->vol->set_slot(vol->Slot);
      Dmsg3(rdbglvl, "swapping: slot=%d Vol=%s dev=%s\n", dev->vol->get_slot(),
         dev->vol->vol_name, dev->print_name());
   }

   init_device_wait_timers(dcr);

   tape_previously_mounted = dev->can_read() || dev->can_append() ||
                             dev->is_labeled();
// tape_initially_mounted = tape_previously_mounted;

   /* Volume info is always needed because of VolType */
   Dmsg1(rdbglvl, "dir_get_volume_info vol=%s\n", dcr->VolumeName);
   if (!dir_get_volume_info(dcr, GET_VOL_INFO_FOR_READ)) {
      Dmsg2(rdbglvl, "dir_get_vol_info failed for vol=%s: %s\n",
         dcr->VolumeName, jcr->errmsg);
      Jmsg1(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
   }
   dev->set_load();                /* set to load volume */

   for ( ;; ) {
      /* If not polling limit retries */
      if (!dev->poll && retry++ > 10) {
         break;
      }
      dev->clear_labeled();              /* force reread of label */
      if (job_canceled(jcr)) {
         char ed1[50];
         Mmsg1(dev->errmsg, _("Job %s canceled.\n"), edit_int64(jcr->JobId, ed1));
         Jmsg(jcr, M_INFO, 0, dev->errmsg);
         goto get_out;                /* error return */
      }

      dcr->do_unload();
      dcr->do_swapping(SD_READ);
      dcr->do_load(SD_READ);
      set_dcr_from_vol(dcr, vol);          /* refresh dcr with desired volume info */

      /*
       * This code ensures that the device is ready for
       * reading. If it is a file, it opens it.
       * If it is a tape, it checks the volume name
       */
      Dmsg1(rdbglvl, "open vol=%s\n", dcr->VolumeName);
      if (!dev->open(dcr, OPEN_READ_ONLY)) {
         if (!dev->poll) {
            Jmsg4(jcr, M_WARNING, 0, _("Read open %s device %s Volume \"%s\" failed: ERR=%s\n"),
                  dev->print_type(), dev->print_name(), dcr->VolumeName, dev->bstrerror());
         }
         goto default_path;
      }
      Dmsg1(rdbglvl, "opened dev %s OK\n", dev->print_name());

      /* Read Volume Label */
      Dmsg0(rdbglvl, "calling read-vol-label\n");
      vol_label_status = read_dev_volume_label(dcr);
      switch (vol_label_status) {
      case VOL_OK:
         Dmsg0(rdbglvl, "Got correct volume.\n");
         ok = true;
         dev->VolCatInfo = dcr->VolCatInfo;     /* structure assignment */
         break;                    /* got it */
      case VOL_IO_ERROR:
         Dmsg0(rdbglvl, "IO Error\n");
         /*
          * Send error message generated by read_dev_volume_label()
          *  only we really had a tape mounted. This supresses superfluous
          *  error messages when nothing is mounted.
          */
         if (tape_previously_mounted) {
            Jmsg(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
         }
         goto default_path;
      case VOL_TYPE_ERROR:
         Jmsg(jcr, M_FATAL, 0, dev->errmsg);
         goto get_out;
      case VOL_NAME_ERROR:
         Dmsg3(rdbglvl, "Vol name=%s want=%s drv=%s.\n", dev->VolHdr.VolumeName,
               dcr->VolumeName, dev->print_name());
         if (dev->is_volume_to_unload()) {
            goto default_path;
         }
         dev->set_unload();              /* force unload of unwanted tape */
         if (!unload_autochanger(dcr, -1)) {
            /* at least free the device so we can re-open with correct volume */
            dev->close();
            free_volume(dev);
         }
         dev->set_load();
         /* Fall through */
      default:
         Jmsg1(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
default_path:
         Dmsg0(rdbglvl, "default path\n");
         tape_previously_mounted = true;

         /*
          * If the device requires mount, close it, so the device can be ejected.
          */
         if (dev->requires_mount()) {
            dev->close();
            free_volume(dev);
         }

         /* Call autochanger only once unless ask_sysop called */
         if (try_autochanger) {
            int stat;
            Dmsg2(rdbglvl, "calling autoload Vol=%s Slot=%d\n",
               dcr->VolumeName, dcr->VolCatInfo.Slot);
            stat = autoload_device(dcr, SD_READ, NULL);
            if (stat > 0) {
               try_autochanger = false;
               continue;              /* try reading volume mounted */
            }
         }

         /* Mount a specific volume and no other */
         Dmsg0(rdbglvl, "calling dir_ask_sysop\n");
         if (!dir_ask_sysop_to_mount_volume(dcr, SD_READ)) {
            goto get_out;             /* error return */
         }

         /* Volume info is always needed because of VolType */
         Dmsg1(150, "dir_get_volume_info vol=%s\n", dcr->VolumeName);
         if (!dir_get_volume_info(dcr, GET_VOL_INFO_FOR_READ)) {
            Dmsg2(150, "dir_get_vol_info failed for vol=%s: %s\n",
                  dcr->VolumeName, jcr->errmsg);
            Jmsg1(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
         }
         dev->set_load();                /* set to load volume */

         try_autochanger = true;      /* permit trying the autochanger again */

         continue;                    /* try reading again */
      } /* end switch */
      break;
   } /* end for loop */

   if (!ok) {
      Jmsg2(jcr, M_FATAL, 0, _("Too many errors trying to mount %s device %s for reading.\n"),
            dev->print_type(), dev->print_name());
      goto get_out;
   }

   dev->clear_append();
   dev->set_read();
   jcr->sendJobStatus(JS_Running);
   Jmsg(jcr, M_INFO, 0, _("Ready to read from volume \"%s\" on %s device %s.\n"),
      dcr->VolumeName, dev->print_type(), dev->print_name());

get_out:
   dev->Lock();
   dcr->clear_reserved();
   /* If failed and not writing plugin close device */
   if (!ok && dev->num_writers == 0 && dev->num_reserved() == 0) {
      generate_plugin_event(jcr, bsdEventDeviceClose, dcr);
   }
   /*
    * Normally we are blocked, but in at least one error case above
    *   we are not blocked because we unsuccessfully tried changing
    *   devices.
    */
   if (dev->is_blocked()) {
      dev->dunblock(DEV_LOCKED);
   } else {
      dev->Unlock();               /* dunblock() unlock the device too */
   }
   Dmsg2(rdbglvl, "dcr=%p dev=%p\n", dcr, dcr->dev);
   Dmsg2(rdbglvl, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);
   dev->Unlock_read_acquire();
   Leave(rdbglvl);
   return ok;
}
Пример #10
0
Файл: sql.c Проект: AlD/bareos
int list_result(void *vctx, int nb_col, char **row)
{
   SQL_FIELD *field;
   int i, col_len, max_len = 0;
   int num_fields;
   char buf[2000], ewc[30];

   LIST_CTX *pctx = (LIST_CTX *)vctx;
   DB_LIST_HANDLER *send = pctx->send;
   e_list_type type = pctx->type;
   B_DB *mdb = pctx->mdb;
   void *ctx = pctx->ctx;
   JCR *jcr = pctx->jcr;

   num_fields = sql_num_fields(mdb);
   switch (type) {
   case NF_LIST:
   case RAW_LIST:
      /*
       * No need to calculate things like maximum field lenght for
       * unformated or raw output.
       */
      break;
   case HORZ_LIST:
   case VERT_LIST:
      if (!pctx->once) {
         pctx->once = true;

         Dmsg1(800, "list_result starts looking at %d fields\n", num_fields);
         /*
          * Determine column display widths
          */
         sql_field_seek(mdb, 0);
         for (i = 0; i < num_fields; i++) {
            Dmsg1(800, "list_result processing field %d\n", i);
            field = sql_fetch_field(mdb);
            if (!field) {
               break;
            }
            col_len = cstrlen(field->name);
            if (type == VERT_LIST) {
               if (col_len > max_len) {
                  max_len = col_len;
               }
            } else {
               if (sql_field_is_numeric(mdb, field->type) && (int)field->max_length > 0) { /* fixup for commas */
                  field->max_length += (field->max_length - 1) / 3;
               }
               if (col_len < (int)field->max_length) {
                  col_len = field->max_length;
               }
               if (col_len < 4 && !sql_field_is_not_null(mdb, field->flags)) {
                  col_len = 4;                 /* 4 = length of the word "NULL" */
               }
               field->max_length = col_len;    /* reset column info */
            }
         }

         pctx->num_rows++;

         Dmsg0(800, "list_result finished first loop\n");
         if (type == VERT_LIST) {
            break;
         }

         Dmsg1(800, "list_result starts second loop looking at %d fields\n", num_fields);

         /*
          * Keep the result to display the same line at the end of the table
          */
         list_dashes(mdb, last_line_handler, pctx);
         send(ctx, pctx->line);

         send(ctx, "|");
         sql_field_seek(mdb, 0);
         for (i = 0; i < num_fields; i++) {
            Dmsg1(800, "list_result looking at field %d\n", i);
            field = sql_fetch_field(mdb);
            if (!field) {
               break;
            }
            max_len = max_length(field->max_length);
            bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name);
            send(ctx, buf);
         }
         send(ctx, "\n");
         list_dashes(mdb, send, ctx);
      }
      break;
   default:
      break;
   }

   switch (type) {
   case NF_LIST:
   case RAW_LIST:
      Dmsg1(800, "list_result starts third loop looking at %d fields\n", num_fields);
      sql_field_seek(mdb, 0);
      for (i = 0; i < num_fields; i++) {
         field = sql_fetch_field(mdb);
         if (!field) {
            break;
         }
         if (row[i] == NULL) {
            bsnprintf(buf, sizeof(buf), " %s", "NULL");
         } else {
            bsnprintf(buf, sizeof(buf), " %s", row[i]);
         }
         send(ctx, buf);
      }
      if (type != RAW_LIST) {
         send(ctx, "\n");
      }
      break;
   case HORZ_LIST:
      Dmsg1(800, "list_result starts third loop looking at %d fields\n", num_fields);
      sql_field_seek(mdb, 0);
      send(ctx, "|");
      for (i = 0; i < num_fields; i++) {
         field = sql_fetch_field(mdb);
         if (!field) {
            break;
         }
         max_len = max_length(field->max_length);
         if (row[i] == NULL) {
            bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL");
         } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
            bsnprintf(buf, sizeof(buf), " %*s |", max_len,
                      add_commas(row[i], ewc));
         } else {
            bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]);
         }
         send(ctx, buf);
      }
      send(ctx, "\n");
      break;
   case VERT_LIST:
      Dmsg1(800, "list_result starts vertical list at %d fields\n", num_fields);
      sql_field_seek(mdb, 0);
      for (i = 0; i < num_fields; i++) {
         field = sql_fetch_field(mdb);
         if (!field) {
            break;
         }
         if (row[i] == NULL) {
            bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL");
         } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
            bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name,
                      add_commas(row[i], ewc));
         } else {
            bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]);
         }
         send(ctx, buf);
      }
      send(ctx, "\n");
      break;
   default:
      break;
   }
   return 0;
}
Пример #11
0
Файл: sql.c Проект: AlD/bareos
/*
 * If full_list is set, we list vertically, otherwise, we
 *  list on one line horizontally.
 * Return number of rows
 */
int list_result(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type type)
{
   SQL_FIELD *field;
   SQL_ROW row;
   int i, col_len, max_len = 0;
   int num_fields;
   char buf[2000], ewc[30];

   Dmsg0(800, "list_result starts\n");
   if (sql_num_rows(mdb) == 0) {
      send(ctx, _("No results to list.\n"));
      return sql_num_rows(mdb);
   }

   num_fields = sql_num_fields(mdb);
   switch (type) {
   case NF_LIST:
   case RAW_LIST:
      /*
       * No need to calculate things like column widths for
       * unformated or raw output.
       */
      break;
   case HORZ_LIST:
   case VERT_LIST:
      Dmsg1(800, "list_result starts looking at %d fields\n", num_fields);
      /*
       * Determine column display widths
       */
      sql_field_seek(mdb, 0);
      for (i = 0; i < num_fields; i++) {
         Dmsg1(800, "list_result processing field %d\n", i);
         field = sql_fetch_field(mdb);
         if (!field) {
            break;
         }
         col_len = cstrlen(field->name);
         if (type == VERT_LIST) {
            if (col_len > max_len) {
               max_len = col_len;
            }
         } else {
            if (sql_field_is_numeric(mdb, field->type) && (int)field->max_length > 0) { /* fixup for commas */
               field->max_length += (field->max_length - 1) / 3;
            }
            if (col_len < (int)field->max_length) {
               col_len = field->max_length;
            }
            if (col_len < 4 && !sql_field_is_not_null(mdb, field->flags)) {
               col_len = 4;                 /* 4 = length of the word "NULL" */
            }
            field->max_length = col_len;    /* reset column info */
         }
      }
      break;
   }

   Dmsg0(800, "list_result finished first loop\n");

   switch (type) {
   case NF_LIST:
   case RAW_LIST:
      Dmsg1(800, "list_result starts second loop looking at %d fields\n", num_fields);
      while ((row = sql_fetch_row(mdb)) != NULL) {
         sql_field_seek(mdb, 0);
         for (i = 0; i < num_fields; i++) {
            field = sql_fetch_field(mdb);
            if (!field) {
               break;
            }
            if (row[i] == NULL) {
               bsnprintf(buf, sizeof(buf), " %s", "NULL");
            } else {
               bsnprintf(buf, sizeof(buf), " %s", row[i]);
            }
            send(ctx, buf);
         }
         if (type != RAW_LIST) {
            send(ctx, "\n");
         }
      }
      break;
   case HORZ_LIST:
      Dmsg1(800, "list_result starts second loop looking at %d fields\n", num_fields);
      list_dashes(mdb, send, ctx);
      send(ctx, "|");
      sql_field_seek(mdb, 0);
      for (i = 0; i < num_fields; i++) {
         Dmsg1(800, "list_result looking at field %d\n", i);
         field = sql_fetch_field(mdb);
         if (!field) {
            break;
         }
         max_len = max_length(field->max_length);
         bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name);
         send(ctx, buf);
      }
      send(ctx, "\n");
      list_dashes(mdb, send, ctx);

      Dmsg1(800, "list_result starts third loop looking at %d fields\n", num_fields);
      while ((row = sql_fetch_row(mdb)) != NULL) {
         sql_field_seek(mdb, 0);
         send(ctx, "|");
         for (i = 0; i < num_fields; i++) {
            field = sql_fetch_field(mdb);
            if (!field) {
               break;
            }
            max_len = max_length(field->max_length);
            if (row[i] == NULL) {
               bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL");
            } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
               bsnprintf(buf, sizeof(buf), " %*s |", max_len,
                         add_commas(row[i], ewc));
            } else {
               bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]);
            }
            send(ctx, buf);
         }
         send(ctx, "\n");
      }
      list_dashes(mdb, send, ctx);
      break;
   case VERT_LIST:
      Dmsg1(800, "list_result starts vertical list at %d fields\n", num_fields);
      while ((row = sql_fetch_row(mdb)) != NULL) {
         sql_field_seek(mdb, 0);
         for (i = 0; i < num_fields; i++) {
            field = sql_fetch_field(mdb);
            if (!field) {
               break;
            }
            if (row[i] == NULL) {
               bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL");
            } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
               bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name,
                   add_commas(row[i], ewc));
            } else {
               bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]);
            }
            send(ctx, buf);
         }
         send(ctx, "\n");
      }
      break;
   }
   return sql_num_rows(mdb);
}
Пример #12
0
/*
 * Wait for SysOp to mount a tape on a specific device
 *
 *   Returns: W_ERROR, W_TIMEOUT, W_POLL, W_MOUNT, or W_WAKE
 */
int wait_for_sysop(DCR *dcr)
{
   struct timeval tv;
   struct timezone tz;
   struct timespec timeout;
   time_t last_heartbeat = 0;
   time_t first_start = time(NULL);
   int stat = 0;
   int add_wait;
   bool unmounted;
   DEVICE *dev = dcr->dev;
   JCR *jcr = dcr->jcr;

   dev->Lock();
   Dmsg1(dbglvl, "Enter blocked=%s\n", dev->print_blocked());

   /*
    * Since we want to mount a tape, make sure current one is
    *  not marked as using this drive.
    */
   volume_unused(dcr);

   unmounted = dev->is_device_unmounted();
   dev->poll = false;
   /*
    * Wait requested time (dev->rem_wait_sec).  However, we also wake up every
    *    HB_TIME seconds and send a heartbeat to the FD and the Director
    *    to keep stateful firewalls from closing them down while waiting
    *    for the operator.
    */
   add_wait = dev->rem_wait_sec;
   if (me->heartbeat_interval && add_wait > me->heartbeat_interval) {
      add_wait = me->heartbeat_interval;
   }
   /* If the user did not unmount the tape and we are polling, ensure
    *  that we poll at the correct interval.
    */
   if (!unmounted && dev->vol_poll_interval && add_wait > dev->vol_poll_interval) {
      add_wait = dev->vol_poll_interval;
   }

   if (!unmounted) {
      Dmsg1(dbglvl, "blocked=%s\n", dev->print_blocked());
      dev->dev_prev_blocked = dev->blocked();
      dev->set_blocked(BST_WAITING_FOR_SYSOP); /* indicate waiting for mount */
   }

   for ( ; !job_canceled(jcr); ) {
      time_t now, start, total_waited;

      gettimeofday(&tv, &tz);
      timeout.tv_nsec = tv.tv_usec * 1000;
      timeout.tv_sec = tv.tv_sec + add_wait;

      Dmsg4(dbglvl, "I'm going to sleep on device %s. HB=%d rem_wait=%d add_wait=%d\n",
         dev->print_name(), (int)me->heartbeat_interval, dev->rem_wait_sec, add_wait);
      start = time(NULL);

      /* Wait required time */
      stat = dev->next_vol_timedwait(&timeout);

      Dmsg2(dbglvl, "Wokeup from sleep on device stat=%d blocked=%s\n", stat,
         dev->print_blocked());
      now = time(NULL);
      total_waited = now - first_start;
      dev->rem_wait_sec -= (now - start);

      /* Note, this always triggers the first time. We want that. */
      if (me->heartbeat_interval) {
         if (now - last_heartbeat >= me->heartbeat_interval) {
            /* send heartbeats */
            if (jcr->file_bsock) {
               jcr->file_bsock->signal(BNET_HEARTBEAT);
               Dmsg0(dbglvl, "Send heartbeat to FD.\n");
            }
            if (jcr->dir_bsock) {
               jcr->dir_bsock->signal(BNET_HEARTBEAT);
            }
            last_heartbeat = now;
         }
      }

      if (stat == EINVAL) {
         berrno be;
         Jmsg1(jcr, M_FATAL, 0, _("pthread timedwait error. ERR=%s\n"), be.bstrerror(stat));
         stat = W_ERROR;               /* error */
         break;
      }

      /*
       * Continue waiting if operator is labeling volumes
       */
      if (dev->blocked() == BST_WRITING_LABEL) {
         continue;
      }

      if (dev->rem_wait_sec <= 0) {  /* on exceeding wait time return */
         Dmsg0(dbglvl, "Exceed wait time.\n");
         stat = W_TIMEOUT;
         break;
      }

      /*
       * Check if user unmounted the device while we were waiting
       */
      unmounted = dev->is_device_unmounted();

      if (!unmounted && dev->vol_poll_interval &&
          (total_waited >= dev->vol_poll_interval)) {
         Dmsg1(dbglvl, "poll return in wait blocked=%s\n", dev->print_blocked());
         dev->poll = true;            /* returning a poll event */
         stat = W_POLL;
         break;
      }
      /*
       * Check if user mounted the device while we were waiting
       */
      if (dev->blocked() == BST_MOUNT) {   /* mount request ? */
         Dmsg0(dbglvl, "Mounted return.\n");
         stat = W_MOUNT;
         break;
      }

      /*
       * If we did not timeout, then some event happened, so
       *   return to check if state changed.
       */
      if (stat != ETIMEDOUT) {
         berrno be;
         Dmsg2(dbglvl, "Wake return. stat=%d. ERR=%s\n", stat, be.bstrerror(stat));
         stat = W_WAKE;          /* someone woke us */
         break;
      }

      /*
       * At this point, we know we woke up because of a timeout,
       *   that was due to a heartbeat, because any other reason would
       *   have caused us to return, so update the wait counters and continue.
       */
      add_wait = dev->rem_wait_sec;
      if (me->heartbeat_interval && add_wait > me->heartbeat_interval) {
         add_wait = me->heartbeat_interval;
      }
      /* If the user did not unmount the tape and we are polling, ensure
       *  that we poll at the correct interval.
       */
      if (!unmounted && dev->vol_poll_interval &&
           add_wait > dev->vol_poll_interval - total_waited) {
         add_wait = dev->vol_poll_interval - total_waited;
      }
      if (add_wait < 0) {
         add_wait = 0;
      }
   }

   if (!unmounted) {
      dev->set_blocked(dev->dev_prev_blocked);    /* restore entry state */
      Dmsg1(dbglvl, "set %s\n", dev->print_blocked());
   }
   Dmsg1(dbglvl, "Exit blocked=%s\n", dev->print_blocked());
   dev->Unlock();
   return stat;
}
Пример #13
0
static bRC do_set_scsi_encryption_key(void *value)
{
   DCR *dcr;
   DEVICE *dev;
   DEVRES *device;
   DIRRES *director;
   char StoredVolEncrKey[MAX_NAME_LENGTH];
   char VolEncrKey[MAX_NAME_LENGTH];

   /*
    * Unpack the arguments passed in.
    */
   dcr = (DCR *)value;
   if (!dcr) {
      return bRC_Error;
   }
   dev = dcr->dev;
   if (!dev) {
      return bRC_Error;
   }
   device = dev->device;
   if (!device) {
      return bRC_Error;
   }

   *StoredVolEncrKey = '\0';
   if (!get_volume_encryption_key(dcr, StoredVolEncrKey)) {
      return bRC_Error;
   }

   /*
    * See if a volume encryption key is available.
    */
   if (!*StoredVolEncrKey) {
      Dmsg0(dbglvl, "scsicrypto-sd: No encryption key to load on device\n");
      return bRC_OK;
   }

   /*
    * See if device supports hardware encryption.
    */
   if (!device->drive_crypto_enabled) {
      Dmsg0(dbglvl, "scsicrypto-sd: Trying to load encryption key on drive without support\n");
      Emsg0(M_ERROR, 0,
            _("scsicrypto-sd: Trying to load encryption key on drive without support\n"));
      return bRC_Error;
   }

   /*
    * The key passed from the director to the storage daemon is always base64 encoded.
    */
   base64_to_bin(VolEncrKey, sizeof(VolEncrKey), StoredVolEncrKey, strlen(StoredVolEncrKey));

   /*
    * See if we have an key encryption key in the config then the passed key
    * has been wrapped using RFC3394 key wrapping. We first copy the current
    * wrapped key into a temporary variable for unwrapping.
    */
   if (dcr->jcr && dcr->jcr->director) {
      director = dcr->jcr->director;
      if (director->keyencrkey) {
         char WrappedVolEncrKey[MAX_NAME_LENGTH];

         memcpy(WrappedVolEncrKey, VolEncrKey, MAX_NAME_LENGTH);
         memset(VolEncrKey, 0, MAX_NAME_LENGTH);

         if (aes_unwrap((unsigned char *)director->keyencrkey,
                        DEFAULT_PASSPHRASE_LENGTH / 8,
                        (unsigned char *)WrappedVolEncrKey,
                        (unsigned char *)VolEncrKey) != 0) {
            Dmsg1(dbglvl,
                  "scsicrypto-sd: Failed to unwrap encryption key using %s\n", director->keyencrkey);
            Emsg0(M_ERROR, 0,
                  _("scsicrypto-sd: Failed to unwrap encryption key, probably wrong KeyEncryptionKey in config\n"));
            return bRC_Error;
         }
      }
   }

   Dmsg1(dbglvl, "scsicrypto-sd: Loading new crypto key %s\n", VolEncrKey);

   P(crypto_operation_mutex);
   if (set_scsi_encryption_key(dev->fd(), dev->dev_name, VolEncrKey)) {
      dev->set_crypto_enabled();
      V(crypto_operation_mutex);
      return bRC_OK;
   } else {
      V(crypto_operation_mutex);
      return bRC_Error;
   }
}
Пример #14
0
/*
 * Release resources allocated during backup.
 */
void backup_cleanup(JCR *jcr, int TermCode)
{
   char sdt[50], edt[50], schedt[50];
   char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
   char ec6[30], ec7[30], ec8[30], elapsed[50];
   char term_code[100], fd_term_msg[100], sd_term_msg[100];
   const char *term_msg;
   int msg_type = M_INFO;
   MEDIA_DBR mr;
   CLIENT_DBR cr;
   double kbps, compression;
   utime_t RunTime;

   if (jcr->get_JobLevel() == L_VIRTUAL_FULL) {
      vbackup_cleanup(jcr, TermCode);
      return;
   }

   Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
   memset(&mr, 0, sizeof(mr));
   memset(&cr, 0, sizeof(cr));

   update_job_end(jcr, TermCode);

   if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
      Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
         db_strerror(jcr->db));
      set_jcr_job_status(jcr, JS_ErrorTerminated);
   }

   bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
   if (!db_get_client_record(jcr, jcr->db, &cr)) {
      Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
         db_strerror(jcr->db));
   }

   bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
   if (!db_get_media_record(jcr, jcr->db, &mr)) {
      Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
         mr.VolumeName, db_strerror(jcr->db));
      set_jcr_job_status(jcr, JS_ErrorTerminated);
   }

   update_bootstrap_file(jcr);

   switch (jcr->JobStatus) {
      case JS_Terminated:
         if (jcr->JobErrors || jcr->SDErrors) {
            term_msg = _("Backup OK -- with warnings");
         } else {
            term_msg = _("Backup OK");
         }
         break;
      case JS_Warnings:
         term_msg = _("Backup OK -- with warnings");
         break;
      case JS_FatalError:
      case JS_ErrorTerminated:
         term_msg = _("*** Backup Error ***");
         msg_type = M_ERROR;          /* Generate error message */
         if (jcr->store_bsock) {
            jcr->store_bsock->signal(BNET_TERMINATE);
            if (jcr->SD_msg_chan) {
               pthread_cancel(jcr->SD_msg_chan);
            }
         }
         break;
      case JS_Canceled:
         term_msg = _("Backup Canceled");
         if (jcr->store_bsock) {
            jcr->store_bsock->signal(BNET_TERMINATE);
            if (jcr->SD_msg_chan) {
               pthread_cancel(jcr->SD_msg_chan);
            }
         }
         break;
      default:
         term_msg = term_code;
         sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
         break;
   }
   bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
   bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
   bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
   RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
   if (RunTime <= 0) {
      kbps = 0;
   } else {
      kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
   }
   if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
      /*
       * Note, if the job has erred, most likely it did not write any
       *  tape, so suppress this "error" message since in that case
       *  it is normal.  Or look at it the other way, only for a
       *  normal exit should we complain about this error.
       */
      if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
         Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
      }
      jcr->VolumeName[0] = 0;         /* none */
   }

   if (jcr->ReadBytes == 0) {
      bstrncpy(compress, "None", sizeof(compress));
   } else {
      compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
      if (compression < 0.5) {
         bstrncpy(compress, "None", sizeof(compress));
      } else {
         bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
      }
   }
   jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
   jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));

// bmicrosleep(15, 0);                /* for debugging SIGHUP */

   Jmsg(jcr, msg_type, 0, _("%s %s %s (%s): %s\n"
"  Build OS:               %s %s %s\n"
"  JobId:                  %d\n"
"  Job:                    %s\n"
"  Backup Level:           %s%s\n"
"  Client:                 \"%s\" %s\n"
"  FileSet:                \"%s\" %s\n"
"  Pool:                   \"%s\" (From %s)\n"
"  Catalog:                \"%s\" (From %s)\n"
"  Storage:                \"%s\" (From %s)\n"
"  Scheduled time:         %s\n"
"  Start time:             %s\n"
"  End time:               %s\n"
"  Elapsed time:           %s\n"
"  Priority:               %d\n"
"  FD Files Written:       %s\n"
"  SD Files Written:       %s\n"
"  FD Bytes Written:       %s (%sB)\n"
"  SD Bytes Written:       %s (%sB)\n"
"  Rate:                   %.1f KB/s\n"
"  Software Compression:   %s\n"
"  VSS:                    %s\n"
"  Encryption:             %s\n"
"  Accurate:               %s\n"
"  Volume name(s):         %s\n"
"  Volume Session Id:      %d\n"
"  Volume Session Time:    %d\n"
"  Last Volume Bytes:      %s (%sB)\n"
"  Non-fatal FD errors:    %d\n"
"  SD Errors:              %d\n"
"  FD termination status:  %s\n"
"  SD termination status:  %s\n"
"  Termination:            %s\n\n"),
        BACULA, my_name, VERSION, LSMDATE, edt,
        HOST_OS, DISTNAME, DISTVER,
        jcr->jr.JobId,
        jcr->jr.Job,
        level_to_str(jcr->get_JobLevel()), jcr->since,
        jcr->client->name(), cr.Uname,
        jcr->fileset->name(), jcr->FSCreateTime,
        jcr->pool->name(), jcr->pool_source,
        jcr->catalog->name(), jcr->catalog_source,
        jcr->wstore->name(), jcr->wstore_source,
        schedt,
        sdt,
        edt,
        edit_utime(RunTime, elapsed, sizeof(elapsed)),
        jcr->JobPriority,
        edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
        edit_uint64_with_commas(jcr->SDJobFiles, ec2),
        edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
        edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
        edit_uint64_with_commas(jcr->SDJobBytes, ec5),
        edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
        kbps,
        compress,
        jcr->VSS?_("yes"):_("no"),
        jcr->Encrypt?_("yes"):_("no"),
        jcr->accurate?_("yes"):_("no"),
        jcr->VolumeName,
        jcr->VolSessionId,
        jcr->VolSessionTime,
        edit_uint64_with_commas(mr.VolBytes, ec7),
        edit_uint64_with_suffix(mr.VolBytes, ec8),
        jcr->JobErrors,
        jcr->SDErrors,
        fd_term_msg,
        sd_term_msg,
        term_msg);

   Dmsg0(100, "Leave backup_cleanup()\n");
}
Пример #15
0
/*
 * Store Messages Destination information
 */
static void store_msgs(LEX *lc, RES_ITEM *item, int index, int pass)
{
   int token;
   char *cmd;
   POOLMEM *dest;
   int dest_len;
   URES *res_all = (URES *)my_config->m_res_all;

   Dmsg2(900, "store_msgs pass=%d code=%d\n", pass, item->code);
   if (pass == 1) {
      switch (item->code) {
      case MD_STDOUT:
      case MD_STDERR:
      case MD_SYSLOG:              /* syslog */
      case MD_CONSOLE:
      case MD_CATALOG:
         scan_types(lc, (MSGSRES *)(item->value), item->code, NULL, NULL);
         break;
      case MD_OPERATOR:            /* send to operator */
      case MD_DIRECTOR:            /* send to Director */
      case MD_MAIL:                /* mail */
      case MD_MAIL_ON_ERROR:       /* mail if Job errors */
      case MD_MAIL_ON_SUCCESS:     /* mail if Job succeeds */
         if (item->code == MD_OPERATOR) {
            cmd = res_all->res_msgs.operator_cmd;
         } else {
            cmd = res_all->res_msgs.mail_cmd;
         }
         dest = get_pool_memory(PM_MESSAGE);
         dest[0] = 0;
         dest_len = 0;
         /*
          * Pick up comma separated list of destinations
          */
         for ( ;; ) {
            token = lex_get_token(lc, T_NAME);   /* scan destination */
            dest = check_pool_memory_size(dest, dest_len + lc->str_len + 2);
            if (dest[0] != 0) {
               pm_strcat(dest, " ");  /* separate multiple destinations with space */
               dest_len++;
            }
            pm_strcat(dest, lc->str);
            dest_len += lc->str_len;
            Dmsg2(900, "store_msgs newdest=%s: dest=%s:\n", lc->str, NPRT(dest));
            token = lex_get_token(lc, T_SKIP_EOL);
            if (token == T_COMMA) {
               continue;           /* get another destination */
            }
            if (token != T_EQUALS) {
               scan_err1(lc, _("expected an =, got: %s"), lc->str);
               return;
            }
            break;
         }
         Dmsg1(900, "mail_cmd=%s\n", NPRT(cmd));
         scan_types(lc, (MSGSRES *)(item->value), item->code, dest, cmd);
         free_pool_memory(dest);
         Dmsg0(900, "done with dest codes\n");
         break;
      case MD_FILE:                /* file */
      case MD_APPEND:              /* append */
         dest = get_pool_memory(PM_MESSAGE);
         /*
          * Pick up a single destination
          */
         token = lex_get_token(lc, T_NAME);   /* scan destination */
         pm_strcpy(dest, lc->str);
         dest_len = lc->str_len;
         token = lex_get_token(lc, T_SKIP_EOL);
         Dmsg1(900, "store_msgs dest=%s:\n", NPRT(dest));
         if (token != T_EQUALS) {
            scan_err1(lc, _("expected an =, got: %s"), lc->str);
            return;
         }
         scan_types(lc, (MSGSRES *)(item->value), item->code, dest, NULL);
         free_pool_memory(dest);
         Dmsg0(900, "done with dest codes\n");
         break;
      default:
         scan_err1(lc, _("Unknown item code: %d\n"), item->code);
         return;
      }
   }
   scan_to_eol(lc);
   set_bit(index, res_all->hdr.item_present);
   Dmsg0(900, "Done store_msgs\n");
}
Пример #16
0
static void list_running_jobs_plain(STATUS_PKT *sp)
{
   JCR *njcr;
   int len, sec, bps;
   bool found = false;
   POOL_MEM msg(PM_MESSAGE);
   char dt[MAX_TIME_LENGTH], b1[32], b2[32], b3[32], b4[32];

   /*
    * List running jobs
    */
   Dmsg0(1000, "Begin status jcr loop.\n");
   len = Mmsg(msg, _("\nRunning Jobs:\n"));
   sendit(msg, len, sp);
   const char *vss = "";
#ifdef WIN32_VSS
   if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
      vss = "VSS ";
   }
#endif

   foreach_jcr(njcr) {
      bstrftime_nc(dt, sizeof(dt), njcr->start_time);
      if (njcr->JobId == 0) {
         len = Mmsg(msg, _("Director connected at: %s\n"), dt);
      } else {
         len = Mmsg(msg, _("JobId %d Job %s is running.\n"),
                    njcr->JobId, njcr->Job);
         sendit(msg, len, sp);
         len = Mmsg(msg, _("    %s%s %s Job started: %s\n"),
                    vss, level_to_str(njcr->getJobLevel()),
                    job_type_to_str(njcr->getJobType()), dt);
      }
      sendit(msg, len, sp);
      if (njcr->JobId == 0) {
         continue;
      }
      sec = time(NULL) - njcr->start_time;
      if (sec <= 0) {
         sec = 1;
      }
      bps = (int)(njcr->JobBytes / sec);
      len = Mmsg(msg,  _("    Files=%s Bytes=%s Bytes/sec=%s Errors=%d\n"
                         "    Bwlimit=%s\n"),
                 edit_uint64_with_commas(njcr->JobFiles, b1),
                 edit_uint64_with_commas(njcr->JobBytes, b2),
                 edit_uint64_with_commas(bps, b3),
                 njcr->JobErrors,
                 edit_uint64_with_commas(njcr->max_bandwidth, b4));
      sendit(msg, len, sp);
      len = Mmsg(msg, _("    Files Examined=%s\n"),
                 edit_uint64_with_commas(njcr->num_files_examined, b1));
      sendit(msg, len, sp);
      if (njcr->JobFiles > 0) {
         njcr->lock();
         len = Mmsg(msg, _("    Processing file: %s\n"), njcr->last_fname);
         njcr->unlock();
         sendit(msg, len, sp);
      }

      found = true;
      if (njcr->store_bsock) {
         len = Mmsg(msg, "    SDReadSeqNo=%" lld " fd=%d\n",
                    njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
         sendit(msg, len, sp);
      } else {
         len = Mmsg(msg, _("    SDSocket closed.\n"));
         sendit(msg, len, sp);
      }
   }
   endeach_jcr(njcr);

   if (!found) {
      len = Mmsg(msg, _("No Jobs running.\n"));
      sendit(msg, len, sp);
   }

   len = pm_strcpy(msg, _("====\n"));
   sendit(msg, len, sp);
}
Пример #17
0
/*
 * Store a size in bytes
 */
static void store_int_unit(LEX *lc, RES_ITEM *item, int index, int pass,
                           bool size32, enum unit_type type)
{
   int token;
   uint64_t uvalue;
   char bsize[500];
   URES *res_all = (URES *)my_config->m_res_all;

   Dmsg0(900, "Enter store_unit\n");
   token = lex_get_token(lc, T_SKIP_EOL);
   errno = 0;
   switch (token) {
   case T_NUMBER:
   case T_IDENTIFIER:
   case T_UNQUOTED_STRING:
      bstrncpy(bsize, lc->str, sizeof(bsize));  /* save first part */
      /*
       * If terminated by space, scan and get modifier
       */
      while (lc->ch == ' ') {
         token = lex_get_token(lc, T_ALL);
         switch (token) {
         case T_NUMBER:
         case T_IDENTIFIER:
         case T_UNQUOTED_STRING:
            bstrncat(bsize, lc->str, sizeof(bsize));
            break;
         }
      }

      switch (type) {
      case STORE_SIZE:
         if (!size_to_uint64(bsize, &uvalue)) {
            scan_err1(lc, _("expected a size number, got: %s"), lc->str);
            return;
         }
         break;
      case STORE_SPEED:
         if (!speed_to_uint64(bsize, &uvalue)) {
            scan_err1(lc, _("expected a speed number, got: %s"), lc->str);
            return;
         }
         break;
      default:
         scan_err0(lc, _("unknown unit type encountered"));
         return;
      }

      if (size32) {
         *(item->ui32value) = (uint32_t)uvalue;
      } else {
         switch (type) {
         case STORE_SIZE:
            *(item->i64value) = uvalue;
            break;
         case STORE_SPEED:
            *(item->ui64value) = uvalue;
            break;
         }
      }
      break;
   default:
      scan_err2(lc, _("expected a %s, got: %s"),
                (type == STORE_SIZE)?_("size"):_("speed"), lc->str);
      return;
   }
   if (token != T_EOL) {
      scan_to_eol(lc);
   }
   set_bit(index, res_all->hdr.item_present);
   Dmsg0(900, "Leave store_unit\n");
}
Пример #18
0
/*
 * Prune File records from the database. For any Job which
 * is older than the retention period, we unconditionally delete
 * all File records for that Job.  This is simple enough that no
 * temporary tables are needed. We simply make an in memory list of
 * the JobIds meeting the prune conditions, then delete all File records
 * pointing to each of those JobIds.
 *
 * This routine assumes you want the pruning to be done. All checking
 *  must be done before calling this routine.
 */
int prune_files(UAContext *ua, CLIENT *client)
{
   struct del_ctx del;
   struct s_count_ctx cnt;
   POOL_MEM query(PM_MESSAGE);
   utime_t now, period;
   CLIENT_DBR cr;
   char ed1[50], ed2[50];

   db_lock(ua->db);
   memset(&cr, 0, sizeof(cr));
   memset(&del, 0, sizeof(del));
   bstrncpy(cr.Name, client->hdr.name, sizeof(cr.Name));
   if (!db_create_client_record(ua->jcr, ua->db, &cr)) {
      db_unlock(ua->db);
      return 0;
   }

   period = client->FileRetention;
   now = (utime_t)time(NULL);

   /* Select Jobs -- for counting */
   Mmsg(query, count_select_job, edit_int64(now - period, ed1), 
        edit_int64(cr.ClientId, ed2));
   Dmsg3(050, "select now=%u period=%u sql=%s\n", (uint32_t)now, 
               (uint32_t)period, query.c_str());
   cnt.count = 0;
   if (!db_sql_query(ua->db, query.c_str(), del_count_handler, (void *)&cnt)) {
      ua->error_msg("%s", db_strerror(ua->db));
      Dmsg0(050, "Count failed\n");
      goto bail_out;
   }

   if (cnt.count == 0) {
      if (ua->verbose) {
         ua->warning_msg(_("No Files found to prune.\n"));
      }
      goto bail_out;
   }

   if (cnt.count < MAX_DEL_LIST_LEN) {
      del.max_ids = cnt.count + 1;
   } else {
      del.max_ids = MAX_DEL_LIST_LEN;
   }
   del.tot_ids = 0;

   del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids);

   /* Now process same set but making a delete list */
   Mmsg(query, select_job, edit_int64(now - period, ed1), 
        edit_int64(cr.ClientId, ed2));
   db_sql_query(ua->db, query.c_str(), file_delete_handler, (void *)&del);

   purge_files_from_job_list(ua, del);

   edit_uint64_with_commas(del.num_del, ed1);
   ua->info_msg(_("Pruned Files from %s Jobs for client %s from catalog.\n"),
      ed1, client->name());

bail_out:
   db_unlock(ua->db);
   if (del.JobId) {
      free(del.JobId);
   }
   return 1;
}
Пример #19
0
/*
 * When a filemark is encountered while reading, the following happens.  If
 * there are data remaining in the buffer when the filemark is found, the
 * buffered data is returned.  The next read returns zero bytes.  The following
 * read returns data from the next file.  The end of recorded data is signaled
 * by returning zero bytes for two consecutive read calls.  The third read
 * returns an error.
 */
ssize_t vtape::d_read(int, void *buffer, size_t count)
{
   ASSERT(online);
   ASSERT(current_file >= 0);
   ssize_t nb;
   uint32_t s;

   Dmsg2(dbglevel*2, "read %i:%i\n", current_file, current_block);

   if (atEOT || atEOD) {
      errno = EIO;
      return -1;
   }

   if (atEOF) {
      if (!next_FM) {
         atEOD = true;
         atEOF = false;
         current_block=-1;
         return 0;
      }
      atEOF=false;
   }

   check_eof();

   atEOD = atBOT = false;

   /* reading size of data */
   nb = ::read(fd, &s, sizeof(uint32_t));
   if (nb <= 0) {
      atEOF = true;             /* TODO: check this */
      return 0;
   }

   if (s > count) {             /* not enough buffer to read block */
      Dmsg2(dbglevel, "Need more buffer to read next block %i > %i\n",s,count);
      lseek(fd, s, SEEK_CUR);
      errno = ENOMEM;
      return -1;
   }

   if (!s) {                    /* EOF */
      atEOF = true;
      if (read_fm(VT_SKIP_EOF)) {
         current_file++;
      }

      return 0;
   }

   /* reading data itself */
   nb = ::read(fd, buffer, s);
   if (nb != (ssize_t)s) { /* read error */
      errno=EIO;
      atEOT=true;
      current_block = -1;
      Dmsg0(dbglevel, "EOT during reading\n");
      return -1;
   }                    /* read ok */

   if (current_block >= 0) {
      current_block++;
   }

   return nb;
}
Пример #20
0
/*
 * Pruning Jobs is a bit more complicated than purging Files
 * because we delete Job records only if there is a more current
 * backup of the FileSet. Otherwise, we keep the Job record.
 * In other words, we never delete the only Job record that
 * contains a current backup of a FileSet. This prevents the
 * Volume from being recycled and destroying a current backup.
 *
 * For Verify Jobs, we do not delete the last InitCatalog.
 *
 * For Restore Jobs there are no restrictions.
 */
int prune_jobs(UAContext *ua, CLIENT *client, int JobType)
{
   struct del_ctx del;
   POOL_MEM query(PM_MESSAGE);
   utime_t now, period;
   CLIENT_DBR cr;
   char ed1[50], ed2[50];

   db_lock(ua->db);
   memset(&cr, 0, sizeof(cr));
   memset(&del, 0, sizeof(del));

   bstrncpy(cr.Name, client->name(), sizeof(cr.Name));
   if (!db_create_client_record(ua->jcr, ua->db, &cr)) {
      db_unlock(ua->db);
      return 0;
   }

   period = client->JobRetention;
   now = (utime_t)time(NULL);

   /* Drop any previous temporary tables still there */
   drop_temp_tables(ua);

   /* Create temp tables and indicies */
   if (!create_temp_tables(ua)) {
      goto bail_out;
   }

   /*
    * Select all files that are older than the JobRetention period
    *  and stuff them into the "DeletionCandidates" table.
    */
   edit_int64(now - period, ed1);
   Mmsg(query, insert_delcand, (char)JobType, ed1, 
        edit_int64(cr.ClientId, ed2));
   if (!db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL)) {
      if (ua->verbose) {
         ua->error_msg("%s", db_strerror(ua->db));
      }
      Dmsg0(050, "insert delcand failed\n");
      goto bail_out;
   }

   del.max_ids = 100;
   del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids);
   del.PurgedFiles = (char *)malloc(del.max_ids);

   /* ed1 = JobTDate */
   edit_int64(cr.ClientId, ed2);
   switch (JobType) {
   case JT_BACKUP:
      Mmsg(query, select_backup_del, ed1, ed2);
      break;
   case JT_RESTORE:
      Mmsg(query, select_restore_del, ed1, ed2);
      break;
   case JT_VERIFY:
      Mmsg(query, select_verify_del, ed1, ed2);
      break;
   case JT_ADMIN:
      Mmsg(query, select_admin_del, ed1, ed2);
      break;
   case JT_COPY:
      Mmsg(query, select_copy_del, ed1, ed2);
      break;
   case JT_MIGRATE:
      Mmsg(query, select_migrate_del, ed1, ed2);
      break;
   }

   Dmsg1(150, "Query=%s\n", query.c_str());
   if (!db_sql_query(ua->db, query.c_str(), job_delete_handler, (void *)&del)) {
      ua->error_msg("%s", db_strerror(ua->db));
   }

   purge_job_list_from_catalog(ua, del);

   if (del.num_del > 0) {
      ua->info_msg(_("Pruned %d %s for client %s from catalog.\n"), del.num_del,
         del.num_del==1?_("Job"):_("Jobs"), client->name());
    } else if (ua->verbose) {
       ua->info_msg(_("No Jobs found to prune.\n"));
    }

bail_out:
   drop_temp_tables(ua);
   db_unlock(ua->db);
   if (del.JobId) {
      free(del.JobId);
   }
   if (del.PurgedFiles) {
      free(del.PurgedFiles);
   }
   return 1;
}
Пример #21
0
/*
 * Create a Job Control Record and link it into JCR chain
 * Returns newly allocated JCR
 * Note, since each daemon has a different JCR, he passes
 *  us the size.
 */
JCR *new_jcr(int size, JCR_free_HANDLER *daemon_free_jcr)
{
   JCR *jcr;
   MQUEUE_ITEM *item = NULL;
   struct sigaction sigtimer;
   int status;

   Dmsg0(dbglvl, "Enter new_jcr\n");
   status = pthread_once(&key_once, create_jcr_key);
   if (status != 0) {
      berrno be;
      Jmsg1(NULL, M_ABORT, 0, _("pthread_once failed. ERR=%s\n"), be.bstrerror(status));
   }
   jcr = (JCR *)malloc(size);
   memset(jcr, 0, size);
   /* Note for the director, this value is changed in jobq.c */
   jcr->my_thread_id = pthread_self();
   jcr->msg_queue = New(dlist(item, &item->link));
   if ((status = pthread_mutex_init(&jcr->msg_queue_mutex, NULL)) != 0) {
      berrno be;
      Jmsg(NULL, M_ABORT, 0, _("Could not init msg_queue mutex. ERR=%s\n"),
         be.bstrerror(status));
   }
   jcr->job_end_push.init(1, false);
   jcr->sched_time = time(NULL);
   jcr->initial_sched_time = jcr->sched_time;
   jcr->daemon_free_jcr = daemon_free_jcr;    /* plug daemon free routine */
   jcr->init_mutex();
   jcr->inc_use_count();
   jcr->VolumeName = get_pool_memory(PM_FNAME);
   jcr->VolumeName[0] = 0;
   jcr->errmsg = get_pool_memory(PM_MESSAGE);
   jcr->errmsg[0] = 0;
   jcr->comment = get_pool_memory(PM_FNAME);
   jcr->comment[0] = 0;
   /* Setup some dummy values */
   bstrncpy(jcr->Job, "*System*", sizeof(jcr->Job));
   jcr->JobId = 0;
   jcr->setJobType(JT_SYSTEM);           /* internal job until defined */
   jcr->setJobLevel(L_NONE);
   jcr->setJobStatus(JS_Created);        /* ready to run */
   sigtimer.sa_flags = 0;
   sigtimer.sa_handler = timeout_handler;
   sigfillset(&sigtimer.sa_mask);
   sigaction(TIMEOUT_SIGNAL, &sigtimer, NULL);

   /*
    * Locking jobs is a global lock that is needed
    * so that the Director can stop new jobs from being
    * added to the jcr chain while it processes a new
    * conf file and does the job_end_push().
    */
   lock_jobs();
   lock_jcr_chain();
   if (!jcrs) {
      jcrs = New(dlist(jcr, &jcr->link));
   }
   jcrs->append(jcr);
   unlock_jcr_chain();
   unlock_jobs();

   return jcr;
}
Пример #22
0
/*
 * This function returns a truth value depending on the state of soft quotas.
 * The function compares the total jobbytes against the soft quota.
 *
 * If the quotas are not strict (the default) it checks the jobbytes value against
 * the quota limit previously when running in burst mode during the grace period.
 *
 * It checks if we have exceeded our grace time.
 *
 * If the value is true, the quota is reached and termination of the job should occur.
 *
 * Returns: true on reaching soft quota
 *          false on not reaching soft quota.
 */
bool quota_check_softquotas(JCR *jcr)
{
   bool retval = false;
   uint64_t now = (uint64_t)time(NULL);

   /*
    * Do not check if the softquota is not set
    */
   if (jcr->res.client->SoftQuota == 0) {
      goto bail_out;
   }

   Dmsg1(dbglvl, "Checking soft quotas for JobId %d\n", jcr->JobId);
   if (!jcr->HasQuota) {
      if (jcr->res.client->QuotaIncludeFailedJobs) {
         if (!db_get_quota_jobbytes(jcr, jcr->db, &jcr->jr,
                                    jcr->res.client->JobRetention)) {
            Jmsg(jcr, M_WARNING, 0, _("Error getting Quota value: ERR=%s"),
                 db_strerror(jcr->db));
            goto bail_out;
         }
         Dmsg0(dbglvl, "Quota Includes Failed Jobs\n");
      } else {
         if (!db_get_quota_jobbytes_nofailed(jcr, jcr->db, &jcr->jr,
                                             jcr->res.client->JobRetention)) {
            Jmsg(jcr, M_WARNING, 0, _("Error getting Quota value: ERR=%s"),
                 db_strerror(jcr->db));
            goto bail_out;
         }
         Jmsg(jcr, M_INFO, 0, _("Quota does NOT include Failed Jobs\n"));
      }
      jcr->HasQuota = true;
   }

   Dmsg2(dbglvl, "Quota for %s is %llu\n", jcr->jr.Name, jcr->jr.JobSumTotalBytes);
   Dmsg2(dbglvl, "QuotaLimit for %s is %llu\n", jcr->jr.Name, jcr->res.client->QuotaLimit);
   Dmsg2(dbglvl, "HardQuota for %s is %llu\n", jcr->jr.Name, jcr->res.client->HardQuota);
   Dmsg2(dbglvl, "SoftQuota for %s is %llu\n", jcr->jr.Name, jcr->res.client->SoftQuota);
   Dmsg2(dbglvl, "SoftQuota Grace Period for %s is %d\n", jcr->jr.Name, jcr->res.client->SoftQuotaGracePeriod);
   Dmsg2(dbglvl, "SoftQuota Grace Time for %s is %d\n", jcr->jr.Name, jcr->res.client->GraceTime);

   if (jcr->jr.JobSumTotalBytes > jcr->res.client->SoftQuota) {
      /*
       * Only warn once about softquotas in the job
       * Check if gracetime has been set
       */
      if (jcr->res.client->GraceTime == 0 && jcr->res.client->SoftQuotaGracePeriod) {
         Dmsg1(dbglvl, "update_quota_gracetime: %d\n", now);
         if (!db_update_quota_gracetime(jcr, jcr->db, &jcr->jr)) {
            Jmsg(jcr, M_WARNING, 0, _("Error setting Quota gracetime: ERR=%s"),
                 db_strerror(jcr->db));
         } else {
             Jmsg(jcr, M_ERROR, 0, _("Softquota Exceeded, Grace Period starts now.\n"));
         }
         jcr->res.client->GraceTime = now;
         goto bail_out;
      } else if (jcr->res.client->SoftQuotaGracePeriod &&
                (now - (uint64_t)jcr->res.client->GraceTime) < (uint64_t)jcr->res.client->SoftQuotaGracePeriod) {
         Jmsg(jcr, M_ERROR, 0, _("Softquota Exceeded, will be enforced after Grace Period expires.\n"));
      } else if (jcr->res.client->SoftQuotaGracePeriod &&
                (now - (uint64_t)jcr->res.client->GraceTime) > (uint64_t)jcr->res.client->SoftQuotaGracePeriod) {
         /*
          * If gracetime has expired update else check more if not set softlimit yet then set and bail out.
          */
         if (jcr->res.client->QuotaLimit < 1) {
           if (!db_update_quota_softlimit(jcr, jcr->db, &jcr->jr)) {
               Jmsg(jcr, M_WARNING, 0, _("Error setting Quota Softlimit: ERR=%s"),
                    db_strerror(jcr->db));
           }
           Jmsg(jcr, M_WARNING, 0, _("Softquota Exceeded and Grace Period expired.\n"));
           Jmsg(jcr, M_INFO, 0, _("Setting Burst Quota to %d Bytes.\n"),
                jcr->jr.JobSumTotalBytes);
           jcr->res.client->QuotaLimit = jcr->jr.JobSumTotalBytes;
           retval = true;
           goto bail_out;
         } else {
            /*
             * If we use strict quotas enforce the pure soft quota limit.
             */
            if (jcr->res.client->StrictQuotas &&
                jcr->jr.JobSumTotalBytes > jcr->res.client->SoftQuota) {
               Dmsg0(dbglvl, "Softquota Exceeded, enforcing Strict Quota Limit.\n");
               retval = true;
               goto bail_out;
            } else if (!jcr->res.client->StrictQuotas &&
                       jcr->jr.JobSumTotalBytes >= jcr->res.client->QuotaLimit) {
               /*
                * If strict quotas turned off use the last known limit
                */
               Jmsg(jcr, M_WARNING, 0, _("Softquota Exceeded, enforcing Burst Quota Limit.\n"));
               retval = true;
               goto bail_out;
            }
         }
      }
   }

bail_out:
   return retval;
}
Пример #23
0
bool CONFIG::parse_config_file(const char *cf, void *caller_ctx, LEX_ERROR_HANDLER *scan_error,
                               LEX_WARNING_HANDLER *scan_warning, int32_t err_type)
{
   bool result = true;
   LEX *lc = NULL;
   int token, i, pass;
   int res_type = 0;
   enum parse_state state = p_none;
   RES_TABLE *res_table = NULL;
   RES_ITEM *items = NULL;
   RES_ITEM *item = NULL;
   int level = 0;

   /*
    * Make two passes. The first builds the name symbol table,
    * and the second picks up the items.
    */
   Dmsg0(900, "Enter parse_config()\n");
   for (pass = 1; pass <= 2; pass++) {
      Dmsg1(900, "parse_config pass %d\n", pass);
      if ((lc = lex_open_file(lc, cf, scan_error, scan_warning)) == NULL) {
         berrno be;

         /*
          * We must create a lex packet to print the error
          */
         lc = (LEX *)malloc(sizeof(LEX));
         memset(lc, 0, sizeof(LEX));

         if (scan_error) {
            lc->scan_error = scan_error;
         } else {
            lex_set_default_error_handler(lc);
         }

         if (scan_warning) {
            lc->scan_warning = scan_warning;
         } else {
            lex_set_default_warning_handler(lc);
         }

         lex_set_error_handler_error_type(lc, err_type) ;
         scan_err2(lc, _("Cannot open config file \"%s\": %s\n"),
            cf, be.bstrerror());
         free(lc);

         return 0;
      }
      lex_set_error_handler_error_type(lc, err_type);
      lc->error_counter = 0;
      lc->caller_ctx = caller_ctx;

      while ((token=lex_get_token(lc, T_ALL)) != T_EOF) {
         Dmsg3(900, "parse state=%d pass=%d got token=%s\n", state, pass,
               lex_tok_to_str(token));
         switch (state) {
         case p_none:
            if (token == T_EOL) {
               break;
            } else if (token == T_UTF8_BOM) {
               /*
                * We can assume the file is UTF-8 as we have seen a UTF-8 BOM
                */
               break;
            } else if (token == T_UTF16_BOM) {
               scan_err0(lc, _("Currently we cannot handle UTF-16 source files. "
                               "Please convert the conf file to UTF-8\n"));
               goto bail_out;
            } else if (token != T_IDENTIFIER) {
               scan_err1(lc, _("Expected a Resource name identifier, got: %s"), lc->str);
               goto bail_out;
            }
            res_table = get_resource_table(lc->str);
            if(res_table && res_table->items) {
               items = res_table->items;
               state = p_resource;
               res_type = res_table->rcode;
               init_resource(res_type, items, pass);
            }
            if (state == p_none) {
               scan_err1(lc, _("expected resource name, got: %s"), lc->str);
               goto bail_out;
            }
            break;
         case p_resource:
            switch (token) {
            case T_BOB:
               level++;
               break;
            case T_IDENTIFIER:
               if (level != 1) {
                  scan_err1(lc, _("not in resource definition: %s"), lc->str);
                  goto bail_out;
               }
               i = get_resource_item_index(items, lc->str);
               if (i>=0) {
                  item = &items[i];
                  /*
                   * If the CFG_ITEM_NO_EQUALS flag is set we do NOT
                   *   scan for = after the keyword
                   */
                  if (!(item->flags & CFG_ITEM_NO_EQUALS)) {
                     token = lex_get_token(lc, T_SKIP_EOL);
                     Dmsg1 (900, "in T_IDENT got token=%s\n", lex_tok_to_str(token));
                     if (token != T_EQUALS) {
                        scan_err1(lc, _("expected an equals, got: %s"), lc->str);
                        goto bail_out;
                     }
                  }

                  /*
                   * See if we are processing a deprecated keyword if so warn the user about it.
                   */
                  if (item->flags & CFG_ITEM_DEPRECATED) {
                     scan_warn2(lc, _("using deprecated keyword %s on line %d"), item->name, lc->line_no);
                     /*
                      * As we only want to warn we continue parsing the config. So no goto bail_out here.
                      */
                  }

                  Dmsg1(800, "calling handler for %s\n", item->name);

                  /*
                   * Call item handler
                   */
                  if (!store_resource(item->type, lc, item, i, pass)) {
                     /*
                      * None of the generic types fired if there is a registered callback call that now.
                      */
                     if (m_store_res) {
                        m_store_res(lc, item, i, pass);
                     }
                  }
               } else {
                  Dmsg2(900, "level=%d id=%s\n", level, lc->str);
                  Dmsg1(900, "Keyword = %s\n", lc->str);
                  scan_err1(lc, _("Keyword \"%s\" not permitted in this resource.\n"
                                  "Perhaps you left the trailing brace off of the previous resource."), lc->str);
                  goto bail_out;
               }
               break;

            case T_EOB:
               level--;
               state = p_none;
               Dmsg0(900, "T_EOB => define new resource\n");
               if (((URES *)m_res_all)->hdr.name == NULL) {
                  scan_err0(lc, _("Name not specified for resource"));
                  goto bail_out;
               }
               /* save resource */
               if (!save_resource(res_type, items, pass)) {
                  scan_err0(lc, _("save_resource failed"));
                  goto bail_out;
               };
               break;

            case T_EOL:
               break;

            default:
               scan_err2(lc, _("unexpected token %d %s in resource definition"),
                  token, lex_tok_to_str(token));
               goto bail_out;
            }
            break;
         default:
            scan_err1(lc, _("Unknown parser state %d\n"), state);
            goto bail_out;
         }
      }
      if (state != p_none) {
         scan_err0(lc, _("End of conf file reached with unclosed resource."));
         goto bail_out;
      }
      if (debug_level >= 900 && pass == 2) {
         int i;
         for (i = m_r_first; i <= m_r_last; i++) {
            dump_resource(i, m_res_head[i-m_r_first], prtmsg, NULL, false);
         }
      }

      if (lc->error_counter > 0) {
         result = false;
      }

      lc = lex_close_file(lc);
   }
   Dmsg0(900, "Leave parse_config_file()\n");

   return result;

bail_out:
   if (lc) {
      lc = lex_close_file(lc);
   }

   return false;
}
Пример #24
0
/*
 * Create the file, or the directory
 *
 * fname is the original filename
 * ofile is the output filename (may be in a different directory)
 *
 * Returns:  CF_SKIP     if file should be skipped
 *           CF_ERROR    on error
 *           CF_EXTRACT  file created and data to restore
 *           CF_CREATED  file created no data to restore
 *
 * Note, we create the file here, except for special files,
 * we do not set the attributes because we want to first
 * write the file, then when the writing is done, set the
 * attributes.
 *
 * So, we return with the file descriptor open for normal files.
 */
int create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
{
   mode_t new_mode, parent_mode;
   int flags;
   uid_t uid;
   gid_t gid;
   int pnl;
   bool exists = false;
   struct stat mstatp;
#ifndef HAVE_WIN32
   bool isOnRoot;
#endif

   bfd->reparse_point = false;
   if (is_win32_stream(attr->data_stream)) {
      set_win32_backup(bfd);
   } else {
      set_portable_backup(bfd);
   }

   new_mode = attr->statp.st_mode;
   Dmsg3(200, "type=%d newmode=%x file=%s\n", attr->type, new_mode, attr->ofname);
   parent_mode = S_IWUSR | S_IXUSR | new_mode;
   gid = attr->statp.st_gid;
   uid = attr->statp.st_uid;

#ifdef HAVE_WIN32
   if (!bfd->use_backup_api) {
      /*
       * Eliminate invalid windows filename characters from foreign filenames
       */
      char *ch = (char *)attr->ofname;
      if (ch[0] != 0 && ch[1] != 0) {
         ch += 2;
         while (*ch) {
            switch (*ch) {
            case ':':
            case '<':
            case '>':
            case '*':
            case '?':
            case '|':
               *ch = '_';
                break;
            }
            ch++;
         }
      }
   }
#endif

   Dmsg2(400, "Replace=%c %d\n", (char)replace, replace);
   if (lstat(attr->ofname, &mstatp) == 0) {
      exists = true;
      switch (replace) {
      case REPLACE_IFNEWER:
         if (attr->statp.st_mtime <= mstatp.st_mtime) {
            Qmsg(jcr, M_INFO, 0, _("File skipped. Not newer: %s\n"), attr->ofname);
            return CF_SKIP;
         }
         break;
      case REPLACE_IFOLDER:
         if (attr->statp.st_mtime >= mstatp.st_mtime) {
            Qmsg(jcr, M_INFO, 0, _("File skipped. Not older: %s\n"), attr->ofname);
            return CF_SKIP;
         }
         break;
      case REPLACE_NEVER:
         /*
          * Set attributes if we created this directory
          */
         if (attr->type == FT_DIREND && path_list_lookup(jcr->path_list, attr->ofname)) {
            break;
         }
         Qmsg(jcr, M_INFO, 0, _("File skipped. Already exists: %s\n"), attr->ofname);
         return CF_SKIP;
      case REPLACE_ALWAYS:
         break;
      }
   }

   switch (attr->type) {
   case FT_RAW:                       /* Raw device to be written */
   case FT_FIFO:                      /* FIFO to be written to */
   case FT_LNKSAVED:                  /* Hard linked, file already saved */
   case FT_LNK:
   case FT_SPEC:                      /* Fifo, ... to be backed up */
   case FT_REGE:                      /* Empty file */
   case FT_REG:                       /* Regular file */
      /*
       * Note, we do not delete FT_RAW because these are device files
       * or FIFOs that should already exist. If we blow it away,
       * we may blow away a FIFO that is being used to read the
       * restore data, or we may blow away a partition definition.
       */
      if (exists && attr->type != FT_RAW && attr->type != FT_FIFO) {
         /* Get rid of old copy */
         Dmsg1(400, "unlink %s\n", attr->ofname);
         if (secure_erase(jcr, attr->ofname) == -1) {
            berrno be;

            Qmsg(jcr, M_ERROR, 0, _("File %s already exists and could not be replaced. ERR=%s.\n"),
                 attr->ofname, be.bstrerror());
            /* Continue despite error */
         }
      }

      /*
       * Here we do some preliminary work for all the above
       *   types to create the path to the file if it does
       *   not already exist.  Below, we will split to
       *   do the file type specific work
       */
      pnl = separate_path_and_file(jcr, attr->fname, attr->ofname);
      if (pnl < 0) {
         return CF_ERROR;
      }

      /*
       * If path length is <= 0 we are making a file in the root
       *  directory. Assume that the directory already exists.
       */
      if (pnl > 0) {
         char savechr;
         savechr = attr->ofname[pnl];
         attr->ofname[pnl] = 0;                 /* terminate path */

         if (!path_already_seen(jcr, attr->ofname, pnl)) {
            Dmsg1(400, "Make path %s\n", attr->ofname);
            /*
             * If we need to make the directory, ensure that it is with
             * execute bit set (i.e. parent_mode), and preserve what already
             * exists. Normally, this should do nothing.
             */
            if (!makepath(attr, attr->ofname, parent_mode, parent_mode, uid, gid, 1)) {
               Dmsg1(10, "Could not make path. %s\n", attr->ofname);
               attr->ofname[pnl] = savechr;     /* restore full name */
               return CF_ERROR;
            }
         }
         attr->ofname[pnl] = savechr;           /* restore full name */
      }

      /*
       * Now we do the specific work for each file type
       */
      switch(attr->type) {
      case FT_REGE:
      case FT_REG:
         Dmsg1(100, "Create=%s\n", attr->ofname);
         flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY; /*  O_NOFOLLOW; */
         if (IS_CTG(attr->statp.st_mode)) {
            flags |= O_CTG;              /* set contiguous bit if needed */
         }

         if (is_bopen(bfd)) {
            Qmsg1(jcr, M_ERROR, 0, _("bpkt already open fid=%d\n"), bfd->fid);
            bclose(bfd);
         }

         if (bopen(bfd, attr->ofname, flags, 0, attr->statp.st_rdev) < 0) {
            berrno be;

            be.set_errno(bfd->berrno);
            Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            Dmsg2(100,"Could not create %s: ERR=%s\n", attr->ofname, be.bstrerror());

            return CF_ERROR;
         }

         return CF_EXTRACT;

#ifndef HAVE_WIN32 /* None of these exist in MS Windows */
      case FT_RAW:                    /* Bareos raw device e.g. /dev/sda1 */
      case FT_FIFO:                   /* Bareos fifo to save data */
      case FT_SPEC:
         flags = O_WRONLY | O_BINARY;

         isOnRoot = bstrcmp(attr->fname, attr->ofname) ? 1 : 0;
         if (S_ISFIFO(attr->statp.st_mode)) {
            Dmsg1(400, "Restore fifo: %s\n", attr->ofname);
            if (mkfifo(attr->ofname, attr->statp.st_mode) != 0 && errno != EEXIST) {
               berrno be;
               Qmsg2(jcr, M_ERROR, 0, _("Cannot make fifo %s: ERR=%s\n"),
                     attr->ofname, be.bstrerror());
               return CF_ERROR;
            }
         } else if (S_ISSOCK(attr->statp.st_mode)) {
             Dmsg1(200, "Skipping restore of socket: %s\n", attr->ofname);
#ifdef S_IFDOOR /* Solaris high speed RPC mechanism */
         } else if (S_ISDOOR(attr->statp.st_mode)) {
             Dmsg1(200, "Skipping restore of door file: %s\n", attr->ofname);
#endif
#ifdef S_IFPORT /* Solaris event port for handling AIO */
         } else if (S_ISPORT(attr->statp.st_mode)) {
             Dmsg1(200, "Skipping restore of event port file: %s\n", attr->ofname);
#endif
         } else if ((S_ISBLK(attr->statp.st_mode) || S_ISCHR(attr->statp.st_mode)) && !exists && isOnRoot) {
             /*
              * Fatal: Restoring a device on root-file system, but device node does not exist.
              * Should not create a dump file.
              */
             Qmsg1(jcr, M_ERROR, 0, _("Device restore on root failed, device %s missing.\n"), attr->fname);
             return CF_ERROR;
         } else if (S_ISBLK(attr->statp.st_mode) || S_ISCHR(attr->statp.st_mode)) {
             Dmsg1(400, "Restoring a device as a file: %s\n", attr->ofname);
             flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
         } else {
            Dmsg1(400, "Restore node: %s\n", attr->ofname);
            if (mknod(attr->ofname, attr->statp.st_mode, attr->statp.st_rdev) != 0 && errno != EEXIST) {
               berrno be;
               Qmsg2(jcr, M_ERROR, 0, _("Cannot make node %s: ERR=%s\n"),
                     attr->ofname, be.bstrerror());
               return CF_ERROR;
            }
         }

         /*
          * Here we are going to attempt to restore to a FIFO, which
          * means that the FIFO must already exist, AND there must
          * be some process already attempting to read from the
          * FIFO, so we open it write-only.
          */
         if (attr->type == FT_RAW || attr->type == FT_FIFO) {
            btimer_t *tid;
            Dmsg1(400, "FT_RAW|FT_FIFO %s\n", attr->ofname);
            /*
             * Timeout open() in 60 seconds
             */
            if (attr->type == FT_FIFO) {
               Dmsg0(400, "Set FIFO timer\n");
               tid = start_thread_timer(jcr, pthread_self(), 60);
            } else {
               tid = NULL;
            }
            if (is_bopen(bfd)) {
               Qmsg1(jcr, M_ERROR, 0, _("bpkt already open fid=%d\n"), bfd->fid);
            }
            Dmsg2(400, "open %s flags=0x%x\n", attr->ofname, flags);
            if ((bopen(bfd, attr->ofname, flags, 0, 0)) < 0) {
               berrno be;
               be.set_errno(bfd->berrno);
               Qmsg2(jcr, M_ERROR, 0, _("Could not open %s: ERR=%s\n"),
                     attr->ofname, be.bstrerror());
               Dmsg2(400, "Could not open %s: ERR=%s\n", attr->ofname, be.bstrerror());
               stop_thread_timer(tid);
               return CF_ERROR;
            }
            stop_thread_timer(tid);
            return CF_EXTRACT;
         }
         Dmsg1(400, "FT_SPEC %s\n", attr->ofname);
         return CF_CREATED;

      case FT_LNKSAVED:                  /* Hard linked, file already saved */
         Dmsg2(130, "Hard link %s => %s\n", attr->ofname, attr->olname);
         if (link(attr->olname, attr->ofname) != 0) {
            berrno be;
#ifdef HAVE_CHFLAGS
            struct stat s;

            /*
             * If using BSD user flags, maybe has a file flag preventing this.
             * So attempt to disable, retry link, and reset flags.
             * Note that BSD securelevel may prevent disabling flag.
             */
            if (stat(attr->olname, &s) == 0 && s.st_flags != 0) {
               if (chflags(attr->olname, 0) == 0) {
                  if (link(attr->olname, attr->ofname) != 0) {
                     /*
                      * Restore original file flags even when linking failed
                      */
                     if (chflags(attr->olname, s.st_flags) < 0) {
                        Qmsg2(jcr, M_ERROR, 0, _("Could not restore file flags for file %s: ERR=%s\n"),
                              attr->olname, be.bstrerror());
                     }
#endif /* HAVE_CHFLAGS */
            Qmsg3(jcr, M_ERROR, 0, _("Could not hard link %s -> %s: ERR=%s\n"),
                  attr->ofname, attr->olname, be.bstrerror());
            Dmsg3(200, "Could not hard link %s -> %s: ERR=%s\n",
                  attr->ofname, attr->olname, be.bstrerror());
            return CF_ERROR;
#ifdef HAVE_CHFLAGS
                  }
                  /*
                   * Finally restore original file flags
                   */
                  if (chflags(attr->olname, s.st_flags) < 0) {
                     Qmsg2(jcr, M_ERROR, 0, _("Could not restore file flags for file %s: ERR=%s\n"),
                            attr->olname, be.bstrerror());
                  }
               } else {
                 Qmsg2(jcr, M_ERROR, 0, _("Could not reset file flags for file %s: ERR=%s\n"),
                       attr->olname, be.bstrerror());
               }
            } else {
              Qmsg3(jcr, M_ERROR, 0, _("Could not hard link %s -> %s: ERR=%s\n"),
                    attr->ofname, attr->olname, be.bstrerror());
              return CF_ERROR;
            }
#endif /* HAVE_CHFLAGS */

         }
         return CF_CREATED;

#endif /* HAVE_WIN32 */
#ifdef HAVE_WIN32
      case FT_LNK:
         /*
          * Handle Windows Symlink-Like Reparse Points
          * - Directory Symlinks
          * - File Symlinks
          * - Volume Mount Points
          * - Junctions
          */
         Dmsg2(130, "FT_LNK should restore: %s -> %s\n", attr->ofname, attr->olname);
         if (attr->statp.st_rdev & FILE_ATTRIBUTE_VOLUME_MOUNT_POINT) {
            /*
             * We do not restore volume mount points
             */
            Dmsg0(130, "Skipping Volume Mount Point\n");
            return CF_SKIP;
         }
         if (win32_symlink(attr->olname, attr->ofname, attr->statp.st_rdev) != 0 && errno != EEXIST) {
            berrno be;
            Qmsg3(jcr, M_ERROR, 0, _("Could not symlink %s -> %s: ERR=%s\n"),
                  attr->ofname, attr->olname, be.bstrerror());
            return CF_ERROR;
         }
         return CF_CREATED;
#else
      case FT_LNK:
         /*
          * Unix/Linux symlink handling
          */
         Dmsg2(130, "FT_LNK should restore: %s -> %s\n", attr->ofname, attr->olname);
         if (symlink(attr->olname, attr->ofname) != 0 && errno != EEXIST) {
            berrno be;
            Qmsg3(jcr, M_ERROR, 0, _("Could not symlink %s -> %s: ERR=%s\n"),
                  attr->ofname, attr->olname, be.bstrerror());
            return CF_ERROR;
         }
         return CF_CREATED;
#endif
      } /* End inner switch */

   case FT_REPARSE:
   case FT_JUNCTION:
      bfd->reparse_point = true;
      /*
       * Fall through wanted
       */
   case FT_DIRBEGIN:
   case FT_DIREND:
      Dmsg2(200, "Make dir mode=%o dir=%s\n", new_mode, attr->ofname);
      if (!makepath(attr, attr->ofname, new_mode, parent_mode, uid, gid, 0)) {
         return CF_ERROR;
      }
      /*
       * If we are using the Win32 Backup API, we open the directory so
       * that the security info will be read and saved.
       */
      if (!is_portable_backup(bfd)) {
         if (is_bopen(bfd)) {
            Qmsg1(jcr, M_ERROR, 0, _("bpkt already open fid=%d\n"), bfd->fid);
         }
         if (bopen(bfd, attr->ofname, O_WRONLY | O_BINARY, 0, attr->statp.st_rdev) < 0) {
            berrno be;
            be.set_errno(bfd->berrno);
#ifdef HAVE_WIN32
            /*
             * Check for trying to create a drive, if so, skip
             */
            if (attr->ofname[1] == ':' &&
                IsPathSeparator(attr->ofname[2]) &&
                attr->ofname[3] == '\0') {
               return CF_SKIP;
            }
#endif
            Qmsg2(jcr, M_ERROR, 0, _("Could not open %s: ERR=%s\n"),
                  attr->ofname, be.bstrerror());
            return CF_ERROR;
         }
         return CF_EXTRACT;
      } else {
         return CF_CREATED;
      }

   case FT_DELETED:
      Qmsg2(jcr, M_INFO, 0, _("Original file %s have been deleted: type=%d\n"), attr->fname, attr->type);
      break;
   /*
    * The following should not occur
    */
   case FT_NOACCESS:
   case FT_NOFOLLOW:
   case FT_NOSTAT:
   case FT_DIRNOCHG:
   case FT_NOCHG:
   case FT_ISARCH:
   case FT_NORECURSE:
   case FT_NOFSCHG:
   case FT_NOOPEN:
      Qmsg2(jcr, M_ERROR, 0, _("Original file %s not saved: type=%d\n"), attr->fname, attr->type);
      break;
   default:
      Qmsg2(jcr, M_ERROR, 0, _("Unknown file type %d; not restored: %s\n"), attr->type, attr->fname);
      break;
   }
   return CF_ERROR;
}
Пример #25
0
/*
 * This routine is called only during a Verify
 */
void get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId)
{
   BSOCK   *fd;
   int n, len;
   FILE_DBR fdbr;
   struct stat statf;                 /* file stat */
   struct stat statc;                 /* catalog stat */
   char buf[MAXSTRING];
   POOLMEM *fname = get_pool_memory(PM_MESSAGE);
   int do_Digest = CRYPTO_DIGEST_NONE;
   int32_t file_index = 0;

   memset(&fdbr, 0, sizeof(FILE_DBR));
   fd = jcr->file_bsock;
   fdbr.JobId = JobId;
   jcr->FileIndex = 0;

   Dmsg0(20, "bdird: waiting to receive file attributes\n");
   /*
    * Get Attributes and Signature from File daemon
    * We expect:
    *   FileIndex
    *   Stream
    *   Options or Digest (MD5/SHA1)
    *   Filename
    *   Attributes
    *   Link name  ???
    */
   while ((n=bget_dirmsg(fd)) >= 0 && !job_canceled(jcr)) {
      int stream;
      char *attr, *p, *fn;
      char Opts_Digest[MAXSTRING];        /* Verify Opts or MD5/SHA1 digest */

      if (job_canceled(jcr)) {
         return;
      }
      fname = check_pool_memory_size(fname, fd->msglen);
      jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
      Dmsg1(200, "Atts+Digest=%s\n", fd->msg);
      if ((len = sscanf(fd->msg, "%ld %d %100s", &file_index, &stream,
            fname)) != 3) {
         Jmsg3(jcr, M_FATAL, 0, _("bird<filed: bad attributes, expected 3 fields got %d\n"
" mslen=%d msg=%s\n"), len, fd->msglen, fd->msg);
         return;
      }
      /*
       * We read the Options or Signature into fname
       *  to prevent overrun, now copy it to proper location.
       */
      bstrncpy(Opts_Digest, fname, sizeof(Opts_Digest));
      p = fd->msg;
      skip_nonspaces(&p);             /* skip FileIndex */
      skip_spaces(&p);
      skip_nonspaces(&p);             /* skip Stream */
      skip_spaces(&p);
      skip_nonspaces(&p);             /* skip Opts_Digest */
      p++;                            /* skip space */
      fn = fname;
      while (*p != 0) {
         *fn++ = *p++;                /* copy filename */
      }
      *fn = *p++;                     /* term filename and point to attribs */
      attr = p;
      /*
       * Got attributes stream, decode it
       */
      if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
         int32_t LinkFIf, LinkFIc;
         Dmsg2(400, "file_index=%d attr=%s\n", file_index, attr);
         jcr->JobFiles++;
         jcr->FileIndex = file_index;    /* remember attribute file_index */
         jcr->previous_jr.FileIndex = file_index;
         decode_stat(attr, &statf, &LinkFIf);  /* decode file stat packet */
         do_Digest = CRYPTO_DIGEST_NONE;
         jcr->fn_printed = false;
         pm_strcpy(jcr->fname, fname);  /* move filename into JCR */

         Dmsg2(040, "dird<filed: stream=%d %s\n", stream, jcr->fname);
         Dmsg1(020, "dird<filed: attr=%s\n", attr);

         /*
          * Find equivalent record in the database
          */
         fdbr.FileId = 0;
         if (!db_get_file_attributes_record(jcr, jcr->db, jcr->fname,
              &jcr->previous_jr, &fdbr)) {
            Jmsg(jcr, M_INFO, 0, _("New file: %s\n"), jcr->fname);
            Dmsg1(020, _("File not in catalog: %s\n"), jcr->fname);
            set_jcr_job_status(jcr, JS_Differences);
            continue;
         } else {
            /*
             * mark file record as visited by stuffing the
             * current JobId, which is unique, into the MarkId field.
             */
            db_mark_file_record(jcr, jcr->db, fdbr.FileId, jcr->JobId);
         }

         Dmsg3(400, "Found %s in catalog. inx=%d Opts=%s\n", jcr->fname,
            file_index, Opts_Digest);
         decode_stat(fdbr.LStat, &statc, &LinkFIc); /* decode catalog stat */
         /*
          * Loop over options supplied by user and verify the
          * fields he requests.
          */
         for (p=Opts_Digest; *p; p++) {
            char ed1[30], ed2[30];
            switch (*p) {
            case 'i':                /* compare INODEs */
               if (statc.st_ino != statf.st_ino) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_ino   differ. Cat: %s File: %s\n"),
                     edit_uint64((uint64_t)statc.st_ino, ed1),
                     edit_uint64((uint64_t)statf.st_ino, ed2));
                  set_jcr_job_status(jcr, JS_Differences);
               }
               break;
            case 'p':                /* permissions bits */
               if (statc.st_mode != statf.st_mode) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_mode  differ. Cat: %x File: %x\n"),
                     (uint32_t)statc.st_mode, (uint32_t)statf.st_mode);
                  set_jcr_job_status(jcr, JS_Differences);
               }
               break;
            case 'n':                /* number of links */
               if (statc.st_nlink != statf.st_nlink) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_nlink differ. Cat: %d File: %d\n"),
                     (uint32_t)statc.st_nlink, (uint32_t)statf.st_nlink);
                  set_jcr_job_status(jcr, JS_Differences);
               }
               break;
            case 'u':                /* user id */
               if (statc.st_uid != statf.st_uid) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_uid   differ. Cat: %u File: %u\n"),
                     (uint32_t)statc.st_uid, (uint32_t)statf.st_uid);
                  set_jcr_job_status(jcr, JS_Differences);
               }
               break;
            case 'g':                /* group id */
               if (statc.st_gid != statf.st_gid) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_gid   differ. Cat: %u File: %u\n"),
                     (uint32_t)statc.st_gid, (uint32_t)statf.st_gid);
                  set_jcr_job_status(jcr, JS_Differences);
               }
               break;
            case 's':                /* size */
               if (statc.st_size != statf.st_size) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_size  differ. Cat: %s File: %s\n"),
                     edit_uint64((uint64_t)statc.st_size, ed1),
                     edit_uint64((uint64_t)statf.st_size, ed2));
                  set_jcr_job_status(jcr, JS_Differences);
               }
               break;
            case 'a':                /* access time */
               if (statc.st_atime != statf.st_atime) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_atime differs\n"));
                  set_jcr_job_status(jcr, JS_Differences);
               }
               break;
            case 'm':
               if (statc.st_mtime != statf.st_mtime) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_mtime differs\n"));
                  set_jcr_job_status(jcr, JS_Differences);
               }
               break;
            case 'c':                /* ctime */
               if (statc.st_ctime != statf.st_ctime) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_ctime differs\n"));
                  set_jcr_job_status(jcr, JS_Differences);
               }
               break;
            case 'd':                /* file size decrease */
               if (statc.st_size > statf.st_size) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_size  decrease. Cat: %s File: %s\n"),
                     edit_uint64((uint64_t)statc.st_size, ed1),
                     edit_uint64((uint64_t)statf.st_size, ed2));
                  set_jcr_job_status(jcr, JS_Differences);
               }
               break;
            case '5':                /* compare MD5 */
               Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname);
               do_Digest = CRYPTO_DIGEST_MD5;
               break;
            case '1':                 /* compare SHA1 */
               do_Digest = CRYPTO_DIGEST_SHA1;
               break;
            case ':':
            case 'V':
            default:
               break;
            }
         }
      /*
       * Got Digest Signature from Storage daemon
       *  It came across in the Opts_Digest field.
       */
      } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
         Dmsg2(400, "stream=Digest inx=%d Digest=%s\n", file_index, Opts_Digest);
         /*
          * When ever we get a digest it MUST have been
          * preceded by an attributes record, which sets attr_file_index
          */
         if (jcr->FileIndex != (uint32_t)file_index) {
            Jmsg2(jcr, M_FATAL, 0, _("MD5/SHA1 index %d not same as attributes %d\n"),
               file_index, jcr->FileIndex);
            return;
         }
         if (do_Digest != CRYPTO_DIGEST_NONE) {
            db_escape_string(jcr, jcr->db, buf, Opts_Digest, strlen(Opts_Digest));
            if (strcmp(buf, fdbr.Digest) != 0) {
               prt_fname(jcr);
               Jmsg(jcr, M_INFO, 0, _("      %s differs. File=%s Cat=%s\n"),
                    stream_to_ascii(stream), buf, fdbr.Digest);
               set_jcr_job_status(jcr, JS_Differences);
            }
            do_Digest = CRYPTO_DIGEST_NONE;
         }
      }
      jcr->JobFiles = file_index;
   }
   if (is_bnet_error(fd)) {
      berrno be;
      Jmsg2(jcr, M_FATAL, 0, _("bdird<filed: bad attributes from filed n=%d : %s\n"),
                        n, be.bstrerror());
      return;
   }

   /* Now find all the files that are missing -- i.e. all files in
    *  the database where the MarkId != current JobId
    */
   jcr->fn_printed = false;
   bsnprintf(buf, sizeof(buf),
      "SELECT Path.Path,Filename.Name FROM File,Path,Filename "
      "WHERE File.JobId=%d "
      "AND File.MarkId!=%d AND File.PathId=Path.PathId "
      "AND File.FilenameId=Filename.FilenameId",
         JobId, jcr->JobId);
   /* missing_handler is called for each file found */
   db_sql_query(jcr->db, buf, missing_handler, (void *)jcr);
   if (jcr->fn_printed) {
      set_jcr_job_status(jcr, JS_Differences);
   }
   free_pool_memory(fname);
}
Пример #26
0
/*
 * NB! This routine locks the device, but if committing will
 *     not unlock it. If not committing, it will be unlocked.
 */
static bool despool_data(DCR *dcr, bool commit)
{
   DEVICE *rdev;
   DCR *rdcr;
   bool ok = true;
   DEV_BLOCK *block;
   JCR *jcr = dcr->jcr;
   int stat;
   char ec1[50];

   Dmsg0(100, "Despooling data\n");
   /*
    * Commit means that the job is done, so we commit, otherwise, we
    *  are despooling because of user spool size max or some error  
    *  (e.g. filesystem full).
    */
   if (commit) {
      Jmsg(jcr, M_INFO, 0, _("Committing spooled data to Volume \"%s\". Despooling %s bytes ...\n"),
         jcr->dcr->VolumeName,
         edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
      set_jcr_job_status(jcr, JS_DataCommitting);
   } else {
      Jmsg(jcr, M_INFO, 0, _("Writing spooled data to Volume. Despooling %s bytes ...\n"),
         edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
      set_jcr_job_status(jcr, JS_DataDespooling);
   }
   set_jcr_job_status(jcr, JS_DataDespooling);
   dir_send_job_status(jcr);
   dcr->despool_wait = true;
   dcr->spooling = false;
   /*
    * We work with device blocked, but not locked so that
    *  other threads -- e.g. reservations can lock the device
    *  structure.
    */
   dcr->dblock(BST_DESPOOLING);
   dcr->despool_wait = false;
   dcr->despooling = true;

   /*
    * This is really quite kludgy and should be fixed some time.
    * We create a dev structure to read from the spool file
    * in rdev and rdcr.
    */
   rdev = (DEVICE *)malloc(sizeof(DEVICE));
   memset(rdev, 0, sizeof(DEVICE));
   rdev->dev_name = get_memory(strlen(spool_name)+1);
   bstrncpy(rdev->dev_name, spool_name, sizeof(rdev->dev_name));
   rdev->errmsg = get_pool_memory(PM_EMSG);
   *rdev->errmsg = 0;
   rdev->max_block_size = dcr->dev->max_block_size;
   rdev->min_block_size = dcr->dev->min_block_size;
   rdev->device = dcr->dev->device;
   rdcr = new_dcr(jcr, NULL, rdev);
   rdcr->spool_fd = dcr->spool_fd;
   block = dcr->block;                /* save block */
   dcr->block = rdcr->block;          /* make read and write block the same */

   Dmsg1(800, "read/write block size = %d\n", block->buf_len);
   lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */

#if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
   posix_fadvise(rdcr->spool_fd, 0, 0, POSIX_FADV_WILLNEED);
#endif

   /* Add run time, to get current wait time */
   int32_t despool_start = time(NULL) - jcr->run_time;

   set_new_file_parameters(dcr);

   for ( ; ok; ) {
      if (job_canceled(jcr)) {
         ok = false;
         break;
      }
      stat = read_block_from_spool_file(rdcr);
      if (stat == RB_EOT) {
         break;
      } else if (stat == RB_ERROR) {
         ok = false;
         break;
      }
      ok = write_block_to_device(dcr);
      if (!ok) {
         Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
               dcr->dev->print_name(), dcr->dev->bstrerror());
         Dmsg2(000, "Fatal append error on device %s: ERR=%s\n",
               dcr->dev->print_name(), dcr->dev->bstrerror());
      }
      Dmsg3(800, "Write block ok=%d FI=%d LI=%d\n", ok, block->FirstIndex, block->LastIndex);
   }

   if (!dir_create_jobmedia_record(dcr)) {
      Jmsg2(jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
         dcr->VolCatInfo.VolCatName, jcr->Job);
   }
   /* Set new file/block parameters for current dcr */
   set_new_file_parameters(dcr);

   /*
    * Subtracting run_time give us elapsed time - wait_time since 
    * we started despooling. Note, don't use time_t as it is 32 or 64
    * bits depending on the OS and doesn't edit with %d
    */
   int32_t despool_elapsed = time(NULL) - despool_start - jcr->run_time;

   if (despool_elapsed <= 0) {
      despool_elapsed = 1;
   }

   Jmsg(dcr->jcr, M_INFO, 0, _("Despooling elapsed time = %02d:%02d:%02d, Transfer rate = %s bytes/second\n"),
         despool_elapsed / 3600, despool_elapsed % 3600 / 60, despool_elapsed % 60,
         edit_uint64_with_suffix(jcr->dcr->job_spool_size / despool_elapsed, ec1));

   dcr->block = block;                /* reset block */

   lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
   if (ftruncate(rdcr->spool_fd, 0) != 0) {
      berrno be;
      Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
         be.bstrerror());
      /* Note, try continuing despite ftruncate problem */
   }

   P(mutex);
   if (spool_stats.data_size < dcr->job_spool_size) {
      spool_stats.data_size = 0;
   } else {
      spool_stats.data_size -= dcr->job_spool_size;
   }
   V(mutex);
   P(dcr->dev->spool_mutex);
   dcr->dev->spool_size -= dcr->job_spool_size;
   dcr->job_spool_size = 0;            /* zap size in input dcr */
   V(dcr->dev->spool_mutex);
   free_memory(rdev->dev_name);
   free_pool_memory(rdev->errmsg);
   /* Be careful to NULL the jcr and free rdev after free_dcr() */
   rdcr->jcr = NULL;
   rdcr->dev = NULL;
   free_dcr(rdcr);
   free(rdev);
   dcr->spooling = true;           /* turn on spooling again */
   dcr->despooling = false;

   /* 
    * We are done, so unblock the device, but if we have done a 
    *  commit, leave it locked so that the job cleanup does not
    *  need to wait to release the device (no re-acquire of the lock).
    */
   dcr->dlock();
   unblock_device(dcr->dev);
   /* If doing a commit, leave the device locked -- unlocked in release_device() */
   if (!commit) {
      dcr->dunlock();
   }
   set_jcr_job_status(jcr, JS_Running);
   dir_send_job_status(jcr);
   return ok;
}
Пример #27
0
/*
 * If s == NULL we are just waiting on FD for status changes.
 * If s != NULL we are asking the UPS to tell us the value of something.
 *
 * If s == NULL there is a much more fine-grained locking.
 */
int getline(char *s, int len, UPSINFO *ups)
{
   int i = 0;
   int ending = 0;
   char c;
   int retval;
   int wait;

   if (s != NULL)
      wait = TIMER_FAST;   /* 1 sec, expect fast response */
   else
      wait = ups->wait_time;

#ifdef HAVE_MINGW
   /* Set read() timeout since we have no select() support. */
   {
      COMMTIMEOUTS ct;
      HANDLE h = (HANDLE)_get_osfhandle(ups->fd);
      ct.ReadIntervalTimeout = MAXDWORD;
      ct.ReadTotalTimeoutMultiplier = MAXDWORD;
      ct.ReadTotalTimeoutConstant = wait * 1000;
      ct.WriteTotalTimeoutMultiplier = 0;
      ct.WriteTotalTimeoutConstant = 0;
      SetCommTimeouts(h, &ct);
   }
#endif

   while (!ending) {
#if !defined(HAVE_MINGW)
      fd_set rfds;
      struct timeval tv;

      FD_ZERO(&rfds);
      FD_SET(ups->fd, &rfds);
      tv.tv_sec = wait;
      tv.tv_usec = 0;

      errno = 0;
      retval = select((ups->fd) + 1, &rfds, NULL, NULL, &tv);

      switch (retval) {
      case 0:                     /* No chars available in TIMER seconds. */
         return FAILURE;
      case -1:
         if (errno == EINTR || errno == EAGAIN) {       /* assume SIGCHLD */
            continue;
         } else if (errno == EBADF) {
            return FAILURE;               /* We're probably shutting down */
         }
         Error_abort1("Select error on UPS FD. %s\n", strerror(errno));
         break;
      default:
         break;
      }
#endif

      do {
         retval = read(ups->fd, &c, 1);
      } while (retval == -1 && (errno == EAGAIN || errno == EINTR));
      if (retval == 0) {
         return FAILURE;
      }

      switch (c) {
         /*
          * Here we can be called in two ways:
          * 
          * s == NULL
          *     The shm lock is not held so we must hold it here.
          *
          * s != NULL
          *     We are called from a routine that have 
          *     already held the shm lock so no need to hold it
          *     another time. Simply update the UPS structure
          *     fields and the shm will be updated when
          *     write_unlock is called by the calling
          *     routine.
          *
          * If something changes on the UPS, a special character is
          * sent over the serial line but no \n\r sequence is sent:
          * only a single character. This way if s == NULL, if we
          * receive a character like this we must return immediately
          * and not wait for a string completion.
          */
      case UPS_ON_BATT:           /* UPS_ON_BATT = '!'   */
         if (s == NULL)
            write_lock(ups);
         ups->clear_online();
         Dmsg0(80, "Got UPS ON BATT.\n");
         if (s == NULL) {
            write_unlock(ups);
            ending = 1;
         }
         break;
      case UPS_REPLACE_BATTERY:   /* UPS_REPLACE_BATTERY = '#'   */
         if (s == NULL)
            write_lock(ups);
         ups->set_replacebatt();
         Dmsg0(80, "Got UPS REPLACE_BATT.\n");
         if (s == NULL) {
            write_unlock(ups);
            ending = 1;
         }
         break;
      case UPS_ON_LINE:           /* UPS_ON_LINE = '$'   */
         if (s == NULL)
            write_lock(ups);
         ups->set_online();
         Dmsg0(80, "Got UPS ON LINE.\n");
         if (s == NULL) {
            write_unlock(ups);
            ending = 1;
         }
         break;
      case BATT_LOW:              /* BATT_LOW    = '%'   */
         if (s == NULL)
            write_lock(ups);
         ups->set_battlow();
         Dmsg0(80, "Got UPS BATT_LOW.\n");
         if (s == NULL) {
            write_unlock(ups);
            ending = 1;
         }
         break;
      case BATT_OK:               /* BATT_OK     = '+'   */
         if (s == NULL)
            write_lock(ups);
         ups->clear_battlow();
         Dmsg0(80, "Got UPS BATT_OK.\n");
         if (s == NULL) {
            write_unlock(ups);
            ending = 1;
         }
         break;

      case UPS_EPROM_CHANGE:      /* UPS_EPROM_CHANGE = '|' */
      case UPS_TRAILOR:           /* UPS_TRAILOR = ':'      */
         break;

         /* NOTE: The UPS terminates what it sends to us
          * with a \r\n. Thus the line feed signals the
          * end of what we are to receive.
          */
      case UPS_LF:                /* UPS_LF      = '\n'  */
         if (s != NULL)
            ending = 1;            /* This what we waited for */
         break;
      case UPS_CR:                /* UPS_CR      = '\r'  */
         break;
      default:
         if (s != NULL) {
            if (i + 1 < len)
               s[i++] = c;
            else
               ending = 1;         /* no more room in buffer */
         }
         break;
      }
   }

   if (s != NULL) {
      s[i] = '\0';
   }
   return SUCCESS;
}
Пример #28
0
int main (int argc, char *argv[])
{
   int ch;
   bool ok;
   char *iVolumeName = NULL;
   char *oVolumeName = NULL;
   char *DirectorName = NULL;
   DIRRES *director = NULL;
   bool ignore_label_errors = false;
   DCR *in_dcr, *out_dcr;

   setlocale(LC_ALL, "");
   bindtextdomain("bareos", LOCALEDIR);
   textdomain("bareos");
   init_stack_dump();

   my_name_is(argc, argv, "bcopy");
   lmgr_init_thread();
   init_msg(NULL, NULL);

   while ((ch = getopt(argc, argv, "b:c:D:d:i:o:pvw:?")) != -1) {
      switch (ch) {
      case 'b':
         bsr = parse_bsr(NULL, optarg);
         break;

      case 'c':                    /* specify config file */
         if (configfile != NULL) {
            free(configfile);
         }
         configfile = bstrdup(optarg);
         break;

      case 'D':                    /* specify director name */
         if (DirectorName != NULL) {
            free(DirectorName);
         }
         DirectorName = bstrdup(optarg);
         break;

      case 'd':                    /* debug level */
         if (*optarg == 't') {
            dbg_timestamp = true;
         } else {
            debug_level = atoi(optarg);
            if (debug_level <= 0) {
               debug_level = 1;
            }
         }
         break;

      case 'i':                    /* input Volume name */
         iVolumeName = optarg;
         break;

      case 'o':                    /* output Volume name */
         oVolumeName = optarg;
         break;

      case 'p':
         ignore_label_errors = true;
         forge_on = true;
         break;

      case 'v':
         verbose++;
         break;

      case 'w':
         wd = optarg;
         break;

      case '?':
      default:
         usage();

      }
   }
   argc -= optind;
   argv += optind;

   if (argc != 2) {
      Pmsg0(0, _("Wrong number of arguments: \n"));
      usage();
   }

   OSDependentInit();

   working_directory = wd;

   if (configfile == NULL) {
      configfile = bstrdup(CONFIG_FILE);
   }

   my_config = new_config_parser();
   parse_sd_config(my_config, configfile, M_ERROR_TERM);

   if (DirectorName) {
      foreach_res(director, R_DIRECTOR) {
         if (bstrcmp(director->hdr.name, DirectorName)) {
            break;
         }
      }
      if (!director) {
         Emsg2(M_ERROR_TERM, 0, _("No Director resource named %s defined in %s. Cannot continue.\n"),
               DirectorName, configfile);
      }
   }

   load_sd_plugins(me->plugin_directory, me->plugin_names);

   read_crypto_cache(me->working_directory, "bareos-sd",
                     get_first_port_host_order(me->SDaddrs));

   /*
    * Setup and acquire input device for reading
    */
   Dmsg0(100, "About to setup input jcr\n");

   in_dcr = New(DCR);
   in_jcr = setup_jcr("bcopy", argv[0], bsr, director, in_dcr, iVolumeName, true); /* read device */
   if (!in_jcr) {
      exit(1);
   }

   in_jcr->ignore_label_errors = ignore_label_errors;

   in_dev = in_jcr->dcr->dev;
   if (!in_dev) {
      exit(1);
   }

   /*
    * Setup output device for writing
    */
   Dmsg0(100, "About to setup output jcr\n");

   out_dcr = New(DCR);
   out_jcr = setup_jcr("bcopy", argv[1], bsr, director, out_dcr, oVolumeName, false); /* write device */
   if (!out_jcr) {
      exit(1);
   }

   out_dev = out_jcr->dcr->dev;
   if (!out_dev) {
      exit(1);
   }

   Dmsg0(100, "About to acquire device for writing\n");

   /*
    * For we must now acquire the device for writing
    */
   out_dev->rLock(false);
   if (!out_dev->open(out_jcr->dcr, OPEN_READ_WRITE)) {
      Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg);
      out_dev->Unlock();
      exit(1);
   }
   out_dev->Unlock();
   if (!acquire_device_for_append(out_jcr->dcr)) {
      free_jcr(in_jcr);
      exit(1);
   }
   out_block = out_jcr->dcr->block;

   ok = read_records(in_jcr->dcr, record_cb, mount_next_read_volume);

   if (ok || out_dev->can_write()) {
      if (!out_jcr->dcr->write_block_to_device()) {
         Pmsg0(000, _("Write of last block failed.\n"));
      }
   }

   Pmsg2(000, _("%u Jobs copied. %u records copied.\n"), jobs, records);

   in_dev->term();
   out_dev->term();

   free_jcr(in_jcr);
   free_jcr(out_jcr);

   return 0;
}
Пример #29
0
/*
 * Write a Volume Label
 *  !!! Note, this is ONLY used for writing
 *            a fresh volume label.  Any data
 *            after the label will be destroyed,
 *            in fact, we write the label 5 times !!!!
 *
 *  This routine should be used only when labeling a blank tape.
 */
bool write_new_volume_label_to_dev(DCR *dcr, const char *VolName,
                                   const char *PoolName, bool relabel)
{
   JCR *jcr = dcr->jcr;
   DEVICE *dev = dcr->dev;
   DEV_BLOCK *block = dcr->block;

   Dmsg0(150, "write_volume_label()\n");
   if (*VolName == 0) {
      Pmsg0(0, "=== ERROR: write_new_volume_label_to_dev called with NULL VolName\n");
      goto bail_out;
   }

   if (relabel) {
      volume_unused(dcr);             /* mark current volume unused */
      /* Truncate device */
      if (!dev->truncate(dcr)) {
         goto bail_out;
      }
      if (!dev->is_tape()) {
         dev->close(dcr);             /* make sure file closed for rename */
      }
   }

   /* Set the new filename for open, ... */
   dev->setVolCatName(VolName);
   dcr->setVolCatName(VolName);
   Dmsg1(150, "New VolName=%s\n", VolName);
   if (!dev->open(dcr, OPEN_READ_WRITE)) {
      /* If device is not tape, attempt to create it */
      if (dev->is_tape() || !dev->open(dcr, CREATE_READ_WRITE)) {
         Jmsg3(jcr, M_WARNING, 0, _("Open device %s Volume \"%s\" failed: ERR=%s\n"),
               dev->print_name(), dcr->VolumeName, dev->bstrerror());
         goto bail_out;
      }
   }
   Dmsg1(150, "Label type=%d\n", dev->label_type);

   /*
    * Let any stored plugin know that we are about to write a new label to the volume.
    */
   if (generate_plugin_event(jcr, bsdEventLabelWrite, dcr) != bRC_OK) {
      Dmsg0(200, "Error from bsdEventLabelWrite plugin event.\n");
      goto bail_out;
   }

   for ( ;; ) {
      empty_block(block);
      if (!dev->rewind(dcr)) {
         Dmsg2(130, "Bad status on %s from rewind: ERR=%s\n", dev->print_name(), dev->print_errmsg());
         if (!forge_on) {
            goto bail_out;
         }
      }

      /* Temporarily mark in append state to enable writing */
      dev->set_append();

      /* Create PRE_LABEL */
      create_volume_label(dev, VolName, PoolName);

      /*
       * If we have already detected an ANSI label, re-read it
       *   to skip past it. Otherwise, we write a new one if
       *   so requested.
       */
      if (dev->label_type != B_BAREOS_LABEL) {
         if (read_ansi_ibm_label(dcr) != VOL_OK) {
            dev->rewind(dcr);
            goto bail_out;
         }
      } else if (!write_ansi_ibm_labels(dcr, ANSI_VOL_LABEL, VolName)) {
         goto bail_out;
      }

      create_volume_label_record(dcr, dev, dcr->rec);
      dcr->rec->Stream = 0;
      dcr->rec->maskedStream = 0;

      if (!write_record_to_block(dcr, dcr->rec)) {
         Dmsg2(130, "Bad Label write on %s: ERR=%s\n", dev->print_name(), dev->print_errmsg());
         goto bail_out;
      } else {
         Dmsg2(130, "Wrote label of %d bytes to %s\n", dcr->rec->data_len, dev->print_name());
      }

      Dmsg0(130, "Call write_block_to_dev()\n");
      if (!dcr->write_block_to_dev()) {
         Dmsg2(130, "Bad Label write on %s: ERR=%s\n", dev->print_name(), dev->print_errmsg());
         goto bail_out;
      }
      break;
   }
   dev = dcr->dev;

   Dmsg0(130, " Wrote block to device\n");

   if (dev->weof(1)) {
      dev->set_labeled();
      write_ansi_ibm_labels(dcr, ANSI_EOF_LABEL, dev->VolHdr.VolumeName);
   }

   if (debug_level >= 20)  {
      dump_volume_label(dev);
   }
   Dmsg0(100, "Call reserve_volume\n");
   if (reserve_volume(dcr, VolName) == NULL) {
      Mmsg2(jcr->errmsg, _("Could not reserve volume %s on %s\n"),
           dev->VolHdr.VolumeName, dev->print_name());
      Dmsg1(100, "%s", jcr->errmsg);
      goto bail_out;
   }
   dev = dcr->dev;                    /* may have changed in reserve_volume */

   dev->clear_append();               /* remove append since this is PRE_LABEL */
   return true;

bail_out:
   volume_unused(dcr);
   dev->clear_volhdr();
   dev->clear_append();               /* remove append since this is PRE_LABEL */
   return false;
}
Пример #30
0
/*
 * Read a Record from the block
 *
 * Returns: false if nothing read or if the continuation record does not match.
 *                In both of these cases, a block read must be done.
 *          true  if at least the record header was read, this
 *                routine may have to be called again with a new
 *                block if the entire record was not read.
 */
bool read_record_from_block(DCR *dcr, DEV_RECORD *rec)
{
   ser_declare;
   uint32_t remlen;
   uint32_t VolSessionId;
   uint32_t VolSessionTime;
   int32_t  FileIndex;
   int32_t  Stream;
   uint32_t data_bytes;
   uint32_t rhl;
   char buf1[100], buf2[100];

   remlen = dcr->block->binbuf;

   /*
    * Clear state flags
    */
   clear_all_bits(REC_STATE_MAX, rec->state_bits);
   if (dcr->block->dev->is_tape()) {
      set_bit(REC_ISTAPE, rec->state_bits);
   }
   rec->Block = ((DEVICE *)(dcr->block->dev))->EndBlock;
   rec->File = ((DEVICE *)(dcr->block->dev))->EndFile;

   /*
    * Get the header. There is always a full header, otherwise we find it in the next block.
    */
   Dmsg3(450, "Block=%d Ver=%d size=%u\n", dcr->block->BlockNumber, dcr->block->BlockVer,
         dcr->block->block_len);
   if (dcr->block->BlockVer == 1) {
      rhl = RECHDR1_LENGTH;
   } else {
      rhl = RECHDR2_LENGTH;
   }
   if (remlen >= rhl) {
      Dmsg4(450, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
            remlen, rec->data_len, rec->remainder, dcr->block->BlockVer);

      unser_begin(dcr->block->bufp, WRITE_RECHDR_LENGTH);
      if (dcr->block->BlockVer == 1) {
         unser_uint32(VolSessionId);
         unser_uint32(VolSessionTime);
      } else {
         VolSessionId = dcr->block->VolSessionId;
         VolSessionTime = dcr->block->VolSessionTime;
      }
      unser_int32(FileIndex);
      unser_int32(Stream);
      unser_uint32(data_bytes);

      dcr->block->bufp += rhl;
      dcr->block->binbuf -= rhl;
      remlen -= rhl;

      /*
       * If we are looking for more (remainder!=0), we reject anything
       * where the VolSessionId and VolSessionTime don't agree
       */
      if (rec->remainder && (rec->VolSessionId != VolSessionId ||
                             rec->VolSessionTime != VolSessionTime)) {
         set_bit(REC_NO_MATCH, rec->state_bits);
         Dmsg0(450, "remainder and VolSession doesn't match\n");
         return false;             /* This is from some other Session */
      }

      /*
       * If Stream is negative, it means that this is a continuation
       * of a previous partially written record.
       */
      if (Stream < 0) {               /* continuation record? */
         Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n", rec->remainder);
         set_bit(REC_CONTINUATION, rec->state_bits);
         if (!rec->remainder) {       /* if we didn't read previously */
            rec->data_len = 0;        /* return data as if no continuation */
         } else if (rec->Stream != -Stream) {
            set_bit(REC_NO_MATCH, rec->state_bits);
            return false;             /* This is from some other Session */
         }
         rec->Stream = -Stream;       /* set correct Stream */
         rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
      } else {                        /* Regular record */
         rec->Stream = Stream;
         rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
         rec->data_len = 0;           /* transfer to beginning of data */
      }
      rec->VolSessionId = VolSessionId;
      rec->VolSessionTime = VolSessionTime;
      rec->FileIndex = FileIndex;
      if (FileIndex > 0) {
         if (dcr->block->FirstIndex == 0) {
            dcr->block->FirstIndex = FileIndex;
         }
         dcr->block->LastIndex = FileIndex;
      }

      Dmsg6(450, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
                 "remlen=%d data_len=%d\n",
         FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
         stream_to_ascii(buf2, rec->Stream, rec->FileIndex), data_bytes, remlen,
         rec->data_len);
   } else {
      /*
       * No more records in this block because the number
       * of remaining bytes are less than a record header
       * length, so return empty handed, but indicate that
       * he must read again. By returning, we allow the
       * higher level routine to fetch the next block and
       * then reread.
       */
      Dmsg0(450, "read_record_block: nothing\n");
      set_bit(REC_NO_HEADER, rec->state_bits);
      set_bit(REC_BLOCK_EMPTY, rec->state_bits);
      empty_block(dcr->block);                 /* mark block empty */
      return false;
   }

   /* Sanity check */
   if (data_bytes >= MAX_BLOCK_LENGTH) {
      /*
       * Something is wrong, force read of next block, abort
       *   continuing with this block.
       */
      set_bit(REC_NO_HEADER, rec->state_bits);
      set_bit(REC_BLOCK_EMPTY, rec->state_bits);
      empty_block(dcr->block);
      Jmsg2(dcr->jcr, M_WARNING, 0, _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
         MAX_BLOCK_LENGTH, data_bytes);
      return false;
   }

   rec->data = check_pool_memory_size(rec->data, rec->data_len + data_bytes);

   /*
    * At this point, we have read the header, now we
    * must transfer as much of the data record as
    * possible taking into account: 1. A partial
    * data record may have previously been transferred,
    * 2. The current block may not contain the whole data
    * record.
    */
   if (remlen >= data_bytes) {
      /*
       * Got whole record
       */
      memcpy(rec->data+rec->data_len, dcr->block->bufp, data_bytes);
      dcr->block->bufp += data_bytes;
      dcr->block->binbuf -= data_bytes;
      rec->data_len += data_bytes;
   } else {
      /*
       * Partial record
       */
      memcpy(rec->data+rec->data_len, dcr->block->bufp, remlen);
      dcr->block->bufp += remlen;
      dcr->block->binbuf -= remlen;
      rec->data_len += remlen;
      rec->remainder = 1;             /* partial record transferred */
      Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
      set_bit(REC_PARTIAL_RECORD, rec->state_bits);
      set_bit(REC_BLOCK_EMPTY, rec->state_bits);
      return true;
   }
   rec->remainder = 0;

   Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
         FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
         stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);

   return true;                       /* transferred full record */
}