3 * Program to scan a Bacula Volume and compare it with
4 * the catalog and optionally synchronize the catalog
7 * Kern E. Sibbald, December 2001
13 Copyright (C) 2001-2006 Kern Sibbald
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License
17 version 2 as amended with additional clauses defined in the
18 file LICENSE in the main source directory.
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 the file LICENSE for additional details.
29 #include "findlib/find.h"
30 #include "cats/cats.h"
33 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
35 /* Forward referenced functions */
36 static void do_scan(void);
37 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
38 static int create_file_attributes_record(B_DB *db, JCR *mjcr,
39 char *fname, char *lname, int type,
40 char *ap, DEV_RECORD *rec);
41 static int create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl);
42 static bool update_media_record(B_DB *db, MEDIA_DBR *mr);
43 static int create_pool_record(B_DB *db, POOL_DBR *pr);
44 static JCR *create_job_record(B_DB *db, JOB_DBR *mr, SESSION_LABEL *label, DEV_RECORD *rec);
45 static int update_job_record(B_DB *db, JOB_DBR *mr, SESSION_LABEL *elabel,
47 static int create_client_record(B_DB *db, CLIENT_DBR *cr);
48 static int create_fileset_record(B_DB *db, FILESET_DBR *fsr);
49 static int create_jobmedia_record(B_DB *db, JCR *jcr);
50 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId);
51 static int update_digest_record(B_DB *db, char *digest, DEV_RECORD *rec, int type);
55 static DEVICE *dev = NULL;
57 static JCR *bjcr; /* jcr for bscan */
58 static BSR *bsr = NULL;
63 static FILESET_DBR fsr;
66 static SESSION_LABEL label;
67 static SESSION_LABEL elabel;
70 static time_t lasttime = 0;
72 static const char *db_name = "bacula";
73 static const char *db_user = "bacula";
74 static const char *db_password = "";
75 static const char *db_host = NULL;
76 static const char *wd = NULL;
77 static bool update_db = false;
78 static bool update_vol_info = false;
79 static bool list_records = false;
80 static int ignored_msgs = 0;
82 static uint64_t currentVolumeSize;
83 static int last_pct = -1;
84 static bool showProgress = false;
85 static int num_jobs = 0;
86 static int num_pools = 0;
87 static int num_media = 0;
88 static int num_files = 0;
90 #define CONFIG_FILE "bacula-sd.conf"
91 char *configfile = NULL;
92 STORES *me = NULL; /* our Global resource */
93 bool forge_on = false; /* proceed inspite of I/O errors */
94 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
95 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
101 "Copyright (C) 2001-%s Kern Sibbald.\n"
102 "\nVersion: %s (%s)\n\n"
103 "Usage: bscan [ options ] <bacula-archive>\n"
104 " -b bootstrap specify a bootstrap file\n"
105 " -c <file> specify configuration file\n"
106 " -d <nn> set debug level to nn\n"
107 " -m update media info in database\n"
108 " -n <name> specify the database name (default bacula)\n"
109 " -u <user> specify database user name (default bacula)\n"
110 " -P <password specify database password (default none)\n"
111 " -h <host> specify database host (default NULL)\n"
112 " -p proceed inspite of I/O errors\n"
114 " -s synchronize or store in database\n"
115 " -S show scan progress periodically\n"
117 " -V <Volumes> specify Volume names (separated by |)\n"
118 " -w <dir> specify working directory (default from conf file)\n"
119 " -? print this message\n\n"), BYEAR, VERSION, BDATE);
123 int main (int argc, char *argv[])
126 struct stat stat_buf;
127 char *VolumeName = NULL;
129 setlocale(LC_ALL, "");
130 bindtextdomain("bacula", LOCALEDIR);
131 textdomain("bacula");
133 my_name_is(argc, argv, "bscan");
134 init_msg(NULL, NULL);
137 while ((ch = getopt(argc, argv, "b:c:d:h:mn:pP:rsSu:vV:w:?")) != -1) {
143 bsr = parse_bsr(NULL, optarg);
146 case 'c': /* specify config file */
147 if (configfile != NULL) {
150 configfile = bstrdup(optarg);
153 case 'd': /* debug level */
154 debug_level = atoi(optarg);
155 if (debug_level <= 0)
164 update_vol_info = true;
176 db_password = optarg;
195 case 'V': /* Volume name */
213 Pmsg0(0, _("Wrong number of arguments: \n"));
217 if (configfile == NULL) {
218 configfile = bstrdup(CONFIG_FILE);
221 parse_config(configfile);
223 me = (STORES *)GetNextRes(R_STORAGE, NULL);
226 Emsg1(M_ERROR_TERM, 0, _("No Storage resource defined in %s. Cannot continue.\n"),
230 /* Check if -w option given, otherwise use resource for working directory */
232 working_directory = wd;
233 } else if (!me->working_directory) {
234 Emsg1(M_ERROR_TERM, 0, _("No Working Directory defined in %s. Cannot continue.\n"),
237 working_directory = me->working_directory;
240 /* Check that working directory is good */
241 if (stat(working_directory, &stat_buf) != 0) {
242 Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s not found. Cannot continue.\n"),
245 if (!S_ISDIR(stat_buf.st_mode)) {
246 Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s is not a directory. Cannot continue.\n"),
250 bjcr = setup_jcr("bscan", argv[0], bsr, VolumeName, 1); /* read device */
254 dev = bjcr->read_dcr->dev;
259 currentVolumeSize = sb.st_size;
260 Pmsg1(000, _("First Volume Size = %sn"),
261 edit_uint64(currentVolumeSize, ed1));
264 if ((db=db_init_database(NULL, db_name, db_user, db_password,
265 db_host, 0, NULL, 0)) == NULL) {
266 Emsg0(M_ERROR_TERM, 0, _("Could not init Bacula database\n"));
268 if (!db_open_database(NULL, db)) {
269 Emsg0(M_ERROR_TERM, 0, db_strerror(db));
271 Dmsg0(200, "Database opened\n");
273 Pmsg2(000, _("Using Database: %s, User: %s\n"), db_name, db_user);
278 printf("Records added or updated in the catalog:\n%7d Media\n%7d Pool\n%7d Job\n%7d File\n",
279 num_media, num_pools, num_jobs, num_files);
282 printf("Records would have been added or updated in the catalog:\n%7d Media\n%7d Pool\n%7d Job\n%7d File\n",
283 num_media, num_pools, num_jobs, num_files);
292 * We are at the end of reading a tape. Now, we simulate handling
293 * the end of writing a tape by wiffling through the attached
294 * jcrs creating jobmedia records.
296 static bool bscan_mount_next_read_volume(DCR *dcr)
298 DEVICE *dev = dcr->dev;
300 Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->VolCatInfo.VolCatName);
301 foreach_dlist(mdcr, dev->attached_dcrs) {
302 JCR *mjcr = mdcr->jcr;
303 if (mjcr->JobId == 0) {
307 Pmsg1(000, _("Create JobMedia for Job %s\n"), mjcr->Job);
309 if (dev->is_tape()) {
310 mdcr->EndBlock = dcr->EndBlock;
311 mdcr->EndFile = dcr->EndFile;
313 // mdcr->EndBlock = (uint32_t)dcr->file_addr;
314 // mdcr->EndFile = (uint32_t)(dcr->file_addr >> 32);
316 mjcr->read_dcr->VolLastIndex = dcr->VolLastIndex;
317 if (!create_jobmedia_record(db, mjcr)) {
318 Pmsg2(000, _("Could not create JobMedia record for Volume=%s Job=%s\n"),
319 dev->VolCatInfo.VolCatName, mjcr->Job);
322 /* Now let common read routine get up next tape. Note,
323 * we call mount_next... with bscan's jcr because that is where we
324 * have the Volume list, but we get attached.
326 bool stat = mount_next_read_volume(dcr);
332 currentVolumeSize = sb.st_size;
333 Pmsg1(000, _("First Volume Size = %sn"),
334 edit_uint64(currentVolumeSize, ed1));
339 static void do_scan()
343 memset(&ar, 0, sizeof(ar));
344 memset(&pr, 0, sizeof(pr));
345 memset(&jr, 0, sizeof(jr));
346 memset(&cr, 0, sizeof(cr));
347 memset(&fsr, 0, sizeof(fsr));
348 memset(&fr, 0, sizeof(fr));
350 /* Detach bscan's jcr as we are not a real Job on the tape */
352 read_records(bjcr->read_dcr, record_cb, bscan_mount_next_read_volume);
358 * Returns: true if OK
361 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
365 DEVICE *dev = dcr->dev;
366 JCR *bjcr = dcr->jcr;
367 DEV_BLOCK *block = dcr->block;
368 char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
370 if (rec->data_len > 0) {
371 mr.VolBytes += rec->data_len + WRITE_RECHDR_LENGTH; /* Accumulate Volume bytes */
372 if (showProgress && currentVolumeSize > 0) {
373 int pct = (mr.VolBytes * 100) / currentVolumeSize;
374 if (pct != last_pct) {
375 fprintf(stdout, _("done: %d%%\n"), pct);
383 Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
384 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
385 rec->Stream, rec->data_len);
388 * Check for Start or End of Session Record
391 if (rec->FileIndex < 0) {
392 bool save_update_db = update_db;
395 dump_label_record(dev, rec, 1);
397 switch (rec->FileIndex) {
399 Pmsg0(000, _("Volume is prelabeled. This tape cannot be scanned.\n"));
404 unser_volume_label(dev, rec);
405 /* Check Pool info */
406 bstrncpy(pr.Name, dev->VolHdr.PoolName, sizeof(pr.Name));
407 bstrncpy(pr.PoolType, dev->VolHdr.PoolType, sizeof(pr.PoolType));
409 if (db_get_pool_record(bjcr, db, &pr)) {
411 Pmsg1(000, _("Pool record for %s found in DB.\n"), pr.Name);
415 Pmsg1(000, _("VOL_LABEL: Pool record not found for Pool: %s\n"),
418 create_pool_record(db, &pr);
420 if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
421 Pmsg2(000, _("VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n"),
422 pr.PoolType, dev->VolHdr.PoolType);
424 } else if (verbose) {
425 Pmsg1(000, _("Pool type \"%s\" is OK.\n"), pr.PoolType);
428 /* Check Media Info */
429 memset(&mr, 0, sizeof(mr));
430 bstrncpy(mr.VolumeName, dev->VolHdr.VolumeName, sizeof(mr.VolumeName));
431 mr.PoolId = pr.PoolId;
433 if (db_get_media_record(bjcr, db, &mr)) {
435 Pmsg1(000, _("Media record for %s found in DB.\n"), mr.VolumeName);
437 /* Clear out some volume statistics that will be updated */
438 mr.VolJobs = mr.VolFiles = mr.VolBlocks = 0;
439 mr.VolBytes = rec->data_len + 20;
442 Pmsg1(000, _("VOL_LABEL: Media record not found for Volume: %s\n"),
445 bstrncpy(mr.MediaType, dev->VolHdr.MediaType, sizeof(mr.MediaType));
446 create_media_record(db, &mr, &dev->VolHdr);
448 if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
449 Pmsg2(000, _("VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n"),
450 mr.MediaType, dev->VolHdr.MediaType);
451 return true; /* ignore error */
452 } else if (verbose) {
453 Pmsg1(000, _("Media type \"%s\" is OK.\n"), mr.MediaType);
455 /* Reset some DCR variables */
456 foreach_dlist(dcr, dev->attached_dcrs) {
457 dcr->VolFirstIndex = dcr->FileIndex = 0;
458 dcr->StartBlock = dcr->EndBlock = 0;
459 dcr->StartFile = dcr->EndFile = 0;
462 Pmsg1(000, _("VOL_LABEL: OK for Volume: %s\n"), mr.VolumeName);
468 if (ignored_msgs > 0) {
469 Pmsg1(000, _("%d \"errors\" ignored before first Start of Session record.\n"),
473 unser_session_label(&label, rec);
474 memset(&jr, 0, sizeof(jr));
475 bstrncpy(jr.Job, label.Job, sizeof(jr.Job));
476 if (db_get_job_record(bjcr, db, &jr)) {
477 /* Job record already exists in DB */
478 update_db = false; /* don't change db in create_job_record */
480 Pmsg1(000, _("SOS_LABEL: Found Job record for JobId: %d\n"), jr.JobId);
483 /* Must create a Job record in DB */
485 Pmsg1(000, _("SOS_LABEL: Job record not found for JobId: %d\n"),
489 /* Create Client record if not already there */
490 bstrncpy(cr.Name, label.ClientName, sizeof(cr.Name));
491 create_client_record(db, &cr);
492 jr.ClientId = cr.ClientId;
494 /* process label, if Job record exists don't update db */
495 mjcr = create_job_record(db, &jr, &label, rec);
496 dcr = mjcr->read_dcr;
497 update_db = save_update_db;
499 jr.PoolId = pr.PoolId;
501 /* Set start positions into JCR */
502 if (dev->is_tape()) {
504 * Note, we have already advanced past current block,
505 * so the correct number is block_num - 1
507 dcr->StartBlock = dev->block_num - 1;
508 dcr->StartFile = dev->file;
510 dcr->StartBlock = (uint32_t)dev->file_addr;
511 dcr->StartFile = (uint32_t)(dev->file_addr >> 32);
514 mjcr->start_time = jr.StartTime;
515 mjcr->JobLevel = jr.JobLevel;
517 mjcr->client_name = get_pool_memory(PM_FNAME);
518 pm_strcpy(mjcr->client_name, label.ClientName);
519 mjcr->fileset_name = get_pool_memory(PM_FNAME);
520 pm_strcpy(mjcr->fileset_name, label.FileSetName);
521 bstrncpy(dcr->pool_type, label.PoolType, sizeof(dcr->pool_type));
522 bstrncpy(dcr->pool_name, label.PoolName, sizeof(dcr->pool_name));
524 if (rec->VolSessionId != jr.VolSessionId) {
525 Pmsg3(000, _("SOS_LABEL: VolSessId mismatch for JobId=%u. DB=%d Vol=%d\n"),
527 jr.VolSessionId, rec->VolSessionId);
528 return true; /* ignore error */
530 if (rec->VolSessionTime != jr.VolSessionTime) {
531 Pmsg3(000, _("SOS_LABEL: VolSessTime mismatch for JobId=%u. DB=%d Vol=%d\n"),
533 jr.VolSessionTime, rec->VolSessionTime);
534 return true; /* ignore error */
536 if (jr.PoolId != pr.PoolId) {
537 Pmsg3(000, _("SOS_LABEL: PoolId mismatch for JobId=%u. DB=%d Vol=%d\n"),
539 jr.PoolId, pr.PoolId);
540 return true; /* ignore error */
545 unser_session_label(&elabel, rec);
547 /* Create FileSet record */
548 bstrncpy(fsr.FileSet, label.FileSetName, sizeof(fsr.FileSet));
549 bstrncpy(fsr.MD5, label.FileSetMD5, sizeof(fsr.MD5));
550 create_fileset_record(db, &fsr);
551 jr.FileSetId = fsr.FileSetId;
553 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
555 Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
556 rec->VolSessionId, rec->VolSessionTime);
560 /* Do the final update to the Job record */
561 update_job_record(db, &jr, &elabel, rec);
563 mjcr->end_time = jr.EndTime;
564 mjcr->JobStatus = JS_Terminated;
566 /* Create JobMedia record */
567 mjcr->read_dcr->VolLastIndex = dcr->VolLastIndex;
568 create_jobmedia_record(db, mjcr);
569 dev->attached_dcrs->remove(mjcr->read_dcr);
577 case EOT_LABEL: /* end of all tapes */
579 * Wiffle through all jobs still open and close
584 foreach_dlist(mdcr, dev->attached_dcrs) {
585 JCR *mjcr = mdcr->jcr;
586 if (!mjcr || mjcr->JobId == 0) {
589 jr.JobId = mjcr->JobId;
590 /* Mark Job as Error Terimined */
591 jr.JobStatus = JS_ErrorTerminated;
592 jr.JobFiles = mjcr->JobFiles;
593 jr.JobBytes = mjcr->JobBytes;
594 jr.VolSessionId = mjcr->VolSessionId;
595 jr.VolSessionTime = mjcr->VolSessionTime;
596 jr.JobTDate = (utime_t)mjcr->start_time;
597 jr.ClientId = mjcr->ClientId;
598 if (!db_update_job_end_record(bjcr, db, &jr)) {
599 Pmsg1(0, _("Could not update job record. ERR=%s\n"), db_strerror(db));
601 mjcr->read_dcr = NULL;
605 mr.VolFiles = rec->File;
606 mr.VolBlocks = rec->Block;
607 mr.VolBytes += mr.VolBlocks * WRITE_BLKHDR_LENGTH; /* approx. */
609 update_media_record(db, &mr);
610 Pmsg3(0, _("End of all Volumes. VolFiles=%u VolBlocks=%u VolBytes=%s\n"), mr.VolFiles,
611 mr.VolBlocks, edit_uint64_with_commas(mr.VolBytes, ec1));
619 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
621 if (mr.VolJobs > 0) {
622 Pmsg2(000, _("Could not find Job for SessId=%d SessTime=%d record.\n"),
623 rec->VolSessionId, rec->VolSessionTime);
629 dcr = mjcr->read_dcr;
630 if (dcr->VolFirstIndex == 0) {
631 dcr->VolFirstIndex = block->FirstIndex;
634 /* File Attributes stream */
635 switch (rec->Stream) {
636 case STREAM_UNIX_ATTRIBUTES:
637 case STREAM_UNIX_ATTRIBUTES_EX:
639 if (!unpack_attributes_record(bjcr, rec->Stream, rec->data, attr)) {
640 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
643 if (attr->file_index != rec->FileIndex) {
644 Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
645 rec->FileIndex, attr->file_index);
649 decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
650 build_attr_output_fnames(bjcr, attr);
651 print_ls_output(bjcr, attr);
653 fr.JobId = mjcr->JobId;
656 if (verbose && (num_files & 0x7FFF) == 0) {
657 char ed1[30], ed2[30], ed3[30], ed4[30];
658 Pmsg4(000, _("%s file records. At file:blk=%s:%s bytes=%s\n"),
659 edit_uint64_with_commas(num_files, ed1),
660 edit_uint64_with_commas(rec->File, ed2),
661 edit_uint64_with_commas(rec->Block, ed3),
662 edit_uint64_with_commas(mr.VolBytes, ed4));
664 create_file_attributes_record(db, mjcr, attr->fname, attr->lname,
665 attr->type, attr->attr, rec);
670 case STREAM_WIN32_DATA:
671 case STREAM_FILE_DATA:
672 case STREAM_SPARSE_DATA:
673 case STREAM_ENCRYPTED_FILE_DATA:
674 case STREAM_ENCRYPTED_WIN32_DATA:
675 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
677 * For encrypted stream, this is an approximation.
678 * The data must be decrypted to know the correct length.
680 mjcr->JobBytes += rec->data_len;
681 if (rec->Stream == STREAM_SPARSE_DATA) {
682 mjcr->JobBytes -= sizeof(uint64_t);
685 free_jcr(mjcr); /* done using JCR */
688 case STREAM_GZIP_DATA:
689 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
690 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
691 /* No correct, we should (decrypt and) expand it
694 mjcr->JobBytes += rec->data_len;
698 case STREAM_SPARSE_GZIP_DATA:
699 mjcr->JobBytes += rec->data_len - sizeof(uint64_t); /* No correct, we should expand it */
700 free_jcr(mjcr); /* done using JCR */
703 /* Win32 GZIP stream */
704 case STREAM_WIN32_GZIP_DATA:
705 mjcr->JobBytes += rec->data_len;
706 free_jcr(mjcr); /* done using JCR */
709 case STREAM_MD5_DIGEST:
710 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_MD5_SIZE, true);
712 Pmsg1(000, _("Got MD5 record: %s\n"), digest);
714 update_digest_record(db, digest, rec, CRYPTO_DIGEST_MD5);
717 case STREAM_SHA1_DIGEST:
718 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA1_SIZE, true);
720 Pmsg1(000, _("Got SHA1 record: %s\n"), digest);
722 update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA1);
725 case STREAM_SHA256_DIGEST:
726 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA256_SIZE, true);
728 Pmsg1(000, _("Got SHA256 record: %s\n"), digest);
730 update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA256);
733 case STREAM_SHA512_DIGEST:
734 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA512_SIZE, true);
736 Pmsg1(000, _("Got SHA512 record: %s\n"), digest);
738 update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA512);
741 case STREAM_ENCRYPTED_SESSION_DATA:
742 // TODO landonf: Investigate crypto support in bscan
744 Pmsg0(000, _("Got signed digest record\n"));
748 case STREAM_SIGNED_DIGEST:
749 // TODO landonf: Investigate crypto support in bscan
751 Pmsg0(000, _("Got signed digest record\n"));
755 case STREAM_PROGRAM_NAMES:
757 Pmsg1(000, _("Got Prog Names Stream: %s\n"), rec->data);
761 case STREAM_PROGRAM_DATA:
763 Pmsg0(000, _("Got Prog Data Stream record.\n"));
767 case STREAM_UNIX_ATTRIBUTES_ACCESS_ACL: /* Standard ACL attributes on UNIX */
768 case STREAM_UNIX_ATTRIBUTES_DEFAULT_ACL: /* Default ACL attributes on UNIX */
769 /* Ignore Unix attributes */
773 Pmsg2(0, _("Unknown stream type!!! stream=%d len=%i\n"), rec->Stream, rec->data_len);
780 * Free the Job Control Record if no one is still using it.
781 * Called from main free_jcr() routine in src/lib/jcr.c so
782 * that we can do our Director specific cleanup of the jcr.
784 static void bscan_free_jcr(JCR *jcr)
786 Dmsg0(200, "Start bscan free_jcr\n");
788 if (jcr->file_bsock) {
789 Dmsg0(200, "Close File bsock\n");
790 bnet_close(jcr->file_bsock);
792 if (jcr->store_bsock) {
793 Dmsg0(200, "Close Store bsock\n");
794 bnet_close(jcr->store_bsock);
796 if (jcr->RestoreBootstrap) {
797 free(jcr->RestoreBootstrap);
804 free_dcr(jcr->read_dcr);
805 jcr->read_dcr = NULL;
807 Dmsg0(200, "End bscan free_jcr\n");
811 * We got a File Attributes record on the tape. Now, lookup the Job
812 * record, and then create the attributes record.
814 static int create_file_attributes_record(B_DB *db, JCR *mjcr,
815 char *fname, char *lname, int type,
816 char *ap, DEV_RECORD *rec)
818 DCR *dcr = mjcr->read_dcr;
821 ar.ClientId = mjcr->ClientId;
822 ar.JobId = mjcr->JobId;
823 ar.Stream = rec->Stream;
824 ar.FileIndex = rec->FileIndex;
826 if (dcr->VolFirstIndex == 0) {
827 dcr->VolFirstIndex = rec->FileIndex;
829 dcr->FileIndex = rec->FileIndex;
836 if (!db_create_file_attributes_record(bjcr, db, &ar)) {
837 Pmsg1(0, _("Could not create File Attributes record. ERR=%s\n"), db_strerror(db));
840 mjcr->FileId = ar.FileId;
843 Pmsg1(000, _("Created File record: %s\n"), fname);
849 * For each Volume we see, we create a Medium record
851 static int create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl)
856 /* We mark Vols as Archive to keep them from being re-written */
857 bstrncpy(mr->VolStatus, "Archive", sizeof(mr->VolStatus));
858 mr->VolRetention = 365 * 3600 * 24; /* 1 year */
860 if (vl->VerNum >= 11) {
861 mr->FirstWritten = btime_to_utime(vl->write_btime);
862 mr->LabelDate = btime_to_utime(vl->label_btime);
864 /* DEPRECATED DO NOT USE */
865 dt.julian_day_number = vl->write_date;
866 dt.julian_day_fraction = vl->write_time;
868 mr->FirstWritten = mktime(&tm);
869 dt.julian_day_number = vl->label_date;
870 dt.julian_day_fraction = vl->label_time;
872 mr->LabelDate = mktime(&tm);
874 lasttime = mr->LabelDate;
880 if (!db_create_media_record(bjcr, db, mr)) {
881 Pmsg1(0, _("Could not create media record. ERR=%s\n"), db_strerror(db));
884 if (!db_update_media_record(bjcr, db, mr)) {
885 Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
889 Pmsg1(000, _("Created Media record for Volume: %s\n"), mr->VolumeName);
896 * Called at end of media to update it
898 static bool update_media_record(B_DB *db, MEDIA_DBR *mr)
900 if (!update_db && !update_vol_info) {
904 mr->LastWritten = lasttime;
905 if (!db_update_media_record(bjcr, db, mr)) {
906 Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
910 Pmsg1(000, _("Updated Media record at end of Volume: %s\n"), mr->VolumeName);
917 static int create_pool_record(B_DB *db, POOL_DBR *pr)
921 pr->VolRetention = 355 * 3600 * 24; /* 1 year */
926 if (!db_create_pool_record(bjcr, db, pr)) {
927 Pmsg1(0, _("Could not create pool record. ERR=%s\n"), db_strerror(db));
931 Pmsg1(000, _("Created Pool record for Pool: %s\n"), pr->Name);
939 * Called from SOS to create a client for the current Job
941 static int create_client_record(B_DB *db, CLIENT_DBR *cr)
946 if (!db_create_client_record(bjcr, db, cr)) {
947 Pmsg1(0, _("Could not create Client record. ERR=%s\n"), db_strerror(db));
951 Pmsg1(000, _("Created Client record for Client: %s\n"), cr->Name);
956 static int create_fileset_record(B_DB *db, FILESET_DBR *fsr)
962 if (fsr->MD5[0] == 0) {
963 fsr->MD5[0] = ' '; /* Equivalent to nothing */
966 if (db_get_fileset_record(bjcr, db, fsr)) {
968 Pmsg1(000, _("Fileset \"%s\" already exists.\n"), fsr->FileSet);
971 if (!db_create_fileset_record(bjcr, db, fsr)) {
972 Pmsg2(0, _("Could not create FileSet record \"%s\". ERR=%s\n"),
973 fsr->FileSet, db_strerror(db));
977 Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet);
984 * Simulate the two calls on the database to create
985 * the Job record and to update it when the Job actually
988 static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label,
995 jr->JobId = label->JobId;
996 jr->JobType = label->JobType;
997 jr->JobLevel = label->JobLevel;
998 jr->JobStatus = JS_Created;
999 bstrncpy(jr->Name, label->JobName, sizeof(jr->Name));
1000 bstrncpy(jr->Job, label->Job, sizeof(jr->Job));
1001 if (label->VerNum >= 11) {
1002 jr->SchedTime = btime_to_unix(label->write_btime);
1004 dt.julian_day_number = label->write_date;
1005 dt.julian_day_fraction = label->write_time;
1006 tm_decode(&dt, &tm);
1007 jr->SchedTime = mktime(&tm);
1010 jr->StartTime = jr->SchedTime;
1011 jr->JobTDate = (utime_t)jr->SchedTime;
1012 jr->VolSessionId = rec->VolSessionId;
1013 jr->VolSessionTime = rec->VolSessionTime;
1015 /* Now create a JCR as if starting the Job */
1016 mjcr = create_jcr(jr, rec, label->JobId);
1022 /* This creates the bare essentials */
1023 if (!db_create_job_record(bjcr, db, jr)) {
1024 Pmsg1(0, _("Could not create JobId record. ERR=%s\n"), db_strerror(db));
1028 /* This adds the client, StartTime, JobTDate, ... */
1029 if (!db_update_job_start_record(bjcr, db, jr)) {
1030 Pmsg1(0, _("Could not update job start record. ERR=%s\n"), db_strerror(db));
1033 Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"), jr->JobId,
1035 mjcr->JobId = jr->JobId; /* set new JobId */
1040 * Simulate the database call that updates the Job
1041 * at Job termination time.
1043 static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
1046 struct date_time dt;
1050 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1052 Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
1053 rec->VolSessionId, rec->VolSessionTime);
1056 if (elabel->VerNum >= 11) {
1057 jr->EndTime = btime_to_unix(elabel->write_btime);
1059 dt.julian_day_number = elabel->write_date;
1060 dt.julian_day_fraction = elabel->write_time;
1061 tm_decode(&dt, &tm);
1062 jr->EndTime = mktime(&tm);
1064 lasttime = jr->EndTime;
1065 mjcr->end_time = jr->EndTime;
1067 jr->JobId = mjcr->JobId;
1068 jr->JobStatus = elabel->JobStatus;
1069 mjcr->JobStatus = elabel->JobStatus;
1070 jr->JobFiles = elabel->JobFiles;
1071 jr->JobBytes = elabel->JobBytes;
1072 jr->VolSessionId = rec->VolSessionId;
1073 jr->VolSessionTime = rec->VolSessionTime;
1074 jr->JobTDate = (utime_t)mjcr->start_time;
1075 jr->ClientId = mjcr->ClientId;
1082 if (!db_update_job_end_record(bjcr, db, jr)) {
1083 Pmsg2(0, _("Could not update JobId=%u record. ERR=%s\n"), jr->JobId, db_strerror(db));
1088 Pmsg3(000, _("Updated Job termination record for JobId=%u Level=%s TermStat=%c\n"),
1089 jr->JobId, job_level_to_str(mjcr->JobLevel), jr->JobStatus);
1092 const char *term_msg;
1093 static char term_code[70];
1094 char sdt[50], edt[50];
1095 char ec1[30], ec2[30], ec3[30];
1097 switch (mjcr->JobStatus) {
1099 term_msg = _("Backup OK");
1102 case JS_ErrorTerminated:
1103 term_msg = _("*** Backup Error ***");
1106 term_msg = _("Backup Canceled");
1109 term_msg = term_code;
1110 sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus);
1113 bstrftime(sdt, sizeof(sdt), mjcr->start_time);
1114 bstrftime(edt, sizeof(edt), mjcr->end_time);
1115 Pmsg14(000, _("%s\n"
1119 "Backup Level: %s\n"
1123 "Files Written: %s\n"
1124 "Bytes Written: %s\n"
1125 "Volume Session Id: %d\n"
1126 "Volume Session Time: %d\n"
1127 "Last Volume Bytes: %s\n"
1128 "Termination: %s\n\n"),
1133 job_level_to_str(mjcr->JobLevel),
1137 edit_uint64_with_commas(mjcr->JobFiles, ec1),
1138 edit_uint64_with_commas(mjcr->JobBytes, ec2),
1140 mjcr->VolSessionTime,
1141 edit_uint64_with_commas(mr.VolBytes, ec3),
1148 static int create_jobmedia_record(B_DB *db, JCR *mjcr)
1151 DCR *dcr = mjcr->read_dcr;
1153 if (dev->is_tape()) {
1154 dcr->EndBlock = dev->EndBlock;
1155 dcr->EndFile = dev->EndFile;
1158 dcr->EndBlock = (uint32_t)dev->file_addr;
1159 dcr->EndFile = (uint32_t)(dev->file_addr >> 32);
1163 memset(&jmr, 0, sizeof(jmr));
1164 jmr.JobId = mjcr->JobId;
1165 jmr.MediaId = mr.MediaId;
1166 jmr.FirstIndex = dcr->VolFirstIndex;
1167 jmr.LastIndex = dcr->VolLastIndex;
1168 jmr.StartFile = dcr->StartFile;
1169 jmr.EndFile = dcr->EndFile;
1170 jmr.StartBlock = dcr->StartBlock;
1171 jmr.EndBlock = dcr->EndBlock;
1178 if (!db_create_jobmedia_record(bjcr, db, &jmr)) {
1179 Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db_strerror(db));
1183 Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"),
1184 jmr.JobId, jmr.MediaId);
1190 * Simulate the database call that updates the MD5/SHA1 record
1192 static int update_digest_record(B_DB *db, char *digest, DEV_RECORD *rec, int type)
1196 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1198 if (mr.VolJobs > 0) {
1199 Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"),
1200 rec->VolSessionId, rec->VolSessionTime);
1207 if (!update_db || mjcr->FileId == 0) {
1212 if (!db_add_digest_to_file_record(bjcr, db, mjcr->FileId, digest, type)) {
1213 Pmsg1(0, _("Could not add MD5/SHA1 to File record. ERR=%s\n"), db_strerror(db));
1218 Pmsg0(000, _("Updated MD5/SHA1 record\n"));
1226 * Create a JCR as if we are really starting the job
1228 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
1232 * Transfer as much as possible to the Job JCR. Most important is
1233 * the JobId and the ClientId.
1235 jobjcr = new_jcr(sizeof(JCR), bscan_free_jcr);
1236 jobjcr->JobType = jr->JobType;
1237 jobjcr->JobLevel = jr->JobLevel;
1238 jobjcr->JobStatus = jr->JobStatus;
1239 bstrncpy(jobjcr->Job, jr->Job, sizeof(jobjcr->Job));
1240 jobjcr->JobId = JobId; /* this is JobId on tape */
1241 jobjcr->sched_time = jr->SchedTime;
1242 jobjcr->start_time = jr->StartTime;
1243 jobjcr->VolSessionId = rec->VolSessionId;
1244 jobjcr->VolSessionTime = rec->VolSessionTime;
1245 jobjcr->ClientId = jr->ClientId;
1246 jobjcr->read_dcr = new_dcr(jobjcr, dev);
1251 /* Dummies to replace askdir.c */
1252 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing) { return 1;}
1253 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
1254 bool dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
1255 bool dir_create_jobmedia_record(DCR *dcr) { return 1; }
1256 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
1257 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
1258 bool dir_send_job_status(JCR *jcr) {return 1;}
1259 int generate_job_event(JCR *jcr, const char *event) { return 1; }
1261 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
1263 DEVICE *dev = dcr->dev;
1264 Dmsg0(20, "Enter dir_ask_sysop_to_mount_volume\n");
1265 /* Close device so user can use autochanger if desired */
1266 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
1267 dcr->VolumeName, dev->print_name());