]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
Add new Verify Data plugin framework
authorEric Bollengier <eric@baculasystems.com>
Thu, 7 Oct 2021 16:35:13 +0000 (18:35 +0200)
committerEric Bollengier <eric@baculasystems.com>
Wed, 6 Sep 2023 07:49:01 +0000 (09:49 +0200)
bacula/src/dird/job.c
bacula/src/dird/ua_run.c
bacula/src/dird/verify.c
bacula/src/filed/fd_plugins.c
bacula/src/filed/fd_plugins.h
bacula/src/filed/filed_conf.c
bacula/src/filed/filed_conf.h
bacula/src/filed/job.c
bacula/src/filed/verify_vol.c
bacula/src/jcr.h
bacula/src/version.h

index d59a9a580b3519066176aaf83b5c1586e179be1b..90e4471215f273b082303f023924e7b50234326d 100644 (file)
@@ -1594,13 +1594,13 @@ void dird_free_jcr(JCR *jcr)
 
    delete jcr->store_mngr;
 
-   if (jcr->JobId != 0)
+   if (jcr->JobId != 0) {
       write_state_file(director->working_directory, "bacula-dir", get_first_port_host_order(director->DIRaddrs));
-
+   }
+   bfree_and_null(jcr->plugin_options);
    if (jcr->plugin_config) {
       free_plugin_config_items(jcr->plugin_config);
-      delete jcr->plugin_config;
-      jcr->plugin_config = NULL;
+      bdelete_and_null(jcr->plugin_config);
    }
    free_plugins(jcr);                 /* release instantiated plugins */
 
index 8f6fa9bc490e69b44d8295ac0cc6f330e86f7184..c6d3c4132782fa43e9405b73d2f8dbbb7b3d09f8 100644 (file)
@@ -1223,7 +1223,7 @@ int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc)
          add_prompt(ua, _("Replace"));       /* 10 */
          add_prompt(ua, _("JobId"));         /* 11 */
       }
-      if (jcr->getJobType() == JT_BACKUP || jcr->getJobType() == JT_RESTORE) {
+      if (jcr->getJobType() == JT_BACKUP || jcr->getJobType() == JT_RESTORE || jcr->is_JobType(JT_VERIFY)) {
          add_prompt(ua, _("Plugin Options")); /* 12 */
       }
       switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
@@ -1558,9 +1558,7 @@ static bool set_run_context_in_jcr(UAContext *ua, JCR *jcr, run_ctx &rc)
    }
 
    if (rc.plugin_options) {
-      if (jcr->plugin_options) {
-         free(jcr->plugin_options);
-      }
+      bfree_and_null(jcr->plugin_options);
       jcr->plugin_options = bstrdup(rc.plugin_options);
       rc.plugin_options = NULL;
    }
