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 Bacula® - The Network Backup Solution
15 Copyright (C) 2001-2006 Free Software Foundation Europe e.V.
17 The main author of Bacula is Kern Sibbald, with contributions from
18 many others, a complete list can be found in the file AUTHORS.
19 This program is Free Software; you can redistribute it and/or
20 modify it under the terms of version two of the GNU General Public
21 License as published by the Free Software Foundation plus additions
22 that are listed in the file LICENSE.
24 This program is distributed in the hope that it will be useful, but
25 WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 General Public License for more details.
29 You should have received a copy of the GNU General Public License
30 along with this program; if not, write to the Free Software
31 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
34 Bacula® is a registered trademark of John Walker.
35 The licensor of Bacula is the Free Software Foundation Europe
36 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
37 Switzerland, email:ftf@fsfeurope.org.
42 #include "findlib/find.h"
43 #include "cats/cats.h"
46 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
48 /* Forward referenced functions */
49 static void do_scan(void);
50 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
51 static int create_file_attributes_record(B_DB *db, JCR *mjcr,
52 char *fname, char *lname, int type,
53 char *ap, DEV_RECORD *rec);
54 static int create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl);
55 static bool update_media_record(B_DB *db, MEDIA_DBR *mr);
56 static int create_pool_record(B_DB *db, POOL_DBR *pr);
57 static JCR *create_job_record(B_DB *db, JOB_DBR *mr, SESSION_LABEL *label, DEV_RECORD *rec);
58 static int update_job_record(B_DB *db, JOB_DBR *mr, SESSION_LABEL *elabel,
60 static int create_client_record(B_DB *db, CLIENT_DBR *cr);
61 static int create_fileset_record(B_DB *db, FILESET_DBR *fsr);
62 static int create_jobmedia_record(B_DB *db, JCR *jcr);
63 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId);
64 static int update_digest_record(B_DB *db, char *digest, DEV_RECORD *rec, int type);
68 static DEVICE *dev = NULL;
70 static JCR *bjcr; /* jcr for bscan */
71 static BSR *bsr = NULL;
76 static FILESET_DBR fsr;
79 static SESSION_LABEL label;
80 static SESSION_LABEL elabel;
83 static time_t lasttime = 0;
85 static const char *db_name = "bacula";
86 static const char *db_user = "bacula";
87 static const char *db_password = "";
88 static const char *db_host = NULL;
89 static const char *wd = NULL;
90 static bool update_db = false;
91 static bool update_vol_info = false;
92 static bool list_records = false;
93 static int ignored_msgs = 0;
95 static uint64_t currentVolumeSize;
96 static int last_pct = -1;
97 static bool showProgress = false;
98 static int num_jobs = 0;
99 static int num_pools = 0;
100 static int num_media = 0;
101 static int num_files = 0;
103 #define CONFIG_FILE "bacula-sd.conf"
104 char *configfile = NULL;
105 STORES *me = NULL; /* our Global resource */
106 bool forge_on = false; /* proceed inspite of I/O errors */
107 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
108 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
115 "\nVersion: %s (%s)\n\n"
116 "Usage: bscan [ options ] <bacula-archive>\n"
117 " -b bootstrap specify a bootstrap file\n"
118 " -c <file> specify configuration file\n"
119 " -d <nn> set debug level to nn\n"
120 " -m update media info in database\n"
121 " -n <name> specify the database name (default bacula)\n"
122 " -u <user> specify database user name (default bacula)\n"
123 " -P <password specify database password (default none)\n"
124 " -h <host> specify database host (default NULL)\n"
125 " -p proceed inspite of I/O errors\n"
127 " -s synchronize or store in database\n"
128 " -S show scan progress periodically\n"
130 " -V <Volumes> specify Volume names (separated by |)\n"
131 " -w <dir> specify working directory (default from conf file)\n"
132 " -? print this message\n\n"), 2001, VERSION, BDATE);
136 int main (int argc, char *argv[])
139 struct stat stat_buf;
140 char *VolumeName = NULL;
142 setlocale(LC_ALL, "");
143 bindtextdomain("bacula", LOCALEDIR);
144 textdomain("bacula");
147 my_name_is(argc, argv, "bscan");
148 init_msg(NULL, NULL);
152 while ((ch = getopt(argc, argv, "b:c:d:h:mn:pP:rsSu:vV:w:?")) != -1) {
158 bsr = parse_bsr(NULL, optarg);
161 case 'c': /* specify config file */
162 if (configfile != NULL) {
165 configfile = bstrdup(optarg);
168 case 'd': /* debug level */
169 debug_level = atoi(optarg);
170 if (debug_level <= 0)
179 update_vol_info = true;
191 db_password = optarg;
210 case 'V': /* Volume name */
228 Pmsg0(0, _("Wrong number of arguments: \n"));
232 if (configfile == NULL) {
233 configfile = bstrdup(CONFIG_FILE);
236 parse_config(configfile);
238 me = (STORES *)GetNextRes(R_STORAGE, NULL);
241 Emsg1(M_ERROR_TERM, 0, _("No Storage resource defined in %s. Cannot continue.\n"),
245 /* Check if -w option given, otherwise use resource for working directory */
247 working_directory = wd;
248 } else if (!me->working_directory) {
249 Emsg1(M_ERROR_TERM, 0, _("No Working Directory defined in %s. Cannot continue.\n"),
252 working_directory = me->working_directory;
255 /* Check that working directory is good */
256 if (stat(working_directory, &stat_buf) != 0) {
257 Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s not found. Cannot continue.\n"),
260 if (!S_ISDIR(stat_buf.st_mode)) {
261 Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s is not a directory. Cannot continue.\n"),
265 bjcr = setup_jcr("bscan", argv[0], bsr, VolumeName, 1); /* read device */
269 dev = bjcr->read_dcr->dev;
273 fstat(dev->fd(), &sb);
274 currentVolumeSize = sb.st_size;
275 Pmsg1(000, _("First Volume Size = %sn"),
276 edit_uint64(currentVolumeSize, ed1));
279 if ((db=db_init_database(NULL, db_name, db_user, db_password,
280 db_host, 0, NULL, 0)) == NULL) {
281 Emsg0(M_ERROR_TERM, 0, _("Could not init Bacula database\n"));
283 if (!db_open_database(NULL, db)) {
284 Emsg0(M_ERROR_TERM, 0, db_strerror(db));
286 Dmsg0(200, "Database opened\n");
288 Pmsg2(000, _("Using Database: %s, User: %s\n"), db_name, db_user);
293 printf("Records added or updated in the catalog:\n%7d Media\n%7d Pool\n%7d Job\n%7d File\n",
294 num_media, num_pools, num_jobs, num_files);
297 printf("Records would have been added or updated in the catalog:\n%7d Media\n%7d Pool\n%7d Job\n%7d File\n",
298 num_media, num_pools, num_jobs, num_files);
307 * We are at the end of reading a tape. Now, we simulate handling
308 * the end of writing a tape by wiffling through the attached
309 * jcrs creating jobmedia records.
311 static bool bscan_mount_next_read_volume(DCR *dcr)
313 DEVICE *dev = dcr->dev;
315 Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->VolCatInfo.VolCatName);
316 foreach_dlist(mdcr, dev->attached_dcrs) {
317 JCR *mjcr = mdcr->jcr;
318 if (mjcr->JobId == 0) {
322 Pmsg1(000, _("Create JobMedia for Job %s\n"), mjcr->Job);
324 if (dev->is_tape()) {
325 mdcr->EndBlock = dcr->EndBlock;
326 mdcr->EndFile = dcr->EndFile;
328 // mdcr->EndBlock = (uint32_t)dcr->file_addr;
329 // mdcr->EndFile = (uint32_t)(dcr->file_addr >> 32);
331 mjcr->read_dcr->VolLastIndex = dcr->VolLastIndex;
332 if (!create_jobmedia_record(db, mjcr)) {
333 Pmsg2(000, _("Could not create JobMedia record for Volume=%s Job=%s\n"),
334 dev->VolCatInfo.VolCatName, mjcr->Job);
337 /* Now let common read routine get up next tape. Note,
338 * we call mount_next... with bscan's jcr because that is where we
339 * have the Volume list, but we get attached.
341 bool stat = mount_next_read_volume(dcr);
346 fstat(dev->fd(), &sb);
347 currentVolumeSize = sb.st_size;
348 Pmsg1(000, _("First Volume Size = %sn"),
349 edit_uint64(currentVolumeSize, ed1));
354 static void do_scan()
358 memset(&ar, 0, sizeof(ar));
359 memset(&pr, 0, sizeof(pr));
360 memset(&jr, 0, sizeof(jr));
361 memset(&cr, 0, sizeof(cr));
362 memset(&fsr, 0, sizeof(fsr));
363 memset(&fr, 0, sizeof(fr));
365 /* Detach bscan's jcr as we are not a real Job on the tape */
367 read_records(bjcr->read_dcr, record_cb, bscan_mount_next_read_volume);
373 * Returns: true if OK
376 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
380 DEVICE *dev = dcr->dev;
381 JCR *bjcr = dcr->jcr;
382 DEV_BLOCK *block = dcr->block;
383 char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
385 if (rec->data_len > 0) {
386 mr.VolBytes += rec->data_len + WRITE_RECHDR_LENGTH; /* Accumulate Volume bytes */
387 if (showProgress && currentVolumeSize > 0) {
388 int pct = (mr.VolBytes * 100) / currentVolumeSize;
389 if (pct != last_pct) {
390 fprintf(stdout, _("done: %d%%\n"), pct);
398 Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
399 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
400 rec->Stream, rec->data_len);
403 * Check for Start or End of Session Record
406 if (rec->FileIndex < 0) {
407 bool save_update_db = update_db;
410 dump_label_record(dev, rec, 1);
412 switch (rec->FileIndex) {
414 Pmsg0(000, _("Volume is prelabeled. This tape cannot be scanned.\n"));
419 unser_volume_label(dev, rec);
420 /* Check Pool info */
421 bstrncpy(pr.Name, dev->VolHdr.PoolName, sizeof(pr.Name));
422 bstrncpy(pr.PoolType, dev->VolHdr.PoolType, sizeof(pr.PoolType));
424 if (db_get_pool_record(bjcr, db, &pr)) {
426 Pmsg1(000, _("Pool record for %s found in DB.\n"), pr.Name);
430 Pmsg1(000, _("VOL_LABEL: Pool record not found for Pool: %s\n"),
433 create_pool_record(db, &pr);
435 if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
436 Pmsg2(000, _("VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n"),
437 pr.PoolType, dev->VolHdr.PoolType);
439 } else if (verbose) {
440 Pmsg1(000, _("Pool type \"%s\" is OK.\n"), pr.PoolType);
443 /* Check Media Info */
444 memset(&mr, 0, sizeof(mr));
445 bstrncpy(mr.VolumeName, dev->VolHdr.VolumeName, sizeof(mr.VolumeName));
446 mr.PoolId = pr.PoolId;
448 if (db_get_media_record(bjcr, db, &mr)) {
450 Pmsg1(000, _("Media record for %s found in DB.\n"), mr.VolumeName);
452 /* Clear out some volume statistics that will be updated */
453 mr.VolJobs = mr.VolFiles = mr.VolBlocks = 0;
454 mr.VolBytes = rec->data_len + 20;
457 Pmsg1(000, _("VOL_LABEL: Media record not found for Volume: %s\n"),
460 bstrncpy(mr.MediaType, dev->VolHdr.MediaType, sizeof(mr.MediaType));
461 create_media_record(db, &mr, &dev->VolHdr);
463 if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
464 Pmsg2(000, _("VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n"),
465 mr.MediaType, dev->VolHdr.MediaType);
466 return true; /* ignore error */
467 } else if (verbose) {
468 Pmsg1(000, _("Media type \"%s\" is OK.\n"), mr.MediaType);
470 /* Reset some DCR variables */
471 foreach_dlist(dcr, dev->attached_dcrs) {
472 dcr->VolFirstIndex = dcr->FileIndex = 0;
473 dcr->StartBlock = dcr->EndBlock = 0;
474 dcr->StartFile = dcr->EndFile = 0;
477 Pmsg1(000, _("VOL_LABEL: OK for Volume: %s\n"), mr.VolumeName);
483 if (ignored_msgs > 0) {
484 Pmsg1(000, _("%d \"errors\" ignored before first Start of Session record.\n"),
488 unser_session_label(&label, rec);
489 memset(&jr, 0, sizeof(jr));
490 bstrncpy(jr.Job, label.Job, sizeof(jr.Job));
491 if (db_get_job_record(bjcr, db, &jr)) {
492 /* Job record already exists in DB */
493 update_db = false; /* don't change db in create_job_record */
495 Pmsg1(000, _("SOS_LABEL: Found Job record for JobId: %d\n"), jr.JobId);
498 /* Must create a Job record in DB */
500 Pmsg1(000, _("SOS_LABEL: Job record not found for JobId: %d\n"),
504 /* Create Client record if not already there */
505 bstrncpy(cr.Name, label.ClientName, sizeof(cr.Name));
506 create_client_record(db, &cr);
507 jr.ClientId = cr.ClientId;
509 /* process label, if Job record exists don't update db */
510 mjcr = create_job_record(db, &jr, &label, rec);
511 dcr = mjcr->read_dcr;
512 update_db = save_update_db;
514 jr.PoolId = pr.PoolId;
516 /* Set start positions into JCR */
517 if (dev->is_tape()) {
519 * Note, we have already advanced past current block,
520 * so the correct number is block_num - 1
522 dcr->StartBlock = dev->block_num - 1;
523 dcr->StartFile = dev->file;
525 dcr->StartBlock = (uint32_t)dev->file_addr;
526 dcr->StartFile = (uint32_t)(dev->file_addr >> 32);
529 mjcr->start_time = jr.StartTime;
530 mjcr->JobLevel = jr.JobLevel;
532 mjcr->client_name = get_pool_memory(PM_FNAME);
533 pm_strcpy(mjcr->client_name, label.ClientName);
534 mjcr->fileset_name = get_pool_memory(PM_FNAME);
535 pm_strcpy(mjcr->fileset_name, label.FileSetName);
536 bstrncpy(dcr->pool_type, label.PoolType, sizeof(dcr->pool_type));
537 bstrncpy(dcr->pool_name, label.PoolName, sizeof(dcr->pool_name));
539 if (rec->VolSessionId != jr.VolSessionId) {
540 Pmsg3(000, _("SOS_LABEL: VolSessId mismatch for JobId=%u. DB=%d Vol=%d\n"),
542 jr.VolSessionId, rec->VolSessionId);
543 return true; /* ignore error */
545 if (rec->VolSessionTime != jr.VolSessionTime) {
546 Pmsg3(000, _("SOS_LABEL: VolSessTime mismatch for JobId=%u. DB=%d Vol=%d\n"),
548 jr.VolSessionTime, rec->VolSessionTime);
549 return true; /* ignore error */
551 if (jr.PoolId != pr.PoolId) {
552 Pmsg3(000, _("SOS_LABEL: PoolId mismatch for JobId=%u. DB=%d Vol=%d\n"),
554 jr.PoolId, pr.PoolId);
555 return true; /* ignore error */
560 unser_session_label(&elabel, rec);
562 /* Create FileSet record */
563 bstrncpy(fsr.FileSet, label.FileSetName, sizeof(fsr.FileSet));
564 bstrncpy(fsr.MD5, label.FileSetMD5, sizeof(fsr.MD5));
565 create_fileset_record(db, &fsr);
566 jr.FileSetId = fsr.FileSetId;
568 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
570 Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
571 rec->VolSessionId, rec->VolSessionTime);
575 /* Do the final update to the Job record */
576 update_job_record(db, &jr, &elabel, rec);
578 mjcr->end_time = jr.EndTime;
579 mjcr->JobStatus = JS_Terminated;
581 /* Create JobMedia record */
582 mjcr->read_dcr->VolLastIndex = dcr->VolLastIndex;
583 create_jobmedia_record(db, mjcr);
584 detach_dcr_from_dev(mjcr->read_dcr);
592 case EOT_LABEL: /* end of all tapes */
594 * Wiffle through all jobs still open and close
599 foreach_dlist(mdcr, dev->attached_dcrs) {
600 JCR *mjcr = mdcr->jcr;
601 if (!mjcr || mjcr->JobId == 0) {
604 jr.JobId = mjcr->JobId;
605 /* Mark Job as Error Terimined */
606 jr.JobStatus = JS_ErrorTerminated;
607 jr.JobFiles = mjcr->JobFiles;
608 jr.JobBytes = mjcr->JobBytes;
609 jr.VolSessionId = mjcr->VolSessionId;
610 jr.VolSessionTime = mjcr->VolSessionTime;
611 jr.JobTDate = (utime_t)mjcr->start_time;
612 jr.ClientId = mjcr->ClientId;
613 if (!db_update_job_end_record(bjcr, db, &jr)) {
614 Pmsg1(0, _("Could not update job record. ERR=%s\n"), db_strerror(db));
616 mjcr->read_dcr = NULL;
620 mr.VolFiles = rec->File;
621 mr.VolBlocks = rec->Block;
622 mr.VolBytes += mr.VolBlocks * WRITE_BLKHDR_LENGTH; /* approx. */
624 update_media_record(db, &mr);
625 Pmsg3(0, _("End of all Volumes. VolFiles=%u VolBlocks=%u VolBytes=%s\n"), mr.VolFiles,
626 mr.VolBlocks, edit_uint64_with_commas(mr.VolBytes, ec1));
634 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
636 if (mr.VolJobs > 0) {
637 Pmsg2(000, _("Could not find Job for SessId=%d SessTime=%d record.\n"),
638 rec->VolSessionId, rec->VolSessionTime);
644 dcr = mjcr->read_dcr;
645 if (dcr->VolFirstIndex == 0) {
646 dcr->VolFirstIndex = block->FirstIndex;
649 /* File Attributes stream */
650 switch (rec->Stream) {
651 case STREAM_UNIX_ATTRIBUTES:
652 case STREAM_UNIX_ATTRIBUTES_EX:
654 if (!unpack_attributes_record(bjcr, rec->Stream, rec->data, attr)) {
655 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
658 if (attr->file_index != rec->FileIndex) {
659 Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
660 rec->FileIndex, attr->file_index);
664 decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
665 build_attr_output_fnames(bjcr, attr);
666 print_ls_output(bjcr, attr);
668 fr.JobId = mjcr->JobId;
671 if (verbose && (num_files & 0x7FFF) == 0) {
672 char ed1[30], ed2[30], ed3[30], ed4[30];
673 Pmsg4(000, _("%s file records. At file:blk=%s:%s bytes=%s\n"),
674 edit_uint64_with_commas(num_files, ed1),
675 edit_uint64_with_commas(rec->File, ed2),
676 edit_uint64_with_commas(rec->Block, ed3),
677 edit_uint64_with_commas(mr.VolBytes, ed4));
679 create_file_attributes_record(db, mjcr, attr->fname, attr->lname,
680 attr->type, attr->attr, rec);
685 case STREAM_WIN32_DATA:
686 case STREAM_FILE_DATA:
687 case STREAM_SPARSE_DATA:
688 case STREAM_ENCRYPTED_FILE_DATA:
689 case STREAM_ENCRYPTED_WIN32_DATA:
690 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
692 * For encrypted stream, this is an approximation.
693 * The data must be decrypted to know the correct length.
695 mjcr->JobBytes += rec->data_len;
696 if (rec->Stream == STREAM_SPARSE_DATA) {
697 mjcr->JobBytes -= sizeof(uint64_t);
700 free_jcr(mjcr); /* done using JCR */
703 case STREAM_GZIP_DATA:
704 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
705 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
706 /* No correct, we should (decrypt and) expand it
709 mjcr->JobBytes += rec->data_len;
713 case STREAM_SPARSE_GZIP_DATA:
714 mjcr->JobBytes += rec->data_len - sizeof(uint64_t); /* No correct, we should expand it */
715 free_jcr(mjcr); /* done using JCR */
718 /* Win32 GZIP stream */
719 case STREAM_WIN32_GZIP_DATA:
720 mjcr->JobBytes += rec->data_len;
721 free_jcr(mjcr); /* done using JCR */
724 case STREAM_MD5_DIGEST:
725 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_MD5_SIZE, true);
727 Pmsg1(000, _("Got MD5 record: %s\n"), digest);
729 update_digest_record(db, digest, rec, CRYPTO_DIGEST_MD5);
732 case STREAM_SHA1_DIGEST:
733 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA1_SIZE, true);
735 Pmsg1(000, _("Got SHA1 record: %s\n"), digest);
737 update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA1);
740 case STREAM_SHA256_DIGEST:
741 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA256_SIZE, true);
743 Pmsg1(000, _("Got SHA256 record: %s\n"), digest);
745 update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA256);
748 case STREAM_SHA512_DIGEST:
749 bin_to_base64(digest, sizeof(digest), (char *)rec->data, CRYPTO_DIGEST_SHA512_SIZE, true);
751 Pmsg1(000, _("Got SHA512 record: %s\n"), digest);
753 update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA512);
756 case STREAM_ENCRYPTED_SESSION_DATA:
757 // TODO landonf: Investigate crypto support in bscan
759 Pmsg0(000, _("Got signed digest record\n"));
763 case STREAM_SIGNED_DIGEST:
764 // TODO landonf: Investigate crypto support in bscan
766 Pmsg0(000, _("Got signed digest record\n"));
770 case STREAM_PROGRAM_NAMES:
772 Pmsg1(000, _("Got Prog Names Stream: %s\n"), rec->data);
776 case STREAM_PROGRAM_DATA:
778 Pmsg0(000, _("Got Prog Data Stream record.\n"));
782 case STREAM_UNIX_ATTRIBUTES_ACCESS_ACL: /* Standard ACL attributes on UNIX */
783 case STREAM_UNIX_ATTRIBUTES_DEFAULT_ACL: /* Default ACL attributes on UNIX */
784 /* Ignore Unix attributes */
788 Pmsg2(0, _("Unknown stream type!!! stream=%d len=%i\n"), rec->Stream, rec->data_len);
795 * Free the Job Control Record if no one is still using it.
796 * Called from main free_jcr() routine in src/lib/jcr.c so
797 * that we can do our Director specific cleanup of the jcr.
799 static void bscan_free_jcr(JCR *jcr)
801 Dmsg0(200, "Start bscan free_jcr\n");
803 if (jcr->file_bsock) {
804 Dmsg0(200, "Close File bsock\n");
805 bnet_close(jcr->file_bsock);
807 if (jcr->store_bsock) {
808 Dmsg0(200, "Close Store bsock\n");
809 bnet_close(jcr->store_bsock);
811 if (jcr->RestoreBootstrap) {
812 free(jcr->RestoreBootstrap);
819 free_dcr(jcr->read_dcr);
820 jcr->read_dcr = NULL;
822 Dmsg0(200, "End bscan free_jcr\n");
826 * We got a File Attributes record on the tape. Now, lookup the Job
827 * record, and then create the attributes record.
829 static int create_file_attributes_record(B_DB *db, JCR *mjcr,
830 char *fname, char *lname, int type,
831 char *ap, DEV_RECORD *rec)
833 DCR *dcr = mjcr->read_dcr;
836 ar.ClientId = mjcr->ClientId;
837 ar.JobId = mjcr->JobId;
838 ar.Stream = rec->Stream;
839 ar.FileIndex = rec->FileIndex;
841 if (dcr->VolFirstIndex == 0) {
842 dcr->VolFirstIndex = rec->FileIndex;
844 dcr->FileIndex = rec->FileIndex;
851 if (!db_create_file_attributes_record(bjcr, db, &ar)) {
852 Pmsg1(0, _("Could not create File Attributes record. ERR=%s\n"), db_strerror(db));
855 mjcr->FileId = ar.FileId;
858 Pmsg1(000, _("Created File record: %s\n"), fname);
864 * For each Volume we see, we create a Medium record
866 static int create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl)
871 /* We mark Vols as Archive to keep them from being re-written */
872 bstrncpy(mr->VolStatus, "Archive", sizeof(mr->VolStatus));
873 mr->VolRetention = 365 * 3600 * 24; /* 1 year */
875 if (vl->VerNum >= 11) {
876 mr->FirstWritten = btime_to_utime(vl->write_btime);
877 mr->LabelDate = btime_to_utime(vl->label_btime);
879 /* DEPRECATED DO NOT USE */
880 dt.julian_day_number = vl->write_date;
881 dt.julian_day_fraction = vl->write_time;
883 mr->FirstWritten = mktime(&tm);
884 dt.julian_day_number = vl->label_date;
885 dt.julian_day_fraction = vl->label_time;
887 mr->LabelDate = mktime(&tm);
889 lasttime = mr->LabelDate;
895 if (!db_create_media_record(bjcr, db, mr)) {
896 Pmsg1(0, _("Could not create media record. ERR=%s\n"), db_strerror(db));
899 if (!db_update_media_record(bjcr, db, mr)) {
900 Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
904 Pmsg1(000, _("Created Media record for Volume: %s\n"), mr->VolumeName);
911 * Called at end of media to update it
913 static bool update_media_record(B_DB *db, MEDIA_DBR *mr)
915 if (!update_db && !update_vol_info) {
919 mr->LastWritten = lasttime;
920 if (!db_update_media_record(bjcr, db, mr)) {
921 Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
925 Pmsg1(000, _("Updated Media record at end of Volume: %s\n"), mr->VolumeName);
932 static int create_pool_record(B_DB *db, POOL_DBR *pr)
936 pr->VolRetention = 355 * 3600 * 24; /* 1 year */
941 if (!db_create_pool_record(bjcr, db, pr)) {
942 Pmsg1(0, _("Could not create pool record. ERR=%s\n"), db_strerror(db));
946 Pmsg1(000, _("Created Pool record for Pool: %s\n"), pr->Name);
954 * Called from SOS to create a client for the current Job
956 static int create_client_record(B_DB *db, CLIENT_DBR *cr)
961 if (!db_create_client_record(bjcr, db, cr)) {
962 Pmsg1(0, _("Could not create Client record. ERR=%s\n"), db_strerror(db));
966 Pmsg1(000, _("Created Client record for Client: %s\n"), cr->Name);
971 static int create_fileset_record(B_DB *db, FILESET_DBR *fsr)
977 if (fsr->MD5[0] == 0) {
978 fsr->MD5[0] = ' '; /* Equivalent to nothing */
981 if (db_get_fileset_record(bjcr, db, fsr)) {
983 Pmsg1(000, _("Fileset \"%s\" already exists.\n"), fsr->FileSet);
986 if (!db_create_fileset_record(bjcr, db, fsr)) {
987 Pmsg2(0, _("Could not create FileSet record \"%s\". ERR=%s\n"),
988 fsr->FileSet, db_strerror(db));
992 Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet);
999 * Simulate the two calls on the database to create
1000 * the Job record and to update it when the Job actually
1003 static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label,
1007 struct date_time dt;
1010 jr->JobId = label->JobId;
1011 jr->JobType = label->JobType;
1012 jr->JobLevel = label->JobLevel;
1013 jr->JobStatus = JS_Created;
1014 bstrncpy(jr->Name, label->JobName, sizeof(jr->Name));
1015 bstrncpy(jr->Job, label->Job, sizeof(jr->Job));
1016 if (label->VerNum >= 11) {
1017 jr->SchedTime = btime_to_unix(label->write_btime);
1019 dt.julian_day_number = label->write_date;
1020 dt.julian_day_fraction = label->write_time;
1021 tm_decode(&dt, &tm);
1022 jr->SchedTime = mktime(&tm);
1025 jr->StartTime = jr->SchedTime;
1026 jr->JobTDate = (utime_t)jr->SchedTime;
1027 jr->VolSessionId = rec->VolSessionId;
1028 jr->VolSessionTime = rec->VolSessionTime;
1030 /* Now create a JCR as if starting the Job */
1031 mjcr = create_jcr(jr, rec, label->JobId);
1037 /* This creates the bare essentials */
1038 if (!db_create_job_record(bjcr, db, jr)) {
1039 Pmsg1(0, _("Could not create JobId record. ERR=%s\n"), db_strerror(db));
1043 /* This adds the client, StartTime, JobTDate, ... */
1044 if (!db_update_job_start_record(bjcr, db, jr)) {
1045 Pmsg1(0, _("Could not update job start record. ERR=%s\n"), db_strerror(db));
1048 Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"), jr->JobId,
1050 mjcr->JobId = jr->JobId; /* set new JobId */
1055 * Simulate the database call that updates the Job
1056 * at Job termination time.
1058 static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
1061 struct date_time dt;
1065 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1067 Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
1068 rec->VolSessionId, rec->VolSessionTime);
1071 if (elabel->VerNum >= 11) {
1072 jr->EndTime = btime_to_unix(elabel->write_btime);
1074 dt.julian_day_number = elabel->write_date;
1075 dt.julian_day_fraction = elabel->write_time;
1076 tm_decode(&dt, &tm);
1077 jr->EndTime = mktime(&tm);
1079 lasttime = jr->EndTime;
1080 mjcr->end_time = jr->EndTime;
1082 jr->JobId = mjcr->JobId;
1083 jr->JobStatus = elabel->JobStatus;
1084 mjcr->JobStatus = elabel->JobStatus;
1085 jr->JobFiles = elabel->JobFiles;
1086 jr->JobBytes = elabel->JobBytes;
1087 jr->VolSessionId = rec->VolSessionId;
1088 jr->VolSessionTime = rec->VolSessionTime;
1089 jr->JobTDate = (utime_t)mjcr->start_time;
1090 jr->ClientId = mjcr->ClientId;
1097 if (!db_update_job_end_record(bjcr, db, jr)) {
1098 Pmsg2(0, _("Could not update JobId=%u record. ERR=%s\n"), jr->JobId, db_strerror(db));
1103 Pmsg3(000, _("Updated Job termination record for JobId=%u Level=%s TermStat=%c\n"),
1104 jr->JobId, job_level_to_str(mjcr->JobLevel), jr->JobStatus);
1107 const char *term_msg;
1108 static char term_code[70];
1109 char sdt[50], edt[50];
1110 char ec1[30], ec2[30], ec3[30];
1112 switch (mjcr->JobStatus) {
1114 term_msg = _("Backup OK");
1117 case JS_ErrorTerminated:
1118 term_msg = _("*** Backup Error ***");
1121 term_msg = _("Backup Canceled");
1124 term_msg = term_code;
1125 sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus);
1128 bstrftime(sdt, sizeof(sdt), mjcr->start_time);
1129 bstrftime(edt, sizeof(edt), mjcr->end_time);
1130 Pmsg14(000, _("%s\n"
1134 "Backup Level: %s\n"
1138 "Files Written: %s\n"
1139 "Bytes Written: %s\n"
1140 "Volume Session Id: %d\n"
1141 "Volume Session Time: %d\n"
1142 "Last Volume Bytes: %s\n"
1143 "Termination: %s\n\n"),
1148 job_level_to_str(mjcr->JobLevel),
1152 edit_uint64_with_commas(mjcr->JobFiles, ec1),
1153 edit_uint64_with_commas(mjcr->JobBytes, ec2),
1155 mjcr->VolSessionTime,
1156 edit_uint64_with_commas(mr.VolBytes, ec3),
1163 static int create_jobmedia_record(B_DB *db, JCR *mjcr)
1166 DCR *dcr = mjcr->read_dcr;
1168 if (dev->is_tape()) {
1169 dcr->EndBlock = dev->EndBlock;
1170 dcr->EndFile = dev->EndFile;
1173 dcr->EndBlock = (uint32_t)dev->file_addr;
1174 dcr->EndFile = (uint32_t)(dev->file_addr >> 32);
1178 memset(&jmr, 0, sizeof(jmr));
1179 jmr.JobId = mjcr->JobId;
1180 jmr.MediaId = mr.MediaId;
1181 jmr.FirstIndex = dcr->VolFirstIndex;
1182 jmr.LastIndex = dcr->VolLastIndex;
1183 jmr.StartFile = dcr->StartFile;
1184 jmr.EndFile = dcr->EndFile;
1185 jmr.StartBlock = dcr->StartBlock;
1186 jmr.EndBlock = dcr->EndBlock;
1193 if (!db_create_jobmedia_record(bjcr, db, &jmr)) {
1194 Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db_strerror(db));
1198 Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"),
1199 jmr.JobId, jmr.MediaId);
1205 * Simulate the database call that updates the MD5/SHA1 record
1207 static int update_digest_record(B_DB *db, char *digest, DEV_RECORD *rec, int type)
1211 mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1213 if (mr.VolJobs > 0) {
1214 Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"),
1215 rec->VolSessionId, rec->VolSessionTime);
1222 if (!update_db || mjcr->FileId == 0) {
1227 if (!db_add_digest_to_file_record(bjcr, db, mjcr->FileId, digest, type)) {
1228 Pmsg1(0, _("Could not add MD5/SHA1 to File record. ERR=%s\n"), db_strerror(db));
1233 Pmsg0(000, _("Updated MD5/SHA1 record\n"));
1241 * Create a JCR as if we are really starting the job
1243 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
1247 * Transfer as much as possible to the Job JCR. Most important is
1248 * the JobId and the ClientId.
1250 jobjcr = new_jcr(sizeof(JCR), bscan_free_jcr);
1251 jobjcr->JobType = jr->JobType;
1252 jobjcr->JobLevel = jr->JobLevel;
1253 jobjcr->JobStatus = jr->JobStatus;
1254 bstrncpy(jobjcr->Job, jr->Job, sizeof(jobjcr->Job));
1255 jobjcr->JobId = JobId; /* this is JobId on tape */
1256 jobjcr->sched_time = jr->SchedTime;
1257 jobjcr->start_time = jr->StartTime;
1258 jobjcr->VolSessionId = rec->VolSessionId;
1259 jobjcr->VolSessionTime = rec->VolSessionTime;
1260 jobjcr->ClientId = jr->ClientId;
1261 jobjcr->read_dcr = new_dcr(jobjcr, dev);
1266 /* Dummies to replace askdir.c */
1267 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
1268 bool dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
1269 bool dir_create_jobmedia_record(DCR *dcr) { return 1; }
1270 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
1271 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
1272 bool dir_send_job_status(JCR *jcr) {return 1;}
1273 int generate_job_event(JCR *jcr, const char *event) { return 1; }
1275 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
1277 DEVICE *dev = dcr->dev;
1278 Dmsg0(20, "Enter dir_ask_sysop_to_mount_volume\n");
1279 /* Close device so user can use autochanger if desired */
1280 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
1281 dcr->VolumeName, dev->print_name());
1287 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
1289 Dmsg0(100, "Fake dir_get_volume_info\n");
1290 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
1291 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
1292 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);