]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bscan.c
Mark translatable strings in all source files.
[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-2005 Kern Sibbald
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
17    version 2 as amended with additional clauses defined in the
18    file LICENSE in the main source directory.
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 
23    the file LICENSE for additional details.
24
25  */
26
27 #include "bacula.h"
28 #include "stored.h"
29 #include "findlib/find.h"
30 #include "cats/cats.h"
31  
32 /* Dummy functions */
33 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
34
35 /* Forward referenced functions */
36 static void do_scan(void);
37 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
38 static int  create_file_attributes_record(B_DB *db, JCR *mjcr,
39                                char *fname, char *lname, int type,
40                                char *ap, DEV_RECORD *rec);
41 static int  create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl);
42 static bool update_media_record(B_DB *db, MEDIA_DBR *mr);
43 static int  create_pool_record(B_DB *db, POOL_DBR *pr);
44 static JCR *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 JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId);
51 static int update_SIG_record(B_DB *db, char *SIGbuf, DEV_RECORD *rec, int type);
52
53
54 /* Global variables */
55 #if defined(HAVE_CYGWIN) || defined(HAVE_WIN32)
56 int win32_client = 1;
57 #else
58 int win32_client = 0;
59 #endif
60
61
62 /* Local variables */
63 static DEVICE *dev = NULL;
64 static B_DB *db;
65 static JCR *bjcr;                     /* jcr for bscan */
66 static BSR *bsr = NULL;
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 static ATTR *attr;
77
78 static time_t lasttime = 0;
79
80 static const char *db_name = "bacula";
81 static const char *db_user = "bacula";
82 static const char *db_password = "";
83 static const char *db_host = NULL;
84 static const char *wd = NULL;
85 static bool update_db = false;
86 static bool update_vol_info = false;
87 static bool list_records = false;
88 static int ignored_msgs = 0;
89
90 static uint64_t currentVolumeSize;
91 static int last_pct = -1;
92 static bool showProgress = false;
93 static int num_jobs = 0;
94 static int num_pools = 0;
95 static int num_media = 0;
96 static int num_files = 0;
97
98 #define CONFIG_FILE "bacula-sd.conf"
99 char *configfile = NULL;
100 STORES *me = NULL;                    /* our Global resource */
101 bool forge_on = false;                /* proceed inspite of I/O errors */
102 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
103 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
104
105
106 static void usage()
107 {
108    fprintf(stderr, _(
109 "Copyright (C) 2001-2005 Kern Sibbald.\n"
110 "\nVersion: %s (%s)\n\n"
111 "Usage: bscan [ options ] <bacula-archive>\n"
112 "       -b bootstrap      specify a bootstrap file\n"
113 "       -c <file>         specify configuration file\n"
114 "       -d <nn>           set debug level to nn\n"
115 "       -m                update media info in database\n"
116 "       -n <name>         specify the database name (default bacula)\n"
117 "       -u <user>         specify database user name (default bacula)\n"
118 "       -P <password      specify database password (default none)\n"
119 "       -h <host>         specify database host (default NULL)\n"
120 "       -p                proceed inspite of I/O errors\n"
121 "       -r                list records\n"
122 "       -s                synchronize or store in database\n"
123 "       -S                show scan progress periodically\n"
124 "       -v                verbose\n"
125 "       -V <Volumes>      specify Volume names (separated by |)\n"
126 "       -w <dir>          specify working directory (default from conf file)\n"
127 "       -?                print this message\n\n"), VERSION, BDATE);
128    exit(1);
129 }
130
131 int main (int argc, char *argv[])
132 {
133    int ch;
134    struct stat stat_buf;
135    char *VolumeName = NULL;
136
137    setlocale(LC_ALL, "");
138    bindtextdomain("bacula", LOCALEDIR);
139    textdomain("bacula");
140
141    my_name_is(argc, argv, "bscan");
142    init_msg(NULL, NULL);
143
144
145    while ((ch = getopt(argc, argv, "b:c:d:h:mn:pP:rsSu:vV:w:?")) != -1) {
146       switch (ch) {
147       case 'S' :
148          showProgress = true;
149          break;
150       case 'b':
151          bsr = parse_bsr(NULL, optarg);
152          break;
153
154       case 'c':                    /* specify config file */
155          if (configfile != NULL) {
156             free(configfile);
157          }
158          configfile = bstrdup(optarg);
159          break;
160
161       case 'd':                    /* debug level */
162          debug_level = atoi(optarg);
163          if (debug_level <= 0)
164             debug_level = 1;
165          break;
166
167       case 'h':
168          db_host = optarg;
169          break;
170
171       case 'm':
172          update_vol_info = true;
173          break;
174
175       case 'n':
176          db_name = optarg;
177          break;
178
179       case 'u':
180          db_user = optarg;
181          break;
182
183       case 'P':
184          db_password = optarg;
185          break;
186
187       case 'p':
188          forge_on = true;
189          break;
190
191       case 'r':
192          list_records = true;
193          break;
194
195       case 's':
196          update_db = true;
197          break;
198
199       case 'v':
200          verbose++;
201          break;
202
203       case 'V':                    /* Volume name */
204          VolumeName = optarg;
205          break;
206
207       case 'w':
208          wd = optarg;
209          break;
210
211       case '?':
212       default:
213          usage();
214
215       }
216    }
217    argc -= optind;
218    argv += optind;
219
220    if (argc != 1) {
221       Pmsg0(0, _("Wrong number of arguments: \n"));
222       usage();
223    }
224
225    if (configfile == NULL) {
226       configfile = bstrdup(CONFIG_FILE);
227    }
228
229    parse_config(configfile);
230    LockRes();
231    me = (STORES *)GetNextRes(R_STORAGE, NULL);
232    if (!me) {
233       UnlockRes();
234       Emsg1(M_ERROR_TERM, 0, _("No Storage resource defined in %s. Cannot continue.\n"),
235          configfile);
236    }
237    UnlockRes();
238    /* Check if -w option given, otherwise use resource for working directory */
239    if (wd) {
240       working_directory = wd;
241    } else if (!me->working_directory) {
242       Emsg1(M_ERROR_TERM, 0, _("No Working Directory defined in %s. Cannot continue.\n"),
243          configfile);
244    } else {
245       working_directory = me->working_directory;
246    }
247
248    /* Check that working directory is good */
249    if (stat(working_directory, &stat_buf) != 0) {
250       Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s not found. Cannot continue.\n"),
251          working_directory);
252    }
253    if (!S_ISDIR(stat_buf.st_mode)) {
254       Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s is not a directory. Cannot continue.\n"),
255          working_directory);
256    }
257
258    bjcr = setup_jcr("bscan", argv[0], bsr, VolumeName, 1); /* read device */
259    if (!bjcr) {
260       exit(1);
261    }
262    dev = bjcr->dcr->dev;
263    if (showProgress) {
264       char ed1[50];
265       struct stat sb;
266       fstat(dev->fd, &sb);
267       currentVolumeSize = sb.st_size;
268       Pmsg1(000, _("First Volume Size = %sn"), 
269          edit_uint64(currentVolumeSize, ed1));
270    }
271
272    if ((db=db_init_database(NULL, db_name, db_user, db_password,
273         db_host, 0, NULL, 0)) == NULL) {
274       Emsg0(M_ERROR_TERM, 0, _("Could not init Bacula database\n"));
275    }
276    if (!db_open_database(NULL, db)) {
277       Emsg0(M_ERROR_TERM, 0, db_strerror(db));
278    }
279    Dmsg0(200, "Database opened\n");
280    if (verbose) {
281       Pmsg2(000, _("Using Database: %s, User: %s\n"), db_name, db_user);
282    }
283
284    do_scan();
285    if (update_db) {
286       printf("Records added or updated in the catalog:\n%7d Media\n%7d Pool\n%7d Job\n%7d File\n",
287          num_media, num_pools, num_jobs, num_files);
288    }
289    else {
290       printf("Records would have been added or updated in the catalog:\n%7d Media\n%7d Pool\n%7d Job\n%7d File\n",
291          num_media, num_pools, num_jobs, num_files);
292    }
293
294    free_jcr(bjcr);
295    term_dev(dev);
296    return 0;
297 }
298
299 /*
300  * We are at the end of reading a tape. Now, we simulate handling
301  *   the end of writing a tape by wiffling through the attached
302  *   jcrs creating jobmedia records.
303  */
304 static bool bscan_mount_next_read_volume(DCR *dcr)
305 {
306    DEVICE *dev = dcr->dev;
307    DCR *mdcr;
308    Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->VolCatInfo.VolCatName);
309    foreach_dlist(mdcr, dev->attached_dcrs) {
310       JCR *mjcr = mdcr->jcr;
311       if (mjcr->JobId == 0) {
312          continue;
313       }
314       if (verbose) {
315          Pmsg1(000, _("Create JobMedia for Job %s\n"), mjcr->Job);
316       }
317       if (dev->is_tape()) {
318          mdcr->EndBlock = dcr->EndBlock;
319          mdcr->EndFile = dcr->EndFile;
320 //    } else {
321 //       mdcr->EndBlock = (uint32_t)dcr->file_addr;
322 //       mdcr->EndFile = (uint32_t)(dcr->file_addr >> 32);
323       }
324       if (!create_jobmedia_record(db, mjcr)) {
325          Pmsg2(000, _("Could not create JobMedia record for Volume=%s Job=%s\n"),
326             dev->VolCatInfo.VolCatName, mjcr->Job);
327       }
328    }
329    /* Now let common read routine get up next tape. Note,
330     * we call mount_next... with bscan's jcr because that is where we
331     * have the Volume list, but we get attached.
332     */
333    bool stat = mount_next_read_volume(dcr);
334
335    if (showProgress) {
336       char ed1[50];
337       struct stat sb;
338       fstat(dev->fd, &sb);
339       currentVolumeSize = sb.st_size;
340       Pmsg1(000, _("First Volume Size = %sn"), 
341          edit_uint64(currentVolumeSize, ed1));
342    }
343    return stat;
344 }
345
346 static void do_scan()
347 {
348    attr = new_attr();
349
350    memset(&ar, 0, sizeof(ar));
351    memset(&pr, 0, sizeof(pr));
352    memset(&jr, 0, sizeof(jr));
353    memset(&cr, 0, sizeof(cr));
354    memset(&fsr, 0, sizeof(fsr));
355    memset(&fr, 0, sizeof(fr));
356
357    /* Detach bscan's jcr as we are not a real Job on the tape */
358
359    read_records(bjcr->dcr, record_cb, bscan_mount_next_read_volume);
360
361    free_attr(attr);
362 }
363
364 /*
365  * Returns: true  if OK
366  *          false if error
367  */
368 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
369 {
370    JCR *mjcr;
371    char ec1[30];
372    DEVICE *dev = dcr->dev;
373    JCR *bjcr = dcr->jcr;
374    DEV_BLOCK *block = dcr->block;
375
376    if (rec->data_len > 0) {
377       mr.VolBytes += rec->data_len + WRITE_RECHDR_LENGTH; /* Accumulate Volume bytes */
378       if (showProgress) {
379          int pct = (mr.VolBytes * 100) / currentVolumeSize;
380          if (pct != last_pct) {
381             fprintf(stdout, _("done: %d%%\n"), pct);
382             fflush(stdout);
383             last_pct = pct;
384          }
385       }
386    }
387
388    if (list_records) {
389       Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
390             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
391             rec->Stream, rec->data_len);
392    }
393    /*
394     * Check for Start or End of Session Record
395     *
396     */
397    if (rec->FileIndex < 0) {
398       bool save_update_db = update_db;
399
400       if (verbose > 1) {
401          dump_label_record(dev, rec, 1);
402       }
403       switch (rec->FileIndex) {
404       case PRE_LABEL:
405          Pmsg0(000, _("Volume is prelabeled. This tape cannot be scanned.\n"));
406          return false;
407          break;
408
409       case VOL_LABEL:
410          unser_volume_label(dev, rec);
411          /* Check Pool info */
412          bstrncpy(pr.Name, dev->VolHdr.PoolName, sizeof(pr.Name));
413          bstrncpy(pr.PoolType, dev->VolHdr.PoolType, sizeof(pr.PoolType));
414          num_pools++;
415          if (db_get_pool_record(bjcr, db, &pr)) {
416             if (verbose) {
417                Pmsg1(000, _("Pool record for %s found in DB.\n"), pr.Name);
418             }
419          } else {
420             if (!update_db) {
421                Pmsg1(000, _("VOL_LABEL: Pool record not found for Pool: %s\n"),
422                   pr.Name);
423             }
424             create_pool_record(db, &pr);
425          }
426          if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
427             Pmsg2(000, _("VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n"),
428                pr.PoolType, dev->VolHdr.PoolType);
429             return true;
430          } else if (verbose) {
431             Pmsg1(000, _("Pool type \"%s\" is OK.\n"), pr.PoolType);
432          }
433
434          /* Check Media Info */
435          memset(&mr, 0, sizeof(mr));
436          bstrncpy(mr.VolumeName, dev->VolHdr.VolumeName, sizeof(mr.VolumeName));
437          mr.PoolId = pr.PoolId;
438          num_media++;
439          if (db_get_media_record(bjcr, db, &mr)) {
440             if (verbose) {
441                Pmsg1(000, _("Media record for %s found in DB.\n"), mr.VolumeName);
442             }
443             /* Clear out some volume statistics that will be updated */
444             mr.VolJobs = mr.VolFiles = mr.VolBlocks = 0;
445             mr.VolBytes = rec->data_len + 20;
446          } else {
447             if (!update_db) {
448                Pmsg1(000, _("VOL_LABEL: Media record not found for Volume: %s\n"),
449                   mr.VolumeName);
450             }
451             bstrncpy(mr.MediaType, dev->VolHdr.MediaType, sizeof(mr.MediaType));
452             create_media_record(db, &mr, &dev->VolHdr);
453          }
454          if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
455             Pmsg2(000, _("VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n"),
456                mr.MediaType, dev->VolHdr.MediaType);
457             return true;              /* ignore error */
458          } else if (verbose) {
459             Pmsg1(000, _("Media type \"%s\" is OK.\n"), mr.MediaType);
460          }
461          /* Reset some DCR variables */
462          foreach_dlist(dcr, dev->attached_dcrs) {
463             dcr->VolFirstIndex = dcr->FileIndex = 0;
464             dcr->StartBlock = dcr->EndBlock = 0;
465             dcr->StartFile = dcr->EndFile = 0;
466          }
467
468          Pmsg1(000, _("VOL_LABEL: OK for Volume: %s\n"), mr.VolumeName);
469          break;
470
471       case SOS_LABEL:
472          mr.VolJobs++;
473          num_jobs++;
474          if (ignored_msgs > 0) {
475             Pmsg1(000, _("%d \"errors\" ignored before first Start of Session record.\n"),
476                   ignored_msgs);
477             ignored_msgs = 0;
478          }
479          unser_session_label(&label, rec);
480          memset(&jr, 0, sizeof(jr));
481          bstrncpy(jr.Job, label.Job, sizeof(jr.Job));
482          if (db_get_job_record(bjcr, db, &jr)) {
483             /* Job record already exists in DB */
484             update_db = false;  /* don't change db in create_job_record */
485             if (verbose) {
486                Pmsg1(000, _("SOS_LABEL: Found Job record for JobId: %d\n"), jr.JobId);
487             }
488          } else {
489             /* Must create a Job record in DB */
490             if (!update_db) {
491                Pmsg1(000, _("SOS_LABEL: Job record not found for JobId: %d\n"),
492                   jr.JobId);
493             }
494          }
495          /* Create Client record if not already there */
496             bstrncpy(cr.Name, label.ClientName, sizeof(cr.Name));
497             create_client_record(db, &cr);
498             jr.ClientId = cr.ClientId;
499
500          /* process label, if Job record exists don't update db */
501          mjcr = create_job_record(db, &jr, &label, rec);
502          dcr = mjcr->dcr;
503          update_db = save_update_db;
504
505          jr.PoolId = pr.PoolId;
506 #ifdef xxx
507          /* Set start positions into JCR */
508          if (dev->is_tape()) {
509             /*
510              * Note, we have already advanced past current block,
511              *  so the correct number is block_num - 1
512              */
513             dcr->StartBlock = dev->block_num - 1;
514             dcr->StartFile = dev->file;
515          } else {
516             dcr->StartBlock = (uint32_t)dev->file_addr;
517             dcr->StartFile = (uint32_t)(dev->file_addr >> 32);
518          }
519 #endif
520          mjcr->start_time = jr.StartTime;
521          mjcr->JobLevel = jr.JobLevel;
522
523          mjcr->client_name = get_pool_memory(PM_FNAME);
524          pm_strcpy(mjcr->client_name, label.ClientName);
525          mjcr->fileset_name = get_pool_memory(PM_FNAME);
526          pm_strcpy(mjcr->fileset_name, label.FileSetName);
527          bstrncpy(dcr->pool_type, label.PoolType, sizeof(dcr->pool_type));
528          bstrncpy(dcr->pool_name, label.PoolName, sizeof(dcr->pool_name));
529
530          if (rec->VolSessionId != jr.VolSessionId) {
531             Pmsg3(000, _("SOS_LABEL: VolSessId mismatch for JobId=%u. DB=%d Vol=%d\n"),
532                jr.JobId,
533                jr.VolSessionId, rec->VolSessionId);
534             return true;              /* ignore error */
535          }
536          if (rec->VolSessionTime != jr.VolSessionTime) {
537             Pmsg3(000, _("SOS_LABEL: VolSessTime mismatch for JobId=%u. DB=%d Vol=%d\n"),
538                jr.JobId,
539                jr.VolSessionTime, rec->VolSessionTime);
540             return true;              /* ignore error */
541          }
542          if (jr.PoolId != pr.PoolId) {
543             Pmsg3(000, _("SOS_LABEL: PoolId mismatch for JobId=%u. DB=%d Vol=%d\n"),
544                jr.JobId,
545                jr.PoolId, pr.PoolId);
546             return true;              /* ignore error */
547          }
548          break;
549
550       case EOS_LABEL:
551          unser_session_label(&elabel, rec);
552
553          /* Create FileSet record */
554          bstrncpy(fsr.FileSet, label.FileSetName, sizeof(fsr.FileSet));
555          bstrncpy(fsr.MD5, label.FileSetMD5, sizeof(fsr.MD5));
556          create_fileset_record(db, &fsr);
557          jr.FileSetId = fsr.FileSetId;
558
559          mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
560          if (!mjcr) {
561             Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
562                   rec->VolSessionId, rec->VolSessionTime);
563             break;
564          }
565
566          /* Do the final update to the Job record */
567          update_job_record(db, &jr, &elabel, rec);
568
569          mjcr->end_time = jr.EndTime;
570          mjcr->JobStatus = JS_Terminated;
571
572          /* Create JobMedia record */
573          create_jobmedia_record(db, mjcr);
574          dev->attached_dcrs->remove(mjcr->dcr);
575          free_jcr(mjcr);
576
577          break;
578
579       case EOM_LABEL:
580          break;
581
582       case EOT_LABEL:              /* end of all tapes */
583          /*
584           * Wiffle through all jobs still open and close
585           *   them.
586           */
587          if (update_db) {
588             DCR *mdcr;
589             foreach_dlist(mdcr, dev->attached_dcrs) {
590                JCR *mjcr = mdcr->jcr;
591                if (!mjcr || mjcr->JobId == 0) {
592                   continue;
593                }
594                jr.JobId = mjcr->JobId;
595                /* Mark Job as Error Terimined */
596                jr.JobStatus = JS_ErrorTerminated;
597                jr.JobFiles = mjcr->JobFiles;
598                jr.JobBytes = mjcr->JobBytes;
599                jr.VolSessionId = mjcr->VolSessionId;
600                jr.VolSessionTime = mjcr->VolSessionTime;
601                jr.JobTDate = (utime_t)mjcr->start_time;
602                jr.ClientId = mjcr->ClientId;
603                if (!db_update_job_end_record(bjcr, db, &jr)) {
604                   Pmsg1(0, _("Could not update job record. ERR=%s\n"), db_strerror(db));
605                }
606                mjcr->dcr = NULL;
607                free_jcr(mjcr);
608             }
609          }
610          mr.VolFiles = rec->File;
611          mr.VolBlocks = rec->Block;
612          mr.VolBytes += mr.VolBlocks * WRITE_BLKHDR_LENGTH; /* approx. */
613          mr.VolMounts++;
614          update_media_record(db, &mr);
615          Pmsg3(0, _("End of all Volumes. VolFiles=%u VolBlocks=%u VolBytes=%s\n"), mr.VolFiles,
616                     mr.VolBlocks, edit_uint64_with_commas(mr.VolBytes, ec1));
617          break;
618       default:
619          break;
620       } /* end switch */
621       return true;
622    }
623
624    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
625    if (!mjcr) {
626       if (mr.VolJobs > 0) {
627          Pmsg2(000, _("Could not find Job for SessId=%d SessTime=%d record.\n"),
628                       rec->VolSessionId, rec->VolSessionTime);
629       } else {
630          ignored_msgs++;
631       }
632       return true;
633    }
634    dcr = mjcr->dcr;
635    if (dcr->VolFirstIndex == 0) {
636       dcr->VolFirstIndex = block->FirstIndex;
637    }
638
639    /* File Attributes stream */
640    switch (rec->Stream) {
641    case STREAM_UNIX_ATTRIBUTES:
642    case STREAM_UNIX_ATTRIBUTES_EX:
643
644       if (!unpack_attributes_record(bjcr, rec->Stream, rec->data, attr)) {
645          Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
646       }
647
648       if (attr->file_index != rec->FileIndex) {
649          Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
650             rec->FileIndex, attr->file_index);
651       }
652
653       if (verbose > 1) {
654          decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
655          build_attr_output_fnames(bjcr, attr);
656          print_ls_output(bjcr, attr);
657       }
658       fr.JobId = mjcr->JobId;
659       fr.FileId = 0;
660       num_files++;
661       if (verbose && (num_files & 0x7FFF) == 0) {
662          char ed1[30], ed2[30], ed3[30], ed4[30];
663          Pmsg4(000, _("%s file records. At file:blk=%s:%s bytes=%s\n"),
664                      edit_uint64_with_commas(num_files, ed1),
665                      edit_uint64_with_commas(rec->File, ed2),
666                      edit_uint64_with_commas(rec->Block, ed3),
667                      edit_uint64_with_commas(mr.VolBytes, ed4));
668       }
669       create_file_attributes_record(db, mjcr, attr->fname, attr->lname,
670             attr->type, attr->attr, rec);
671       free_jcr(mjcr);
672       break;
673
674    /* Data stream */
675    case STREAM_WIN32_DATA:
676    case STREAM_FILE_DATA:
677    case STREAM_SPARSE_DATA:
678       mjcr->JobBytes += rec->data_len;
679       if (rec->Stream == STREAM_SPARSE_DATA) {
680          mjcr->JobBytes -= sizeof(uint64_t);
681       }
682
683       free_jcr(mjcr);                 /* done using JCR */
684       break;
685
686    case STREAM_GZIP_DATA:
687       mjcr->JobBytes += rec->data_len; /* No correct, we should expand it */
688       free_jcr(mjcr);                 /* done using JCR */
689       break;
690
691    case STREAM_SPARSE_GZIP_DATA:
692       mjcr->JobBytes += rec->data_len - sizeof(uint64_t); /* No correct, we should expand it */
693       free_jcr(mjcr);                 /* done using JCR */
694       break;
695
696    /* Win32 GZIP stream */
697    case STREAM_WIN32_GZIP_DATA:
698       mjcr->JobBytes += rec->data_len;
699       free_jcr(mjcr);                 /* done using JCR */
700       break;
701
702    case STREAM_MD5_SIGNATURE:
703       char MD5buf[50];
704       bin_to_base64(MD5buf, (char *)rec->data, 16); /* encode 16 bytes */
705       if (verbose > 1) {
706          Pmsg1(000, _("Got MD5 record: %s\n"), MD5buf);
707       }
708       update_SIG_record(db, MD5buf, rec, MD5_SIG);
709       break;
710
711    case STREAM_SHA1_SIGNATURE:
712       char SIGbuf[50];
713       bin_to_base64(SIGbuf, (char *)rec->data, 20); /* encode 20 bytes */
714       if (verbose > 1) {
715          Pmsg1(000, _("Got SHA1 record: %s\n"), SIGbuf);
716       }
717       update_SIG_record(db, SIGbuf, rec, SHA1_SIG);
718       break;
719
720
721    case STREAM_PROGRAM_NAMES:
722       if (verbose) {
723          Pmsg1(000, _("Got Prog Names Stream: %s\n"), rec->data);
724       }
725       break;
726
727    case STREAM_PROGRAM_DATA:
728       if (verbose > 1) {
729          Pmsg0(000, _("Got Prog Data Stream record.\n"));
730       }
731       break;
732    default:
733       Pmsg2(0, _("Unknown stream type!!! stream=%d data=%s\n"), rec->Stream, rec->data);
734       break;
735    }
736    return true;
737 }
738
739 /*
740  * Free the Job Control Record if no one is still using it.
741  *  Called from main free_jcr() routine in src/lib/jcr.c so
742  *  that we can do our Director specific cleanup of the jcr.
743  */
744 static void bscan_free_jcr(JCR *jcr)
745 {
746    Dmsg0(200, "Start bscan free_jcr\n");
747
748    if (jcr->file_bsock) {
749       Dmsg0(200, "Close File bsock\n");
750       bnet_close(jcr->file_bsock);
751    }
752    if (jcr->store_bsock) {
753       Dmsg0(200, "Close Store bsock\n");
754       bnet_close(jcr->store_bsock);
755    }
756    if (jcr->RestoreBootstrap) {
757       free(jcr->RestoreBootstrap);
758    }
759    if (jcr->dcr) {
760       free_dcr(jcr->dcr);
761       jcr->dcr = NULL;
762    }
763    Dmsg0(200, "End bscan free_jcr\n");
764 }
765
766 /*
767  * We got a File Attributes record on the tape.  Now, lookup the Job
768  *   record, and then create the attributes record.
769  */
770 static int create_file_attributes_record(B_DB *db, JCR *mjcr,
771                                char *fname, char *lname, int type,
772                                char *ap, DEV_RECORD *rec)
773 {
774    DCR *dcr = mjcr->dcr;
775    ar.fname = fname;
776    ar.link = lname;
777    ar.ClientId = mjcr->ClientId;
778    ar.JobId = mjcr->JobId;
779    ar.Stream = rec->Stream;
780    ar.FileIndex = rec->FileIndex;
781    ar.attr = ap;
782    if (dcr->VolFirstIndex == 0) {
783       dcr->VolFirstIndex = rec->FileIndex;
784    }
785    dcr->FileIndex = rec->FileIndex;
786    mjcr->JobFiles++;
787
788    if (!update_db) {
789       return 1;
790    }
791
792    if (!db_create_file_attributes_record(bjcr, db, &ar)) {
793       Pmsg1(0, _("Could not create File Attributes record. ERR=%s\n"), db_strerror(db));
794       return 0;
795    }
796    mjcr->FileId = ar.FileId;
797
798    if (verbose > 1) {
799       Pmsg1(000, _("Created File record: %s\n"), fname);
800    }
801    return 1;
802 }
803
804 /*
805  * For each Volume we see, we create a Medium record
806  */
807 static int create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl)
808 {
809    struct date_time dt;
810    struct tm tm;
811
812    /* We mark Vols as Archive to keep them from being re-written */
813    bstrncpy(mr->VolStatus, "Archive", sizeof(mr->VolStatus));
814    mr->VolRetention = 365 * 3600 * 24; /* 1 year */
815    if (vl->VerNum >= 11) {
816       mr->FirstWritten = btime_to_utime(vl->write_btime);
817       mr->LabelDate    = btime_to_utime(vl->label_btime);
818    } else {
819       /* DEPRECATED DO NOT USE */
820       dt.julian_day_number = vl->write_date;
821       dt.julian_day_fraction = vl->write_time;
822       tm_decode(&dt, &tm);
823       mr->FirstWritten = mktime(&tm);
824       dt.julian_day_number = vl->label_date;
825       dt.julian_day_fraction = vl->label_time;
826       tm_decode(&dt, &tm);
827       mr->LabelDate = mktime(&tm);
828    }
829    lasttime = mr->LabelDate;
830
831    if (!update_db) {
832       return 1;
833    }
834
835    if (!db_create_media_record(bjcr, db, mr)) {
836       Pmsg1(0, _("Could not create media record. ERR=%s\n"), db_strerror(db));
837       return 0;
838    }
839    if (!db_update_media_record(bjcr, db, mr)) {
840       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
841       return 0;
842    }
843    if (verbose) {
844       Pmsg1(000, _("Created Media record for Volume: %s\n"), mr->VolumeName);
845    }
846    return 1;
847
848 }
849
850 /*
851  * Called at end of media to update it
852  */
853 static bool update_media_record(B_DB *db, MEDIA_DBR *mr)
854 {
855    if (!update_db && !update_vol_info) {
856       return true;
857    }
858
859    mr->LastWritten = lasttime;
860    if (!db_update_media_record(bjcr, db, mr)) {
861       Pmsg1(0, _("Could not update media record. ERR=%s\n"), db_strerror(db));
862       return false;;
863    }
864    if (verbose) {
865       Pmsg1(000, _("Updated Media record at end of Volume: %s\n"), mr->VolumeName);
866    }
867    return true;
868
869 }
870
871
872 static int create_pool_record(B_DB *db, POOL_DBR *pr)
873 {
874    pr->NumVols++;
875    pr->UseCatalog = 1;
876    pr->VolRetention = 355 * 3600 * 24; /* 1 year */
877
878    if (!update_db) {
879       return 1;
880    }
881    if (!db_create_pool_record(bjcr, db, pr)) {
882       Pmsg1(0, _("Could not create pool record. ERR=%s\n"), db_strerror(db));
883       return 0;
884    }
885    if (verbose) {
886       Pmsg1(000, _("Created Pool record for Pool: %s\n"), pr->Name);
887    }
888    return 1;
889
890 }
891
892
893 /*
894  * Called from SOS to create a client for the current Job
895  */
896 static int create_client_record(B_DB *db, CLIENT_DBR *cr)
897 {
898    if (!update_db) {
899       return 1;
900    }
901    if (!db_create_client_record(bjcr, db, cr)) {
902       Pmsg1(0, _("Could not create Client record. ERR=%s\n"), db_strerror(db));
903       return 0;
904    }
905    if (verbose) {
906       Pmsg1(000, _("Created Client record for Client: %s\n"), cr->Name);
907    }
908    return 1;
909 }
910
911 static int create_fileset_record(B_DB *db, FILESET_DBR *fsr)
912 {
913    if (!update_db) {
914       return 1;
915    }
916    fsr->FileSetId = 0;
917    if (fsr->MD5[0] == 0) {
918       fsr->MD5[0] = ' ';              /* Equivalent to nothing */
919       fsr->MD5[1] = 0;
920    }
921    if (db_get_fileset_record(bjcr, db, fsr)) {
922       if (verbose) {
923          Pmsg1(000, _("Fileset \"%s\" already exists.\n"), fsr->FileSet);
924       }
925    } else {
926       if (!db_create_fileset_record(bjcr, db, fsr)) {
927          Pmsg2(0, _("Could not create FileSet record \"%s\". ERR=%s\n"),
928             fsr->FileSet, db_strerror(db));
929          return 0;
930       }
931       if (verbose) {
932          Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet);
933       }
934    }
935    return 1;
936 }
937
938 /*
939  * Simulate the two calls on the database to create
940  *  the Job record and to update it when the Job actually
941  *  begins running.
942  */
943 static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label,
944                              DEV_RECORD *rec)
945 {
946    JCR *mjcr;
947    struct date_time dt;
948    struct tm tm;
949
950    jr->JobId = label->JobId;
951    jr->JobType = label->JobType;
952    jr->JobLevel = label->JobLevel;
953    jr->JobStatus = JS_Created;
954    bstrncpy(jr->Name, label->JobName, sizeof(jr->Name));
955    bstrncpy(jr->Job, label->Job, sizeof(jr->Job));
956    if (label->VerNum >= 11) {
957       jr->SchedTime = btime_to_unix(label->write_btime);
958    } else {
959       dt.julian_day_number = label->write_date;
960       dt.julian_day_fraction = label->write_time;
961       tm_decode(&dt, &tm);
962       jr->SchedTime = mktime(&tm);
963    }
964
965    jr->StartTime = jr->SchedTime;
966    jr->JobTDate = (utime_t)jr->SchedTime;
967    jr->VolSessionId = rec->VolSessionId;
968    jr->VolSessionTime = rec->VolSessionTime;
969
970    /* Now create a JCR as if starting the Job */
971    mjcr = create_jcr(jr, rec, label->JobId);
972
973    if (!update_db) {
974       return mjcr;
975    }
976
977    /* This creates the bare essentials */
978    if (!db_create_job_record(bjcr, db, jr)) {
979       Pmsg1(0, _("Could not create JobId record. ERR=%s\n"), db_strerror(db));
980       return mjcr;
981    }
982
983    /* This adds the client, StartTime, JobTDate, ... */
984    if (!db_update_job_start_record(bjcr, db, jr)) {
985       Pmsg1(0, _("Could not update job start record. ERR=%s\n"), db_strerror(db));
986       return mjcr;
987    }
988    Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"), jr->JobId,
989          label->JobId);
990    mjcr->JobId = jr->JobId;           /* set new JobId */
991    return mjcr;
992 }
993
994 /*
995  * Simulate the database call that updates the Job
996  *  at Job termination time.
997  */
998 static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
999                               DEV_RECORD *rec)
1000 {
1001    struct date_time dt;
1002    struct tm tm;
1003    JCR *mjcr;
1004
1005    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1006    if (!mjcr) {
1007       Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
1008                    rec->VolSessionId, rec->VolSessionTime);
1009       return 0;
1010    }
1011    if (elabel->VerNum >= 11) {
1012       jr->EndTime = btime_to_unix(elabel->write_btime);
1013    } else {
1014       dt.julian_day_number = elabel->write_date;
1015       dt.julian_day_fraction = elabel->write_time;
1016       tm_decode(&dt, &tm);
1017       jr->EndTime = mktime(&tm);
1018    }
1019    lasttime = jr->EndTime;
1020    mjcr->end_time = jr->EndTime;
1021
1022    jr->JobId = mjcr->JobId;
1023    jr->JobStatus = elabel->JobStatus;
1024    mjcr->JobStatus = elabel->JobStatus;
1025    jr->JobFiles = elabel->JobFiles;
1026    jr->JobBytes = elabel->JobBytes;
1027    jr->VolSessionId = rec->VolSessionId;
1028    jr->VolSessionTime = rec->VolSessionTime;
1029    jr->JobTDate = (utime_t)mjcr->start_time;
1030    jr->ClientId = mjcr->ClientId;
1031
1032    if (!update_db) {
1033       free_jcr(mjcr);
1034       return 1;
1035    }
1036
1037    if (!db_update_job_end_record(bjcr, db, jr)) {
1038       Pmsg2(0, _("Could not update JobId=%u record. ERR=%s\n"), jr->JobId,  db_strerror(db));
1039       free_jcr(mjcr);
1040       return 0;
1041    }
1042    if (verbose) {
1043       Pmsg2(000, _("Updated Job termination record for JobId=%u TermStat=%c\n"), jr->JobId,
1044          jr->JobStatus);
1045    }
1046    if (verbose > 1) {
1047       const char *term_msg;
1048       static char term_code[70];
1049       char sdt[50], edt[50];
1050       char ec1[30], ec2[30], ec3[30];
1051
1052       switch (mjcr->JobStatus) {
1053       case JS_Terminated:
1054          term_msg = _("Backup OK");
1055          break;
1056       case JS_FatalError:
1057       case JS_ErrorTerminated:
1058          term_msg = _("*** Backup Error ***");
1059          break;
1060       case JS_Canceled:
1061          term_msg = _("Backup Canceled");
1062          break;
1063       default:
1064          term_msg = term_code;
1065          sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus);
1066          break;
1067       }
1068       bstrftime(sdt, sizeof(sdt), mjcr->start_time);
1069       bstrftime(edt, sizeof(edt), mjcr->end_time);
1070       Pmsg14(000,  _("%s\n"
1071 "JobId:                  %d\n"
1072 "Job:                    %s\n"
1073 "FileSet:                %s\n"
1074 "Backup Level:           %s\n"
1075 "Client:                 %s\n"
1076 "Start time:             %s\n"
1077 "End time:               %s\n"
1078 "Files Written:          %s\n"
1079 "Bytes Written:          %s\n"
1080 "Volume Session Id:      %d\n"
1081 "Volume Session Time:    %d\n"
1082 "Last Volume Bytes:      %s\n"
1083 "Termination:            %s\n\n"),
1084         edt,
1085         mjcr->JobId,
1086         mjcr->Job,
1087         mjcr->fileset_name,
1088         job_level_to_str(mjcr->JobLevel),
1089         mjcr->client_name,
1090         sdt,
1091         edt,
1092         edit_uint64_with_commas(mjcr->JobFiles, ec1),
1093         edit_uint64_with_commas(mjcr->JobBytes, ec2),
1094         mjcr->VolSessionId,
1095         mjcr->VolSessionTime,
1096         edit_uint64_with_commas(mr.VolBytes, ec3),
1097         term_msg);
1098    }
1099    free_jcr(mjcr);
1100    return 1;
1101 }
1102
1103 static int create_jobmedia_record(B_DB *db, JCR *mjcr)
1104 {
1105    JOBMEDIA_DBR jmr;
1106    DCR *dcr = mjcr->dcr;
1107
1108    if (dev->is_tape()) {
1109       dcr->EndBlock = dev->EndBlock;
1110       dcr->EndFile  = dev->EndFile;
1111 #ifdef needed
1112    } else {
1113       dcr->EndBlock = (uint32_t)dev->file_addr;
1114       dcr->EndFile = (uint32_t)(dev->file_addr >> 32);
1115 #endif
1116    } 
1117
1118    memset(&jmr, 0, sizeof(jmr));
1119    jmr.JobId = mjcr->JobId;
1120    jmr.MediaId = mr.MediaId;
1121    jmr.FirstIndex = dcr->VolFirstIndex;
1122    jmr.LastIndex = dcr->FileIndex;
1123    jmr.StartFile = dcr->StartFile;
1124    jmr.EndFile = dcr->EndFile;
1125    jmr.StartBlock = dcr->StartBlock;
1126    jmr.EndBlock = dcr->EndBlock;
1127
1128
1129    if (!update_db) {
1130       return 1;
1131    }
1132
1133    if (!db_create_jobmedia_record(bjcr, db, &jmr)) {
1134       Pmsg1(0, _("Could not create JobMedia record. ERR=%s\n"), db_strerror(db));
1135       return 0;
1136    }
1137    if (verbose) {
1138       Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"),
1139                 jmr.JobId, jmr.MediaId);
1140    }
1141    return 1;
1142 }
1143
1144 /*
1145  * Simulate the database call that updates the MD5/SHA1 record
1146  */
1147 static int update_SIG_record(B_DB *db, char *SIGbuf, DEV_RECORD *rec, int type)
1148 {
1149    JCR *mjcr;
1150
1151    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
1152    if (!mjcr) {
1153       if (mr.VolJobs > 0) {
1154          Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"),
1155                       rec->VolSessionId, rec->VolSessionTime);
1156       } else {
1157          ignored_msgs++;
1158       }
1159       return 0;
1160    }
1161
1162    if (!update_db || mjcr->FileId == 0) {
1163       free_jcr(mjcr);
1164       return 1;
1165    }
1166
1167    if (!db_add_SIG_to_file_record(bjcr, db, mjcr->FileId, SIGbuf, type)) {
1168       Pmsg1(0, _("Could not add MD5/SHA1 to File record. ERR=%s\n"), db_strerror(db));
1169       free_jcr(mjcr);
1170       return 0;
1171    }
1172    if (verbose > 1) {
1173       Pmsg0(000, _("Updated MD5/SHA1 record\n"));
1174    }
1175    free_jcr(mjcr);
1176    return 1;
1177 }
1178
1179
1180 /*
1181  * Create a JCR as if we are really starting the job
1182  */
1183 static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
1184 {
1185    JCR *jobjcr;
1186    /*
1187     * Transfer as much as possible to the Job JCR. Most important is
1188     *   the JobId and the ClientId.
1189     */
1190    jobjcr = new_jcr(sizeof(JCR), bscan_free_jcr);
1191    jobjcr->JobType = jr->JobType;
1192    jobjcr->JobLevel = jr->JobLevel;
1193    jobjcr->JobStatus = jr->JobStatus;
1194    bstrncpy(jobjcr->Job, jr->Job, sizeof(jobjcr->Job));
1195    jobjcr->JobId = JobId;      /* this is JobId on tape */
1196    jobjcr->sched_time = jr->SchedTime;
1197    jobjcr->start_time = jr->StartTime;
1198    jobjcr->VolSessionId = rec->VolSessionId;
1199    jobjcr->VolSessionTime = rec->VolSessionTime;
1200    jobjcr->ClientId = jr->ClientId;
1201    new_dcr(jobjcr, dev);
1202    return jobjcr;
1203 }
1204
1205 /* Dummies to replace askdir.c */
1206 bool    dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing) { return 1;}
1207 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
1208 bool    dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
1209 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
1210 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
1211 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
1212 bool    dir_send_job_status(JCR *jcr) {return 1;}
1213 int     generate_job_event(JCR *jcr, const char *event) { return 1; }
1214 VOLRES *new_volume(DCR *dcr, const char *VolumeName) { return NULL; }
1215 bool    free_volume(DEVICE *dev) { return true; }
1216 void    free_unused_volume(DCR *dcr) { }
1217
1218 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
1219 {
1220    DEVICE *dev = dcr->dev;
1221    Dmsg0(20, "Enter dir_ask_sysop_to_mount_volume\n");
1222    /* Close device so user can use autochanger if desired */
1223    if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
1224       offline_dev(dev);
1225    }
1226    force_close_device(dev);
1227    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
1228          dcr->VolumeName, dev->print_name());
1229    getchar();
1230    return true;
1231 }