@@ -2017,16 +2015,17 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
             ua->signal(BNET_RUN_CMD);
             ua->send_msg("Type: Verify\n"
                         "Title: Run Verify Job\n"
-                        "JobName:     %s\n"
-                        "Level:       %s\n"
-                        "Client:      %s\n"
-                        "FileSet:     %s\n"
-                        "Pool:        %s (From %s)\n"
-                        "Storage:     %s (From %s)\n"
-                        "Verify Job:  %s\n"
-                        "Verify List: %s\n"
-                        "When:        %s\n"
-                        "Priority:    %d\n",
+                        "JobName:        %s\n"
+                        "Level:          %s\n"
+                        "Client:         %s\n"
+                        "FileSet:        %s\n"
+                        "Pool:           %s (From %s)\n"
+                        "Storage:        %s (From %s)\n"
+                        "Verify Job:     %s\n"
+                        "Verify List:    %s\n"
+                        "When:           %s\n"
+                        "Priority:       %d\n"
+                        "Plugin Options: %s\n",
               job->name(),
               level_to_str(edl, sizeof(edl), jcr->getJobLevel()),
               jcr->client->name(),
@@ -2036,19 +2035,21 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
               Name,
               verify_list,
               bstrutime(dt, sizeof(dt), jcr->sched_time),
-              jcr->JobPriority);
+              jcr->JobPriority,
+              NPRT(jcr->plugin_options));
          } else {
             ua->send_msg(_("Run Verify Job\n"
-                        "JobName:     %s\n"
-                        "Level:       %s\n"
-                        "Client:      %s\n"
-                        "FileSet:     %s\n"
-                        "Pool:        %s (From %s)\n"
-                        "Storage:     %s (From %s)\n"
-                        "Verify Job:  %s\n"
-                        "Verify List: %s\n"
-                        "When:        %s\n"
-                        "Priority:    %d\n"),
+                        "JobName:        %s\n"
+                        "Level:          %s\n"
+                        "Client:         %s\n"
+                        "FileSet:        %s\n"
+                        "Pool:           %s (From %s)\n"
+                        "Storage:        %s (From %s)\n"
+                        "Verify Job:     %s\n"
+                        "Verify List:    %s\n"
+                        "When:           %s\n"
+                        "Priority:       %d\n"
+                        "Plugin Options: %s\n"),
               job->name(),
               level_to_str(edl, sizeof(edl), jcr->getJobLevel()),
               jcr->client->name(),
@@ -2058,7 +2059,8 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
               Name,
               verify_list,
               bstrutime(dt, sizeof(dt), jcr->sched_time),
-              jcr->JobPriority);
+              jcr->JobPriority,
+              NPRT(jcr->plugin_options));
          }
       }
       break;
@@ -2559,15 +2561,13 @@ static bool scan_run_command_line_arguments(UAContext *ua, run_ctx &rc)
                kw_ok = true;
                break;
             case 25: /* pluginoptions */
-               ua->send_msg(_("Plugin Options not yet implemented.\n"));
-               return false;
                if (rc.plugin_options) {
                   ua->send_msg(_("Plugin Options specified twice.\n"));
                   return false;
                }
                rc.plugin_options = ua->argv[i];
                if (!acl_access_ok(ua, PluginOptions_ACL, rc.plugin_options)) {
-                  ua->send_msg(_("No authoriztion for \"PluginOptions\" specification.\n"));
+                  ua->send_msg(_("No authorization for \"PluginOptions\" specification.\n"));
                   return false;
                }
                kw_ok = true;
@@ -2734,6 +2734,10 @@ static bool scan_run_command_line_arguments(UAContext *ua, run_ctx &rc)
       rc.verify_job = rc.job->verify_job;
    }
 
+   if (rc.job->JobType == JT_VERIFY && rc.job->PluginOptions) {
+      rc.plugin_options = rc.job->PluginOptions;
+   }
+
    if (rc.previous_job_name) {
       rc.previous_job = GetJobResWithName(rc.previous_job_name);
       if (!rc.previous_job) {
index cc26c46e8474a7bcca3dc7c0d10a98003f00673f..eb7f540033c9a8ac911ed8781eb96def4d228f9d 100644 (file)
@@ -41,6 +41,7 @@ static char verifycmd[] = "verify level=%s\n";
 
 /* Responses received from File daemon */
 static char OKverify[]  = "2000 OK verify\n";
+static char OKPluginOptions[] = "2000 OK plugin options\n";
 
 /* Commands received from Storage daemon */
 static char OKbootstrap[] = "3000 OK bootstrap\n";
@@ -369,6 +370,18 @@ bool do_verify(JCR *jcr)
       goto bail_out;
    }
 
+   if (jcr->plugin_options) {
+      if (jcr->FDVersion < 15) {
+         Jmsg1(jcr, M_FATAL, 0, _("Unable to send PluginOptions to FD. Please upgrade the FD from %d to 15.\n"), jcr->FDVersion);
+         goto bail_out;
+      }
+      bash_spaces(jcr->plugin_options);
+      fd->fsend("pluginoptions=%s\n", jcr->plugin_options);
+      if (!response(jcr, fd, BSOCK_TYPE_FD, OKPluginOptions, "Verify", DISPLAY_ERROR)) {
+         goto bail_out;
+      }
+   }
+
    /*
     * Send verify command/level to File daemon
     */
index 2878e017d5c7ddce2322cca6cd6f6eb892f85e97..a092e7dc9a3444b53d46dd627255ccf1ab5fa20b 100644 (file)
@@ -75,6 +75,7 @@ static bool get_plugin_name(JCR *jcr, char *cmd, int *ret);
 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp);
 static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp);
 static bRC baculaAccurateAttribs(bpContext *ctx, accurate_attribs_pkt *att);
