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