From 9b6ea3a965af2695754fa47f39028e35988cfe4f Mon Sep 17 00:00:00 2001
From: Jakub Hradil <jkhradil@gmail.com>
Date: Tue, 27 May 2014 23:34:29 +0200
Subject: [PATCH] Fix for purge command to purge and truncate volumes in a
 single run

---
 src/cats/protos.h   |  2 +-
 src/cats/sql_get.c  | 25 ++++++++++++++++++-------
 src/dird/ua_purge.c | 41 ++++++++++++++++++++++++++++++++++-------
 3 files changed, 53 insertions(+), 15 deletions(-)

diff --git a/src/cats/protos.h b/src/cats/protos.h
index fcca71c..3db8123 100644
--- a/src/cats/protos.h
+++ b/src/cats/protos.h
@@ -91,7 +91,7 @@ int db_get_num_media_records(JCR *jcr, B_DB *mdb);
 int db_get_num_pool_records(JCR *jcr, B_DB *mdb);
 int db_get_pool_ids(JCR *jcr, B_DB *mdb, int *num_ids, DBId_t **ids);
 bool db_get_client_ids(JCR *jcr, B_DB *mdb, int *num_ids, DBId_t **ids);
-bool db_get_media_ids(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr, int *num_ids, uint32_t **ids);
+bool db_get_media_ids(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr, POOL_MEM *volumes, int *num_ids, uint32_t **ids);
 int db_get_job_volume_parameters(JCR *jcr, B_DB *mdb, JobId_t JobId, VOL_PARAMS **VolParams);
 bool db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cdbr);
 bool db_get_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr);
diff --git a/src/cats/sql_get.c b/src/cats/sql_get.c
index 057e5f2..b4df0d2 100644
--- a/src/cats/sql_get.c
+++ b/src/cats/sql_get.c
@@ -861,14 +861,15 @@ int db_get_num_media_records(JCR *jcr, B_DB *mdb)
 
 /**
  * This function returns a list of all the Media record ids for
- *     the current Pool, the correct Media Type, Recyle, Enabled, StorageId, VolBytes
- *     VolumeName if specified
+ *     the current Pool, the correct Media Type, Recyle, Enabled, StorageId, VolBytes and
+ *     volumes or VolumeName if specified
+ *  Comma separated list of volumes takes precedence over VolumeName.
  *  The caller must free ids if non-NULL.
  *
  *  Returns false: on failure
  *          true:  on success
  */