+static void plugin_register_verify_data(bpContext *ctx);
 
 /*
  * These will be plugged into the global pointer structure for
@@ -126,9 +127,11 @@ struct bacula_ctx {
    bool disabled;                        /* set if plugin disabled */
    bool restoreFileStarted;
    bool createFileCalled;
+   bool verifyFileCalled;                /* true if startVerifyFile() has been called */
    bool cancelCalled;                    /* true if the plugin got the cancel event */
    findINCEXE *exclude;                  /* pointer to exclude files */
    findINCEXE *include;                  /* pointer to include/exclude files */
+   Plugin *plugin;                       /* pointer to the plugin itself */
 };
 
 static bacula_ctx *get_bacula_ctx(bpContext *plugin_ctx)
@@ -231,6 +234,7 @@ void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
    case bEventEndVerifyJob:
       call_if_canceled = true; /* plugin *must* see this call */
       break;
+   case bEventStartVerifyJob:
    case bEventStartRestoreJob:
       break;
    case bEventEndRestoreJob:
@@ -647,7 +651,10 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       if (is_plugin_disabled(jcr)) {
          goto bail_out;
       }
-
+      if (!plug_func(plugin)->startBackupFile || !plug_func(plugin)->endBackupFile) {
+         Dmsg1(dbglvl, "Plugin not suitable for Backup job %s\n", cmd);
+         goto bail_out;
+      }
       Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
       /* Send the backup command to the right plugin*/
       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
@@ -813,7 +820,10 @@ int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       if (is_plugin_disabled(jcr)) {
          goto bail_out;
       }
-
+      if (!plug_func(plugin)->startBackupFile || !plug_func(plugin)->endBackupFile) {
+         Dmsg1(dbglvl, "Plugin not suitable for Estimate job %s\n", cmd);
+         goto bail_out;
+      }
       Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
       /* Send the backup command to the right plugin*/
       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
@@ -1038,10 +1048,14 @@ bool plugin_name_stream(JCR *jcr, char *name)
          Dmsg1(dbglvl, "Plugin %s disabled\n", cmd);
          goto bail_out;
       }
+      if (!plug_func(plugin)->startRestoreFile || !plug_func(plugin)->endRestoreFile) {
+         Dmsg1(dbglvl, "Plugin not suitable for Restore job %s\n", cmd);
+         goto bail_out;
+      }
       Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
       event.eventType = bEventRestoreCommand;
       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
-            &event, cmd) != bRC_OK) {
+                                               &event, cmd) != bRC_OK) {
          Dmsg1(dbglvl, "Handle event failed. Plugin=%s\n", cmd);
          goto bail_out;
       }
@@ -1071,6 +1085,31 @@ bail_out:
    return start;
 }
 
