]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/next_vol.c
Start applying new FSFE copyright.
[bacula/bacula] / bacula / src / dird / next_vol.c
1 /*
2  *
3  *   Bacula Director -- next_vol -- handles finding the next
4  *    volume for append.  Split out of catreq.c August MMIII
5  *    catalog request from the Storage daemon.
6
7  *     Kern Sibbald, March MMI
8  *
9  *   Version $Id$
10  */
11 /*
12    Bacula® - The Network Backup Solution
13
14    Copyright (C) 2001-2006 Free Software Foundation Europe e.V.
15
16    The main author of Bacula is Kern Sibbald, with contributions from
17    many others, a complete list can be found in the file AUTHORS.
18    This program is Free Software; you can redistribute it and/or
19    modify it under the terms of version two of the GNU General Public
20    License as published by the Free Software Foundation plus additions
21    that are listed in the file LICENSE.
22
23    This program is distributed in the hope that it will be useful, but
24    WITHOUT ANY WARRANTY; without even the implied warranty of
25    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26    General Public License for more details.
27
28    You should have received a copy of the GNU General Public License
29    along with this program; if not, write to the Free Software
30    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31    02110-1301, USA.
32
33    Bacula® is a registered trademark ofJohn Walker.
34    The licensor of Bacula is the Free Software Foundation Europe
35    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
36    Switzerland, email:ftf@fsfeurope.org.
37 */
38
39 #include "bacula.h"
40 #include "dird.h"
41
42 static bool get_scratch_volume(JCR *jcr, MEDIA_DBR *mr, bool InChanger);
43
44
45 /*
46  *  Items needed:
47  *   mr.PoolId must be set
48  *   jcr->wstore
49  *   jcr->db
50  *   jcr->pool
51  *   MEDIA_DBR mr (zeroed out)
52  *   create -- whether or not to create a new volume
53  */
54 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create)
55 {
56    int retry = 0;
57    bool ok;
58    bool InChanger;
59    STORE *store = jcr->wstore;
60
61    bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
62    Dmsg2(100, "CatReq FindMedia: PoolId=%d, MediaType=%s\n", (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       Dmsg4(100, "after find_next_vol index=%d ok=%d InChanger=%d Vstat=%s\n",
79             index, ok, InChanger, mr->VolStatus);
80
81       if (!ok) {
82          /*
83           * 2. Try finding a recycled volume
84           */
85          ok = find_recycled_volume(jcr, InChanger, mr);
86          Dmsg2(100, "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                prune_volumes(jcr);
97                ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
98                if (!ok) {
99                   /*
100                    * 5. Try pulling a volume from the Scratch pool
101                    */ 
102                   ok = get_scratch_volume(jcr, mr, InChanger);
103                }
104                /*
105                 * If we are using an Autochanger and have not found
106                 * a volume, retry looking for any volume. 
107                 */
108                if (InChanger) {
109                   InChanger = false;
110                   if (!ok) {
111                      continue;           /* retry again accepting any volume */
112                   }
113                }
114             }
115          }
116
117
118          if (!ok && create) {
119             /*
120              * 6. Try "creating" a new Volume
121              */
122             ok = newVolume(jcr, mr);
123          }
124          /*
125           *  Look at more drastic ways to find an Appendable Volume
126           */
127          if (!ok && (jcr->pool->purge_oldest_volume ||
128                      jcr->pool->recycle_oldest_volume)) {
129             Dmsg2(200, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
130                 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
131             /* Find oldest volume to recycle */
132             ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
133             Dmsg1(400, "Find oldest=%d\n", ok);
134             if (ok) {
135                UAContext *ua;
136                Dmsg0(400, "Try purge.\n");
137                /*
138                 * 7.  Try to purging oldest volume only if not UA calling us.
139                 */
140                ua = new_ua_context(jcr);
141                if (jcr->pool->purge_oldest_volume && create) {
142                   Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
143                   ok = purge_jobs_from_volume(ua, mr);
144                /*
145                 * 8. or try recycling the oldest volume
146                 */
147                } else if (jcr->pool->recycle_oldest_volume) {
148                   Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
149                   ok = prune_volume(ua, mr);
150                }
151                free_ua_context(ua);
152                if (ok) {
153                   ok = recycle_volume(jcr, mr);
154                   Dmsg1(400, "Recycle after purge oldest=%d\n", ok);
155                }
156             }
157          }
158       }
159       Dmsg2(100, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
160       if (ok) {
161          /* If we can use the volume, check if it is expired */
162          if (has_volume_expired(jcr, mr)) {
163             if (retry++ < 200) {            /* sanity check */
164                continue;                    /* try again from the top */
165             } else {
166                Jmsg(jcr, M_ERROR, 0, _(
167 "We seem to be looping trying to find the next volume. I give up.\n"));
168             }
169          }
170       }
171       break;
172    } /* end for loop */
173    db_unlock(jcr->db);
174    return ok;
175 }
176
177 /*
178  * Check if any time limits or use limits have expired
179  *   if so, set the VolStatus appropriately.
180  */
181 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
182 {
183    bool expired = false;
184    /*
185     * Check limits and expirations if "Append" and it has been used
186     * i.e. mr->VolJobs > 0
187     *
188     */
189    if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
190       /* First handle Max Volume Bytes */
191       if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
192          Jmsg(jcr, M_INFO, 0, _("Max Volume bytes exceeded. "
193              "Marking Volume \"%s\" as Full.\n"), mr->VolumeName);
194          bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
195          expired = true;
196
197       /* Now see if Volume should only be used once */
198       } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
199          Jmsg(jcr, M_INFO, 0, _("Volume used once. "
200              "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
201          bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
202          expired = true;
203
204       /* Now see if Max Jobs written to volume */
205       } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
206          Jmsg(jcr, M_INFO, 0, _("Max Volume jobs exceeded. "
207              "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
208          bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
209          expired = true;
210
211       /* Now see if Max Files written to volume */
212       } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
213          Jmsg(jcr, M_INFO, 0, _("Max Volume files exceeded. "
214              "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
215          bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
216          expired = true;
217
218       /* Finally, check Use duration expiration */
219       } else if (mr->VolUseDuration > 0) {
220          utime_t now = time(NULL);
221          /* See if Vol Use has expired */
222          if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
223             Jmsg(jcr, M_INFO, 0, _("Max configured use duration exceeded. "
224                "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
225             bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
226             expired = true;
227          }
228       }
229    }
230    if (expired) {
231       /* Need to update media */
232       if (!db_update_media_record(jcr, jcr->db, mr)) {
233          Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
234               mr->VolumeName, db_strerror(jcr->db));
235       }
236    }
237    return expired;
238 }
239
240 /*
241  * Try hard to recycle the current volume
242  *
243  *  Returns: on failure - reason = NULL
244  *           on success - reason - pointer to reason
245  */
246 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
247 {
248    int ok;
249
250    *reason = NULL;
251
252    /*  Check if a duration or limit has expired */
253    if (has_volume_expired(jcr, mr)) {
254       *reason = _("volume has expired");
255       /* Keep going because we may be able to recycle volume */
256    }
257
258    /*
259     * Now see if we can use the volume as is
260     */
261    if (strcmp(mr->VolStatus, "Append") == 0 ||
262        strcmp(mr->VolStatus, "Recycle") == 0) {
263       *reason = NULL;
264       return;
265    }
266
267    /*
268     * Check if the Volume is already marked for recycling
269     */
270    if (strcmp(mr->VolStatus, "Purged") == 0) {
271       if (recycle_volume(jcr, mr)) {
272          Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
273          *reason = NULL;
274          return;
275       } else {
276          /* In principle this shouldn't happen */
277          *reason = _("and recycling of current volume failed");
278          return;
279       }
280    }
281
282    /* At this point, the volume is not valid for writing */
283    *reason = _("but should be Append, Purged or Recycle");
284
285    /*
286     * What we're trying to do here is see if the current volume is
287     * "recyclable" - ie. if we prune all expired jobs off it, is
288     * it now possible to reuse it for the job that it is currently
289     * needed for?
290     */
291    if ((mr->LastWritten + mr->VolRetention) < (utime_t)time(NULL)
292          && mr->Recycle && jcr->pool->recycle_current_volume
293          && (strcmp(mr->VolStatus, "Full") == 0 ||
294             strcmp(mr->VolStatus, "Used") == 0)) {
295       /*
296        * Attempt prune of current volume to see if we can
297        * recycle it for use.
298        */
299       UAContext *ua;
300
301       ua = new_ua_context(jcr);
302       ok = prune_volume(ua, mr);
303       free_ua_context(ua);
304
305       if (ok) {
306          /* If fully purged, recycle current volume */
307          if (recycle_volume(jcr, mr)) {
308             Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
309             *reason = NULL;
310          } else {
311             *reason = _("but should be Append, Purged or Recycle (recycling of the "
312                "current volume failed)");
313          }
314       } else {
315          *reason = _("but should be Append, Purged or Recycle (cannot automatically "
316             "recycle current volume, as it still contains unpruned data "
317             "or the Volume Retention time has not expired.)");
318       }
319    }
320 }
321
322 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
323
324 static bool get_scratch_volume(JCR *jcr, MEDIA_DBR *mr, bool InChanger)
325 {
326    MEDIA_DBR smr;
327    POOL_DBR spr, pr;
328    bool ok = false;
329    char ed1[50], ed2[50];
330
331    /* Only one thread at a time can pull from the scratch pool */
332    P(mutex);
333    /* 
334     * Get Pool record for Scratch Pool
335     */
336    memset(&spr, 0, sizeof(spr));
337    bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
338    if (db_get_pool_record(jcr, jcr->db, &spr)) {
339       memset(&smr, 0, sizeof(smr));
340       smr.PoolId = spr.PoolId;
341       if (InChanger) {       
342          smr.StorageId = mr->StorageId;  /* want only Scratch Volumes in changer */
343       }
344       bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus));  /* want only appendable volumes */
345       bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
346       if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
347          POOL_MEM query(PM_MESSAGE);
348
349          /*   
350           * Get pool record where the Scratch Volume will go to ensure
351           * that we can add a Volume.
352           */
353          memset(&pr, 0, sizeof(pr));
354          bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
355          if (!db_get_pool_record(jcr, jcr->db, &pr)) {
356             Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"), 
357                  db_strerror(jcr->db));
358             goto bail_out;
359          }
360          if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
361             Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"),
362                jcr->pool->hdr.name, pr.MaxVols);
363             goto bail_out;
364          }
365
366          /* OK, now move Scratch Volume */
367          db_lock(jcr->db);
368          Mmsg(query, "UPDATE Media SET PoolId=%s WHERE MediaId=%s",
369               edit_int64(mr->PoolId, ed1),
370               edit_int64(smr.MediaId, ed2));
371          ok = db_sql_query(jcr->db, query.c_str(), NULL, NULL);  
372          db_unlock(jcr->db);
373          if (!ok) {
374             Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
375                db_strerror(jcr->db));
376            goto bail_out;
377           }
378          Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"), 
379               smr.VolumeName);
380          /* Set new Pool Id in smr record, then copy it to mr */
381          smr.PoolId = mr->PoolId;
382          memcpy(mr, &smr, sizeof(MEDIA_DBR));
383          /* Set default parameters from current pool */
384          set_pool_dbr_defaults_in_media_dbr(mr, &pr);
385          if (!db_update_media_record(jcr, jcr->db, mr)) {
386             Jmsg(jcr, M_WARNING, 0, _("Unable to update Volume record: ERR=%s"), 
387                  db_strerror(jcr->db));
388             ok = false;
389          }
390       }
391    }
392 bail_out:
393    V(mutex);
394    return ok;
395 }