Esempio n. 1
0
/*
 * The bootstrap is stored in a file, so open the file, and loop
 * through it processing each storage device in turn. If the
 * storage is different from the prior one, we open a new connection
 * to the new storage and do a restore for that part.
 *
 * This permits handling multiple storage daemons for a single
 * restore.  E.g. your Full is stored on tape, and Incrementals
 * on disk.
 */
static inline bool do_ndmp_restore_bootstrap(JCR *jcr)
{
   int cnt;
   BSOCK *sd;
   BSR *bsr;
   NIS *nis = NULL;
   int32_t current_fi;
   bootstrap_info info;
   BSR_FINDEX *fileindex;
   struct ndm_session ndmp_sess;
   struct ndm_job_param ndmp_job;
   bool session_initialized = false;
   bool retval = false;
   int NdmpLoglevel;

   if (jcr->res.client->ndmp_loglevel > me->ndmp_loglevel) {
      NdmpLoglevel = jcr->res.client->ndmp_loglevel;
   } else {
      NdmpLoglevel = me->ndmp_loglevel;
   }

   /*
    * We first parse the BSR ourself so we know what to restore.
    */
   jcr->bsr = parse_bsr(jcr, jcr->RestoreBootstrap);
   if (!jcr->bsr) {
      Jmsg(jcr, M_FATAL, 0, _("Error parsing bootstrap file.\n"));
      goto bail_out;
   }

   /*
    * Setup all paired read storage.
    */
   set_paired_storage(jcr);
   if (!jcr->res.pstore) {
      Jmsg(jcr, M_FATAL, 0,
           _("Read storage %s doesn't point to storage definition with paired storage option.\n"),
           jcr->res.rstore->name());
      goto bail_out;
   }

   /*
    * Open the bootstrap file
    */
   if (!open_bootstrap_file(jcr, info)) {
      goto bail_out;
   }

   nis = (NIS *)malloc(sizeof(NIS));
   memset(nis, 0, sizeof(NIS));

   /*
    * Read the bootstrap file
    */
   bsr = jcr->bsr;
   while (!feof(info.bs)) {
      if (!select_next_rstore(jcr, info)) {
         goto cleanup;
      }

      /*
       * Initialize the ndmp restore job. We build the generic job once per storage daemon
       * and reuse the job definition for each seperate sub-restore we perform as
       * part of the whole job. We only free the env_table between every sub-restore.
       */
      if (!ndmp_build_client_job(jcr, jcr->res.client, jcr->res.pstore, NDM_JOB_OP_EXTRACT, &ndmp_job)) {
         goto cleanup;
      }

      /*
       * 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(10, "Open connection with storage daemon\n");
      jcr->setJobStatus(JS_WaitSD);

      /*
       * Start conversation with Storage daemon
       */
      if (!connect_to_storage_daemon(jcr, 10, me->SDConnectTimeout, true)) {
         goto cleanup;
      }
      sd = jcr->store_bsock;

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

      jcr->setJobStatus(JS_Running);

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

      if (!sd->fsend("run")) {
         goto cleanup;
      }

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

      /*
       * Walk over each bsr record
       */
      cnt = 0;
      for (bsr = jcr->bsr; bsr; bsr = bsr->next) {
         /*
          * Walk each fileindex of the current BSR record. Each different fileindex is
          * a separate NDMP stream.
          */
         for (fileindex = bsr->FileIndex; fileindex; fileindex = fileindex->next) {
            for (current_fi = fileindex->findex; current_fi <= fileindex->findex2; current_fi++) {
               /*
                * See if this is the first Restore NDMP stream or not. For NDMP we can have multiple Backup
                * runs as part of the same Job. When we are restoring data from a Native Storage Daemon
                * we let it know to expect a next restore session. It will generate a new authorization
                * key so we wait for the nextrun_ready conditional variable to be raised by the msg_thread.
                */
               if (jcr->store_bsock && cnt > 0) {
                  jcr->store_bsock->fsend("nextrun");
                  P(mutex);
                  pthread_cond_wait(&jcr->nextrun_ready, &mutex);
                  V(mutex);
               }

               /*
                * Perform the actual NDMP job.
                * Initialize a new NDMP session
                */
               memset(&ndmp_sess, 0, sizeof(ndmp_sess));
               ndmp_sess.conn_snooping = (me->ndmp_snooping) ? 1 : 0;
               ndmp_sess.control_agent_enabled = 1;

               ndmp_sess.param = (struct ndm_session_param *)malloc(sizeof(struct ndm_session_param));
               memset(ndmp_sess.param, 0, sizeof(struct ndm_session_param));
               ndmp_sess.param->log.deliver = ndmp_loghandler;
               ndmp_sess.param->log_level = native_to_ndmp_loglevel(NdmpLoglevel, debug_level, nis);
               nis->jcr = jcr;
               ndmp_sess.param->log.ctx = nis;
               ndmp_sess.param->log_tag = bstrdup("DIR-NDMP");

               /*
                * Initialize the session structure.
                */
               if (ndma_session_initialize(&ndmp_sess)) {
                  goto cleanup_ndmp;
               }
               session_initialized = true;

               /*
                * Copy the actual job to perform.
                */
               jcr->jr.FileIndex = current_fi;
               if (bsr->sessid && bsr->sesstime) {
                  jcr->jr.VolSessionId = bsr->sessid->sessid;
                  jcr->jr.VolSessionTime = bsr->sesstime->sesstime;
               } else {
                  Jmsg(jcr, M_FATAL, 0, _("Wrong BSR missing sessid and/or sesstime\n"));
                  goto cleanup_ndmp;
               }

               memcpy(&ndmp_sess.control_acb->job, &ndmp_job, sizeof(struct ndm_job_param));
               if (!fill_restore_environment(jcr,
                        current_fi,
                        &ndmp_sess.control_acb->job)) {
                  Jmsg(jcr, M_ERROR, 0, _("ERROR in fill_restore_environment\n"));
                  goto cleanup_ndmp;
               }

               ndma_job_auto_adjust(&ndmp_sess.control_acb->job);
               if (!ndmp_validate_job(jcr, &ndmp_sess.control_acb->job)) {
                  Jmsg(jcr, M_ERROR, 0, _("ERROR in ndmp_validate_job\n"));
                  goto cleanup_ndmp;
               }

               /*
                * Commission the session for a run.
                */
               if (ndma_session_commission(&ndmp_sess)) {
                  Jmsg(jcr, M_ERROR, 0, _("ERROR in ndma_session_commission\n"));
                  goto cleanup_ndmp;
               }

               /*
                * Setup the DMA.
                */
               if (ndmca_connect_control_agent(&ndmp_sess)) {
                  Jmsg(jcr, M_ERROR, 0, _("ERROR in ndmca_connect_control_agent\n"));
                  goto cleanup_ndmp;
               }

               ndmp_sess.conn_open = 1;
               ndmp_sess.conn_authorized = 1;

               /*
                * Let the DMA perform its magic.
                */
               if (ndmca_control_agent(&ndmp_sess) != 0) {
                  Jmsg(jcr, M_ERROR, 0, _("ERROR in ndmca_control_agent\n"));
                  goto cleanup_ndmp;
               }

               /*
                * See if there were any errors during the restore.
                */
               if (!extract_post_restore_stats(jcr, &ndmp_sess)) {
                  Jmsg(jcr, M_ERROR, 0, _("ERROR in extract_post_restore_stats\n"));
                  goto cleanup_ndmp;
               }

               /*
                * Reset the NDMP session states.
                */
               ndma_session_decommission(&ndmp_sess);

               /*
                * Cleanup the job after it has run.
                */
               ndma_destroy_env_list(&ndmp_sess.control_acb->job.env_tab);
               ndma_destroy_env_list(&ndmp_sess.control_acb->job.result_env_tab);
               ndma_destroy_nlist(&ndmp_sess.control_acb->job.nlist_tab);

               /*
                * Release any tape device name allocated.
                */
               if (ndmp_sess.control_acb->job.tape_device) {
                  free(ndmp_sess.control_acb->job.tape_device);
                  ndmp_sess.control_acb->job.tape_device = NULL;
               }

               /*
                * Destroy the session.
                */
               ndma_session_destroy(&ndmp_sess);

               /*
                * Free the param block.
                */
               free(ndmp_sess.param->log_tag);
               free(ndmp_sess.param);
               ndmp_sess.param = NULL;

               /*
                * Reset the initialized state so we don't try to cleanup again.
                */
               session_initialized = false;

               /*
                * Keep track that we finished this part of the restore.
                */
               cnt++;
            }
         }
      }

      /*
       * Tell the storage daemon we are done.
       */
      jcr->store_bsock->fsend("finish");
      wait_for_storage_daemon_termination(jcr);
   }

   /*
    * Jump to the generic cleanup done for every Job.
    */
   retval = true;
   goto cleanup;

cleanup_ndmp:
   /*
    * Only need to cleanup when things are initialized.
    */
   if (session_initialized) {
      ndma_destroy_env_list(&ndmp_sess.control_acb->job.env_tab);
      ndma_destroy_env_list(&ndmp_sess.control_acb->job.result_env_tab);
      ndma_destroy_nlist(&ndmp_sess.control_acb->job.nlist_tab);

      if (ndmp_sess.control_acb->job.tape_device) {
         free(ndmp_sess.control_acb->job.tape_device);
      }

      /*
       * Destroy the session.
       */
      ndma_session_destroy(&ndmp_sess);
   }

   if (ndmp_sess.param) {
      free(ndmp_sess.param->log_tag);
      free(ndmp_sess.param);
   }

cleanup:
   if (nis) {
      free(nis);
   }
   free_paired_storage(jcr);
   close_bootstrap_file(info);

bail_out:
   free_tree(jcr->restore_tree_root);
   jcr->restore_tree_root = NULL;
   return retval;
}
Esempio n. 2
0
/**
 * The bootstrap is stored in a file, so open the file, and loop
 *   through it processing each storage device in turn. If the
 *   storage is different from the prior one, we open a new connection
 *   to the new storage and do a restore for that part.
 *
 * This permits handling multiple storage daemons for a single
 *   restore.  E.g. your Full is stored on tape, and Incrementals
 *   on disk.
 */
