]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
Add FileEvents features
authorEric Bollengier <eric@baculasystems.com>
Wed, 7 Sep 2022 14:44:09 +0000 (16:44 +0200)
committerEric Bollengier <eric@baculasystems.com>
Thu, 14 Sep 2023 11:56:59 +0000 (13:56 +0200)
23 files changed:
bacula/src/cats/bdb.h
bacula/src/cats/cats.h
bacula/src/cats/protos.h
bacula/src/cats/sql_create.c
bacula/src/cats/sql_list.c
bacula/src/dird/catreq.c
bacula/src/dird/getmsg.c
bacula/src/dird/protos.h
bacula/src/dird/ua_output.c
bacula/src/filed/backup.c
bacula/src/filed/fd_plugins.c
bacula/src/filed/fd_plugins.h
bacula/src/filed/job.c
bacula/src/filed/restore.c
bacula/src/filed/verify_vol.c
bacula/src/findlib/bfile.c
bacula/src/jcr.h
bacula/src/plugins/fd/test-plugin-fd.c
bacula/src/stored/append.c
bacula/src/stored/bextract.c
bacula/src/stored/bscan.c
bacula/src/stored/record_util.c
bacula/src/streams.h

index e640d60fb7efeec78e62c45f6f42a73388139420..4de12cdd0b968ae037c97f65e92ac0cf2e9bc824 100644 (file)
@@ -216,6 +216,7 @@ public:
    bool bdb_create_snapshot_record(JCR *jcr, SNAPSHOT_DBR *snap);
    int bdb_create_file_record(JCR *jcr, ATTR_DBR *ar);
    bool bdb_create_batch_file_attributes_record(JCR *jcr, ATTR_DBR *ar);
+   bool bdb_create_fileevent_record(JCR *jcr, FILEEVENT_DBR *event);
 
    /* sql_get.c */
    char *bdb_get_jobids(const char *jobids, POOLMEM **ret, bool append);
