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