2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- backup.c -- responsible for doing backup jobs
32 * Kern Sibbald, March MM
34 * Basic tasks done here:
35 * Open DB and create records for this job.
36 * Open Message Channel with Storage daemon to tell him a job will be starting.
37 * Open connection with File daemon and pass him commands
39 * When the File daemon finishes the job, update the DB.
48 /* Commands sent to File daemon */
49 static char backupcmd[] = "backup\n";
50 static char storaddr[] = "storage address=%s port=%d ssl=%d\n";
52 /* Responses received from File daemon */
53 static char OKbackup[] = "2000 OK backup\n";
54 static char OKstore[] = "2000 OK storage\n";
55 static char EndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
56 "ReadBytes=%llu JobBytes=%llu Errors=%u "
57 "VSS=%d Encrypt=%d\n";
58 /* Pre 1.39.29 (04Dec06) EndJob */
59 static char OldEndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
60 "ReadBytes=%llu JobBytes=%llu Errors=%u\n";
62 * Called here before the job is run to do the job
65 bool do_backup_init(JCR *jcr)
68 if (jcr->get_JobLevel() == L_VIRTUAL_FULL) {
69 return do_vbackup_init(jcr);
71 free_rstorage(jcr); /* we don't read so release */
73 if (!get_or_create_fileset_record(jcr)) {
78 * Get definitive Job level and since time
80 get_level_since_time(jcr, jcr->since, sizeof(jcr->since));
82 apply_pool_overrides(jcr);
84 if (!allow_duplicate_job(jcr)) {
88 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
89 if (jcr->jr.PoolId == 0) {
93 /* If pool storage specified, use it instead of job storage */
94 copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
97 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
101 create_clones(jcr); /* run any clone jobs */
107 * Foreach files in currrent list, send "/path/fname\0LStat" to FD
109 static int accurate_list_handler(void *ctx, int num_fields, char **row)
111 JCR *jcr = (JCR *)ctx;
113 if (job_canceled(jcr)) {
117 if (row[2] > 0) { /* discard when file_index == 0 */
118 jcr->file_bsock->fsend("%s%s%c%s", row[0], row[1], 0, row[4]);
124 * Send current file list to FD
125 * DIR -> FD : accurate files=xxxx
126 * DIR -> FD : /path/to/file\0Lstat
127 * DIR -> FD : /path/to/dir/\0Lstat
131 bool send_accurate_current_files(JCR *jcr)
135 if (!jcr->accurate || job_canceled(jcr) || jcr->get_JobLevel()==L_FULL) {
138 POOLMEM *jobids = get_pool_memory(PM_FNAME);
139 db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, jobids);
142 free_pool_memory(jobids);
143 Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
146 Jmsg(jcr, M_INFO, 0, _("Sending Accurate information.\n"));
148 /* to be able to allocate the right size for htable */
149 POOLMEM *nb = get_pool_memory(PM_FNAME);
150 *nb = 0; /* clear buffer */
151 Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)",jobids);
152 db_sql_query(jcr->db, buf.c_str(), db_get_int_handler, nb);
153 Dmsg2(200, "jobids=%s nb=%s\n", jobids, nb);
154 jcr->file_bsock->fsend("accurate files=%s\n", nb);
156 db_get_file_list(jcr, jcr->db, jobids, accurate_list_handler, (void *)jcr);
158 free_pool_memory(jobids);
159 free_pool_memory(nb);
161 jcr->file_bsock->signal(BNET_EOD);
162 /* TODO: use response() ? */
168 * Do a backup of the specified FileSet
170 * Returns: false on failure
173 bool do_backup(JCR *jcr)
176 int tls_need = BNET_TLS_NONE;
181 if (jcr->get_JobLevel() == L_VIRTUAL_FULL) {
182 return do_vbackup(jcr);
185 /* Print Job Start message */
186 Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
187 edit_uint64(jcr->JobId, ed1), jcr->Job);
189 set_jcr_job_status(jcr, JS_Running);
190 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
191 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
192 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
197 * Open a message channel connection with the Storage
198 * daemon. This is to let him know that our client
199 * will be contacting him for a backup session.
202 Dmsg0(110, "Open connection with storage daemon\n");
203 set_jcr_job_status(jcr, JS_WaitSD);
205 * Start conversation with Storage daemon
207 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
211 * Now start a job with the Storage daemon
213 if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
218 * Start the job prior to starting the message thread below
219 * to avoid two threads from using the BSOCK structure at
222 if (!bnet_fsend(jcr->store_bsock, "run")) {
227 * Now start a Storage daemon message thread. Note,
228 * this thread is used to provide the catalog services
229 * for the backup job, including inserting the attributes
230 * into the catalog. See catalog_update() in catreq.c
232 if (!start_storage_daemon_message_thread(jcr)) {
235 Dmsg0(150, "Storage daemon connection OK\n");
237 set_jcr_job_status(jcr, JS_WaitFD);
238 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
242 set_jcr_job_status(jcr, JS_Running);
243 fd = jcr->file_bsock;
245 if (!send_include_list(jcr)) {
249 if (!send_exclude_list(jcr)) {
253 if (!send_level_command(jcr)) {
258 * send Storage daemon address to the File daemon
261 if (store->SDDport == 0) {
262 store->SDDport = store->SDport;
265 /* TLS Requirement */
266 if (store->tls_enable) {
267 if (store->tls_require) {
268 tls_need = BNET_TLS_REQUIRED;
270 tls_need = BNET_TLS_OK;
274 fd->fsend(storaddr, store->address, store->SDDport, tls_need);
275 if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
279 if (!send_runscripts_commands(jcr)) {
284 * We re-update the job start record so that the start
285 * time is set after the run before job. This avoids
286 * that any files created by the run before job will
287 * be saved twice. They will be backed up in the current
288 * job, but not in the next one unless they are changed.
289 * Without this, they will be backed up in this job and
290 * in the next job run because in that case, their date
291 * is after the start of this run.
293 jcr->start_time = time(NULL);
294 jcr->jr.StartTime = jcr->start_time;
295 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
296 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
300 * If backup is in accurate mode, we send the list of
303 if (!send_accurate_current_files(jcr)) {
307 /* Send backup command */
308 fd->fsend(backupcmd);
309 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
313 /* Pickup Job termination data */
314 stat = wait_for_job_termination(jcr);
315 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
316 if (stat == JS_Terminated) {
317 backup_cleanup(jcr, stat);
322 /* Come here only after starting SD thread */
324 set_jcr_job_status(jcr, JS_ErrorTerminated);
325 Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
327 wait_for_job_termination(jcr, FDConnectTimeout);
328 Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
334 * Here we wait for the File daemon to signal termination,
335 * then we wait for the Storage daemon. When both
336 * are done, we return the job status.
337 * Also used by restore.c
339 int wait_for_job_termination(JCR *jcr, int timeout)
342 BSOCK *fd = jcr->file_bsock;
344 uint32_t JobFiles, Errors;
345 uint64_t ReadBytes = 0;
346 uint64_t JobBytes = 0;
351 set_jcr_job_status(jcr, JS_Running);
355 tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
357 /* Wait for Client to terminate */
358 while ((n = bget_dirmsg(fd)) >= 0) {
360 (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
361 &ReadBytes, &JobBytes, &Errors, &VSS, &Encrypt) == 7 ||
362 sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
363 &ReadBytes, &JobBytes, &Errors) == 5)) {
365 set_jcr_job_status(jcr, jcr->FDJobStatus);
366 Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
368 Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
371 if (job_canceled(jcr)) {
376 stop_bsock_timer(tid);
379 if (is_bnet_error(fd)) {
380 Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
381 job_type_to_str(jcr->get_JobType()), fd->bstrerror());
383 fd->signal(BNET_TERMINATE); /* tell Client we are terminating */
386 /* Force cancel in SD if failing */
387 if (job_canceled(jcr) || !fd_ok) {
388 cancel_storage_daemon_job(jcr);
391 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
392 wait_for_storage_daemon_termination(jcr);
394 /* Return values from FD */
396 jcr->JobFiles = JobFiles;
397 jcr->Errors = Errors;
398 jcr->ReadBytes = ReadBytes;
399 jcr->JobBytes = JobBytes;
401 jcr->Encrypt = Encrypt;
403 Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
406 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
407 // jcr->JobStatus, jcr->SDJobStatus);
409 /* Return the first error status we find Dir, FD, or SD */
410 if (!fd_ok || is_bnet_error(fd)) { /* if fd not set, that use !fd_ok */
411 jcr->FDJobStatus = JS_ErrorTerminated;
413 if (jcr->JobStatus != JS_Terminated) {
414 return jcr->JobStatus;
416 if (jcr->FDJobStatus != JS_Terminated) {
417 return jcr->FDJobStatus;
419 return jcr->SDJobStatus;
423 * Release resources allocated during backup.
425 void backup_cleanup(JCR *jcr, int TermCode)
427 char sdt[50], edt[50], schedt[50];
428 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
429 char ec6[30], ec7[30], ec8[30], elapsed[50];
430 char term_code[100], fd_term_msg[100], sd_term_msg[100];
431 const char *term_msg;
432 int msg_type = M_INFO;
435 double kbps, compression;
438 if (jcr->get_JobLevel() == L_VIRTUAL_FULL) {
439 vbackup_cleanup(jcr, TermCode);
442 Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
443 memset(&mr, 0, sizeof(mr));
444 memset(&cr, 0, sizeof(cr));
446 update_job_end(jcr, TermCode);
448 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
449 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
450 db_strerror(jcr->db));
451 set_jcr_job_status(jcr, JS_ErrorTerminated);
454 bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
455 if (!db_get_client_record(jcr, jcr->db, &cr)) {
456 Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
457 db_strerror(jcr->db));
460 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
461 if (!db_get_media_record(jcr, jcr->db, &mr)) {
462 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
463 mr.VolumeName, db_strerror(jcr->db));
464 set_jcr_job_status(jcr, JS_ErrorTerminated);
467 update_bootstrap_file(jcr);
469 switch (jcr->JobStatus) {
471 if (jcr->Errors || jcr->SDErrors) {
472 term_msg = _("Backup OK -- with warnings");
474 term_msg = _("Backup OK");
478 case JS_ErrorTerminated:
479 term_msg = _("*** Backup Error ***");
480 msg_type = M_ERROR; /* Generate error message */
481 if (jcr->store_bsock) {
482 jcr->store_bsock->signal(BNET_TERMINATE);
483 if (jcr->SD_msg_chan) {
484 pthread_cancel(jcr->SD_msg_chan);
489 term_msg = _("Backup Canceled");
490 if (jcr->store_bsock) {
491 jcr->store_bsock->signal(BNET_TERMINATE);
492 if (jcr->SD_msg_chan) {
493 pthread_cancel(jcr->SD_msg_chan);
498 term_msg = term_code;
499 sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
502 bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
503 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
504 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
505 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
509 kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
511 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
513 * Note, if the job has erred, most likely it did not write any
514 * tape, so suppress this "error" message since in that case
515 * it is normal. Or look at it the other way, only for a
516 * normal exit should we complain about this error.
518 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
519 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
521 jcr->VolumeName[0] = 0; /* none */
524 if (jcr->ReadBytes == 0) {
525 bstrncpy(compress, "None", sizeof(compress));
527 compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
528 if (compression < 0.5) {
529 bstrncpy(compress, "None", sizeof(compress));
531 bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
534 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
535 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
537 // bmicrosleep(15, 0); /* for debugging SIGHUP */
539 Jmsg(jcr, msg_type, 0, _("Bacula %s %s (%s): %s\n"
540 " Build OS: %s %s %s\n"
543 " Backup Level: %s%s\n"
544 " Client: \"%s\" %s\n"
545 " FileSet: \"%s\" %s\n"
546 " Pool: \"%s\" (From %s)\n"
547 " Catalog: \"%s\" (From %s)\n"
548 " Storage: \"%s\" (From %s)\n"
549 " Scheduled time: %s\n"
552 " Elapsed time: %s\n"
554 " FD Files Written: %s\n"
555 " SD Files Written: %s\n"
556 " FD Bytes Written: %s (%sB)\n"
557 " SD Bytes Written: %s (%sB)\n"
559 " Software Compression: %s\n"
563 " Volume name(s): %s\n"
564 " Volume Session Id: %d\n"
565 " Volume Session Time: %d\n"
566 " Last Volume Bytes: %s (%sB)\n"
567 " Non-fatal FD errors: %d\n"
569 " FD termination status: %s\n"
570 " SD termination status: %s\n"
571 " Termination: %s\n\n"),
572 my_name, VERSION, LSMDATE, edt,
573 HOST_OS, DISTNAME, DISTVER,
576 level_to_str(jcr->get_JobLevel()), jcr->since,
577 jcr->client->name(), cr.Uname,
578 jcr->fileset->name(), jcr->FSCreateTime,
579 jcr->pool->name(), jcr->pool_source,
580 jcr->catalog->name(), jcr->catalog_source,
581 jcr->wstore->name(), jcr->wstore_source,
585 edit_utime(RunTime, elapsed, sizeof(elapsed)),
587 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
588 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
589 edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
590 edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
591 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
592 edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
595 jcr->VSS?_("yes"):_("no"),
596 jcr->Encrypt?_("yes"):_("no"),
597 jcr->accurate?_("yes"):_("no"),
601 edit_uint64_with_commas(mr.VolBytes, ec7),
602 edit_uint64_with_suffix(mr.VolBytes, ec8),
609 Dmsg0(100, "Leave backup_cleanup()\n");
612 void update_bootstrap_file(JCR *jcr)
614 /* Now update the bootstrap file if any */
615 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
616 jcr->job->WriteBootstrap) {
620 POOLMEM *fname = get_pool_memory(PM_FNAME);
621 fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
623 VOL_PARAMS *VolParams = NULL;
629 bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
630 fd = bpipe ? bpipe->wfd : NULL;
632 /* ***FIXME*** handle BASE */
633 fd = fopen(fname, jcr->get_JobLevel()==L_FULL?"w+b":"a+b");
636 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
639 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
640 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
641 if (jcr->SDJobFiles != 0) {
642 set_jcr_job_status(jcr, JS_ErrorTerminated);
646 /* Start output with when and who wrote it */
647 bstrftimes(edt, sizeof(edt), time(NULL));
648 fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
649 level_to_str(jcr->get_JobLevel()), jcr->since);
650 for (int i=0; i < VolCount; i++) {
651 /* Write the record */
652 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
653 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
654 if (VolParams[i].Slot > 0) {
655 fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
657 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
658 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
659 fprintf(fd, "VolFile=%u-%u\n", VolParams[i].StartFile,
660 VolParams[i].EndFile);
661 fprintf(fd, "VolBlock=%u-%u\n", VolParams[i].StartBlock,
662 VolParams[i].EndBlock);
663 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
664 VolParams[i].LastIndex);
676 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
677 "%s: ERR=%s\n"), fname, be.bstrerror());
678 set_jcr_job_status(jcr, JS_ErrorTerminated);
680 free_pool_memory(fname);