@@ -277,6 +278,7 @@ public:
    void bdb_list_fileevents_for_job(JCR *jcr, uint32_t jobid, char etype, DB_LIST_HANDLER sendit, void *ctx, e_list_type type);
    void bdb_list_media_records(JCR *jcr, MEDIA_DBR *mdbr, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
    void bdb_list_jobmedia_records(JCR *jcr, JobId_t JobId, char *volume, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
+   void bdb_list_fileevents_records(JCR *jcr, FILEEVENT_DBR *event, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
    void bdb_list_filemedia_records(JCR *jcr, JobId_t JobId, uint32_t FileIndex, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
    void bdb_list_joblog_records(JCR *jcr, JobId_t JobId, const char *pattern, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
    int  bdb_list_sql_query(JCR *jcr, const char *title, const char *query, DB_LIST_HANDLER *sendit, void *ctx, int verbose, e_list_type type);
index 6de2842a9d7a701e65edfddae6b1627127277b0f..aa13dca0967aee926b20323895ae86d19452cfad 100644 (file)
@@ -713,6 +713,35 @@ public:
    bool check();                /* check if valid */
 };
 
+class FILEEVENT_DBR: public SMARTALLOC
+{
+public:
+   DBId_t FileIndex;            // FileIndex of the File
+   DBId_t JobId;                // JobId of the file
+   DBId_t SourceJobId;          // Verify/Restore JobId
+   char   Type;                 // antivirus, malware, lost file
+   int    Severity;             // level of severity (0 OK, 100 Important)
+   char   Description[MAX_NAME_LENGTH];
+   char   Source[MAX_NAME_LENGTH];
+   FILEEVENT_DBR(): FileIndex(0), JobId(0), Type(0),
+                    Severity(0)
+   {
+      *Description = *Source = 0;
+   };
+   ~FILEEVENT_DBR() {};
+
+   bool unpack(uint32_t stream, const char *data, int data_len) {
+      SourceJobId = JobId = FileIndex = 0;
+      if (scan_string(data, "%c %d %127s %127s 0", &Type, &Severity, Source, Description) == 4) {
+         unbash_spaces(Source);
+         unbash_spaces(Description);
+         return true;
+      }
+      Dmsg1(50, "Got incorrect stream for FileEvent %s\n", data);
+      return false;
+   };
+};
+
 /* Call back context for getting a 32/64 bit value from the database */
 class db_int64_ctx {
 public:
index 2243eabfbd7425fa1b3f82e5763a0c9aad30bbbf..68675d7a7fd2f09bec88cb9a959d40f6580d9849 100644 (file)
@@ -164,7 +164,8 @@ void bdb_free_restoreobject_record(JCR *jcr, ROBJECT_DBR *rr);
            bdb_disable_batch_insert(disable)
 #define db_create_snapshot_record(jcr, mdb, sr) \
            mdb->bdb_create_snapshot_record(jcr, sr)
-
+#define db_create_fileevent_record(jcr, mdb, ev) \
+           mdb->bdb_create_fileevent_record(jcr, ev)
 
 /* sql_delete.c */
 #define db_delete_pool_record(jcr, mdb, pool_dbr) \
@@ -296,6 +297,8 @@ void bdb_free_restoreobject_record(JCR *jcr, ROBJECT_DBR *rr);
            mdb->bdb_list_jobmedia_records(jcr, JobId, volume, sendit, ctx, type)
 #define db_list_filemedia_records(jcr, mdb, JobId, FI, sendit, ctx, type) \
            mdb->bdb_list_filemedia_records(jcr, JobId, FI, sendit, ctx, type)
+#define db_list_fileevents_records(jcr, mdb, event, sendit, ctx, type) \
+           mdb->bdb_list_fileevents_records(jcr, event, sendit, ctx, type)
 #define db_list_joblog_records(jcr, mdb, JobId, pattern, sendit, ctx, type) \
    mdb->bdb_list_joblog_records(jcr, JobId, pattern, sendit, ctx, type)
 #define db_list_sql_query(jcr, mdb, title, query, sendit, ctx, verbose, type) \
index 3a4caaa87f896e101c040551869f4f40b2f59eb3..48a8d76d66dec5d28bef033429a22db503b3cc5d 100644 (file)
@@ -1460,4 +1460,21 @@ bool BDB::bdb_create_tag_record(JCR *jcr, TAG_DBR *tag)
    return ret;
 }
 
+bool BDB::bdb_create_fileevent_record(JCR *jcr, FILEEVENT_DBR *event)
+{
+   char esc1[MAX_ESCAPE_NAME_LENGTH];
+   char esc2[MAX_ESCAPE_NAME_LENGTH];
+   bool ret=false;
+
+   bdb_lock();
+   bdb_escape_string(jcr, esc1, event->Description, strlen(event->Description));
+   bdb_escape_string(jcr, esc2, event->Source, strlen(event->Source));
+   Mmsg(cmd, "INSERT INTO FileEvents (SourceJobId, JobId, FileIndex, Type, Description, Severity, Source) "
+        " VALUES (%lu, %lu, %ld, '%c', '%s', %d, '%s')",
+        event->SourceJobId, event->JobId, event->FileIndex, event->Type, esc1, event->Severity, esc2);
+   ret = bdb_sql_query(cmd, NULL, (void *)NULL);
+   bdb_unlock();
+   return ret;
+}
+
 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */
index d0ac2cf77e382fc0074dcd8f5cd0cb8a2b6ebda7..561a239add71ac2426275fcf30f7d5747eebf407 100644 (file)
@@ -645,6 +645,53 @@ void BDB::bdb_list_filemedia_records(JCR *jcr, uint32_t JobId, uint32_t FileInde
    bdb_unlock();
 }
 
+/* List FileEvents records for a given job/file */
+void BDB::bdb_list_fileevents_records(JCR *jcr, FILEEVENT_DBR *rec,
+                                      DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
+{
+   POOL_MEM tmp, filter;
+   char ed1[50];
+
+   Mmsg(filter, "FileEvents.JobId=%s ", edit_int64(rec->JobId, ed1));
+
+   if (rec->FileIndex > 0) {
+      Mmsg(tmp, "AND FileEvents.FileIndex=%s ", edit_int64(rec->FileIndex, ed1));
+      pm_strcat(filter, tmp.c_str());
+   }
+
+   if (B_ISALPHA(rec->Type)) {
+      Mmsg(tmp, "AND FileEvents.Type='%c' ", rec->Type);
+      pm_strcat(filter, tmp.c_str());
+   }
+
+   if (rec->Severity > 0) {
+      Mmsg(tmp, "AND FileEvents.Severity >= %d ", rec->Severity);
+      pm_strcat(filter, tmp.c_str());
+   }
+   
+   bdb_lock();
+   const char *where = get_acls(DB_ACL_BIT(DB_ACL_JOB) | DB_ACL_BIT(DB_ACL_CLIENT), false);
+   const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_CLIENT)) : "";
+
+   if (type == VERT_LIST || type == JSON_LIST) {
+      Mmsg(cmd, "SELECT JobId,FileIndex,Path,Filename,Source,Severity,Type,Description "
+           "FROM FileEvents JOIN File USING (Jobid, FileIndex) JOIN Path USING (PathId) %s WHERE "
+           "%s %s ORDER BY JobId, FileIndex ASC", join, filter.c_str(), where);
+   } else {
+      Mmsg(cmd, "SELECT JobId,Path,Filename,Severity,Type,Description "
+           "FROM FileEvents JOIN File USING (Jobid, FileIndex) JOIN Path USING (PathId) %s WHERE "
+           "%s %s ORDER BY JobId, FileIndex ASC", join, filter.c_str(), where);
+   }
+   if (!QueryDB(jcr, cmd)) {
+      bdb_unlock();
+      return;
+   }
+
+   list_result(jcr, this, "fileevents", sendit, ctx, type);
+
+   sql_free_result();
+   bdb_unlock();
+}
 
 void BDB::bdb_list_copies_records(JCR *jcr, uint32_t limit, char *JobIds,
                             DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
index 1a38b2336e518d65e8e2e8169f17fdbcb7e93a38..d3b14d71001e53d2af2e7d03b59fa8799f778d23 100644 (file)
@@ -49,6 +49,11 @@ static char Update_media[] = "CatReq JobId=%ld UpdateMedia VolName=%s"
    " VolFirstWritten=%lld VolType=%u VolParts=%d VolCloudParts=%d"
    " LastPartBytes=%lld Enabled=%d Recycle=%d\n";
 
+static char FileEvent_add[] = "%c %d %127s %127s 0";
+/* Full format when coming from the Verify Job */
+static char FileEvent_fd_add[] = "CatReq JobId=%ld FileEvent %lu %lu %lu %c %d %127s %127s 0";
+static char FileEvent_fd_add1[] = "CatReq JobId=%ld FileEvent %lu";
+
 static char Create_jobmedia[] = "CatReq JobId=%ld CreateJobMedia\n";
 
 static char Create_filemedia[] = "CatReq JobId=%ld CreateFileMedia\n";
@@ -121,6 +126,55 @@ static int send_volume_info_to_storage_daemon(JCR *jcr, BSOCK *sd, MEDIA_DBR *mr
    return stat;
 }
 
+static bool catreq_fileevent(JCR *jcr, DBId_t FileIndex, const char *p)
+{
+   FILEEVENT_DBR event;
+   bool ok=false;
+   uint32_t t1, t2, t3, t4;
+   event.FileIndex = FileIndex;
+
+   /* If the string is going through the attribute flow, we don't have the CatReq header */
+   if (scan_string(p, FileEvent_add, &event.Type, &event.Severity, event.Source, event.Description) == 4)
+   {
+      event.JobId = jcr->JobId; // Might need to look for previous jobid for example
+      event.FileIndex = FileIndex;
+      ok = true;
+
+   /* We get the VolSessionTime, VolSessionId and FileIndex in the protocol, but we keep it for later */
+   } else if (scan_string(p, FileEvent_fd_add, &t1, &t2, &t3, &t4,
+                          &event.Type, &event.Severity, event.Source, event.Description) == 8)
+   {
+      event.JobId = jcr->previous_jr.JobId;
+      ok = true;
+   }
+   if (ok) {
+      event.SourceJobId = jcr->JobId;
+      unbash_spaces(event.Source);
+      unbash_spaces(event.Description);
+      if (!db_create_fileevent_record(jcr, jcr->db, &event)) {
+         Jmsg(jcr, M_WARNING, 0, _("Catalog error creating FileEvent record. %s"),
+              db_strerror(jcr->db));
+      }
+      return true;
+
+   }  else {
+      Dmsg1(50, "Error while decoding FileEvent %s\n", p);
+   }
+   return false;
+}
+
+/* Can be Snapshot or FileEvent requests from the FD */
+void fd_catreq(JCR *jcr, BSOCK *bs)
+{
+   DBId_t FileIndex=0, xjobid;
+   if (scan_string(bs->msg, FileEvent_fd_add1, &xjobid, &FileIndex) == 2) {
+      catreq_fileevent(jcr, FileIndex, bs->msg);
+
+   } else {
+      snapshot_catreq(jcr, bs);
+   }
+}
+
 void catalog_request(JCR *jcr, BSOCK *bs)
 {
    MEDIA_DBR mr, sdmr;
@@ -697,6 +751,8 @@ static void update_attribute(JCR *jcr, char *msg, int32_t msglen)
       if (!db_create_restore_object_record(jcr, jcr->db, &ro)) {
          Jmsg1(jcr, M_FATAL, 0, _("Restore object create error. %s"), db_strerror(jcr->db));
       }
+   } else if (Stream == STREAM_FILEEVENT) {
+      catreq_fileevent(jcr, FileIndex, p);
 
    } else if (crypto_digest_stream_type(Stream) != CRYPTO_DIGEST_NONE) {
       fname = p;
index a10c7443d4fac8c0c94175bb633a2c21264aa830..b831689ca0751f87caaf4a5882b149407889322e 100644 (file)
@@ -57,7 +57,6 @@ static char Device_update[]   = "DevUpd JobId=%127s "
    "DevWriteBytes=%d\n";
 #endif
 
-
 static char OK_msg[] = "1000 OK\n";
 
 
@@ -268,9 +267,9 @@ int bget_dirmsg(JCR *jcr, BSOCK *bs, BSOCK_CLIENT_TYPE role)
          catalog_request(jcr, bs);
          continue;
       }
-      /* Only the Snapshot commands are authorized for the FD */
+      /* Only the Snapshot and FileEvent commands are authorized for the FD */
       if (role==BSOCK_TYPE_FD && bs->msg[0] == 'C') {
-         snapshot_catreq(jcr, bs);
+         fd_catreq(jcr, bs);
          continue;
       }
       if (role==BSOCK_TYPE_SD && bs->msg[0] == 'U') {        /* SD sending attributes */
index 468e5e4e94d2eb185590633b8c40c270db79e183..9987333b7c874166d94283b5abc6cfd912ccb6a2 100644 (file)
@@ -80,6 +80,7 @@ bool split_bsr_loop(JCR *jcr, bootstrap_info &info);
 
 
 /* catreq.c */
+extern void fd_catreq(JCR *jcr, BSOCK *bs);
 extern void catalog_request(JCR *jcr, BSOCK *bs);
 extern void catalog_update(JCR *jcr, BSOCK *bs);
 extern bool despool_attributes_from_file(JCR *jcr, const char *file);
index 10eff1f5ff54abbc62a42c944b7b2a68ad4c196c..183415de63c100d65d48108b10bdc547a4029915 100644 (file)
@@ -337,6 +337,7 @@ bail_out:
  *  list objects [type=objecttype job_id=id clientname=n,status=S] - list plugin objects
  *  list pluginrestoreconf jobid=x,y,z [id=k]
  *  list filemedia jobid=x fileindex=z
+ *  list fileevents jobid=x fileindex=z
  *  list metadata type=[email|attachment] tenant=xxx owner=xxx jobid=<x,w,z> client=<cli>
  *             from=<str>
  *             to=<str> cc=<str> tags=<str> 
@@ -624,6 +625,27 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
          }
          return 1;
 
+      } else if (strcasecmp(ua->argk[i], NT_("fileevents")) == 0) {
+         FILEEVENT_DBR event;
+         for (j=i+1; j<ua->argc; j++) {
+            if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
+               event.JobId = str_to_int64(ua->argv[j]);
+
+            } else if (strcasecmp(ua->argk[j], NT_("fileindex")) == 0 && ua->argv[j]) {
+               event.FileIndex = str_to_int64(ua->argv[j]);
+
+            } else if (strcasecmp(ua->argk[j], NT_("severity")) == 0 && ua->argv[j]) {
+               event.Severity = str_to_int64(ua->argv[j]);
+
+            } else if (strcasecmp(ua->argk[j], NT_("Type")) == 0 && ua->argv[j]) {
+               event.Type = ua->argv[j][0];
+            }
+         }
+         if (event.JobId) {
+            db_list_fileevents_records(ua->jcr, ua->db, &event, prtit, ua, llist);
+         }
+         return 1;
+
       /* List JOBLOG */
       } else if (strcasecmp(ua->argk[i], NT_("joblog")) == 0) {
          bool done = false;
index 755d3c0fbba12716231936e14d19f33db6a70d28..27f1f07b05157a74694f18f5c19dbc333929a048 100644 (file)
@@ -308,6 +308,41 @@ bail_out:
    return stat;
 }
 
+
+/*
+ * Send the fileevent_pkt to the SD for the record
+ *  Returns: true if OK
+ *           false if error
+ */
+bool fileevent_save(JCR *jcr, fileevent_pkt *fevent)
+{
+   bool stat = false;
+   BSOCK *sd = jcr->store_bsock;
+
+   Dmsg1(50, "Sending STREAM_FILEEVENT %d\n", jcr->JobFiles);
+   stat = sd->fsend("%ld %d 0", jcr->JobFiles, STREAM_FILEEVENT);
+   if (!stat) {
+      goto bail_out;
+   }
+
+   bash_spaces(fevent->Description);
+   bash_spaces(fevent->Source);
+   stat = sd->fsend("%c %d %s %s 0", fevent->Type, fevent->Severity, fevent->Source, fevent->Description);
+   if (!stat) {
+      goto bail_out;
+   }
+   sd->signal(BNET_EOD); /* indicate end of attributes data */
+
+bail_out:
+   if (!stat) {
+      if (!jcr->is_canceled() && !jcr->is_incomplete()) {
+         Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
+               sd->bstrerror());
+      }
+   }
+   return stat;
+}
+
 /**
  * Called here by find() for each file included.
  *   This is a callback. The original is find_files() above.
@@ -644,6 +679,17 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
       goto bail_out;
    }
 
+   /*
+    * Handle FileEvent that might have been generated during the backup
+    * of the file.
+    */
+   struct fileevent_pkt *fevent;
+   foreach_alist(fevent, jcr->fileevents) {
+      if (!fileevent_save(jcr, fevent)) {
+         break;
+      }
+   }
+
 good_rtn:
    rtnstat = 1;
 
index a229f728f41ba5dadf9ce5a0e37a836203692d90..d9d36cf0de45125a51a04d0b4bbee85e6b1dcae9 100644 (file)
@@ -77,6 +77,7 @@ 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);
 static bRC baculaAddPlugin(bpContext *ctx, const char *file);
