X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fdird%2Fautoprune.c;h=df4cb5542db2150b1a15cf77cc5269423a87f35a;hb=c205f98aff775e96e18dc0af2f194e76f2ddaaa9;hp=70cef3fe39d35c9e3c37435bf3d56f9c502e9053;hpb=458061b568c4451c7f1d2212879645b2625651fb;p=bacula%2Fbacula diff --git a/bacula/src/dird/autoprune.c b/bacula/src/dird/autoprune.c index 70cef3fe39..df4cb5542d 100644 --- a/bacula/src/dird/autoprune.c +++ b/bacula/src/dird/autoprune.c @@ -1,31 +1,38 @@ /* - * - * Bacula Director -- Automatic Pruning - * Applies retention periods - * - * Kern Sibbald, May MMII - * - * Version $Id$ - */ + Bacula® - The Network Backup Solution -/* - Copyright (C) 2002 Kern Sibbald and John Walker + Copyright (C) 2002-2007 Free Software Foundation Europe e.V. - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. + 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 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 + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public - License along with this program; if not, write to the Free - Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - MA 02111-1307, USA. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + Bacula® is a registered trademark of John Walker. + The licensor of Bacula is the Free Software Foundation Europe + (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, + Switzerland, email:ftf@fsfeurope.org. +*/ +/* + * + * Bacula Director -- Automatic Pruning + * Applies retention periods + * + * Kern Sibbald, May MMII + * + * Version $Id$ */ #include "bacula.h" @@ -34,130 +41,176 @@ /* Forward referenced functions */ -/* - * The pruning code was written to be referenced by the - * User Agent (i.e. the console), so to properly access it and - * to ensure that the Job gets the proper output, we create - * a User Agent context. This is a sort of mini-kludge. - */ -void create_ua_context(JCR *jcr, UAContext *ua) -{ - memset(ua, 0, sizeof(UAContext)); - ua->jcr = jcr; - ua->db = jcr->db; - ua->cmd = get_pool_memory(PM_FNAME); - ua->args = get_pool_memory(PM_FNAME); - ua->verbose = 1; -} - -void free_ua_context(UAContext *ua) -{ - if (ua->cmd) { - free_pool_memory(ua->cmd); - } - if (ua->args) { - free_pool_memory(ua->args); - } -} /* * Auto Prune Jobs and Files. This is called at the end of every * Job. We do not prune volumes here. */ -int do_autoprune(JCR *jcr) +void do_autoprune(JCR *jcr) { - UAContext ua; + UAContext *ua; CLIENT *client; - int pruned; + bool pruned; - if (!jcr->client) { /* temp -- remove me */ - return 1; + if (!jcr->client) { /* temp -- remove me */ + return; } - create_ua_context(jcr, &ua); - + ua = new_ua_context(jcr); client = jcr->client; if (jcr->job->PruneJobs || jcr->client->AutoPrune) { Jmsg(jcr, M_INFO, 0, _("Begin pruning Jobs.\n")); - prune_jobs(&ua, client, jcr->JobType); - pruned = TRUE; + prune_jobs(ua, client, jcr->JobType); + pruned = true; } else { - pruned = FALSE; + pruned = false; } - + if (jcr->job->PruneFiles || jcr->client->AutoPrune) { Jmsg(jcr, M_INFO, 0, _("Begin pruning Files.\n")); - prune_files(&ua, client); - pruned = TRUE; + prune_files(ua, client); + pruned = true; } if (pruned) { Jmsg(jcr, M_INFO, 0, _("End auto prune.\n\n")); } - - free_ua_context(&ua); - return 1; + free_ua_context(ua); + return; } /* - * Prune all volumes in current Pool. This is called from - * catreq.c when the Storage daemon is asking for another + * Prune at least one Volume in current Pool. This is called from + * catreq.c => next_vol.c when the Storage daemon is asking for another * volume and no appendable volumes are available. * - * Return 0: on error - * number of Volumes Purged + * Return: false if nothing pruned + * true if pruned, and mr is set to pruned volume */ -int prune_volumes(JCR *jcr) +bool prune_volumes(JCR *jcr, bool InChanger, MEDIA_DBR *mr) { - int stat = 0; + int count; int i; - uint32_t *ids = NULL; - int num_ids = 0; - MEDIA_DBR mr; - POOL_DBR pr; - UAContext ua; - + dbid_list ids; + struct del_ctx prune_list; + POOL_MEM query(PM_MESSAGE); + UAContext *ua; + bool ok = false; + char ed1[50], ed2[100], ed3[50]; + POOL_DBR spr; + + Dmsg1(050, "Prune volumes PoolId=%d\n", jcr->jr.PoolId); if (!jcr->job->PruneVolumes && !jcr->pool->AutoPrune) { Dmsg0(100, "AutoPrune not set in Pool.\n"); return 0; } - memset(&mr, 0, sizeof(mr)); - memset(&pr, 0, sizeof(pr)); - create_ua_context(jcr, &ua); + + memset(&prune_list, 0, sizeof(prune_list)); + prune_list.max_ids = 10000; + prune_list.JobId = (JobId_t *)malloc(sizeof(JobId_t) * prune_list.max_ids); + + ua = new_ua_context(jcr); db_lock(jcr->db); - /* Get the Pool Record and a list of Media Id's in the Pool */ - pr.PoolId = jcr->PoolId; - if (!db_get_pool_record(jcr, jcr->db, &pr) || !db_get_media_ids(jcr, jcr->db, &num_ids, &ids)) { + /* Edit PoolId */ + edit_int64(mr->PoolId, ed1); + /* + * Get Pool record for Scratch Pool + */ + memset(&spr, 0, sizeof(spr)); + bstrncpy(spr.Name, "Scratch", sizeof(spr.Name)); + if (db_get_pool_record(jcr, jcr->db, &spr)) { + edit_int64(spr.PoolId, ed2); + bstrncat(ed2, ",", sizeof(ed2)); + } else { + ed2[0] = 0; + } + Dmsg1(050, "Scratch pool=%s\n", ed2); + /* + * ed2 ends up with scratch poolid and current poolid or + * just current poolid if there is no scratch pool + */ + bstrncat(ed2, ed1, sizeof(ed2)); + + /* + * Get the List of all media ids in the current Pool or whose + * RecyclePoolId is the current pool or the scratch pool + */ + const char *select = "SELECT DISTINCT MediaId,LastWritten FROM Media WHERE " + "(PoolId=%s OR RecyclePoolId IN (%s)) AND MediaType='%s' %s" + "ORDER BY LastWritten ASC,MediaId"; + + if (InChanger) { + char changer[100]; + /* Ensure it is in this autochanger */ + bsnprintf(changer, sizeof(changer), "AND InChanger=1 AND StorageId=%s ", + edit_int64(mr->StorageId, ed3)); + Mmsg(query, select, ed1, ed2, mr->MediaType, changer); + } else { + Mmsg(query, select, ed1, ed2, mr->MediaType, ""); + } + + Dmsg1(050, "query=%s\n", query.c_str()); + if (!db_get_query_dbids(ua->jcr, ua->db, query, ids)) { Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db)); goto bail_out; } - /* Visit each Volume and Prune it */ - for (i=0; idb, &mr)) { + Dmsg1(050, "num_ids=%d\n", ids.num_ids); + + /* Visit each Volume and Prune it until we find one that is purged */ + for (i=0; idb, &lmr)) { Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db)); - continue; + continue; } - /* Prune only Volumes from current Pool */ - if (pr.PoolId != mr.PoolId) { - continue; + /* Don't prune archived volumes */ + if (lmr.Enabled == 2) { + continue; } - /* Prune only Volumes with status "Full" or "Used" */ - if (strcmp(mr.VolStatus, "Full") == 0 || strcmp(mr.VolStatus, "Used") == 0) { - Dmsg1(200, "Prune Volume %s\n", mr.VolumeName); - stat += prune_volume(&ua, &pr, &mr); - Dmsg1(200, "Num pruned = %d\n", stat); + /* Prune only Volumes with status "Full", or "Used" */ + if (strcmp(lmr.VolStatus, "Full") == 0 || + strcmp(lmr.VolStatus, "Used") == 0) { + Dmsg2(050, "Add prune list MediaId=%d Volume %s\n", (int)lmr.MediaId, lmr.VolumeName); + count = get_prune_list_for_volume(ua, &lmr, &prune_list); + Dmsg1(050, "Num pruned = %d\n", count); + if (count != 0) { + purge_job_list_from_catalog(ua, prune_list); + prune_list.num_ids = 0; /* reset count */ + } + ok = is_volume_purged(ua, &lmr); + /* + * If purged and not moved to another Pool, + * then we stop pruning and take this volume. + */ + if (ok && lmr.PoolId == mr->PoolId) { + Dmsg2(050, "Vol=%s MediaId=%d purged.\n", lmr.VolumeName, (int)lmr.MediaId); + mr = &lmr; /* struct copy */ + break; /* got a volume */ + } + /* + * We purged something but did not get a volume in the current pool. + * It must be a scratch volume, so try to get it. + */ + if (ok && get_scratch_volume(jcr, InChanger, mr)) { + break; /* got a volume */ + } + ok = false; /* clear OK, in case we fall out */ + } else { + Dmsg2(050, "Nothing pruned MediaId=%d Volume=%s\n", (int)lmr.MediaId, lmr.VolumeName); } - } + } bail_out: db_unlock(jcr->db); - free_ua_context(&ua); - if (ids) { - free(ids); + free_ua_context(ua); + if (prune_list.JobId) { + free(prune_list.JobId); } - return stat; + return ok; }