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;
50 static void add_volume_to_exclude_list(JCR *jcr, int index, MEDIA_DBR *mr)
54 *jcr->next_vol_list = 0;
56 } else if (*jcr->next_vol_list) {
57 pm_strcat(jcr->next_vol_list, ",");
59 pm_strcat(jcr->next_vol_list, edit_int64(mr->MediaId, ed1));
61 /* The list is valid only in find_next_volume_for_append() */
62 mr->exclude_list = NULL;
65 static void set_volume_to_exclude_list(JCR *jcr, int index, MEDIA_DBR *mr)
68 *jcr->next_vol_list = 0;
70 mr->exclude_list = jcr->next_vol_list;
75 * mr.PoolId must be set
76 * mr.ScratchPoolId could be set (used if create==true)
80 * MEDIA_DBR mr with PoolId set
81 * create -- whether or not to create a new volume
83 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index,
84 bool create, bool prune)
89 STORE *store = jcr->wstore;
91 bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
92 Dmsg6(dbglvl, "find_next_vol_for_append: JobId=%u PoolId=%d, MediaType=%s index=%d create=%d prune=%d\n",
93 (uint32_t)jcr->JobId, (int)mr->PoolId, mr->MediaType, index,
96 * If we are using an Autochanger, restrict Volume
97 * search to the Autochanger on the first pass
99 InChanger = (store->autochanger)? true : false;
101 /* Make sure we don't send two times the same volume in the same session */
102 set_volume_to_exclude_list(jcr, index, mr);
105 * Find the Next Volume for Append
109 bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus)); /* want only appendable volumes */
111 * 1. Look for volume with "Append" status.
113 set_storageid_in_mr(store, mr); /* put StorageId in new record */
114 ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
118 * No volume found, apply algorithm
120 Dmsg4(dbglvl, "after find_next_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
121 ok, index, InChanger, mr->VolStatus);
123 * 2. Try finding a recycled volume
125 ok = find_recycled_volume(jcr, InChanger, mr, store);
126 set_storageid_in_mr(store, mr); /* put StorageId in new record */
127 Dmsg2(dbglvl, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten);
130 * 3. Try recycling any purged volume
132 ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store);
133 set_storageid_in_mr(store, mr); /* put StorageId in new record */
136 * 4. Try pruning Volumes
139 Dmsg0(dbglvl, "Call prune_volumes\n");
140 prune_volumes(jcr, InChanger, mr, store);
142 ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store);
143 set_storageid_in_mr(store, mr); /* put StorageId in new record */
145 Dmsg4(dbglvl, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
146 ok, index, InChanger, mr->VolStatus);
148 * 5. Try pulling a volume from the Scratch pool
150 ok = get_scratch_volume(jcr, InChanger, mr, store);
151 set_storageid_in_mr(store, mr); /* put StorageId in new record */
152 Dmsg4(dbglvl, "after get scratch volume ok=%d index=%d InChanger=%d Vstat=%s\n",
153 ok, index, InChanger, mr->VolStatus);
156 * If we are using an Autochanger and have not found
157 * a volume, retry looking for any volume.
159 if (!ok && InChanger) {
161 continue; /* retry again accepting any volume */
169 * 6. Try "creating" a new Volume
171 ok = newVolume(jcr, mr, store);
174 * Look at more drastic ways to find an Appendable Volume
176 if (!ok && (jcr->pool->purge_oldest_volume ||
177 jcr->pool->recycle_oldest_volume)) {
178 Dmsg2(dbglvl, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
179 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
180 /* Find oldest volume to recycle */
181 set_storageid_in_mr(store, mr); /* update storage id */
182 ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
183 set_storageid_in_mr(store, mr); /* update storageid */
184 Dmsg1(dbglvl, "Find oldest=%d Volume\n", ok);
187 Dmsg0(dbglvl, "Try purge Volume.\n");
189 * 7. Try to purging oldest volume only if not UA calling us.
191 ua = new_ua_context(jcr);
192 if (jcr->pool->purge_oldest_volume && create) {
193 Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
194 ok = purge_jobs_from_volume(ua, mr);
196 * 8. or try recycling the oldest volume
198 } else if (jcr->pool->recycle_oldest_volume) {
199 Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
200 ok = prune_volume(ua, mr);
204 ok = recycle_volume(jcr, mr);
205 Dmsg1(dbglvl, "Recycle after purge oldest=%d\n", ok);
210 Dmsg2(dbglvl, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
212 /* If we can use the volume, check if it is expired */
213 if (has_volume_expired(jcr, mr)) {
214 if (retry++ < 200) { /* sanity check */
215 continue; /* try again from the top */
217 Jmsg(jcr, M_ERROR, 0, _(
218 "We seem to be looping trying to find the next volume. I give up.\n"));
225 Dmsg1(dbglvl, "return ok=%d find_next_vol\n", ok);
227 /* We keep the record of all previous volumes requested */
229 add_volume_to_exclude_list(jcr, index, mr);;
235 * Check if any time limits or use limits have expired
236 * if so, set the VolStatus appropriately.
238 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
240 bool expired = false;
243 * Check limits and expirations if "Append" and it has been used
244 * i.e. mr->VolJobs > 0
247 if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
248 /* First handle Max Volume Bytes */
249 if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
250 Jmsg(jcr, M_INFO, 0, _("Max Volume bytes=%s exceeded. "
251 "Marking Volume \"%s\" as Full.\n"),
252 edit_uint64_with_commas(mr->MaxVolBytes, ed1), mr->VolumeName);
253 bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
256 /* Now see if Volume should only be used once */
257 } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
258 Jmsg(jcr, M_INFO, 0, _("Volume used once. "
259 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
260 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
263 /* Now see if Max Jobs written to volume */
264 } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
265 Jmsg(jcr, M_INFO, 0, _("Max Volume jobs=%s exceeded. "
266 "Marking Volume \"%s\" as Used.\n"),
267 edit_uint64_with_commas(mr->MaxVolJobs, ed1), mr->VolumeName);
268 Dmsg3(dbglvl, "MaxVolJobs=%d JobId=%d Vol=%s\n", mr->MaxVolJobs,
269 (uint32_t)jcr->JobId, mr->VolumeName);
270 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
273 /* Now see if Max Files written to volume */
274 } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
275 Jmsg(jcr, M_INFO, 0, _("Max Volume files=%s exceeded. "
276 "Marking Volume \"%s\" as Used.\n"),
277 edit_uint64_with_commas(mr->MaxVolFiles, ed1), mr->VolumeName);
278 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
281 /* Finally, check Use duration expiration */
282 } else if (mr->VolUseDuration > 0) {
283 utime_t now = time(NULL);
284 /* See if Vol Use has expired */
285 if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
286 Jmsg(jcr, M_INFO, 0, _("Max configured use duration=%s sec. exceeded. "
287 "Marking Volume \"%s\" as Used.\n"),
288 edit_uint64_with_commas(mr->VolUseDuration, ed1), mr->VolumeName);
289 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
295 /* Need to update media */
296 Dmsg1(dbglvl, "Vol=%s has expired update media record\n", mr->VolumeName);
297 set_storageid_in_mr(NULL, mr);
298 if (!db_update_media_record(jcr, jcr->db, mr)) {
299 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
300 mr->VolumeName, db_strerror(jcr->db));
303 Dmsg2(dbglvl, "Vol=%s expired=%d\n", mr->VolumeName, expired);
308 * Try hard to recycle the current volume
310 * Returns: on failure - reason = NULL
311 * on success - reason - pointer to reason
313 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
319 /* Check if a duration or limit has expired */
320 if (has_volume_expired(jcr, mr)) {
321 *reason = _("volume has expired");
322 /* Keep going because we may be able to recycle volume */
326 * Now see if we can use the volume as is
328 if (strcmp(mr->VolStatus, "Append") == 0 ||
329 strcmp(mr->VolStatus, "Recycle") == 0) {
335 * Check if the Volume is already marked for recycling
337 if (strcmp(mr->VolStatus, "Purged") == 0) {
338 if (recycle_volume(jcr, mr)) {
339 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
343 /* In principle this shouldn't happen */
344 *reason = _("and recycling of current volume failed");
349 /* At this point, the volume is not valid for writing */
350 *reason = _("but should be Append, Purged or Recycle");
353 * What we're trying to do here is see if the current volume is
354 * "recyclable" - ie. if we prune all expired jobs off it, is
355 * it now possible to reuse it for the job that it is currently
359 *reason = _("volume has recycling disabled");
363 * Check retention period from last written, but recycle to within
364 * a minute to try to catch close calls ...
366 if ((mr->LastWritten + mr->VolRetention - 60) < (utime_t)time(NULL)
367 && jcr->pool->recycle_current_volume
368 && (strcmp(mr->VolStatus, "Full") == 0 ||
369 strcmp(mr->VolStatus, "Used") == 0)) {
371 * Attempt prune of current volume to see if we can
372 * recycle it for use.
376 ua = new_ua_context(jcr);
377 ok = prune_volume(ua, mr);
381 /* If fully purged, recycle current volume */
382 if (recycle_volume(jcr, mr)) {
383 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
386 *reason = _("but should be Append, Purged or Recycle (recycling of the "
387 "current volume failed)");
390 *reason = _("but should be Append, Purged or Recycle (cannot automatically "
391 "recycle current volume, as it still contains unpruned data "
392 "or the Volume Retention time has not expired.)");
397 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
399 bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr,
402 MEDIA_DBR smr; /* for searching scratch pool */
407 /* Only one thread at a time can pull from the scratch pool */
410 * Get Pool record for Scratch Pool
411 * choose between ScratchPoolId and Scratch
412 * db_get_pool_numvols will first try ScratchPoolId,
413 * and then try the pool named Scratch
415 memset(&spr, 0, sizeof(spr));
416 bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
417 spr.PoolId = mr->ScratchPoolId;
418 if (db_get_pool_record(jcr, jcr->db, &spr)) {
419 smr.PoolId = spr.PoolId;
420 bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus)); /* want only appendable volumes */
421 bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
424 * If we do not find a valid Scratch volume, try
425 * recycling any existing purged volumes, then
426 * try to take the oldest volume.
428 set_storageid_in_mr(store, &smr); /* put StorageId in new record */
429 if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
432 } else if (find_recycled_volume(jcr, InChanger, &smr, store)) {
435 } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr, store)) {
440 POOL_MEM query(PM_MESSAGE);
443 * Get pool record where the Scratch Volume will go to ensure
444 * that we can add a Volume.
446 memset(&pr, 0, sizeof(pr));
447 bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
449 if (!db_get_pool_numvols(jcr, jcr->db, &pr)) {
450 Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"),
451 db_strerror(jcr->db));
455 /* Make sure there is room for another volume */
456 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
457 Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"),
458 jcr->pool->name(), pr.MaxVols);
463 set_storageid_in_mr(store, mr);
465 /* Set default parameters from current pool */
466 set_pool_dbr_defaults_in_media_dbr(mr, &pr);
469 * set_pool_dbr_defaults_in_media_dbr set VolStatus to Append,
470 * we could have Recycled media, also, we retain the old
473 bstrncpy(mr->VolStatus, smr.VolStatus, sizeof(smr.VolStatus));
474 mr->RecyclePoolId = smr.RecyclePoolId;
476 if (!db_update_media_record(jcr, jcr->db, mr)) {
477 Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
478 db_strerror(jcr->db));
482 Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"),