/*
Bacula® - The Network Backup Solution
- Copyright (C) 2002-2007 Free Software Foundation Europe e.V.
+ Copyright (C) 2002-2008 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.
This program is Free Software; you can redistribute it and/or
modify it under the terms of version two of the GNU General Public
- License as published by the Free Software Foundation plus additions
- that are listed in the file LICENSE.
+ License as published by the Free Software Foundation and included
+ in the file LICENSE.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
- Bacula® is a registered trademark of John Walker.
+ Bacula® is a registered trademark of Kern Sibbald.
The licensor of Bacula is the Free Software Foundation Europe
(FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
Switzerland, email:ftf@fsfeurope.org.
NT_("Volume"),
NULL};
- bsendmsg(ua, _(
+ ua->warning_msg(_(
"\nThis command is can be DANGEROUS!!!\n\n"
"It purges (deletes) all Files from a Job,\n"
"JobId, Client or Volume; or it purges (deletes)\n"
return 1;
case 1: /* Volume */
if (select_media_dbr(ua, &mr)) {
- purge_jobs_from_volume(ua, &mr);
+ purge_jobs_from_volume(ua, &mr, /*force*/true);
}
return 1;
}
case 2:
while ((i=find_arg(ua, NT_("volume"))) >= 0) {
if (select_media_dbr(ua, &mr)) {
- purge_jobs_from_volume(ua, &mr);
+ purge_jobs_from_volume(ua, &mr, /*force*/true);
}
*ua->argk[i] = 0; /* zap keyword already seen */
- bsendmsg(ua, "\n");
+ ua->send_msg("\n");
}
return 1;
default:
break;
case 2: /* Volume */
if (select_media_dbr(ua, &mr)) {
- purge_jobs_from_volume(ua, &mr);
+ purge_jobs_from_volume(ua, &mr, /*force*/true);
}
break;
}
del.max_ids = 1000;
del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids);
- bsendmsg(ua, _("Begin purging files for Client \"%s\"\n"), cr.Name);
+ ua->info_msg(_("Begin purging files for Client \"%s\"\n"), cr.Name);
Mmsg(query, select_jobsfiles_from_client, edit_int64(cr.ClientId, ed1));
Dmsg1(050, "select sql=%s\n", query.c_str());
purge_files_from_job_list(ua, del);
if (del.num_ids == 0) {
- bsendmsg(ua, _("No Files found for client %s to purge from %s catalog.\n"),
+ ua->warning_msg(_("No Files found for client %s to purge from %s catalog.\n"),
client->name(), client->catalog->name());
} else {
- bsendmsg(ua, _("Files for %d Jobs for client \"%s\" purged from %s catalog.\n"), del.num_ids,
+ ua->info_msg(_("Files for %d Jobs for client \"%s\" purged from %s catalog.\n"), del.num_ids,
client->name(), client->catalog->name());
}
del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids);
del.PurgedFiles = (char *)malloc(del.max_ids);
- bsendmsg(ua, _("Begin purging jobs from Client \"%s\"\n"), cr.Name);
+ ua->info_msg(_("Begin purging jobs from Client \"%s\"\n"), cr.Name);
Mmsg(query, select_jobs_from_client, edit_int64(cr.ClientId, ed1));
Dmsg1(150, "select sql=%s\n", query.c_str());
purge_job_list_from_catalog(ua, del);
if (del.num_ids == 0) {
- bsendmsg(ua, _("No Files found for client %s to purge from %s catalog.\n"),
+ ua->warning_msg(_("No Files found for client %s to purge from %s catalog.\n"),
client->name(), client->catalog->name());
} else {
- bsendmsg(ua, _("%d Jobs for client %s purged from %s catalog.\n"), del.num_ids,
+ ua->info_msg(_("%d Jobs for client %s purged from %s catalog.\n"), del.num_ids,
client->name(), client->catalog->name());
}
}
}
+/*
+ * Change the type of the next copy job to backup.
+ * We need to upgrade the next copy of a normal job,
+ * and also upgrade the next copy when the normal job
+ * already have been purged.
+ *
+ * JobId: 1 PriorJobId: 0 (original)
+ * JobId: 2 PriorJobId: 1 (first copy)
+ * JobId: 3 PriorJobId: 1 (second copy)
+ *
+ * JobId: 2 PriorJobId: 1 (first copy, now regular backup)
+ * JobId: 3 PriorJobId: 1 (second copy)
+ *
+ * => Search through PriorJobId in jobid and
+ * PriorJobId in PriorJobId (jobid)
+ */
+void upgrade_copies(UAContext *ua, char *jobs)
+{
+ POOL_MEM query(PM_MESSAGE);
+
+ db_lock(ua->db);
+ /* Do it in two times for mysql */
+ Mmsg(query, "CREATE TEMPORARY TABLE cpy_tmp AS "
+ "SELECT MIN(JobId) AS JobId FROM Job " /* Choose the oldest job */
+ "WHERE Type='%c' "
+ "AND ( PriorJobId IN (%s) "
+ "OR "
+ " PriorJobId IN ( "
+ "SELECT PriorJobId "
+ "FROM Job "
+ "WHERE JobId IN (%s) "
+ " AND Type='B' "
+ ") "
+ ") "
+ "GROUP BY PriorJobId ", /* one result per copy */
+ JT_JOB_COPY, jobs, jobs);
+ db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
+ Dmsg1(050, "Upgrade copies Log sql=%s\n", query.c_str());
+
+ /* Now upgrade first copy to Backup */
+ Mmsg(query, "UPDATE Job SET Type='B' " /* JT_JOB_COPY => JT_BACKUP */
+ "WHERE JobId IN ( SELECT JobId FROM cpy_tmp )");
+
+ db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
+
+ Mmsg(query, "DROP TABLE cpy_tmp");
+ db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
+
+ db_unlock(ua->db);
+}
+
/*
* Remove all records from catalog for a list of JobIds
*/
db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
Dmsg1(050, "Delete Log sql=%s\n", query.c_str());
+ upgrade_copies(ua, jobs);
+
/* Now remove the Job record itself */
Mmsg(query, "DELETE FROM Job WHERE JobId IN (%s)", jobs);
db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
+
Dmsg1(050, "Delete Job sql=%s\n", query.c_str());
}
-
void purge_files_from_volume(UAContext *ua, MEDIA_DBR *mr )
{} /* ***FIXME*** implement */
* Returns: 1 if Volume purged
* 0 if Volume not purged
*/
-bool purge_jobs_from_volume(UAContext *ua, MEDIA_DBR *mr)
+bool purge_jobs_from_volume(UAContext *ua, MEDIA_DBR *mr, bool force)
{
POOL_MEM query(PM_MESSAGE);
struct del_ctx del;
strcmp(mr->VolStatus, "Used") == 0 ||
strcmp(mr->VolStatus, "Error") == 0;
if (!stat) {
- bsendmsg(ua, "\n");
- bsendmsg(ua, _("Volume \"%s\" has VolStatus \"%s\" and cannot be purged.\n"
+ ua->error_msg(_("\nVolume \"%s\" has VolStatus \"%s\" and cannot be purged.\n"
"The VolStatus must be: Append, Full, Used, or Error to be purged.\n"),
mr->VolumeName, mr->VolStatus);
- goto bail_out;
+ return 0;
}
memset(&jr, 0, sizeof(jr));
Mmsg(query, "SELECT DISTINCT JobId FROM JobMedia WHERE MediaId=%s",
edit_int64(mr->MediaId, ed1));
if (!db_sql_query(ua->db, query.c_str(), file_delete_handler, (void *)&del)) {
- bsendmsg(ua, "%s", db_strerror(ua->db));
+ ua->error_msg("%s", db_strerror(ua->db));
Dmsg0(050, "Count failed\n");
goto bail_out;
}
purge_job_list_from_catalog(ua, del);
- bsendmsg(ua, _("%d File%s on Volume \"%s\" purged from catalog.\n"), del.num_del,
+ ua->info_msg(_("%d File%s on Volume \"%s\" purged from catalog.\n"), del.num_del,
del.num_del==1?"":"s", mr->VolumeName);
- purged = is_volume_purged(ua, mr);
+ purged = is_volume_purged(ua, mr, force);
bail_out:
if (del.JobId) {
*
* Returns: true if volume purged
* false if not
+ *
+ * Note, we normally will not purge a volume that has Firstor LastWritten
+ * zero, because it means the volume is most likely being written
+ * however, if the user manually purges using the purge command in
+ * the console, he has been warned, and we go ahead and purge
+ * the volume anyway, if possible).
*/
-bool is_volume_purged(UAContext *ua, MEDIA_DBR *mr)
+bool is_volume_purged(UAContext *ua, MEDIA_DBR *mr, bool force)
{
POOL_MEM query(PM_MESSAGE);
struct s_count_ctx cnt;
bool purged = false;
char ed1[50];
+ if (!force && (mr->FirstWritten == 0 || mr->LastWritten == 0)) {
+ goto bail_out; /* not written cannot purge */
+ }
+
if (strcmp(mr->VolStatus, "Purged") == 0) {
purged = true;
goto bail_out;
}
+
/* If purged, mark it so */
cnt.count = 0;
Mmsg(query, "SELECT count(*) FROM JobMedia WHERE MediaId=%s",
edit_int64(mr->MediaId, ed1));
if (!db_sql_query(ua->db, query.c_str(), del_count_handler, (void *)&cnt)) {
- bsendmsg(ua, "%s", db_strerror(ua->db));
+ ua->error_msg("%s", db_strerror(ua->db));
Dmsg0(050, "Count failed\n");
goto bail_out;
}
if (cnt.count == 0) {
- bsendmsg(ua, _("There are no more Jobs associated with Volume \"%s\". Marking it purged.\n"),
+ ua->warning_msg(_("There are no more Jobs associated with Volume \"%s\". Marking it purged.\n"),
mr->VolumeName);
if (!(purged = mark_media_purged(ua, mr))) {
- bsendmsg(ua, "%s", db_strerror(ua->db));
+ ua->error_msg("%s", db_strerror(ua->db));
}
}
bail_out:
}
pm_strcpy(jcr->VolumeName, mr->VolumeName);
generate_job_event(jcr, "VolumePurged");
+ generate_plugin_event(jcr, bEventVolumePurged);
/*
* If the RecyclePool is defined, move the volume there
*/
{
/* check if destination pool size is ok */
if (newpr.MaxVols > 0 && newpr.NumVols >= newpr.MaxVols) {
- bsendmsg(ua, _("Unable move recycled Volume in full "
+ ua->error_msg(_("Unable move recycled Volume in full "
"Pool \"%s\" MaxVols=%d\n"),
newpr.Name, newpr.MaxVols);
update_vol_pool(ua, newpr.Name, mr, &oldpr);
}
} else {
- bsendmsg(ua, "%s", db_strerror(ua->db));
+ ua->error_msg("%s", db_strerror(ua->db));
}
}
/* Send message to Job report, if it is a *real* job */
if (jcr && jcr->JobId > 0) {
- Jmsg1(jcr, M_INFO, 0, _("All records pruned from Volume \"%s\"; marking it \"Purged\"\n"),
+ Jmsg(jcr, M_INFO, 0, _("All records pruned from Volume \"%s\"; marking it \"Purged\"\n"),
mr->VolumeName);
}
return true;
} else {
- bsendmsg(ua, _("Cannot purge Volume with VolStatus=%s\n"), mr->VolStatus);
+ ua->error_msg(_("Cannot purge Volume with VolStatus=%s\n"), mr->VolStatus);
}
return strcmp(mr->VolStatus, "Purged") == 0;
}