2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2010 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- next_vol -- handles finding the next
31 * volume for append. Split out of catreq.c August MMIII
32 * catalog request from the Storage daemon.
34 * Kern Sibbald, March MMI
43 * mr.PoolId must be set
44 * mr.StorageId should also be set
45 * mr.ScratchPoolId could be set (used if create==true)
49 * MEDIA_DBR mr with PoolId set
50 * create -- whether or not to create a new volume
52 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index,
53 bool create, bool prune)
58 STORE *store = jcr->wstore;
60 bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
61 Dmsg3(100, "find_next_vol_for_append: JobId=%u PoolId=%d, MediaType=%s\n",
62 (uint32_t)jcr->JobId, (int)mr->PoolId, mr->MediaType);
64 * If we are using an Autochanger, restrict Volume
65 * search to the Autochanger on the first pass
67 InChanger = store->autochanger;
69 * Find the Next Volume for Append
73 bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus)); /* want only appendable volumes */
75 * 1. Look for volume with "Append" status.
77 ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
80 Dmsg4(150, "after find_next_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
81 ok, index, InChanger, mr->VolStatus);
83 * 2. Try finding a recycled volume
85 ok = find_recycled_volume(jcr, InChanger, mr);
86 Dmsg2(150, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten);
89 * 3. Try recycling any purged volume
91 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
94 * 4. Try pruning Volumes
97 Dmsg0(150, "Call prune_volumes\n");
98 prune_volumes(jcr, InChanger, mr);
100 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
102 Dmsg4(150, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
103 ok, index, InChanger, mr->VolStatus);
105 * 5. Try pulling a volume from the Scratch pool
107 ok = get_scratch_volume(jcr, InChanger, mr);
108 Dmsg4(150, "after get scratch volume ok=%d index=%d InChanger=%d Vstat=%s\n",
109 ok, index, InChanger, mr->VolStatus);
112 * If we are using an Autochanger and have not found
113 * a volume, retry looking for any volume.
115 if (!ok && InChanger) {
117 continue; /* retry again accepting any volume */
125 * 6. Try "creating" a new Volume
127 ok = newVolume(jcr, mr);
130 * Look at more drastic ways to find an Appendable Volume
132 if (!ok && (jcr->pool->purge_oldest_volume ||
133 jcr->pool->recycle_oldest_volume)) {
134 Dmsg2(200, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
135 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
136 /* Find oldest volume to recycle */
137 ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
138 Dmsg1(200, "Find oldest=%d Volume\n", ok);
141 Dmsg0(200, "Try purge Volume.\n");
143 * 7. Try to purging oldest volume only if not UA calling us.
145 ua = new_ua_context(jcr);
146 if (jcr->pool->purge_oldest_volume && create) {
147 Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
148 ok = purge_jobs_from_volume(ua, mr);
150 * 8. or try recycling the oldest volume
152 } else if (jcr->pool->recycle_oldest_volume) {
153 Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
154 ok = prune_volume(ua, mr);
158 ok = recycle_volume(jcr, mr);
159 Dmsg1(400, "Recycle after purge oldest=%d\n", ok);
164 Dmsg2(100, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
166 /* If we can use the volume, check if it is expired */
167 if (has_volume_expired(jcr, mr)) {
168 if (retry++ < 200) { /* sanity check */
169 continue; /* try again from the top */
171 Jmsg(jcr, M_ERROR, 0, _(
172 "We seem to be looping trying to find the next volume. I give up.\n"));
179 Dmsg1(150, "return ok=%d find_next_vol\n", ok);
184 * Check if any time limits or use limits have expired
185 * if so, set the VolStatus appropriately.
187 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
189 bool expired = false;
192 * Check limits and expirations if "Append" and it has been used
193 * i.e. mr->VolJobs > 0
196 if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
197 /* First handle Max Volume Bytes */
198 if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
199 Jmsg(jcr, M_INFO, 0, _("Max Volume bytes=%s exceeded. "
200 "Marking Volume \"%s\" as Full.\n"),
201 edit_uint64_with_commas(mr->MaxVolBytes, ed1), mr->VolumeName);
202 bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
205 /* Now see if Volume should only be used once */
206 } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
207 Jmsg(jcr, M_INFO, 0, _("Volume used once. "
208 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
209 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
212 /* Now see if Max Jobs written to volume */
213 } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
214 Jmsg(jcr, M_INFO, 0, _("Max Volume jobs=%s exceeded. "
215 "Marking Volume \"%s\" as Used.\n"),
216 edit_uint64_with_commas(mr->MaxVolJobs, ed1), mr->VolumeName);
217 Dmsg3(100, "MaxVolJobs=%d JobId=%d Vol=%s\n", mr->MaxVolJobs,
218 (uint32_t)jcr->JobId, mr->VolumeName);
219 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
222 /* Now see if Max Files written to volume */
223 } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
224 Jmsg(jcr, M_INFO, 0, _("Max Volume files=%s exceeded. "
225 "Marking Volume \"%s\" as Used.\n"),
226 edit_uint64_with_commas(mr->MaxVolFiles, ed1), mr->VolumeName);
227 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
230 /* Finally, check Use duration expiration */
231 } else if (mr->VolUseDuration > 0) {
232 utime_t now = time(NULL);
233 /* See if Vol Use has expired */
234 if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
235 Jmsg(jcr, M_INFO, 0, _("Max configured use duration=%s sec. exceeded. "
236 "Marking Volume \"%s\" as Used.\n"),
237 edit_uint64_with_commas(mr->VolUseDuration, ed1), mr->VolumeName);
238 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
244 /* Need to update media */
245 Dmsg1(150, "Vol=%s has expired update media record\n", mr->VolumeName);
246 if (!db_update_media_record(jcr, jcr->db, mr)) {
247 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
248 mr->VolumeName, db_strerror(jcr->db));
251 Dmsg2(150, "Vol=%s expired=%d\n", mr->VolumeName, expired);
256 * Try hard to recycle the current volume
258 * Returns: on failure - reason = NULL
259 * on success - reason - pointer to reason
261 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
267 /* Check if a duration or limit has expired */
268 if (has_volume_expired(jcr, mr)) {
269 *reason = _("volume has expired");
270 /* Keep going because we may be able to recycle volume */
274 * Now see if we can use the volume as is
276 if (strcmp(mr->VolStatus, "Append") == 0 ||
277 strcmp(mr->VolStatus, "Recycle") == 0) {
283 * Check if the Volume is already marked for recycling
285 if (strcmp(mr->VolStatus, "Purged") == 0) {
286 if (recycle_volume(jcr, mr)) {
287 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
291 /* In principle this shouldn't happen */
292 *reason = _("and recycling of current volume failed");
297 /* At this point, the volume is not valid for writing */
298 *reason = _("but should be Append, Purged or Recycle");
301 * What we're trying to do here is see if the current volume is
302 * "recyclable" - ie. if we prune all expired jobs off it, is
303 * it now possible to reuse it for the job that it is currently
307 *reason = _("volume has recycling disabled");
310 if ((mr->LastWritten + mr->VolRetention) < (utime_t)time(NULL)
311 && jcr->pool->recycle_current_volume
312 && (strcmp(mr->VolStatus, "Full") == 0 ||
313 strcmp(mr->VolStatus, "Used") == 0)) {
315 * Attempt prune of current volume to see if we can
316 * recycle it for use.
320 ua = new_ua_context(jcr);
321 ok = prune_volume(ua, mr);
325 /* If fully purged, recycle current volume */
326 if (recycle_volume(jcr, mr)) {
327 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
330 *reason = _("but should be Append, Purged or Recycle (recycling of the "
331 "current volume failed)");
334 *reason = _("but should be Append, Purged or Recycle (cannot automatically "
335 "recycle current volume, as it still contains unpruned data "
336 "or the Volume Retention time has not expired.)");
341 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
343 bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr)
345 MEDIA_DBR smr; /* for searching scratch pool */
350 /* Only one thread at a time can pull from the scratch pool */
353 * Get Pool record for Scratch Pool
354 * choose between ScratchPoolId and Scratch
355 * db_get_pool_record will first try ScratchPoolId,
356 * and then try the pool named Scratch
358 memset(&spr, 0, sizeof(spr));
359 bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
360 spr.PoolId = mr->ScratchPoolId;
361 if (db_get_pool_record(jcr, jcr->db, &spr)) {
362 memset(&smr, 0, sizeof(smr));
363 smr.PoolId = spr.PoolId;
365 smr.StorageId = mr->StorageId; /* want only Scratch Volumes in changer */
367 bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus)); /* want only appendable volumes */
368 bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
371 * If we do not find a valid Scratch volume, try
372 * recycling any existing purged volumes, then
373 * try to take the oldest volume.
375 if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
378 } else if (find_recycled_volume(jcr, InChanger, &smr)) {
381 } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr)) {
386 POOL_MEM query(PM_MESSAGE);
389 * Get pool record where the Scratch Volume will go to ensure
390 * that we can add a Volume.
392 memset(&pr, 0, sizeof(pr));
393 bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
395 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
396 Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"),
397 db_strerror(jcr->db));
401 /* Make sure there is room for another volume */
402 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
403 Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"),
404 jcr->pool->name(), pr.MaxVols);
408 memcpy(mr, &smr, sizeof(MEDIA_DBR));
410 /* Set default parameters from current pool */
411 set_pool_dbr_defaults_in_media_dbr(mr, &pr);
414 * set_pool_dbr_defaults_in_media_dbr set VolStatus to Append,
415 * we could have Recycled media, also, we retain the old
418 bstrncpy(mr->VolStatus, smr.VolStatus, sizeof(smr.VolStatus));
419 mr->RecyclePoolId = smr.RecyclePoolId;
421 if (!db_update_media_record(jcr, jcr->db, mr)) {
422 Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
423 db_strerror(jcr->db));
427 Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"),