]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
dird: Add support for Object Management
authorMichal Rakowski <michal.rakowski@baculasystems.com>
Wed, 14 Oct 2020 14:25:41 +0000 (16:25 +0200)
committerEric Bollengier <eric@baculasystems.com>
Thu, 24 Mar 2022 08:02:57 +0000 (09:02 +0100)
bacula/src/dird/catreq.c
bacula/src/dird/protos.h
bacula/src/dird/ua_cmds.c
bacula/src/dird/ua_output.c
bacula/src/dird/ua_restore.c
bacula/src/dird/ua_tree.c

index 3663f63812875b0dcb78188f9a88f30f14d997be..4a4e120637e10992826972dad8749ea6aeb85c61 100644 (file)
@@ -640,13 +640,13 @@ static void update_attribute(JCR *jcr, char *msg, int32_t msglen)
 
    } else if (Stream == STREAM_PLUGIN_OBJECT) {
       OBJECT_DBR obj_r;
+      obj_r.parse_plugin_object_string(&p);
 
-      skip_nonspaces(&p);                  /* skip FileIndex */
-      skip_spaces(&p);
-      skip_nonspaces(&p);                  /* skip FileType */
-      skip_spaces(&p);
-
-      parse_plugin_object_string(&p, &obj_r);
+      if (jcr->wjcr) {
+         obj_r.JobId = jcr->wjcr->JobId;
+      } else {
+         obj_r.JobId = jcr->JobId;
+      }
 
       if (!db_create_object_record(jcr, jcr->db, &obj_r)) {
          Jmsg1(jcr, M_FATAL, 0, _("Plugin object create error. %s"), db_strerror(jcr->db));
index 7fdbabce606218660c08c319b2c55a14ca65da32..adf26a0af51ab9c791dcb7d17160abbee5aa3dca 100644 (file)
@@ -328,6 +328,7 @@ void list_dir_status_header(UAContext *ua);
 
 /* ua_tree.c */
 bool user_select_files_from_tree(TREE_CTX *tree);
+bool user_select_files_from_tree_plugin_obj(TREE_CTX *tree);
 int insert_tree_handler(void *ctx, int num_fields, char **row);
 bool check_directory_acl(char **last_dir, alist *dir_acl, const char *path);
 
index 0f225dfb7d253beb496904d37baec4b7865cd14b..bb74722c808cd8a386a43f140ffeb483d12b75e1 100644 (file)
@@ -141,7 +141,8 @@ static struct cmdstruct commands[] = {                                      /* C
    NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name> [tag=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [level=<l>] [jobtype=<t>] [order=<asc/desc>] [limit=<n>]|\n"
        "\tjobtotals | pools | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn> |\n"
        "\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot |\n"
-       "\tfilemedia jobid=<nn> fileindex=<mm> | clients\n"), false},
+       "\tfilemedia jobid=<nn> fileindex=<mm> | clients\n"
+      "\tobject [jobid=<jobid> client=<cli> type=<name>  [order=<asc/desc>] [limit=<n>\n"), false},
 
  { NT_("messages"),   messagescmd,   _("Display pending messages"),   NT_(""),    false},
  { NT_("memory"),     memory_cmd,    _("Print current memory usage"), NT_(""),    true},
@@ -162,7 +163,8 @@ static struct cmdstruct commands[] = {                                      /* C
  { NT_("restore"),    restore_cmd,   _("Restore files"),
    NT_("where=</path> client=<client> storage=<storage> bootstrap=<file> "
        "restorejob=<job> restoreclient=<cli> noautoparent"
-       "\n\tcomment=<text> jobid=<jobid> jobuser=<user> jobgroup=<grp> copies done select all"), false},
+       "\n\tcomment=<text> jobid=<jobid> jobuser=<user> jobgroup=<grp> copies done select all"
+       "\n\tobjectid=<objid>"), false},
 
  { NT_("relabel"),    relabel_cmd,   _("Relabel a tape"),
    NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
index 24dd5817250cbc01debc0d9b25b4489104950f6a..28e1a923b9565582bbb9c6896cd6066a8a0d1b9c 100644 (file)
@@ -326,7 +326,7 @@ bail_out:
  *  list nextvol job=xx  - list the next vol to be used by job
  *  list nextvolume job=xx - same as above.
  *  list copies jobid=x,y,z
- *  list objects [type=objecttype] [job_id=n objectid=m] - list plugin objects
+ *  list objects [type=objecttype job_id=id clientname=n] - list plugin objects
  *  list pluginrestoreconf jobid=x,y,z [id=k]
  *  list filemedia jobid=x fileindex=z
  *
@@ -662,32 +662,48 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
          db_free_restoreobject_record(ua->jcr, &rr);
          return 1;
 
+      /* List PLUGIN OBJECTS */
       } else if (strcasecmp(ua->argk[i], NT_("object")) == 0 ||
                  strcasecmp(ua->argk[i], NT_("objects")) == 0) {
-         class OBJECT_DBR obj_r;
-         j = find_arg_with_value(ua, NT_("type"));
-         if (j >= 0) {
-            bstrncpy(obj_r.ObjectType, ua->argv[j], sizeof(obj_r.ObjectType));
-         }
+         OBJECT_DBR obj_r;
 
          for (j=i+1; j<ua->argc; j++) {
-            //TODO job_id arg should be handled different probably because of the collision with the 'list jobid=nn' cmd'
-            if (strcasecmp(ua->argk[j], NT_("job_id")) == 0 && ua->argv[j]) {
-               if (is_a_number(ua->argv[j])) {
+            if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
+               if (is_a_number(ua->argv[j]) && acl_access_jobid_ok(ua, ua->argv[j])) {
                   obj_r.JobId = str_to_uint64(ua->argv[j]);
-               } else {
+                } else {
                   ua->error_msg(_("Invalid jobid argument\n"));
                   return 1;
                }
+
             } else if ((strcasecmp(ua->argk[j], NT_("objectid")) == 0) &&
-                        ua->argv[j])
-            {
+                        ua->argv[j]) {
                if (is_a_number(ua->argv[j])) {
                   obj_r.ObjectId = str_to_uint64(ua->argv[j]);
                } else {
                   ua->error_msg(_("Invalid objectid argument\n"));
                   return 1;
                }
+
+            } else if (strcasecmp(ua->argk[j], NT_("client")) == 0) {
+               if (!acl_access_ok(ua, Client_ACL, ua->argk[j])) {
+                  ua->error_msg(_("Access to Client=%s not authorized.\n"), ua->argk[j]);
+                  return 0;
+               }
+               bstrncpy(obj_r.ClientName, ua->argv[j], sizeof(obj_r.ClientName));
+
+            } else if (strcasecmp(ua->argk[j], NT_("name")) == 0) {
+               bstrncpy(obj_r.ObjectName, ua->argv[j], sizeof(obj_r.ObjectName));
+
+            } else if (strcasecmp(ua->argk[j], NT_("type")) == 0) {
+               bstrncpy(obj_r.ObjectType, ua->argv[j], sizeof(obj_r.ObjectType));
+
+            } else if (strcasecmp(ua->argk[j], NT_("limit")) == 0 && ua->argv[j]) {
+               obj_r.limit = atoi(ua->argv[j]);
+
+            } else if (strcasecmp(ua->argk[j], NT_("order")) == 0 && ua->argv[j]) {
+               /* Other order are tested before */
+               obj_r.order = bstrcasecmp(ua->argv[j], "DESC") == 0;
             }
          }
 
@@ -696,12 +712,8 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
             return 1;
          }
 
-         int k = find_arg_with_value(ua, NT_("type"));
-         if (k >= 0) {
-            bstrncpy(obj_r.ObjectType, ua->argv[k], sizeof(obj_r.ObjectType));
-         }
-
          db_list_plugin_objects(ua->jcr, ua->db, &obj_r, prtit, ua, llist);
+         return 1;
 
       /* List MEDIA or VOLUMES */
       } else if (strcasecmp(ua->argk[i], NT_("media")) == 0 ||
@@ -841,7 +853,10 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
                  || strcasecmp(ua->argk[i], NT_("long")) == 0
                  || strcasecmp(ua->argk[i], NT_("start")) == 0
                  || strcasecmp(ua->argk[i], NT_("end")) == 0
-                 || strcasecmp(ua->argk[i], NT_("name")) == 0
+                 || strcasecmp(ua->argk[i], NT_("objecttype")) == 0
+                 || strcasecmp(ua->argk[i], NT_("objectid")) == 0
+                 || strcasecmp(ua->argk[i], NT_("clientname")) == 0
+                 || strcasecmp(ua->argk[i], NT_("jobid")) == 0
          ) {
          /* Ignore it */
       } else if (strcasecmp(ua->argk[i], NT_("snapshot")) == 0 ||
index 8b38921bbf1d95e0168e19851eb99415cb533417..f271304d9cd73059aa4abbce5aec7665ec64665c 100644 (file)
@@ -269,6 +269,7 @@ int restore_cmd(UAContext *ua, const char *cmd)
       break;
    }
 
+
    if (rx.bsr_list->size() > 0) {
       char ed1[50];
       if (!complete_bsr(ua, rx.bsr_list)) {   /* find Vol, SessId, SessTime from JobIds */
@@ -547,6 +548,76 @@ static int get_restore_client_name(UAContext *ua, RESTORE_CTX &rx, char * Restor
 }
 
 
+static int select_files_from_plugin_obj(UAContext *ua, OBJECT_DBR *obj_r, RESTORE_CTX *rx)
+{
+   if (!db_get_plugin_object_record(ua->jcr, ua->db, obj_r)) {
+      ua->error_msg(_("Failed to get plugin object for specified object parameters\n"));
+      return 0;
+   }
+
+   JOB_DBR jr;
+   memset(&jr, 0, sizeof(JOB_DBR));
+   jr.JobId = obj_r->JobId;
+   if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
+      ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
+                    ua->cmd, db_strerror(ua->db));
+      return 0;
+   }
+
+   if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
+      ua->error_msg(_("Access to JobId=%d (Job \"%s\") not authorized.\n"),
+            jr.JobId, jr.Name);
+      return 0;
+   }
+
+   CLIENT_DBR cr;
+   cr.ClientId = jr.ClientId;
+   if (!db_get_client_record(ua->jcr, ua->db, &cr)) {
+      ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
+                    ua->cmd, db_strerror(ua->db));
+      return 0;
+   }
+
+   if (!acl_access_ok(ua, Client_ACL, cr.Name)) {
+      ua->error_msg(_("Access to ClientId=%d not authorized.\n"),
+            jr.ClientId);
+      return 0;
+   }
+
+   bool dir = obj_r->Filename[0] != 0 ? false : true;
+   if (dir) {
+      pm_strcpy(ua->cmd, obj_r->Path);
+   } else {
+      Mmsg(ua->cmd, "%s%s", obj_r->Path, obj_r->Filename);
+   }
+
+   db_list_ctx jobids;
+   if (!db_get_accurate_jobids(ua->jcr, ua->db, &jr, &jobids)) {
+      return 0;
+   }
+   pm_strcpy(rx->JobIds, jobids.list);
+
+   if (!get_client_name(ua, rx)) {
+      return 0;
+   }
+
+   insert_one_file_or_dir(ua, rx, jr.cStartTime, dir);
+
+   return 1;
+}
+
+static int object_type_hander(void *ctx, int num_fields, char **row)
+{
+   if (num_fields != 2) {
+      return 1;
+   }
+   alist *val = (alist *)ctx;
+
+   val[0].append(bstrdup(row[0]));
+   val[1].append(bstrdup(row[1]));
+
+   return 0;
+}
 
 /*
  * The first step in the restore process is for the user to
@@ -581,6 +652,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
       _("Find the JobIds for a backup for a client before a specified time"),
       _("Enter a list of directories to restore for found JobIds"),
       _("Select full restore to a specified Job date"),
+      _("Select object to restore"),
       _("Cancel"),
       NULL };
 
@@ -619,6 +691,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
 
       "jobuser",       /* 28 */
       "jobgroup",      /* 29 */
