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