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