+static void fill_restore_pkt(JCR *jcr, ATTR *attr, restore_pkt *rp)
+{
+   rp->pkt_size = sizeof(restore_pkt);
+   rp->pkt_end = sizeof(restore_pkt);
+   rp->delta_seq = attr->delta_seq;
+   rp->stream = attr->stream;
+   rp->data_stream = attr->data_stream;
+   rp->type = attr->type;
+   rp->file_index = attr->file_index;
+   rp->LinkFI = attr->LinkFI;
+   rp->uid = attr->uid;
+   rp->statp = attr->statp;                /* structure assignment */
+   rp->attrEx = attr->attrEx;
+   if (jcr->is_JobType(JT_VERIFY)) {
+      rp->ofname = attr->fname;
+   } else {
+      rp->ofname = attr->ofname;
+      rp->olname = attr->olname;
+   }
+   rp->where = jcr->where;
+   rp->RegexWhere = jcr->RegexWhere;
+   rp->replace = jcr->replace;
+   rp->create_status = CF_ERROR;
+}
+
 /**
  * Tell the plugin to create the file.  Return values are
  *   This is called only during Restore
@@ -1094,24 +1133,13 @@ int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
    if (!plugin || !plugin_ctx || jcr->is_job_canceled()) {
       return CF_ERROR;
    }
+   if (!plug_func(plugin)->createFile) {
+      Dmsg0(dbglvl, "Plugin not suitable for Restore job\n");
+      return CF_ERROR;
+   }
+
+   fill_restore_pkt(jcr, attr, &rp);
 
-   rp.pkt_size = sizeof(rp);
-   rp.pkt_end = sizeof(rp);
-   rp.delta_seq = attr->delta_seq;
-   rp.stream = attr->stream;
-   rp.data_stream = attr->data_stream;
-   rp.type = attr->type;
-   rp.file_index = attr->file_index;
-   rp.LinkFI = attr->LinkFI;
-   rp.uid = attr->uid;
-   rp.statp = attr->statp;                /* structure assignment */
-   rp.attrEx = attr->attrEx;
-   rp.ofname = attr->ofname;
-   rp.olname = attr->olname;
-   rp.where = jcr->where;
-   rp.RegexWhere = jcr->RegexWhere;
-   rp.replace = jcr->replace;
-   rp.create_status = CF_ERROR;
    Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
          rp.stream, rp.type, rp.LinkFI, rp.ofname);
    if (rp.attrEx) {
@@ -1718,6 +1746,7 @@ void new_plugins(JCR *jcr)
       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
       memset(b_ctx, 0, sizeof(bacula_ctx));
       b_ctx->jcr = jcr;
+      b_ctx->plugin = plugin;
       plugin_ctx_list[i].bContext = (void *)b_ctx;   /* Bacula private context */
       plugin_ctx_list[i].pContext = NULL;
       if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) {
@@ -2102,14 +2131,18 @@ static bRC baculaRegisterEvents(bpContext *ctx, ...)
    va_list args;
    uint32_t event;
 
-   Dsm_check(999);
-   if (!ctx) {
-      return bRC_Error;
-   }
-
    va_start(args, ctx);
    while ((event = va_arg(args, uint32_t))) {
       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
+      switch (event) {
+      case bEventVerifyStream:
+         /* We will call specific function, maybe VerifyFile() + pluginIO */
+         plugin_register_verify_data(ctx);
+         break;
+      default:
+         Dmsg0(50, "Event registration not implemented\n");
+         break;
+      }
    }
    va_end(args);
    Dsm_check(999);
@@ -2522,6 +2555,182 @@ static bRC baculaAccurateAttribs(bpContext *ctx, accurate_attribs_pkt *att)
    return bRC_OK;
 }
 
