]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/next_vol.c
- Remove argument from create_bacula_database for SQLite as it
[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
16    version 2 as amended with additional clauses defined in the
17    file LICENSE in the main source directory.
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 
22    the file LICENSE for additional details.
23
24  */
25
26 #include "bacula.h"
27 #include "dird.h"
28
29 /*
30  *  Items needed:
31  *   mr.PoolId must be set
32  *   jcr->store
33  *   jcr->db
34  *   jcr->pool
35  *   MEDIA_DBR mr (zeroed out)
36  *   create -- whether or not to create a new volume
37  */
38 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create)
39 {
40    int retry = 0;
41    bool ok;
42    bool InChanger;
43    STORE *store = jcr->store;
44
45    bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
46    Dmsg2(100, "CatReq FindMedia: Id=%d, MediaType=%s\n", (int)mr->PoolId, mr->MediaType);
47    /*
48     * If we are using an Autochanger, restrict Volume
49     *   search to the Autochanger on the first pass
50     */
51    InChanger = store->autochanger;
52    /*
53     * Find the Next Volume for Append
54     */
55    db_lock(jcr->db);
56    for ( ;; ) {
57       bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus));  /* want only appendable volumes */
58       /*
59        *  1. Look for volume with "Append" status.
60        */
61       ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
62       Dmsg2(100, "catreq after find_next_vol ok=%d FW=%d\n", ok, mr->FirstWritten);
63       if (!ok) {
64          /*
65           * 2. Try finding a recycled volume
66           */
67          ok = find_recycled_volume(jcr, InChanger, mr);
68          Dmsg2(100, "find_recycled_volume %d FW=%d\n", ok, mr->FirstWritten);
69          if (!ok) {
70             /*
71              * 3. Try recycling any purged volume
72              */
73             ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
74             if (!ok) {
75                /*
76                 * 4. Try pruning Volumes
77                 */
78                prune_volumes(jcr);
79                ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
80                if (InChanger) {
81                   InChanger = false;
82                   if (!ok) {
83                      continue;           /* retry again accepting any volume */
84                   }
85                }
86                Dmsg2(200, "find_recycled_volume2 %d FW=%d\n", ok, mr->FirstWritten);
87                if (!ok && create) {
88                   /*
89                    * 5. Try "creating" a new Volume
90                    */
91                   ok = newVolume(jcr, mr);
92                }
93             }
94          }
95
96          if (!ok) {
97             MEDIA_DBR smr;
98             POOL_DBR pr;
99             POOLMEM *query;
100             char ed1[50], ed2[50];
101             /*
102              * 6. Try pulling a volume from the Scratch pool
103              */ 
104              memset(&pr, 0, sizeof(pr));
105              bstrncpy(pr.Name, "Scratch", sizeof(pr.Name));
106              if (db_get_pool_record(jcr, jcr->db, &pr)) {
107                 memset(&smr, 0, sizeof(smr));
108                 smr.PoolId = pr.PoolId;
109                 bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus));  /* want only appendable volumes */
110                 bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
111                 if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
112                    query = get_pool_memory(PM_MESSAGE);
113                    db_lock(jcr->db);
114                    Mmsg(query, "UPDATE Media SET PoolId=%s WHERE MediaId=%s",
115                         edit_int64(mr->PoolId, ed1),
116                         edit_int64(smr.MediaId, ed2));
117                    ok = db_sql_query(jcr->db, query, NULL, NULL);  
118                    db_unlock(jcr->db);
119                    Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"), 
120                         smr.VolumeName);
121                    /* Set new Pool Id in smr record, then copy it to mr */
122                    smr.PoolId = mr->PoolId;
123                    memcpy(mr, &smr, sizeof(MEDIA_DBR));
124                 }
125              }
126          }
127          /*
128           *  Look at more drastic ways to find an Appendable Volume
129           */
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);
137             if (ok) {
138                UAContext *ua;
139                Dmsg0(400, "Try purge.\n");
140                /*
141                 * 5.  Try to purging oldest volume only if not UA calling us.
142                 */
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);
147                /*
148                 * 5. or try recycling the oldest volume
149                 */
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);
153                }
154                free_ua_context(ua);
155                if (ok) {
156                   ok = recycle_volume(jcr, mr);
157                   Dmsg1(400, "Recycle after purge oldest=%d\n", ok);
158                }
159             }
160          }
161       }
162       Dmsg2(100, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
163       if (ok) {
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 */
168             } else {
169                Jmsg(jcr, M_ERROR, 0, _(
170 "We seem to be looping trying to find the next volume. I give up.\n"));
171             }
172          }
173       }
174       break;
175    } /* end for loop */
176    db_unlock(jcr->db);
177    return ok;
178 }
179
180 /*
181  * Check if any time limits or use limits have expired
182  *   if so, set the VolStatus appropriately.
183  */
184 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
185 {
186    bool expired = false;
187    /*
188     * Check limits and expirations if "Append" and it has been used
189     * i.e. mr->VolJobs > 0
190     *
191     */
192    if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
193       /* First handle Max Volume Bytes */
194       if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
195          Jmsg(jcr, M_INFO, 0, _("Max Volume bytes exceeded. "
196              "Marking Volume \"%s\" as Full.\n"), mr->VolumeName);
197          bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
198          expired = true;
199
200       /* Now see if Volume should only be used once */
201       } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
202          Jmsg(jcr, M_INFO, 0, _("Volume used once. "
203              "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
204          bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
205          expired = true;
206
207       /* Now see if Max Jobs written to volume */
208       } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
209          Jmsg(jcr, M_INFO, 0, _("Max Volume jobs exceeded. "
210              "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
211          bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
212          expired = true;
213
214       /* Now see if Max Files written to volume */
215       } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
216          Jmsg(jcr, M_INFO, 0, _("Max Volume files exceeded. "
217              "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
218          bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
219          expired = true;
220
221       /* Finally, check Use duration expiration */
222       } else if (mr->VolUseDuration > 0) {
223          utime_t now = time(NULL);
224          /* See if Vol Use has expired */
225          if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
226             Jmsg(jcr, M_INFO, 0, _("Max configured use duration exceeded. "
227                "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
228             bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
229             expired = true;
230          }
231       }
232    }
233    if (expired) {
234       /* Need to update media */
235       if (!db_update_media_record(jcr, jcr->db, mr)) {
236          Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
237               mr->VolumeName, db_strerror(jcr->db));
238       }
239    }
240    return expired;
241 }
242
243 /*
244  * Try hard to recycle the current volume
245  *
246  *  Returns: on failure - reason = NULL
247  *           on success - reason - pointer to reason
248  */
249 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
250 {
251    int ok;
252
253    *reason = NULL;
254
255    /*  Check if a duration or limit has expired */
256    if (has_volume_expired(jcr, mr)) {
257       *reason = _("volume has expired");
258       /* Keep going because we may be able to recycle volume */
259    }
260
261    /*
262     * Now see if we can use the volume as is
263     */
264    if (strcmp(mr->VolStatus, "Append") == 0 ||
265        strcmp(mr->VolStatus, "Recycle") == 0) {
266       *reason = NULL;
267       return;
268    }
269
270    /*
271     * Check if the Volume is already marked for recycling
272     */
273    if (strcmp(mr->VolStatus, "Purged") == 0) {
274       if (recycle_volume(jcr, mr)) {
275          Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
276          *reason = NULL;
277          return;
278       } else {
279          /* In principle this shouldn't happen */
280          *reason = _("and recycling of current volume failed");
281          return;
282       }
283    }
284
285    /* At this point, the volume is not valid for writing */
286    *reason = _("but should be Append, Purged or Recycle");
287
288    /*
289     * What we're trying to do here is see if the current volume is
290     * "recyclable" - ie. if we prune all expired jobs off it, is
291     * it now possible to reuse it for the job that it is currently
292     * needed for?
293     */
294    if ((mr->LastWritten + mr->VolRetention) < (utime_t)time(NULL)
295          && mr->Recycle && jcr->pool->recycle_current_volume
296          && (strcmp(mr->VolStatus, "Full") == 0 ||
297             strcmp(mr->VolStatus, "Used") == 0)) {
298       /*
299        * Attempt prune of current volume to see if we can
300        * recycle it for use.
301        */
302       UAContext *ua;
303
304       ua = new_ua_context(jcr);
305       ok = prune_volume(ua, mr);
306       free_ua_context(ua);
307
308       if (ok) {
309          /* If fully purged, recycle current volume */
310          if (recycle_volume(jcr, mr)) {
311             Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
312             *reason = NULL;
313          } else {
314             *reason = _("but should be Append, Purged or Recycle (recycling of the "
315                "current volume failed)");
316          }
317       } else {
318          *reason = _("but should be Append, Purged or Recycle (cannot automatically "
319             "recycle current volume, as it still contains unpruned data)");
320       }
321    }
322 }