]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/filed/fd_plugins.c
Fix FD crash when plugin running and cancel given
[bacula/bacula] / bacula / src / filed / fd_plugins.c
index a7a9fe88f70620c5a964d5a3e6ff0e3e802e10c2..df13465431b5e8219715d40f67f6150922a0ef52 100644 (file)
@@ -1,7 +1,7 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2007-2008 Free Software Foundation Europe e.V.
+   Copyright (C) 2007-2010 Free Software Foundation Europe e.V.
 
    The main author of Bacula is Kern Sibbald, with contributions from
    many others, a complete list can be found in the file AUTHORS.
@@ -56,12 +56,13 @@ static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
 static bRC baculaRegisterEvents(bpContext *ctx, ...);
 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
-  int type, time_t mtime, const char *fmt, ...);
+  int type, utime_t mtime, const char *fmt, ...);
 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
   int level, const char *fmt, ...);
 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
               size_t size);
 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
+static bool is_plugin_compatible(Plugin *plugin);
 
 /*
  * These will be plugged into the global pointer structure for
@@ -122,7 +123,7 @@ void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
    Plugin *plugin;
    int i = 0;
 
-   if (!plugin_list || !jcr->plugin_ctx_list) {
+   if (!plugin_list || !jcr || !jcr->plugin_ctx_list || job_canceled(jcr)) {
       return;                         /* Return if no plugins loaded */
    }
 
@@ -150,6 +151,45 @@ void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
    return;
 }
 
+/*
+ * Check if file was seen for accurate
+ */
+bool plugin_check_file(JCR *jcr, char *fname)
+{
+   Plugin *plugin;
+   int rc = bRC_OK;
+   int i = 0;
+
+   if (!plugin_list || !jcr || !jcr->plugin_ctx_list || job_canceled(jcr)) {
+      return false;                      /* Return if no plugins loaded */
+   }
+
+   bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
+
+   Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
+
+   /* Pass event to every plugin */
+   foreach_alist(plugin, plugin_list) {
+      jcr->plugin_ctx = &plugin_ctx_list[i++];
+      jcr->plugin = plugin;
+      if (is_plugin_disabled(jcr)) {
+         continue;
+      }
+      if (plug_func(plugin)->checkFile == NULL) {
+         continue;
+      }
+      rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
+      if (rc == bRC_Seen) {
+         break;
+      }
+   }
+
+   jcr->plugin = NULL;
+   jcr->plugin_ctx = NULL;
+   return rc == bRC_Seen;
+}
+
+
 /*   
  * Sequence of calls for a backup:
  * 1. plugin_save() here is called with ff_pkt
@@ -174,8 +214,11 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
    char *cmd = ff_pkt->top_fname;
    struct save_pkt sp;
    bEvent event;
+   POOL_MEM fname(PM_FNAME);
+   POOL_MEM link(PM_FNAME);
 
-   if (!plugin_list || !jcr->plugin_ctx_list) {
+   if (!plugin_list || !jcr->plugin_ctx_list || job_canceled(jcr)) {
+      Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not loaded.\n", cmd);
       return 1;                            /* Return if no plugins loaded */
    }
 
@@ -237,13 +280,22 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
          }
          jcr->plugin_sp = &sp;
          ff_pkt = jcr->ff;
-         ff_pkt->fname = sp.fname;
-         ff_pkt->link = sp.link;
+         /*
+          * Copy fname and link because save_file() zaps them.  This 
+          *  avoids zaping the plugin's strings.
+          */
+         pm_strcpy(fname, sp.fname);
+         pm_strcpy(link, sp.link);
+         ff_pkt->fname = fname.c_str();
+         ff_pkt->link = link.c_str();
          ff_pkt->type = sp.type;
          memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
-         Dmsg1(dbglvl, "Save_file: file=%s\n", ff_pkt->fname);
+         Dmsg1(dbglvl, "Save_file: file=%s\n", fname.c_str());
          save_file(jcr, ff_pkt, true);
          bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
+         if (rc == bRC_More || rc == bRC_OK) {
+            accurate_mark_file_as_seen(jcr, fname.c_str());
+         }
          if (rc == bRC_More) {
             continue;
          }