-bool db_get_media_ids(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr, int *num_ids, uint32_t *ids[])
+bool db_get_media_ids(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr, POOL_MEM *volumes, int *num_ids, uint32_t *ids[])
 {
    SQL_ROW row;
    int i = 0;
@@ -877,10 +878,15 @@ bool db_get_media_ids(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr, int *num_ids, uint32_t
    bool ok = false;
    char buf[MAX_NAME_LENGTH*3]; /* Can contain MAX_NAME_LENGTH*2+1 + AND ....='' */
    char esc[MAX_NAME_LENGTH*2+1];
+   bool have_volumes = false;
 
    db_lock(mdb);
    *ids = NULL;
 
+   if (volumes != NULL) {
+      if (*volumes->c_str() != 0) have_volumes = true;
+   }
+
    Mmsg(mdb->cmd, "SELECT DISTINCT MediaId FROM Media WHERE Recycle=%d AND Enabled=%d ",
         mr->Recycle, mr->Enabled);
 
@@ -905,15 +911,20 @@ bool db_get_media_ids(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr, int *num_ids, uint32_t
       pm_strcat(mdb->cmd, buf);
    }
 
-   if (*mr->VolumeName) {
+   if (*mr->VolStatus) {
+      db_escape_string(jcr, mdb, esc, mr->VolStatus, strlen(mr->VolStatus));
+      bsnprintf(buf, sizeof(buf), "AND VolStatus = '%s' ", esc);
+      pm_strcat(mdb->cmd, buf);
+   }
+
+   if (*mr->VolumeName && !have_volumes) {
       db_escape_string(jcr, mdb, esc, mr->VolumeName, strlen(mr->VolumeName));
       bsnprintf(buf, sizeof(buf), "AND VolumeName = '%s' ", esc);
       pm_strcat(mdb->cmd, buf);
    }
 
-   if (*mr->VolStatus) {
-      db_escape_string(jcr, mdb, esc, mr->VolStatus, strlen(mr->VolStatus));
-      bsnprintf(buf, sizeof(buf), "AND VolStatus = '%s' ", esc);
+   if (have_volumes) {
+      bsnprintf(buf, sizeof(buf), "AND VolumeName IN (%s) ", volumes->c_str());
       pm_strcat(mdb->cmd, buf);
    }
 
diff --git a/src/dird/ua_purge.c b/src/dird/ua_purge.c
index 9f41430..62a4e9b 100644
--- a/src/dird/ua_purge.c
+++ b/src/dird/ua_purge.c
@@ -62,6 +62,8 @@ int purge_cmd(UAContext *ua, const char *cmd)
    CLIENTRES *client;
    MEDIA_DBR mr;
    JOB_DBR  jr;
+   POOLMEM *cmd_holder = get_pool_memory(PM_FNAME);
+
    static const char *keywords[] = {
       NT_("files"),
       NT_("jobs"),
@@ -141,10 +143,8 @@ int purge_cmd(UAContext *ua, const char *cmd)
       }
    /* Volume */
    case 2:
-      /* Perform ActionOnPurge (action=truncate) */
-      if (find_arg(ua, "action") >= 0) {
-         return action_on_purge_cmd(ua, ua->cmd);
-      }
+      /* Store cmd for later reuse */
+      pm_memcpy(cmd_holder, ua->cmd, sizeof_pool_memory(ua->cmd));
 
       while ((i=find_arg(ua, NT_("volume"))) >= 0) {
          if (select_media_dbr(ua, &mr)) {
@@ -152,6 +152,24 @@ int purge_cmd(UAContext *ua, const char *cmd)
          }
          *ua->argk[i] = 0;            /* zap keyword already seen */
          ua->send_msg("\n");
+
+         /*
+          * Add volume=mr.VolumeName to cmd_holder if we have a new volume name from interactive selection.
+          * In certain cases this can produce duplicates, which we don't prevent as there are no side effects.
+          */
+         if (!bstrcmp(ua->cmd, cmd_holder)) {
+            pm_strcat(cmd_holder, " volume=");
+            pm_strcat(cmd_holder, mr.VolumeName);
+         }
+      }
+
+      /* Restore ua args based on cmd_holder */
+      pm_memcpy(ua->cmd, cmd_holder, sizeof_pool_memory(cmd_holder));
+      parse_args(ua->cmd, &ua->args, &ua->argc, ua->argk, ua->argv, MAX_CMD_ARGS);
+
+      /* Perform ActionOnPurge (action=truncate) */
+      if (find_arg(ua, "action") >= 0) {
+         return action_on_purge_cmd(ua, ua->cmd);
       }
       return 1;
    /* Quota */
@@ -191,6 +209,7 @@ int purge_cmd(UAContext *ua, const char *cmd)
          purge_quota_from_client(ua, client);
       }
    }
+   free_pool_memory(cmd_holder);
    return 1;
 }
 
@@ -695,6 +714,9 @@ static int action_on_purge_cmd(UAContext *ua, const char *cmd)
    MEDIA_DBR mr;
    POOL_DBR pr;
    BSOCK *sd = NULL;
+   char buf[MAX_NAME_LENGTH*3]; /* Can contain MAX_NAME_LENGTH*2+1 + AND ....='' */
+   char esc[MAX_NAME_LENGTH*2+1];
+   POOL_MEM volumes(PM_FNAME);
 
    memset(&pr, 0, sizeof(pr));
 
@@ -705,8 +727,13 @@ static int action_on_purge_cmd(UAContext *ua, const char *cmd)
 
       } else if (bstrcasecmp(ua->argk[i], NT_("volume")) &&
                  is_name_valid(ua->argv[i], NULL)) {
-         bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
-
+         db_escape_string(ua->jcr, ua->db, esc, ua->argv[i], strlen(ua->argv[i]));
+         if (*volumes.c_str() == 0) {
+            bsnprintf(buf, sizeof(buf), "'%s'", esc);
+         } else {
+            bsnprintf(buf, sizeof(buf), ",'%s'", esc);
+         }
+         pm_strcat(volumes, buf);
       } else if (bstrcasecmp(ua->argk[i], NT_("devicetype")) &&
                  ua->argv[i]) {
          bstrncpy(mr.MediaType, ua->argv[i], sizeof(mr.MediaType));
@@ -765,7 +792,7 @@ static int action_on_purge_cmd(UAContext *ua, const char *cmd)
    mr.VolBytes = 10000;
    set_storageid_in_mr(store, &mr);
    bstrncpy(mr.VolStatus, "Purged", sizeof(mr.VolStatus));
-   if (!db_get_media_ids(ua->jcr, ua->db, &mr, &nb, &results)) {
+   if (!db_get_media_ids(ua->jcr, ua->db, &mr, &volumes, &nb, &results)) {
       Dmsg0(100, "No results from db_get_media_ids\n");
       goto bail_out;
    }
-- 
1.8.5.3

