1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2001-2011 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2016 Planets Communications B.V.
6 Copyright (C) 2013-2020 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
11 in the file LICENSE.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301, USA.
22 */
23 /*
24 * Kern E. Sibbald, December 2001
25 */
26 /**
27 * @file
28 * Program to scan a Bareos Volume and compare it with
29 * the catalog and optionally synchronize the catalog
30 * with the tape.
31 */
32
33 #include "include/bareos.h"
34 #include "stored/stored.h"
35 #include "stored/stored_globals.h"
36 #include "stored/jcr_private.h"
37 #include "lib/crypto_cache.h"
38 #include "findlib/find.h"
39 #include "cats/cats.h"
40 #include "cats/cats_backends.h"
41 #include "cats/sql.h"
42 #include "stored/acquire.h"
43 #include "stored/butil.h"
44 #include "stored/label.h"
45 #include "stored/mount.h"
46 #include "stored/read_record.h"
47 #include "lib/attribs.h"
48 #include "lib/edit.h"
49 #include "lib/parse_bsr.h"
50 #include "lib/bsignal.h"
51 #include "include/jcr.h"
52 #include "lib/bsock.h"
53 #include "lib/parse_conf.h"
54 #include "lib/util.h"
55
56 /* Dummy functions */
57 namespace storagedaemon {
58 extern bool ParseSdConfig(const char* configfile, int exit_code);
59 }
60
61 using namespace storagedaemon;
62
63 /* Forward referenced functions */
64 static void do_scan(void);
65 static bool RecordCb(DeviceControlRecord* dcr, DeviceRecord* rec);
66 static bool CreateFileAttributesRecord(BareosDb* db,
67 JobControlRecord* mjcr,
68 char* fname,
69 char* lname,
70 int type,
71 char* ap,
72 DeviceRecord* rec);
73 static bool CreateMediaRecord(BareosDb* db,
74 MediaDbRecord* mr,
75 Volume_Label* vl);
76 static bool UpdateMediaRecord(BareosDb* db, MediaDbRecord* mr);
77 static bool CreatePoolRecord(BareosDb* db, PoolDbRecord* pr);
78 static JobControlRecord* CreateJobRecord(BareosDb* db,
79 JobDbRecord* mr,
80 Session_Label* label,
81 DeviceRecord* rec);
82 static bool UpdateJobRecord(BareosDb* db,
83 JobDbRecord* mr,
84 Session_Label* elabel,
85 DeviceRecord* rec);
86 static bool CreateClientRecord(BareosDb* db, ClientDbRecord* cr);
87 static bool CreateFilesetRecord(BareosDb* db, FileSetDbRecord* fsr);
88 static bool CreateJobmediaRecord(BareosDb* db, JobControlRecord* jcr);
89 static JobControlRecord* create_jcr(JobDbRecord* jr,
90 DeviceRecord* rec,
91 uint32_t JobId);
92 static bool UpdateDigestRecord(BareosDb* db,
93 char* digest,
94 DeviceRecord* rec,
95 int type);
96
97 /* Local variables */
98 static Device* dev = NULL;
99 static BareosDb* db;
100 static JobControlRecord* bjcr; /* jcr for bscan */
101 static BootStrapRecord* bsr = NULL;
102 static MediaDbRecord mr;
103 static PoolDbRecord pr;
104 static JobDbRecord jr;
105 static ClientDbRecord cr;
106 static FileSetDbRecord fsr;
107 static RestoreObjectDbRecord rop;
108 static AttributesDbRecord ar;
109 static FileDbRecord fr;
110 static Session_Label label;
111 static Session_Label elabel;
112 static Attributes* attr;
113
114 static time_t lasttime = 0;
115
116 static const char* backend_directory = _PATH_BAREOS_BACKENDDIR;
117 static const char* db_driver = "NULL";
118 static const char* db_name = "bareos";
119 static const char* db_user = "bareos";
120 static const char* db_password = "";
121 static const char* db_host = NULL;
122 static int db_port = 0;
123 static const char* wd = NULL;
124 static bool update_db = false;
125 static bool update_vol_info = false;
126 static bool list_records = false;
127 static int ignored_msgs = 0;
128
129 static uint64_t currentVolumeSize;
130 static int last_pct = -1;
131 static bool showProgress = false;
132 static int num_jobs = 0;
133 static int num_pools = 0;
134 static int num_media = 0;
135 static int num_files = 0;
136 static int num_restoreobjects = 0;
137
usage()138 static void usage()
139 {
140 kBareosVersionStrings.PrintCopyrightWithFsfAndPlanets(stderr, 2001);
141 fprintf(
142 stderr,
143 _("Usage: bscan [ options ] <device-name>\n"
144 " -B <drivername> specify the database driver name (default "
145 "NULL) <postgresql|mysql|sqlite3>\n"
146 " -b <bootstrap> specify a bootstrap file\n"
147 " -c <path> specify a Storage configuration file or "
148 "directory\n"
149 " -d <nnn> set debug level to <nnn>\n"
150 " -dt print timestamp in debug output\n"
151 " -m update media info in database\n"
152 " -D <director> specify a director name specified in the "
153 "storage daemon\n"
154 " configuration file for the Key Encryption "
155 "Key selection\n"
156 " -a <directory> specify the database backend directory "
157 "(default %s)\n"
158 " -n <name> specify the database name (default bareos)\n"
159 " -u <user> specify database user name (default bareos)\n"
160 " -P <password> specify database password (default none)\n"
161 " -h <host> specify database host (default NULL)\n"
162 " -t <port> specify database port (default 0)\n"
163 " -p proceed inspite of I/O errors\n"
164 " -r list records\n"
165 " -s synchronize or store in database\n"
166 " -S show scan progress periodically\n"
167 " -v verbose\n"
168 " -V <Volumes> specify Volume names (separated by |)\n"
169 " -w <directory> specify working directory (default from "
170 "configuration file)\n"
171 " -? print this message\n\n"
172 "example:\n"
173 "bscan -B postgresql -V Full-0001 FileStorage\n"),
174 backend_directory);
175 exit(1);
176 }
177
main(int argc,char * argv[])178 int main(int argc, char* argv[])
179 {
180 int ch;
181 struct stat stat_buf;
182 char* VolumeName = NULL;
183 char* DirectorName = NULL;
184 DirectorResource* director = NULL;
185 DeviceControlRecord* dcr;
186 #if defined(HAVE_DYNAMIC_CATS_BACKENDS)
187 std::vector<std::string> backend_directories;
188 #endif
189
190 setlocale(LC_ALL, "");
191 tzset();
192 bindtextdomain("bareos", LOCALEDIR);
193 textdomain("bareos");
194 InitStackDump();
195
196 MyNameIs(argc, argv, "bscan");
197 InitMsg(NULL, NULL);
198
199 OSDependentInit();
200
201 while ((ch = getopt(argc, argv, "a:B:b:c:d:D:h:p:mn:pP:q:rsSt:u:vV:w:?")) !=
202 -1) {
203 switch (ch) {
204 case 'a':
205 backend_directory = optarg;
206 break;
207
208 case 'B':
209 db_driver = optarg;
210 break;
211
212 case 'b':
213 bsr = libbareos::parse_bsr(NULL, optarg);
214 break;
215
216 case 'c': /* specify config file */
217 if (configfile != NULL) { free(configfile); }
218 configfile = strdup(optarg);
219 break;
220
221 case 'D': /* specify director name */
222 if (DirectorName != NULL) { free(DirectorName); }
223 DirectorName = strdup(optarg);
224 break;
225
226 case 'd': /* debug level */
227 if (*optarg == 't') {
228 dbg_timestamp = true;
229 } else {
230 debug_level = atoi(optarg);
231 if (debug_level <= 0) { debug_level = 1; }
232 }
233 break;
234
235 case 'h':
236 db_host = optarg;
237 break;
238
239 case 't':
240 db_port = atoi(optarg);
241 break;
242
243 case 'm':
244 update_vol_info = true;
245 break;
246
247 case 'n':
248 db_name = optarg;
249 break;
250
251 case 'P':
252 db_password = optarg;
253 break;
254
255 case 'p':
256 forge_on = true;
257 break;
258
259 case 'r':
260 list_records = true;
261 break;
262
263 case 'S':
264 showProgress = true;
265 break;
266
267 case 's':
268 update_db = true;
269 break;
270
271 case 'u':
272 db_user = optarg;
273 break;
274
275 case 'V': /* Volume name */
276 VolumeName = optarg;
277 break;
278
279 case 'v':
280 verbose++;
281 break;
282
283 case 'w':
284 wd = optarg;
285 break;
286
287 case '?':
288 default:
289 usage();
290 }
291 }
292 argc -= optind;
293 argv += optind;
294
295 if (argc != 1) {
296 Pmsg0(0, _("Wrong number of arguments: \n"));
297 usage();
298 }
299
300 my_config = InitSdConfig(configfile, M_ERROR_TERM);
301 ParseSdConfig(configfile, M_ERROR_TERM);
302
303 if (DirectorName) {
304 foreach_res (director, R_DIRECTOR) {
305 if (bstrcmp(director->resource_name_, DirectorName)) { break; }
306 }
307 if (!director) {
308 Emsg2(
309 M_ERROR_TERM, 0,
310 _("No Director resource named %s defined in %s. Cannot continue.\n"),
311 DirectorName, configfile);
312 }
313 }
314
315 LoadSdPlugins(me->plugin_directory, me->plugin_names);
316
317 ReadCryptoCache(me->working_directory, "bareos-sd",
318 GetFirstPortHostOrder(me->SDaddrs));
319
320 /* Check if -w option given, otherwise use resource for working directory */
321 if (wd) {
322 working_directory = wd;
323 } else if (!me->working_directory) {
324 Emsg1(M_ERROR_TERM, 0,
325 _("No Working Directory defined in %s. Cannot continue.\n"),
326 configfile);
327 } else {
328 working_directory = me->working_directory;
329 }
330
331 /* Check that working directory is good */
332 if (stat(working_directory, &stat_buf) != 0) {
333 Emsg1(M_ERROR_TERM, 0,
334 _("Working Directory: %s not found. Cannot continue.\n"),
335 working_directory);
336 }
337 if (!S_ISDIR(stat_buf.st_mode)) {
338 Emsg1(M_ERROR_TERM, 0,
339 _("Working Directory: %s is not a directory. Cannot continue.\n"),
340 working_directory);
341 }
342
343 dcr = new DeviceControlRecord;
344 bjcr = SetupJcr("bscan", argv[0], bsr, director, dcr, VolumeName, true);
345 if (!bjcr) { exit(1); }
346 dev = bjcr->impl->read_dcr->dev;
347
348 if (showProgress) {
349 char ed1[50];
350 struct stat sb;
351 fstat(dev->fd(), &sb);
352 currentVolumeSize = sb.st_size;
353 Pmsg1(000, _("First Volume Size = %s\n"),
354 edit_uint64(currentVolumeSize, ed1));
355 }
356
357 #if defined(HAVE_DYNAMIC_CATS_BACKENDS)
358 backend_directories.emplace_back(backend_directory);
359 DbSetBackendDirs(backend_directories);
360 #endif
361
362 db = db_init_database(NULL, db_driver, db_name, db_user, db_password, db_host,
363 db_port, NULL, false, false, false, false);
364 if (db == NULL) {
365 Emsg0(M_ERROR_TERM, 0, _("Could not init Bareos database\n"));
366 }
367 if (!db->OpenDatabase(NULL)) { Emsg0(M_ERROR_TERM, 0, db->strerror()); }
368 Dmsg0(200, "Database opened\n");
369 if (verbose) {
370 Pmsg2(000, _("Using Database: %s, User: %s\n"), db_name, db_user);
371 }
372
373 do_scan();
374 if (update_db) {
375 printf(
376 "Records added or updated in the catalog:\n%7d Media\n"
377 "%7d Pool\n%7d Job\n%7d File\n%7d RestoreObject\n",
378 num_media, num_pools, num_jobs, num_files, num_restoreobjects);
379 } else {
380 printf(
381 "Records would have been added or updated in the catalog:\n"
382 "%7d Media\n%7d Pool\n%7d Job\n%7d File\n%7d RestoreObject\n",
383 num_media, num_pools, num_jobs, num_files, num_restoreobjects);
384 }
385 DbFlushBackends();
386
387 CleanDevice(bjcr->impl->dcr);
388 dev->term();
389 FreeDeviceControlRecord(bjcr->impl->dcr);
390 FreeJcr(bjcr);
391
392 return 0;
393 }
394
395 /**
396 * We are at the end of reading a tape. Now, we simulate handling
397 * the end of writing a tape by wiffling through the attached
398 * jcrs creating jobmedia records.
399 */
BscanMountNextReadVolume(DeviceControlRecord * dcr)400 static bool BscanMountNextReadVolume(DeviceControlRecord* dcr)
401 {
402 bool status;
403 Device* dev = dcr->dev;
404 DeviceControlRecord* mdcr;
405
406 Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->getVolCatName());
407 foreach_dlist (mdcr, dev->attached_dcrs) {
408 JobControlRecord* mjcr = mdcr->jcr;
409 Dmsg1(000, "========== JobId=%u ========\n", mjcr->JobId);
410 if (mjcr->JobId == 0) { continue; }
411 if (verbose) { Pmsg1(000, _("Create JobMedia for Job %s\n"), mjcr->Job); }
412 mdcr->StartBlock = dcr->StartBlock;
413 mdcr->StartFile = dcr->StartFile;
414 mdcr->EndBlock = dcr->EndBlock;
415 mdcr->EndFile = dcr->EndFile;
416 mdcr->VolMediaId = dcr->VolMediaId;
417 mjcr->impl->read_dcr->VolLastIndex = dcr->VolLastIndex;
418 if (mjcr->impl->insert_jobmedia_records) {
419 if (!CreateJobmediaRecord(db, mjcr)) {
420 Pmsg2(000, _("Could not create JobMedia record for Volume=%s Job=%s\n"),
421 dev->getVolCatName(), mjcr->Job);
422 }
423 }
424 }
425
426 UpdateMediaRecord(db, &mr);
427
428 /* Now let common read routine get up next tape. Note,
429 * we call mount_next... with bscan's jcr because that is where we
430 * have the Volume list, but we get attached.
431 */
432 status = MountNextReadVolume(dcr);
433 if (showProgress) {
434 char ed1[50];
435 struct stat sb;
436 fstat(dev->fd(), &sb);
437 currentVolumeSize = sb.st_size;
438 Pmsg1(000, _("First Volume Size = %s\n"),
439 edit_uint64(currentVolumeSize, ed1));
440 }
441 return status;
442 }
443
do_scan()444 static void do_scan()
445 {
446 attr = new_attr(bjcr);
447
448 AttributesDbRecord ar_emtpy;
449 PoolDbRecord pr_empty;
450 JobDbRecord jr_empty;
451 ClientDbRecord cr_empty;
452 FileSetDbRecord fsr_empty;
453 FileDbRecord fr_empty;
454
455 ar = ar_emtpy;
456 pr = pr_empty;
457 jr = jr_empty;
458 cr = cr_empty;
459 fsr = fsr_empty;
460 fr = fr_empty;
461
462 /*
463 * Detach bscan's jcr as we are not a real Job on the tape
464 */
465 ReadRecords(bjcr->impl->read_dcr, RecordCb, BscanMountNextReadVolume);
466
467 if (update_db) {
468 db->WriteBatchFileRecords(bjcr); /* used by bulk batch file insert */
469 }
470
471 FreeAttr(attr);
472 }
473
474 /**
475 * Returns: true if OK
476 * false if error
477 */
UnpackRestoreObject(JobControlRecord * jcr,int32_t stream,char * rec,int32_t reclen,RestoreObjectDbRecord * rop)478 static inline bool UnpackRestoreObject(JobControlRecord* jcr,
479 int32_t stream,
480 char* rec,
481 int32_t reclen,
482 RestoreObjectDbRecord* rop)
483 {
484 char* bp;
485 uint32_t JobFiles;
486
487 if (sscanf(rec, "%d %d %d %d %d %d ", &JobFiles, &rop->Stream,
488 &rop->object_index, &rop->object_len, &rop->object_full_len,
489 &rop->object_compression) != 6) {
490 return false;
491 }
492
493 /*
494 * Skip over the first 6 fields we scanned in the previous scan.
495 */
496 bp = rec;
497 for (int i = 0; i < 6; i++) {
498 bp = strchr(bp, ' ');
499 if (bp) {
500 bp++;
501 } else {
502 return false;
503 }
504 }
505
506 rop->plugin_name = bp;
507 rop->object_name = rop->plugin_name + strlen(rop->plugin_name) + 1;
508 rop->object = rop->object_name + strlen(rop->object_name) + 1;
509
510 return true;
511 }
512
513 /**
514 * Returns: true if OK
515 * false if error
516 */
RecordCb(DeviceControlRecord * dcr,DeviceRecord * rec)517 static bool RecordCb(DeviceControlRecord* dcr, DeviceRecord* rec)
518 {
519 JobControlRecord* mjcr;
520 char ec1[30];
521 Device* dev = dcr->dev;
522 JobControlRecord* bjcr = dcr->jcr;
523 DeviceBlock* block = dcr->block;
524 PoolMem sql_buffer;
525 db_int64_ctx jmr_count;
526 char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
527
528 if (rec->data_len > 0) {
529 mr.VolBytes +=
530 rec->data_len + WRITE_RECHDR_LENGTH; /* Accumulate Volume bytes */
531 if (showProgress && currentVolumeSize > 0) {
532 int pct = (mr.VolBytes * 100) / currentVolumeSize;
533 if (pct != last_pct) {
534 fprintf(stdout, _("done: %d%%\n"), pct);
535 fflush(stdout);
536 last_pct = pct;
537 }
538 }
539 }
540
541 if (list_records) {
542 Pmsg5(000,
543 _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
544 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex, rec->Stream,
545 rec->data_len);
546 }
547
548 /*
549 * Check for Start or End of Session Record
550 */
551 if (rec->FileIndex < 0) {
552 bool save_update_db = update_db;
553
554 if (verbose > 1) { DumpLabelRecord(dev, rec, true); }
555 switch (rec->FileIndex) {
556 case PRE_LABEL:
557 Pmsg0(000, _("Volume is prelabeled. This tape cannot be scanned.\n"));
558 return false;
559 break;
560
561 case VOL_LABEL:
562 UnserVolumeLabel(dev, rec);
563 /*
564 * Check Pool info
565 */
566 bstrncpy(pr.Name, dev->VolHdr.PoolName, sizeof(pr.Name));
567 bstrncpy(pr.PoolType, dev->VolHdr.PoolType, sizeof(pr.PoolType));
568 num_pools++;
569 if (db->GetPoolRecord(bjcr, &pr)) {
570 if (verbose) {
571 Pmsg1(000, _("Pool record for %s found in DB.\n"), pr.Name);
572 }
573 } else {
574 if (!update_db) {
575 Pmsg1(000, _("VOL_LABEL: Pool record not found for Pool: %s\n"),
576 pr.Name);
577 }
578 CreatePoolRecord(db, &pr);
579 }
580 if (!bstrcmp(pr.PoolType, dev->VolHdr.PoolType)) {
581 Pmsg2(000, _("VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n"),
582 pr.PoolType, dev->VolHdr.PoolType);
583 return true;
584 } else if (verbose) {
585 Pmsg1(000, _("Pool type \"%s\" is OK.\n"), pr.PoolType);
586 }
587
588 /*
589 * Check Media Info
590 */
591 mr = MediaDbRecord{};
592 bstrncpy(mr.VolumeName, dev->VolHdr.VolumeName, sizeof(mr.VolumeName));
593 mr.PoolId = pr.PoolId;
594 num_media++;
595 if (db->GetMediaRecord(bjcr, &mr)) {
596 if (verbose) {
597 Pmsg1(000, _("Media record for %s found in DB.\n"), mr.VolumeName);
598 }
599 /*
600 * Clear out some volume statistics that will be updated
601 */
602 mr.VolJobs = mr.VolFiles = mr.VolBlocks = 0;
603 mr.VolBytes = rec->data_len + 20;
604 } else {
605 if (!update_db) {
606 Pmsg1(000, _("VOL_LABEL: Media record not found for Volume: %s\n"),
607 mr.VolumeName);
608 }
609 bstrncpy(mr.MediaType, dev->VolHdr.MediaType, sizeof(mr.MediaType));
610 CreateMediaRecord(db, &mr, &dev->VolHdr);
611 }
612 if (!bstrcmp(mr.MediaType, dev->VolHdr.MediaType)) {
613 Pmsg2(000, _("VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n"),
614 mr.MediaType, dev->VolHdr.MediaType);
615 return true; /* ignore error */
616 } else if (verbose) {
617 Pmsg1(000, _("Media type \"%s\" is OK.\n"), mr.MediaType);
618 }
619
620 /*
621 * Reset some DeviceControlRecord variables
622 */
623 foreach_dlist (dcr, dev->attached_dcrs) {
624 dcr->VolFirstIndex = dcr->FileIndex = 0;
625 dcr->StartBlock = dcr->EndBlock = 0;
626 dcr->StartFile = dcr->EndFile = 0;
627 dcr->VolMediaId = 0;
628 }
629
630 Pmsg1(000, _("VOL_LABEL: OK for Volume: %s\n"), mr.VolumeName);
631 break;
632
633 case SOS_LABEL:
634 if (bsr && rec->match_stat < 1) {
635 /*
636 * Skipping record, because does not match BootStrapRecord filter
637 */
638 Dmsg0(200, _("SOS_LABEL skipped. Record does not match "
639 "BootStrapRecord filter.\n"));
640 } else {
641 mr.VolJobs++;
642 num_jobs++;
643 if (ignored_msgs > 0) {
644 Pmsg1(000,
645 _("%d \"errors\" ignored before first Start of Session "
646 "record.\n"),
647 ignored_msgs);
648 ignored_msgs = 0;
649 }
650 UnserSessionLabel(&label, rec);
651 jr = JobDbRecord{};
652 bstrncpy(jr.Job, label.Job, sizeof(jr.Job));
653 if (db->GetJobRecord(bjcr, &jr)) {
654 /*
655 * Job record already exists in DB
656 */
657 update_db = false; /* don't change db in CreateJobRecord */
658 if (verbose) {
659 Pmsg1(000, _("SOS_LABEL: Found Job record for JobId: %d\n"),
660 jr.JobId);
661 }
662 } else {
663 /*
664 * Must create a Job record in DB
665 */
666 if (!update_db) {
667 Pmsg1(000, _("SOS_LABEL: Job record not found for JobId: %d\n"),
668 jr.JobId);
669 }
670 }
671
672 /*
673 * Create Client record if not already there
674 */
675 bstrncpy(cr.Name, label.ClientName, sizeof(cr.Name));
676 CreateClientRecord(db, &cr);
677 jr.ClientId = cr.ClientId;
678
679 /*
680 * Process label, if Job record exists don't update db
681 */
682 mjcr = CreateJobRecord(db, &jr, &label, rec);
683 dcr = mjcr->impl->read_dcr;
684 update_db = save_update_db;
685
686 jr.PoolId = pr.PoolId;
687 mjcr->start_time = jr.StartTime;
688 mjcr->setJobLevel(jr.JobLevel);
689
690 mjcr->client_name = GetPoolMemory(PM_FNAME);
691 PmStrcpy(mjcr->client_name, label.ClientName);
692 mjcr->impl->fileset_name = GetPoolMemory(PM_FNAME);
693 PmStrcpy(mjcr->impl->fileset_name, label.FileSetName);
694 bstrncpy(dcr->pool_type, label.PoolType, sizeof(dcr->pool_type));
695 bstrncpy(dcr->pool_name, label.PoolName, sizeof(dcr->pool_name));
696
697 /*
698 * Look for existing Job Media records for this job. If there are
699 * any, no new ones need be created. This may occur if File
700 * Retention has expired before Job Retention, or if the volume
701 * has already been bscan'd
702 */
703 Mmsg(sql_buffer, "SELECT count(*) from JobMedia where JobId=%d",
704 jr.JobId);
705 db->SqlQuery(sql_buffer.c_str(), db_int64_handler, &jmr_count);
706 if (jmr_count.value > 0) {
707 mjcr->impl->insert_jobmedia_records = false;
708 } else {
709 mjcr->impl->insert_jobmedia_records = true;
710 }
711
712 if (rec->VolSessionId != jr.VolSessionId) {
713 Pmsg3(
714 000,
715 _("SOS_LABEL: VolSessId mismatch for JobId=%u. DB=%d Vol=%d\n"),
716 jr.JobId, jr.VolSessionId, rec->VolSessionId);
717 return true; /* ignore error */
718 }
719 if (rec->VolSessionTime != jr.VolSessionTime) {
720 Pmsg3(000,
721 _("SOS_LABEL: VolSessTime mismatch for JobId=%u. DB=%d "
722 "Vol=%d\n"),
723 jr.JobId, jr.VolSessionTime, rec->VolSessionTime);
724 return true; /* ignore error */
725 }
726 if (jr.PoolId != pr.PoolId) {
727 Pmsg3(000,
728 _("SOS_LABEL: PoolId mismatch for JobId=%u. DB=%d Vol=%d\n"),
729 jr.JobId, jr.PoolId, pr.PoolId);
730 return true; /* ignore error */
731 }
732 }
733 break;
734
735 case EOS_LABEL:
736 if (bsr && rec->match_stat < 1) {
737 /*
738 * Skipping record, because does not match BootStrapRecord filter
739 */
740 Dmsg0(200, _("EOS_LABEL skipped. Record does not match "
741 "BootStrapRecord filter.\n"));
742 } else {
743 UnserSessionLabel(&elabel, rec);
744
745 /*
746 * Create FileSet record
747 */
748 bstrncpy(fsr.FileSet, label.FileSetName, sizeof(fsr.FileSet));
749 bstrncpy(fsr.MD5, label.FileSetMD5, sizeof(fsr.MD5));
750 CreateFilesetRecord(db, &fsr);
751 jr.FileSetId = fsr.FileSetId;
752
753 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
754 if (!mjcr) {
755 Pmsg2(000,
756 _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
757 rec->VolSessionId, rec->VolSessionTime);
758 break;
759 }
760
761 /*
762 * Do the final update to the Job record
763 */
764 UpdateJobRecord(db, &jr, &elabel, rec);
765
766 mjcr->end_time = jr.EndTime;
767 mjcr->setJobStatus(JS_Terminated);
768
769 /*
770 * Create JobMedia record
771 */
772 mjcr->impl->read_dcr->VolLastIndex = dcr->VolLastIndex;
773 if (mjcr->impl->insert_jobmedia_records) {
774 CreateJobmediaRecord(db, mjcr);
775 }
776 FreeDeviceControlRecord(mjcr->impl->read_dcr);
777 FreeJcr(mjcr);
778 }
779 break;
780
781 case EOM_LABEL:
782 break;
783
784 case EOT_LABEL: /* end of all tapes */
785 /*
786 * Wiffle through all jobs still open and close them.
787 */
788 if (update_db) {
789 DeviceControlRecord* mdcr;
790 foreach_dlist (mdcr, dev->attached_dcrs) {
791 JobControlRecord* mjcr = mdcr->jcr;
792 if (!mjcr || mjcr->JobId == 0) { continue; }
793 jr.JobId = mjcr->JobId;
794 jr.JobStatus = JS_ErrorTerminated; /* Mark Job as Error Terimined */
795 jr.JobFiles = mjcr->JobFiles;
796 jr.JobBytes = mjcr->JobBytes;
797 jr.VolSessionId = mjcr->VolSessionId;
798 jr.VolSessionTime = mjcr->VolSessionTime;
799 jr.JobTDate = (utime_t)mjcr->start_time;
800 jr.ClientId = mjcr->ClientId;
801 if (!db->UpdateJobEndRecord(bjcr, &jr)) {
802 Pmsg1(0, _("Could not update job record. ERR=%s\n"),
803 db->strerror());
804 }
805 }
806 }
807 mr.VolFiles = rec->File;
808 mr.VolBlocks = rec->Block;
809 mr.VolBytes += mr.VolBlocks * WRITE_BLKHDR_LENGTH; /* approx. */
810 mr.VolMounts++;
811 UpdateMediaRecord(db, &mr);
812 Pmsg3(0,
813 _("End of all Volumes. VolFiles=%u VolBlocks=%u VolBytes=%s\n"),
814 mr.VolFiles, mr.VolBlocks,
815 edit_uint64_with_commas(mr.VolBytes, ec1));
816 break;
817 default:
818 break;
819 } /* end switch */
820 return true;
821 }
822
823 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
824 if (!mjcr) {
825 if (mr.VolJobs > 0) {
826 Pmsg2(000, _("Could not find Job for SessId=%d SessTime=%d record.\n"),
827 rec->VolSessionId, rec->VolSessionTime);
828 } else {
829 ignored_msgs++;
830 }
831 return true;
832 }
833 dcr = mjcr->impl->read_dcr;
834 if (dcr->VolFirstIndex == 0) { dcr->VolFirstIndex = block->FirstIndex; }
835
836 /*
837 * File Attributes stream
838 */
839 switch (rec->maskedStream) {
840 case STREAM_UNIX_ATTRIBUTES:
841 case STREAM_UNIX_ATTRIBUTES_EX:
842 if (!UnpackAttributesRecord(bjcr, rec->Stream, rec->data, rec->data_len,
843 attr)) {
844 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
845 }
846
847 if (verbose > 1) {
848 DecodeStat(attr->attr, &attr->statp, sizeof(attr->statp),
849 &attr->LinkFI);
850 BuildAttrOutputFnames(bjcr, attr);
851 PrintLsOutput(bjcr, attr);
852 }
853 fr.JobId = mjcr->JobId;
854 fr.FileId = 0;
855 num_files++;
856 if (verbose && (num_files & 0x7FFF) == 0) {
857 char ed1[30], ed2[30], ed3[30], ed4[30];
858 Pmsg4(000, _("%s file records. At file:blk=%s:%s bytes=%s\n"),
859 edit_uint64_with_commas(num_files, ed1),
860 edit_uint64_with_commas(rec->File, ed2),
861 edit_uint64_with_commas(rec->Block, ed3),
862 edit_uint64_with_commas(mr.VolBytes, ed4));
863 }
864 CreateFileAttributesRecord(db, mjcr, attr->fname, attr->lname, attr->type,
865 attr->attr, rec);
866 FreeJcr(mjcr);
867 break;
868
869 case STREAM_RESTORE_OBJECT:
870 if (!UnpackRestoreObject(bjcr, rec->Stream, rec->data, rec->data_len,
871 &rop)) {
872 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
873 }
874 rop.FileIndex = mjcr->FileId;
875 rop.JobId = mjcr->JobId;
876
877
878 if (update_db) { db->CreateRestoreObjectRecord(mjcr, &rop); }
879
880 num_restoreobjects++;
881
882 FreeJcr(mjcr); /* done using JobControlRecord */
883 break;
884
885 /*
886 * Data streams
887 */
888 case STREAM_WIN32_DATA:
889 case STREAM_FILE_DATA:
890 case STREAM_SPARSE_DATA:
891 case STREAM_MACOS_FORK_DATA:
892 case STREAM_ENCRYPTED_FILE_DATA:
893 case STREAM_ENCRYPTED_WIN32_DATA:
894 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
895 /*
896 * For encrypted stream, this is an approximation.
897 * The data must be decrypted to know the correct length.
898 */
899 mjcr->JobBytes += rec->data_len;
900 if (rec->maskedStream == STREAM_SPARSE_DATA) {
901 mjcr->JobBytes -= sizeof(uint64_t);
902 }
903
904 FreeJcr(mjcr); /* done using JobControlRecord */
905 break;
906
907 case STREAM_GZIP_DATA:
908 case STREAM_COMPRESSED_DATA:
909 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
910 case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
911 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
912 case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
913 /*
914 * Not correct, we should (decrypt and) expand it.
915 */
916 mjcr->JobBytes += rec->data_len;
917 FreeJcr(mjcr);
918 break;
919
920 case STREAM_SPARSE_GZIP_DATA:
921 case STREAM_SPARSE_COMPRESSED_DATA:
922 mjcr->JobBytes += rec->data_len -
923 sizeof(uint64_t); /* Not correct, we should expand it */
924 FreeJcr(mjcr); /* done using JobControlRecord */
925 break;
926
927 /*
928 * Win32 GZIP stream
929 */
930 case STREAM_WIN32_GZIP_DATA:
931 case STREAM_WIN32_COMPRESSED_DATA:
932 mjcr->JobBytes += rec->data_len;
933 FreeJcr(mjcr); /* done using JobControlRecord */
934 break;
935
936 case STREAM_MD5_DIGEST:
937 BinToBase64(digest, sizeof(digest), (char*)rec->data,
938 CRYPTO_DIGEST_MD5_SIZE, true);
939 if (verbose > 1) { Pmsg1(000, _("Got MD5 record: %s\n"), digest); }
940 UpdateDigestRecord(db, digest, rec, CRYPTO_DIGEST_MD5);
941 break;
942
943 case STREAM_SHA1_DIGEST:
944 BinToBase64(digest, sizeof(digest), (char*)rec->data,
945 CRYPTO_DIGEST_SHA1_SIZE, true);
946 if (verbose > 1) { Pmsg1(000, _("Got SHA1 record: %s\n"), digest); }
947 UpdateDigestRecord(db, digest, rec, CRYPTO_DIGEST_SHA1);
948 break;
949
950 case STREAM_SHA256_DIGEST:
951 BinToBase64(digest, sizeof(digest), (char*)rec->data,
952 CRYPTO_DIGEST_SHA256_SIZE, true);
953 if (verbose > 1) { Pmsg1(000, _("Got SHA256 record: %s\n"), digest); }
954 UpdateDigestRecord(db, digest, rec, CRYPTO_DIGEST_SHA256);
955 break;
956
957 case STREAM_SHA512_DIGEST:
958 BinToBase64(digest, sizeof(digest), (char*)rec->data,
959 CRYPTO_DIGEST_SHA512_SIZE, true);
960 if (verbose > 1) { Pmsg1(000, _("Got SHA512 record: %s\n"), digest); }
961 UpdateDigestRecord(db, digest, rec, CRYPTO_DIGEST_SHA512);
962 break;
963
964 case STREAM_ENCRYPTED_SESSION_DATA:
965 // TODO landonf: Investigate crypto support in bscan
966 if (verbose > 1) { Pmsg0(000, _("Got signed digest record\n")); }
967 break;
968
969 case STREAM_SIGNED_DIGEST:
970 // TODO landonf: Investigate crypto support in bscan
971 if (verbose > 1) { Pmsg0(000, _("Got signed digest record\n")); }
972 break;
973
974 case STREAM_PROGRAM_NAMES:
975 if (verbose) { Pmsg1(000, _("Got Prog Names Stream: %s\n"), rec->data); }
976 break;
977
978 case STREAM_PROGRAM_DATA:
979 if (verbose > 1) { Pmsg0(000, _("Got Prog Data Stream record.\n")); }
980 break;
981
982 case STREAM_HFSPLUS_ATTRIBUTES:
983 /* Ignore OSX attributes */
984 break;
985
986 case STREAM_PLUGIN_NAME:
987 case STREAM_PLUGIN_DATA:
988 /* Ignore plugin data */
989 break;
990
991 case STREAM_UNIX_ACCESS_ACL: /* Deprecated Standard ACL attributes on UNIX
992 */
993 case STREAM_UNIX_DEFAULT_ACL: /* Deprecated Default ACL attributes on UNIX
994 */
995 case STREAM_ACL_AIX_TEXT:
996 case STREAM_ACL_DARWIN_ACCESS_ACL:
997 case STREAM_ACL_FREEBSD_DEFAULT_ACL:
998 case STREAM_ACL_FREEBSD_ACCESS_ACL:
999 case STREAM_ACL_HPUX_ACL_ENTRY:
1000 case STREAM_ACL_IRIX_DEFAULT_ACL:
1001 case STREAM_ACL_IRIX_ACCESS_ACL:
1002 case STREAM_ACL_LINUX_DEFAULT_ACL:
1003 case STREAM_ACL_LINUX_ACCESS_ACL:
1004 case STREAM_ACL_TRU64_DEFAULT_ACL:
1005 case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
1006 case STREAM_ACL_TRU64_ACCESS_ACL:
1007 case STREAM_ACL_SOLARIS_ACLENT:
1008 case STREAM_ACL_SOLARIS_ACE:
1009 case STREAM_ACL_AFS_TEXT:
1010 case STREAM_ACL_AIX_AIXC:
1011 case STREAM_ACL_AIX_NFS4:
1012 case STREAM_ACL_FREEBSD_NFS4_ACL:
1013 case STREAM_ACL_HURD_DEFAULT_ACL:
1014 case STREAM_ACL_HURD_ACCESS_ACL:
1015 case STREAM_ACL_PLUGIN:
1016 /* Ignore Unix ACL attributes */
1017 break;
1018
1019 case STREAM_XATTR_PLUGIN:
1020 case STREAM_XATTR_HURD:
1021 case STREAM_XATTR_IRIX:
1022 case STREAM_XATTR_TRU64:
1023 case STREAM_XATTR_AIX:
1024 case STREAM_XATTR_OPENBSD:
1025 case STREAM_XATTR_SOLARIS_SYS:
1026 case STREAM_XATTR_SOLARIS:
1027 case STREAM_XATTR_DARWIN:
1028 case STREAM_XATTR_FREEBSD:
1029 case STREAM_XATTR_LINUX:
1030 case STREAM_XATTR_NETBSD:
1031 /* Ignore Unix Extended attributes */
1032 break;
1033
1034 case STREAM_NDMP_SEPARATOR:
1035 /* Ignore NDMP separators */
1036 break;
1037
1038 default:
1039 Pmsg2(0, _("Unknown stream type!!! stream=%d len=%i\n"), rec->Stream,
1040 rec->data_len);
1041 break;
1042 }
1043
1044 return true;
1045 }
1046
1047 /**
1048 * Free the Job Control Record if no one is still using it.
1049 * Called from main FreeJcr() routine in src/lib/jcr.c so
1050 * that we can do our Director specific cleanup of the jcr.
1051 */
BscanFreeJcr(JobControlRecord * jcr)1052 static void BscanFreeJcr(JobControlRecord* jcr)
1053 {
1054 Dmsg0(200, "Start bscan FreeJcr\n");
1055
1056 if (jcr->file_bsock) {
1057 Dmsg0(200, "Close File bsock\n");
1058 jcr->file_bsock->close();
1059 delete jcr->file_bsock;
1060 jcr->file_bsock = NULL;
1061 }
1062
1063 if (jcr->store_bsock) {
1064 Dmsg0(200, "Close Store bsock\n");
1065 jcr->store_bsock->close();
1066 delete jcr->store_bsock;
1067 jcr->store_bsock = NULL;
1068 }
1069
1070 if (jcr->RestoreBootstrap) { free(jcr->RestoreBootstrap); }
1071
1072 if (jcr->impl->dcr) {
1073 FreeDeviceControlRecord(jcr->impl->dcr);
1074 jcr->impl->dcr = NULL;
1075 }
1076
1077 if (jcr->impl->read_dcr) {
1078 FreeDeviceControlRecord(jcr->impl->read_dcr);
1079 jcr->impl->read_dcr = NULL;
1080 }
1081
1082 if (jcr->impl) {
1083 delete jcr->impl;
1084 jcr->impl = nullptr;
1085 }
1086
1087 Dmsg0(200, "End bscan FreeJcr\n");
1088 }
1089
1090 /**
1091 * We got a File Attributes record on the tape. Now, lookup the Job
1092 * record, and then create the attributes record.
1093 */
CreateFileAttributesRecord(BareosDb * db,JobControlRecord * mjcr,char * fname,char * lname,int type,char * ap,DeviceRecord * rec)1094 static bool CreateFileAttributesRecord(BareosDb* db,
1095 JobControlRecord* mjcr,
1096 char* fname,
1097 char* lname,
1098 int type,
1099 char* ap,
1100 DeviceRecord* rec)
1101 {
1102 DeviceControlRecord* dcr = mjcr->impl->read_dcr;
1103 ar.fname = fname;
1104 ar.link = lname;
1105 ar.ClientId = mjcr->ClientId;
1106 ar.JobId = mjcr->JobId;
1107 ar.Stream = rec->Stream;
1108 if (type == FT_DELETED) {
1109 ar.FileIndex = 0;
1110 } else {
1111 ar.FileIndex = rec->FileIndex;
1112 }
1113 ar.attr = ap;
1114 if (dcr->VolFirstIndex == 0) { dcr->VolFirstIndex = rec->FileIndex; }
1115 dcr->FileIndex = rec->FileIndex;
1116 mjcr->JobFiles++;
1117
1118 if (!update_db) { return true; }
1119
1120 if (!db->CreateFileAttributesRecord(bjcr, &ar)) {
1121 Pmsg1(0, _("Could not create File Attributes record. ERR=%s\n"),
1122 db->strerror());
1123 return false;
1124 }
1125 mjcr->FileId = ar.FileId;
1126
1127 if (verbose > 1) { Pmsg1(000, _("Created File record: %s\n"), fname); }
1128
1129 return true;
1130 }
1131
1132 /**
1133 * For each Volume we see, we create a Medium record
1134 */
CreateMediaRecord(BareosDb * db,MediaDbRecord * mr,Volume_Label * vl)1135 static bool CreateMediaRecord(BareosDb* db, MediaDbRecord* mr, Volume_Label* vl)
1136 {
1137 struct date_time dt;
1138 struct tm tm;
1139
1140 /*
1141 * We mark Vols as Archive to keep them from being re-written
1142 */
1143 bstrncpy(mr->VolStatus, "Archive", sizeof(mr->VolStatus));
1144 mr->VolRetention = 365 * 3600 * 24; /* 1 year */
1145 mr->Enabled = 1;
1146 if (vl->VerNum >= 11) {
1147 mr->set_first_written = true; /* Save FirstWritten during update_media */
1148 mr->FirstWritten = BtimeToUtime(vl->write_btime);
1149 mr->LabelDate = BtimeToUtime(vl->label_btime);
1150 } else {
1151 /*
1152 * DEPRECATED DO NOT USE
1153 */
1154 dt.julian_day_number = vl->write_date;
1155 dt.julian_day_fraction = vl->write_time;
1156 TmDecode(&dt, &tm);
1157 mr->FirstWritten = mktime(&tm);
1158 dt.julian_day_number = vl->label_date;
1159 dt.julian_day_fraction = vl->label_time;
1160 TmDecode(&dt, &tm);
1161 mr->LabelDate = mktime(&tm);
1162 }
1163 lasttime = mr->LabelDate;
1164
1165 if (mr->VolJobs == 0) { mr->VolJobs = 1; }
1166
1167 if (mr->VolMounts == 0) { mr->VolMounts = 1; }
1168
1169 if (!update_db) { return true; }
1170
1171 if (!db->CreateMediaRecord(bjcr, mr)) {
1172 Pmsg1(000, _("Could not create media record. ERR=%s\n"), db->strerror());
1173 return false;
1174 }
1175 if (!db->UpdateMediaRecord(bjcr, mr)) {
1176 Pmsg1(000, _("Could not update media record. ERR=%s\n"), db->strerror());
1177 return false;
1178 }
1179 if (verbose) {
1180 Pmsg1(000, _("Created Media record for Volume: %s\n"), mr->VolumeName);
1181 }
1182
1183 return true;
1184 }
1185
1186 /**
1187 * Called at end of media to update it
1188 */
UpdateMediaRecord(BareosDb * db,MediaDbRecord * mr)1189 static bool UpdateMediaRecord(BareosDb* db, MediaDbRecord* mr)
1190 {
1191 if (!update_db && !update_vol_info) { return true; }
1192
1193 mr->LastWritten = lasttime;
1194 if (!db->UpdateMediaRecord(bjcr, mr)) {
1195 Pmsg1(000, _("Could not update media record. ERR=%s\n"), db->strerror());
1196 return false;
1197 }
1198
1199 if (verbose) {
1200 Pmsg1(000, _("Updated Media record at end of Volume: %s\n"),
1201 mr->VolumeName);
1202 }
1203
1204 return true;
1205 }
1206
CreatePoolRecord(BareosDb * db,PoolDbRecord * pr)1207 static bool CreatePoolRecord(BareosDb* db, PoolDbRecord* pr)
1208 {
1209 pr->NumVols++;
1210 pr->UseCatalog = 1;
1211 pr->VolRetention = 355 * 3600 * 24; /* 1 year */
1212
1213 if (!update_db) { return true; }
1214
1215 if (!db->CreatePoolRecord(bjcr, pr)) {
1216 Pmsg1(000, _("Could not create pool record. ERR=%s\n"), db->strerror());
1217 return false;
1218 }
1219
1220 if (verbose) {
1221 Pmsg1(000, _("Created Pool record for Pool: %s\n"), pr->Name);
1222 }
1223
1224 return true;
1225 }
1226
1227 /**
1228 * Called from SOS to create a client for the current Job
1229 */
CreateClientRecord(BareosDb * db,ClientDbRecord * cr)1230 static bool CreateClientRecord(BareosDb* db, ClientDbRecord* cr)
1231 {
1232 /*
1233 * Note, update_db can temporarily be set false while
1234 * updating the database, so we must ensure that ClientId is non-zero.
1235 */
1236 if (!update_db) {
1237 cr->ClientId = 0;
1238 if (!db->GetClientRecord(bjcr, cr)) {
1239 Pmsg1(0, _("Could not get Client record. ERR=%s\n"), db->strerror());
1240 return false;
1241 }
1242
1243 return true;
1244 }
1245
1246 if (!db->CreateClientRecord(bjcr, cr)) {
1247 Pmsg1(000, _("Could not create Client record. ERR=%s\n"), db->strerror());
1248 return false;
1249 }
1250
1251 if (verbose) {
1252 Pmsg1(000, _("Created Client record for Client: %s\n"), cr->Name);
1253 }
1254
1255 return true;
1256 }
1257
CreateFilesetRecord(BareosDb * db,FileSetDbRecord * fsr)1258 static bool CreateFilesetRecord(BareosDb* db, FileSetDbRecord* fsr)
1259 {
1260 if (!update_db) { return true; }
1261
1262 fsr->FileSetId = 0;
1263 if (fsr->MD5[0] == 0) {
1264 fsr->MD5[0] = ' '; /* Equivalent to nothing */
1265 fsr->MD5[1] = 0;
1266 }
1267
1268 if (db->GetFilesetRecord(bjcr, fsr)) {
1269 if (verbose) {
1270 Pmsg1(000, _("Fileset \"%s\" already exists.\n"), fsr->FileSet);
1271 }
1272 } else {
1273 if (!db->CreateFilesetRecord(bjcr, fsr)) {
1274 Pmsg2(000, _("Could not create FileSet record \"%s\". ERR=%s\n"),
1275 fsr->FileSet, db->strerror());
1276 return false;
1277 }
1278
1279 if (verbose) {
1280 Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet);
1281 }
1282 }
1283
1284 return true;
1285 }
1286
1287 /**
1288 * Simulate the two calls on the database to create the Job record and
1289 * to update it when the Job actually begins running.
1290 */
CreateJobRecord(BareosDb * db,JobDbRecord * jr,Session_Label * label,DeviceRecord * rec)1291 static JobControlRecord* CreateJobRecord(BareosDb* db,
1292 JobDbRecord* jr,
1293 Session_Label* label,
1294 DeviceRecord* rec)
1295 {
1296 JobControlRecord* mjcr;
1297 struct date_time dt;
1298 struct tm tm;
1299
1300 jr->JobId = label->JobId;
1301 jr->JobType = label->JobType;
1302 jr->JobLevel = label->JobLevel;
1303 jr->JobStatus = JS_Created;
1304 bstrncpy(jr->Name, label->JobName, sizeof(jr->Name));
1305 bstrncpy(jr->Job, label->Job, sizeof(jr->Job));
1306 if (label->VerNum >= 11) {
1307 jr->SchedTime = BtimeToUnix(label->write_btime);
1308 } else {
1309 dt.julian_day_number = label->write_date;
1310 dt.julian_day_fraction = label->write_time;
1311 TmDecode(&dt, &tm);
1312 jr->SchedTime = mktime(&tm);
1313 }
1314
1315 jr->StartTime = jr->SchedTime;
1316 jr->JobTDate = (utime_t)jr->SchedTime;
1317 jr->VolSessionId = rec->VolSessionId;
1318 jr->VolSessionTime = rec->VolSessionTime;
1319
1320 /* Now create a JobControlRecord as if starting the Job */
1321 mjcr = create_jcr(jr, rec, label->JobId);
1322
1323 if (!update_db) { return mjcr; }
1324
1325 /*
1326 * This creates the bare essentials
1327 */
1328 if (!db->CreateJobRecord(bjcr, jr)) {
1329 Pmsg1(0, _("Could not create JobId record. ERR=%s\n"), db->strerror());
1330 return mjcr;
1331 }
1332
1333 /*
1334 * This adds the client, StartTime, JobTDate, ...
1335 */
1336 if (!db->UpdateJobStartRecord(bjcr, jr)) {
1337 Pmsg1(0, _("Could not update job start record. ERR=%s\n"), db->strerror());
1338 return mjcr;
1339 }
1340
1341 Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"),
1342 jr->JobId, label->JobId);
1343 mjcr->JobId = jr->JobId; /* set new JobId */
1344
1345 return mjcr;
1346 }
1347
1348 /**
1349 * Simulate the database call that updates the Job at Job termination time.
1350 */
UpdateJobRecord(BareosDb * db,JobDbRecord * jr,Session_Label * elabel,DeviceRecord * rec)1351 static bool UpdateJobRecord(BareosDb* db,
1352 JobDbRecord* jr,
1353 Session_Label* elabel,
1354 DeviceRecord* rec)
1355 {
1356 struct date_time dt;
1357 struct tm tm;
1358 JobControlRecord* mjcr;
1359
1360 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1361 if (!mjcr) {
1362 Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
1363 rec->VolSessionId, rec->VolSessionTime);
1364 return false;
1365 }
1366
1367 if (elabel->VerNum >= 11) {
1368 jr->EndTime = BtimeToUnix(elabel->write_btime);
1369 } else {
1370 dt.julian_day_number = elabel->write_date;
1371 dt.julian_day_fraction = elabel->write_time;
1372 TmDecode(&dt, &tm);
1373 jr->EndTime = mktime(&tm);
1374 }
1375
1376 lasttime = jr->EndTime;
1377 mjcr->end_time = jr->EndTime;
1378
1379 jr->JobId = mjcr->JobId;
1380 jr->JobStatus = elabel->JobStatus;
1381 mjcr->JobStatus = elabel->JobStatus;
1382 jr->JobFiles = elabel->JobFiles;
1383 if (jr->JobFiles > 0) { /* If we found files, force PurgedFiles */
1384 jr->PurgedFiles = 0;
1385 }
1386 jr->JobBytes = elabel->JobBytes;
1387 jr->VolSessionId = rec->VolSessionId;
1388 jr->VolSessionTime = rec->VolSessionTime;
1389 jr->JobTDate = (utime_t)mjcr->start_time;
1390 jr->ClientId = mjcr->ClientId;
1391
1392 if (!update_db) {
1393 FreeJcr(mjcr);
1394 return true;
1395 }
1396
1397 if (!db->UpdateJobEndRecord(bjcr, jr)) {
1398 Pmsg2(0, _("Could not update JobId=%u record. ERR=%s\n"), jr->JobId,
1399 db->strerror());
1400 FreeJcr(mjcr);
1401 return false;
1402 }
1403
1404 if (verbose) {
1405 Pmsg3(
1406 000,
1407 _("Updated Job termination record for JobId=%u Level=%s TermStat=%c\n"),
1408 jr->JobId, job_level_to_str(mjcr->getJobLevel()), jr->JobStatus);
1409 }
1410
1411 if (verbose > 1) {
1412 const char* TermMsg;
1413 static char term_code[70];
1414 char sdt[50], edt[50];
1415 char ec1[30], ec2[30], ec3[30];
1416
1417 switch (mjcr->JobStatus) {
1418 case JS_Terminated:
1419 TermMsg = _("Backup OK");
1420 break;
1421 case JS_Warnings:
1422 TermMsg = _("Backup OK -- with warnings");
1423 break;
1424 case JS_FatalError:
1425 case JS_ErrorTerminated:
1426 TermMsg = _("*** Backup Error ***");
1427 break;
1428 case JS_Canceled:
1429 TermMsg = _("Backup Canceled");
1430 break;
1431 default:
1432 TermMsg = term_code;
1433 sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus);
1434 break;
1435 }
1436 bstrftime(sdt, sizeof(sdt), mjcr->start_time);
1437 bstrftime(edt, sizeof(edt), mjcr->end_time);
1438 Pmsg15(000,
1439 _("%s\n"
1440 "JobId: %d\n"
1441 "Job: %s\n"
1442 "FileSet: %s\n"
1443 "Backup Level: %s\n"
1444 "Client: %s\n"
1445 "Start time: %s\n"
1446 "End time: %s\n"
1447 "Files Written: %s\n"
1448 "Bytes Written: %s\n"
1449 "Volume Session Id: %d\n"
1450 "Volume Session Time: %d\n"
1451 "Last Volume Bytes: %s\n"
1452 "Bareos binary info: %s\n"
1453 "Termination: %s\n\n"),
1454 edt, mjcr->JobId, mjcr->Job, mjcr->impl->fileset_name,
1455 job_level_to_str(mjcr->getJobLevel()), mjcr->client_name, sdt, edt,
1456 edit_uint64_with_commas(mjcr->JobFiles, ec1),
1457 edit_uint64_with_commas(mjcr->JobBytes, ec2), mjcr->VolSessionId,
1458 mjcr->VolSessionTime, edit_uint64_with_commas(mr.VolBytes, ec3),
1459 kBareosVersionStrings.BinaryInfo, TermMsg);
1460 }
1461 FreeJcr(mjcr);
1462
1463 return true;
1464 }
1465
CreateJobmediaRecord(BareosDb * db,JobControlRecord * mjcr)1466 static bool CreateJobmediaRecord(BareosDb* db, JobControlRecord* mjcr)
1467 {
1468 JobMediaDbRecord jmr;
1469 DeviceControlRecord* dcr = mjcr->impl->read_dcr;
1470
1471 dcr->EndBlock = dev->EndBlock;
1472 dcr->EndFile = dev->EndFile;
1473 dcr->VolMediaId = dev->VolCatInfo.VolMediaId;
1474
1475 jmr.JobId = mjcr->JobId;
1476 jmr.MediaId = mr.MediaId;
1477 jmr.FirstIndex = dcr->VolFirstIndex;
1478 jmr.LastIndex = dcr->VolLastIndex;
1479 jmr.StartFile = dcr->StartFile;
1480 jmr.EndFile = dcr->EndFile;
1481 jmr.StartBlock = dcr->StartBlock;
1482 jmr.EndBlock = dcr->EndBlock;
1483
1484 if (!update_db) { return true; }
1485
1486 if (!db->CreateJobmediaRecord(bjcr, &jmr)) {
1487 Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db->strerror());
1488 return false;
1489 }
1490 if (verbose) {
1491 Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"), jmr.JobId,
1492 jmr.MediaId);
1493 }
1494
1495 return true;
1496 }
1497
1498 /**
1499 * Simulate the database call that updates the MD5/SHA1 record
1500 */
UpdateDigestRecord(BareosDb * db,char * digest,DeviceRecord * rec,int type)1501 static bool UpdateDigestRecord(BareosDb* db,
1502 char* digest,
1503 DeviceRecord* rec,
1504 int type)
1505 {
1506 JobControlRecord* mjcr;
1507
1508 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1509 if (!mjcr) {
1510 if (mr.VolJobs > 0) {
1511 Pmsg2(000,
1512 _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"),
1513 rec->VolSessionId, rec->VolSessionTime);
1514 } else {
1515 ignored_msgs++;
1516 }
1517 return false;
1518 }
1519
1520 if (!update_db || mjcr->FileId == 0) {
1521 FreeJcr(mjcr);
1522 return true;
1523 }
1524
1525 if (!db->AddDigestToFileRecord(bjcr, mjcr->FileId, digest, type)) {
1526 Pmsg1(0, _("Could not add MD5/SHA1 to File record. ERR=%s\n"),
1527 db->strerror());
1528 FreeJcr(mjcr);
1529 return false;
1530 }
1531
1532 if (verbose > 1) { Pmsg0(000, _("Updated MD5/SHA1 record\n")); }
1533 FreeJcr(mjcr);
1534
1535 return true;
1536 }
1537
1538 /**
1539 * Create a JobControlRecord as if we are really starting the job
1540 */
create_jcr(JobDbRecord * jr,DeviceRecord * rec,uint32_t JobId)1541 static JobControlRecord* create_jcr(JobDbRecord* jr,
1542 DeviceRecord* rec,
1543 uint32_t JobId)
1544 {
1545 JobControlRecord* jobjcr;
1546 /*
1547 * Transfer as much as possible to the Job JobControlRecord. Most important is
1548 * the JobId and the ClientId.
1549 */
1550 jobjcr = new_jcr(BscanFreeJcr);
1551 jobjcr->impl = new JobControlRecordPrivate;
1552 jobjcr->setJobType(jr->JobType);
1553 jobjcr->setJobLevel(jr->JobLevel);
1554 jobjcr->JobStatus = jr->JobStatus;
1555 bstrncpy(jobjcr->Job, jr->Job, sizeof(jobjcr->Job));
1556 jobjcr->JobId = JobId; /* this is JobId on tape */
1557 jobjcr->sched_time = jr->SchedTime;
1558 jobjcr->start_time = jr->StartTime;
1559 jobjcr->VolSessionId = rec->VolSessionId;
1560 jobjcr->VolSessionTime = rec->VolSessionTime;
1561 jobjcr->ClientId = jr->ClientId;
1562 jobjcr->impl->dcr = jobjcr->impl->read_dcr = new DeviceControlRecord;
1563 SetupNewDcrDevice(jobjcr, jobjcr->impl->dcr, dev, NULL);
1564
1565 return jobjcr;
1566 }
1567