static inline bool do_native_restore_bootstrap(JCR *jcr)
{
   STORERES *store;
   bootstrap_info info;
   BSOCK *fd = NULL;
   BSOCK *sd = NULL;
   bool first_time = true;
   POOL_MEM restore_cmd(PM_MESSAGE);

   /*
    * This command is used for each part
    */
   build_restore_command(jcr, restore_cmd);

   /*
    * Open the bootstrap file
    */
   if (!open_bootstrap_file(jcr, info)) {
      goto bail_out;
   }

   /*
    * Read the bootstrap file
    */
   jcr->passive_client = jcr->res.client->passive;
   while (!feof(info.bs)) {
      if (!select_next_rstore(jcr, info)) {
         goto bail_out;
      }
      store = jcr->res.rstore;

      /**
       * 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(10, "Open connection with storage daemon\n");
      jcr->setJobStatus(JS_WaitSD);

      /*
       * Start conversation with Storage daemon
       */
      if (!connect_to_storage_daemon(jcr, 10, me->SDConnectTimeout, true)) {
         goto bail_out;
      }
      sd = jcr->store_bsock;

      /*
       * Now start a job with the Storage daemon
       */
      if (!start_storage_daemon_job(jcr, jcr->rstorage, NULL)) {
         goto bail_out;
      }

      if (first_time) {
         /*
          * Start conversation with File daemon
          */
         jcr->setJobStatus(JS_WaitFD);
         jcr->keep_sd_auth_key = true; /* don't clear the sd_auth_key now */
         if (!connect_to_file_daemon(jcr, 10, me->FDConnectTimeout, true, true)) {
            goto bail_out;
         }
         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;
         }
      }

      jcr->setJobStatus(JS_Running);

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

      if (!jcr->passive_client) {
         int tls_need = BNET_TLS_NONE;

         /*
          * When the client is not in passive mode we can put the SD in
          * listen mode for the FD connection. And ask the FD to connect
          * to 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");

         /*
          * Send Storage daemon address to the File daemon,
          * then wait for File daemon to make connection
          * with Storage 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);
         memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));

         Dmsg1(6, "dird>filed: %s\n", fd->msg);
         if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
            goto bail_out;
         }
      } else {
         int tls_need = BNET_TLS_NONE;
         CLIENTRES *client = jcr->res.client;
         /*
          * In passive mode we tell the FD what authorization key to use
          * and the ask the SD to initiate the connection.
          */
         fd->fsend(setauthorizationcmd, jcr->sd_auth_key);
         memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));

         Dmsg1(6, "dird>filed: %s\n", fd->msg);
         if (!response(jcr, fd, OKAuthorization, "Setauthorization", DISPLAY_ERROR)) {
            goto bail_out;
         }

         /*
          * TLS Requirement
          */
         tls_need = BNET_TLS_NONE;
         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");
      }

      /*
       * Declare the job started to start the MaxRunTime check
       */
      jcr->setJobStarted();

      /*
       * Only pass "global" commands to the FD once
       */
      if (first_time) {
         first_time = false;
         if (!send_runscripts_commands(jcr)) {
            goto bail_out;
         }

         if (!send_restore_objects(jcr)) {
            Dmsg0(000, "FAIL: Send restore objects\n");
            goto bail_out;
         }

         /*
          * Only FD version 52 and later understand the sending of plugin options.
          */
         if (jcr->FDVersion >= FD_VERSION_52) {
            if (!send_plugin_options(jcr)) {
               Dmsg0(000, "FAIL: Send plugin options\n");
               goto bail_out;
            }
         } else {
            /*
             * Plugin options specified and not a FD that understands the new protocol keyword.
             */
            if (jcr->plugin_options) {
               Jmsg(jcr, M_FATAL, 0,
                     _("Client \"%s\" doesn't support plugin option passing. "
                       "Please upgrade your client or disable compat mode.\n"),
                    jcr->res.client->name());
               goto bail_out;
            }
         }
      }

      fd->fsend("%s", restore_cmd.c_str());

      if (!response(jcr, fd, OKrestore, "Restore", DISPLAY_ERROR)) {
         goto bail_out;
      }

      if (jcr->FDVersion < FD_VERSION_2) { /* Old FD */
         break;                 /* we do only one loop */
      } else {
         if (!response(jcr, fd, OKstoreend, "Store end", DISPLAY_ERROR)) {
            goto bail_out;
         }
         wait_for_storage_daemon_termination(jcr);
      }
   } /* the whole boostrap has been send */

   if (fd && jcr->FDVersion >= FD_VERSION_2) {
      fd->fsend("endrestore");
   }

   close_bootstrap_file(info);
   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;
   }

   close_bootstrap_file(info);
   return false;
}