@@ -251,7 +303,7 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       }
       goto bail_out;
    }
-   Jmsg1(jcr, M_ERROR, 0, "Command plugin \"%s\" not found.\n", cmd);
+   Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
 
 bail_out:
    jcr->cmd_plugin = false;
@@ -273,6 +325,9 @@ bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
       Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
       return false;
    }
+   if (job_canceled(jcr)) {
+      return false;
+   }
   
    if (start) {
       index++;                  /* JobFiles not incremented yet */
@@ -396,6 +451,7 @@ bail_out:
 
 /*
  * Tell the plugin to create the file.  Return values are
+ *   This is called only during Restore
  *
  *  CF_ERROR    -- error
  *  CF_SKIP     -- skip processing this file
@@ -411,7 +467,7 @@ int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
    int flags;
    int rc;
 
-   if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr)) {
+   if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || job_canceled(jcr)) {
       return CF_ERROR;
    }
    rp.pkt_size = sizeof(rp);
@@ -470,6 +526,7 @@ int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
  * Reset the file attributes after all file I/O is done -- this allows
  *  the previous access time/dates to be set properly, and it also allows
  *  us to properly set directory permissions.
+ *  Not currently Implemented.
  */
 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
 {
@@ -481,12 +538,15 @@ bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
    return true;
 }
 
+/*
+ * Print to file the plugin info.
+ */
 void dump_fd_plugin(Plugin *plugin, FILE *fp)
 {
    if (!plugin) {
       return ;
    }
-   pInfo *info = (pInfo *) plugin->pinfo;
+   pInfo *info = (pInfo *)plugin->pinfo;
    fprintf(fp, "\tversion=%d\n", info->version);
    fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
    fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
@@ -510,7 +570,8 @@ void load_fd_plugins(const char *plugin_dir)
    }
 
    plugin_list = New(alist(10, not_owned_by_alist));
-   if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type)) {
+   if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
+                     is_plugin_compatible)) {
       /* Either none found, or some error */
       if (plugin_list->size() == 0) {
          delete plugin_list;
@@ -526,15 +587,58 @@ void load_fd_plugins(const char *plugin_dir)
    plugin_bread  = my_plugin_bread;
    plugin_bwrite = my_plugin_bwrite;
    plugin_blseek = my_plugin_blseek;
+
+   /* 
+    * Verify that the plugin is acceptable, and print information
+    *  about it.
+    */
    foreach_alist(plugin, plugin_list) {
       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
-
    }
 
    dbg_plugin_add_hook(dump_fd_plugin);
 }
 
+/*
+ * Check if a plugin is compatible.  Called by the load_plugin function
+ *  to allow us to verify the plugin.
+ */
+static bool is_plugin_compatible(Plugin *plugin)
+{
+   pInfo *info = (pInfo *)plugin->pinfo;
+   Dmsg0(50, "is_plugin_compatible called\n");
+   if (debug_level >= 50) {
+      dump_fd_plugin(plugin, stdin);
+   }
+   if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
+      Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
+           plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
+      Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
+           plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
+
+      return false;
+   }
+   if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
+      Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
+           plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
+      Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
+           plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
+      return false;
+   }
+   if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
+       strcmp(info->plugin_license, "GPLv2") != 0) {
+      Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
+           plugin->file, info->plugin_license);
+      Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
+           plugin->file, info->plugin_license);
+      return false;
+   }
+      
+   return true;
+}
+
+
 /*
  * Create a new instance of each plugin for this Job
  *   Note, plugin_list can exist but jcr->plugin_ctx_list can
@@ -549,6 +653,9 @@ void new_plugins(JCR *jcr)
       Dmsg0(dbglvl, "plugin list is NULL\n");
       return;
    }
+   if (job_canceled(jcr)) {
+      return;
+   }
 
    int num = plugin_list->size();
 
@@ -604,6 +711,9 @@ static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode
    struct io_pkt io;
 
    Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
+   if (!plugin || !jcr->plugin_ctx) {
+      return 0;
+   }
    io.pkt_size = sizeof(io);
    io.pkt_end = sizeof(io);
    io.func = IO_OPEN;
@@ -631,7 +741,11 @@ static int my_plugin_bclose(BFILE *bfd)
    JCR *jcr = bfd->jcr;
    Plugin *plugin = (Plugin *)jcr->plugin;
    struct io_pkt io;
+
    Dmsg0(dbglvl, "===== plugin_bclose\n");
+   if (!plugin || !jcr->plugin_ctx) {
+      return 0;
+   }
    io.pkt_size = sizeof(io);
    io.pkt_end = sizeof(io);
    io.func = IO_CLOSE;
@@ -656,7 +770,11 @@ static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
    JCR *jcr = bfd->jcr;
    Plugin *plugin = (Plugin *)jcr->plugin;
    struct io_pkt io;
+
    Dmsg0(dbglvl, "plugin_bread\n");
+   if (!plugin || !jcr->plugin_ctx) {
+      return 0;
+   }
    io.pkt_size = sizeof(io);
    io.pkt_end = sizeof(io);
    io.func = IO_READ;
@@ -680,7 +798,11 @@ static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
    JCR *jcr = bfd->jcr;
    Plugin *plugin = (Plugin *)jcr->plugin;
    struct io_pkt io;
+
    Dmsg0(dbglvl, "plugin_bwrite\n");
+   if (!plugin || !jcr->plugin_ctx) {
+      return 0;
+   }
    io.pkt_size = sizeof(io);
    io.pkt_end = sizeof(io);
    io.func = IO_WRITE;
@@ -704,7 +826,11 @@ static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
    JCR *jcr = bfd->jcr;
    Plugin *plugin = (Plugin *)jcr->plugin;
    struct io_pkt io;
+
    Dmsg0(dbglvl, "plugin_bseek\n");
+   if (!plugin || !jcr->plugin_ctx) {
+      return 0;
+   }
    io.pkt_size = sizeof(io);
    io.pkt_end = sizeof(io);
    io.func = IO_SEEK;
@@ -732,12 +858,12 @@ static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
 {
    JCR *jcr;
-   jcr = ((bacula_ctx *)ctx->bContext)->jcr;
-// Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
    if (!value || !ctx) {
       return bRC_Error;
    }
    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
+// Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
+   jcr = ((bacula_ctx *)ctx->bContext)->jcr;
    if (!jcr) {
       return bRC_Error;
    }
@@ -752,22 +878,61 @@ static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
       Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
       break;
    case bVarLevel:
+      *((int *)value) = jcr->getJobLevel();
+      Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
+      break;
    case bVarType:
+      *((int *)value) = jcr->getJobType();
+      Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
+      break;
    case bVarClient:
+      *((char **)value) = jcr->client_name;
+      Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
+      break;
    case bVarJobName:
+      *((char **)value) = jcr->Job;
+      Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
+      break;
    case bVarJobStatus:
+      *((int *)value) = jcr->JobStatus;
+      Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
+      break;
    case bVarSinceTime:
+      *((int *)value) = (int)jcr->mtime;
+      Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
       break;
+   case bVarAccurate:
+      *((int *)value) = (int)jcr->accurate;
+      Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
+      break;
+   case bVarFileSeen:
+      break;                 /* a write only variable, ignore read request */
    }
    return bRC_OK;
 }
 
 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
 {
+   JCR *jcr;
    if (!value || !ctx) {
       return bRC_Error;
    }
-   Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
+   jcr = ((bacula_ctx *)ctx->bContext)->jcr;
+// Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
+   jcr = ((bacula_ctx *)ctx->bContext)->jcr;
+   if (!jcr) {
+      return bRC_Error;
+   }
+// Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
+   switch (var) {
+   case bVarFileSeen:
+      if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
+         return bRC_Error;
+      } 
+      break;
+   default:
+      break;
+   }
    return bRC_OK;
 }
 
@@ -789,7 +954,7 @@ static bRC baculaRegisterEvents(bpContext *ctx, ...)
 }
 
 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
-  int type, time_t mtime, const char *fmt, ...)
+  int type, utime_t mtime, const char *fmt, ...)
 {
    va_list arg_ptr;
    char buf[2000];