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->previous_jr.JobId = input_jobid;
84 if (!db_get_job_record(jcr, jcr->db, &jcr->previous_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->previous_jr.JobStatus != 'T') {
90 Jmsg(jcr, M_FATAL, 0, _("Last Job %d did not terminate normally. JobStatus=%c\n"),
91 input_jobid, jcr->previous_jr.JobStatus);
94 Jmsg(jcr, M_INFO, 0, _("%s using JobId=%d Job=%s\n"),
95 Type, jcr->previous_jr.JobId, jcr->previous_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->previous_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->previous_jr.JobFiles;
143 jcr->ExpectedFiles = write_bsr_file(ua, rx);
144 if (jcr->ExpectedFiles == 0) {
152 jcr->needs_sd = true;
157 * Do a Migration, Archive, or Copy of a previous job
159 * Returns: false on failure
162 bool do_mac(JCR *jcr)
168 switch(jcr->JobType) {
184 /* Print Job Start message */
185 Jmsg(jcr, M_INFO, 0, _("Start %s JobId %s, Job=%s\n"),
186 Type, edit_uint64(jcr->JobId, ed1), jcr->Job);
188 set_jcr_job_status(jcr, JS_Running);
189 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
190 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
191 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
196 * Open a message channel connection with the Storage
197 * daemon. This is to let him know that our client
198 * will be contacting him for a backup session.
201 Dmsg0(110, "Open connection with storage daemon\n");
202 set_jcr_job_status(jcr, JS_WaitSD);
204 * Start conversation with Storage daemon
206 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
210 * Now start a job with the Storage daemon
212 if (!start_storage_daemon_job(jcr, jcr->storage, jcr->storage)) {
216 * Now start a Storage daemon message thread
218 if (!start_storage_daemon_message_thread(jcr)) {
221 Dmsg0(150, "Storage daemon connection OK\n");
223 /* Pickup Job termination data */
224 set_jcr_job_status(jcr, JS_Running);
226 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
227 wait_for_storage_daemon_termination(jcr);
229 if (jcr->JobStatus != JS_Terminated) {
230 stat = jcr->JobStatus;
232 stat = jcr->SDJobStatus;
234 if (stat == JS_Terminated) {
235 mac_cleanup(jcr, stat);
243 * Release resources allocated during backup.
245 void mac_cleanup(JCR *jcr, int TermCode)
247 char sdt[50], edt[50];
248 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
249 char term_code[100], fd_term_msg[100], sd_term_msg[100];
250 const char *term_msg;
253 double kbps, compression;
257 switch(jcr->JobType) {
272 Dmsg2(100, "Enter mac_cleanup %d %c\n", TermCode, TermCode);
273 dequeue_messages(jcr); /* display any queued messages */
274 memset(&mr, 0, sizeof(mr));
275 set_jcr_job_status(jcr, TermCode);
277 update_job_end_record(jcr); /* update database */
279 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
280 Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
281 db_strerror(jcr->db));
282 set_jcr_job_status(jcr, JS_ErrorTerminated);
285 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
286 if (!db_get_media_record(jcr, jcr->db, &mr)) {
287 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
288 mr.VolumeName, db_strerror(jcr->db));
289 set_jcr_job_status(jcr, JS_ErrorTerminated);
292 /* Now update the bootstrap file if any */
293 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
294 jcr->job->WriteBootstrap) {
298 char *fname = jcr->job->WriteBootstrap;
299 VOL_PARAMS *VolParams = NULL;
305 bpipe = open_bpipe(fname, 0, "w");
306 fd = bpipe ? bpipe->wfd : NULL;
308 /* ***FIXME*** handle BASE */
309 fd = fopen(fname, jcr->JobLevel==L_FULL?"w+":"a+");
312 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
315 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
316 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
317 if (jcr->SDJobFiles != 0) {
318 set_jcr_job_status(jcr, JS_ErrorTerminated);
322 for (int i=0; i < VolCount; i++) {
323 /* Write the record */
324 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
325 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
326 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
327 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
328 fprintf(fd, "VolFile=%u-%u\n", VolParams[i].StartFile,
329 VolParams[i].EndFile);
330 fprintf(fd, "VolBlock=%u-%u\n", VolParams[i].StartBlock,
331 VolParams[i].EndBlock);
332 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
333 VolParams[i].LastIndex);
345 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
346 "%s: ERR=%s\n"), fname, be.strerror());
347 set_jcr_job_status(jcr, JS_ErrorTerminated);
351 msg_type = M_INFO; /* by default INFO message */
352 switch (jcr->JobStatus) {
354 if (jcr->Errors || jcr->SDErrors) {
355 term_msg = _("Backup OK -- with warnings");
357 term_msg = _("Backup OK");
361 case JS_ErrorTerminated:
362 term_msg = _("*** Backup Error ***");
363 msg_type = M_ERROR; /* Generate error message */
364 if (jcr->store_bsock) {
365 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
366 if (jcr->SD_msg_chan) {
367 pthread_cancel(jcr->SD_msg_chan);
372 term_msg = _("Backup Canceled");
373 if (jcr->store_bsock) {
374 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
375 if (jcr->SD_msg_chan) {
376 pthread_cancel(jcr->SD_msg_chan);
381 term_msg = term_code;
382 sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
385 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
386 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
387 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
391 kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
393 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
395 * Note, if the job has erred, most likely it did not write any
396 * tape, so suppress this "error" message since in that case
397 * it is normal. Or look at it the other way, only for a
398 * normal exit should we complain about this error.
400 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
401 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
403 jcr->VolumeName[0] = 0; /* none */
406 if (jcr->ReadBytes == 0) {
407 bstrncpy(compress, "None", sizeof(compress));
409 compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
410 if (compression < 0.5) {
411 bstrncpy(compress, "None", sizeof(compress));
413 bsnprintf(compress, sizeof(compress), "%.1f %%", (float)compression);
416 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
417 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
419 // bmicrosleep(15, 0); /* for debugging SIGHUP */
421 Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
424 " Backup Level: %s%s\n"
426 " FileSet: \"%s\" %s\n"
430 " FD Files Written: %s\n"
431 " SD Files Written: %s\n"
432 " FD Bytes Written: %s\n"
433 " SD Bytes Written: %s\n"
435 " Software Compression: %s\n"
436 " Volume name(s): %s\n"
437 " Volume Session Id: %d\n"
438 " Volume Session Time: %d\n"
439 " Last Volume Bytes: %s\n"
440 " Non-fatal FD errors: %d\n"
442 " FD termination status: %s\n"
443 " SD termination status: %s\n"
444 " Termination: %s\n\n"),
450 level_to_str(jcr->JobLevel), jcr->since,
451 jcr->client->hdr.name,
452 jcr->fileset->hdr.name, jcr->FSCreateTime,
456 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
457 edit_uint64_with_commas(jcr->SDJobFiles, ec4),
458 edit_uint64_with_commas(jcr->jr.JobBytes, ec2),
459 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
465 edit_uint64_with_commas(mr.VolBytes, ec3),
472 Dmsg0(100, "Leave mac_cleanup()\n");