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 plus additions
11 that are listed in the file LICENSE.
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
42 static bool get_scratch_volume(JCR *jcr, MEDIA_DBR *mr, bool InChanger);
47 * mr.PoolId must be set
51 * MEDIA_DBR mr with PoolId set
52 * create -- whether or not to create a new volume
54 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index,
55 bool create, bool prune)
60 STORE *store = jcr->wstore;
62 bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
63 Dmsg2(150, "find_next_vol_for_append: PoolId=%d, MediaType=%s\n", (int)mr->PoolId, mr->MediaType);
65 * If we are using an Autochanger, restrict Volume
66 * search to the Autochanger on the first pass
68 InChanger = store->autochanger;
70 * Find the Next Volume for Append
74 bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus)); /* want only appendable volumes */
76 * 1. Look for volume with "Append" status.
78 ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
81 Dmsg4(050, "after find_next_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
82 ok, index, InChanger, mr->VolStatus);
84 * 2. Try finding a recycled volume
86 ok = find_recycled_volume(jcr, InChanger, mr);
87 Dmsg2(150, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten);
90 * 3. Try recycling any purged volume
92 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
95 * 4. Try pruning Volumes
97 Dmsg0(150, "Call prune_volumes\n");
99 prune_volumes(jcr, mr);
101 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
103 Dmsg4(050, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
104 ok, index, InChanger, mr->VolStatus);
106 * 5. Try pulling a volume from the Scratch pool
108 ok = get_scratch_volume(jcr, mr, InChanger);
111 * If we are using an Autochanger and have not found
112 * a volume, retry looking for any volume.
117 continue; /* retry again accepting any volume */
126 * 6. Try "creating" a new Volume
128 ok = newVolume(jcr, mr);
131 * Look at more drastic ways to find an Appendable Volume
133 if (!ok && (jcr->pool->purge_oldest_volume ||
134 jcr->pool->recycle_oldest_volume)) {
135 Dmsg2(200, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
136 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
137 /* Find oldest volume to recycle */
138 ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
139 Dmsg1(400, "Find oldest=%d\n", ok);
142 Dmsg0(400, "Try purge.\n");
144 * 7. Try to purging oldest volume only if not UA calling us.
146 ua = new_ua_context(jcr);
147 if (jcr->pool->purge_oldest_volume && create) {
148 Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
149 ok = purge_jobs_from_volume(ua, mr);
151 * 8. or try recycling the oldest volume
153 } else if (jcr->pool->recycle_oldest_volume) {
154 Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
155 ok = prune_volume(ua, mr);
159 ok = recycle_volume(jcr, mr);
160 Dmsg1(400, "Recycle after purge oldest=%d\n", ok);
165 Dmsg2(100, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
167 /* If we can use the volume, check if it is expired */
168 if (has_volume_expired(jcr, mr)) {
169 if (retry++ < 200) { /* sanity check */
170 continue; /* try again from the top */
172 Jmsg(jcr, M_ERROR, 0, _(
173 "We seem to be looping trying to find the next volume. I give up.\n"));
180 Dmsg1(150, "return ok=%d find_next_vol\n", ok);
185 * Check if any time limits or use limits have expired
186 * if so, set the VolStatus appropriately.
188 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
190 bool expired = false;
192 * Check limits and expirations if "Append" and it has been used
193 * i.e. mr->VolJobs > 0
196 if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
197 /* First handle Max Volume Bytes */
198 if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
199 Jmsg(jcr, M_INFO, 0, _("Max Volume bytes exceeded. "
200 "Marking Volume \"%s\" as Full.\n"), mr->VolumeName);
201 bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
204 /* Now see if Volume should only be used once */
205 } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
206 Jmsg(jcr, M_INFO, 0, _("Volume used once. "
207 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
208 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
211 /* Now see if Max Jobs written to volume */
212 } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
213 Jmsg(jcr, M_INFO, 0, _("Max Volume jobs exceeded. "
214 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
215 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
218 /* Now see if Max Files written to volume */
219 } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
220 Jmsg(jcr, M_INFO, 0, _("Max Volume files exceeded. "
221 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
222 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
225 /* Finally, check Use duration expiration */
226 } else if (mr->VolUseDuration > 0) {
227 utime_t now = time(NULL);
228 /* See if Vol Use has expired */
229 if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
230 Jmsg(jcr, M_INFO, 0, _("Max configured use duration exceeded. "
231 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
232 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
238 /* Need to update media */
239 if (!db_update_media_record(jcr, jcr->db, mr)) {
240 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
241 mr->VolumeName, db_strerror(jcr->db));
248 * Try hard to recycle the current volume
250 * Returns: on failure - reason = NULL
251 * on success - reason - pointer to reason
253 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
259 /* Check if a duration or limit has expired */
260 if (has_volume_expired(jcr, mr)) {
261 *reason = _("volume has expired");
262 /* Keep going because we may be able to recycle volume */
266 * Now see if we can use the volume as is
268 if (strcmp(mr->VolStatus, "Append") == 0 ||
269 strcmp(mr->VolStatus, "Recycle") == 0) {
275 * Check if the Volume is already marked for recycling
277 if (strcmp(mr->VolStatus, "Purged") == 0) {
278 if (recycle_volume(jcr, mr)) {
279 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
283 /* In principle this shouldn't happen */
284 *reason = _("and recycling of current volume failed");
289 /* At this point, the volume is not valid for writing */
290 *reason = _("but should be Append, Purged or Recycle");
293 * What we're trying to do here is see if the current volume is
294 * "recyclable" - ie. if we prune all expired jobs off it, is
295 * it now possible to reuse it for the job that it is currently
298 if ((mr->LastWritten + mr->VolRetention) < (utime_t)time(NULL)
299 && mr->Recycle && jcr->pool->recycle_current_volume
300 && (strcmp(mr->VolStatus, "Full") == 0 ||
301 strcmp(mr->VolStatus, "Used") == 0)) {
303 * Attempt prune of current volume to see if we can
304 * recycle it for use.
308 ua = new_ua_context(jcr);
309 ok = prune_volume(ua, mr);
313 /* If fully purged, recycle current volume */
314 if (recycle_volume(jcr, mr)) {
315 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
318 *reason = _("but should be Append, Purged or Recycle (recycling of the "
319 "current volume failed)");
322 *reason = _("but should be Append, Purged or Recycle (cannot automatically "
323 "recycle current volume, as it still contains unpruned data "
324 "or the Volume Retention time has not expired.)");
329 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
331 static bool get_scratch_volume(JCR *jcr, MEDIA_DBR *mr, bool InChanger)
337 char ed1[50], ed2[50];
339 /* Only one thread at a time can pull from the scratch pool */
342 * Get Pool record for Scratch Pool
344 memset(&spr, 0, sizeof(spr));
345 bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
346 if (db_get_pool_record(jcr, jcr->db, &spr)) {
347 memset(&smr, 0, sizeof(smr));
348 smr.PoolId = spr.PoolId;
350 smr.StorageId = mr->StorageId; /* want only Scratch Volumes in changer */
352 bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus)); /* want only appendable volumes */
353 bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
356 * If we do not find a valid Scratch volume, try
357 * recycling any existing purged volumes, then
358 * try to take the oldest volume.
360 if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
363 } else if (find_recycled_volume(jcr, InChanger, &smr)) {
366 } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr)) {
371 POOL_MEM query(PM_MESSAGE);
374 * Get pool record where the Scratch Volume will go to ensure
375 * that we can add a Volume.
377 memset(&pr, 0, sizeof(pr));
378 bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
379 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
380 Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"),
381 db_strerror(jcr->db));
384 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
385 Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"),
386 jcr->pool->hdr.name, pr.MaxVols);
390 /* OK, now move Scratch Volume */
392 Mmsg(query, "UPDATE Media SET PoolId=%s WHERE MediaId=%s",
393 edit_int64(mr->PoolId, ed1),
394 edit_int64(smr.MediaId, ed2));
395 ok = db_sql_query(jcr->db, query.c_str(), NULL, NULL);
398 Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
399 db_strerror(jcr->db));
402 Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"),
404 /* Set new Pool Id in smr record, then copy it to mr */
405 smr.PoolId = mr->PoolId;
406 memcpy(mr, &smr, sizeof(MEDIA_DBR));
407 /* Set default parameters from current pool */
408 set_pool_dbr_defaults_in_media_dbr(mr, &pr);
409 /* set_pool_dbr_defaults_in_media_dbr set VolStatus to Append,
410 * we could have Recycled media */
411 bstrncpy(mr->VolStatus, smr.VolStatus, sizeof(smr.VolStatus));
412 if (!db_update_media_record(jcr, jcr->db, mr)) {
413 Jmsg(jcr, M_WARNING, 0, _("Unable to update Volume record: ERR=%s"),
414 db_strerror(jcr->db));