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