]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bscan.c
Server address binding + bscan updates -- see kes25Sep02
[bacula/bacula] / bacula / src / stored / bscan.c
1 /*
2  *
3  *  Program to scan a Bacula Volume and compare it with
4  *    the catalog and optionally synchronize the catalog
5  *    with the tape.
6  *
7  *   Kern E. Sibbald, December 2001
8  *
9  *
10  *   Version $Id$
11  */
12 /*
13    Copyright (C) 2001, 2002 Kern Sibbald and John Walker
14
15    This program is free software; you can redistribute it and/or
16    modify it under the terms of the GNU General Public License as
17    published by the Free Software Foundation; either version 2 of
18    the License, or (at your option) any later version.
19
20    This program is distributed in the hope that it will be useful,
21    but WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23    General Public License for more details.
24
25    You should have received a copy of the GNU General Public
26    License along with this program; if not, write to the Free
27    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
28    MA 02111-1307, USA.
29
30  */
31
32 #include "bacula.h"
33 #include "stored.h"
34 #include "findlib/find.h"
35 #include "cats/cats.h"
36
37 /* Forward referenced functions */
38 static void do_scan(char *fname);
39 static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec);
40 static int  create_file_attributes_record(B_DB *db, char *fname, char *lname, int type,
41                                char *ap, DEV_RECORD *rec);
42 static int  create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl);
43 static int  create_pool_record(B_DB *db, POOL_DBR *pr);
44 static int  create_job_record(B_DB *db, JOB_DBR *mr, SESSION_LABEL *label, DEV_RECORD *rec);
45 static int  update_job_record(B_DB *db, JOB_DBR *mr, SESSION_LABEL *elabel, 
46                               DEV_RECORD *rec);
47 static int  create_client_record(B_DB *db, CLIENT_DBR *cr);
48 static int  create_fileset_record(B_DB *db, FILESET_DBR *fsr);
49 static int  create_jobmedia_record(B_DB *db, JCR *jcr);
50 static void create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId);
51
52
53 /* Global variables */
54 static DEVICE *dev = NULL;
55 static B_DB *db;
56 static JCR *jcr;                      /* jcr for bscan */
57 static JCR *jobjcr;                   /* jcr for simulating running job */
58 static BSR *bsr;
59 static struct stat statp;
60 static int type;
61 static long record_file_index;
62 static POOLMEM *fname;                       /* original file name */
63 static POOLMEM *ofile;                       /* output name with prefix */
64 static POOLMEM *lname;                       /* link name */
65 static MEDIA_DBR mr;
66 static POOL_DBR pr;
67 static JOB_DBR jr;
68 static CLIENT_DBR cr;
69 static FILESET_DBR fsr;
70 static ATTR_DBR ar;
71 static SESSION_LABEL label;
72 static SESSION_LABEL elabel;
73
74 static char *db_name = "bacula";
75 static char *db_user = "bacula";
76 static char *db_password = "";
77 static char *wd = "/tmp";
78 static int verbose = 0;
79 static int update_db = 0;
80
81 static void usage()
82 {
83    fprintf(stderr, _(
84 "\nVersion: " VERSION " (" DATE ")\n\n"
85 "Usage: bscan [-d debug_level] <bacula-archive>\n"
86 "       -b bootstrap      specify a bootstrap file\n"
87 "       -dnn              set debug level to nn\n"
88 "       -n name           specify the database name (default bacula)\n"
89 "       -u user           specify database user name (default bacula)\n"
90 "       -p password       specify database password (default none)\n"
91 "       -s                synchronize or store in database\n"
92 "       -v                verbose\n"
93 "       -w dir            specify working directory (default /tmp)\n"
94 "       -?                print this message\n\n"));
95    exit(1);
96 }
97
98 int main (int argc, char *argv[])
99 {
100    int ch;
101
102    my_name_is(argc, argv, "bscan");
103    init_msg(NULL, NULL);
104
105
106    while ((ch = getopt(argc, argv, "b:d:n:p:su:vw:?")) != -1) {
107       switch (ch) {
108          case 'b':
109             bsr = parse_bsr(NULL, optarg);
110             break;
111          case 'd':                    /* debug level */
112             debug_level = atoi(optarg);
113             if (debug_level <= 0)
114                debug_level = 1; 
115             break;
116
117          case 'n':
118             db_name = optarg;
119             break;
120
121          case 'u':
122             db_user = optarg;
123             break;
124
125          case 'p':
126             db_password = optarg;
127             break;
128
129          case 's':
130             update_db = 1;
131             break;
132
133          case 'v':
134             verbose++;
135             break;
136
137          case 'w':
138             wd = optarg;
139             break;
140
141          case '?':
142          default:
143             usage();
144
145       }  
146    }
147    argc -= optind;
148    argv += optind;
149
150    if (argc != 1) {
151       Pmsg0(0, _("Wrong number of arguments: \n"));
152       usage();
153    }
154
155    working_directory = wd;
156
157    jcr = setup_jcr("bscan", argv[0], bsr);
158
159    if ((db=db_init_database(NULL, db_name, db_user, db_password)) == NULL) {
160       Emsg0(M_ERROR_TERM, 0, _("Could not init Bacula database\n"));
161    }
162    if (!db_open_database(db)) {
163       Emsg0(M_ERROR_TERM, 0, db_strerror(db));
164    }
165    Dmsg0(200, "Database opened\n");
166    if (verbose) {
167       Pmsg2(000, _("Using Database: %s, User: %s\n"), db_name, db_user);
168    }
169
170    do_scan(argv[0]);
171
172    free_jcr(jcr);
173    return 0;
174 }
175   
176
177 static void do_scan(char *devname)             
178 {
179
180    dev = setup_to_read_device(jcr);
181    if (!dev) { 
182       exit(1);
183    }
184
185    fname = get_pool_memory(PM_FNAME);
186    ofile = get_pool_memory(PM_FNAME);
187    lname = get_pool_memory(PM_FNAME);
188
189    memset(&ar, 0, sizeof(ar));
190    memset(&pr, 0, sizeof(pr));
191    memset(&jr, 0, sizeof(jr));
192    memset(&cr, 0, sizeof(cr));
193    memset(&fsr, 0, sizeof(fsr));
194
195    detach_jcr_from_device(dev, jcr);
196
197    read_records(jcr, dev, record_cb, mount_next_read_volume);
198    release_device(jcr, dev);
199
200    free_pool_memory(fname);
201    free_pool_memory(ofile);
202    free_pool_memory(lname);
203    term_dev(dev);
204 }
205
206 static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
207 {
208    /* 
209     * Check for Start or End of Session Record 
210     *
211     */
212    if (rec->FileIndex < 0) {
213
214       if (verbose > 1) {
215          dump_label_record(dev, rec, 1);
216       }
217       switch (rec->FileIndex) {
218          case PRE_LABEL:
219             Pmsg0(000, "Volume is prelabeled. This tape cannot be scanned.\n");
220             return;
221             break;
222          case VOL_LABEL:
223             unser_volume_label(dev, rec);
224             /* Check Pool info */
225             strcpy(pr.Name, dev->VolHdr.PoolName);
226             strcpy(pr.PoolType, dev->VolHdr.PoolType);
227             if (db_get_pool_record(db, &pr)) {
228                if (verbose) {
229                   Pmsg1(000, "Pool record for %s found in DB.\n", pr.Name);
230                }
231             } else {
232                Pmsg1(000, "VOL_LABEL: Pool record not found for Pool: %s\n",
233                   pr.Name);
234                create_pool_record(db, &pr);
235             }
236             if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
237                Pmsg2(000, "VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n",
238                   pr.PoolType, dev->VolHdr.PoolType);
239                return;
240             } else if (verbose) {
241                Pmsg1(000, "Pool type \"%s\" is OK.\n", pr.PoolType);
242             }
243
244             /* Check Media Info */
245             memset(&mr, 0, sizeof(mr));
246             strcpy(mr.VolumeName, dev->VolHdr.VolName);
247             mr.PoolId = pr.PoolId;
248             if (db_get_media_record(db, &mr)) {
249                if (verbose) {
250                   Pmsg1(000, "Media record for %s found in DB.\n", mr.VolumeName);
251                }
252             } else {
253                Pmsg1(000, "VOL_LABEL: Media record not found for Volume: %s\n",
254                   mr.VolumeName);
255                strcpy(mr.MediaType, dev->VolHdr.MediaType);
256                create_media_record(db, &mr, &dev->VolHdr);
257             }
258             if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
259                Pmsg2(000, "VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n",
260                   mr.MediaType, dev->VolHdr.MediaType);
261                return;
262             } else if (verbose) {
263                Pmsg1(000, "Media type \"%s\" is OK.\n", mr.MediaType);
264             }
265             /* Reset some JCR variables */
266             for (JCR *mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) {
267                mjcr->VolFirstFile = mjcr->FileIndex = 0;
268                mjcr->start_block = mjcr->end_block = 0;
269                mjcr->start_file = mjcr->end_file = 0;
270             }
271
272             Pmsg1(000, "VOL_LABEL: OK for Volume: %s\n", mr.VolumeName);
273             break;
274          case SOS_LABEL:
275             unser_session_label(&label, rec);
276             memset(&jr, 0, sizeof(jr));
277             jr.JobId = label.JobId;
278             if (db_get_job_record(db, &jr)) {
279                /* Job record already exists in DB */
280                create_jcr(&jr, rec, jr.JobId);
281                attach_jcr_to_device(dev, jobjcr);
282                if (verbose) {
283                   Pmsg1(000, _("SOS_LABEL: Found Job record for JobId: %d\n"), jr.JobId);
284                }
285             } else {
286              
287                /* Must create a Job record in DB */
288                Pmsg1(000, "SOS_LABEL: Job record not found for JobId: %d\n",
289                   jr.JobId);
290
291                /* Create Client record */
292                strcpy(cr.Name, label.ClientName);
293                create_client_record(db, &cr);
294                jr.ClientId = cr.ClientId;
295
296                create_job_record(db, &jr, &label, rec);
297                jr.PoolId = pr.PoolId;
298                /* Set start positions into JCR */
299                jobjcr->start_block = dev->block_num;
300                jobjcr->start_file = dev->file;
301             }
302             if (rec->VolSessionId != jr.VolSessionId) {
303                Pmsg3(000, "SOS_LABEL: VolSessId mismatch for JobId=%u. DB=%d Vol=%d\n",
304                   jr.JobId,
305                   jr.VolSessionId, rec->VolSessionId);
306                return;
307             }
308             if (rec->VolSessionTime != jr.VolSessionTime) {
309                Pmsg3(000, "SOS_LABEL: VolSessTime mismatch for JobId=%u. DB=%d Vol=%d\n",
310                   jr.JobId,
311                   jr.VolSessionTime, rec->VolSessionTime);
312                return;
313             }
314             if (jr.PoolId != pr.PoolId) {
315                Pmsg3(000, "SOS_LABEL: PoolId mismatch for JobId=%u. DB=%d Vol=%d\n",
316                   jr.JobId,
317                   jr.PoolId, pr.PoolId);
318                return;
319             }
320             break;
321          case EOS_LABEL:
322             unser_session_label(&elabel, rec);
323
324             /* Create FileSet record */
325             strcpy(fsr.FileSet, label.FileSetName);
326             create_fileset_record(db, &fsr);
327             jr.FileSetId = fsr.FileSetId;
328
329             /* Do the final update to the Job record */
330             update_job_record(db, &jr, &elabel, rec);
331
332             /* Create JobMedia record */
333             jobjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
334             if (!jobjcr) {
335                Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
336                    rec->VolSessionId, rec->VolSessionTime);
337                break;
338             }
339
340             jobjcr->end_block = dev->block_num;
341             jobjcr->end_file = dev->file;
342             create_jobmedia_record(db, jobjcr);
343             detach_jcr_from_device(dev, jobjcr);
344             free_jcr(jobjcr);
345
346             Pmsg1(000, "EOS_LABEL: OK for JobId=%d\n", elabel.JobId);
347             break;
348          case EOM_LABEL:
349             break;
350          default:
351             break;
352       }
353       return;
354    }
355
356
357    /* File Attributes stream */
358    if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
359       char *ap, *lp, *fp;
360
361       if (sizeof_pool_memory(fname) < rec->data_len) {
362          fname = realloc_pool_memory(fname, rec->data_len + 1);
363       }
364       if (sizeof_pool_memory(lname) < rec->data_len) {
365          lname = realloc_pool_memory(lname, rec->data_len + 1);
366       }
367       *fname = 0;
368       *lname = 0;
369
370       /*              
371        * An Attributes record consists of:
372        *    File_index
373        *    Type   (FT_types)
374        *    Filename
375        *    Attributes
376        *    Link name (if file linked i.e. FT_LNK)
377        *
378        */
379       sscanf(rec->data, "%ld %d", &record_file_index, &type);
380       if (record_file_index != rec->FileIndex)
381          Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
382             rec->FileIndex, record_file_index);
383       ap = rec->data;
384       while (*ap++ != ' ')         /* skip record file index */
385          ;
386       while (*ap++ != ' ')         /* skip type */
387          ;
388       /* Save filename and position to attributes */
389       fp = fname;
390       while (*ap != 0) {
391          *fp++  = *ap++;
392       }
393       *fp = *ap++;                 /* terminate filename & point to attribs */
394
395       /* Skip through attributes to link name */
396       lp = ap;
397       while (*lp++ != 0) {
398          ;
399       }
400       strcat(lname, lp);        /* "save" link name */
401
402
403       if (verbose > 1) {
404          decode_stat(ap, &statp);
405          print_ls_output(fname, lname, type, &statp);   
406       }
407       create_file_attributes_record(db, fname, lname, type, ap, rec);
408
409    /* Data stream and extracting */
410    } else if (rec->Stream == STREAM_FILE_DATA) {
411
412    } else if (rec->Stream == STREAM_GZIP_DATA) {
413
414    } else if (rec->Stream == STREAM_MD5_SIGNATURE) {
415       if (verbose > 1) {
416          Pmsg0(000, _("Got MD5 record.\n"));
417       }
418       /* ****FIXME**** implement db_update_md5_record */
419    } else {
420       Pmsg2(0, _("Unknown stream type!!! stream=%d data=%s\n"), rec->Stream, rec->data);
421    }
422    return;
423 }
424
425 /*
426  * Free the Job Control Record if no one is still using it.
427  *  Called from main free_jcr() routine in src/lib/jcr.c so
428  *  that we can do our Director specific cleanup of the jcr.
429  */
430 static void dird_free_jcr(JCR *jcr)
431 {
432    Dmsg0(200, "Start dird free_jcr\n");
433
434    if (jcr->file_bsock) {
435       Dmsg0(200, "Close File bsock\n");
436       bnet_close(jcr->file_bsock);
437    }
438    if (jcr->store_bsock) {
439       Dmsg0(200, "Close Store bsock\n");
440       bnet_close(jcr->store_bsock);
441    }
442    if (jcr->RestoreBootstrap) {
443       free(jcr->RestoreBootstrap);
444    }
445    Dmsg0(200, "End dird free_jcr\n");
446 }
447
448 /*
449  * We got a File Attributes record on the tape.  Now, lookup the Job
450  *   record, and then create the attributes record.
451  */
452 static int create_file_attributes_record(B_DB *db, char *fname, char *lname, int type,
453                                char *ap, DEV_RECORD *rec)
454 {
455    JCR *mjcr;
456
457    if (!update_db) {
458       return 1;
459    }
460
461    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
462    if (!mjcr) {
463       Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for Attributes record.\n"),
464                    rec->VolSessionId, rec->VolSessionTime);
465       return 0;
466    }
467    ar.fname = fname;
468    ar.link = lname;
469    ar.ClientId = mjcr->ClientId;
470    ar.JobId = mjcr->JobId;
471    ar.Stream = rec->Stream;
472    ar.FileIndex = rec->FileIndex;
473    ar.attr = ap;
474    if (mjcr->VolFirstFile == 0) {
475       mjcr->VolFirstFile = rec->FileIndex;
476    }
477    mjcr->FileIndex = rec->FileIndex;
478    free_jcr(mjcr);                    /* done using JCR */
479
480    if (!db_create_file_attributes_record(db, &ar)) {
481       Pmsg1(0, _("Could not create File Attributes record. ERR=%s\n"), db_strerror(db));
482       return 0;
483    }
484    if (verbose > 1) {
485       Pmsg1(000, _("Created File record: %s\n"), fname);   
486    }
487    return 1;
488 }
489
490 /*
491  * For each Volume we see, we create a Medium record
492  */
493 static int create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl)
494 {
495    struct date_time dt;
496    struct tm tm;
497    if (!update_db) {
498       return 1;
499    }
500    strcpy(mr->VolStatus, "Full");
501    mr->VolRetention = 355 * 3600 * 24; /* 1 year */
502    dt.julian_day_number = vl->write_date;
503    dt.julian_day_fraction = vl->write_time;
504    tm_decode(&dt, &tm);
505    mr->FirstWritten = mktime(&tm);
506    dt.julian_day_number = vl->label_date;
507    dt.julian_day_fraction = vl->label_time;
508    tm_decode(&dt, &tm);
509    mr->LabelDate = mktime(&tm);
510    if (!db_create_media_record(db, mr)) {
511       Pmsg1(0, _("Could not create media record. ERR=%s\n"), db_strerror(db));
512       return 0;
513    }
514    if (!db_update_media_record(db, mr)) {
515       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
516       return 0;
517    }
518    if (verbose) {
519       Pmsg2(000, _("Created Media record for Volume: %s, JobId: %d\n"), 
520                    mr->VolumeName, jr.JobId);
521    }
522    return 1;
523
524 }
525
526 static int create_pool_record(B_DB *db, POOL_DBR *pr)
527 {
528    if (!update_db) {
529       return 1;
530    }
531    pr->NumVols++;
532    pr->UseCatalog = 1;
533    pr->VolRetention = 355 * 3600 * 24; /* 1 year */
534    if (!db_create_pool_record(db, pr)) {
535       Pmsg1(0, _("Could not create pool record. ERR=%s\n"), db_strerror(db));
536       return 0;
537    }
538    if (verbose) {
539       Pmsg1(000, _("Created Pool record for Pool: %s\n"), pr->Name);
540    }
541    return 1;
542
543 }
544
545
546 /*
547  * Called from SOS to create a client for the current Job 
548  */
549 static int create_client_record(B_DB *db, CLIENT_DBR *cr)
550 {
551    if (!update_db) {
552       return 1;
553    }
554    if (!db_create_client_record(db, cr)) {
555       Pmsg1(0, _("Could not create Client record. ERR=%s\n"), db_strerror(db));
556       return 0;
557    }
558    if (verbose) {
559       Pmsg1(000, _("Created Client record for Client: %s\n"), cr->Name);
560    }
561    return 1;
562 }
563
564 static int create_fileset_record(B_DB *db, FILESET_DBR *fsr)
565 {
566    if (!update_db) {
567       return 1;
568    }
569    if (!db_create_fileset_record(db, fsr)) {
570       Pmsg1(0, _("Could not create FileSet record. ERR=%s\n"), db_strerror(db));
571       return 0;
572    }
573    if (verbose) {
574       Pmsg1(000, _("Created FileSet record %s\n"), fsr->FileSet);
575    }
576    return 1;
577 }
578
579 /*
580  * Simulate the two calls on the database to create
581  *  the Job record and to update it when the Job actually
582  *  begins running.
583  */
584 static int create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label, 
585                              DEV_RECORD *rec)
586 {
587    struct date_time dt;
588    struct tm tm;
589
590    if (!update_db) {
591       return 1;
592    }
593    Pmsg1(000, _("Creating Job record for JobId: %d\n"), jr->JobId);
594
595    jr->JobId = label->JobId;
596    jr->Type = label->JobType;
597    jr->Level = label->JobLevel;
598    jr->JobStatus = JS_Created;
599    strcpy(jr->Name, label->JobName);
600    strcpy(jr->Job, label->Job);
601    dt.julian_day_number = label->write_date;
602    dt.julian_day_fraction = label->write_time;
603    tm_decode(&dt, &tm);
604    jr->SchedTime = mktime(&tm);
605    jr->StartTime = jr->SchedTime;
606    jr->JobTDate = (btime_t)jr->SchedTime;
607    jr->VolSessionId = rec->VolSessionId;
608    jr->VolSessionTime = rec->VolSessionTime;
609
610    /* This creates the bare essentials */
611    if (!db_create_job_record(db, jr)) {
612       Pmsg1(0, _("Could not create job record. ERR=%s\n"), db_strerror(db));
613       return 0;
614    }
615
616    /* Now create a JCR as if starting the Job */
617    create_jcr(jr, rec, label->JobId);
618
619    /* This adds the client, StartTime, JobTDate, ... */
620    if (!db_update_job_start_record(db, jr)) {
621       Pmsg1(0, _("Could not update job start record. ERR=%s\n"), db_strerror(db));
622       return 0;
623    }
624    if (verbose) {
625       Pmsg1(000, _("Created Job record for JobId: %d\n"), jr->JobId);
626    }
627    return 1;
628 }
629
630 /* 
631  * Simulate the database call that updates the Job 
632  *  at Job termination time.
633  */
634 static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
635                               DEV_RECORD *rec)
636 {
637    struct date_time dt;
638    struct tm tm;
639    JCR *mjcr;
640
641    if (!update_db) {
642       return 1;
643    }
644    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
645    if (!mjcr) {
646       Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
647                    rec->VolSessionId, rec->VolSessionTime);
648       return 0;
649    }
650    dt.julian_day_number = elabel->write_date;
651    dt.julian_day_fraction = elabel->write_time;
652    tm_decode(&dt, &tm);
653    jr->JobId = mjcr->JobId;
654    jr->JobStatus = JS_Terminated;     /* ***FIXME*** need to add to EOS label */
655    jr->EndTime = mktime(&tm);
656    jr->JobFiles = elabel->JobFiles;
657    jr->JobBytes = elabel->JobBytes;
658    jr->VolSessionId = rec->VolSessionId;
659    jr->VolSessionTime = rec->VolSessionTime;
660    jr->JobTDate = (btime_t)mjcr->start_time;
661    jr->ClientId = mjcr->ClientId;
662    if (!db_update_job_end_record(db, jr)) {
663       Pmsg1(0, _("Could not update job record. ERR=%s\n"), db_strerror(db));
664       free_jcr(mjcr);
665       return 0;
666    }
667    if (verbose) {
668       Pmsg1(000, _("Updated Job termination record for JobId: %d\n"), jr->JobId);
669    }
670    free_jcr(mjcr);
671    return 1;
672 }
673
674 static int create_jobmedia_record(B_DB *db, JCR *mjcr)
675 {
676    JOBMEDIA_DBR jmr;
677
678    if (!update_db) {
679       return 1;
680    }
681    memset(&jmr, 0, sizeof(jmr));
682    jmr.JobId = mjcr->JobId;
683    jmr.MediaId = mr.MediaId;
684    jmr.FirstIndex = mjcr->VolFirstFile;
685    jmr.LastIndex = mjcr->FileIndex;
686    jmr.StartFile = mjcr->start_file;
687    jmr.EndFile = mjcr->end_file;
688    jmr.StartBlock = mjcr->start_block;
689    jmr.EndBlock = mjcr->end_block;
690
691    if (!db_create_jobmedia_record(db, &jmr)) {
692       Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db_strerror(db));
693       return 0;
694    }
695    if (verbose) {
696       Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"), 
697                 jmr.JobId, jmr.MediaId);
698    }
699    return 1;
700 }
701
702 /* 
703  * Create a JCR as if we are really starting the job
704  */
705 static void create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
706 {
707    /*
708     * Transfer as much as possible to the Job JCR. Most important is
709     *   the JobId and the ClientId.
710     */
711    jobjcr = new_jcr(sizeof(JCR), dird_free_jcr);
712    jobjcr->JobType = jr->Type;
713    jobjcr->JobLevel = jr->Level;
714    jobjcr->JobStatus = jr->JobStatus;
715    strcpy(jobjcr->Job, jr->Job);
716    jobjcr->JobId = JobId;      /* this is JobId on tape */
717    jobjcr->sched_time = jr->SchedTime;
718    jobjcr->start_time = jr->StartTime;
719    jobjcr->VolSessionId = rec->VolSessionId;
720    jobjcr->VolSessionTime = rec->VolSessionTime;
721    jobjcr->ClientId = jr->ClientId;
722    attach_jcr_to_device(dev, jobjcr);
723 }
724
725 /* Dummies to replace askdir.c */
726 int     dir_get_volume_info(JCR *jcr, int writing) { return 1;}
727 int     dir_find_next_appendable_volume(JCR *jcr) { return 1;}
728 int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
729 int     dir_create_jobmedia_record(JCR *jcr) { return 1; }
730 int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
731 int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
732 int     dir_send_job_status(JCR *jcr) {return 1;}
733
734
735 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
736 {
737    /*  
738     * We are at the end of reading a tape. Now, we simulate handling
739     *   the end of writing a tape by wiffling through the attached
740     *   jcrs creating jobmedia records.
741     */
742    Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->VolCatInfo.VolCatName);
743    for (JCR *mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) {
744       if (verbose) {
745          Pmsg1(000, "create JobMedia for Job %s\n", mjcr->Job);
746       }
747       mjcr->end_block = dev->block_num;
748       mjcr->end_file = dev->file;
749       if (!create_jobmedia_record(db, mjcr)) {
750          Pmsg2(000, _("Could not create JobMedia record for Volume=%s Job=%s\n"),
751             dev->VolCatInfo.VolCatName, mjcr->Job);
752       }
753    }
754
755    fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ",
756       jcr->VolumeName, dev_name(dev));
757    getchar();   
758    return 1;
759 }