+      "objectid",      /* 30 */
       NULL
    };
 
@@ -713,12 +786,22 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
       /*
        * All keywords 7 or greater are ignored or handled by a select prompt
        */
+      case 30:
+         {
+            OBJECT_DBR obj_r;
+            obj_r.ObjectId = str_to_int64(ua->argv[i]);
+            if (!select_files_from_plugin_obj(ua, &obj_r, rx)) {
+               return 0;
+            }
+            return 2;
+         }
       default:
          break;
       }
    }
 
    if (!done) {
+      //TODO Cleanup msg
       ua->send_msg(_("\nFirst you select one or more JobIds that contain files\n"
                   "to be restored. You will be presented several methods\n"
                   "of specifying the JobIds. Then you will be allowed to\n"
@@ -936,7 +1019,100 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
          pm_strcpy(rx->JobIds, jobids.list);
          Dmsg1(30, "Item 12: jobids = %s\n", rx->JobIds);
          break;
-      case 12:                        /* Cancel or quit */
+
+       case 12: /* Select Object to restore */
+         {
+            POOL_MEM query, esc(PM_MESSAGE);
+            gui_save = ua->jcr->gui;
+            ua->jcr->gui = true;
+
+            // Array in form of ObjectCategories (at [0]) and corresponding ObjectTypes (at [1])
+            alist types[2];
+            Mmsg(query, "SELECT DISTINCT ObjectCategory, ObjectType FROM Object ORDER BY ObjectCategory, ObjectType ASC");
+            if (!db_sql_query(ua->db, query.c_str(), object_type_hander, types)) {
+               ua->error_msg(_("SQL Query failed: %s\n"), db_strerror(ua->db));
+               return 0;
+            }
+
+            char *t;
+            POOL_MEM tmp(PM_MESSAGE);
+            start_prompt(ua, _("List of the Object Types:\n"));
+            for (int i=0; i < types[0].size(); i++) {
+               // Build prompt "ObjectType ObjectCategory" - e.g. "PostgreSQL Database"
+               Mmsg(tmp, "%s %s", types[1].get(i), types[0].get(i));
+               add_prompt(ua, tmp.c_str());
+            }
+
+            int idx = do_prompt(ua, "", _("Select item:"), NULL, 0);
+            if (idx < 0) {
+               return 0;
+            }
+
+            alist object_list;
+            alist *alist_ptr = &object_list;
+
+            POOL_MEM esc_cat, esc_type;
+            char *cat = (char*)types[0].get(idx);
+            char *type = (char*)types[1].get(idx);
+            esc_cat.check_size(strlen(cat)*2+1);
+            esc_type.check_size(strlen(type)*2+1);
+
+            /* TODO: Check if we need db_lock() */
+            db_escape_string(ua->jcr, ua->db, esc_cat.c_str(), cat, strlen(cat));
+            db_escape_string(ua->jcr, ua->db, esc_type.c_str(), type, strlen(type));
+
+            Mmsg(query, "SELECT DISTINCT ObjectName FROM Object WHERE ObjectCategory='%s' AND ObjectType='%s' ORDER BY ObjectName ASC",
+                 esc_cat.c_str(), esc_type.c_str());
+
+            if (!db_sql_query(ua->db, query.c_str(), db_string_list_handler, &alist_ptr)) {
+               ua->error_msg(_("SQL Query failed: %s\n"), db_strerror(ua->db));
+               return 0;
+            }
+
+            Mmsg(tmp, "List of the %s %s Objects:\n", types[1].get(idx), types[0].get(idx));
+            start_prompt(ua, tmp.c_str());
+            foreach_alist(t, &object_list) {
+               add_prompt(ua, t);
+            }
+
+            idx = do_prompt(ua, "", _("Select Object:"), NULL, 0);
+            if (idx < 0) {
+               return 0;
+            }
+
+            ua->info_msg(_("Objects available:\n"));
+            esc.check_size(strlen((char*)object_list.get(idx))*2+1);
+            db_escape_string(ua->jcr, ua->db, esc.c_str(),
+                             (char*)object_list.get(idx), strlen((char*)object_list.get(idx)));
+            Mmsg(query, "SELECT Object.ObjectId AS ObjectId, Object.ObjectName AS ObjectName, Client.Name as Client, "
+                                "Object.ObjectSource AS ObjectSource, "
+                               "Job.StartTime AS StartTime, Object.ObjectSize AS ObjectSize "
+                        "FROM Object JOIN Job USING (JobId) "
+                        "JOIN Client ON (Job.ClientId = Client.ClientId) "
+                        "WHERE Object.ObjectName='%s' AND Object.ObjectCategory='%s' AND Object.ObjectType='%s'",
+                 esc.c_str(), esc_cat.c_str(), esc_type.c_str());
+
+            if (!db_list_sql_query(ua->jcr, ua->db, query.c_str(), prtit, ua, 0, HORZ_LIST)) {
+               ua->error_msg(_("SQL Query failed: %s\n"), db_strerror(ua->db));
+               return 0;
+            }
+
+            //TODO Validation if id entered matches one from list above would be nice...
+            if (!get_pint(ua, "Enter ID of Object to be restored: ")) {
+               ua->info_msg(_("Selection aborted, nothing done.\n"));
+               break;
+            }
+
+            OBJECT_DBR obj_r;
+            obj_r.ObjectId = ua->pint32_val;
+            if (!select_files_from_plugin_obj(ua, &obj_r, rx)) {
+               return 0;
+            }
+
+            ua->jcr->gui = gui_save;
+            return 2;
+         }
+      case 13:                        /* Cancel or quit */
          return 0;
       }
    }
index cb23f9a317ccca9bd65b09af5f37ce410a890d99..088268c5dfbb0bff48d3db822c26171e9db026b8 100644 (file)
@@ -60,6 +60,8 @@ static int dot_lscmd(UAContext *ua, TREE_CTX *tree);
 static int dot_helpcmd(UAContext *ua, TREE_CTX *tree);
 static int dot_lsmarkcmd(UAContext *ua, TREE_CTX *tree);
 
+static int set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, bool extract);
+
 struct cmdstruct { const char *key; int (*func)(UAContext *ua, TREE_CTX *tree); const char *help; };
 static struct cmdstruct commands[] = {
  { NT_("add"),        markcmd,      _("add dir/file to be restored recursively, wildcards allowed")},