]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/next_vol.c
53e941eaba2f205970bfa370643e92909d10d1d0
[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    Copyright (C) 2001-2005 Kern Sibbald
13
14    This program is free software; you can redistribute it and/or
15    modify it under the terms of the GNU General Public License as
16    published by the Free Software Foundation; either version 2 of
17    the License, or (at your option) any later version.
18
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22    General Public License for more details.
23
24    You should have received a copy of the GNU General Public
25    License along with this program; if not, write to the Free
26    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27    MA 02111-1307, USA.
28
29  */
30
31 #include "bacula.h"
32 #include "dird.h"
33
34 /*
35  *  Items needed:
36  *   mr.PoolId must be set
37  *   jcr->store
38  *   jcr->db
39  *   jcr->pool
40  *   MEDIA_DBR mr (zeroed out)
41  *   create -- whether or not to create a new volume
42  */
43 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, bool create)
44 {
45    int retry = 0;
46    bool ok;
47    bool InChanger;
48    STORE *store = jcr->store;
49
50    bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
51    Dmsg2(100, "CatReq FindMedia: Id=%d, MediaType=%s\n", (int)mr->PoolId, mr->MediaType);
52    /*
53     * If we are using an Autochanger, restrict Volume
54     *   search to the Autochanger on the first pass
55     */
56    InChanger = store->autochanger;
57    /*
58     * Find the Next Volume for Append
59     */
60    db_lock(jcr->db);
61    for ( ;; ) {
62       bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus));  /* want only appendable volumes */
63       /*
64        *  1. Look for volume with "Append" status.
65        */
66       ok = db_find_next_volume(jcr, jcr->db, 1, InChanger, mr);
67       Dmsg2(100, "catreq after find_next_vol ok=%d FW=%d\n", ok, mr->FirstWritten);
68       if (!ok) {
69          /*
70           * 2. Try finding a recycled volume
71           */
72          ok = find_recycled_volume(jcr, InChanger, mr);
73          Dmsg2(100, "find_recycled_volume %d FW=%d\n", ok, mr->FirstWritten);
74          if (!ok) {
75             /*
76              * 3. Try recycling any purged volume
77              */
78             ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
79             if (!ok) {
80                /*
81                 * 4. Try pruning Volumes
82                 */
83                prune_volumes(jcr);
84                ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
85                if (InChanger) {
86                   InChanger = false;
87                   if (!ok) {
88                      continue;           /* retry again accepting any volume */
89                   }
90                }
91                Dmsg2(200, "find_recycled_volume2 %d FW=%d\n", ok, mr->FirstWritten);
92                if (!ok && create) {
93                   /*
94                    * 5. Try "creating" a new Volume
95                    */
96                   ok = newVolume(jcr, mr);
97                }
98             }
99          }
100
101          /*
102           *  Look at more drastic ways to find an Appendable Volume
103           */
104          if (!ok && (jcr->pool->purge_oldest_volume ||
105                      jcr->pool->recycle_oldest_volume)) {
106             Dmsg2(200, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
107                 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
108             /* Find oldest volume to recycle */
109             ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
110             Dmsg1(400, "Find oldest=%d\n", ok);
111             if (ok) {
112                UAContext *ua;
113                Dmsg0(400, "Try purge.\n");
114                /*
115                 * 5.  Try to purging oldest volume only if not UA calling us.
116                 */
117                ua = new_ua_context(jcr);
118                if (jcr->pool->purge_oldest_volume && create) {
119                   Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
120                   ok = purge_jobs_from_volume(ua, mr);
121                /*
122                 * 5. or try recycling the oldest volume
123                 */
124                } else if (jcr->pool->recycle_oldest_volume) {
125                   Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
126                   ok = prune_volume(ua, mr);
127                }
128                free_ua_context(ua);
129                if (ok) {
130                   ok = recycle_volume(jcr, mr);
131                   Dmsg1(400, "Recycle after purge oldest=%d\n", ok);
132                }
133             }
134          }
135       }
136       Dmsg2(100, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
137       if (ok) {
138          /* If we can use the volume, check if it is expired */
139          if (has_volume_expired(jcr, mr)) {
140             if (retry++ < 200) {            /* sanity check */
141                continue;                    /* try again from the top */
142             } else {
143                Jmsg(jcr, M_ERROR, 0, _(
144 "We seem to be looping trying to find the next volume. I give up.\n"));
145             }
146          }
147       }
148       break;
149    } /* end for loop */
150    db_unlock(jcr->db);
151    return ok;
152 }
153
154 /*
155  * Check if any time limits or use limits have expired
156  *   if so, set the VolStatus appropriately.
157  */
158 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
159 {
160    bool expired = false;
161    /*
162     * Check limits and expirations if "Append" and it has been used
163     * i.e. mr->VolJobs > 0
164     *
165     */
166    if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
167       /* First handle Max Volume Bytes */
168       if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
169          Jmsg(jcr, M_INFO, 0, _("Max Volume bytes exceeded. "
170              "Marking Volume \"%s\" as Full.\n"), mr->VolumeName);
171          bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
172          expired = true;
173
174       /* Now see if Volume should only be used once */
175       } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
176          Jmsg(jcr, M_INFO, 0, _("Volume used once. "
177              "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
178          bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
179          expired = true;
180
181       /* Now see if Max Jobs written to volume */
182       } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
183          Jmsg(jcr, M_INFO, 0, _("Max Volume jobs exceeded. "
184              "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
185          bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
186          expired = true;
187
188       /* Now see if Max Files written to volume */
189       } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
190          Jmsg(jcr, M_INFO, 0, _("Max Volume files exceeded. "
191              "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
192          bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
193          expired = true;
194
195       /* Finally, check Use duration expiration */
196       } else if (mr->VolUseDuration > 0) {
197          utime_t now = time(NULL);
198          /* See if Vol Use has expired */
199          if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
200             Jmsg(jcr, M_INFO, 0, _("Max configured use duration exceeded. "
201                "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
202             bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
203             expired = true;
204          }
205       }
206    }
207    if (expired) {
208       /* Need to update media */
209       if (!db_update_media_record(jcr, jcr->db, mr)) {
210          Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
211               mr->VolumeName, db_strerror(jcr->db));
212       }
213    }
214    return expired;
215 }
216
217 /*
218  * Try hard to recycle the current volume
219  *
220  *  Returns: on failure - reason = NULL
221  *           on success - reason - pointer to reason
222  */
223 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
224 {
225    int ok;
226
227    *reason = NULL;
228
229    /*  Check if a duration or limit has expired */
230    if (has_volume_expired(jcr, mr)) {
231       *reason = "volume has expired";
232       /* Keep going because we may be able to recycle volume */
233    }
234
235    /*
236     * Now see if we can use the volume as is
237     */
238    if (strcmp(mr->VolStatus, "Append") == 0 ||
239        strcmp(mr->VolStatus, "Recycle") == 0) {
240       *reason = NULL;
241       return;
242    }
243
244    /*
245     * Check if the Volume is already marked for recycling
246     */
247    if (strcmp(mr->VolStatus, "Purged") == 0) {
248       if (recycle_volume(jcr, mr)) {
249          Jmsg(jcr, M_INFO, 0, "Recycled current volume \"%s\"\n", mr->VolumeName);
250          *reason = NULL;
251          return;
252       } else {
253          /* In principle this shouldn't happen */
254          *reason = "and recycling of current volume failed";
255          return;
256       }
257    }
258
259    /* At this point, the volume is not valid for writing */
260    *reason = "but should be Append, Purged or Recycle";
261
262    /*
263     * What we're trying to do here is see if the current volume is
264     * "recyclable" - ie. if we prune all expired jobs off it, is
265     * it now possible to reuse it for the job that it is currently
266     * needed for?
267     */
268    if ((mr->LastWritten + mr->VolRetention) < (utime_t)time(NULL)
269          && mr->Recycle && jcr->pool->recycle_current_volume
270          && (strcmp(mr->VolStatus, "Full") == 0 ||
271             strcmp(mr->VolStatus, "Used") == 0)) {
272       /*
273        * Attempt prune of current volume to see if we can
274        * recycle it for use.
275        */
276       UAContext *ua;
277
278       ua = new_ua_context(jcr);
279       ok = prune_volume(ua, mr);
280       free_ua_context(ua);
281
282       if (ok) {
283          /* If fully purged, recycle current volume */
284          if (recycle_volume(jcr, mr)) {
285             Jmsg(jcr, M_INFO, 0, "Recycled current volume \"%s\"\n", mr->VolumeName);
286             *reason = NULL;
287          } else {
288             *reason = "but should be Append, Purged or Recycle (recycling of the "
289                "current volume failed)";
290          }
291       } else {
292          *reason = "but should be Append, Purged or Recycle (cannot automatically "
293             "recycle current volume, as it still contains unpruned data)";
294       }
295    }
296 }