]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/autoprune.c
Tweak leave SQL library links in rpm
[bacula/bacula] / bacula / src / dird / autoprune.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *
21  *   Bacula Director -- Automatic Pruning
22  *      Applies retention periods
23  *
24  *     Kern Sibbald, May MMII
25  *
26  *   Version $Id$
27  */
28
29 #include "bacula.h"
30 #include "dird.h"
31 #include "ua.h"
32
33 /* Forward referenced functions */
34
35
36 /*
37  * Auto Prune Jobs and Files. This is called at the end of every
38  *   Job.  We do not prune volumes here.
39  */
40 void do_autoprune(JCR *jcr)
41 {
42    UAContext *ua;
43    CLIENT *client;
44    POOL *pool;
45    bool pruned;
46
47    if (!jcr->client) {                /* temp -- remove me */
48       return;
49    }
50
51    ua = new_ua_context(jcr);
52    client = jcr->client;
53    pool = jcr->pool;
54
55    if (jcr->job->PruneJobs || jcr->client->AutoPrune) {
56       prune_jobs(ua, client, pool, jcr->getJobType());
57       pruned = true;
58    } else {
59       pruned = false;
60    }
61
62    if (jcr->job->PruneFiles || jcr->client->AutoPrune) {
63       prune_files(ua, client, pool);
64       pruned = true;
65    }
66    if (pruned) {
67       Jmsg(jcr, M_INFO, 0, _("End auto prune.\n\n"));
68    }
69    free_ua_context(ua);
70    return;
71 }
72
73 /*
74  * Prune at least one Volume in current Pool. This is called from
75  *   catreq.c => next_vol.c when the Storage daemon is asking for another
76  *   volume and no appendable volumes are available.
77  *
78  */
79 void prune_volumes(JCR *jcr, bool InChanger, MEDIA_DBR *mr,
80         STORE *store)
81 {
82    int count;
83    int i;
84    dbid_list ids;
85    struct del_ctx prune_list;
86    POOL_MEM query(PM_MESSAGE), changer(PM_MESSAGE);
87    UAContext *ua;
88    char ed1[50], ed2[100], ed3[50];
89
90    POOL_DBR spr;
91
92    Dmsg1(100, "Prune volumes PoolId=%d\n", jcr->jr.PoolId);
93    if (!jcr->job->PruneVolumes && !jcr->pool->AutoPrune) {
94       Dmsg0(100, "AutoPrune not set in Pool.\n");
95       return;
96    }
97
98    memset(&prune_list, 0, sizeof(prune_list));
99    prune_list.max_ids = 10000;
100    prune_list.JobId = (JobId_t *)malloc(sizeof(JobId_t) * prune_list.max_ids);
101
102    ua = new_ua_context(jcr);
103    db_lock(jcr->db);
104
105    /* Edit PoolId */
106    edit_int64(mr->PoolId, ed1);
107    /*
108     * Get Pool record for Scratch Pool
109     */
110    memset(&spr, 0, sizeof(spr));
111    bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
112    if (db_get_pool_record(jcr, jcr->db, &spr)) {
113       edit_int64(spr.PoolId, ed2);
114       bstrncat(ed2, ",", sizeof(ed2));
115    } else {
116       ed2[0] = 0;
117    }
118
119    if (mr->ScratchPoolId) {
120       edit_int64(mr->ScratchPoolId, ed3);
121       bstrncat(ed2, ed3, sizeof(ed2));
122       bstrncat(ed2, ",", sizeof(ed2));
123    }
124
125    Dmsg1(100, "Scratch pool(s)=%s\n", ed2);
126    /*
127     * ed2 ends up with scratch poolid and current poolid or
128     *   just current poolid if there is no scratch pool
129     */
130    bstrncat(ed2, ed1, sizeof(ed2));
131
132    /*
133     * Get the List of all media ids in the current Pool or whose
134     *  RecyclePoolId is the current pool or the scratch pool
135     */
136    const char *select = "SELECT DISTINCT MediaId,LastWritten FROM Media WHERE "
137         "(PoolId=%s OR RecyclePoolId IN (%s)) AND MediaType='%s' %s"
138         "ORDER BY LastWritten ASC,MediaId";
139
140    set_storageid_in_mr(store, mr);
141    if (InChanger) {
142       Mmsg(changer, "AND InChanger=1 AND StorageId=%s ", edit_int64(mr->StorageId, ed3));
143    }
144
145    Mmsg(query, select, ed1, ed2, mr->MediaType, changer.c_str());
146
147    Dmsg1(100, "query=%s\n", query.c_str());
148    if (!db_get_query_dbids(ua->jcr, ua->db, query, ids)) {
149       Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
150       goto bail_out;
151    }
152
153    Dmsg1(100, "Volume prune num_ids=%d\n", ids.num_ids);
154
155    /* Visit each Volume and Prune it until we find one that is purged */
156    for (i=0; i<ids.num_ids; i++) {
157       MEDIA_DBR lmr;
158       lmr.MediaId = ids.DBId[i];
159       Dmsg1(100, "Get record MediaId=%d\n", (int)lmr.MediaId);
160       if (!db_get_media_record(jcr, jcr->db, &lmr)) {
161          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
162          continue;
163       }
164       Dmsg1(100, "Examine vol=%s\n", lmr.VolumeName);
165       /* Don't prune archived volumes */
166       if (lmr.Enabled == 2) {
167          Dmsg1(100, "Vol=%s disabled\n", lmr.VolumeName);
168          continue;
169       }
170       /* Prune only Volumes with status "Full", or "Used" */
171       if (strcmp(lmr.VolStatus, "Full")   == 0 ||
172           strcmp(lmr.VolStatus, "Used")   == 0) {
173          Dmsg2(100, "Add prune list MediaId=%d Volume %s\n", (int)lmr.MediaId, lmr.VolumeName);
174          count = get_prune_list_for_volume(ua, &lmr, &prune_list);
175          Dmsg1(100, "Num pruned = %d\n", count);
176          if (count != 0) {
177             purge_job_list_from_catalog(ua, prune_list);
178             prune_list.num_ids = 0;             /* reset count */
179          }
180          if (!is_volume_purged(ua, &lmr, false)) {
181             Dmsg1(100, "Vol=%s not pruned\n", lmr.VolumeName);
182             continue;
183          }
184          Dmsg1(100, "Vol=%s is purged\n", lmr.VolumeName);
185
186          /*
187           * Since we are also pruning the Scratch pool, continue
188           *   until and check if this volume is available (InChanger + StorageId)
189           * If not, just skip this volume and try the next one
190           */
191          if (InChanger) {
192             if (!lmr.InChanger || (lmr.StorageId != mr->StorageId)) {
193                Dmsg1(100, "Vol=%s not inchanger\n", lmr.VolumeName);
194                continue;                  /* skip this volume, ie not loadable */
195             }
196          }
197
198          if (!lmr.Recycle) {
199             Dmsg1(100, "Vol=%s not recyclable\n", lmr.VolumeName);
200             continue;
201          }
202
203          /*
204           * If purged and not moved to another Pool,
205           *   then we stop pruning and take this volume.
206           */
207          if (lmr.PoolId == mr->PoolId) {
208             Dmsg2(100, "Got Vol=%s MediaId=%d purged.\n", lmr.VolumeName, (int)lmr.MediaId);
209             mr->copy(&lmr);
210             set_storageid_in_mr(store, mr);
211             break;                        /* got a volume */
212          }
213       }
214    }
215
216 bail_out:
217    Dmsg0(100, "Leave prune volumes\n");
218    db_unlock(jcr->db);
219    free_ua_context(ua);
220    if (prune_list.JobId) {
221       free(prune_list.JobId);
222    }
223    return;
224 }