]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/next_vol.c
Tweak copyright dates + subroutine name
[bacula/bacula] / bacula / src / dird / next_vol.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2001-2010 Free Software Foundation Europe e.V.
5
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
11    in the file LICENSE.
12
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.
17
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
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
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.
27 */
28 /*
29  *
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.
33
34  *     Kern Sibbald, March MMI
35  *
36  */
37
38 #include "bacula.h"
39 #include "dird.h"
40
41 /*
42  *  Items needed:
43  *   mr.PoolId must be set
44  *   mr.StorageId should also be set
45  *   mr.ScratchPoolId could be set (used if create==true)
46  *   jcr->wstore
47  *   jcr->db
48  *   jcr->pool
49  *   MEDIA_DBR mr with PoolId set
50  *   create -- whether or not to create a new volume
51  */
52 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index,             
53                                 bool create, bool prune)
54 {
55    int retry = 0;
56    bool ok;
57    bool InChanger;
58    STORE *store = jcr->wstore;
59
60    bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
61    Dmsg3(100, "find_next_vol_for_append: JobId=%u PoolId=%d, MediaType=%s\n", 
62          (uint32_t)jcr->JobId, (int)mr->PoolId, mr->MediaType);
63    /*
64     * If we are using an Autochanger, restrict Volume
65     *   search to the Autochanger on the first pass
66     */
67    InChanger = store->autochanger;
68    /*
69     * Find the Next Volume for Append
70     */
71    db_lock(jcr->db);
72    for ( ;; ) {
73       bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus));  /* want only appendable volumes */
74       /*
75        *  1. Look for volume with "Append" status.
76        */
77       ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
78
79       if (!ok) {
80          Dmsg4(150, "after find_next_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
81                ok, index, InChanger, mr->VolStatus);
82          /*
83           * 2. Try finding a recycled volume
84           */
85          ok = find_recycled_volume(jcr, InChanger, mr);
86          Dmsg2(150, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten);
87          if (!ok) {
88             /*
89              * 3. Try recycling any purged volume
90              */
91             ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
92             if (!ok) {
93                /*
94                 * 4. Try pruning Volumes
95                 */
96                if (prune) {
97                   Dmsg0(150, "Call prune_volumes\n");
98                   prune_volumes(jcr, InChanger, mr);
99                }
100                ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
101                if (!ok && create) {
102                   Dmsg4(150, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
103                         ok, index, InChanger, mr->VolStatus);
104                   /*
105                    * 5. Try pulling a volume from the Scratch pool
106                    */ 
107                   ok = get_scratch_volume(jcr, InChanger, mr);
108                   Dmsg4(150, "after get scratch volume ok=%d index=%d InChanger=%d Vstat=%s\n",
109                         ok, index, InChanger, mr->VolStatus);
110                }
111                /*
112                 * If we are using an Autochanger and have not found
113                 * a volume, retry looking for any volume. 
114                 */
115                if (!ok && InChanger) {
116                   InChanger = false;
117                   continue;           /* retry again accepting any volume */
118                }
119             }
120          }
121
122
123          if (!ok && create) {
124             /*
125              * 6. Try "creating" a new Volume
126              */
127             ok = newVolume(jcr, mr);
128          }
129          /*
130           *  Look at more drastic ways to find an Appendable Volume
131           */
132          if (!ok && (jcr->pool->purge_oldest_volume ||
133                      jcr->pool->recycle_oldest_volume)) {
134             Dmsg2(200, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
135                 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
136             /* Find oldest volume to recycle */
137             ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
138             Dmsg1(200, "Find oldest=%d Volume\n", ok);
139             if (ok && prune) {
140                UAContext *ua;
141                Dmsg0(200, "Try purge Volume.\n");
142                /*
143                 * 7.  Try to purging oldest volume only if not UA calling us.
144                 */
145                ua = new_ua_context(jcr);
146                if (jcr->pool->purge_oldest_volume && create) {
147                   Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
148                   ok = purge_jobs_from_volume(ua, mr);
149                /*
150                 * 8. or try recycling the oldest volume
151                 */
152                } else if (jcr->pool->recycle_oldest_volume) {
153                   Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
154                   ok = prune_volume(ua, mr);
155                }
156                free_ua_context(ua);
157                if (ok) {
158                   ok = recycle_volume(jcr, mr);
159                   Dmsg1(400, "Recycle after purge oldest=%d\n", ok);
160                }
161             }
162          }
163       }
164       Dmsg2(100, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
165       if (ok) {
166          /* If we can use the volume, check if it is expired */
167          if (has_volume_expired(jcr, mr)) {
168             if (retry++ < 200) {            /* sanity check */
169                continue;                    /* try again from the top */
170             } else {
171                Jmsg(jcr, M_ERROR, 0, _(
172 "We seem to be looping trying to find the next volume. I give up.\n"));
173             }
174          }
175       }
176       break;
177    } /* end for loop */
178    db_unlock(jcr->db);
179    Dmsg1(150, "return ok=%d find_next_vol\n", ok);
180    return ok;
181 }
182
183 /*
184  * Check if any time limits or use limits have expired
185  *   if so, set the VolStatus appropriately.
186  */
187 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
188 {
189    bool expired = false;
190    /*
191     * Check limits and expirations if "Append" and it has been used
192     * i.e. mr->VolJobs > 0
193     *
194     */
195    if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
196       /* First handle Max Volume Bytes */
197       if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
198          Jmsg(jcr, M_INFO, 0, _("Max Volume bytes exceeded. "
199              "Marking Volume \"%s\" as Full.\n"), mr->VolumeName);
200          bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
201          expired = true;
202
203       /* Now see if Volume should only be used once */
204       } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
205          Jmsg(jcr, M_INFO, 0, _("Volume used once. "
206              "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
207          bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
208          expired = true;
209
210       /* Now see if Max Jobs written to volume */
211       } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
212          Jmsg(jcr, M_INFO, 0, _("Max Volume jobs exceeded. "
213              "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
214          Dmsg3(100, "MaxVolJobs=%d JobId=%d Vol=%s\n", mr->MaxVolJobs,
215                (uint32_t)jcr->JobId, mr->VolumeName);
216          bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
217          expired = true;
218
219       /* Now see if Max Files written to volume */
220       } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
221          Jmsg(jcr, M_INFO, 0, _("Max Volume files exceeded. "
222              "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
223          bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
224          expired = true;
225
226       /* Finally, check Use duration expiration */
227       } else if (mr->VolUseDuration > 0) {
228          utime_t now = time(NULL);
229          /* See if Vol Use has expired */
230          if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
231             Jmsg(jcr, M_INFO, 0, _("Max configured use duration exceeded. "
232                "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
233             bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
234             expired = true;
235          }
236       }
237    }
238    if (expired) {
239       /* Need to update media */
240       Dmsg1(150, "Vol=%s has expired update media record\n", mr->VolumeName);
241       if (!db_update_media_record(jcr, jcr->db, mr)) {
242          Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
243               mr->VolumeName, db_strerror(jcr->db));
244       }
245    }
246    Dmsg2(150, "Vol=%s expired=%d\n", mr->VolumeName, expired);
247    return expired;
248 }
249
250 /*
251  * Try hard to recycle the current volume
252  *
253  *  Returns: on failure - reason = NULL
254  *           on success - reason - pointer to reason
255  */
256 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
257 {
258    int ok;
259
260    *reason = NULL;
261
262    /*  Check if a duration or limit has expired */
263    if (has_volume_expired(jcr, mr)) {
264       *reason = _("volume has expired");
265       /* Keep going because we may be able to recycle volume */
266    }
267
268    /*
269     * Now see if we can use the volume as is
270     */
271    if (strcmp(mr->VolStatus, "Append") == 0 ||
272        strcmp(mr->VolStatus, "Recycle") == 0) {
273       *reason = NULL;
274       return;
275    }
276
277    /*
278     * Check if the Volume is already marked for recycling
279     */
280    if (strcmp(mr->VolStatus, "Purged") == 0) {
281       if (recycle_volume(jcr, mr)) {
282          Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
283          *reason = NULL;
284          return;
285       } else {
286          /* In principle this shouldn't happen */
287          *reason = _("and recycling of current volume failed");
288          return;
289       }
290    }
291
292    /* At this point, the volume is not valid for writing */
293    *reason = _("but should be Append, Purged or Recycle");
294
295    /*
296     * What we're trying to do here is see if the current volume is
297     * "recyclable" - ie. if we prune all expired jobs off it, is
298     * it now possible to reuse it for the job that it is currently
299     * needed for?
300     */
301    if (!mr->Recycle) {
302       *reason = _("volume has recycling disabled");
303       return;
304    }
305    if ((mr->LastWritten + mr->VolRetention) < (utime_t)time(NULL)
306          && jcr->pool->recycle_current_volume
307          && (strcmp(mr->VolStatus, "Full") == 0 ||
308             strcmp(mr->VolStatus, "Used") == 0)) {
309       /*
310        * Attempt prune of current volume to see if we can
311        * recycle it for use.
312        */
313       UAContext *ua;
314
315       ua = new_ua_context(jcr);
316       ok = prune_volume(ua, mr);
317       free_ua_context(ua);
318
319       if (ok) {
320          /* If fully purged, recycle current volume */
321          if (recycle_volume(jcr, mr)) {
322             Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
323             *reason = NULL;
324          } else {
325             *reason = _("but should be Append, Purged or Recycle (recycling of the "
326                "current volume failed)");
327          }
328       } else {
329          *reason = _("but should be Append, Purged or Recycle (cannot automatically "
330             "recycle current volume, as it still contains unpruned data "
331             "or the Volume Retention time has not expired.)");
332       }
333    }
334 }
335
336 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
337
338 bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr)
339 {
340    MEDIA_DBR smr;                        /* for searching scratch pool */
341    POOL_DBR spr, pr;
342    bool ok = false;
343    bool found = false;
344
345    /* Only one thread at a time can pull from the scratch pool */
346    P(mutex);
347    /* 
348     * Get Pool record for Scratch Pool
349     * choose between ScratchPoolId and Scratch
350     * db_get_pool_record will first try ScratchPoolId, 
351     * and then try the pool named Scratch
352     */
353    memset(&spr, 0, sizeof(spr));
354    bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
355    spr.PoolId = mr->ScratchPoolId;
356    if (db_get_pool_record(jcr, jcr->db, &spr)) {
357       memset(&smr, 0, sizeof(smr));
358       smr.PoolId = spr.PoolId;
359       if (InChanger) {       
360          smr.StorageId = mr->StorageId;  /* want only Scratch Volumes in changer */
361       }
362       bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus));  /* want only appendable volumes */
363       bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
364
365       /*
366        * If we do not find a valid Scratch volume, try
367        *  recycling any existing purged volumes, then
368        *  try to take the oldest volume.
369        */
370       if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
371          found = true;
372
373       } else if (find_recycled_volume(jcr, InChanger, &smr)) {
374          found = true;
375
376       } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr)) {
377          found = true;
378       }
379
380       if (found) {
381          POOL_MEM query(PM_MESSAGE);
382
383          /*   
384           * Get pool record where the Scratch Volume will go to ensure
385           * that we can add a Volume.
386           */
387          memset(&pr, 0, sizeof(pr));
388          bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
389
390          if (!db_get_pool_record(jcr, jcr->db, &pr)) {
391             Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"), 
392                  db_strerror(jcr->db));
393             goto bail_out;
394          }
395          
396          /* Make sure there is room for another volume */
397          if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
398             Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"),
399                  jcr->pool->name(), pr.MaxVols);
400             goto bail_out;
401          }
402
403          memcpy(mr, &smr, sizeof(MEDIA_DBR)); 
404
405          /* Set default parameters from current pool */
406          set_pool_dbr_defaults_in_media_dbr(mr, &pr);
407
408          /*
409           * set_pool_dbr_defaults_in_media_dbr set VolStatus to Append,
410           *   we could have Recycled media, also, we retain the old
411           *   RecyclePoolId.
412           */
413          bstrncpy(mr->VolStatus, smr.VolStatus, sizeof(smr.VolStatus));
414          mr->RecyclePoolId = smr.RecyclePoolId;
415
416          if (!db_update_media_record(jcr, jcr->db, mr)) {
417             Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
418                  db_strerror(jcr->db));
419             goto bail_out;
420          }
421
422          Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"), 
423               mr->VolumeName);
424          
425          ok = true;
426       }
427    }
428 bail_out:
429    V(mutex);
430    return ok;
431 }