2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2001-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
22 * Bacula Director -- next_vol -- handles finding the next
23 * volume for append. Split out of catreq.c August MMIII
24 * catalog request from the Storage daemon.
26 * Kern Sibbald, March MMI
33 static int const dbglvl = 50; /* debug level */
36 * We setup the StorageId if it is
37 * an autochanger from the Storage and put it in
39 * store == NULL => use existing StorageId
41 void set_storageid_in_mr(STORE *store, MEDIA_DBR *mr)
46 /* At this point we know store != NULL */
47 mr->StorageId = store->StorageId;
52 * mr.PoolId must be set
53 * mr.ScratchPoolId could be set (used if create==true)
57 * MEDIA_DBR mr with PoolId set
58 * create -- whether or not to create a new volume
60 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index,
61 bool create, bool prune)
66 STORE *store = jcr->wstore;
68 bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
69 Dmsg6(dbglvl, "find_next_vol_for_append: JobId=%u PoolId=%d, MediaType=%s index=%d create=%d prune=%d\n",
70 (uint32_t)jcr->JobId, (int)mr->PoolId, mr->MediaType, index,
73 * If we are using an Autochanger, restrict Volume
74 * search to the Autochanger on the first pass
76 InChanger = (store->autochanger)? true : false;
79 * Find the Next Volume for Append
83 bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus)); /* want only appendable volumes */
85 * 1. Look for volume with "Append" status.
87 set_storageid_in_mr(store, mr); /* put StorageId in new record */
88 ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
92 * No volume found, apply algorithm
94 Dmsg4(dbglvl, "after find_next_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
95 ok, index, InChanger, mr->VolStatus);
97 * 2. Try finding a recycled volume
99 ok = find_recycled_volume(jcr, InChanger, mr, store);
100 set_storageid_in_mr(store, mr); /* put StorageId in new record */
101 Dmsg2(dbglvl, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten);
104 * 3. Try recycling any purged volume
106 ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store);
107 set_storageid_in_mr(store, mr); /* put StorageId in new record */
110 * 4. Try pruning Volumes
113 Dmsg0(dbglvl, "Call prune_volumes\n");
114 prune_volumes(jcr, InChanger, mr, store);
116 ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store);
117 set_storageid_in_mr(store, mr); /* put StorageId in new record */
119 Dmsg4(dbglvl, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
120 ok, index, InChanger, mr->VolStatus);
122 * 5. Try pulling a volume from the Scratch pool
124 ok = get_scratch_volume(jcr, InChanger, mr, store);
125 set_storageid_in_mr(store, mr); /* put StorageId in new record */
126 Dmsg4(dbglvl, "after get scratch volume ok=%d index=%d InChanger=%d Vstat=%s\n",
127 ok, index, InChanger, mr->VolStatus);
130 * If we are using an Autochanger and have not found
131 * a volume, retry looking for any volume.
133 if (!ok && InChanger) {
135 continue; /* retry again accepting any volume */
143 * 6. Try "creating" a new Volume
145 ok = newVolume(jcr, mr, store);
148 * Look at more drastic ways to find an Appendable Volume
150 if (!ok && (jcr->pool->purge_oldest_volume ||
151 jcr->pool->recycle_oldest_volume)) {
152 Dmsg2(dbglvl, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
153 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
154 /* Find oldest volume to recycle */
155 set_storageid_in_mr(store, mr); /* update storage id */
156 ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
157 set_storageid_in_mr(store, mr); /* update storageid */
158 Dmsg1(dbglvl, "Find oldest=%d Volume\n", ok);
161 Dmsg0(dbglvl, "Try purge Volume.\n");
163 * 7. Try to purging oldest volume only if not UA calling us.
165 ua = new_ua_context(jcr);
166 if (jcr->pool->purge_oldest_volume && create) {
167 Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
168 ok = purge_jobs_from_volume(ua, mr);
170 * 8. or try recycling the oldest volume
172 } else if (jcr->pool->recycle_oldest_volume) {
173 Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
174 ok = prune_volume(ua, mr);
178 ok = recycle_volume(jcr, mr);
179 Dmsg1(dbglvl, "Recycle after purge oldest=%d\n", ok);
184 Dmsg2(dbglvl, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
186 /* If we can use the volume, check if it is expired */
187 if (has_volume_expired(jcr, mr)) {
188 if (retry++ < 200) { /* sanity check */
189 continue; /* try again from the top */
191 Jmsg(jcr, M_ERROR, 0, _(
192 "We seem to be looping trying to find the next volume. I give up.\n"));
199 Dmsg1(dbglvl, "return ok=%d find_next_vol\n", ok);
204 * Check if any time limits or use limits have expired
205 * if so, set the VolStatus appropriately.
207 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
209 bool expired = false;
212 * Check limits and expirations if "Append" and it has been used
213 * i.e. mr->VolJobs > 0
216 if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
217 /* First handle Max Volume Bytes */
218 if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
219 Jmsg(jcr, M_INFO, 0, _("Max Volume bytes=%s exceeded. "
220 "Marking Volume \"%s\" as Full.\n"),
221 edit_uint64_with_commas(mr->MaxVolBytes, ed1), mr->VolumeName);
222 bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
225 /* Now see if Volume should only be used once */
226 } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
227 Jmsg(jcr, M_INFO, 0, _("Volume used once. "
228 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
229 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
232 /* Now see if Max Jobs written to volume */
233 } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
234 Jmsg(jcr, M_INFO, 0, _("Max Volume jobs=%s exceeded. "
235 "Marking Volume \"%s\" as Used.\n"),
236 edit_uint64_with_commas(mr->MaxVolJobs, ed1), mr->VolumeName);
237 Dmsg3(dbglvl, "MaxVolJobs=%d JobId=%d Vol=%s\n", mr->MaxVolJobs,
238 (uint32_t)jcr->JobId, mr->VolumeName);
239 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
242 /* Now see if Max Files written to volume */
243 } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
244 Jmsg(jcr, M_INFO, 0, _("Max Volume files=%s exceeded. "
245 "Marking Volume \"%s\" as Used.\n"),
246 edit_uint64_with_commas(mr->MaxVolFiles, ed1), mr->VolumeName);
247 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
250 /* Finally, check Use duration expiration */
251 } else if (mr->VolUseDuration > 0) {
252 utime_t now = time(NULL);
253 /* See if Vol Use has expired */
254 if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
255 Jmsg(jcr, M_INFO, 0, _("Max configured use duration=%s sec. exceeded. "
256 "Marking Volume \"%s\" as Used.\n"),
257 edit_uint64_with_commas(mr->VolUseDuration, ed1), mr->VolumeName);
258 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
264 /* Need to update media */
265 Dmsg1(dbglvl, "Vol=%s has expired update media record\n", mr->VolumeName);
266 set_storageid_in_mr(NULL, mr);
267 if (!db_update_media_record(jcr, jcr->db, mr)) {
268 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
269 mr->VolumeName, db_strerror(jcr->db));
272 Dmsg2(dbglvl, "Vol=%s expired=%d\n", mr->VolumeName, expired);
277 * Try hard to recycle the current volume
279 * Returns: on failure - reason = NULL
280 * on success - reason - pointer to reason
282 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
288 /* Check if a duration or limit has expired */
289 if (has_volume_expired(jcr, mr)) {
290 *reason = _("volume has expired");
291 /* Keep going because we may be able to recycle volume */
295 * Now see if we can use the volume as is
297 if (strcmp(mr->VolStatus, "Append") == 0 ||
298 strcmp(mr->VolStatus, "Recycle") == 0) {
304 * Check if the Volume is already marked for recycling
306 if (strcmp(mr->VolStatus, "Purged") == 0) {
307 if (recycle_volume(jcr, mr)) {
308 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
312 /* In principle this shouldn't happen */
313 *reason = _("and recycling of current volume failed");
318 /* At this point, the volume is not valid for writing */
319 *reason = _("but should be Append, Purged or Recycle");
322 * What we're trying to do here is see if the current volume is
323 * "recyclable" - ie. if we prune all expired jobs off it, is
324 * it now possible to reuse it for the job that it is currently
328 *reason = _("volume has recycling disabled");
332 * Check retention period from last written, but recycle to within
333 * a minute to try to catch close calls ...
335 if ((mr->LastWritten + mr->VolRetention - 60) < (utime_t)time(NULL)
336 && jcr->pool->recycle_current_volume
337 && (strcmp(mr->VolStatus, "Full") == 0 ||
338 strcmp(mr->VolStatus, "Used") == 0)) {
340 * Attempt prune of current volume to see if we can
341 * recycle it for use.
345 ua = new_ua_context(jcr);
346 ok = prune_volume(ua, mr);
350 /* If fully purged, recycle current volume */
351 if (recycle_volume(jcr, mr)) {
352 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
355 *reason = _("but should be Append, Purged or Recycle (recycling of the "
356 "current volume failed)");
359 *reason = _("but should be Append, Purged or Recycle (cannot automatically "
360 "recycle current volume, as it still contains unpruned data "
361 "or the Volume Retention time has not expired.)");
366 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
368 bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr,
371 MEDIA_DBR smr; /* for searching scratch pool */
376 /* Only one thread at a time can pull from the scratch pool */
379 * Get Pool record for Scratch Pool
380 * choose between ScratchPoolId and Scratch
381 * db_get_pool_numvols will first try ScratchPoolId,
382 * and then try the pool named Scratch
384 memset(&spr, 0, sizeof(spr));
385 bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
386 spr.PoolId = mr->ScratchPoolId;
387 if (db_get_pool_record(jcr, jcr->db, &spr)) {
388 smr.PoolId = spr.PoolId;
389 bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus)); /* want only appendable volumes */
390 bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
393 * If we do not find a valid Scratch volume, try
394 * recycling any existing purged volumes, then
395 * try to take the oldest volume.
397 set_storageid_in_mr(store, &smr); /* put StorageId in new record */
398 if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
401 } else if (find_recycled_volume(jcr, InChanger, &smr, store)) {
404 } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr, store)) {
409 POOL_MEM query(PM_MESSAGE);
412 * Get pool record where the Scratch Volume will go to ensure
413 * that we can add a Volume.
415 memset(&pr, 0, sizeof(pr));
416 bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
418 if (!db_get_pool_numvols(jcr, jcr->db, &pr)) {
419 Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"),
420 db_strerror(jcr->db));
424 /* Make sure there is room for another volume */
425 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
426 Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"),
427 jcr->pool->name(), pr.MaxVols);
432 set_storageid_in_mr(store, mr);
434 /* Set default parameters from current pool */
435 set_pool_dbr_defaults_in_media_dbr(mr, &pr);
438 * set_pool_dbr_defaults_in_media_dbr set VolStatus to Append,
439 * we could have Recycled media, also, we retain the old
442 bstrncpy(mr->VolStatus, smr.VolStatus, sizeof(smr.VolStatus));
443 mr->RecyclePoolId = smr.RecyclePoolId;
445 if (!db_update_media_record(jcr, jcr->db, mr)) {
446 Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
447 db_strerror(jcr->db));
451 Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"),