+static bRC baculaAddFileEvent(bpContext *ctx, struct fileevent_pkt *event);
 
 /*
  * These will be plugged into the global pointer structure for
@@ -117,7 +118,8 @@ static bFuncs bfuncs = {
    baculaCheckChanges,
    baculaAcceptFile,
    baculaAccurateAttribs,
-   baculaAddPlugin
+   baculaAddPlugin,
+   baculaAddFileEvent
 };
 
 /*
@@ -2312,6 +2314,30 @@ static bRC baculaAddInclude(bpContext *ctx, const char *file)
    return bRC_OK;
 }
 
+/**
+ * Let the plugin create FileEvent to report problems on files
+ */
+static bRC baculaAddFileEvent(bpContext *ctx, struct fileevent_pkt *event)
+{
+   JCR *jcr;
+   bacula_ctx *bctx;
+   struct fileevent_pkt *e;
+   Dsm_check(999);
+   if (!is_ctx_good(ctx, jcr, bctx)) {
+      return bRC_Error;
+   }
+   // Sanity check
+   if (!event) {
+      return bRC_Error;
+   }
+   e = (struct fileevent_pkt*) malloc(sizeof(struct fileevent_pkt));
+   memcpy(e, event, sizeof(struct fileevent_pkt));
+   jcr->fileevents->append(e);
+   
+   Dsm_check(999);
+   return bRC_OK;
+}
+
 /**
  * Let the plugin define plugin to be included
  *  from the main backup.
index 3b788046e65e7c75488fc5bc1df89c7367a4087c..b43d1f92e1def4e43fd1a01d36a15091299b8538 100644 (file)
@@ -554,7 +554,7 @@ typedef struct s_baculaFuncs {
    bRC (*AcceptFile)(bpContext *ctx, struct save_pkt *sp); /* Need fname and statp */
    bRC (*getAccurateAttribs)(bpContext *ctx, accurate_attribs_pkt *att);
    bRC (*AddPlugin)(bpContext *ctx, const char *file);
