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