2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 Bacula® is a registered trademark of Kern Sibbald.
18 * Bacula Director -- next_vol -- handles finding the next
19 * volume for append. Split out of catreq.c August MMIII
20 * catalog request from the Storage daemon.
22 * Written by Kern Sibbald, March MMI
29 static int const dbglvl = 50; /* debug level */
32 * We setup the StorageId if it is
33 * an autochanger from the Storage and put it in
35 * store == NULL => use existing StorageId
37 void set_storageid_in_mr(STORE *store, MEDIA_DBR *mr)
42 /* At this point we know store != NULL */
43 mr->StorageId = store->StorageId;
48 * mr.PoolId must be set
49 * mr.ScratchPoolId could be set (used if create==true)
53 * MEDIA_DBR mr with PoolId set
54 * create -- whether or not to create a new volume
56 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index,
57 bool create, bool prune)
62 STORE *store = jcr->wstore;
64 bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
65 Dmsg6(dbglvl, "find_next_vol_for_append: JobId=%u PoolId=%d, MediaType=%s index=%d create=%d prune=%d\n",
66 (uint32_t)jcr->JobId, (int)mr->PoolId, mr->MediaType, index,
69 * If we are using an Autochanger, restrict Volume
70 * search to the Autochanger on the first pass
72 InChanger = (store->autochanger)? true : false;
75 * Find the Next Volume for Append
79 bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus)); /* want only appendable volumes */
81 * 1. Look for volume with "Append" status.
83 set_storageid_in_mr(store, mr); /* put StorageId in new record */
84 ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
88 * No volume found, apply algorithm
90 Dmsg4(dbglvl, "after find_next_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
91 ok, index, InChanger, mr->VolStatus);
93 * 2. Try finding a recycled volume
95 ok = find_recycled_volume(jcr, InChanger, mr, store);
96 set_storageid_in_mr(store, mr); /* put StorageId in new record */
97 Dmsg2(dbglvl, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten);
100 * 3. Try recycling any purged volume
102 ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store);
103 set_storageid_in_mr(store, mr); /* put StorageId in new record */
106 * 4. Try pruning Volumes
109 Dmsg0(dbglvl, "Call prune_volumes\n");
110 prune_volumes(jcr, InChanger, mr, store);
112 ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store);
113 set_storageid_in_mr(store, mr); /* put StorageId in new record */
115 Dmsg4(dbglvl, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
116 ok, index, InChanger, mr->VolStatus);
118 * 5. Try pulling a volume from the Scratch pool
120 ok = get_scratch_volume(jcr, InChanger, mr, store);
121 set_storageid_in_mr(store, mr); /* put StorageId in new record */
122 Dmsg4(dbglvl, "after get scratch volume ok=%d index=%d InChanger=%d Vstat=%s\n",
123 ok, index, InChanger, mr->VolStatus);
126 * If we are using an Autochanger and have not found
127 * a volume, retry looking for any volume.
129 if (!ok && InChanger) {
131 continue; /* retry again accepting any volume */
139 * 6. Try "creating" a new Volume
141 ok = newVolume(jcr, mr, store);
144 * Look at more drastic ways to find an Appendable Volume
146 if (!ok && (jcr->pool->purge_oldest_volume ||
147 jcr->pool->recycle_oldest_volume)) {
148 Dmsg2(dbglvl, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
149 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
150 /* Find oldest volume to recycle */
151 set_storageid_in_mr(store, mr); /* update storage id */
152 ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
153 set_storageid_in_mr(store, mr); /* update storageid */
154 Dmsg1(dbglvl, "Find oldest=%d Volume\n", ok);
157 Dmsg0(dbglvl, "Try purge Volume.\n");
159 * 7. Try to purging oldest volume only if not UA calling us.
161 ua = new_ua_context(jcr);
162 if (jcr->pool->purge_oldest_volume && create) {
163 Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
164 ok = purge_jobs_from_volume(ua, mr);
166 * 8. or try recycling the oldest volume
168 } else if (jcr->pool->recycle_oldest_volume) {
169 Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
170 ok = prune_volume(ua, mr);
174 ok = recycle_volume(jcr, mr);
175 Dmsg1(dbglvl, "Recycle after purge oldest=%d\n", ok);
180 Dmsg2(dbglvl, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
182 /* If we can use the volume, check if it is expired */
183 if (has_volume_expired(jcr, mr)) {
184 if (retry++ < 200) { /* sanity check */
185 continue; /* try again from the top */
187 Jmsg(jcr, M_ERROR, 0, _(
188 "We seem to be looping trying to find the next volume. I give up.\n"));
195 Dmsg1(dbglvl, "return ok=%d find_next_vol\n", ok);
200 * Check if any time limits or use limits have expired
201 * if so, set the VolStatus appropriately.
203 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
205 bool expired = false;
208 * Check limits and expirations if "Append" and it has been used
209 * i.e. mr->VolJobs > 0
212 if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
213 /* First handle Max Volume Bytes */
214 if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
215 Jmsg(jcr, M_INFO, 0, _("Max Volume bytes=%s exceeded. "
216 "Marking Volume \"%s\" as Full.\n"),
217 edit_uint64_with_commas(mr->MaxVolBytes, ed1), mr->VolumeName);
218 bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
221 /* Now see if Volume should only be used once */
222 } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
223 Jmsg(jcr, M_INFO, 0, _("Volume used once. "
224 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
225 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
228 /* Now see if Max Jobs written to volume */
229 } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
230 Jmsg(jcr, M_INFO, 0, _("Max Volume jobs=%s exceeded. "
231 "Marking Volume \"%s\" as Used.\n"),
232 edit_uint64_with_commas(mr->MaxVolJobs, ed1), mr->VolumeName);
233 Dmsg3(dbglvl, "MaxVolJobs=%d JobId=%d Vol=%s\n", mr->MaxVolJobs,
234 (uint32_t)jcr->JobId, mr->VolumeName);
235 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
238 /* Now see if Max Files written to volume */
239 } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
240 Jmsg(jcr, M_INFO, 0, _("Max Volume files=%s exceeded. "
241 "Marking Volume \"%s\" as Used.\n"),
242 edit_uint64_with_commas(mr->MaxVolFiles, ed1), mr->VolumeName);
243 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
246 /* Finally, check Use duration expiration */
247 } else if (mr->VolUseDuration > 0) {
248 utime_t now = time(NULL);
249 /* See if Vol Use has expired */
250 if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
251 Jmsg(jcr, M_INFO, 0, _("Max configured use duration=%s sec. exceeded. "
252 "Marking Volume \"%s\" as Used.\n"),
253 edit_uint64_with_commas(mr->VolUseDuration, ed1), mr->VolumeName);
254 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
260 /* Need to update media */
261 Dmsg1(dbglvl, "Vol=%s has expired update media record\n", mr->VolumeName);
262 set_storageid_in_mr(NULL, mr);
263 if (!db_update_media_record(jcr, jcr->db, mr)) {
264 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
265 mr->VolumeName, db_strerror(jcr->db));
268 Dmsg2(dbglvl, "Vol=%s expired=%d\n", mr->VolumeName, expired);
273 * Try hard to recycle the current volume
275 * Returns: on failure - reason = NULL
276 * on success - reason - pointer to reason
278 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
284 /* Check if a duration or limit has expired */
285 if (has_volume_expired(jcr, mr)) {
286 *reason = _("volume has expired");
287 /* Keep going because we may be able to recycle volume */
291 * Now see if we can use the volume as is
293 if (strcmp(mr->VolStatus, "Append") == 0 ||
294 strcmp(mr->VolStatus, "Recycle") == 0) {
300 * Check if the Volume is already marked for recycling
302 if (strcmp(mr->VolStatus, "Purged") == 0) {
303 if (recycle_volume(jcr, mr)) {
304 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
308 /* In principle this shouldn't happen */
309 *reason = _("and recycling of current volume failed");
314 /* At this point, the volume is not valid for writing */
315 *reason = _("but should be Append, Purged or Recycle");
318 * What we're trying to do here is see if the current volume is
319 * "recyclable" - ie. if we prune all expired jobs off it, is
320 * it now possible to reuse it for the job that it is currently
324 *reason = _("volume has recycling disabled");
328 * Check retention period from last written, but recycle to within
329 * a minute to try to catch close calls ...
331 if ((mr->LastWritten + mr->VolRetention - 60) < (utime_t)time(NULL)
332 && jcr->pool->recycle_current_volume
333 && (strcmp(mr->VolStatus, "Full") == 0 ||
334 strcmp(mr->VolStatus, "Used") == 0)) {
336 * Attempt prune of current volume to see if we can
337 * recycle it for use.
341 ua = new_ua_context(jcr);
342 ok = prune_volume(ua, mr);
346 /* If fully purged, recycle current volume */
347 if (recycle_volume(jcr, mr)) {
348 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
351 *reason = _("but should be Append, Purged or Recycle (recycling of the "
352 "current volume failed)");
355 *reason = _("but should be Append, Purged or Recycle (cannot automatically "
356 "recycle current volume, as it still contains unpruned data "
357 "or the Volume Retention time has not expired.)");
362 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
364 bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr,
367 MEDIA_DBR smr; /* for searching scratch pool */
372 /* Only one thread at a time can pull from the scratch pool */
375 * Get Pool record for Scratch Pool
376 * choose between ScratchPoolId and Scratch
377 * db_get_pool_numvols will first try ScratchPoolId,
378 * and then try the pool named Scratch
380 memset(&spr, 0, sizeof(spr));
381 bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
382 spr.PoolId = mr->ScratchPoolId;
383 if (db_get_pool_record(jcr, jcr->db, &spr)) {
384 smr.PoolId = spr.PoolId;
385 bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus)); /* want only appendable volumes */
386 bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
389 * If we do not find a valid Scratch volume, try
390 * recycling any existing purged volumes, then
391 * try to take the oldest volume.
393 set_storageid_in_mr(store, &smr); /* put StorageId in new record */
394 if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
397 } else if (find_recycled_volume(jcr, InChanger, &smr, store)) {
400 } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr, store)) {
405 POOL_MEM query(PM_MESSAGE);
408 * Get pool record where the Scratch Volume will go to ensure
409 * that we can add a Volume.
411 memset(&pr, 0, sizeof(pr));
412 bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
414 if (!db_get_pool_numvols(jcr, jcr->db, &pr)) {
415 Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"),
416 db_strerror(jcr->db));
420 /* Make sure there is room for another volume */
421 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
422 Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"),
423 jcr->pool->name(), pr.MaxVols);
428 set_storageid_in_mr(store, mr);
430 /* Set default parameters from current pool */
431 set_pool_dbr_defaults_in_media_dbr(mr, &pr);
434 * set_pool_dbr_defaults_in_media_dbr set VolStatus to Append,
435 * we could have Recycled media, also, we retain the old
438 bstrncpy(mr->VolStatus, smr.VolStatus, sizeof(smr.VolStatus));
439 mr->RecyclePoolId = smr.RecyclePoolId;
441 if (!db_update_media_record(jcr, jcr->db, mr)) {
442 Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
443 db_strerror(jcr->db));
447 Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"),