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 John Walker.
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 free_rstorage(jcr); /* we don't read so release */
70 if (!get_or_create_fileset_record(jcr)) {
75 * Get definitive Job level and since time
77 get_level_since_time(jcr, jcr->since, sizeof(jcr->since));
79 apply_pool_overrides(jcr);
81 if (!allow_duplicate_job(jcr)) {
85 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
86 if (jcr->jr.PoolId == 0) {
90 /* If pool storage specified, use it instead of job storage */
91 copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
94 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
98 create_clones(jcr); /* run any clone jobs */
104 * Foreach files in currrent list, send "/path/fname\0LStat" to FD
106 static int accurate_list_handler(void *ctx, int num_fields, char **row)
108 JCR *jcr = (JCR *)ctx;
110 if (job_canceled(jcr)) {
114 if (row[2] > 0) { /* discard when file_index == 0 */
115 jcr->file_bsock->fsend("%s%s%c%s", row[0], row[1], 0, row[4]);
121 * Send current file list to FD
122 * DIR -> FD : accurate files=xxxx
123 * DIR -> FD : /path/to/file\0Lstat
124 * DIR -> FD : /path/to/dir/\0Lstat
128 bool send_accurate_current_files(JCR *jcr)
132 if (jcr->accurate==false || job_canceled(jcr) || jcr->JobLevel==L_FULL) {
135 POOLMEM *jobids = get_pool_memory(PM_FNAME);
136 db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, jobids);
139 free_pool_memory(jobids);
140 Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
143 Jmsg(jcr, M_INFO, 0, _("Sending Accurate information.\n"));
145 /* to be able to allocate the right size for htable */
146 POOLMEM *nb = get_pool_memory(PM_FNAME);
147 Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)",jobids);
148 db_sql_query(jcr->db, buf.c_str(), db_get_int_handler, nb);
149 jcr->file_bsock->fsend("accurate files=%s\n", nb);
151 db_get_file_list(jcr, jcr->db, jobids, accurate_list_handler, (void *)jcr);
153 free_pool_memory(jobids);
154 free_pool_memory(nb);
156 jcr->file_bsock->signal(BNET_EOD);
157 /* TODO: use response() ? */
163 * Do a backup of the specified FileSet
165 * Returns: false on failure
168 bool do_backup(JCR *jcr)
171 int tls_need = BNET_TLS_NONE;
177 /* Print Job Start message */
178 Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
179 edit_uint64(jcr->JobId, ed1), jcr->Job);
181 set_jcr_job_status(jcr, JS_Running);
182 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
183 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
184 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
189 * Open a message channel connection with the Storage
190 * daemon. This is to let him know that our client
191 * will be contacting him for a backup session.
194 Dmsg0(110, "Open connection with storage daemon\n");
195 set_jcr_job_status(jcr, JS_WaitSD);
197 * Start conversation with Storage daemon
199 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
203 * Now start a job with the Storage daemon
205 if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
210 * Start the job prior to starting the message thread below
211 * to avoid two threads from using the BSOCK structure at
214 if (!bnet_fsend(jcr->store_bsock, "run")) {
219 * Now start a Storage daemon message thread. Note,
220 * this thread is used to provide the catalog services
221 * for the backup job, including inserting the attributes
222 * into the catalog. See catalog_update() in catreq.c
224 if (!start_storage_daemon_message_thread(jcr)) {
227 Dmsg0(150, "Storage daemon connection OK\n");
229 set_jcr_job_status(jcr, JS_WaitFD);
230 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
234 set_jcr_job_status(jcr, JS_Running);
235 fd = jcr->file_bsock;
237 if (!send_include_list(jcr)) {
241 if (!send_exclude_list(jcr)) {
245 if (!send_level_command(jcr)) {
250 * send Storage daemon address to the File daemon
253 if (store->SDDport == 0) {
254 store->SDDport = store->SDport;
257 /* TLS Requirement */
258 if (store->tls_enable) {
259 if (store->tls_require) {
260 tls_need = BNET_TLS_REQUIRED;
262 tls_need = BNET_TLS_OK;
266 fd->fsend(storaddr, store->address, store->SDDport, tls_need);
267 if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
271 if (!send_runscripts_commands(jcr)) {
276 * We re-update the job start record so that the start
277 * time is set after the run before job. This avoids
278 * that any files created by the run before job will
279 * be saved twice. They will be backed up in the current
280 * job, but not in the next one unless they are changed.
281 * Without this, they will be backed up in this job and
282 * in the next job run because in that case, their date
283 * is after the start of this run.
285 jcr->start_time = time(NULL);
286 jcr->jr.StartTime = jcr->start_time;
287 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
288 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
292 * If backup is in accurate mode, we send the list of
295 if (!send_accurate_current_files(jcr)) {
299 /* Send backup command */
300 fd->fsend(backupcmd);
301 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
305 /* Pickup Job termination data */
306 stat = wait_for_job_termination(jcr);
307 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
308 if (stat == JS_Terminated) {
309 backup_cleanup(jcr, stat);
314 /* Come here only after starting SD thread */
316 set_jcr_job_status(jcr, JS_ErrorTerminated);
317 Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
319 wait_for_job_termination(jcr, FDConnectTimeout);
320 Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
326 * Here we wait for the File daemon to signal termination,
327 * then we wait for the Storage daemon. When both
328 * are done, we return the job status.
329 * Also used by restore.c
331 int wait_for_job_termination(JCR *jcr, int timeout)
334 BSOCK *fd = jcr->file_bsock;
336 uint32_t JobFiles, Errors;
337 uint64_t ReadBytes = 0;
338 uint64_t JobBytes = 0;
343 set_jcr_job_status(jcr, JS_Running);
347 tid = start_bsock_timer(fd, timeout); /* TODO: use user timeout */
349 /* Wait for Client to terminate */
350 while ((n = bget_dirmsg(fd)) >= 0) {
352 (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
353 &ReadBytes, &JobBytes, &Errors, &VSS, &Encrypt) == 7 ||
354 sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
355 &ReadBytes, &JobBytes, &Errors) == 5)) {
357 set_jcr_job_status(jcr, jcr->FDJobStatus);
358 Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
360 Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
363 if (job_canceled(jcr)) {
368 stop_bsock_timer(tid);
371 if (is_bnet_error(fd)) {
372 Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
373 job_type_to_str(jcr->JobType), fd->bstrerror());
375 fd->signal(BNET_TERMINATE); /* tell Client we are terminating */
378 /* Force cancel in SD if failing */
379 if (job_canceled(jcr) || !fd_ok) {
380 cancel_storage_daemon_job(jcr);
383 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
384 wait_for_storage_daemon_termination(jcr);
386 /* Return values from FD */
388 jcr->JobFiles = JobFiles;
389 jcr->Errors = Errors;
390 jcr->ReadBytes = ReadBytes;
391 jcr->JobBytes = JobBytes;
393 jcr->Encrypt = Encrypt;
395 Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
398 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
399 // jcr->JobStatus, jcr->SDJobStatus);
401 /* Return the first error status we find Dir, FD, or SD */
402 if (!fd_ok || is_bnet_error(fd)) { /* if fd not set, that use !fd_ok */
403 jcr->FDJobStatus = JS_ErrorTerminated;
405 if (jcr->JobStatus != JS_Terminated) {
406 return jcr->JobStatus;
408 if (jcr->FDJobStatus != JS_Terminated) {
409 return jcr->FDJobStatus;
411 return jcr->SDJobStatus;
415 * Release resources allocated during backup.
417 void backup_cleanup(JCR *jcr, int TermCode)
419 char sdt[50], edt[50], schedt[50];
420 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
421 char ec6[30], ec7[30], ec8[30], elapsed[50];
422 char term_code[100], fd_term_msg[100], sd_term_msg[100];
423 const char *term_msg;
424 int msg_type = M_INFO;
427 double kbps, compression;
430 Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
431 memset(&mr, 0, sizeof(mr));
432 memset(&cr, 0, sizeof(cr));
434 update_job_end(jcr, TermCode);
436 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
437 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
438 db_strerror(jcr->db));
439 set_jcr_job_status(jcr, JS_ErrorTerminated);
442 bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
443 if (!db_get_client_record(jcr, jcr->db, &cr)) {
444 Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
445 db_strerror(jcr->db));
448 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
449 if (!db_get_media_record(jcr, jcr->db, &mr)) {
450 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
451 mr.VolumeName, db_strerror(jcr->db));
452 set_jcr_job_status(jcr, JS_ErrorTerminated);
455 update_bootstrap_file(jcr);
457 switch (jcr->JobStatus) {
459 if (jcr->Errors || jcr->SDErrors) {
460 term_msg = _("Backup OK -- with warnings");
462 term_msg = _("Backup OK");
466 case JS_ErrorTerminated:
467 term_msg = _("*** Backup Error ***");
468 msg_type = M_ERROR; /* Generate error message */
469 if (jcr->store_bsock) {
470 jcr->store_bsock->signal(BNET_TERMINATE);
471 if (jcr->SD_msg_chan) {
472 pthread_cancel(jcr->SD_msg_chan);
477 term_msg = _("Backup Canceled");
478 if (jcr->store_bsock) {
479 jcr->store_bsock->signal(BNET_TERMINATE);
480 if (jcr->SD_msg_chan) {
481 pthread_cancel(jcr->SD_msg_chan);
486 term_msg = term_code;
487 sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
490 bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
491 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
492 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
493 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
497 kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
499 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
501 * Note, if the job has erred, most likely it did not write any
502 * tape, so suppress this "error" message since in that case
503 * it is normal. Or look at it the other way, only for a
504 * normal exit should we complain about this error.
506 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
507 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
509 jcr->VolumeName[0] = 0; /* none */
512 if (jcr->ReadBytes == 0) {
513 bstrncpy(compress, "None", sizeof(compress));
515 compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
516 if (compression < 0.5) {
517 bstrncpy(compress, "None", sizeof(compress));
519 bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
522 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
523 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
525 // bmicrosleep(15, 0); /* for debugging SIGHUP */
527 Jmsg(jcr, msg_type, 0, _("Bacula %s %s (%s): %s\n"
528 " Build OS: %s %s %s\n"
531 " Backup Level: %s%s\n"
532 " Client: \"%s\" %s\n"
533 " FileSet: \"%s\" %s\n"
534 " Pool: \"%s\" (From %s)\n"
535 " Catalog: \"%s\" (From %s)\n"
536 " Storage: \"%s\" (From %s)\n"
537 " Scheduled time: %s\n"
540 " Elapsed time: %s\n"
542 " FD Files Written: %s\n"
543 " SD Files Written: %s\n"
544 " FD Bytes Written: %s (%sB)\n"
545 " SD Bytes Written: %s (%sB)\n"
547 " Software Compression: %s\n"
551 " Volume name(s): %s\n"
552 " Volume Session Id: %d\n"
553 " Volume Session Time: %d\n"
554 " Last Volume Bytes: %s (%sB)\n"
555 " Non-fatal FD errors: %d\n"
557 " FD termination status: %s\n"
558 " SD termination status: %s\n"
559 " Termination: %s\n\n"),
560 my_name, VERSION, LSMDATE, edt,
561 HOST_OS, DISTNAME, DISTVER,
564 level_to_str(jcr->JobLevel), jcr->since,
565 jcr->client->name(), cr.Uname,
566 jcr->fileset->name(), jcr->FSCreateTime,
567 jcr->pool->name(), jcr->pool_source,
568 jcr->catalog->name(), jcr->catalog_source,
569 jcr->wstore->name(), jcr->wstore_source,
573 edit_utime(RunTime, elapsed, sizeof(elapsed)),
575 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
576 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
577 edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
578 edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
579 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
580 edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
583 jcr->VSS?_("yes"):_("no"),
584 jcr->Encrypt?_("yes"):_("no"),
585 jcr->accurate?_("yes"):_("no"),
589 edit_uint64_with_commas(mr.VolBytes, ec7),
590 edit_uint64_with_suffix(mr.VolBytes, ec8),
597 Dmsg0(100, "Leave backup_cleanup()\n");
600 void update_bootstrap_file(JCR *jcr)
602 /* Now update the bootstrap file if any */
603 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
604 jcr->job->WriteBootstrap) {
608 POOLMEM *fname = get_pool_memory(PM_FNAME);
609 fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
611 VOL_PARAMS *VolParams = NULL;
617 bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
618 fd = bpipe ? bpipe->wfd : NULL;
620 /* ***FIXME*** handle BASE */
621 fd = fopen(fname, jcr->JobLevel==L_FULL?"w+b":"a+b");
624 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
627 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
628 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
629 if (jcr->SDJobFiles != 0) {
630 set_jcr_job_status(jcr, JS_ErrorTerminated);
634 /* Start output with when and who wrote it */
635 bstrftimes(edt, sizeof(edt), time(NULL));
636 fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
637 level_to_str(jcr->JobLevel), jcr->since);
638 for (int i=0; i < VolCount; i++) {
639 /* Write the record */
640 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
641 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
642 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
643 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
644 fprintf(fd, "VolFile=%u-%u\n", VolParams[i].StartFile,
645 VolParams[i].EndFile);
646 fprintf(fd, "VolBlock=%u-%u\n", VolParams[i].StartBlock,
647 VolParams[i].EndBlock);
648 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
649 VolParams[i].LastIndex);
661 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
662 "%s: ERR=%s\n"), fname, be.bstrerror());
663 set_jcr_job_status(jcr, JS_ErrorTerminated);
665 free_pool_memory(fname);