/* * Prune records from database * * prune files (from) client=xxx [pool=yyy] * prune jobs (from) client=xxx [pool=yyy] * prune volume=xxx * prune stats */ int prunecmd(UAContext *ua, const char *cmd) { CLIENTRES *client; POOLRES *pool; POOL_DBR pr; MEDIA_DBR mr; utime_t retention; int kw; static const char *keywords[] = { NT_("Files"), NT_("Jobs"), NT_("Volume"), NT_("Stats"), NULL}; if (!open_client_db(ua)) { return false; } /* First search args */ kw = find_arg_keyword(ua, keywords); if (kw < 0 || kw > 3) { /* no args, so ask user */ kw = do_keyword_prompt(ua, _("Choose item to prune"), keywords); } switch (kw) { case 0: /* prune files */ if (!(client = get_client_resource(ua))) { return false; } if (find_arg_with_value(ua, "pool") >= 0) { pool = get_pool_resource(ua); } else { pool = NULL; } /* Pool File Retention takes precedence over client File Retention */ if (pool && pool->FileRetention > 0) { if (!confirm_retention(ua, &pool->FileRetention, "File")) { return false; } } else if (!confirm_retention(ua, &client->FileRetention, "File")) { return false; } prune_files(ua, client, pool); return true; case 1: /* prune jobs */ if (!(client = get_client_resource(ua))) { return false; } if (find_arg_with_value(ua, "pool") >= 0) { pool = get_pool_resource(ua); } else { pool = NULL; } /* Pool Job Retention takes precedence over client Job Retention */ if (pool && pool->JobRetention > 0) { if (!confirm_retention(ua, &pool->JobRetention, "Job")) { return false; } } else if (!confirm_retention(ua, &client->JobRetention, "Job")) { return false; } /* ****FIXME**** allow user to select JobType */ prune_jobs(ua, client, pool, JT_BACKUP); return 1; case 2: /* prune volume */ if (!select_pool_and_media_dbr(ua, &pr, &mr)) { return false; } if (mr.Enabled == 2) { ua->error_msg(_("Cannot prune Volume \"%s\" because it is archived.\n"), mr.VolumeName); return false; } if (!confirm_retention(ua, &mr.VolRetention, "Volume")) { return false; } prune_volume(ua, &mr); return true; case 3: /* prune stats */ if (!director->stats_retention) { return false; } retention = director->stats_retention; if (!confirm_retention(ua, &retention, "Statistics")) { return false; } prune_stats(ua, retention); return true; default: break; } return true; }
/* * Purge records from database * * Purge Files (from) [Job|JobId|Client|Volume] * Purge Jobs (from) [Client|Volume] * * N.B. Not all above is implemented yet. */ int purgecmd(UAContext *ua, const char *cmd) { int i; CLIENTRES *client; MEDIA_DBR mr; JOB_DBR jr; static const char *keywords[] = { NT_("files"), NT_("jobs"), NT_("volume"), NT_("quota"), NULL }; static const char *files_keywords[] = { NT_("Job"), NT_("JobId"), NT_("Client"), NT_("Volume"), NULL }; static const char *quota_keywords[] = { NT_("Client"), NULL }; static const char *jobs_keywords[] = { NT_("Client"), NT_("Volume"), NULL }; ua->warning_msg(_( "\nThis command can be DANGEROUS!!!\n\n" "It purges (deletes) all Files from a Job,\n" "JobId, Client or Volume; or it purges (deletes)\n" "all Jobs from a Client or Volume without regard\n" "to retention periods. Normally you should use the\n" "PRUNE command, which respects retention periods.\n")); if (!open_db(ua)) { return 1; } switch (find_arg_keyword(ua, keywords)) { /* Files */ case 0: switch(find_arg_keyword(ua, files_keywords)) { case 0: /* Job */ case 1: /* JobId */ if (get_job_dbr(ua, &jr)) { char jobid[50]; edit_int64(jr.JobId, jobid); purge_files_from_jobs(ua, jobid); } return 1; case 2: /* client */ client = get_client_resource(ua); if (client) { purge_files_from_client(ua, client); } return 1; case 3: /* Volume */ if (select_media_dbr(ua, &mr)) { purge_files_from_volume(ua, &mr); } return 1; } /* Jobs */ case 1: switch(find_arg_keyword(ua, jobs_keywords)) { case 0: /* client */ client = get_client_resource(ua); if (client) { purge_jobs_from_client(ua, client); } return 1; case 1: /* Volume */ if (select_media_dbr(ua, &mr)) { purge_jobs_from_volume(ua, &mr, /*force*/true); } return 1; } /* Volume */ case 2: /* Perform ActionOnPurge (action=truncate) */ if (find_arg(ua, "action") >= 0) { return action_on_purge_cmd(ua, ua->cmd); } while ((i=find_arg(ua, NT_("volume"))) >= 0) { if (select_media_dbr(ua, &mr)) { purge_jobs_from_volume(ua, &mr, /*force*/true); } *ua->argk[i] = 0; /* zap keyword already seen */ ua->send_msg("\n"); } return 1; /* Quota */ case 3: switch(find_arg_keyword(ua, quota_keywords)) { case 0: /* client */ client = get_client_resource(ua); if (client) { purge_quota_from_client(ua, client); } return 1; } default: break; } switch (do_keyword_prompt(ua, _("Choose item to purge"), keywords)) { case 0: /* files */ client = get_client_resource(ua); if (client) { purge_files_from_client(ua, client); } break; case 1: /* jobs */ client = get_client_resource(ua); if (client) { purge_jobs_from_client(ua, client); } break; case 2: /* Volume */ if (select_media_dbr(ua, &mr)) { purge_jobs_from_volume(ua, &mr, /*force*/true); } break; case 3: client = get_client_resource(ua); /* Quota */ if (client) { purge_quota_from_client(ua, client); } } return 1; }
/* * Prune records from database * * prune files client=xxx [pool=yyy] * prune jobs client=xxx [pool=yyy] * prune volume=xxx * prune stats * prune directory=xxx [client=xxx] [recursive] */ int prune_cmd(UAContext *ua, const char *cmd) { CLIENTRES *client; POOLRES *pool; POOL_DBR pr; MEDIA_DBR mr; utime_t retention; int kw; static const char *keywords[] = { NT_("Files"), NT_("Jobs"), NT_("Volume"), NT_("Stats"), NT_("Directory"), NULL }; if (!open_client_db(ua, true)) { return false; } /* * First search args */ kw = find_arg_keyword(ua, keywords); if (kw < 0 || kw > 4) { /* * No args, so ask user */ kw = do_keyword_prompt(ua, _("Choose item to prune"), keywords); } switch (kw) { case 0: /* prune files */ if (!(client = get_client_resource(ua))) { return false; } if (find_arg_with_value(ua, NT_("pool")) >= 0) { pool = get_pool_resource(ua); } else { pool = NULL; } /* * Pool File Retention takes precedence over client File Retention */ if (pool && pool->FileRetention > 0) { if (!confirm_retention(ua, &pool->FileRetention, "File")) { return false; } } else if (!confirm_retention(ua, &client->FileRetention, "File")) { return false; } prune_files(ua, client, pool); return true; case 1: { /* prune jobs */ int i; char jobtype[MAX_NAME_LENGTH]; if (!(client = get_client_resource(ua))) { return false; } if (find_arg_with_value(ua, NT_("pool")) >= 0) { pool = get_pool_resource(ua); } else { pool = NULL; } /* * Ask what jobtype to prune. */ if ((i = find_arg_with_value(ua, NT_("jobtype"))) >= 0) { bstrncpy(jobtype, ua->argv[i], sizeof(jobtype)); } else { start_prompt(ua, _("Jobtype to prune:\n")); for (i = 0; jobtypes[i].type_name; i++) { add_prompt(ua, jobtypes[i].type_name); } if (do_prompt(ua, _("JobType"), _("Select Job Type"), jobtype, sizeof(jobtype)) < 0) { return true; } } for (i = 0; jobtypes[i].type_name; i++) { if (bstrcasecmp(jobtypes[i].type_name, jobtype)) { break; } } if (!jobtypes[i].type_name) { ua->warning_msg(_("Illegal jobtype %s.\n"), jobtype); return false; } /* * Pool Job Retention takes precedence over client Job Retention */ if (pool && pool->JobRetention > 0) { if (!confirm_retention(ua, &pool->JobRetention, "Job")) { return false; } } else if (!confirm_retention(ua, &client->JobRetention, "Job")) { return false; } if (jobtypes[i].type_name) { return prune_jobs(ua, client, pool, jobtypes[i].job_type); } return false; } case 2: /* prune volume */ if (!select_pool_and_media_dbr(ua, &pr, &mr)) { return false; } if (mr.Enabled == 2) { ua->error_msg(_("Cannot prune Volume \"%s\" because it is archived.\n"), mr.VolumeName); return false; } if (!confirm_retention(ua, &mr.VolRetention, "Volume")) { return false; } return prune_volume(ua, &mr); case 3: /* prune stats */ if (!me->stats_retention) { return false; } retention = me->stats_retention; if (!confirm_retention(ua, &retention, "Statistics")) { return false; } return prune_stats(ua, retention); case 4: /* prune directory */ if (find_arg_with_value(ua, NT_("client")) >= 0) { if (!(client = get_client_resource(ua))) { return false; } } else { client = NULL; } return prune_directory(ua, client); default: break; } return true; }
/* * Prune Directory meta data records from the database. */ static bool prune_directory(UAContext *ua, CLIENTRES *client) { int i, len; CLIENT_DBR cr; char *prune_topdir = NULL; POOL_MEM query(PM_MESSAGE), temp(PM_MESSAGE); bool recursive = false; bool retval = false; /* * See if a client was selected. */ if (!client) { if (!get_yesno(ua, _("No client restriction given really remove " "directory for all clients (yes/no): ")) || ua->pint32_val == 0) { if (!(client = get_client_resource(ua))) { return false; } } } /* * See if we need to recursively remove all directories under a certain path. */ recursive = find_arg(ua, NT_("recursive")) >= 0; /* * Get the directory to prune. */ i = find_arg_with_value(ua, NT_("directory")); if (i >= 0) { pm_strcpy(temp, ua->argv[i]); } else { if (recursive) { if (!get_cmd(ua, _("Please enter the full path prefix to remove: "), false)) { return false; } } else { if (!get_cmd(ua, _("Please enter the full path to remove: "), false)) { return false; } } pm_strcpy(temp, ua->cmd); } /* * See if the directory ends in a / and escape it for usage in a database query. */ len = strlen(temp.c_str()); if (*(temp.c_str() + len - 1) != '/') { pm_strcat(temp, "/"); len++; } prune_topdir = (char *)malloc(len * 2 + 1); db_escape_string(ua->jcr, ua->db, prune_topdir, temp.c_str(), len); /* * Remove all files in particular directory. */ if (recursive) { Mmsg(query, "DELETE FROM file WHERE pathid IN (" "SELECT pathid FROM path " "WHERE path LIKE '%s%%'" ")", prune_topdir); } else { Mmsg(query, "DELETE FROM file WHERE pathid IN (" "SELECT pathid FROM path " "WHERE path LIKE '%s'" ")", prune_topdir); } if (client) { char ed1[50]; memset(&cr, 0, sizeof(cr)); bstrncpy(cr.Name, client->name(), sizeof(cr.Name)); if (!db_create_client_record(ua->jcr, ua->db, &cr)) { goto bail_out; } Mmsg(temp, " AND JobId IN (" "SELECT JobId FROM Job " "WHERE ClientId=%s" ")", edit_int64(cr.ClientId, ed1)); pm_strcat(query, temp.c_str()); } db_lock(ua->db); db_sql_query(ua->db, query.c_str()); db_unlock(ua->db); /* * If we removed the entries from the file table without limiting it to a * certain client we created orphaned path entries as no one is referencing * them anymore. */ if (!client) { if (!get_yesno(ua, _("Cleanup orphaned path records (yes/no):")) || ua->pint32_val == 0) { retval = true; goto bail_out; } if (recursive) { Mmsg(query, "DELETE FROM path " "WHERE path LIKE '%s%%'", prune_topdir); } else { Mmsg(query, "DELETE FROM path " "WHERE path LIKE '%s'", prune_topdir); } db_lock(ua->db); db_sql_query(ua->db, query.c_str()); db_unlock(ua->db); } retval = true; bail_out: if (prune_topdir) { free(prune_topdir); } return retval; }
/* * Prune records from database * * prune files (from) client=xxx * prune jobs (from) client=xxx * prune volume=xxx * prune stats */ int prunecmd(UAContext *ua, const char *cmd) { DIRRES *dir; CLIENT *client; POOL_DBR pr; MEDIA_DBR mr; utime_t retention; int kw; static const char *keywords[] = { NT_("Files"), NT_("Jobs"), NT_("Volume"), NT_("Stats"), NULL}; if (!open_client_db(ua)) { return false; } /* First search args */ kw = find_arg_keyword(ua, keywords); if (kw < 0 || kw > 3) { /* no args, so ask user */ kw = do_keyword_prompt(ua, _("Choose item to prune"), keywords); } switch (kw) { case 0: /* prune files */ client = get_client_resource(ua); if (!client || !confirm_retention(ua, &client->FileRetention, "File")) { return false; } prune_files(ua, client); return true; case 1: /* prune jobs */ client = get_client_resource(ua); if (!client || !confirm_retention(ua, &client->JobRetention, "Job")) { return false; } /* ****FIXME**** allow user to select JobType */ prune_jobs(ua, client, JT_BACKUP); return 1; case 2: /* prune volume */ if (!select_pool_and_media_dbr(ua, &pr, &mr)) { return false; } if (mr.Enabled == 2) { ua->error_msg(_("Cannot prune Volume \"%s\" because it is archived.\n"), mr.VolumeName); return false; } if (!confirm_retention(ua, &mr.VolRetention, "Volume")) { return false; } prune_volume(ua, &mr); return true; case 3: /* prune stats */ dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL); if (!dir->stats_retention) { return false; } retention = dir->stats_retention; if (!confirm_retention(ua, &retention, "Statistics")) { return false; } prune_stats(ua, retention); return true; default: break; } return true; }