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)
179 switch(jcr->JobType) {
195 Dmsg4(100, "Target: Name=%s JobId=%d Type=%c Level=%c\n",
196 jcr->target_jr.Name, jcr->target_jr.JobId,
197 jcr->target_jr.JobType, jcr->target_jr.JobLevel);
199 Dmsg4(100, "Current: Name=%s JobId=%d Type=%c Level=%c\n",
200 jcr->jr.Name, jcr->jr.JobId,
201 jcr->jr.JobType, jcr->jr.JobLevel);
204 job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
205 tjob = (JOB *)GetResWithName(R_JOB, jcr->target_jr.Name);
211 tjcr = jcr->target_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
212 set_jcr_defaults(tjcr, tjob);
214 if (!setup_job(tjcr)) {
217 tjcr->PoolId = jcr->PoolId;
218 tjcr->jr.PoolId = jcr->jr.PoolId;
219 tjcr->jr.FileSetId = jcr->jr.FileSetId;
221 /* Print Job Start message */
222 Jmsg(jcr, M_INFO, 0, _("Start %s JobId %s, Job=%s\n"),
223 Type, edit_uint64(jcr->JobId, ed1), jcr->Job);
225 set_jcr_job_status(jcr, JS_Running);
226 set_jcr_job_status(jcr, JS_Running);
227 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
228 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
229 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
233 if (!db_update_job_start_record(tjcr, tjcr->db, &tjcr->jr)) {
234 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(tjcr->db));
240 * Open a message channel connection with the Storage
241 * daemon. This is to let him know that our client
242 * will be contacting him for a backup session.
245 Dmsg0(110, "Open connection with storage daemon\n");
246 set_jcr_job_status(jcr, JS_WaitSD);
247 set_jcr_job_status(tjcr, JS_WaitSD);
249 * Start conversation with Storage daemon
251 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
254 sd = jcr->store_bsock;
256 * Now start a job with the Storage daemon
258 if (!start_storage_daemon_job(jcr, tjcr->storage, jcr->storage)) {
261 Dmsg0(150, "Storage daemon connection OK\n");
263 if (!send_bootstrap_file(jcr, sd) ||
264 !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
270 * Now start a Storage daemon message thread
272 if (!start_storage_daemon_message_thread(jcr)) {
276 if (!bnet_fsend(sd, "run")) {
280 set_jcr_job_status(jcr, JS_Running);
281 set_jcr_job_status(tjcr, JS_Running);
283 /* Pickup Job termination data */
284 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
285 wait_for_storage_daemon_termination(jcr);
287 jcr->JobStatus = jcr->SDJobStatus;
288 if (jcr->JobStatus == JS_Terminated) {
289 mac_cleanup(jcr, jcr->JobStatus);
297 * Release resources allocated during backup.
299 void mac_cleanup(JCR *jcr, int TermCode)
301 char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
302 char ec1[30], ec2[30], ec3[30];
303 char term_code[100], sd_term_msg[100];
304 const char *term_msg;
310 JCR *tjcr = jcr->target_jcr;
311 POOL_MEM query(PM_MESSAGE);
313 switch(jcr->JobType) {
328 /* Ensure target is defined to avoid a lot of testing */
332 tjcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
333 tjcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
334 tjcr->VolSessionId = jcr->VolSessionId;
335 tjcr->VolSessionTime = jcr->VolSessionTime;
337 Dmsg2(100, "Enter mac_cleanup %d %c\n", TermCode, TermCode);
338 dequeue_messages(jcr); /* display any queued messages */
339 memset(&mr, 0, sizeof(mr));
340 set_jcr_job_status(jcr, TermCode);
341 set_jcr_job_status(tjcr, TermCode);
344 update_job_end_record(jcr); /* update database */
345 update_job_end_record(tjcr);
347 Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
348 "JobTDate=%s WHERE JobId=%s",
349 jcr->target_jr.cStartTime, jcr->target_jr.cEndTime,
350 edit_uint64(jcr->target_jr.JobTDate, ec1),
351 edit_uint64(tjcr->jr.JobId, ec2));
352 db_sql_query(tjcr->db, query.c_str(), NULL, NULL);
354 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
355 Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
356 db_strerror(jcr->db));
357 set_jcr_job_status(jcr, JS_ErrorTerminated);
360 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
361 if (!db_get_media_record(jcr, jcr->db, &mr)) {
362 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
363 mr.VolumeName, db_strerror(jcr->db));
364 set_jcr_job_status(jcr, JS_ErrorTerminated);
367 update_bootstrap_file(tjcr);
369 msg_type = M_INFO; /* by default INFO message */
370 switch (jcr->JobStatus) {
372 if (jcr->Errors || jcr->SDErrors) {
373 term_msg = _("%s OK -- with warnings");
375 term_msg = _("%s OK");
379 case JS_ErrorTerminated:
380 term_msg = _("*** %s Error ***");
381 msg_type = M_ERROR; /* Generate error message */
382 if (jcr->store_bsock) {
383 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
384 if (jcr->SD_msg_chan) {
385 pthread_cancel(jcr->SD_msg_chan);
390 term_msg = _("%s Canceled");
391 if (jcr->store_bsock) {
392 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
393 if (jcr->SD_msg_chan) {
394 pthread_cancel(jcr->SD_msg_chan);
399 term_msg = _("Inappropriate %s term code");
402 bsnprintf(term_code, sizeof(term_code), term_msg, Type);
403 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
404 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
405 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
409 kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
411 if (!db_get_job_volume_names(tjcr, tjcr->db, tjcr->jr.JobId, &tjcr->VolumeName)) {
413 * Note, if the job has erred, most likely it did not write any
414 * tape, so suppress this "error" message since in that case
415 * it is normal. Or look at it the other way, only for a
416 * normal exit should we complain about this error.
418 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
419 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(tjcr->db));
421 tjcr->VolumeName[0] = 0; /* none */
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"
429 " Migration JobId: %u\n"
430 " Backup JobId: %u\n"
432 " Backup Level: %s%s\n"
434 " FileSet: \"%s\" %s\n"
438 " SD Files Written: %s\n"
439 " SD Bytes Written: %s\n"
441 " Volume name(s): %s\n"
442 " Volume Session Id: %d\n"
443 " Volume Session Time: %d\n"
444 " Last Volume Bytes: %s\n"
446 " SD termination status: %s\n"
447 " Termination: %s\n\n"),
454 level_to_str(jcr->JobLevel), jcr->since,
455 jcr->client->hdr.name,
456 jcr->fileset->hdr.name, jcr->FSCreateTime,
460 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
461 edit_uint64_with_commas(jcr->SDJobBytes, ec3),
466 edit_uint64_with_commas(mr.VolBytes, ec1),
471 Dmsg1(100, "Leave mac_cleanup() target_jcr=0x%x\n", jcr->target_jcr);
472 free_jcr(jcr->target_jcr);