]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bscan.c
- Correct typo in Copyright
[bacula/bacula] / bacula / src / stored / bscan.c
1 /*
2  *
3  *  Program to scan a Bacula Volume and compare it with
4  *    the catalog and optionally synchronize the catalog
5  *    with the tape.
6  *
7  *   Kern E. Sibbald, December 2001
8  *
9  *
10  *   Version $Id$
11  */
12 /*
13    Copyright (C) 2001-2005 Kern Sibbald
14
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.
19
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.
24
25  */
26
27 #include "bacula.h"
28 #include "stored.h"
29 #include "findlib/find.h"
30 #include "cats/cats.h"
31  
32 /* Dummy functions */
33 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
34
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,
46                               DEV_RECORD *rec);
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_SIG_record(B_DB *db, char *SIGbuf, DEV_RECORD *rec, int type);
52
53
54 /* Global variables */
55 #if defined(HAVE_CYGWIN) || defined(HAVE_WIN32)
56 int win32_client = 1;
57 #else
58 int win32_client = 0;
59 #endif
60
61
62 /* Local variables */
63 static DEVICE *dev = NULL;
64 static B_DB *db;
65 static JCR *bjcr;                     /* jcr for bscan */
66 static BSR *bsr = NULL;
67 static MEDIA_DBR mr;
68 static POOL_DBR pr;
69 static JOB_DBR jr;
70 static CLIENT_DBR cr;
71 static FILESET_DBR fsr;
72 static ATTR_DBR ar;
73 static FILE_DBR fr;
74 static SESSION_LABEL label;
75 static SESSION_LABEL elabel;
76 static ATTR *attr;
77
78 static time_t lasttime = 0;
79
80 static const char *db_name = "bacula";
81 static const char *db_user = "bacula";
82 static const char *db_password = "";
83 static const char *db_host = NULL;
84 static const char *wd = NULL;
85 static bool update_db = false;
86 static bool update_vol_info = false;
87 static bool list_records = false;
88 static int ignored_msgs = 0;
89
90 static uint64_t currentVolumeSize;
91 static int last_pct = -1;
92 static bool showProgress = false;
93 static int num_jobs = 0;
94 static int num_pools = 0;
95 static int num_media = 0;
96 static int num_files = 0;
97
98 #define CONFIG_FILE "bacula-sd.conf"
99 char *configfile = NULL;
100 STORES *me = NULL;                    /* our Global resource */
101 bool forge_on = false;                /* proceed inspite of I/O errors */
102 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
103 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
104
105
106 static void usage()
107 {
108    fprintf(stderr, _(
109 "Copyright (C) 2001-2005 Kern Sibbald.\n"
110 "\nVersion: " VERSION " (" BDATE ")\n\n"
111 "Usage: bscan [ options ] <bacula-archive>\n"
112 "       -b bootstrap      specify a bootstrap file\n"
113 "       -c <file>         specify configuration file\n"
114 "       -d <nn>           set debug level to nn\n"
115 "       -m                update media info in database\n"
116 "       -n <name>         specify the database name (default bacula)\n"
117 "       -u <user>         specify database user name (default bacula)\n"
118 "       -P <password      specify database password (default none)\n"
119 "       -h <host>         specify database host (default NULL)\n"
120 "       -p                proceed inspite of I/O errors\n"
121 "       -r                list records\n"
122 "       -s                synchronize or store in database\n"
123 "       -S                show scan progress periodically\n"
124 "       -v                verbose\n"
125 "       -V <Volumes>      specify Volume names (separated by |)\n"
126 "       -w <dir>          specify working directory (default from conf file)\n"
127 "       -?                print this message\n\n"));
128    exit(1);
129 }
130
131 int main (int argc, char *argv[])
132 {
133    int ch;
134    struct stat stat_buf;
135    char *VolumeName = NULL;
136
137    my_name_is(argc, argv, "bscan");
138    init_msg(NULL, NULL);
139
140
141    while ((ch = getopt(argc, argv, "b:c:d:h:mn:pP:rsSu:vV:w:?")) != -1) {
142       switch (ch) {
143       case 'S' :
144          showProgress = true;
145          break;
146       case 'b':
147          bsr = parse_bsr(NULL, optarg);
148          break;
149
150       case 'c':                    /* specify config file */
151          if (configfile != NULL) {
152             free(configfile);
153          }
154          configfile = bstrdup(optarg);
155          break;
156
157       case 'd':                    /* debug level */
158          debug_level = atoi(optarg);
159          if (debug_level <= 0)
160             debug_level = 1;
161          break;
162
163       case 'h':
164          db_host = optarg;
165          break;
166
167       case 'm':
168          update_vol_info = true;
169          break;
170
171       case 'n':
172          db_name = optarg;
173          break;
174
175       case 'u':
176          db_user = optarg;
177          break;
178
179       case 'P':
180          db_password = optarg;
181          break;
182
183       case 'p':
184          forge_on = true;
185          break;
186
187       case 'r':
188          list_records = true;
189          break;
190
191       case 's':
192          update_db = true;
193          break;
194
195       case 'v':
196          verbose++;
197          break;
198
199       case 'V':                    /* Volume name */
200          VolumeName = optarg;
201          break;
202
203       case 'w':
204          wd = optarg;
205          break;
206
207       case '?':
208       default:
209          usage();
210
211       }
212    }
213    argc -= optind;
214    argv += optind;
215
216    if (argc != 1) {
217       Pmsg0(0, _("Wrong number of arguments: \n"));
218       usage();
219    }
220
221    if (configfile == NULL) {
222       configfile = bstrdup(CONFIG_FILE);
223    }
224
225    parse_config(configfile);
226    LockRes();
227    me = (STORES *)GetNextRes(R_STORAGE, NULL);
228    if (!me) {
229       UnlockRes();
230       Emsg1(M_ERROR_TERM, 0, _("No Storage resource defined in %s. Cannot continue.\n"),
231          configfile);
232    }
233    UnlockRes();
234    /* Check if -w option given, otherwise use resource for working directory */
235    if (wd) {
236       working_directory = wd;
237    } else if (!me->working_directory) {
238       Emsg1(M_ERROR_TERM, 0, _("No Working Directory defined in %s. Cannot continue.\n"),
239          configfile);
240    } else {
241       working_directory = me->working_directory;
242    }
243
244    /* Check that working directory is good */
245    if (stat(working_directory, &stat_buf) != 0) {
246       Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s not found. Cannot continue.\n"),
247          working_directory);
248    }
249    if (!S_ISDIR(stat_buf.st_mode)) {
250       Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s is not a directory. Cannot continue.\n"),
251          working_directory);
252    }
253
254    bjcr = setup_jcr("bscan", argv[0], bsr, VolumeName, 1); /* read device */
255    if (!bjcr) {
256       exit(1);
257    }
258    dev = bjcr->dcr->dev;
259    if (showProgress) {
260       char ed1[50];
261       struct stat sb;
262       fstat(dev->fd, &sb);
263       currentVolumeSize = sb.st_size;
264       Pmsg1(000, _("First Volume Size = %sn"), 
265          edit_uint64(currentVolumeSize, ed1));
266    }
267
268    if ((db=db_init_database(NULL, db_name, db_user, db_password,
269         db_host, 0, NULL, 0)) == NULL) {
270       Emsg0(M_ERROR_TERM, 0, _("Could not init Bacula database\n"));
271    }
272    if (!db_open_database(NULL, db)) {
273       Emsg0(M_ERROR_TERM, 0, db_strerror(db));
274    }
275    Dmsg0(200, "Database opened\n");
276    if (verbose) {
277       Pmsg2(000, _("Using Database: %s, User: %s\n"), db_name, db_user);
278    }
279
280    do_scan();
281    printf("Records %sadded or updated in the catalog:\n%7d Media\n%7d Pool\n%7d Job\n%7d File\n",
282       update_db?"":"would have been ",
283       num_media, num_pools, num_jobs, num_files);
284
285    free_jcr(bjcr);
286    term_dev(dev);
287    return 0;
288 }
289
290 /*
291  * We are at the end of reading a tape. Now, we simulate handling
292  *   the end of writing a tape by wiffling through the attached
293  *   jcrs creating jobmedia records.
294  */
295 static bool bscan_mount_next_read_volume(DCR *dcr)
296 {
297    DEVICE *dev = dcr->dev;
298    DCR *mdcr;
299    Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->VolCatInfo.VolCatName);
300    foreach_dlist(mdcr, dev->attached_dcrs) {
301       JCR *mjcr = mdcr->jcr;
302       if (mjcr->JobId == 0) {
303          continue;
304       }
305       if (verbose) {
306          Pmsg1(000, _("Create JobMedia for Job %s\n"), mjcr->Job);
307       }
308       if (dev->is_tape()) {
309          mdcr->EndBlock = dcr->EndBlock;
310          mdcr->EndFile = dcr->EndFile;
311 //    } else {
312 //       mdcr->EndBlock = (uint32_t)dcr->file_addr;
313 //       mdcr->EndFile = (uint32_t)(dcr->file_addr >> 32);
314       }
315       if (!create_jobmedia_record(db, mjcr)) {
316          Pmsg2(000, _("Could not create JobMedia record for Volume=%s Job=%s\n"),
317             dev->VolCatInfo.VolCatName, mjcr->Job);
318       }
319    }
320    /* Now let common read routine get up next tape. Note,
321     * we call mount_next... with bscan's jcr because that is where we
322     * have the Volume list, but we get attached.
323     */
324    bool stat = mount_next_read_volume(dcr);
325
326    if (showProgress) {
327       char ed1[50];
328       struct stat sb;
329       fstat(dev->fd, &sb);
330       currentVolumeSize = sb.st_size;
331       Pmsg1(000, _("First Volume Size = %sn"), 
332          edit_uint64(currentVolumeSize, ed1));
333    }
334    return stat;
335 }
336
337 static void do_scan()
338 {
339    attr = new_attr();
340
341    memset(&ar, 0, sizeof(ar));
342    memset(&pr, 0, sizeof(pr));
343    memset(&jr, 0, sizeof(jr));
344    memset(&cr, 0, sizeof(cr));
345    memset(&fsr, 0, sizeof(fsr));
346    memset(&fr, 0, sizeof(fr));
347
348    /* Detach bscan's jcr as we are not a real Job on the tape */
349
350    read_records(bjcr->dcr, record_cb, bscan_mount_next_read_volume);
351
352    free_attr(attr);
353 }
354
355 /*
356  * Returns: true  if OK
357  *          false if error
358  */
359 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
360 {
361    JCR *mjcr;
362    char ec1[30];
363    DEVICE *dev = dcr->dev;
364    JCR *bjcr = dcr->jcr;
365    DEV_BLOCK *block = dcr->block;
366
367    if (rec->data_len > 0) {
368       mr.VolBytes += rec->data_len + WRITE_RECHDR_LENGTH; /* Accumulate Volume bytes */
369       if (showProgress) {
370          int pct = (mr.VolBytes * 100) / currentVolumeSize;
371          if (pct != last_pct) {
372             fprintf(stdout, "done: %d%%\n", pct);
373             fflush(stdout);
374             last_pct = pct;
375          }
376       }
377    }
378
379    if (list_records) {
380       Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
381             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
382             rec->Stream, rec->data_len);
383    }
384    /*
385     * Check for Start or End of Session Record
386     *
387     */
388    if (rec->FileIndex < 0) {
389       bool save_update_db = update_db;
390
391       if (verbose > 1) {
392          dump_label_record(dev, rec, 1);
393       }
394       switch (rec->FileIndex) {
395       case PRE_LABEL:
396          Pmsg0(000, _("Volume is prelabeled. This tape cannot be scanned.\n"));
397          return false;
398          break;
399
400       case VOL_LABEL:
401          unser_volume_label(dev, rec);
402          /* Check Pool info */
403          bstrncpy(pr.Name, dev->VolHdr.PoolName, sizeof(pr.Name));
404          bstrncpy(pr.PoolType, dev->VolHdr.PoolType, sizeof(pr.PoolType));
405          num_pools++;
406          if (db_get_pool_record(bjcr, db, &pr)) {
407             if (verbose) {
408                Pmsg1(000, _("Pool record for %s found in DB.\n"), pr.Name);
409             }
410          } else {
411             if (!update_db) {
412                Pmsg1(000, _("VOL_LABEL: Pool record not found for Pool: %s\n"),
413                   pr.Name);
414             }
415             create_pool_record(db, &pr);
416          }
417          if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
418             Pmsg2(000, _("VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n"),
419                pr.PoolType, dev->VolHdr.PoolType);
420             return true;
421          } else if (verbose) {
422             Pmsg1(000, _("Pool type \"%s\" is OK.\n"), pr.PoolType);
423          }
424
425          /* Check Media Info */
426          memset(&mr, 0, sizeof(mr));
427          bstrncpy(mr.VolumeName, dev->VolHdr.VolumeName, sizeof(mr.VolumeName));
428          mr.PoolId = pr.PoolId;
429          num_media++;
430          if (db_get_media_record(bjcr, db, &mr)) {
431             if (verbose) {
432                Pmsg1(000, _("Media record for %s found in DB.\n"), mr.VolumeName);
433             }
434             /* Clear out some volume statistics that will be updated */
435             mr.VolJobs = mr.VolFiles = mr.VolBlocks = 0;
436             mr.VolBytes = rec->data_len + 20;
437          } else {
438             if (!update_db) {
439                Pmsg1(000, _("VOL_LABEL: Media record not found for Volume: %s\n"),
440                   mr.VolumeName);
441             }
442             bstrncpy(mr.MediaType, dev->VolHdr.MediaType, sizeof(mr.MediaType));
443             create_media_record(db, &mr, &dev->VolHdr);
444          }
445          if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
446             Pmsg2(000, _("VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n"),
447                mr.MediaType, dev->VolHdr.MediaType);
448             return true;              /* ignore error */
449          } else if (verbose) {
450             Pmsg1(000, _("Media type \"%s\" is OK.\n"), mr.MediaType);
451          }
452          /* Reset some DCR variables */
453          foreach_dlist(dcr, dev->attached_dcrs) {
454             dcr->VolFirstIndex = dcr->FileIndex = 0;
455             dcr->StartBlock = dcr->EndBlock = 0;
456             dcr->StartFile = dcr->EndFile = 0;
457          }
458
459          Pmsg1(000, _("VOL_LABEL: OK for Volume: %s\n"), mr.VolumeName);
460          break;
461
462       case SOS_LABEL:
463          mr.VolJobs++;
464          num_jobs++;
465          if (ignored_msgs > 0) {
466             Pmsg1(000, _("%d \"errors\" ignored before first Start of Session record.\n"),
467                   ignored_msgs);
468             ignored_msgs = 0;
469          }
470          unser_session_label(&label, rec);
471          memset(&jr, 0, sizeof(jr));
472          bstrncpy(jr.Job, label.Job, sizeof(jr.Job));
473          if (db_get_job_record(bjcr, db, &jr)) {
474             /* Job record already exists in DB */
475             update_db = false;  /* don't change db in create_job_record */
476             if (verbose) {
477                Pmsg1(000, _("SOS_LABEL: Found Job record for JobId: %d\n"), jr.JobId);
478             }
479          } else {
480             /* Must create a Job record in DB */
481             if (!update_db) {
482                Pmsg1(000, _("SOS_LABEL: Job record not found for JobId: %d\n"),
483                   jr.JobId);
484             }
485          }
486          /* Create Client record if not already there */
487             bstrncpy(cr.Name, label.ClientName, sizeof(cr.Name));
488             create_client_record(db, &cr);
489             jr.ClientId = cr.ClientId;
490
491          /* process label, if Job record exists don't update db */
492          mjcr = create_job_record(db, &jr, &label, rec);
493          dcr = mjcr->dcr;
494          update_db = save_update_db;
495
496          jr.PoolId = pr.PoolId;
497          /* Set start positions into JCR */
498          if (dev->is_tape()) {
499             /*
500              * Note, we have already advanced past current block,
501              *  so the correct number is block_num - 1
502              */
503             dcr->StartBlock = dev->block_num - 1;
504             dcr->StartFile = dev->file;
505          } else {
506             dcr->StartBlock = (uint32_t)dev->file_addr;
507             dcr->StartFile = (uint32_t)(dev->file_addr >> 32);
508          }
509          mjcr->start_time = jr.StartTime;
510          mjcr->JobLevel = jr.JobLevel;
511
512          mjcr->client_name = get_pool_memory(PM_FNAME);
513          pm_strcpy(mjcr->client_name, label.ClientName);
514          mjcr->fileset_name = get_pool_memory(PM_FNAME);
515          pm_strcpy(mjcr->fileset_name, label.FileSetName);
516          bstrncpy(dcr->pool_type, label.PoolType, sizeof(dcr->pool_type));
517          bstrncpy(dcr->pool_name, label.PoolName, sizeof(dcr->pool_name));
518
519          if (rec->VolSessionId != jr.VolSessionId) {
520             Pmsg3(000, _("SOS_LABEL: VolSessId mismatch for JobId=%u. DB=%d Vol=%d\n"),
521                jr.JobId,
522                jr.VolSessionId, rec->VolSessionId);
523             return true;              /* ignore error */
524          }
525          if (rec->VolSessionTime != jr.VolSessionTime) {
526             Pmsg3(000, _("SOS_LABEL: VolSessTime mismatch for JobId=%u. DB=%d Vol=%d\n"),
527                jr.JobId,
528                jr.VolSessionTime, rec->VolSessionTime);
529             return true;              /* ignore error */
530          }
531          if (jr.PoolId != pr.PoolId) {
532             Pmsg3(000, _("SOS_LABEL: PoolId mismatch for JobId=%u. DB=%d Vol=%d\n"),
533                jr.JobId,
534                jr.PoolId, pr.PoolId);
535             return true;              /* ignore error */
536          }
537          break;
538
539       case EOS_LABEL:
540          unser_session_label(&elabel, rec);
541
542          /* Create FileSet record */
543          bstrncpy(fsr.FileSet, label.FileSetName, sizeof(fsr.FileSet));
544          bstrncpy(fsr.MD5, label.FileSetMD5, sizeof(fsr.MD5));
545          create_fileset_record(db, &fsr);
546          jr.FileSetId = fsr.FileSetId;
547
548          mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
549          if (!mjcr) {
550             Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
551                   rec->VolSessionId, rec->VolSessionTime);
552             break;
553          }
554
555          /* Do the final update to the Job record */
556          update_job_record(db, &jr, &elabel, rec);
557
558          mjcr->end_time = jr.EndTime;
559          mjcr->JobStatus = JS_Terminated;
560
561          /* Create JobMedia record */
562          create_jobmedia_record(db, mjcr);
563          dev->attached_dcrs->remove(mjcr->dcr);
564          free_jcr(mjcr);
565
566          break;
567
568       case EOM_LABEL:
569          break;
570
571       case EOT_LABEL:              /* end of all tapes */
572          /*
573           * Wiffle through all jobs still open and close
574           *   them.
575           */
576          if (update_db) {
577             DCR *mdcr;
578             foreach_dlist(mdcr, dev->attached_dcrs) {
579                JCR *mjcr = mdcr->jcr;
580                if (!mjcr || mjcr->JobId == 0) {
581                   continue;
582                }
583                jr.JobId = mjcr->JobId;
584                /* Mark Job as Error Terimined */
585                jr.JobStatus = JS_ErrorTerminated;
586                jr.JobFiles = mjcr->JobFiles;
587                jr.JobBytes = mjcr->JobBytes;
588                jr.VolSessionId = mjcr->VolSessionId;
589                jr.VolSessionTime = mjcr->VolSessionTime;
590                jr.JobTDate = (utime_t)mjcr->start_time;
591                jr.ClientId = mjcr->ClientId;
592                if (!db_update_job_end_record(bjcr, db, &jr)) {
593                   Pmsg1(0, _("Could not update job record. ERR=%s\n"), db_strerror(db));
594                }
595                mjcr->dcr = NULL;
596                free_jcr(mjcr);
597             }
598          }
599          mr.VolFiles = rec->File;
600          mr.VolBlocks = rec->Block;
601          mr.VolBytes += mr.VolBlocks * WRITE_BLKHDR_LENGTH; /* approx. */
602          mr.VolMounts++;
603          update_media_record(db, &mr);
604          Pmsg3(0, _("End of all Volumes. VolFiles=%u VolBlocks=%u VolBytes=%s\n"), mr.VolFiles,
605                     mr.VolBlocks, edit_uint64_with_commas(mr.VolBytes, ec1));
606          break;
607       default:
608          break;
609       } /* end switch */
610       return true;
611    }
612
613    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
614    if (!mjcr) {
615       if (mr.VolJobs > 0) {
616          Pmsg2(000, _("Could not find Job for SessId=%d SessTime=%d record.\n"),
617                       rec->VolSessionId, rec->VolSessionTime);
618       } else {
619          ignored_msgs++;
620       }
621       return true;
622    }
623    dcr = mjcr->dcr;
624    if (dcr->VolFirstIndex == 0) {
625       dcr->VolFirstIndex = block->FirstIndex;
626    }
627
628    /* File Attributes stream */
629    switch (rec->Stream) {
630    case STREAM_UNIX_ATTRIBUTES:
631    case STREAM_UNIX_ATTRIBUTES_EX:
632
633       if (!unpack_attributes_record(bjcr, rec->Stream, rec->data, attr)) {
634          Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
635       }
636
637       if (attr->file_index != rec->FileIndex) {
638          Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
639             rec->FileIndex, attr->file_index);
640       }
641
642       if (verbose > 1) {
643          decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
644          build_attr_output_fnames(bjcr, attr);
645          print_ls_output(bjcr, attr);
646       }
647       fr.JobId = mjcr->JobId;
648       fr.FileId = 0;
649       num_files++;
650       if (verbose && (num_files & 0x7FFF) == 0) {
651          char ed1[30], ed2[30], ed3[30], ed4[30];
652          Pmsg4(000, _("%s file records. At file:blk=%s:%s bytes=%s\n"),
653                      edit_uint64_with_commas(num_files, ed1),
654                      edit_uint64_with_commas(rec->File, ed2),
655                      edit_uint64_with_commas(rec->Block, ed3),
656                      edit_uint64_with_commas(mr.VolBytes, ed4));
657       }
658       create_file_attributes_record(db, mjcr, attr->fname, attr->lname,
659             attr->type, attr->attr, rec);
660       free_jcr(mjcr);
661       break;
662
663    /* Data stream */
664    case STREAM_WIN32_DATA:
665    case STREAM_FILE_DATA:
666    case STREAM_SPARSE_DATA:
667       mjcr->JobBytes += rec->data_len;
668       if (rec->Stream == STREAM_SPARSE_DATA) {
669          mjcr->JobBytes -= sizeof(uint64_t);
670       }
671
672       free_jcr(mjcr);                 /* done using JCR */
673       break;
674
675    case STREAM_GZIP_DATA:
676       mjcr->JobBytes += rec->data_len; /* No correct, we should expand it */
677       free_jcr(mjcr);                 /* done using JCR */
678       break;
679
680    case STREAM_SPARSE_GZIP_DATA:
681       mjcr->JobBytes += rec->data_len - sizeof(uint64_t); /* No correct, we should expand it */
682       free_jcr(mjcr);                 /* done using JCR */
683       break;
684
685    /* Win32 GZIP stream */
686    case STREAM_WIN32_GZIP_DATA:
687       mjcr->JobBytes += rec->data_len;
688       free_jcr(mjcr);                 /* done using JCR */
689       break;
690
691    case STREAM_MD5_SIGNATURE:
692       char MD5buf[50];
693       bin_to_base64(MD5buf, (char *)rec->data, 16); /* encode 16 bytes */
694       if (verbose > 1) {
695          Pmsg1(000, _("Got MD5 record: %s\n"), MD5buf);
696       }
697       update_SIG_record(db, MD5buf, rec, MD5_SIG);
698       break;
699
700    case STREAM_SHA1_SIGNATURE:
701       char SIGbuf[50];
702       bin_to_base64(SIGbuf, (char *)rec->data, 20); /* encode 20 bytes */
703       if (verbose > 1) {
704          Pmsg1(000, _("Got SHA1 record: %s\n"), SIGbuf);
705       }
706       update_SIG_record(db, SIGbuf, rec, SHA1_SIG);
707       break;
708
709
710    case STREAM_PROGRAM_NAMES:
711       if (verbose) {
712          Pmsg1(000, _("Got Prog Names Stream: %s\n"), rec->data);
713       }
714       break;
715
716    case STREAM_PROGRAM_DATA:
717       if (verbose > 1) {
718          Pmsg0(000, _("Got Prog Data Stream record.\n"));
719       }
720       break;
721    default:
722       Pmsg2(0, _("Unknown stream type!!! stream=%d data=%s\n"), rec->Stream, rec->data);
723       break;
724    }
725    return true;
726 }
727
728 /*
729  * Free the Job Control Record if no one is still using it.
730  *  Called from main free_jcr() routine in src/lib/jcr.c so
731  *  that we can do our Director specific cleanup of the jcr.
732  */
733 static void bscan_free_jcr(JCR *jcr)
734 {
735    Dmsg0(200, "Start bscan free_jcr\n");
736
737    if (jcr->file_bsock) {
738       Dmsg0(200, "Close File bsock\n");
739       bnet_close(jcr->file_bsock);
740    }
741    if (jcr->store_bsock) {
742       Dmsg0(200, "Close Store bsock\n");
743       bnet_close(jcr->store_bsock);
744    }
745    if (jcr->RestoreBootstrap) {
746       free(jcr->RestoreBootstrap);
747    }
748    if (jcr->dcr) {
749       free_dcr(jcr->dcr);
750       jcr->dcr = NULL;
751    }
752    Dmsg0(200, "End bscan free_jcr\n");
753 }
754
755 /*
756  * We got a File Attributes record on the tape.  Now, lookup the Job
757  *   record, and then create the attributes record.
758  */
759 static int create_file_attributes_record(B_DB *db, JCR *mjcr,
760                                char *fname, char *lname, int type,
761                                char *ap, DEV_RECORD *rec)
762 {
763    DCR *dcr = mjcr->dcr;
764    ar.fname = fname;
765    ar.link = lname;
766    ar.ClientId = mjcr->ClientId;
767    ar.JobId = mjcr->JobId;
768    ar.Stream = rec->Stream;
769    ar.FileIndex = rec->FileIndex;
770    ar.attr = ap;
771    if (dcr->VolFirstIndex == 0) {
772       dcr->VolFirstIndex = rec->FileIndex;
773    }
774    dcr->FileIndex = rec->FileIndex;
775    mjcr->JobFiles++;
776
777    if (!update_db) {
778       return 1;
779    }
780
781    if (!db_create_file_attributes_record(bjcr, db, &ar)) {
782       Pmsg1(0, _("Could not create File Attributes record. ERR=%s\n"), db_strerror(db));
783       return 0;
784    }
785    mjcr->FileId = ar.FileId;
786
787    if (verbose > 1) {
788       Pmsg1(000, _("Created File record: %s\n"), fname);
789    }
790    return 1;
791 }
792
793 /*
794  * For each Volume we see, we create a Medium record
795  */
796 static int create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl)
797 {
798    struct date_time dt;
799    struct tm tm;
800
801    /* We mark Vols as Archive to keep them from being re-written */
802    bstrncpy(mr->VolStatus, "Archive", sizeof(mr->VolStatus));
803    mr->VolRetention = 365 * 3600 * 24; /* 1 year */
804    if (vl->VerNum >= 11) {
805       mr->FirstWritten = btime_to_utime(vl->write_btime);
806       mr->LabelDate    = btime_to_utime(vl->label_btime);
807    } else {
808       /* DEPRECATED DO NOT USE */
809       dt.julian_day_number = vl->write_date;
810       dt.julian_day_fraction = vl->write_time;
811       tm_decode(&dt, &tm);
812       mr->FirstWritten = mktime(&tm);
813       dt.julian_day_number = vl->label_date;
814       dt.julian_day_fraction = vl->label_time;
815       tm_decode(&dt, &tm);
816       mr->LabelDate = mktime(&tm);
817    }
818    lasttime = mr->LabelDate;
819
820    if (!update_db) {
821       return 1;
822    }
823
824    if (!db_create_media_record(bjcr, db, mr)) {
825       Pmsg1(0, _("Could not create media record. ERR=%s\n"), db_strerror(db));
826       return 0;
827    }
828    if (!db_update_media_record(bjcr, db, mr)) {
829       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
830       return 0;
831    }
832    if (verbose) {
833       Pmsg1(000, _("Created Media record for Volume: %s\n"), mr->VolumeName);
834    }
835    return 1;
836
837 }
838
839 /*
840  * Called at end of media to update it
841  */
842 static bool update_media_record(B_DB *db, MEDIA_DBR *mr)
843 {
844    if (!update_db && !update_vol_info) {
845       return true;
846    }
847
848    mr->LastWritten = lasttime;
849    if (!db_update_media_record(bjcr, db, mr)) {
850       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
851       return false;;
852    }
853    if (verbose) {
854       Pmsg1(000, _("Updated Media record at end of Volume: %s\n"), mr->VolumeName);
855    }
856    return true;
857
858 }
859
860
861 static int create_pool_record(B_DB *db, POOL_DBR *pr)
862 {
863    pr->NumVols++;
864    pr->UseCatalog = 1;
865    pr->VolRetention = 355 * 3600 * 24; /* 1 year */
866
867    if (!update_db) {
868       return 1;
869    }
870    if (!db_create_pool_record(bjcr, db, pr)) {
871       Pmsg1(0, _("Could not create pool record. ERR=%s\n"), db_strerror(db));
872       return 0;
873    }
874    if (verbose) {
875       Pmsg1(000, _("Created Pool record for Pool: %s\n"), pr->Name);
876    }
877    return 1;
878
879 }
880
881
882 /*
883  * Called from SOS to create a client for the current Job
884  */
885 static int create_client_record(B_DB *db, CLIENT_DBR *cr)
886 {
887    if (!update_db) {
888       return 1;
889    }
890    if (!db_create_client_record(bjcr, db, cr)) {
891       Pmsg1(0, _("Could not create Client record. ERR=%s\n"), db_strerror(db));
892       return 0;
893    }
894    if (verbose) {
895       Pmsg1(000, _("Created Client record for Client: %s\n"), cr->Name);
896    }
897    return 1;
898 }
899
900 static int create_fileset_record(B_DB *db, FILESET_DBR *fsr)
901 {
902    if (!update_db) {
903       return 1;
904    }
905    fsr->FileSetId = 0;
906    if (fsr->MD5[0] == 0) {
907       fsr->MD5[0] = ' ';              /* Equivalent to nothing */
908       fsr->MD5[1] = 0;
909    }
910    if (db_get_fileset_record(bjcr, db, fsr)) {
911       if (verbose) {
912          Pmsg1(000, _("Fileset \"%s\" already exists.\n"), fsr->FileSet);
913       }
914    } else {
915       if (!db_create_fileset_record(bjcr, db, fsr)) {
916          Pmsg2(0, _("Could not create FileSet record \"%s\". ERR=%s\n"),
917             fsr->FileSet, db_strerror(db));
918          return 0;
919       }
920       if (verbose) {
921          Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet);
922       }
923    }
924    return 1;
925 }
926
927 /*
928  * Simulate the two calls on the database to create
929  *  the Job record and to update it when the Job actually
930  *  begins running.
931  */
932 static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label,
933                              DEV_RECORD *rec)
934 {
935    JCR *mjcr;
936    struct date_time dt;
937    struct tm tm;
938
939    jr->JobId = label->JobId;
940    jr->JobType = label->JobType;
941    jr->JobLevel = label->JobLevel;
942    jr->JobStatus = JS_Created;
943    bstrncpy(jr->Name, label->JobName, sizeof(jr->Name));
944    bstrncpy(jr->Job, label->Job, sizeof(jr->Job));
945    if (label->VerNum >= 11) {
946       jr->SchedTime = btime_to_unix(label->write_btime);
947    } else {
948       dt.julian_day_number = label->write_date;
949       dt.julian_day_fraction = label->write_time;
950       tm_decode(&dt, &tm);
951       jr->SchedTime = mktime(&tm);
952    }
953
954    jr->StartTime = jr->SchedTime;
955    jr->JobTDate = (utime_t)jr->SchedTime;
956    jr->VolSessionId = rec->VolSessionId;
957    jr->VolSessionTime = rec->VolSessionTime;
958
959    /* Now create a JCR as if starting the Job */
960    mjcr = create_jcr(jr, rec, label->JobId);
961
962    if (!update_db) {
963       return mjcr;
964    }
965
966    /* This creates the bare essentials */
967    if (!db_create_job_record(bjcr, db, jr)) {
968       Pmsg1(0, _("Could not create JobId record. ERR=%s\n"), db_strerror(db));
969       return mjcr;
970    }
971
972    /* This adds the client, StartTime, JobTDate, ... */
973    if (!db_update_job_start_record(bjcr, db, jr)) {
974       Pmsg1(0, _("Could not update job start record. ERR=%s\n"), db_strerror(db));
975       return mjcr;
976    }
977    Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"), jr->JobId,
978          label->JobId);
979    mjcr->JobId = jr->JobId;           /* set new JobId */
980    return mjcr;
981 }
982
983 /*
984  * Simulate the database call that updates the Job
985  *  at Job termination time.
986  */
987 static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
988                               DEV_RECORD *rec)
989 {
990    struct date_time dt;
991    struct tm tm;
992    JCR *mjcr;
993
994    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
995    if (!mjcr) {
996       Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
997                    rec->VolSessionId, rec->VolSessionTime);
998       return 0;
999    }
1000    if (elabel->VerNum >= 11) {
1001       jr->EndTime = btime_to_unix(elabel->write_btime);
1002    } else {
1003       dt.julian_day_number = elabel->write_date;
1004       dt.julian_day_fraction = elabel->write_time;
1005       tm_decode(&dt, &tm);
1006       jr->EndTime = mktime(&tm);
1007    }
1008    lasttime = jr->EndTime;
1009    mjcr->end_time = jr->EndTime;
1010
1011    jr->JobId = mjcr->JobId;
1012    jr->JobStatus = elabel->JobStatus;
1013    mjcr->JobStatus = elabel->JobStatus;
1014    jr->JobFiles = elabel->JobFiles;
1015    jr->JobBytes = elabel->JobBytes;
1016    jr->VolSessionId = rec->VolSessionId;
1017    jr->VolSessionTime = rec->VolSessionTime;
1018    jr->JobTDate = (utime_t)mjcr->start_time;
1019    jr->ClientId = mjcr->ClientId;
1020
1021    if (!update_db) {
1022       free_jcr(mjcr);
1023       return 1;
1024    }
1025
1026    if (!db_update_job_end_record(bjcr, db, jr)) {
1027       Pmsg2(0, _("Could not update JobId=%u record. ERR=%s\n"), jr->JobId,  db_strerror(db));
1028       free_jcr(mjcr);
1029       return 0;
1030    }
1031    if (verbose) {
1032       Pmsg2(000, _("Updated Job termination record for JobId=%u TermStat=%c\n"), jr->JobId,
1033          jr->JobStatus);
1034    }
1035    if (verbose > 1) {
1036       const char *term_msg;
1037       static char term_code[70];
1038       char sdt[50], edt[50];
1039       char ec1[30], ec2[30], ec3[30];
1040
1041       switch (mjcr->JobStatus) {
1042       case JS_Terminated:
1043          term_msg = _("Backup OK");
1044          break;
1045       case JS_FatalError:
1046       case JS_ErrorTerminated:
1047          term_msg = _("*** Backup Error ***");
1048          break;
1049       case JS_Canceled:
1050          term_msg = _("Backup Canceled");
1051          break;
1052       default:
1053          term_msg = term_code;
1054          sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus);
1055          break;
1056       }
1057       bstrftime(sdt, sizeof(sdt), mjcr->start_time);
1058       bstrftime(edt, sizeof(edt), mjcr->end_time);
1059       Pmsg14(000,  _("%s\n"
1060 "JobId:                  %d\n"
1061 "Job:                    %s\n"
1062 "FileSet:                %s\n"
1063 "Backup Level:           %s\n"
1064 "Client:                 %s\n"
1065 "Start time:             %s\n"
1066 "End time:               %s\n"
1067 "Files Written:          %s\n"
1068 "Bytes Written:          %s\n"
1069 "Volume Session Id:      %d\n"
1070 "Volume Session Time:    %d\n"
1071 "Last Volume Bytes:      %s\n"
1072 "Termination:            %s\n\n"),
1073         edt,
1074         mjcr->JobId,
1075         mjcr->Job,
1076         mjcr->fileset_name,
1077         job_level_to_str(mjcr->JobLevel),
1078         mjcr->client_name,
1079         sdt,
1080         edt,
1081         edit_uint64_with_commas(mjcr->JobFiles, ec1),
1082         edit_uint64_with_commas(mjcr->JobBytes, ec2),
1083         mjcr->VolSessionId,
1084         mjcr->VolSessionTime,
1085         edit_uint64_with_commas(mr.VolBytes, ec3),
1086         term_msg);
1087    }
1088    free_jcr(mjcr);
1089    return 1;
1090 }
1091
1092 static int create_jobmedia_record(B_DB *db, JCR *mjcr)
1093 {
1094    JOBMEDIA_DBR jmr;
1095    DCR *dcr = mjcr->dcr;
1096
1097    if (dev->is_tape()) {
1098       dcr->EndBlock = dev->EndBlock;
1099       dcr->EndFile  = dev->EndFile;
1100 #ifdef needed
1101    } else {
1102       dcr->EndBlock = (uint32_t)dev->file_addr;
1103       dcr->EndFile = (uint32_t)(dev->file_addr >> 32);
1104 #endif
1105    } 
1106
1107    memset(&jmr, 0, sizeof(jmr));
1108    jmr.JobId = mjcr->JobId;
1109    jmr.MediaId = mr.MediaId;
1110    jmr.FirstIndex = dcr->VolFirstIndex;
1111    jmr.LastIndex = dcr->FileIndex;
1112    jmr.StartFile = dcr->StartFile;
1113    jmr.EndFile = dcr->EndFile;
1114    jmr.StartBlock = dcr->StartBlock;
1115    jmr.EndBlock = dcr->EndBlock;
1116
1117
1118    if (!update_db) {
1119       return 1;
1120    }
1121
1122    if (!db_create_jobmedia_record(bjcr, db, &jmr)) {
1123       Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db_strerror(db));
1124       return 0;
1125    }
1126    if (verbose) {
1127       Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"),
1128                 jmr.JobId, jmr.MediaId);
1129    }
1130    return 1;
1131 }
1132
1133 /*
1134  * Simulate the database call that updates the MD5/SHA1 record
1135  */
1136 static int update_SIG_record(B_DB *db, char *SIGbuf, DEV_RECORD *rec, int type)
1137 {
1138    JCR *mjcr;
1139
1140    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1141    if (!mjcr) {
1142       if (mr.VolJobs > 0) {
1143          Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"),
1144                       rec->VolSessionId, rec->VolSessionTime);
1145       } else {
1146          ignored_msgs++;
1147       }
1148       return 0;
1149    }
1150
1151    if (!update_db || mjcr->FileId == 0) {
1152       free_jcr(mjcr);
1153       return 1;
1154    }
1155
1156    if (!db_add_SIG_to_file_record(bjcr, db, mjcr->FileId, SIGbuf, type)) {
1157       Pmsg1(0, _("Could not add MD5/SHA1 to File record. ERR=%s\n"), db_strerror(db));
1158       free_jcr(mjcr);
1159       return 0;
1160    }
1161    if (verbose > 1) {
1162       Pmsg0(000, _("Updated MD5/SHA1 record\n"));
1163    }
1164    free_jcr(mjcr);
1165    return 1;
1166 }
1167
1168
1169 /*
1170  * Create a JCR as if we are really starting the job
1171  */
1172 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
1173 {
1174    JCR *jobjcr;
1175    /*
1176     * Transfer as much as possible to the Job JCR. Most important is
1177     *   the JobId and the ClientId.
1178     */
1179    jobjcr = new_jcr(sizeof(JCR), bscan_free_jcr);
1180    jobjcr->JobType = jr->JobType;
1181    jobjcr->JobLevel = jr->JobLevel;
1182    jobjcr->JobStatus = jr->JobStatus;
1183    bstrncpy(jobjcr->Job, jr->Job, sizeof(jobjcr->Job));
1184    jobjcr->JobId = JobId;      /* this is JobId on tape */
1185    jobjcr->sched_time = jr->SchedTime;
1186    jobjcr->start_time = jr->StartTime;
1187    jobjcr->VolSessionId = rec->VolSessionId;
1188    jobjcr->VolSessionTime = rec->VolSessionTime;
1189    jobjcr->ClientId = jr->ClientId;
1190    new_dcr(jobjcr, dev);
1191    return jobjcr;
1192 }
1193
1194 /* Dummies to replace askdir.c */
1195 bool    dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing) { return 1;}
1196 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
1197 bool    dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
1198 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
1199 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
1200 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
1201 bool    dir_send_job_status(JCR *jcr) {return 1;}
1202 int     generate_job_event(JCR *jcr, const char *event) { return 1; }
1203 VOLRES *new_volume(DCR *dcr, const char *VolumeName) { return NULL; }
1204 bool    free_volume(DEVICE *dev) { return true; }
1205 void    free_unused_volume(DCR *dcr) { }
1206
1207 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
1208 {
1209    DEVICE *dev = dcr->dev;
1210    Dmsg0(20, "Enter dir_ask_sysop_to_mount_volume\n");
1211    /* Close device so user can use autochanger if desired */
1212    if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
1213       offline_dev(dev);
1214    }
1215    force_close_device(dev);
1216    fprintf(stderr, "Mount Volume \"%s\" on device %s and press return when ready: ",
1217          dcr->VolumeName, dev->print_name());
1218    getchar();
1219    return true;
1220 }