+bRC plugin_verify_data_close(JCR *jcr)
+{
+   bpContext *ctx;
+   bacula_ctx *bctx;
+   struct io_pkt io;
+   if (!jcr->plugin_verify) {
+      return bRC_Skip;              // Nothing registered
+   }
+   foreach_alist(ctx, jcr->plugin_verify) {
+      bctx = get_bacula_ctx(ctx);
+      jcr->plugin_ctx = ctx;
+      jcr->plugin = bctx->plugin;
+      if (is_plugin_disabled(jcr)) {
+         continue;
+      }
+      if (bctx->verifyFileCalled) {
+         io.pkt_size = sizeof(io);
+         io.pkt_end = sizeof(io);
+         io.func = IO_CLOSE;
+         io.count = 0;
+         io.buf = NULL;
+         io.fname = NULL;
+         io.flags = 0;
+         io.mode = O_WRONLY;
+         io.win32 = false;
+         io.lerror = 0;
+         io.status = -1;
+         plug_func(jcr->plugin)->pluginIO(jcr->plugin_ctx, &io);
+         if (io.win32) {
+            errno = b_errno_win32;
+         } else {
+            errno = io.io_errno;
+         }
+         bctx->verifyFileCalled = false;
+         if (io.status >= 0) {
+            // Do something useful?
+         }
+         if (plug_func(jcr->plugin)->endVerifyFile) {
+            plug_func(jcr->plugin)->endVerifyFile(jcr->plugin_ctx); // Check the return code?
+         }
+      }
+   }
+   jcr->plugin_ctx = NULL;
+   jcr->plugin = NULL;
+   return bRC_OK;
+}
+
+
+bRC plugin_verify_data_update(JCR *jcr, char *data, int size)
+{
+   bpContext *ctx;
+   bacula_ctx *bctx;
+   struct io_pkt io;
+
+   if (!jcr->plugin_verify) {
+      return bRC_Skip;              // Nothing registered
+   }
+   foreach_alist(ctx, jcr->plugin_verify) {
+      if (!is_ctx_good(ctx, jcr, bctx)) {
+         return bRC_Error;
+      }
+      jcr->plugin_ctx = ctx;
+      jcr->plugin = bctx->plugin;
+      if (is_plugin_disabled(jcr)) {
+         continue;
+      }
+      if (bctx->verifyFileCalled) {
+         io.pkt_size = sizeof(io);
+         io.pkt_end = sizeof(io);
+         io.func = IO_WRITE;
+         io.count = size;
+         io.buf = data;
+         io.fname = NULL;
+         io.flags = 0;
+         io.mode = O_WRONLY;
+         io.win32 = false;
+         io.lerror = 0;
+         io.status = -1;
+         plug_func(jcr->plugin)->pluginIO(jcr->plugin_ctx, &io);
+         if (io.win32) {
+            errno = b_errno_win32;
+         } else {
+            errno = io.io_errno;
+         }
+         if (io.status >= 0) {
+            // Do something useful? Maybe close
+         }
+         
+      }
+   }
+   jcr->plugin_ctx = NULL;
+   jcr->plugin = NULL;
+   return bRC_OK;
+}
+
+/*
+ * @brief Bacula function called for each new file in verify data job
+ * @param JCR *jcr
+ * @param ATTR *attr
+ * @return int (file descriptor)
+ */
+bRC plugin_verify_data_open(JCR *jcr,  ATTR *attr)
+{
+   bpContext *ctx;
+   restore_pkt rp;
+   bacula_ctx *bctx;
+   struct io_pkt io;
+   if (!jcr->plugin_verify) {
+      return bRC_Skip;              // Nothing registered
+   }
+   foreach_alist(ctx, jcr->plugin_verify) {
+      if (!is_ctx_good(ctx, jcr, bctx)) {
+         return bRC_Error;
+      }
+      jcr->plugin_ctx = ctx;
+      jcr->plugin = bctx->plugin;
+      bctx->verifyFileCalled = false;
+      if (is_plugin_disabled(jcr)) {
+         continue;
+      }
+      fill_restore_pkt(jcr, attr, &rp);
+      if (plug_func(jcr->plugin)->startVerifyFile) {
+         if (plug_func(jcr->plugin)->startVerifyFile(jcr->plugin_ctx, &rp) == bRC_OK) {
+            io.pkt_size = sizeof(io);
+            io.pkt_end = sizeof(io);
+            io.func = IO_OPEN;
+            io.count = 0;
+            io.buf = NULL;
+            io.fname = attr->fname;
+            io.flags = 0;
+            io.mode = O_WRONLY;
+            io.win32 = false;
+            io.lerror = 0;
+            io.status = -1;
+            plug_func(jcr->plugin)->pluginIO(jcr->plugin_ctx, &io);
+            if (io.win32) {
+               errno = b_errno_win32;
+            } else {
+               errno = io.io_errno;
+            }
+            if (io.status >= 0) {
+               bctx->verifyFileCalled = true;
+            }
+         }
+      }
+   }
+   jcr->plugin_ctx = NULL;
+   jcr->plugin = NULL;
+   return bRC_OK;
+}
+
+/*
+ * @brief Bacula function to append a plugin registered to get a copy of the data
+ *        during a Verify Data job
+ * @param JCR *jcr
+ * @param bpContext *ctx
+ * @return bool
+ */
+static void plugin_register_verify_data(bpContext *ctx)
+{
+   JCR *jcr;
+   bacula_ctx *bctx;
+   if (!is_ctx_good(ctx, jcr, bctx)) {
+      return;
+   }
+   if (!jcr->is_JobLevel(L_VERIFY_DATA)) {
+      return;              /* Cannot register for non DATA job */
+   }
+   if (!jcr->plugin_verify) {
+      jcr->plugin_verify = New(alist(5, not_owned_by_alist));
+   }
+   jcr->plugin_verify->append(ctx);
+   /* TODO: check in me->plugins if we have options for this plugin */
+}
+
+
 #ifdef TEST_PROGRAM
 
 int     (*plugin_bopen)(JCR *jcr, const char *fname, uint64_t flags, mode_t mode) = NULL;
