2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2007 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 two of the GNU 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 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 John Walker.
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
44 * mr.PoolId must be set
48 * MEDIA_DBR mr with PoolId set
49 * create -- whether or not to create a new volume
51 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index,
52 bool create, bool prune)
57 STORE *store = jcr->wstore;
59 bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
60 Dmsg2(150, "find_next_vol_for_append: PoolId=%d, MediaType=%s\n", (int)mr->PoolId, mr->MediaType);
62 * If we are using an Autochanger, restrict Volume
63 * search to the Autochanger on the first pass
65 InChanger = store->autochanger;
67 * Find the Next Volume for Append
71 bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus)); /* want only appendable volumes */
73 * 1. Look for volume with "Append" status.
75 ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
78 Dmsg4(050, "after find_next_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
79 ok, index, InChanger, mr->VolStatus);
81 * 2. Try finding a recycled volume
83 ok = find_recycled_volume(jcr, InChanger, mr);
84 Dmsg2(150, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten);
87 * 3. Try recycling any purged volume
89 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
92 * 4. Try pruning Volumes
95 Dmsg0(150, "Call prune_volumes\n");
96 prune_volumes(jcr, InChanger, mr);
98 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
100 Dmsg4(050, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
101 ok, index, InChanger, mr->VolStatus);
103 * 5. Try pulling a volume from the Scratch pool
105 ok = get_scratch_volume(jcr, InChanger, mr);
108 * If we are using an Autochanger and have not found
109 * a volume, retry looking for any volume.
114 continue; /* retry again accepting any volume */
123 * 6. Try "creating" a new Volume
125 ok = newVolume(jcr, mr);
128 * Look at more drastic ways to find an Appendable Volume
130 if (!ok && (jcr->pool->purge_oldest_volume ||
131 jcr->pool->recycle_oldest_volume)) {
132 Dmsg2(200, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
133 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
134 /* Find oldest volume to recycle */
135 ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
136 Dmsg1(400, "Find oldest=%d\n", ok);
139 Dmsg0(400, "Try purge.\n");
141 * 7. Try to purging oldest volume only if not UA calling us.
143 ua = new_ua_context(jcr);
144 if (jcr->pool->purge_oldest_volume && create) {
145 Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
146 ok = purge_jobs_from_volume(ua, mr);
148 * 8. or try recycling the oldest volume
150 } else if (jcr->pool->recycle_oldest_volume) {
151 Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
152 ok = prune_volume(ua, mr);
156 ok = recycle_volume(jcr, mr);
157 Dmsg1(400, "Recycle after purge oldest=%d\n", ok);
162 Dmsg2(100, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
164 /* If we can use the volume, check if it is expired */
165 if (has_volume_expired(jcr, mr)) {
166 if (retry++ < 200) { /* sanity check */
167 continue; /* try again from the top */
169 Jmsg(jcr, M_ERROR, 0, _(
170 "We seem to be looping trying to find the next volume. I give up.\n"));
177 Dmsg1(150, "return ok=%d find_next_vol\n", ok);
182 * Check if any time limits or use limits have expired
183 * if so, set the VolStatus appropriately.
185 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
187 bool expired = false;
189 * Check limits and expirations if "Append" and it has been used
190 * i.e. mr->VolJobs > 0
193 if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
194 /* First handle Max Volume Bytes */
195 if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
196 Jmsg(jcr, M_INFO, 0, _("Max Volume bytes exceeded. "
197 "Marking Volume \"%s\" as Full.\n"), mr->VolumeName);
198 bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
201 /* Now see if Volume should only be used once */
202 } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
203 Jmsg(jcr, M_INFO, 0, _("Volume used once. "
204 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
205 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
208 /* Now see if Max Jobs written to volume */
209 } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
210 Jmsg(jcr, M_INFO, 0, _("Max Volume jobs exceeded. "
211 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
212 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
215 /* Now see if Max Files written to volume */
216 } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
217 Jmsg(jcr, M_INFO, 0, _("Max Volume files exceeded. "
218 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
219 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
222 /* Finally, check Use duration expiration */
223 } else if (mr->VolUseDuration > 0) {
224 utime_t now = time(NULL);
225 /* See if Vol Use has expired */
226 if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
227 Jmsg(jcr, M_INFO, 0, _("Max configured use duration exceeded. "
228 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
229 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
235 /* Need to update media */
236 if (!db_update_media_record(jcr, jcr->db, mr)) {
237 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
238 mr->VolumeName, db_strerror(jcr->db));
245 * Try hard to recycle the current volume
247 * Returns: on failure - reason = NULL
248 * on success - reason - pointer to reason
250 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
256 /* Check if a duration or limit has expired */
257 if (has_volume_expired(jcr, mr)) {
258 *reason = _("volume has expired");
259 /* Keep going because we may be able to recycle volume */
263 * Now see if we can use the volume as is
265 if (strcmp(mr->VolStatus, "Append") == 0 ||
266 strcmp(mr->VolStatus, "Recycle") == 0) {
272 * Check if the Volume is already marked for recycling
274 if (strcmp(mr->VolStatus, "Purged") == 0) {
275 if (recycle_volume(jcr, mr)) {
276 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
280 /* In principle this shouldn't happen */
281 *reason = _("and recycling of current volume failed");
286 /* At this point, the volume is not valid for writing */
287 *reason = _("but should be Append, Purged or Recycle");
290 * What we're trying to do here is see if the current volume is
291 * "recyclable" - ie. if we prune all expired jobs off it, is
292 * it now possible to reuse it for the job that it is currently
295 if ((mr->LastWritten + mr->VolRetention) < (utime_t)time(NULL)
296 && mr->Recycle && jcr->pool->recycle_current_volume
297 && (strcmp(mr->VolStatus, "Full") == 0 ||
298 strcmp(mr->VolStatus, "Used") == 0)) {
300 * Attempt prune of current volume to see if we can
301 * recycle it for use.
305 ua = new_ua_context(jcr);
306 ok = prune_volume(ua, mr);
310 /* If fully purged, recycle current volume */
311 if (recycle_volume(jcr, mr)) {
312 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
315 *reason = _("but should be Append, Purged or Recycle (recycling of the "
316 "current volume failed)");
319 *reason = _("but should be Append, Purged or Recycle (cannot automatically "
320 "recycle current volume, as it still contains unpruned data "
321 "or the Volume Retention time has not expired.)");
326 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
328 bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr)
330 MEDIA_DBR smr; /* for searching scratch pool */
335 /* Only one thread at a time can pull from the scratch pool */
338 * Get Pool record for Scratch Pool
340 memset(&spr, 0, sizeof(spr));
341 bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
342 if (db_get_pool_record(jcr, jcr->db, &spr)) {
343 memset(&smr, 0, sizeof(smr));
344 smr.PoolId = spr.PoolId;
346 smr.StorageId = mr->StorageId; /* want only Scratch Volumes in changer */
348 bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus)); /* want only appendable volumes */
349 bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
352 * If we do not find a valid Scratch volume, try
353 * recycling any existing purged volumes, then
354 * try to take the oldest volume.
356 if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
359 } else if (find_recycled_volume(jcr, InChanger, &smr)) {
362 } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr)) {
367 POOL_MEM query(PM_MESSAGE);
370 * Get pool record where the Scratch Volume will go to ensure
371 * that we can add a Volume.
373 memset(&pr, 0, sizeof(pr));
374 bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
376 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
377 Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"),
378 db_strerror(jcr->db));
382 /* Make sure there is room for another volume */
383 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
384 Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"),
385 jcr->pool->name(), pr.MaxVols);
389 memcpy(mr, &smr, sizeof(MEDIA_DBR));
391 /* Set default parameters from current pool */
392 set_pool_dbr_defaults_in_media_dbr(mr, &pr);
395 * set_pool_dbr_defaults_in_media_dbr set VolStatus to Append,
396 * we could have Recycled media, also, we retain the old
399 bstrncpy(mr->VolStatus, smr.VolStatus, sizeof(smr.VolStatus));
400 mr->RecyclePoolId = smr.RecyclePoolId;
402 if (!db_update_media_record(jcr, jcr->db, mr)) {
403 Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
404 db_strerror(jcr->db));
408 Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"),