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