1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 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  * Bacula Catalog Database Update record interface routines
21  *
22  *    Written by Kern Sibbald, March 2000
23  */
24 
25 #include  "bacula.h"
26 
27 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
28 
29 #include  "cats.h"
30 
31 #define dbglevel1  100
32 #define dbglevel2  400
33 
34 /* -----------------------------------------------------------------------
35  *
36  *   Generic Routines (or almost generic)
37  *
38  * -----------------------------------------------------------------------
39  */
40 
41 /* -----------------------------------------------------------------------
42  *
43  *   Generic Routines (or almost generic)
44  *
45  * -----------------------------------------------------------------------
46  */
47 /* Update the attributes record by adding the file digest */
bdb_add_digest_to_file_record(JCR * jcr,FileId_t FileId,char * digest,int type)48 int BDB::bdb_add_digest_to_file_record(JCR *jcr, FileId_t FileId, char *digest,
49                           int type)
50 {
51    int ret;
52    char ed1[50];
53    int len = strlen(digest);
54 
55    bdb_lock();
56    esc_name = check_pool_memory_size(esc_name, len*2+1);
57    bdb_escape_string(jcr, esc_name, digest, len);
58    Mmsg(cmd, "UPDATE File SET MD5='%s' WHERE FileId=%s", esc_name,
59         edit_int64(FileId, ed1));
60    ret = UpdateDB(jcr, cmd, false);
61    bdb_unlock();
62    return ret;
63 }
64 
65 /* Mark the file record as being visited during database
66  * verify compare. Stuff JobId into the MarkId field
67  */
bdb_mark_file_record(JCR * jcr,FileId_t FileId,JobId_t JobId)68 int BDB::bdb_mark_file_record(JCR *jcr, FileId_t FileId, JobId_t JobId)
69 {
70    int stat;
71    char ed1[50], ed2[50];
72 
73    bdb_lock();
74    Mmsg(cmd, "UPDATE File SET MarkId=%s WHERE FileId=%s",
75       edit_int64(JobId, ed1), edit_int64(FileId, ed2));
76    stat = UpdateDB(jcr, cmd, false);
77    bdb_unlock();
78    return stat;
79 }
80 
81 /*
82  * Update the Job record at start of Job
83  *
84  *  Returns: false on failure
85  *           true  on success
86  */
bdb_update_job_start_record(JCR * jcr,JOB_DBR * jr)87 bool BDB::bdb_update_job_start_record(JCR *jcr, JOB_DBR *jr)
88 {
89    char dt[MAX_TIME_LENGTH];
90    time_t stime;
91    struct tm tm;
92    btime_t JobTDate;
93    int stat;
94    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
95 
96    stime = jr->StartTime;
97    (void)localtime_r(&stime, &tm);
98    strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
99    JobTDate = (btime_t)stime;
100 
101    bdb_lock();
102    Mmsg(cmd, "UPDATE Job SET JobStatus='%c',Level='%c',StartTime='%s',"
103 "ClientId=%s,JobTDate=%s,PoolId=%s,FileSetId=%s WHERE JobId=%s",
104       (char)(jcr->JobStatus),
105       (char)(jr->JobLevel), dt,
106       edit_int64(jr->ClientId, ed1),
107       edit_uint64(JobTDate, ed2),
108       edit_int64(jr->PoolId, ed3),
109       edit_int64(jr->FileSetId, ed4),
110       edit_int64(jr->JobId, ed5));
111 
112    stat = UpdateDB(jcr, cmd, false);
113    changes = 0;
114    bdb_unlock();
115    return stat;
116 }
117 
118 /*
119  * Update Long term statistics with all jobs that were run before
120  * age seconds
121  */
bdb_update_stats(JCR * jcr,utime_t age)122 int BDB::bdb_update_stats(JCR *jcr, utime_t age)
123 {
124    char ed1[30];
125    int rows;
126 
127    utime_t now = (utime_t)time(NULL);
128    edit_uint64(now - age, ed1);
129 
130    bdb_lock();
131 
132    Mmsg(cmd, fill_jobhisto, ed1);
133    QueryDB(jcr, cmd); /* TODO: get a message ? */
134    rows = sql_affected_rows();
135 
136    bdb_unlock();
137 
138    return rows;
139 }
140 
141 /*
142  * Update the Job record at end of Job
143  *
144  *  Returns: 0 on failure
145  *           1 on success
146  */
bdb_update_job_end_record(JCR * jcr,JOB_DBR * jr)147 int BDB::bdb_update_job_end_record(JCR *jcr, JOB_DBR *jr)
148 {
149    char dt[MAX_TIME_LENGTH];
150    char rdt[MAX_TIME_LENGTH];
151    time_t ttime;
152    struct tm tm;
153    int stat;
154    char ed1[30], ed2[30], ed3[50], ed4[50];
155    btime_t JobTDate;
156    char PriorJobId[50];
157 
158    if (jr->PriorJobId) {
159       bstrncpy(PriorJobId, edit_int64(jr->PriorJobId, ed1), sizeof(PriorJobId));
160    } else {
161       bstrncpy(PriorJobId, "0", sizeof(PriorJobId));
162    }
163 
164    ttime = jr->EndTime;
165    (void)localtime_r(&ttime, &tm);
166    strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
167 
168    if (jr->RealEndTime == 0 || jr->RealEndTime < jr->EndTime) {
169       jr->RealEndTime = jr->EndTime;
170    }
171    ttime = jr->RealEndTime;
172    (void)localtime_r(&ttime, &tm);
173    strftime(rdt, sizeof(rdt), "%Y-%m-%d %H:%M:%S", &tm);
174 
175    JobTDate = ttime;
176 
177    bdb_lock();
178    Mmsg(cmd,
179       "UPDATE Job SET JobStatus='%c',EndTime='%s',"
180 "ClientId=%u,JobBytes=%s,ReadBytes=%s,JobFiles=%u,JobErrors=%u,VolSessionId=%u,"
181 "VolSessionTime=%u,PoolId=%u,FileSetId=%u,JobTDate=%s,"
182 "RealEndTime='%s',PriorJobId=%s,HasBase=%u,PurgedFiles=%u WHERE JobId=%s",
183       (char)(jr->JobStatus), dt, jr->ClientId, edit_uint64(jr->JobBytes, ed1),
184       edit_uint64(jr->ReadBytes, ed4),
185       jr->JobFiles, jr->JobErrors, jr->VolSessionId, jr->VolSessionTime,
186       jr->PoolId, jr->FileSetId, edit_uint64(JobTDate, ed2),
187       rdt, PriorJobId, jr->HasBase, jr->PurgedFiles,
188       edit_int64(jr->JobId, ed3));
189 
190    stat = UpdateDB(jcr, cmd, false);
191 
192    bdb_unlock();
193    return stat;
194 }
195 
196 /*
197  * Update Client record
198  *   Returns: 0 on failure
199  *            1 on success
200  */
bdb_update_client_record(JCR * jcr,CLIENT_DBR * cr)201 int BDB::bdb_update_client_record(JCR *jcr, CLIENT_DBR *cr)
202 {
203    int stat;
204    char ed1[50], ed2[50];
205    char esc_name[MAX_ESCAPE_NAME_LENGTH];
206    char esc_uname[MAX_ESCAPE_NAME_LENGTH];
207    CLIENT_DBR tcr;
208 
209    bdb_lock();
210    memcpy(&tcr, cr, sizeof(tcr));
211    if (!bdb_create_client_record(jcr, &tcr)) {
212       bdb_unlock();
213       return 0;
214    }
215 
216    bdb_escape_string(jcr, esc_name, cr->Name, strlen(cr->Name));
217    bdb_escape_string(jcr, esc_uname, cr->Uname, strlen(cr->Uname));
218    Mmsg(cmd,
219 "UPDATE Client SET AutoPrune=%d,FileRetention=%s,JobRetention=%s,"
220 "Uname='%s' WHERE Name='%s'",
221       cr->AutoPrune,
222       edit_uint64(cr->FileRetention, ed1),
223       edit_uint64(cr->JobRetention, ed2),
224       esc_uname, esc_name);
225 
226    stat = UpdateDB(jcr, cmd, false);
227    bdb_unlock();
228    return stat;
229 }
230 
231 
232 /*
233  * Update Counters record
234  *   Returns: 0 on failure
235  *            1 on success
236  */
bdb_update_counter_record(JCR * jcr,COUNTER_DBR * cr)237 int BDB::bdb_update_counter_record(JCR *jcr, COUNTER_DBR *cr)
238 {
239    char esc[MAX_ESCAPE_NAME_LENGTH];
240 
241    bdb_lock();
242    bdb_escape_string(jcr, esc, cr->Counter, strlen(cr->Counter));
243    Mmsg(cmd, update_counter_values[bdb_get_type_index()],
244       cr->MinValue, cr->MaxValue, cr->CurrentValue,
245       cr->WrapCounter, esc);
246 
247    int stat = UpdateDB(jcr, cmd, false);
248    bdb_unlock();
249    return stat;
250 }
251 
252 
bdb_update_pool_record(JCR * jcr,POOL_DBR * pr)253 int BDB::bdb_update_pool_record(JCR *jcr, POOL_DBR *pr)
254 {
255    int stat;
256    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50], ed7[50];
257    char esc[MAX_ESCAPE_NAME_LENGTH];
258 
259    bdb_lock();
260    bdb_escape_string(jcr, esc, pr->LabelFormat, strlen(pr->LabelFormat));
261 
262    Mmsg(cmd, "SELECT count(*) from Media WHERE PoolId=%s",
263       edit_int64(pr->PoolId, ed4));
264    pr->NumVols = get_sql_record_max(jcr, this);
265    Dmsg1(dbglevel2, "NumVols=%d\n", pr->NumVols);
266 
267    Mmsg(cmd,
268 "UPDATE Pool SET NumVols=%u,MaxVols=%u,UseOnce=%d,UseCatalog=%d,"
269 "AcceptAnyVolume=%d,VolRetention='%s',VolUseDuration='%s',"
270 "MaxVolJobs=%u,MaxVolFiles=%u,MaxVolBytes=%s,Recycle=%d,"
271 "AutoPrune=%d,LabelType=%d,LabelFormat='%s',RecyclePoolId=%s,"
272 "ScratchPoolId=%s,ActionOnPurge=%d,CacheRetention='%s' WHERE PoolId=%s",
273       pr->NumVols, pr->MaxVols, pr->UseOnce, pr->UseCatalog,
274       pr->AcceptAnyVolume, edit_uint64(pr->VolRetention, ed1),
275       edit_uint64(pr->VolUseDuration, ed2),
276       pr->MaxVolJobs, pr->MaxVolFiles,
277       edit_uint64(pr->MaxVolBytes, ed3),
278       pr->Recycle, pr->AutoPrune, pr->LabelType,
279       esc, edit_int64(pr->RecyclePoolId,ed5),
280       edit_int64(pr->ScratchPoolId,ed6),
281       pr->ActionOnPurge,
282       edit_uint64(pr->CacheRetention, ed7),
283       ed4);
284    stat = UpdateDB(jcr, cmd, false);
285    bdb_unlock();
286    return stat;
287 }
288 
bdb_update_storage_record(JCR * jcr,STORAGE_DBR * sr)289 bool BDB::bdb_update_storage_record(JCR *jcr, STORAGE_DBR *sr)
290 {
291    int stat;
292    char ed1[50];
293 
294    bdb_lock();
295    Mmsg(cmd, "UPDATE Storage SET AutoChanger=%d WHERE StorageId=%s",
296       sr->AutoChanger, edit_int64(sr->StorageId, ed1));
297 
298    stat = UpdateDB(jcr, cmd, false);
299    bdb_unlock();
300    return stat;
301 }
302 
303 
304 /*
305  * Update the Media Record at end of Session
306  *
307  * Returns: 0 on failure
308  *          numrows on success
309  */
bdb_update_media_record(JCR * jcr,MEDIA_DBR * mr)310 int BDB::bdb_update_media_record(JCR *jcr, MEDIA_DBR *mr)
311 {
312    char dt[MAX_TIME_LENGTH];
313    time_t ttime;
314    struct tm tm;
315    int stat;
316    char ed1[50], ed2[50],  ed3[50],  ed4[50];
317    char ed5[50], ed6[50],  ed7[50],  ed8[50];
318    char ed9[50], ed10[50], ed11[50], ed12[50];
319    char ed13[50], ed14[50], ed15[50], ed16[50];
320    char esc_name[MAX_ESCAPE_NAME_LENGTH];
321    char esc_status[MAX_ESCAPE_NAME_LENGTH];
322 
323    Dmsg1(dbglevel1, "update_media: FirstWritten=%d\n", mr->FirstWritten);
324    bdb_lock();
325    bdb_escape_string(jcr, esc_name, mr->VolumeName, strlen(mr->VolumeName));
326    bdb_escape_string(jcr, esc_status, mr->VolStatus, strlen(mr->VolStatus));
327 
328    if (mr->set_first_written) {
329       Dmsg1(dbglevel2, "Set FirstWritten Vol=%s\n", mr->VolumeName);
330       ttime = mr->FirstWritten;
331       (void)localtime_r(&ttime, &tm);
332       strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
333       Mmsg(cmd, "UPDATE Media SET FirstWritten='%s'"
334            " WHERE VolumeName='%s'", dt, esc_name);
335       stat = UpdateDB(jcr, cmd, false);
336       Dmsg1(dbglevel2, "Firstwritten=%d\n", mr->FirstWritten);
337    }
338 
339    /* Label just done? */
340    if (mr->set_label_date) {
341       ttime = mr->LabelDate;
342       if (ttime == 0) {
343          ttime = time(NULL);
344       }
345       (void)localtime_r(&ttime, &tm);
346       strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
347       Mmsg(cmd, "UPDATE Media SET LabelDate='%s' "
348            "WHERE VolumeName='%s'", dt, esc_name);
349       UpdateDB(jcr, cmd, false);
350    }
351 
352    if (mr->LastWritten != 0) {
353       ttime = mr->LastWritten;
354       (void)localtime_r(&ttime, &tm);
355       strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
356       Mmsg(cmd, "UPDATE Media Set LastWritten='%s' "
357            "WHERE VolumeName='%s'", dt, esc_name);
358       UpdateDB(jcr, cmd, false);
359    }
360 
361    /* sanity checks for #1066 */
362    if (mr->VolReadTime < 0) {
363       mr->VolReadTime = 0;
364    }
365    if (mr->VolWriteTime < 0) {
366       mr->VolWriteTime = 0;
367    }
368 
369    Mmsg(cmd, "UPDATE Media SET VolJobs=%u,"
370         "VolFiles=%u,VolBlocks=%u,VolBytes=%s,VolABytes=%s,"
371         "VolHoleBytes=%s,VolHoles=%u,VolMounts=%u,VolErrors=%u,"
372         "VolWrites=%s,MaxVolBytes=%s,VolStatus='%s',"
373         "Slot=%d,InChanger=%d,VolReadTime=%s,VolWriteTime=%s,VolType=%d,"
374         "VolParts=%d,VolCloudParts=%d,LastPartBytes=%s,"
375         "LabelType=%d,StorageId=%s,PoolId=%s,VolRetention=%s,VolUseDuration=%s,"
376         "MaxVolJobs=%d,MaxVolFiles=%d,Enabled=%d,LocationId=%s,"
377         "ScratchPoolId=%s,RecyclePoolId=%s,RecycleCount=%d,Recycle=%d,"
378         "ActionOnPurge=%d,CacheRetention=%s,EndBlock=%u"
379         " WHERE VolumeName='%s'",
380         mr->VolJobs, mr->VolFiles, mr->VolBlocks,
381         edit_uint64(mr->VolBytes, ed1),
382         edit_uint64(mr->VolABytes, ed2),
383         edit_uint64(mr->VolHoleBytes, ed3),
384         mr->VolHoles, mr->VolMounts, mr->VolErrors,
385         edit_uint64(mr->VolWrites, ed4),
386         edit_uint64(mr->MaxVolBytes, ed5),
387         esc_status, mr->Slot, mr->InChanger,
388         edit_int64(mr->VolReadTime, ed6),
389         edit_int64(mr->VolWriteTime, ed7),
390         mr->VolType,
391         mr->VolParts,
392         mr->VolCloudParts,
393         edit_uint64(mr->LastPartBytes, ed8),
394         mr->LabelType,
395         edit_int64(mr->StorageId, ed9),
396         edit_int64(mr->PoolId, ed10),
397         edit_uint64(mr->VolRetention, ed11),
398         edit_uint64(mr->VolUseDuration, ed12),
399         mr->MaxVolJobs, mr->MaxVolFiles,
400         mr->Enabled, edit_uint64(mr->LocationId, ed13),
401         edit_uint64(mr->ScratchPoolId, ed14),
402         edit_uint64(mr->RecyclePoolId, ed15),
403         mr->RecycleCount,mr->Recycle, mr->ActionOnPurge,
404         edit_uint64(mr->CacheRetention, ed16),
405         mr->EndBlock,
406         esc_name);
407 
408    Dmsg1(dbglevel1, "%s\n", cmd);
409 
410    stat = UpdateDB(jcr, cmd, false);
411 
412    /* Make sure InChanger is 0 for any record having the same Slot */
413    db_make_inchanger_unique(jcr, this, mr);
414 
415    bdb_unlock();
416    return stat;
417 }
418 
419 /*
420  * Update the Media Record Default values from Pool
421  *
422  * Returns: 0 on failure
423  *          numrows on success
424  */
bdb_update_media_defaults(JCR * jcr,MEDIA_DBR * mr)425 int BDB::bdb_update_media_defaults(JCR *jcr, MEDIA_DBR *mr)
426 {
427    int stat;
428    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50];
429    char esc[MAX_ESCAPE_NAME_LENGTH];
430    bool can_be_empty;
431 
432    bdb_lock();
433    if (mr->VolumeName[0]) {
434       bdb_escape_string(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
435       Mmsg(cmd, "UPDATE Media SET "
436            "ActionOnPurge=%d, Recycle=%d,VolRetention=%s,VolUseDuration=%s,"
437            "MaxVolJobs=%u,MaxVolFiles=%u,MaxVolBytes=%s,RecyclePoolId=%s,CacheRetention=%s"
438            " WHERE VolumeName='%s'",
439            mr->ActionOnPurge, mr->Recycle,edit_uint64(mr->VolRetention, ed1),
440            edit_uint64(mr->VolUseDuration, ed2),
441            mr->MaxVolJobs, mr->MaxVolFiles,
442            edit_uint64(mr->MaxVolBytes, ed3),
443            edit_uint64(mr->RecyclePoolId, ed4),
444            edit_uint64(mr->CacheRetention, ed5),
445            esc);
446       can_be_empty = false;
447 
448    } else {
449       Mmsg(cmd, "UPDATE Media SET "
450            "ActionOnPurge=%d, Recycle=%d,VolRetention=%s,VolUseDuration=%s,"
451            "MaxVolJobs=%u,MaxVolFiles=%u,MaxVolBytes=%s,RecyclePoolId=%s,CacheRetention=%s"
452            " WHERE PoolId=%s",
453            mr->ActionOnPurge, mr->Recycle,edit_uint64(mr->VolRetention, ed1),
454            edit_uint64(mr->VolUseDuration, ed2),
455            mr->MaxVolJobs, mr->MaxVolFiles,
456            edit_uint64(mr->MaxVolBytes, ed3),
457            edit_int64(mr->RecyclePoolId, ed4),
458            edit_uint64(mr->CacheRetention, ed5),
459            edit_int64(mr->PoolId, ed6));
460       can_be_empty = true;
461    }
462 
463    Dmsg1(dbglevel1, "%s\n", cmd);
464 
465    stat = UpdateDB(jcr, cmd, can_be_empty);
466 
467    bdb_unlock();
468    return stat;
469 }
470 
471 
472 /*
473  * If we have a non-zero InChanger, ensure that no other Media
474  *  record has InChanger set on the same Slot.
475  *
476  * This routine assumes the database is already locked.
477  */
bdb_make_inchanger_unique(JCR * jcr,MEDIA_DBR * mr)478 void BDB::bdb_make_inchanger_unique(JCR *jcr, MEDIA_DBR *mr)
479 {
480    char ed1[50];
481    char esc[MAX_ESCAPE_NAME_LENGTH];
482 
483    if (mr->InChanger != 0 && mr->Slot != 0 && mr->StorageId != 0) {
484        if (!mr->sid_group) {
485           mr->sid_group = edit_int64(mr->StorageId, mr->sid);
486        }
487        if (mr->MediaId != 0) {
488           Mmsg(cmd, "UPDATE Media SET InChanger=0, Slot=0 WHERE "
489                "Slot=%d AND StorageId IN (%s) AND MediaId!=%s",
490                mr->Slot,
491                mr->sid_group, edit_int64(mr->MediaId, ed1));
492 
493        } else if (*mr->VolumeName) {
494           bdb_escape_string(jcr, esc,mr->VolumeName,strlen(mr->VolumeName));
495           Mmsg(cmd, "UPDATE Media SET InChanger=0, Slot=0 WHERE "
496                "Slot=%d AND StorageId IN (%s) AND VolumeName!='%s'",
497                mr->Slot, mr->sid_group, esc);
498 
499        } else {  /* used by ua_label to reset all volume with this slot */
500           Mmsg(cmd, "UPDATE Media SET InChanger=0, Slot=0 WHERE "
501                "Slot=%d AND StorageId IN (%s)",
502                mr->Slot, mr->sid_group, mr->VolumeName);
503        }
504        Dmsg1(dbglevel1, "%s\n", cmd);
505        UpdateDB(jcr, cmd, true);
506    }
507 }
508 
509 /* Update only Retention */
bdb_update_snapshot_record(JCR * jcr,SNAPSHOT_DBR * sr)510 bool BDB::bdb_update_snapshot_record(JCR *jcr, SNAPSHOT_DBR *sr)
511 {
512    int stat, len;
513    char ed1[50], ed2[50];
514 
515    len = strlen(sr->Comment);
516    bdb_lock();
517 
518    esc_name = check_pool_memory_size(esc_name, len*2+1);
519    bdb_escape_string(jcr, esc_name, sr->Comment, len);
520 
521    Mmsg(cmd, "UPDATE Snapshot SET Retention=%s, Comment='%s' WHERE SnapshotId=%s",
522         edit_int64(sr->Retention, ed2), sr->Comment, edit_int64(sr->SnapshotId, ed1));
523 
524    stat = UpdateDB(jcr, cmd, false);
525    bdb_unlock();
526    return stat;
527 }
528 
529 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */
530