2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 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 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
82 if (jcr->jr.PoolId == 0) {
86 /* If pool storage specified, use it instead of job storage */
87 copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
90 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
94 create_clones(jcr); /* run any clone jobs */
100 * Do a backup of the specified FileSet
102 * Returns: false on failure
105 bool do_backup(JCR *jcr)
108 int tls_need = BNET_TLS_NONE;
114 /* Print Job Start message */
115 Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
116 edit_uint64(jcr->JobId, ed1), jcr->Job);
118 set_jcr_job_status(jcr, JS_Running);
119 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
120 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
121 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
126 * Open a message channel connection with the Storage
127 * daemon. This is to let him know that our client
128 * will be contacting him for a backup session.
131 Dmsg0(110, "Open connection with storage daemon\n");
132 set_jcr_job_status(jcr, JS_WaitSD);
134 * Start conversation with Storage daemon
136 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
140 * Now start a job with the Storage daemon
142 if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
147 * Start the job prior to starting the message thread below
148 * to avoid two threads from using the BSOCK structure at
151 if (!bnet_fsend(jcr->store_bsock, "run")) {
156 * Now start a Storage daemon message thread. Note,
157 * this thread is used to provide the catalog services
158 * for the backup job, including inserting the attributes
159 * into the catalog. See catalog_update() in catreq.c
161 if (!start_storage_daemon_message_thread(jcr)) {
164 Dmsg0(150, "Storage daemon connection OK\n");
166 set_jcr_job_status(jcr, JS_WaitFD);
167 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
168 cancel_storage_daemon_job(jcr);
172 set_jcr_job_status(jcr, JS_Running);
173 fd = jcr->file_bsock;
175 if (!send_include_list(jcr)) {
179 if (!send_exclude_list(jcr)) {
183 if (!send_level_command(jcr)) {
188 * send Storage daemon address to the File daemon
191 if (store->SDDport == 0) {
192 store->SDDport = store->SDport;
195 /* TLS Requirement */
196 if (store->tls_enable) {
197 if (store->tls_require) {
198 tls_need = BNET_TLS_REQUIRED;
200 tls_need = BNET_TLS_OK;
204 bnet_fsend(fd, storaddr, store->address, store->SDDport, tls_need);
205 if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
209 if (!send_runscripts_commands(jcr)) {
214 * We re-update the job start record so that the start
215 * time is set after the run before job. This avoids
216 * that any files created by the run before job will
217 * be saved twice. They will be backed up in the current
218 * job, but not in the next one unless they are changed.
219 * Without this, they will be backed up in this job and
220 * in the next job run because in that case, their date
221 * is after the start of this run.
223 jcr->start_time = time(NULL);
224 jcr->jr.StartTime = jcr->start_time;
225 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
226 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
229 /* Send backup command */
230 bnet_fsend(fd, backupcmd);
231 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
235 /* Pickup Job termination data */
236 stat = wait_for_job_termination(jcr);
237 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
238 if (stat == JS_Terminated) {
239 backup_cleanup(jcr, stat);
244 /* Come here only after starting SD thread */
246 set_jcr_job_status(jcr, JS_ErrorTerminated);
247 Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
249 if (jcr->store_bsock) {
250 jcr->store_bsock->fsend("cancel Job=%s\n", jcr->Job);
252 wait_for_storage_daemon_termination(jcr);
253 Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
259 * Here we wait for the File daemon to signal termination,
260 * then we wait for the Storage daemon. When both
261 * are done, we return the job status.
262 * Also used by restore.c
264 int wait_for_job_termination(JCR *jcr)
267 BSOCK *fd = jcr->file_bsock;
269 uint32_t JobFiles, Errors;
270 uint64_t ReadBytes = 0;
271 uint64_t JobBytes = 0;
275 set_jcr_job_status(jcr, JS_Running);
276 /* Wait for Client to terminate */
277 while ((n = bget_dirmsg(fd)) >= 0) {
279 (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
280 &ReadBytes, &JobBytes, &Errors, &VSS, &Encrypt) == 7 ||
281 sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
282 &ReadBytes, &JobBytes, &Errors) == 5)) {
284 set_jcr_job_status(jcr, jcr->FDJobStatus);
285 Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
287 Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
290 if (job_canceled(jcr)) {
295 if (is_bnet_error(fd)) {
296 Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
297 job_type_to_str(jcr->JobType), fd->bstrerror());
299 bnet_sig(fd, BNET_TERMINATE); /* tell Client we are terminating */
301 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
302 wait_for_storage_daemon_termination(jcr);
305 /* Return values from FD */
307 jcr->JobFiles = JobFiles;
308 jcr->Errors = Errors;
309 jcr->ReadBytes = ReadBytes;
310 jcr->JobBytes = JobBytes;
312 jcr->Encrypt = Encrypt;
314 Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
317 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
318 // jcr->JobStatus, jcr->SDJobStatus);
320 /* Return the first error status we find Dir, FD, or SD */
321 if (!fd_ok || is_bnet_error(fd)) {
322 jcr->FDJobStatus = JS_ErrorTerminated;
324 if (jcr->JobStatus != JS_Terminated) {
325 return jcr->JobStatus;
327 if (jcr->FDJobStatus != JS_Terminated) {
328 return jcr->FDJobStatus;
330 return jcr->SDJobStatus;
334 * Release resources allocated during backup.
336 void backup_cleanup(JCR *jcr, int TermCode)
338 char sdt[50], edt[50], schedt[50];
339 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
340 char ec6[30], ec7[30], ec8[30], elapsed[50];
341 char term_code[100], fd_term_msg[100], sd_term_msg[100];
342 const char *term_msg;
343 int msg_type = M_INFO;
346 double kbps, compression;
349 Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
350 memset(&mr, 0, sizeof(mr));
351 memset(&cr, 0, sizeof(cr));
353 update_job_end(jcr, TermCode);
355 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
356 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
357 db_strerror(jcr->db));
358 set_jcr_job_status(jcr, JS_ErrorTerminated);
361 bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
362 if (!db_get_client_record(jcr, jcr->db, &cr)) {
363 Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
364 db_strerror(jcr->db));
367 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
368 if (!db_get_media_record(jcr, jcr->db, &mr)) {
369 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
370 mr.VolumeName, db_strerror(jcr->db));
371 set_jcr_job_status(jcr, JS_ErrorTerminated);
374 update_bootstrap_file(jcr);
376 switch (jcr->JobStatus) {
378 if (jcr->Errors || jcr->SDErrors) {
379 term_msg = _("Backup OK -- with warnings");
381 term_msg = _("Backup OK");
385 case JS_ErrorTerminated:
386 term_msg = _("*** Backup Error ***");
387 msg_type = M_ERROR; /* Generate error message */
388 if (jcr->store_bsock) {
389 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
390 if (jcr->SD_msg_chan) {
391 pthread_cancel(jcr->SD_msg_chan);
396 term_msg = _("Backup Canceled");
397 if (jcr->store_bsock) {
398 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
399 if (jcr->SD_msg_chan) {
400 pthread_cancel(jcr->SD_msg_chan);
405 term_msg = term_code;
406 sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
409 bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
410 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
411 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
412 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
416 kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
418 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
420 * Note, if the job has erred, most likely it did not write any
421 * tape, so suppress this "error" message since in that case
422 * it is normal. Or look at it the other way, only for a
423 * normal exit should we complain about this error.
425 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
426 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
428 jcr->VolumeName[0] = 0; /* none */
431 if (jcr->ReadBytes == 0) {
432 bstrncpy(compress, "None", sizeof(compress));
434 compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
435 if (compression < 0.5) {
436 bstrncpy(compress, "None", sizeof(compress));
438 bsnprintf(compress, sizeof(compress), "%.1f %%", (float)compression);
441 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
442 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
444 // bmicrosleep(15, 0); /* for debugging SIGHUP */
446 Jmsg(jcr, msg_type, 0, _("Bacula %s %s (%s): %s\n"
447 " Build OS: %s %s %s\n"
450 " Backup Level: %s%s\n"
451 " Client: \"%s\" %s\n"
452 " FileSet: \"%s\" %s\n"
453 " Pool: \"%s\" (From %s)\n"
454 " Storage: \"%s\" (From %s)\n"
455 " Scheduled time: %s\n"
458 " Elapsed time: %s\n"
460 " FD Files Written: %s\n"
461 " SD Files Written: %s\n"
462 " FD Bytes Written: %s (%sB)\n"
463 " SD Bytes Written: %s (%sB)\n"
465 " Software Compression: %s\n"
468 " Volume name(s): %s\n"
469 " Volume Session Id: %d\n"
470 " Volume Session Time: %d\n"
471 " Last Volume Bytes: %s (%sB)\n"
472 " Non-fatal FD errors: %d\n"
474 " FD termination status: %s\n"
475 " SD termination status: %s\n"
476 " Termination: %s\n\n"),
477 my_name, VERSION, LSMDATE, edt,
478 HOST_OS, DISTNAME, DISTVER,
481 level_to_str(jcr->JobLevel), jcr->since,
482 jcr->client->name(), cr.Uname,
483 jcr->fileset->name(), jcr->FSCreateTime,
484 jcr->pool->name(), jcr->pool_source,
485 jcr->wstore->name(), jcr->wstore_source,
489 edit_utime(RunTime, elapsed, sizeof(elapsed)),
491 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
492 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
493 edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
494 edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
495 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
496 edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
500 jcr->Encrypt?"yes":"no",
504 edit_uint64_with_commas(mr.VolBytes, ec7),
505 edit_uint64_with_suffix(mr.VolBytes, ec8),
512 Dmsg0(100, "Leave backup_cleanup()\n");
515 void update_bootstrap_file(JCR *jcr)
517 /* Now update the bootstrap file if any */
518 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
519 jcr->job->WriteBootstrap) {
523 POOLMEM *fname = get_pool_memory(PM_FNAME);
524 fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
526 VOL_PARAMS *VolParams = NULL;
532 bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
533 fd = bpipe ? bpipe->wfd : NULL;
535 /* ***FIXME*** handle BASE */
536 fd = fopen(fname, jcr->JobLevel==L_FULL?"w+b":"a+b");
539 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
542 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
543 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
544 if (jcr->SDJobFiles != 0) {
545 set_jcr_job_status(jcr, JS_ErrorTerminated);
549 /* Start output with when and who wrote it */
550 bstrftimes(edt, sizeof(edt), time(NULL));
551 fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
552 level_to_str(jcr->JobLevel), jcr->since);
553 for (int i=0; i < VolCount; i++) {
554 /* Write the record */
555 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
556 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
557 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
558 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
559 fprintf(fd, "VolFile=%u-%u\n", VolParams[i].StartFile,
560 VolParams[i].EndFile);
561 fprintf(fd, "VolBlock=%u-%u\n", VolParams[i].StartBlock,
562 VolParams[i].EndBlock);
563 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
564 VolParams[i].LastIndex);
576 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
577 "%s: ERR=%s\n"), fname, be.bstrerror());
578 set_jcr_job_status(jcr, JS_ErrorTerminated);
580 free_pool_memory(fname);