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