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