]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bscan.c
28Jul05
[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 #ifdef xxx
498          /* Set start positions into JCR */
499          if (dev->is_tape()) {
500             /*
501              * Note, we have already advanced past current block,
502              *  so the correct number is block_num - 1
503              */
504             dcr->StartBlock = dev->block_num - 1;
505             dcr->StartFile = dev->file;
506          } else {
507             dcr->StartBlock = (uint32_t)dev->file_addr;
508             dcr->StartFile = (uint32_t)(dev->file_addr >> 32);
509          }
510 #endif
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 bscan 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 bscan 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->is_tape()) {
1100       dcr->EndBlock = dev->EndBlock;
1101       dcr->EndFile  = dev->EndFile;
1102 #ifdef needed
1103    } else {
1104       dcr->EndBlock = (uint32_t)dev->file_addr;
1105       dcr->EndFile = (uint32_t)(dev->file_addr >> 32);
1106 #endif
1107    } 
1108
1109    memset(&jmr, 0, sizeof(jmr));
1110    jmr.JobId = mjcr->JobId;
1111    jmr.MediaId = mr.MediaId;
1112    jmr.FirstIndex = dcr->VolFirstIndex;
1113    jmr.LastIndex = dcr->FileIndex;
1114    jmr.StartFile = dcr->StartFile;
1115    jmr.EndFile = dcr->EndFile;
1116    jmr.StartBlock = dcr->StartBlock;
1117    jmr.EndBlock = dcr->EndBlock;
1118
1119
1120    if (!update_db) {
1121       return 1;
1122    }
1123
1124    if (!db_create_jobmedia_record(bjcr, db, &jmr)) {
1125       Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db_strerror(db));
1126       return 0;
1127    }
1128    if (verbose) {
1129       Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"),
1130                 jmr.JobId, jmr.MediaId);
1131    }
1132    return 1;
1133 }
1134
1135 /*
1136  * Simulate the database call that updates the MD5/SHA1 record
1137  */
1138 static int update_SIG_record(B_DB *db, char *SIGbuf, DEV_RECORD *rec, int type)
1139 {
1140    JCR *mjcr;
1141
1142    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1143    if (!mjcr) {
1144       if (mr.VolJobs > 0) {
1145          Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"),
1146                       rec->VolSessionId, rec->VolSessionTime);
1147       } else {
1148          ignored_msgs++;
1149       }
1150       return 0;
1151    }
1152
1153    if (!update_db || mjcr->FileId == 0) {
1154       free_jcr(mjcr);
1155       return 1;
1156    }
1157
1158    if (!db_add_SIG_to_file_record(bjcr, db, mjcr->FileId, SIGbuf, type)) {
1159       Pmsg1(0, _("Could not add MD5/SHA1 to File record. ERR=%s\n"), db_strerror(db));
1160       free_jcr(mjcr);
1161       return 0;
1162    }
1163    if (verbose > 1) {
1164       Pmsg0(000, _("Updated MD5/SHA1 record\n"));
1165    }
1166    free_jcr(mjcr);
1167    return 1;
1168 }
1169
1170
1171 /*
1172  * Create a JCR as if we are really starting the job
1173  */
1174 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
1175 {
1176    JCR *jobjcr;
1177    /*
1178     * Transfer as much as possible to the Job JCR. Most important is
1179     *   the JobId and the ClientId.
1180     */
1181    jobjcr = new_jcr(sizeof(JCR), bscan_free_jcr);
1182    jobjcr->JobType = jr->JobType;
1183    jobjcr->JobLevel = jr->JobLevel;
1184    jobjcr->JobStatus = jr->JobStatus;
1185    bstrncpy(jobjcr->Job, jr->Job, sizeof(jobjcr->Job));
1186    jobjcr->JobId = JobId;      /* this is JobId on tape */
1187    jobjcr->sched_time = jr->SchedTime;
1188    jobjcr->start_time = jr->StartTime;
1189    jobjcr->VolSessionId = rec->VolSessionId;
1190    jobjcr->VolSessionTime = rec->VolSessionTime;
1191    jobjcr->ClientId = jr->ClientId;
1192    new_dcr(jobjcr, dev);
1193    return jobjcr;
1194 }
1195
1196 /* Dummies to replace askdir.c */
1197 bool    dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing) { return 1;}
1198 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
1199 bool    dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
1200 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
1201 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
1202 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
1203 bool    dir_send_job_status(JCR *jcr) {return 1;}
1204 int     generate_job_event(JCR *jcr, const char *event) { return 1; }
1205 VOLRES *new_volume(DCR *dcr, const char *VolumeName) { return NULL; }
1206 bool    free_volume(DEVICE *dev) { return true; }
1207 void    free_unused_volume(DCR *dcr) { }
1208
1209 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
1210 {
1211    DEVICE *dev = dcr->dev;
1212    Dmsg0(20, "Enter dir_ask_sysop_to_mount_volume\n");
1213    /* Close device so user can use autochanger if desired */
1214    if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
1215       offline_dev(dev);
1216    }
1217    force_close_device(dev);
1218    fprintf(stderr, "Mount Volume \"%s\" on device %s and press return when ready: ",
1219          dcr->VolumeName, dev->print_name());
1220    getchar();
1221    return true;
1222 }