index 5c3f1a90611235021bff0e1f47b1881d98b7264f..27329aa1a4939cc8bbc19a0cad4236fe08abd291 100644 (file)
@@ -466,7 +466,8 @@ typedef enum {
   bEventOptionPlugin                    = 23,
   bEventHandleBackupFile                = 24, /* Used with Options Plugin */
   bEventComponentInfo                   = 25, /* Plugin component */
-  bEventFeatures                        = 26  /* Ask for file list, ... "xxx,yyy,zzz" */
+  bEventFeatures                        = 26, /* Ask for file list, ... "xxx,yyy,zzz" */
+  bEventVerifyStream                    = 27, /* Register to get a copy of the data stream during verify */
 } bEventType;
 
 
@@ -513,6 +514,9 @@ bool plugin_restore_xattr(JCR *jcr, char *data, uint32_t length);
 bool plugin_check_stream(JCR *jcr, int32_t &stream);
 bool plugin_query_parameter(JCR *jcr, char *command, char *param, void sendit(JCR *jcr, const char *str));
 bool plugin_backup_metadata(JCR *jcr, FF_PKT *ff_pkt);
+bRC plugin_verify_data_close(JCR *jcr);
+bRC plugin_verify_data_open(JCR *jcr,  ATTR *attr);
+bRC plugin_verify_data_update(JCR *jcr,  char *buf, int len);
 #endif
 
 #ifdef __cplusplus
@@ -613,6 +617,8 @@ typedef struct s_pluginFuncs {
    bRC (*checkStream)(bpContext *ctx, struct stream_pkt *sp);
    bRC (*queryParameter)(bpContext *ctx, struct query_pkt *qp);
    bRC (*metadataRestore)(bpContext *ctx, struct meta_pkt *mp);
+   bRC (*startVerifyFile)(bpContext *ctx, struct restore_pkt *rp);
+   bRC (*endVerifyFile)(bpContext *ctx);
 } pFuncs;
 
 #define plug_func(plugin) ((pFuncs *)(plugin->pfuncs))
