]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bscan.c
075505db5aa4ea736eb7dfc5db701ceb032b2491
[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             mr.VolJobs++;
330             unser_session_label(&label, rec);
331             memset(&jr, 0, sizeof(jr));
332             jr.JobId = label.JobId;
333             if (db_get_job_record(db, &jr)) {
334                /* Job record already exists in DB */
335                update_db = 0;  /* don't change db in create_job_record */
336                if (verbose) {
337                   Pmsg1(000, _("SOS_LABEL: Found Job record for JobId: %d\n"), jr.JobId);
338                }
339             } else {
340                /* Must create a Job record in DB */
341                if (!update_db) {
342                   Pmsg1(000, "SOS_LABEL: Job record not found for JobId: %d\n",
343                      jr.JobId);
344                }
345             }
346             /* Create Client record if not already there */
347                strcpy(cr.Name, label.ClientName);
348                create_client_record(db, &cr);
349                jr.ClientId = cr.ClientId;
350
351             /* process label, if Job record exists don't update db */
352             mjcr = create_job_record(db, &jr, &label, rec);
353             update_db = save_update_db;
354
355             jr.PoolId = pr.PoolId;
356             /* Set start positions into JCR */
357             if (dev->state & ST_TAPE) {
358                mjcr->StartBlock = dev->block_num;
359                mjcr->StartFile = dev->file;
360             } else {
361                mjcr->StartBlock = (uint32_t)dev->file_addr;
362                mjcr->StartFile = (uint32_t)(dev->file_addr >> 32);
363             }
364             mjcr->start_time = jr.StartTime;
365             mjcr->JobLevel = jr.Level;
366
367             mjcr->client_name = get_pool_memory(PM_FNAME);
368             pm_strcpy(&mjcr->client_name, label.ClientName);
369             mjcr->pool_type = get_pool_memory(PM_FNAME);
370             pm_strcpy(&mjcr->pool_type, label.PoolType);
371             mjcr->fileset_name = get_pool_memory(PM_FNAME);
372             pm_strcpy(&mjcr->fileset_name, label.FileSetName);
373             mjcr->pool_name = get_pool_memory(PM_FNAME);
374             pm_strcpy(&mjcr->pool_name, label.PoolName);
375
376             if (rec->VolSessionId != jr.VolSessionId) {
377                Pmsg3(000, "SOS_LABEL: VolSessId mismatch for JobId=%u. DB=%d Vol=%d\n",
378                   jr.JobId,
379                   jr.VolSessionId, rec->VolSessionId);
380                return;
381             }
382             if (rec->VolSessionTime != jr.VolSessionTime) {
383                Pmsg3(000, "SOS_LABEL: VolSessTime mismatch for JobId=%u. DB=%d Vol=%d\n",
384                   jr.JobId,
385                   jr.VolSessionTime, rec->VolSessionTime);
386                return;
387             }
388             if (jr.PoolId != pr.PoolId) {
389                Pmsg3(000, "SOS_LABEL: PoolId mismatch for JobId=%u. DB=%d Vol=%d\n",
390                   jr.JobId,
391                   jr.PoolId, pr.PoolId);
392                return;
393             }
394             break;
395          case EOS_LABEL:
396             unser_session_label(&elabel, rec);
397
398             /* Create FileSet record */
399             strcpy(fsr.FileSet, label.FileSetName);
400             strcpy(fsr.MD5, label.FileSetMD5);
401             create_fileset_record(db, &fsr);
402             jr.FileSetId = fsr.FileSetId;
403
404             mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
405             if (!mjcr) {
406                Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
407                      rec->VolSessionId, rec->VolSessionTime);
408                break;
409             }
410
411             /* Do the final update to the Job record */
412             update_job_record(db, &jr, &elabel, rec);
413
414             mjcr->end_time = jr.EndTime;
415             mjcr->JobStatus = JS_Terminated;
416
417             /* Create JobMedia record */
418             create_jobmedia_record(db, mjcr);
419             detach_jcr_from_device(dev, mjcr);
420             free_jcr(mjcr);
421
422             break;
423          case EOM_LABEL:
424             break;
425          case EOT_LABEL:              /* end of all tapes */
426             /* 
427              * Wiffle through all jobs still open and close
428              *   them.
429              */
430             if (update_db) {
431                for (mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) {
432                   jr.JobId = mjcr->JobId;
433                   jr.JobStatus = JS_ErrorTerminated;
434                   jr.JobFiles = mjcr->JobFiles;
435                   jr.JobBytes = mjcr->JobBytes;
436                   jr.VolSessionId = mjcr->VolSessionId;
437                   jr.VolSessionTime = mjcr->VolSessionTime;
438                   jr.JobTDate = (utime_t)mjcr->start_time;
439                   jr.ClientId = mjcr->ClientId;
440                   free_jcr(mjcr);
441                   if (!db_update_job_end_record(db, &jr)) {
442                      Pmsg1(0, _("Could not update job record. ERR=%s\n"), db_strerror(db));
443                   }
444                }
445             }
446             mr.VolFiles = rec->File;
447             mr.VolBlocks = rec->Block;
448             mr.VolBytes += mr.VolBlocks * WRITE_BLKHDR_LENGTH; /* approx. */
449             mr.VolMounts++;
450             update_media_record(db, &mr);
451             Pmsg3(0, _("End of Volume. VolFiles=%u VolBlocks=%u VolBytes=%s\n"), mr.VolFiles,
452                        mr.VolBlocks, edit_uint64_with_commas(mr.VolBytes, ec1));
453             break;
454          default:
455             break;
456       }
457       return;
458    }
459
460
461    /* File Attributes stream */
462    if (rec->Stream == STREAM_UNIX_ATTRIBUTES || rec->Stream == STREAM_WIN32_ATTRIBUTES) {
463       char *ap, *lp, *fp;
464
465       if (sizeof_pool_memory(fname) < rec->data_len) {
466          fname = realloc_pool_memory(fname, rec->data_len + 1);
467       }
468       if (sizeof_pool_memory(lname) < rec->data_len) {
469          lname = realloc_pool_memory(lname, rec->data_len + 1);
470       }
471       *fname = 0;
472       *lname = 0;
473
474       /*              
475        * An Attributes record consists of:
476        *    File_index
477        *    Type   (FT_types)
478        *    Filename
479        *    Attributes
480        *    Link name (if file linked i.e. FT_LNK)
481        *
482        */
483       sscanf(rec->data, "%ld %d", &record_file_index, &type);
484       if (record_file_index != rec->FileIndex)
485          Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
486             rec->FileIndex, record_file_index);
487       ap = rec->data;
488       while (*ap++ != ' ')         /* skip record file index */
489          ;
490       while (*ap++ != ' ')         /* skip type */
491          ;
492       /* Save filename and position to attributes */
493       fp = fname;
494       while (*ap != 0) {
495          *fp++  = *ap++;
496       }
497       *fp = *ap++;                 /* terminate filename & point to attribs */
498
499       /* Skip through attributes to link name */
500       lp = ap;
501       while (*lp++ != 0) {
502          ;
503       }
504       strcat(lname, lp);        /* "save" link name */
505       if (verbose > 1) {
506          decode_stat(ap, &statp);
507          print_ls_output(fname, lname, type, &statp);   
508       }
509       mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
510       if (!mjcr) {
511          if (mr.VolJobs > 0) {
512             Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for Attributes record.\n"),
513                          rec->VolSessionId, rec->VolSessionTime);
514          }
515          return;
516       }
517       fr.JobId = mjcr->JobId;
518       fr.FileId = 0;
519       if (db_get_file_attributes_record(db, fname, &fr)) {
520          if (verbose > 1) {
521             Pmsg1(000, _("File record already exists for: %s\n"), fname);
522          }
523       } else {
524          create_file_attributes_record(db, mjcr, fname, lname, type, ap, rec);
525       }
526       free_jcr(mjcr);
527
528    /* Data stream */
529    } else if (rec->Stream == STREAM_FILE_DATA) {
530       mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
531       if (!mjcr) {
532          if (mr.VolJobs > 0) {
533             Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for File Data record.\n"),
534                          rec->VolSessionId, rec->VolSessionTime);
535          }
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          if (mr.VolJobs > 0) {
545             Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for Sparse Data record.\n"),
546                          rec->VolSessionId, rec->VolSessionTime);
547          }
548          return;
549       }
550       mjcr->JobBytes += rec->data_len - sizeof(uint64_t);
551       free_jcr(mjcr);                 /* done using JCR */
552
553    } else if (rec->Stream == STREAM_GZIP_DATA) {
554       mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
555       if (!mjcr) {
556          if (mr.VolJobs > 0) {
557             Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for GZIP Data record.\n"),
558                          rec->VolSessionId, rec->VolSessionTime);
559          }
560          return;
561       }
562       mjcr->JobBytes += rec->data_len; /* No correct, we should expand it */
563       free_jcr(mjcr);                 /* done using JCR */
564
565    } else if (rec->Stream == STREAM_SPARSE_GZIP_DATA) {
566       mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
567       if (!mjcr) {
568          if (mr.VolJobs > 0) {
569             Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for Sparse GZIP Data record.\n"),
570                          rec->VolSessionId, rec->VolSessionTime);
571          }
572          return;
573       }
574       mjcr->JobBytes += rec->data_len - sizeof(uint64_t); /* No correct, we should expand it */
575       free_jcr(mjcr);                 /* done using JCR */
576
577
578    } else if (rec->Stream == STREAM_MD5_SIGNATURE) {
579       char MD5buf[30];
580       bin_to_base64(MD5buf, (char *)rec->data, 16); /* encode 16 bytes */
581       if (verbose > 1) {
582          Pmsg1(000, _("Got MD5 record: %s\n"), MD5buf);
583       }
584       update_MD5_record(db, MD5buf, rec);
585
586    } else if (rec->Stream == STREAM_PROGRAM_NAMES) {
587       if (verbose) {
588          Pmsg1(000, _("Got Prog Names Stream: %s\n"), rec->data);
589       }
590    } else if (rec->Stream == STREAM_PROGRAM_DATA) {
591       if (verbose > 1) {
592          Pmsg0(000, _("Got Prog Data Stream record.\n"));
593       }
594    } else {
595       Pmsg2(0, _("Unknown stream type!!! stream=%d data=%s\n"), rec->Stream, rec->data);
596    }
597    return;
598 }
599
600 /*
601  * Free the Job Control Record if no one is still using it.
602  *  Called from main free_jcr() routine in src/lib/jcr.c so
603  *  that we can do our Director specific cleanup of the jcr.
604  */
605 static void dird_free_jcr(JCR *jcr)
606 {
607    Dmsg0(200, "Start dird free_jcr\n");
608
609    if (jcr->file_bsock) {
610       Dmsg0(200, "Close File bsock\n");
611       bnet_close(jcr->file_bsock);
612    }
613    if (jcr->store_bsock) {
614       Dmsg0(200, "Close Store bsock\n");
615       bnet_close(jcr->store_bsock);
616    }
617    if (jcr->RestoreBootstrap) {
618       free(jcr->RestoreBootstrap);
619    }
620    Dmsg0(200, "End dird free_jcr\n");
621 }
622
623 /*
624  * We got a File Attributes record on the tape.  Now, lookup the Job
625  *   record, and then create the attributes record.
626  */
627 static int create_file_attributes_record(B_DB *db, JCR *mjcr,
628                                char *fname, char *lname, int type,
629                                char *ap, DEV_RECORD *rec)
630 {
631
632    ar.fname = fname;
633    ar.link = lname;
634    ar.ClientId = mjcr->ClientId;
635    ar.JobId = mjcr->JobId;
636    ar.Stream = rec->Stream;
637    ar.FileIndex = rec->FileIndex;
638    ar.attr = ap;
639    if (mjcr->VolFirstFile == 0) {
640       mjcr->VolFirstFile = rec->FileIndex;
641    }
642    mjcr->FileIndex = rec->FileIndex;
643    mjcr->JobFiles++;
644
645    if (!update_db) {
646       return 1;
647    }
648
649    if (!db_create_file_attributes_record(db, &ar)) {
650       Pmsg1(0, _("Could not create File Attributes record. ERR=%s\n"), db_strerror(db));
651       return 0;
652    }
653    mjcr->FileId = ar.FileId;
654
655    if (verbose > 1) {
656       Pmsg1(000, _("Created File record: %s\n"), fname);   
657    }
658    return 1;
659 }
660
661 /*
662  * For each Volume we see, we create a Medium record
663  */
664 static int create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl)
665 {
666    struct date_time dt;
667    struct tm tm;
668
669    strcpy(mr->VolStatus, "Full");
670    mr->VolRetention = 365 * 3600 * 24; /* 1 year */
671    if (vl->VerNum >= 11) {
672       mr->FirstWritten = btime_to_utime(vl->write_btime);
673       mr->LabelDate    = btime_to_utime(vl->label_btime);
674    } else {
675       /* DEPRECATED DO NOT USE */
676       dt.julian_day_number = vl->write_date;
677       dt.julian_day_fraction = vl->write_time;
678       tm_decode(&dt, &tm);
679       mr->FirstWritten = mktime(&tm);
680       dt.julian_day_number = vl->label_date;
681       dt.julian_day_fraction = vl->label_time;
682       tm_decode(&dt, &tm);
683       mr->LabelDate = mktime(&tm);
684    }
685    lasttime = mr->LabelDate;
686
687    if (!update_db) {
688       return 1;
689    }
690
691    if (!db_create_media_record(db, mr)) {
692       Pmsg1(0, _("Could not create media record. ERR=%s\n"), db_strerror(db));
693       return 0;
694    }
695    if (!db_update_media_record(db, mr)) {
696       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
697       return 0;
698    }
699    if (verbose) {
700       Pmsg1(000, _("Created Media record for Volume: %s\n"), mr->VolumeName);
701    }
702    return 1;
703
704 }
705
706 /*
707  * Called at end of media to update it
708  */
709 static int update_media_record(B_DB *db, MEDIA_DBR *mr)
710 {
711    if (!update_db && !update_vol_info) {
712       return 1;
713    }
714
715    mr->LastWritten = lasttime;
716    if (!db_update_media_record(db, mr)) {
717       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
718       return 0;
719    }
720    if (verbose) {
721       Pmsg1(000, _("Updated Media record at end of Volume: %s\n"), mr->VolumeName);
722    }
723    return 1;
724
725 }
726
727
728 static int create_pool_record(B_DB *db, POOL_DBR *pr)
729 {
730    pr->NumVols++;
731    pr->UseCatalog = 1;
732    pr->VolRetention = 355 * 3600 * 24; /* 1 year */
733
734    if (!update_db) {
735       return 1;
736    }
737    if (!db_create_pool_record(db, pr)) {
738       Pmsg1(0, _("Could not create pool record. ERR=%s\n"), db_strerror(db));
739       return 0;
740    }
741    if (verbose) {
742       Pmsg1(000, _("Created Pool record for Pool: %s\n"), pr->Name);
743    }
744    return 1;
745
746 }
747
748
749 /*
750  * Called from SOS to create a client for the current Job 
751  */
752 static int create_client_record(B_DB *db, CLIENT_DBR *cr)
753 {
754    if (!update_db) {
755       return 1;
756    }
757    if (!db_create_client_record(db, cr)) {
758       Pmsg1(0, _("Could not create Client record. ERR=%s\n"), db_strerror(db));
759       return 0;
760    }
761    if (verbose) {
762       Pmsg1(000, _("Created Client record for Client: %s\n"), cr->Name);
763    }
764    return 1;
765 }
766
767 static int create_fileset_record(B_DB *db, FILESET_DBR *fsr)
768 {
769    if (!update_db) {
770       return 1;
771    }
772    fsr->FileSetId = 0;
773    if (fsr->MD5[0] == 0) {
774       fsr->MD5[0] = ' ';              /* Equivalent to nothing */
775       fsr->MD5[1] = 0;
776    }
777    if (db_get_fileset_record(db, fsr)) {
778       if (verbose) {
779          Pmsg1(000, _("Fileset \"%s\" already exists.\n"), fsr->FileSet);
780       }
781    } else {
782       if (!db_create_fileset_record(db, fsr)) {
783          Pmsg2(0, _("Could not create FileSet record \"%s\". ERR=%s\n"), 
784             fsr->FileSet, db_strerror(db));
785          return 0;
786       }
787       if (verbose) {
788          Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet);
789       }
790    }
791    return 1;
792 }
793
794 /*
795  * Simulate the two calls on the database to create
796  *  the Job record and to update it when the Job actually
797  *  begins running.
798  */
799 static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label, 
800                              DEV_RECORD *rec)
801 {
802    JCR *mjcr;
803    struct date_time dt;
804    struct tm tm;
805
806    jr->JobId = label->JobId;
807    jr->Type = label->JobType;
808    jr->Level = label->JobLevel;
809    jr->JobStatus = JS_Created;
810    strcpy(jr->Name, label->JobName);
811    strcpy(jr->Job, label->Job);
812    if (label->VerNum >= 11) {
813       jr->SchedTime = btime_to_unix(label->write_btime);
814    } else {
815       dt.julian_day_number = label->write_date;
816       dt.julian_day_fraction = label->write_time;
817       tm_decode(&dt, &tm);
818       jr->SchedTime = mktime(&tm);
819    }
820
821    jr->StartTime = jr->SchedTime;
822    jr->JobTDate = (utime_t)jr->SchedTime;
823    jr->VolSessionId = rec->VolSessionId;
824    jr->VolSessionTime = rec->VolSessionTime;
825
826    /* Now create a JCR as if starting the Job */
827    mjcr = create_jcr(jr, rec, label->JobId);
828
829    if (!update_db) {
830       return mjcr;
831    }
832
833    /* This creates the bare essentials */
834    if (!db_create_job_record(db, jr)) {
835       Pmsg1(0, _("Could not create JobId record. ERR=%s\n"), db_strerror(db));
836       return mjcr;
837    }
838
839    /* This adds the client, StartTime, JobTDate, ... */
840    if (!db_update_job_start_record(db, jr)) {
841       Pmsg1(0, _("Could not update job start record. ERR=%s\n"), db_strerror(db));
842       return mjcr;
843    }
844    Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"), jr->JobId, 
845          label->JobId);
846    mjcr->JobId = jr->JobId;           /* set new JobId */
847    return mjcr;
848 }
849
850 /* 
851  * Simulate the database call that updates the Job 
852  *  at Job termination time.
853  */
854 static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
855                               DEV_RECORD *rec)
856 {
857    struct date_time dt;
858    struct tm tm;
859    JCR *mjcr;
860
861    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
862    if (!mjcr) {
863       Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
864                    rec->VolSessionId, rec->VolSessionTime);
865       return 0;
866    }
867    if (elabel->VerNum >= 11) {
868       jr->EndTime = btime_to_unix(elabel->write_btime);
869    } else {
870       dt.julian_day_number = elabel->write_date;
871       dt.julian_day_fraction = elabel->write_time;
872       tm_decode(&dt, &tm);
873       jr->EndTime = mktime(&tm);
874    }
875    lasttime = jr->EndTime;
876    mjcr->end_time = jr->EndTime;
877
878    jr->JobId = mjcr->JobId;
879    jr->JobStatus = elabel->JobStatus;
880    mjcr->JobStatus = elabel->JobStatus;
881    jr->JobFiles = elabel->JobFiles;
882    jr->JobBytes = elabel->JobBytes;
883    jr->VolSessionId = rec->VolSessionId;
884    jr->VolSessionTime = rec->VolSessionTime;
885    jr->JobTDate = (utime_t)mjcr->start_time;
886    jr->ClientId = mjcr->ClientId;
887
888    if (!update_db) {
889       free_jcr(mjcr);
890       return 1;
891    }
892    
893    if (!db_update_job_end_record(db, jr)) {
894       Pmsg2(0, _("Could not update JobId=%u record. ERR=%s\n"), jr->JobId,  db_strerror(db));
895       free_jcr(mjcr);
896       return 0;
897    }
898    if (verbose) {
899       Pmsg1(000, _("Updated Job termination record for new JobId=%u\n"), jr->JobId);
900    }
901    if (verbose > 1) {
902       char *term_msg;
903       static char term_code[70];
904       char sdt[50], edt[50];
905       char ec1[30], ec2[30], ec3[30];
906
907       switch (mjcr->JobStatus) {
908       case JS_Terminated:
909          term_msg = _("Backup OK");
910          break;
911       case JS_FatalError:
912       case JS_ErrorTerminated:
913          term_msg = _("*** Backup Error ***");
914          break;
915       case JS_Cancelled:
916          term_msg = _("Backup Cancelled");
917          break;
918       default:
919          term_msg = term_code;
920          sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus);
921          break;
922       }
923       bstrftime(sdt, sizeof(sdt), mjcr->start_time);
924       bstrftime(edt, sizeof(edt), mjcr->end_time);
925       Pmsg14(000,  _("%s\n\
926 JobId:                  %d\n\
927 Job:                    %s\n\
928 FileSet:                %s\n\
929 Backup Level:           %s\n\
930 Client:                 %s\n\
931 Start time:             %s\n\
932 End time:               %s\n\
933 Files Written:          %s\n\
934 Bytes Written:          %s\n\
935 Volume Session Id:      %d\n\
936 Volume Session Time:    %d\n\
937 Last Volume Bytes:      %s\n\
938 Termination:            %s\n\n"),
939         edt,
940         mjcr->JobId,
941         mjcr->Job,
942         mjcr->fileset_name,
943         job_level_to_str(mjcr->JobLevel),
944         mjcr->client_name,
945         sdt,
946         edt,
947         edit_uint64_with_commas(mjcr->JobFiles, ec1),
948         edit_uint64_with_commas(mjcr->JobBytes, ec2),
949         mjcr->VolSessionId,
950         mjcr->VolSessionTime,
951         edit_uint64_with_commas(mr.VolBytes, ec3),
952         term_msg);
953    }
954    free_jcr(mjcr);
955    return 1;
956 }
957
958 static int create_jobmedia_record(B_DB *db, JCR *mjcr)
959 {
960    JOBMEDIA_DBR jmr;
961
962    if (dev->state & ST_TAPE) {
963       mjcr->EndBlock = dev->block_num;
964       mjcr->EndFile = dev->file;
965    } else {
966       mjcr->EndBlock = (uint32_t)dev->file_addr;
967       mjcr->EndFile = (uint32_t)(dev->file_addr >> 32);
968    }
969
970    memset(&jmr, 0, sizeof(jmr));
971    jmr.JobId = mjcr->JobId;
972    jmr.MediaId = mr.MediaId;
973    jmr.FirstIndex = mjcr->VolFirstFile;
974    jmr.LastIndex = mjcr->FileIndex;
975    jmr.StartFile = mjcr->StartFile;
976    jmr.EndFile = mjcr->EndFile;
977    jmr.StartBlock = mjcr->StartBlock;
978    jmr.EndBlock = mjcr->EndBlock;
979
980
981    if (!update_db) {
982       return 1;
983    }
984
985    if (!db_create_jobmedia_record(db, &jmr)) {
986       Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db_strerror(db));
987       return 0;
988    }
989    if (verbose) {
990       Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"), 
991                 jmr.JobId, jmr.MediaId);
992    }
993    return 1;
994 }
995
996 /* 
997  * Simulate the database call that updates the MD5 record
998  */
999 static int update_MD5_record(B_DB *db, char *MD5buf, DEV_RECORD *rec)
1000 {
1001    JCR *mjcr;
1002
1003    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1004    if (!mjcr) {
1005       if (mr.VolJobs > 0) {
1006          Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5 record.\n"),
1007                       rec->VolSessionId, rec->VolSessionTime);
1008       }
1009       return 0;
1010    }
1011
1012    if (!update_db) {
1013       free_jcr(mjcr);
1014       return 1;
1015    }
1016    
1017    if (!db_add_MD5_to_file_record(db, mjcr->FileId, MD5buf)) {
1018       Pmsg1(0, _("Could not add MD5 to File record. ERR=%s\n"), db_strerror(db));
1019       free_jcr(mjcr);
1020       return 0;
1021    }
1022    if (verbose > 1) {
1023       Pmsg0(000, _("Updated MD5 record\n"));
1024    }
1025    free_jcr(mjcr);
1026    return 1;
1027 }
1028
1029
1030 /* 
1031  * Create a JCR as if we are really starting the job
1032  */
1033 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
1034 {
1035    JCR *jobjcr;
1036    /*
1037     * Transfer as much as possible to the Job JCR. Most important is
1038     *   the JobId and the ClientId.
1039     */
1040    jobjcr = new_jcr(sizeof(JCR), dird_free_jcr);
1041    jobjcr->JobType = jr->Type;
1042    jobjcr->JobLevel = jr->Level;
1043    jobjcr->JobStatus = jr->JobStatus;
1044    strcpy(jobjcr->Job, jr->Job);
1045    jobjcr->JobId = JobId;      /* this is JobId on tape */
1046    jobjcr->sched_time = jr->SchedTime;
1047    jobjcr->start_time = jr->StartTime;
1048    jobjcr->VolSessionId = rec->VolSessionId;
1049    jobjcr->VolSessionTime = rec->VolSessionTime;
1050    jobjcr->ClientId = jr->ClientId;
1051    attach_jcr_to_device(dev, jobjcr);
1052    return jobjcr;
1053 }
1054
1055 /* Dummies to replace askdir.c */
1056 int     dir_get_volume_info(JCR *jcr, int writing) { return 1;}
1057 int     dir_find_next_appendable_volume(JCR *jcr) { return 1;}
1058 int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
1059 int     dir_create_jobmedia_record(JCR *jcr) { return 1; }
1060 int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
1061 int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
1062 int     dir_send_job_status(JCR *jcr) {return 1;}
1063
1064
1065 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
1066 {
1067    /*  
1068     * We are at the end of reading a tape. Now, we simulate handling
1069     *   the end of writing a tape by wiffling through the attached
1070     *   jcrs creating jobmedia records.
1071     */
1072    Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->VolCatInfo.VolCatName);
1073    for (JCR *mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) {
1074       if (verbose) {
1075          Pmsg1(000, "create JobMedia for Job %s\n", mjcr->Job);
1076       }
1077       if (dev->state & ST_TAPE) {
1078          mjcr->EndBlock = dev->block_num;
1079          mjcr->EndFile = dev->file;
1080       } else {
1081          mjcr->EndBlock = (uint32_t)dev->file_addr;
1082          mjcr->StartBlock = (uint32_t)(dev->file_addr >> 32);
1083       }
1084       if (!create_jobmedia_record(db, mjcr)) {
1085          Pmsg2(000, _("Could not create JobMedia record for Volume=%s Job=%s\n"),
1086             dev->VolCatInfo.VolCatName, mjcr->Job);
1087       }
1088    }
1089
1090    fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ",
1091       jcr->VolumeName, dev_name(dev));
1092    getchar();   
1093    return 1;
1094 }