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 File daemon and pass him commands
13 * When the File daemon finishes the job, update the DB.
18 Copyright (C) 2004-2005 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.
37 * Called here before the job is run to do the job
40 bool do_mac_init(JCR *jcr)
50 switch(jcr->JobType) {
65 if (!get_or_create_fileset_record(jcr)) {
70 * Find JobId of last job that ran.
72 memcpy(&jr, &jcr->jr, sizeof(jr));
73 Name = jcr->job->migration_job->hdr.name;
74 Dmsg1(100, "find last jobid for: %s\n", NPRT(Name));
75 if (!db_find_last_jobid(jcr, jcr->db, Name, &jr)) {
76 Jmsg(jcr, M_FATAL, 0, _(
77 _("Unable to find JobId of previous Job for this client.\n")));
80 input_jobid = jr.JobId;
81 Dmsg1(100, "Last jobid=%d\n", input_jobid);
83 jcr->target_jr.JobId = input_jobid;
84 if (!db_get_job_record(jcr, jcr->db, &jcr->target_jr)) {
85 Jmsg(jcr, M_FATAL, 0, _("Could not get job record for previous Job. ERR=%s"),
86 db_strerror(jcr->db));
89 if (jcr->target_jr.JobStatus != 'T') {
90 Jmsg(jcr, M_FATAL, 0, _("Last Job %d did not terminate normally. JobStatus=%c\n"),
91 input_jobid, jcr->target_jr.JobStatus);
94 Jmsg(jcr, M_INFO, 0, _("%s using JobId=%d Job=%s\n"),
95 Type, jcr->target_jr.JobId, jcr->target_jr.Job);
99 * Get the Pool record -- first apply any level defined pools
101 switch (jcr->JobLevel) {
103 if (jcr->full_pool) {
104 jcr->pool = jcr->full_pool;
109 jcr->pool = jcr->inc_pool;
114 jcr->pool = jcr->dif_pool;
118 memset(&pr, 0, sizeof(pr));
119 bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
121 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
122 /* Try to create the pool */
123 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
124 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
125 db_strerror(jcr->db));
128 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
131 jcr->PoolId = pr.PoolId; /****FIXME**** this can go away */
132 jcr->jr.PoolId = pr.PoolId;
134 memset(&rx, 0, sizeof(rx));
137 rx.bsr->JobId = jcr->target_jr.JobId;
138 ua = new_ua_context(jcr);
139 complete_bsr(ua, rx.bsr);
140 rx.bsr->fi = new_findex();
141 rx.bsr->fi->findex = 1;
142 rx.bsr->fi->findex2 = jcr->target_jr.JobFiles;
143 jcr->ExpectedFiles = write_bsr_file(ua, rx);
144 if (jcr->ExpectedFiles == 0) {
149 if (jcr->RestoreBootstrap) {
150 free(jcr->RestoreBootstrap);
152 POOLMEM *fname = get_pool_memory(PM_MESSAGE);
153 make_unique_restore_filename(ua, &fname);
154 jcr->RestoreBootstrap = bstrdup(fname);
157 free_pool_memory(fname);
159 jcr->needs_sd = true;
164 * Do a Migration, Archive, or Copy of a previous job
166 * Returns: false on failure
169 bool do_mac(JCR *jcr)
175 switch(jcr->JobType) {
191 /* Print Job Start message */
192 Jmsg(jcr, M_INFO, 0, _("Start %s JobId %s, Job=%s\n"),
193 Type, edit_uint64(jcr->JobId, ed1), jcr->Job);
195 set_jcr_job_status(jcr, JS_Running);
196 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
197 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
198 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
203 * Open a message channel connection with the Storage
204 * daemon. This is to let him know that our client
205 * will be contacting him for a backup session.
208 Dmsg0(110, "Open connection with storage daemon\n");
209 set_jcr_job_status(jcr, JS_WaitSD);
211 * Start conversation with Storage daemon
213 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
217 * Now start a job with the Storage daemon
219 if (!start_storage_daemon_job(jcr, jcr->storage, jcr->storage)) {
223 * Now start a Storage daemon message thread
225 if (!start_storage_daemon_message_thread(jcr)) {
228 Dmsg0(150, "Storage daemon connection OK\n");
230 /* Pickup Job termination data */
231 set_jcr_job_status(jcr, JS_Running);
233 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
234 wait_for_storage_daemon_termination(jcr);
236 if (jcr->JobStatus != JS_Terminated) {
237 stat = jcr->JobStatus;
239 stat = jcr->SDJobStatus;
241 if (stat == JS_Terminated) {
242 mac_cleanup(jcr, stat);
250 * Release resources allocated during backup.
252 void mac_cleanup(JCR *jcr, int TermCode)
254 char sdt[50], edt[50];
255 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
256 char term_code[100], fd_term_msg[100], sd_term_msg[100];
257 const char *term_msg;
260 double kbps, compression;
264 switch(jcr->JobType) {
279 Dmsg2(100, "Enter mac_cleanup %d %c\n", TermCode, TermCode);
280 dequeue_messages(jcr); /* display any queued messages */
281 memset(&mr, 0, sizeof(mr));
282 set_jcr_job_status(jcr, TermCode);
284 update_job_end_record(jcr); /* update database */
286 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
287 Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
288 db_strerror(jcr->db));
289 set_jcr_job_status(jcr, JS_ErrorTerminated);
292 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
293 if (!db_get_media_record(jcr, jcr->db, &mr)) {
294 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
295 mr.VolumeName, db_strerror(jcr->db));
296 set_jcr_job_status(jcr, JS_ErrorTerminated);
299 /* Now update the bootstrap file if any */
300 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
301 jcr->job->WriteBootstrap) {
305 char *fname = jcr->job->WriteBootstrap;
306 VOL_PARAMS *VolParams = NULL;
312 bpipe = open_bpipe(fname, 0, "w");
313 fd = bpipe ? bpipe->wfd : NULL;
315 /* ***FIXME*** handle BASE */
316 fd = fopen(fname, jcr->JobLevel==L_FULL?"w+":"a+");
319 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
322 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
323 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
324 if (jcr->SDJobFiles != 0) {
325 set_jcr_job_status(jcr, JS_ErrorTerminated);
329 for (int i=0; i < VolCount; i++) {
330 /* Write the record */
331 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
332 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
333 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
334 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
335 fprintf(fd, "VolFile=%u-%u\n", VolParams[i].StartFile,
336 VolParams[i].EndFile);
337 fprintf(fd, "VolBlock=%u-%u\n", VolParams[i].StartBlock,
338 VolParams[i].EndBlock);
339 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
340 VolParams[i].LastIndex);
352 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
353 "%s: ERR=%s\n"), fname, be.strerror());
354 set_jcr_job_status(jcr, JS_ErrorTerminated);
358 msg_type = M_INFO; /* by default INFO message */
359 switch (jcr->JobStatus) {
361 if (jcr->Errors || jcr->SDErrors) {
362 term_msg = _("Backup OK -- with warnings");
364 term_msg = _("Backup OK");
368 case JS_ErrorTerminated:
369 term_msg = _("*** Backup Error ***");
370 msg_type = M_ERROR; /* Generate error message */
371 if (jcr->store_bsock) {
372 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
373 if (jcr->SD_msg_chan) {
374 pthread_cancel(jcr->SD_msg_chan);
379 term_msg = _("Backup Canceled");
380 if (jcr->store_bsock) {
381 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
382 if (jcr->SD_msg_chan) {
383 pthread_cancel(jcr->SD_msg_chan);
388 term_msg = term_code;
389 sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
392 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
393 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
394 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
398 kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
400 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
402 * Note, if the job has erred, most likely it did not write any
403 * tape, so suppress this "error" message since in that case
404 * it is normal. Or look at it the other way, only for a
405 * normal exit should we complain about this error.
407 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
408 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
410 jcr->VolumeName[0] = 0; /* none */
413 if (jcr->ReadBytes == 0) {
414 bstrncpy(compress, "None", sizeof(compress));
416 compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
417 if (compression < 0.5) {
418 bstrncpy(compress, "None", sizeof(compress));
420 bsnprintf(compress, sizeof(compress), "%.1f %%", (float)compression);
423 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
424 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
426 // bmicrosleep(15, 0); /* for debugging SIGHUP */
428 Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
431 " Backup Level: %s%s\n"
433 " FileSet: \"%s\" %s\n"
437 " FD Files Written: %s\n"
438 " SD Files Written: %s\n"
439 " FD Bytes Written: %s\n"
440 " SD Bytes Written: %s\n"
442 " Software Compression: %s\n"
443 " Volume name(s): %s\n"
444 " Volume Session Id: %d\n"
445 " Volume Session Time: %d\n"
446 " Last Volume Bytes: %s\n"
447 " Non-fatal FD errors: %d\n"
449 " FD termination status: %s\n"
450 " SD termination status: %s\n"
451 " Termination: %s\n\n"),
457 level_to_str(jcr->JobLevel), jcr->since,
458 jcr->client->hdr.name,
459 jcr->fileset->hdr.name, jcr->FSCreateTime,
463 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
464 edit_uint64_with_commas(jcr->SDJobFiles, ec4),
465 edit_uint64_with_commas(jcr->jr.JobBytes, ec2),
466 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
472 edit_uint64_with_commas(mr.VolBytes, ec3),
479 Dmsg0(100, "Leave mac_cleanup()\n");