index cfdec034e4ad982ecd31d82394d884bfb78ebb54..363a1d8818db12f8a60245ab98d3c7794624cd30 100644 (file)
@@ -87,6 +87,7 @@ static RES_ITEM cli_items[] = {
    {"PidDirectory",  store_dir,     ITEM(res_client.pid_directory),     0, ITEM_REQUIRED, 0},
    {"SubsysDirectory",  store_dir,  ITEM(res_client.subsys_directory),  0, 0, 0},
    {"PluginDirectory",  store_dir,  ITEM(res_client.plugin_directory),  0, 0, 0},
+   {"Plugin",           store_alist_str, ITEM(res_client.plugins),      0, 0, 0},
    {"SnapshotCommand",  store_str,  ITEM(res_client.snapshot_command), 0, 0, 0},
    {"ScriptsDirectory", store_dir,  ITEM(res_client.scripts_directory),  0, 0, 0},
    {"MaximumConcurrentJobs", store_pint32,  ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 20},
@@ -584,6 +585,9 @@ void free_resource(RES *sres, int type)
       if (res->res_client.plugin_directory) {
          free(res->res_client.plugin_directory);
       }
+      if (res->res_client.plugins) {
+         delete res->res_client.plugins;
+      }
       if (res->res_client.dedup_index_dir) {
          free(res->res_client.dedup_index_dir);
       }
index c17aaf6b79b85105f981d0f0411fadfd327007fd..a4acca622cc75a5c496714fc5879f64dafe63c73 100644 (file)
@@ -127,6 +127,7 @@ struct CLIENT {
    char *pid_directory;
    char *subsys_directory;
    char *plugin_directory;            /* Plugin directory */
+   alist *plugins;                    /* Non job specific Plugin options */
    char *scripts_directory;
    char *snapshot_command;
    char *dedup_index_dir;             /* Directory for local dedup cache (deprecated) */
index ab2737658dca8ea0db4f0a8525fbfed9ef2a714f..354dedf85418524d1b5773df87101e223f2179e5 100644 (file)
@@ -68,6 +68,7 @@ extern int accurate_cmd(JCR *jcr);
 extern int collect_cmd(JCR *jcr);
 
 /* Forward referenced functions */
+static int pluginoptions_cmd(JCR *jcr);
 static int backup_cmd(JCR *jcr);
 static int component_cmd(JCR *jcr);
 static int cancel_cmd(JCR *jcr);
@@ -148,6 +149,7 @@ struct s_cmds cmds[] = {
    {"restorefilelist", restorefilelist_cmd, 0},
    {"statistics",   collect_cmd, 0},
    {"query",        query_cmd,   0},
+   {"pluginoptions", pluginoptions_cmd, 0},
 #ifdef DEVELOPER
    {"exit",         exit_cmd, 0},
 #endif
@@ -3471,7 +3473,10 @@ void filed_free_jcr(JCR *jcr)
 #ifdef WIN32_VSS
    VSSCleanup(jcr->pVSSClient);
 #endif
+   bdelete_and_null(jcr->plugin_verify);
+   bdelete_and_null(jcr->plugin_options_list);
    free_plugins(jcr);                 /* release instantiated plugins */
+
    free_runscripts(jcr->RunScripts);
    delete jcr->RunScripts;
    free_path_list(jcr);
@@ -3529,6 +3534,30 @@ int response(JCR *jcr, BSOCK *sd, char *resp, const char *cmd)
    return 0;
 }
 
+/*
+ * Get PluginOptions from the Director 
+ *
+ */
+static int pluginoptions_cmd(JCR *jcr)
+{
+   POOL_MEM buf(PM_MESSAGE);
+   BSOCK *dir = jcr->dir_bsock;
+   buf.check_size(dir->msglen+1);
+   if (scan_string(dir->msg, "pluginoptions=%s", buf.c_str()) == 1) {
+      unbash_spaces(buf.c_str());
+      if (!jcr->plugin_options_list) {
+         jcr->plugin_options_list = New(alist(5, owned_by_alist));
+      }
+      jcr->plugin_options_list->append(bstrdup(buf.c_str()));
+
+   } else {
+      dir->fsend(_("2992 Bad pluginoptions command\n"));
+      return 0;
+   }
+   dir->fsend("2000 OK plugin options\n");
+   return 1;
+}
+
 /* 
  * Small helper to manager a POLL request when the heartbeat is started
  * When we send POLL, we get a OK message, but if the heartbeat is started
index 384a9f36bf7160da855b374f0982afc83e31cb95..889ab42fafafda56edeefd44127d6998b2ecbd7c 100644 (file)
@@ -254,6 +254,7 @@ void do_verify_volume(JCR *jcr)
    char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
    int stat;
    int bget_ret = 0;
+   char *opts;
    char *wbuf;                        /* write buffer */
    uint32_t wsize;                    /* write size */
    uint32_t rsize;                    /* read size */
@@ -270,6 +271,12 @@ void do_verify_volume(JCR *jcr)
    dir = jcr->dir_bsock;
    jcr->setJobStatus(JS_Running);
 
+   if (jcr->plugin_options_list) {
+      foreach_alist(opts, jcr->plugin_options_list) {
+         generate_plugin_event(jcr, bEventPluginCommand, (void *)opts);
+      }
+   }
+
    LockRes();
    CLIENT *client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
    UnlockRes();
@@ -337,6 +344,10 @@ void do_verify_volume(JCR *jcr)
       case STREAM_UNIX_ATTRIBUTES:
       case STREAM_UNIX_ATTRIBUTES_EX:
          Dmsg0(400, "Stream=Unix Attributes.\n");
+         if (plugin_verify_data_close(jcr) == bRC_Error) {
+            goto bail_out;
+         }
+
          if (!vctx.close_previous_stream()) {
             goto bail_out;
          }
@@ -398,6 +409,11 @@ void do_verify_volume(JCR *jcr)
                Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), dir->bstrerror());
                goto bail_out;
             }
