2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2012 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 */
43 /* Set storage id if possible */
44 void set_storageid_in_mr(STORE *store, MEDIA_DBR *mr)
47 mr->StorageId = store->StorageId;
53 * mr.PoolId must be set
54 * mr.ScratchPoolId could be set (used if create==true)
58 * MEDIA_DBR mr with PoolId set
59 * create -- whether or not to create a new volume
61 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index,
62 bool create, bool prune)
67 STORE *store = jcr->wstore;
69 bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
70 Dmsg3(dbglvl, "find_next_vol_for_append: JobId=%u PoolId=%d, MediaType=%s\n",
71 (uint32_t)jcr->JobId, (int)mr->PoolId, mr->MediaType);
73 * If we are using an Autochanger, restrict Volume
74 * search to the Autochanger on the first pass
76 InChanger = store->autochanger;
78 * Find the Next Volume for Append
82 bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus)); /* want only appendable volumes */
84 * 1. Look for volume with "Append" status.
86 set_storageid_in_mr(store, mr); /* put StorageId in new record */
87 ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
91 * No volume found, apply algorithm
93 Dmsg4(dbglvl, "after find_next_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
94 ok, index, InChanger, mr->VolStatus);
96 * 2. Try finding a recycled volume
98 ok = find_recycled_volume(jcr, InChanger, mr, store);
99 set_storageid_in_mr(store, mr); /* put StorageId in new record */
100 Dmsg2(dbglvl, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten);
103 * 3. Try recycling any purged volume
105 ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store);
106 set_storageid_in_mr(store, mr); /* put StorageId in new record */
109 * 4. Try pruning Volumes
112 Dmsg0(dbglvl, "Call prune_volumes\n");
113 prune_volumes(jcr, InChanger, mr, store);
115 ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store);
116 set_storageid_in_mr(store, mr); /* put StorageId in new record */
118 Dmsg4(dbglvl, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
119 ok, index, InChanger, mr->VolStatus);
121 * 5. Try pulling a volume from the Scratch pool
123 ok = get_scratch_volume(jcr, InChanger, mr, store);
124 set_storageid_in_mr(store, mr); /* put StorageId in new record */
125 Dmsg4(dbglvl, "after get scratch volume ok=%d index=%d InChanger=%d Vstat=%s\n",
126 ok, index, InChanger, mr->VolStatus);
129 * If we are using an Autochanger and have not found
130 * a volume, retry looking for any volume.
132 if (!ok && InChanger) {
134 continue; /* retry again accepting any volume */
142 * 6. Try "creating" a new Volume
144 ok = newVolume(jcr, mr, store);
147 * Look at more drastic ways to find an Appendable Volume
149 if (!ok && (jcr->pool->purge_oldest_volume ||
150 jcr->pool->recycle_oldest_volume)) {
151 Dmsg2(dbglvl, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
152 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
153 /* Find oldest volume to recycle */
154 set_storageid_in_mr(store, mr); /* update storage id */
155 ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
156 set_storageid_in_mr(store, mr); /* update storageid */
157 Dmsg1(dbglvl, "Find oldest=%d Volume\n", ok);
160 Dmsg0(dbglvl, "Try purge Volume.\n");
162 * 7. Try to purging oldest volume only if not UA calling us.
164 ua = new_ua_context(jcr);
165 if (jcr->pool->purge_oldest_volume && create) {
166 Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
167 ok = purge_jobs_from_volume(ua, mr);
169 * 8. or try recycling the oldest volume
171 } else if (jcr->pool->recycle_oldest_volume) {
172 Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
173 ok = prune_volume(ua, mr);
177 ok = recycle_volume(jcr, mr);
178 Dmsg1(dbglvl, "Recycle after purge oldest=%d\n", ok);
183 Dmsg2(dbglvl, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
185 /* If we can use the volume, check if it is expired */
186 if (has_volume_expired(jcr, mr)) {
187 if (retry++ < 200) { /* sanity check */
188 continue; /* try again from the top */
190 Jmsg(jcr, M_ERROR, 0, _(
191 "We seem to be looping trying to find the next volume. I give up.\n"));
198 Dmsg1(dbglvl, "return ok=%d find_next_vol\n", ok);
203 * Check if any time limits or use limits have expired
204 * if so, set the VolStatus appropriately.
206 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
208 bool expired = false;
211 * Check limits and expirations if "Append" and it has been used
212 * i.e. mr->VolJobs > 0
215 if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
216 /* First handle Max Volume Bytes */
217 if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
218 Jmsg(jcr, M_INFO, 0, _("Max Volume bytes=%s exceeded. "
219 "Marking Volume \"%s\" as Full.\n"),
220 edit_uint64_with_commas(mr->MaxVolBytes, ed1), mr->VolumeName);
221 bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
224 /* Now see if Volume should only be used once */
225 } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
226 Jmsg(jcr, M_INFO, 0, _("Volume used once. "
227 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
228 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
231 /* Now see if Max Jobs written to volume */
232 } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
233 Jmsg(jcr, M_INFO, 0, _("Max Volume jobs=%s exceeded. "
234 "Marking Volume \"%s\" as Used.\n"),
235 edit_uint64_with_commas(mr->MaxVolJobs, ed1), mr->VolumeName);
236 Dmsg3(dbglvl, "MaxVolJobs=%d JobId=%d Vol=%s\n", mr->MaxVolJobs,
237 (uint32_t)jcr->JobId, mr->VolumeName);
238 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
241 /* Now see if Max Files written to volume */
242 } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
243 Jmsg(jcr, M_INFO, 0, _("Max Volume files=%s exceeded. "
244 "Marking Volume \"%s\" as Used.\n"),
245 edit_uint64_with_commas(mr->MaxVolFiles, ed1), mr->VolumeName);
246 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
249 /* Finally, check Use duration expiration */
250 } else if (mr->VolUseDuration > 0) {
251 utime_t now = time(NULL);
252 /* See if Vol Use has expired */
253 if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
254 Jmsg(jcr, M_INFO, 0, _("Max configured use duration=%s sec. exceeded. "
255 "Marking Volume \"%s\" as Used.\n"),
256 edit_uint64_with_commas(mr->VolUseDuration, ed1), mr->VolumeName);
257 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
263 /* Need to update media */
264 Dmsg1(dbglvl, "Vol=%s has expired update media record\n", mr->VolumeName);
265 set_storageid_in_mr(NULL, mr);
266 if (!db_update_media_record(jcr, jcr->db, mr)) {
267 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
268 mr->VolumeName, db_strerror(jcr->db));
271 Dmsg2(dbglvl, "Vol=%s expired=%d\n", mr->VolumeName, expired);
276 * Try hard to recycle the current volume
278 * Returns: on failure - reason = NULL
279 * on success - reason - pointer to reason
281 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
287 /* Check if a duration or limit has expired */
288 if (has_volume_expired(jcr, mr)) {
289 *reason = _("volume has expired");
290 /* Keep going because we may be able to recycle volume */
294 * Now see if we can use the volume as is
296 if (strcmp(mr->VolStatus, "Append") == 0 ||
297 strcmp(mr->VolStatus, "Recycle") == 0) {
303 * Check if the Volume is already marked for recycling
305 if (strcmp(mr->VolStatus, "Purged") == 0) {
306 if (recycle_volume(jcr, mr)) {
307 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
311 /* In principle this shouldn't happen */
312 *reason = _("and recycling of current volume failed");
317 /* At this point, the volume is not valid for writing */
318 *reason = _("but should be Append, Purged or Recycle");
321 * What we're trying to do here is see if the current volume is
322 * "recyclable" - ie. if we prune all expired jobs off it, is
323 * it now possible to reuse it for the job that it is currently
327 *reason = _("volume has recycling disabled");
331 * Check retention period from last written, but recycle to within
332 * a minute to try to catch close calls ...
334 if ((mr->LastWritten + mr->VolRetention - 60) < (utime_t)time(NULL)
335 && jcr->pool->recycle_current_volume
336 && (strcmp(mr->VolStatus, "Full") == 0 ||
337 strcmp(mr->VolStatus, "Used") == 0)) {
339 * Attempt prune of current volume to see if we can
340 * recycle it for use.
344 ua = new_ua_context(jcr);
345 ok = prune_volume(ua, mr);
349 /* If fully purged, recycle current volume */
350 if (recycle_volume(jcr, mr)) {
351 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
354 *reason = _("but should be Append, Purged or Recycle (recycling of the "
355 "current volume failed)");
358 *reason = _("but should be Append, Purged or Recycle (cannot automatically "
359 "recycle current volume, as it still contains unpruned data "
360 "or the Volume Retention time has not expired.)");
365 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
367 bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr,
370 MEDIA_DBR smr; /* for searching scratch pool */
375 /* Only one thread at a time can pull from the scratch pool */
378 * Get Pool record for Scratch Pool
379 * choose between ScratchPoolId and Scratch
380 * db_get_pool_record will first try ScratchPoolId,
381 * and then try the pool named Scratch
383 memset(&spr, 0, sizeof(spr));
384 bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
385 spr.PoolId = mr->ScratchPoolId;
386 if (db_get_pool_record(jcr, jcr->db, &spr)) {
387 smr.PoolId = spr.PoolId;
389 smr.StorageId = mr->StorageId; /* want only Scratch Volumes in changer */
391 bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus)); /* want only appendable volumes */
392 bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
395 * If we do not find a valid Scratch volume, try
396 * recycling any existing purged volumes, then
397 * try to take the oldest volume.
399 set_storageid_in_mr(store, &smr); /* put StorageId in new record */
400 if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
403 } else if (find_recycled_volume(jcr, InChanger, &smr, store)) {
406 } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr, store)) {
411 POOL_MEM query(PM_MESSAGE);
414 * Get pool record where the Scratch Volume will go to ensure
415 * that we can add a Volume.
417 memset(&pr, 0, sizeof(pr));
418 bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
420 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
421 Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"),
422 db_strerror(jcr->db));
426 /* Make sure there is room for another volume */
427 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
428 Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"),
429 jcr->pool->name(), pr.MaxVols);
434 set_storageid_in_mr(store, mr);
436 /* Set default parameters from current pool */
437 set_pool_dbr_defaults_in_media_dbr(mr, &pr);
440 * set_pool_dbr_defaults_in_media_dbr set VolStatus to Append,
441 * we could have Recycled media, also, we retain the old
444 bstrncpy(mr->VolStatus, smr.VolStatus, sizeof(smr.VolStatus));
445 mr->RecyclePoolId = smr.RecyclePoolId;
447 if (!db_update_media_record(jcr, jcr->db, mr)) {
448 Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
449 db_strerror(jcr->db));
453 Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"),