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
41 static int const dbglvl = 50; /* debug level */
45 * mr.PoolId must be set
46 * mr.StorageId should also be set
47 * mr.ScratchPoolId could be set (used if create==true)
51 * MEDIA_DBR mr with PoolId set
52 * create -- whether or not to create a new volume
54 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index,
55 bool create, bool prune)
60 STORE *store = jcr->wstore;
62 bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
63 Dmsg3(dbglvl, "find_next_vol_for_append: JobId=%u PoolId=%d, MediaType=%s\n",
64 (uint32_t)jcr->JobId, (int)mr->PoolId, mr->MediaType);
66 * If we are using an Autochanger, restrict Volume
67 * search to the Autochanger on the first pass
69 InChanger = store->autochanger;
71 * Find the Next Volume for Append
75 bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus)); /* want only appendable volumes */
77 * 1. Look for volume with "Append" status.
79 ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
82 Dmsg4(dbglvl, "after find_next_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
83 ok, index, InChanger, mr->VolStatus);
85 * 2. Try finding a recycled volume
87 ok = find_recycled_volume(jcr, InChanger, mr);
88 Dmsg2(dbglvl, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten);
91 * 3. Try recycling any purged volume
93 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
96 * 4. Try pruning Volumes
99 Dmsg0(dbglvl, "Call prune_volumes\n");
100 prune_volumes(jcr, InChanger, mr);
102 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
104 Dmsg4(dbglvl, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
105 ok, index, InChanger, mr->VolStatus);
107 * 5. Try pulling a volume from the Scratch pool
109 ok = get_scratch_volume(jcr, InChanger, mr);
110 Dmsg4(dbglvl, "after get scratch volume ok=%d index=%d InChanger=%d Vstat=%s\n",
111 ok, index, InChanger, mr->VolStatus);
114 * If we are using an Autochanger and have not found
115 * a volume, retry looking for any volume.
117 if (!ok && InChanger) {
119 continue; /* retry again accepting any volume */
127 * 6. Try "creating" a new Volume
129 ok = newVolume(jcr, mr);
132 * Look at more drastic ways to find an Appendable Volume
134 if (!ok && (jcr->pool->purge_oldest_volume ||
135 jcr->pool->recycle_oldest_volume)) {
136 Dmsg2(dbglvl, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
137 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
138 /* Find oldest volume to recycle */
139 ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
140 Dmsg1(dbglvl, "Find oldest=%d Volume\n", ok);
143 Dmsg0(dbglvl, "Try purge Volume.\n");
145 * 7. Try to purging oldest volume only if not UA calling us.
147 ua = new_ua_context(jcr);
148 if (jcr->pool->purge_oldest_volume && create) {
149 Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
150 ok = purge_jobs_from_volume(ua, mr);
152 * 8. or try recycling the oldest volume
154 } else if (jcr->pool->recycle_oldest_volume) {
155 Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
156 ok = prune_volume(ua, mr);
160 ok = recycle_volume(jcr, mr);
161 Dmsg1(dbglvl, "Recycle after purge oldest=%d\n", ok);
166 Dmsg2(dbglvl, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
168 /* If we can use the volume, check if it is expired */
169 if (has_volume_expired(jcr, mr)) {
170 if (retry++ < 200) { /* sanity check */
171 continue; /* try again from the top */
173 Jmsg(jcr, M_ERROR, 0, _(
174 "We seem to be looping trying to find the next volume. I give up.\n"));
181 Dmsg1(dbglvl, "return ok=%d find_next_vol\n", ok);
186 * Check if any time limits or use limits have expired
187 * if so, set the VolStatus appropriately.
189 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
191 bool expired = false;
194 * Check limits and expirations if "Append" and it has been used
195 * i.e. mr->VolJobs > 0
198 if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
199 /* First handle Max Volume Bytes */
200 if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
201 Jmsg(jcr, M_INFO, 0, _("Max Volume bytes=%s exceeded. "
202 "Marking Volume \"%s\" as Full.\n"),
203 edit_uint64_with_commas(mr->MaxVolBytes, ed1), mr->VolumeName);
204 bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
207 /* Now see if Volume should only be used once */
208 } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
209 Jmsg(jcr, M_INFO, 0, _("Volume used once. "
210 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
211 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
214 /* Now see if Max Jobs written to volume */
215 } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
216 Jmsg(jcr, M_INFO, 0, _("Max Volume jobs=%s exceeded. "
217 "Marking Volume \"%s\" as Used.\n"),
218 edit_uint64_with_commas(mr->MaxVolJobs, ed1), mr->VolumeName);
219 Dmsg3(dbglvl, "MaxVolJobs=%d JobId=%d Vol=%s\n", mr->MaxVolJobs,
220 (uint32_t)jcr->JobId, mr->VolumeName);
221 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
224 /* Now see if Max Files written to volume */
225 } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
226 Jmsg(jcr, M_INFO, 0, _("Max Volume files=%s exceeded. "
227 "Marking Volume \"%s\" as Used.\n"),
228 edit_uint64_with_commas(mr->MaxVolFiles, ed1), mr->VolumeName);
229 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
232 /* Finally, check Use duration expiration */
233 } else if (mr->VolUseDuration > 0) {
234 utime_t now = time(NULL);
235 /* See if Vol Use has expired */
236 if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
237 Jmsg(jcr, M_INFO, 0, _("Max configured use duration=%s sec. exceeded. "
238 "Marking Volume \"%s\" as Used.\n"),
239 edit_uint64_with_commas(mr->VolUseDuration, ed1), mr->VolumeName);
240 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
246 /* Need to update media */
247 Dmsg1(dbglvl, "Vol=%s has expired update media record\n", mr->VolumeName);
248 if (!db_update_media_record(jcr, jcr->db, mr)) {
249 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
250 mr->VolumeName, db_strerror(jcr->db));
253 Dmsg2(dbglvl, "Vol=%s expired=%d\n", mr->VolumeName, expired);
258 * Try hard to recycle the current volume
260 * Returns: on failure - reason = NULL
261 * on success - reason - pointer to reason
263 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
269 /* Check if a duration or limit has expired */
270 if (has_volume_expired(jcr, mr)) {
271 *reason = _("volume has expired");
272 /* Keep going because we may be able to recycle volume */
276 * Now see if we can use the volume as is
278 if (strcmp(mr->VolStatus, "Append") == 0 ||
279 strcmp(mr->VolStatus, "Recycle") == 0) {
285 * Check if the Volume is already marked for recycling
287 if (strcmp(mr->VolStatus, "Purged") == 0) {
288 if (recycle_volume(jcr, mr)) {
289 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
293 /* In principle this shouldn't happen */
294 *reason = _("and recycling of current volume failed");
299 /* At this point, the volume is not valid for writing */
300 *reason = _("but should be Append, Purged or Recycle");
303 * What we're trying to do here is see if the current volume is
304 * "recyclable" - ie. if we prune all expired jobs off it, is
305 * it now possible to reuse it for the job that it is currently
309 *reason = _("volume has recycling disabled");
313 * Check retention period from last written, but recycle to within
314 * a minute to try to catch close calls ...
316 if ((mr->LastWritten + mr->VolRetention - 60) < (utime_t)time(NULL)
317 && jcr->pool->recycle_current_volume
318 && (strcmp(mr->VolStatus, "Full") == 0 ||
319 strcmp(mr->VolStatus, "Used") == 0)) {
321 * Attempt prune of current volume to see if we can
322 * recycle it for use.
326 ua = new_ua_context(jcr);
327 ok = prune_volume(ua, mr);
331 /* If fully purged, recycle current volume */
332 if (recycle_volume(jcr, mr)) {
333 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
336 *reason = _("but should be Append, Purged or Recycle (recycling of the "
337 "current volume failed)");
340 *reason = _("but should be Append, Purged or Recycle (cannot automatically "
341 "recycle current volume, as it still contains unpruned data "
342 "or the Volume Retention time has not expired.)");
347 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
349 bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr)
351 MEDIA_DBR smr; /* for searching scratch pool */
356 /* Only one thread at a time can pull from the scratch pool */
359 * Get Pool record for Scratch Pool
360 * choose between ScratchPoolId and Scratch
361 * db_get_pool_record will first try ScratchPoolId,
362 * and then try the pool named Scratch
364 memset(&spr, 0, sizeof(spr));
365 bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
366 spr.PoolId = mr->ScratchPoolId;
367 if (db_get_pool_record(jcr, jcr->db, &spr)) {
368 memset(&smr, 0, sizeof(smr));
369 smr.PoolId = spr.PoolId;
371 smr.StorageId = mr->StorageId; /* want only Scratch Volumes in changer */
373 bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus)); /* want only appendable volumes */
374 bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
377 * If we do not find a valid Scratch volume, try
378 * recycling any existing purged volumes, then
379 * try to take the oldest volume.
381 if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
384 } else if (find_recycled_volume(jcr, InChanger, &smr)) {
387 } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr)) {
392 POOL_MEM query(PM_MESSAGE);
395 * Get pool record where the Scratch Volume will go to ensure
396 * that we can add a Volume.
398 memset(&pr, 0, sizeof(pr));
399 bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
401 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
402 Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"),
403 db_strerror(jcr->db));
407 /* Make sure there is room for another volume */
408 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
409 Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"),
410 jcr->pool->name(), pr.MaxVols);
414 memcpy(mr, &smr, sizeof(MEDIA_DBR));
416 /* Set default parameters from current pool */
417 set_pool_dbr_defaults_in_media_dbr(mr, &pr);
420 * set_pool_dbr_defaults_in_media_dbr set VolStatus to Append,
421 * we could have Recycled media, also, we retain the old
424 bstrncpy(mr->VolStatus, smr.VolStatus, sizeof(smr.VolStatus));
425 mr->RecyclePoolId = smr.RecyclePoolId;
427 if (!db_update_media_record(jcr, jcr->db, mr)) {
428 Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
429 db_strerror(jcr->db));
433 Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"),