-
+   bRC (*AddFileEvent)(bpContext *ctx, struct fileevent_pkt *ev);
 } bFuncs;
 
 
@@ -573,7 +573,7 @@ typedef enum {
 
 #define FD_PLUGIN_MAGIC  "*FDPluginData*"
 
-#define FD_PLUGIN_INTERFACE_VERSION  ( 22 )
+#define FD_PLUGIN_INTERFACE_VERSION  ( 23 + BEEF )
 
 typedef struct s_pluginInfo {
    uint32_t size;
@@ -628,6 +628,16 @@ typedef struct s_pluginFuncs {
 #define plug_func(plugin) ((pFuncs *)(plugin->pfuncs))
 #define plug_info(plugin) ((pInfo *)(plugin->pinfo))
 
+#define FILEEVENT_TYPE_ANTIVIRUS 'a'
+
+struct fileevent_pkt {
+   int32_t FileIndex;
+   char    Type;
+   int     Severity;
+   char    Description[MAX_NAME_LENGTH];
+   char    Source[MAX_NAME_LENGTH];
+};
+
 #ifdef __cplusplus
 }
 #endif
index 8ad7a6dbab318598b8ddbf67aaa5234f3f6bbc7f..f318cf6d57070627f5eb60cfb70264e3da4fa143 100644 (file)
@@ -264,6 +264,7 @@ JCR *new_fd_jcr()
    jcr->ff = init_find_files();
    jcr->start_time = time(NULL);
    jcr->RunScripts = New(alist(10, not_owned_by_alist));
+   jcr->fileevents = New(alist(5, owned_by_alist));
    jcr->last_fname = get_pool_memory(PM_FNAME);
    jcr->last_fname[0] = 0;
    jcr->client_name = get_memory(strlen(my_name) + 1);
@@ -3576,6 +3577,7 @@ void filed_free_jcr(JCR *jcr)
    bdelete_and_null(jcr->plugin_options_list);
    free_plugins(jcr);                 /* release instantiated plugins */
 
+   delete jcr->fileevents;
    free_runscripts(jcr->RunScripts);
    delete jcr->RunScripts;
    free_path_list(jcr);
index 67ec493ffaf013a87a148a8425b351487ed93d95..ed14c2fd7f991744090cd6b54cd24151344cc0f0 100644 (file)
@@ -1059,6 +1059,7 @@ void do_restore(JCR *jcr)
          plugin_name_stream(jcr, bmsg->rbuf);
          break;
 
+      case STREAM_FILEEVENT:
       case STREAM_RESTORE_OBJECT:
          break;                    /* these are sent by Director */
 
index 497e30c4387dce95703cfdf38642b624fa80ca5b..5adf8d5dc75ea374040dab0edd56602c5b5cded7 100644 (file)
@@ -248,6 +248,31 @@ bool v_ctx::close_previous_stream()
    return rtn;
 }
 
+static bool fileevent_dir_save(JCR *jcr, fileevent_pkt *fevent)
+{
+   bool stat = false;
+   BSOCK *dir = jcr->dir_bsock;
+
+   bash_spaces(fevent->Description);
+   bash_spaces(fevent->Source);
+   /* We keep some space for SessionTime and SessionId */
+   stat = dir->fsend("CatReq JobId=%lu FileEvent %lu 0 0 %c %d %s %s 0",
+                     jcr->JobId, fevent->FileIndex, fevent->Type, fevent->Severity,
+                     fevent->Source, fevent->Description);
+   if (!stat) {
+      goto bail_out;
+   }
+
+bail_out:
+   if (!stat) {
+      if (!jcr->is_canceled() && !jcr->is_incomplete()) {
+         Jmsg1(jcr, M_FATAL, 0, _("Network send error to DIR. ERR=%s\n"),
+               dir->bstrerror());
+      }
+   }
+   return stat;
+}
+
 /*
  * Verify attributes or data of the requested files on the Volume
  *
@@ -446,6 +471,9 @@ void do_verify_volume(JCR *jcr)
          jcr->unlock();
          break;
 
+      case STREAM_FILEEVENT:
+         break;                 // information only
+
       case STREAM_PLUGIN_META_BLOB:
       case STREAM_PLUGIN_META_CATALOG:
          //TODO Add some metadata verification when it is possible
@@ -619,6 +647,17 @@ void do_verify_volume(JCR *jcr)
    if (!accurate_finish(jcr)) {
       goto bail_out;
    }
+   /*
+    * Handle FileEvent that might have been generated during the verification
+    * of the file.
+    */
+   struct fileevent_pkt *fevent;
+   foreach_alist(fevent, jcr->fileevents) {
+      if (!fileevent_dir_save(jcr, fevent)) {
+         break;
+      }
+   }
+
    jcr->setJobStatus(JS_Terminated);
    goto ok_out;
 
index 4c3df3fc19191b6bef728c3a612379a38125ee9e..2477be0381323f131b4c6b3b7a45118c7f7aa068 100644 (file)
@@ -218,6 +218,8 @@ const char *stream_to_ascii(int stream)
          return _("Plugin Metadata Blob");
       case STREAM_PLUGIN_META_CATALOG:
          return _("Plugin Metadata Catalog");
+      case STREAM_FILEEVENT:
+         return _("FileEvent");
       default:
          sprintf(buf, "%d", stream);
          return (const char *)buf;
index 83d85d31bfa45e2abbc5f519efd3327bab081cb8..4e657d6f6af2fb03790715230673e4f55086320b 100644 (file)
@@ -517,6 +517,7 @@ public:
    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 */
+   alist *fileevents;                  /* list of the current file events to record and send to the DIR */
 #endif /* FILE_DAEMON */
 
 
index bbeb8000a8f467f08c38eb7633570c4c8f3cabfd..54e4ece9ccdb4569cecb0db4bc7564addc7940ca 100644 (file)
@@ -942,6 +942,16 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io)
          p_ctx->fd = NULL;
       }
       io->status = 0;
