3 * Bacula Director -- mac.c -- responsible for doing
4 * migration, archive, and copy jobs.
6 * Kern Sibbald, September MMIV
8 * Basic tasks done here:
9 * Open DB and create records for this job.
10 * Open Message Channel with Storage daemon to tell him a job will be starting.
11 * Open connection with Storage daemon and pass him commands
13 * When the Storage daemon finishes the job, update the DB.
18 Copyright (C) 2004-2006 Kern Sibbald
20 This program is free software; you can redistribute it and/or
21 modify it under the terms of the GNU General Public License
22 version 2 as amended with additional clauses defined in the
23 file LICENSE in the main source directory.
25 This program is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 the file LICENSE for additional details.
36 static char OKbootstrap[] = "3000 OK bootstrap\n";
39 * Called here before the job is run to do the job
42 bool do_mac_init(JCR *jcr)
52 switch(jcr->JobType) {
67 if (!get_or_create_fileset_record(jcr)) {
72 * Find JobId of last job that ran.
74 memcpy(&jr, &jcr->jr, sizeof(jr));
75 Name = jcr->job->migration_job->hdr.name;
76 Dmsg1(100, "find last jobid for: %s\n", NPRT(Name));
77 if (!db_find_last_jobid(jcr, jcr->db, Name, &jr)) {
78 Jmsg(jcr, M_FATAL, 0, _(
79 _("Unable to find JobId of previous Job for this client.\n")));
82 input_jobid = jr.JobId;
83 Dmsg1(100, "Last jobid=%d\n", input_jobid);
85 jcr->target_jr.JobId = input_jobid;
86 if (!db_get_job_record(jcr, jcr->db, &jcr->target_jr)) {
87 Jmsg(jcr, M_FATAL, 0, _("Could not get job record for previous Job. ERR=%s"),
88 db_strerror(jcr->db));
91 if (jcr->target_jr.JobStatus != 'T') {
92 Jmsg(jcr, M_FATAL, 0, _("Last Job %d did not terminate normally. JobStatus=%c\n"),
93 input_jobid, jcr->target_jr.JobStatus);
96 Jmsg(jcr, M_INFO, 0, _("%s using JobId=%d Job=%s\n"),
97 Type, jcr->target_jr.JobId, jcr->target_jr.Job);
101 * Get the Pool record -- first apply any level defined pools
103 switch (jcr->target_jr.JobLevel) {
105 if (jcr->full_pool) {
106 jcr->pool = jcr->full_pool;
111 jcr->pool = jcr->inc_pool;
116 jcr->pool = jcr->dif_pool;
120 memset(&pr, 0, sizeof(pr));
121 bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
123 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
124 /* Try to create the pool */
125 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
126 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
127 db_strerror(jcr->db));
130 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
133 jcr->PoolId = pr.PoolId; /****FIXME**** this can go away */
134 jcr->jr.PoolId = pr.PoolId;
136 memset(&rx, 0, sizeof(rx));
139 rx.bsr->JobId = jcr->target_jr.JobId;
140 ua = new_ua_context(jcr);
141 complete_bsr(ua, rx.bsr);
142 rx.bsr->fi = new_findex();
143 rx.bsr->fi->findex = 1;
144 rx.bsr->fi->findex2 = jcr->target_jr.JobFiles;
145 jcr->ExpectedFiles = write_bsr_file(ua, rx);
146 if (jcr->ExpectedFiles == 0) {
151 if (jcr->RestoreBootstrap) {
152 free(jcr->RestoreBootstrap);
154 POOLMEM *fname = get_pool_memory(PM_MESSAGE);
155 make_unique_restore_filename(ua, &fname);
156 jcr->RestoreBootstrap = bstrdup(fname);
159 free_pool_memory(fname);
161 jcr->needs_sd = true;
166 * Do a Migration, Archive, or Copy of a previous job
168 * Returns: false on failure
171 bool do_mac(JCR *jcr)
178 switch(jcr->JobType) {
194 /* Print Job Start message */
195 Jmsg(jcr, M_INFO, 0, _("Start %s JobId %s, Job=%s\n"),
196 Type, edit_uint64(jcr->JobId, ed1), jcr->Job);
198 set_jcr_job_status(jcr, JS_Running);
199 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
200 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
201 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
206 * Open a message channel connection with the Storage
207 * daemon. This is to let him know that our client
208 * will be contacting him for a backup session.
211 Dmsg0(110, "Open connection with storage daemon\n");
212 set_jcr_job_status(jcr, JS_WaitSD);
214 * Start conversation with Storage daemon
216 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
219 sd = jcr->store_bsock;
221 * Now start a job with the Storage daemon
223 if (!start_storage_daemon_job(jcr, jcr->storage, NULL)) {
226 Dmsg0(150, "Storage daemon connection OK\n");
228 if (!send_bootstrap_file(jcr, sd) ||
229 !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
235 * Now start a Storage daemon message thread
237 if (!start_storage_daemon_message_thread(jcr)) {
241 if (!bnet_fsend(sd, "run")) {
245 /* Pickup Job termination data */
246 set_jcr_job_status(jcr, JS_Running);
248 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
249 wait_for_storage_daemon_termination(jcr);
251 if (jcr->JobStatus != JS_Terminated) {
252 stat = jcr->JobStatus;
254 stat = jcr->SDJobStatus;
256 if (stat == JS_Terminated) {
257 mac_cleanup(jcr, stat);
265 * Release resources allocated during backup.
267 void mac_cleanup(JCR *jcr, int TermCode)
269 char sdt[50], edt[50];
270 char ec3[30], ec4[30], ec5[30], compress[50];
271 char term_code[100], sd_term_msg[100];
272 const char *term_msg;
275 double kbps, compression;
279 switch(jcr->JobType) {
294 Dmsg2(100, "Enter mac_cleanup %d %c\n", TermCode, TermCode);
295 dequeue_messages(jcr); /* display any queued messages */
296 memset(&mr, 0, sizeof(mr));
297 set_jcr_job_status(jcr, TermCode);
299 update_job_end_record(jcr); /* update database */
301 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
302 Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
303 db_strerror(jcr->db));
304 set_jcr_job_status(jcr, JS_ErrorTerminated);
307 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
308 if (!db_get_media_record(jcr, jcr->db, &mr)) {
309 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
310 mr.VolumeName, db_strerror(jcr->db));
311 set_jcr_job_status(jcr, JS_ErrorTerminated);
314 /* Now update the bootstrap file if any */
315 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
316 jcr->job->WriteBootstrap) {
320 char *fname = jcr->job->WriteBootstrap;
321 VOL_PARAMS *VolParams = NULL;
327 bpipe = open_bpipe(fname, 0, "w");
328 fd = bpipe ? bpipe->wfd : NULL;
330 /* ***FIXME*** handle BASE */
331 fd = fopen(fname, jcr->JobLevel==L_FULL?"w+":"a+");
334 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
337 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
338 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
339 if (jcr->SDJobFiles != 0) {
340 set_jcr_job_status(jcr, JS_ErrorTerminated);
344 for (int i=0; i < VolCount; i++) {
345 /* Write the record */
346 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
347 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
348 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
349 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
350 fprintf(fd, "VolFile=%u-%u\n", VolParams[i].StartFile,
351 VolParams[i].EndFile);
352 fprintf(fd, "VolBlock=%u-%u\n", VolParams[i].StartBlock,
353 VolParams[i].EndBlock);
354 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
355 VolParams[i].LastIndex);
367 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
368 "%s: ERR=%s\n"), fname, be.strerror());
369 set_jcr_job_status(jcr, JS_ErrorTerminated);
373 msg_type = M_INFO; /* by default INFO message */
374 switch (jcr->JobStatus) {
376 if (jcr->Errors || jcr->SDErrors) {
377 term_msg = _("%s OK -- with warnings");
379 term_msg = _("%s OK");
383 case JS_ErrorTerminated:
384 term_msg = _("*** %s Error ***");
385 msg_type = M_ERROR; /* Generate error message */
386 if (jcr->store_bsock) {
387 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
388 if (jcr->SD_msg_chan) {
389 pthread_cancel(jcr->SD_msg_chan);
394 term_msg = _("%s Canceled");
395 if (jcr->store_bsock) {
396 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
397 if (jcr->SD_msg_chan) {
398 pthread_cancel(jcr->SD_msg_chan);
403 term_msg = _("Inappropriate %s term code");
406 bsnprintf(term_code, sizeof(term_code), term_msg, Type);
407 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
408 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
409 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
413 kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
415 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
417 * Note, if the job has erred, most likely it did not write any
418 * tape, so suppress this "error" message since in that case
419 * it is normal. Or look at it the other way, only for a
420 * normal exit should we complain about this error.
422 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
423 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
425 jcr->VolumeName[0] = 0; /* none */
428 if (jcr->ReadBytes == 0) {
429 bstrncpy(compress, "None", sizeof(compress));
431 compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
432 if (compression < 0.5) {
433 bstrncpy(compress, "None", sizeof(compress));
435 bsnprintf(compress, sizeof(compress), "%.1f %%", (float)compression);
438 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
440 // bmicrosleep(15, 0); /* for debugging SIGHUP */
442 Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
445 " Backup Level: %s%s\n"
447 " FileSet: \"%s\" %s\n"
451 " SD Files Written: %s\n"
452 " SD Bytes Written: %s\n"
454 " Software Compression: %s\n"
455 " Volume name(s): %s\n"
456 " Volume Session Id: %d\n"
457 " Volume Session Time: %d\n"
458 " Last Volume Bytes: %s\n"
460 " SD termination status: %s\n"
461 " Termination: %s\n\n"),
467 level_to_str(jcr->JobLevel), jcr->since,
468 jcr->client->hdr.name,
469 jcr->fileset->hdr.name, jcr->FSCreateTime,
473 edit_uint64_with_commas(jcr->SDJobFiles, ec4),
474 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
480 edit_uint64_with_commas(mr.VolBytes, ec3),
485 Dmsg0(100, "Leave mac_cleanup()\n");