+
+         } else if (jcr->is_JobLevel(L_VERIFY_DATA) && attr->type == FT_REG) {
+            if (plugin_verify_data_open(jcr, attr) == bRC_Error) {
+               goto bail_out;
+            }
          }
          break;
 
@@ -460,6 +476,9 @@ void do_verify_volume(JCR *jcr)
             if (!jcr->crypto.digest) {
                jcr->crypto.digest = crypto_digest_new(jcr, vctx.digesttype);
             }
+            if (plugin_verify_data_close(jcr) == bRC_Error) {
+               goto bail_out;
+            }
             vctx.close_previous_stream();
             if (strncmp(digest, vctx.digest,
                         MIN(sizeof(digest), sizeof(vctx.digest))) != 0)
@@ -538,6 +557,11 @@ void do_verify_volume(JCR *jcr)
 
             vctx.update_checksum(wbuf, wsize);
 
+            if (plugin_verify_data_update(jcr, wbuf, wsize) == bRC_Error) {
+               dequeue_messages(jcr);
+               goto bail_out;
+            }
+
             if (vctx.stream == STREAM_WIN32_GZIP_DATA
                 || vctx.stream == STREAM_WIN32_DATA
                 || vctx.stream == STREAM_WIN32_COMPRESSED_DATA
@@ -564,6 +588,9 @@ void do_verify_volume(JCR *jcr)
    if (bget_ret == BNET_EXT_TERMINATE) {
       goto bail_out;
    }
+   if (plugin_verify_data_close(jcr) == bRC_Error) {
+      goto bail_out;
+   }
    if (!vctx.close_previous_stream()) {
       goto bail_out;
    }
index 53038cf5871a72e66aa0dab40b9dab9ab12802ba..57d110dcf8e1a911600f1e63371bd073c6f0edb7 100644 (file)
@@ -489,6 +489,8 @@ public:
    DedupFiledInterface *dedup;        /* help the FD to do deduplication */
    bool dedup_use_cache;              /* use client cache */
    VSSClient *pVSSClient;             /* VSS handler */
+   alist *plugin_verify;              /* Registered plugins that need a copy of the data in verify job */
+   alist *plugin_options_list;        /* list of the options to use in a job */
 #endif /* FILE_DAEMON */
 
 
index add0453c68c530efdf618ac8fcec05e7148c4e55..0f477fd45fb019f747f68dbc7de032ebcc35e40d 100644 (file)
  */
 
 #ifdef COMMUNITY
-#define FD_VERSION 14  /* make same as community Linux FD */
+#define FD_VERSION 15  /* make same as community Linux FD */
 #else
-#define FD_VERSION 14 /* Enterprise FD version */
+#define FD_VERSION 15 /* Enterprise FD version */
 #endif
 
 /*