+      {
+         struct fileevent_pkt event;
+         bstrncpy(event.Description, "A good description", sizeof(event.Description));
+         bstrncpy(event.Source, "A very good source", sizeof(event.Source));
+         event.Type = 'c';
+         event.Severity = 100;
+         bfuncs->AddFileEvent(ctx, &event);
+         bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "Adding FileEvent\n");
+      }
+
       break;
 
    case IO_SEEK:
index 4c9c76348e85b67c8f0b39f5b822405fafd10faa..4ec9555aef48bc2ff32209091fcbca834db6189c 100644 (file)
@@ -440,7 +440,8 @@ fi_checked:
 /* Send attributes and digest to Director for Catalog */
 bool send_attrs_to_dir(JCR *jcr, DEV_RECORD *rec)
 {
-   if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES    ||
+   if (rec->maskedStream == STREAM_FILEEVENT ||
+       rec->maskedStream == STREAM_UNIX_ATTRIBUTES    ||
        rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX ||
        rec->maskedStream == STREAM_RESTORE_OBJECT     ||
        rec->maskedStream == STREAM_PLUGIN_OBJECT ||
@@ -452,7 +453,7 @@ bool send_attrs_to_dir(JCR *jcr, DEV_RECORD *rec)
          if (are_attributes_spooled(jcr)) {
             dir->set_spooling();
          }
-         Dmsg1(850, "Send attributes to dir. FI=%d\n", rec->FileIndex);
+         Dmsg2(850, "Send attributes to dir. FI=%d Stream=%s\n", rec->FileIndex, stream_to_ascii(rec->Stream));
          if (!dir_update_file_attributes(jcr->dcr, rec)) {
             Jmsg(jcr, M_FATAL, 0, _("Error updating file attributes. ERR=%s\n"),
                dir->bstrerror());
index 4032cfa3425446c98bb0cfca9a617927702ee335..8bed0750f3b28328258438423a7f735024529f89 100644 (file)
@@ -729,6 +729,9 @@ static bool record_cb(DCR *dcr, DEV_RECORD *rec)
       // TODO landonf: Investigate crypto support in the storage daemon
       break;
 
+   case STREAM_FILEEVENT:       // Nothing to do in particular
+      break;
+
    case STREAM_PROGRAM_NAMES:
    case STREAM_PROGRAM_DATA:
       if (!prog_name_msg) {
index 97be7bdef3f035dcaaca1a0777160653b12d5508..4a2b6c0f5640fc0e0fbfd304766003aff04e623c 100644 (file)
@@ -433,6 +433,7 @@ static bool record_cb(DCR *dcr, DEV_RECORD *rec)
    DEV_BLOCK *block = dcr->block;
    POOL_MEM sql_buffer;
    db_int64_ctx jmr_count;
+   FILEEVENT_DBR fevent;
 
    char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
 
@@ -971,6 +972,29 @@ static bool record_cb(DCR *dcr, DEV_RECORD *rec)
       }
       break;
 
+   case STREAM_FILEEVENT:
+      if (verbose > 1) {
+         Pmsg0(000, _("Got FileEvent Stream record.\n"));
+      }
+      if (!fevent.unpack(rec->Stream, rec->data, rec->data_len)) {
+         Emsg0(M_ERROR, 0, _("Unable to decode FileEvent.\n"));
+         break;
+      }
+      mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
+      if (!mjcr) {
+         Pmsg2(000, _("Could not find SessId=%d SessTime=%d for File record.\n"),
+               rec->VolSessionId, rec->VolSessionTime);
+         break;
+      }
+      fevent.SourceJobId = fevent.JobId = mjcr->JobId;
+      fevent.FileIndex = mjcr->JobFiles;
+      if (!db_create_fileevent_record(mjcr, mjcr->db, &fevent)) {
+         Jmsg2(mjcr, M_ERROR, 0, _("Failed to insert FileEvent record for file: %d. err: %s\n"),
+               fevent.FileIndex, db_strerror(db));
+      }
+      mjcr->dec_use_count(); /* Decrease reference counter increased by get_jcr_by_session call */
+      break;
+
    case  STREAM_UNIX_ACCESS_ACL:          /* Deprecated Standard ACL attributes on UNIX */
    case  STREAM_UNIX_DEFAULT_ACL:         /* Deprecated Default ACL attributes on UNIX */
    case  STREAM_HFSPLUS_ATTRIBUTES:
index 51513b2c0c835052da7c9d960de74f923c8fbd27..bd24d896b9dac1efdc5e5712ff47a3b9dca73eee 100644 (file)
@@ -161,7 +161,8 @@ const char *stream_to_ascii(char *buf, int stream, int fi)
          return "contADATA-BLOCK-HEADER";
       case STREAM_ADATA_RECORD_HEADER:
          return "contADATA-RECORD-HEADER";
-
+      case STREAM_FILEEVENT:
+         return _("FileEvent");
       default:
          sprintf(buf, "%d", -stream);
          return buf;
@@ -239,6 +240,8 @@ const char *stream_to_ascii(char *buf, int stream, int fi)
       return "ADATA-BLOCK-HEADER";
    case STREAM_ADATA_RECORD_HEADER:
       return "ADATA-RECORD-HEADER";
+   case STREAM_FILEEVENT:
+      return _("FileEvent");
    default:
       sprintf(buf, "%d", stream);
       return buf;
index 9b356b6b01302cffe43fca3ae62289078484eb62..dc155f582b655817d2da4f5b29fbec8e9da7804e 100644 (file)
@@ -90,6 +90,7 @@
 #define STREAM_PLUGIN_NAME                 26    /* Plugin "file" string */
 #define STREAM_PLUGIN_DATA                 27    /* Plugin specific data */
 #define STREAM_RESTORE_OBJECT              28    /* Plugin restore object */
+
 /*
  * Non-gzip compressed streams. Those streams can handle arbitrary
  *  compression algorithm data as an additional header is stored
 #define STREAM_PLUGIN_META_BLOB                35    /* Plugin metadata (blob) for file being backed up */
 #define STREAM_PLUGIN_META_CATALOG             36    /* Plugin metadata (to be stored in catalog) for file being backed up */
 #define STREAM_UNIX_ATTRIBUTE_UPDATE           37    /* File's updated metadata */
+#define STREAM_FILEEVENT                       38    /* FileEvent associated with the current object */
 
 #define STREAM_ADATA_BLOCK_HEADER             200    /* Adata block header */
 #define STREAM_ADATA_RECORD_HEADER            201    /* Adata record header */