3 * Bacula Director -- migrate.c -- responsible for doing
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.
37 static char OKbootstrap[] = "3000 OK bootstrap\n";
38 static bool get_job_to_migrate(JCR *jcr);
41 * Called here before the job is run to do the job
44 bool do_migration_init(JCR *jcr)
48 if (!get_job_to_migrate(jcr)) {
52 if (jcr->previous_jr.JobId == 0) {
53 return true; /* no work */
56 if (!get_or_create_fileset_record(jcr)) {
61 * Get the Pool record -- first apply any level defined pools
63 switch (jcr->previous_jr.JobLevel) {
66 jcr->pool = jcr->full_pool;
71 jcr->pool = jcr->inc_pool;
76 jcr->pool = jcr->dif_pool;
80 memset(&pr, 0, sizeof(pr));
81 bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
83 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
84 /* Try to create the pool */
85 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
86 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
87 db_strerror(jcr->db));
90 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
93 jcr->jr.PoolId = pr.PoolId;
95 /* If pool storage specified, use it instead of job storage */
96 copy_storage(jcr, jcr->pool->storage);
99 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
103 if (!create_restore_bootstrap_file(jcr)) {
110 * Do a Migration of a previous job
112 * Returns: false on failure
115 bool do_migration(JCR *jcr)
124 if (jcr->previous_jr.JobId == 0) {
125 jcr->JobStatus = JS_Terminated;
126 migration_cleanup(jcr, jcr->JobStatus);
127 return true; /* no work */
129 Dmsg4(000, "Target: Name=%s JobId=%d Type=%c Level=%c\n",
130 jcr->previous_jr.Name, jcr->previous_jr.JobId,
131 jcr->previous_jr.JobType, jcr->previous_jr.JobLevel);
133 Dmsg4(000, "Current: Name=%s JobId=%d Type=%c Level=%c\n",
134 jcr->jr.Name, jcr->jr.JobId,
135 jcr->jr.JobType, jcr->jr.JobLevel);
138 job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
139 tjob = (JOB *)GetResWithName(R_JOB, jcr->previous_jr.Name);
146 * Target jcr is the new Job that corresponds to the original
147 * target job. It "runs" at the same time as the current
148 * migration job and becomes a new backup job that replaces
149 * the original backup job. Most operations on the current
150 * migration jcr are also done on the target jcr.
152 tjcr = jcr->previous_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
153 memcpy(&tjcr->previous_jr, &jcr->previous_jr, sizeof(tjcr->previous_jr));
155 /* Turn the tjcr into a "real" job */
156 set_jcr_defaults(tjcr, tjob);
157 if (!setup_job(tjcr)) {
160 /* Set output PoolId and FileSetId. */
161 tjcr->jr.PoolId = jcr->jr.PoolId;
162 tjcr->jr.FileSetId = jcr->jr.FileSetId;
165 * Get the PoolId used with the original job. Then
166 * find the pool name from the database record.
168 memset(&pr, 0, sizeof(pr));
169 pr.PoolId = tjcr->previous_jr.PoolId;
170 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
171 Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
172 edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
175 /* Get the pool resource corresponding to the original job */
176 pool = (POOL *)GetResWithName(R_POOL, pr.Name);
178 Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
182 /* Check Migration time and High/Low water marks */
185 /* If pool storage specified, use it for restore */
186 copy_storage(tjcr, pool->storage);
188 /* If the original backup pool has a NextPool, make sure a
189 * record exists in the database.
191 if (pool->NextPool) {
192 memset(&pr, 0, sizeof(pr));
193 bstrncpy(pr.Name, pool->NextPool->hdr.name, sizeof(pr.Name));
195 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
196 /* Try to create the pool */
197 if (create_pool(jcr, jcr->db, pool->NextPool, POOL_OP_CREATE) < 0) {
198 Jmsg(jcr, M_FATAL, 0, _("Pool \"%s\" not in database. %s"), pr.Name,
199 db_strerror(jcr->db));
202 Jmsg(jcr, M_INFO, 0, _("Pool \"%s\" created in database.\n"), pr.Name);
206 * put the "NextPool" resource pointer in our jcr so that we
207 * can pull the Storage reference from it.
209 tjcr->pool = jcr->pool = pool->NextPool;
210 tjcr->jr.PoolId = jcr->jr.PoolId = pr.PoolId;
213 /* If pool storage specified, use it instead of job storage for backup */
214 copy_storage(jcr, jcr->pool->storage);
216 /* Print Job Start message */
217 Jmsg(jcr, M_INFO, 0, _("Start Migration JobId %s, Job=%s\n"),
218 edit_uint64(jcr->JobId, ed1), jcr->Job);
220 set_jcr_job_status(jcr, JS_Running);
221 set_jcr_job_status(tjcr, JS_Running);
222 Dmsg2(000, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
223 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
224 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
228 if (!db_update_job_start_record(tjcr, tjcr->db, &tjcr->jr)) {
229 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(tjcr->db));
235 * Open a message channel connection with the Storage
236 * daemon. This is to let him know that our client
237 * will be contacting him for a backup session.
240 Dmsg0(110, "Open connection with storage daemon\n");
241 set_jcr_job_status(jcr, JS_WaitSD);
242 set_jcr_job_status(tjcr, JS_WaitSD);
244 * Start conversation with Storage daemon
246 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
249 sd = jcr->store_bsock;
251 * Now start a job with the Storage daemon
253 Dmsg2(000, "Read store=%s, write store=%s\n",
254 ((STORE *)tjcr->storage->first())->hdr.name,
255 ((STORE *)jcr->storage->first())->hdr.name);
256 if (!start_storage_daemon_job(jcr, tjcr->storage, jcr->storage)) {
259 Dmsg0(150, "Storage daemon connection OK\n");
261 if (!send_bootstrap_file(jcr, sd) ||
262 !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
267 * Now start a Storage daemon message thread
269 if (!start_storage_daemon_message_thread(jcr)) {
273 if (!bnet_fsend(sd, "run")) {
277 set_jcr_job_status(jcr, JS_Running);
278 set_jcr_job_status(tjcr, JS_Running);
280 /* Pickup Job termination data */
281 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
282 wait_for_storage_daemon_termination(jcr);
284 jcr->JobStatus = jcr->SDJobStatus;
285 if (jcr->JobStatus == JS_Terminated) {
286 migration_cleanup(jcr, jcr->JobStatus);
293 * Callback handler make list of JobIds
295 static int jobid_handler(void *ctx, int num_fields, char **row)
297 POOLMEM *JobIds = (POOLMEM *)ctx;
299 if (JobIds[0] != 0) {
300 pm_strcat(JobIds, ",");
302 pm_strcat(JobIds, row[0]);
312 static int item_compare(void *item1, void *item2)
314 uitem *i1 = (uitem *)item1;
315 uitem *i2 = (uitem *)item2;
316 return strcmp(i1->item, i2->item);
319 static int unique_name_handler(void *ctx, int num_fields, char **row)
321 dlist *list = (dlist *)ctx;
323 uitem *new_item = (uitem *)malloc(sizeof(uitem));
326 memset(new_item, 0, sizeof(uitem));
327 new_item->item = bstrdup(row[0]);
328 Dmsg1(000, "Item=%s\n", row[0]);
329 item = (uitem *)list->binary_insert((void *)new_item, item_compare);
330 if (item != new_item) { /* already in list */
331 free(new_item->item);
332 free((char *)new_item);
338 const char *sql_smallest_vol =
339 "SELECT MediaId FROM Media,Pool WHERE"
340 " VolStatus in ('Full','Used','Error') AND"
341 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
342 " ORDER BY VolBytes ASC LIMIT 1";
344 const char *sql_oldest_vol =
345 "SELECT MediaId FROM Media,Pool WHERE"
346 " VolStatus in ('Full','Used','Error') AND"
347 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
348 " ORDER BY LastWritten ASC LIMIT 1";
350 const char *sql_jobids_from_mediaid =
351 "SELECT DISTINCT Job.JobId FROM JobMedia,Job"
352 " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId=%s"
353 " ORDER by Job.StartTime";
355 const char *sql_pool_bytes =
356 "SELECT SUM(VolBytes) FROM Media,Pool WHERE"
357 " VolStatus in ('Full','Used','Error','Append') AND"
358 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
360 const char *sql_vol_bytes =
361 "SELECT MediaId FROM Media,Pool WHERE"
362 " VolStatus in ('Full','Used','Error') AND"
363 " Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
364 " VolBytes<%s ORDER BY LastWritten ASC LIMIT 1";
366 const char *sql_client =
367 "SELECT DISTINCT Client.Name from Client,Pool,Media,Job,JobMedia "
368 " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
369 " JobMedia.JobId=Job.JobId AND Job.ClientId=Client.ClientId AND"
370 " Job.PoolId=Media.PoolId";
372 const char *sql_job =
373 "SELECT DISTINCT Job.Name from Job,Pool"
374 " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
376 const char *sql_jobids_from_job =
377 "SELECT DISTINCT Job.JobId FROM Job,Pool"
378 " WHERE Job.Name=%s AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
379 " ORDER by Job.StartTime";
382 const char *sql_ujobid =
383 "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
384 " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
385 " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
387 const char *sql_vol =
388 "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
389 " VolStatus in ('Full','Used','Error') AND"
390 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
394 * Returns: false on error
395 * true if OK and jcr->previous_jr filled in
397 static bool get_job_to_migrate(JCR *jcr)
400 POOL_MEM query(PM_MESSAGE);
401 POOLMEM *JobIds = get_pool_memory(PM_MESSAGE);
407 uitem *last_item = NULL;
412 if (jcr->MigrateJobId != 0) {
413 jcr->previous_jr.JobId = jcr->MigrateJobId;
414 Dmsg1(000, "previous jobid=%u\n", jcr->MigrateJobId);
416 switch (jcr->job->selection_type) {
417 case MT_SMALLEST_VOL:
418 Mmsg(query, sql_smallest_vol, jcr->pool->hdr.name);
419 JobIds = get_pool_memory(PM_MESSAGE);
421 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
422 Jmsg(jcr, M_FATAL, 0,
423 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
426 if (JobIds[0] == 0) {
427 Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
430 /* ***FIXME*** must loop over JobIds */
431 Mmsg(query, sql_jobids_from_mediaid, JobIds);
433 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
434 Jmsg(jcr, M_FATAL, 0,
435 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
438 Dmsg1(000, "Smallest Vol Jobids=%s\n", JobIds);
441 Mmsg(query, sql_oldest_vol, jcr->pool->hdr.name);
442 JobIds = get_pool_memory(PM_MESSAGE);
444 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
445 Jmsg(jcr, M_FATAL, 0,
446 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
449 if (JobIds[0] == 0) {
450 Jmsg(jcr, M_INFO, 0, _("No Volume found to migrate.\n"));
453 Mmsg(query, sql_jobids_from_mediaid, JobIds);
455 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
456 Jmsg(jcr, M_FATAL, 0,
457 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
460 Dmsg1(000, "Oldest Vol Jobids=%s\n", JobIds);
462 case MT_POOL_OCCUPANCY:
463 Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
464 JobIds = get_pool_memory(PM_MESSAGE);
466 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
467 Jmsg(jcr, M_FATAL, 0,
468 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
471 if (JobIds[0] == 0) {
472 Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
475 Dmsg1(000, "Pool Occupancy Jobids=%s\n", JobIds);
478 Dmsg0(000, "Pool time not implemented\n");
481 if (!jcr->job->selection_pattern) {
482 Jmsg(jcr, M_FATAL, 0, _("No Migration Client selection pattern specified.\n"));
485 Dmsg1(000, "Client regex=%s\n", jcr->job->selection_pattern);
486 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
488 regerror(rc, &preg, prbuf, sizeof(prbuf));
489 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
490 jcr->job->selection_pattern, prbuf);
492 item_chain = New(dlist(item, &item->link));
493 Mmsg(query, sql_client, jcr->pool->hdr.name);
494 Dmsg1(100, "query=%s\n", query.c_str());
495 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
496 (void *)item_chain)) {
497 Jmsg(jcr, M_FATAL, 0,
498 _("SQL to get Client failed. ERR=%s\n"), db_strerror(jcr->db));
501 /* Now apply the regex and create the jobs */
502 foreach_dlist(item, item_chain) {
503 const int nmatch = 30;
504 regmatch_t pmatch[nmatch];
505 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
507 Dmsg1(000, "Do Client=%s\n", item->item);
515 if (!jcr->job->selection_pattern) {
516 Jmsg(jcr, M_FATAL, 0, _("No Migration Volume selection pattern specified.\n"));
519 Dmsg1(000, "Volume regex=%s\n", jcr->job->selection_pattern);
520 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
522 regerror(rc, &preg, prbuf, sizeof(prbuf));
523 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
524 jcr->job->selection_pattern, prbuf);
526 item_chain = New(dlist(item, &item->link));
527 Mmsg(query, sql_vol, jcr->pool->hdr.name);
528 Dmsg1(100, "query=%s\n", query.c_str());
529 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
530 (void *)item_chain)) {
531 Jmsg(jcr, M_FATAL, 0,
532 _("SQL to get Job failed. ERR=%s\n"), db_strerror(jcr->db));
535 /* Now apply the regex and create the jobs */
536 foreach_dlist(item, item_chain) {
537 const int nmatch = 30;
538 regmatch_t pmatch[nmatch];
539 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
541 Dmsg1(000, "Do Vol=%s\n", item->item);
549 if (!jcr->job->selection_pattern) {
550 Jmsg(jcr, M_FATAL, 0, _("No Migration Job selection pattern specified.\n"));
553 Dmsg1(000, "Job regex=%s\n", jcr->job->selection_pattern);
554 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
556 regerror(rc, &preg, prbuf, sizeof(prbuf));
557 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
558 jcr->job->selection_pattern, prbuf);
561 item_chain = New(dlist(item, &item->link));
562 Mmsg(query, sql_job, jcr->pool->hdr.name);
563 Dmsg1(000, "query=%s\n", query.c_str());
564 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
565 (void *)item_chain)) {
566 Jmsg(jcr, M_FATAL, 0,
567 _("SQL to get Job failed. ERR=%s\n"), db_strerror(jcr->db));
570 /* Now apply the regex and remove any item not matched */
571 foreach_dlist(item, item_chain) {
572 const int nmatch = 30;
573 regmatch_t pmatch[nmatch];
575 free(last_item->item);
576 item_chain->remove(last_item);
578 Dmsg1(000, "Jobitem=%s\n", item->item);
579 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
581 last_item = NULL; /* keep this one */
587 free(last_item->item);
588 item_chain->remove(last_item);
592 * At this point, we have a list of items in item_chain
593 * that have been matched by the regex, so now we need
594 * to look up their jobids.
596 JobIds = get_pool_memory(PM_MESSAGE);
598 foreach_dlist(item, item_chain) {
599 Dmsg1(000, "Got Job: %s\n", item->item);
600 Mmsg(query, sql_jobids_from_job, item->item, jcr->pool->hdr.name);
601 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
602 Jmsg(jcr, M_FATAL, 0,
603 _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
607 if (JobIds[0] == 0) {
608 Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
611 Dmsg1(000, "Job Jobids=%s\n", JobIds);
612 free_pool_memory(JobIds);
617 if (!jcr->job->selection_pattern) {
618 Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
621 Dmsg1(000, "SQL=%s\n", jcr->job->selection_pattern);
622 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
623 Jmsg(jcr, M_FATAL, 0,
624 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
627 if (JobIds[0] == 0) {
628 Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
631 Dmsg1(000, "Jobids=%s\n", JobIds);
635 Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
642 stat = get_next_jobid_from_list(&p, &JobId);
643 Dmsg2(000, "get_next_jobid stat=%d JobId=%u\n", stat, JobId);
645 Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
647 } else if (stat == 0) {
648 Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
652 jcr->previous_jr.JobId = JobId;
653 Dmsg1(000, "Last jobid=%d\n", jcr->previous_jr.JobId);
655 if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
656 Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
657 edit_int64(jcr->previous_jr.JobId, ed1),
658 db_strerror(jcr->db));
661 Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
662 jcr->previous_jr.JobId, jcr->previous_jr.Job);
665 free_pool_memory(JobIds);
669 free_pool_memory(JobIds);
675 * Release resources allocated during backup.
677 void migration_cleanup(JCR *jcr, int TermCode)
679 char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
680 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
681 char term_code[100], sd_term_msg[100];
682 const char *term_msg;
687 JCR *tjcr = jcr->previous_jcr;
688 POOL_MEM query(PM_MESSAGE);
690 /* Ensure target is defined to avoid a lot of testing */
694 tjcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
695 tjcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
696 tjcr->VolSessionId = jcr->VolSessionId;
697 tjcr->VolSessionTime = jcr->VolSessionTime;
699 Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
700 dequeue_messages(jcr); /* display any queued messages */
701 memset(&mr, 0, sizeof(mr));
702 set_jcr_job_status(jcr, TermCode);
703 set_jcr_job_status(tjcr, TermCode);
706 update_job_end_record(jcr); /* update database */
707 update_job_end_record(tjcr);
709 Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
710 "JobTDate=%s WHERE JobId=%s",
711 jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime,
712 edit_uint64(jcr->previous_jr.JobTDate, ec1),
713 edit_uint64(tjcr->jr.JobId, ec2));
714 db_sql_query(tjcr->db, query.c_str(), NULL, NULL);
716 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
717 Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
718 db_strerror(jcr->db));
719 set_jcr_job_status(jcr, JS_ErrorTerminated);
722 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
723 if (!db_get_media_record(jcr, jcr->db, &mr)) {
724 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
725 mr.VolumeName, db_strerror(jcr->db));
726 set_jcr_job_status(jcr, JS_ErrorTerminated);
729 update_bootstrap_file(tjcr);
731 msg_type = M_INFO; /* by default INFO message */
732 switch (jcr->JobStatus) {
734 if (jcr->Errors || jcr->SDErrors) {
735 term_msg = _("%s OK -- with warnings");
737 term_msg = _("%s OK");
741 case JS_ErrorTerminated:
742 term_msg = _("*** %s Error ***");
743 msg_type = M_ERROR; /* Generate error message */
744 if (jcr->store_bsock) {
745 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
746 if (jcr->SD_msg_chan) {
747 pthread_cancel(jcr->SD_msg_chan);
752 term_msg = _("%s Canceled");
753 if (jcr->store_bsock) {
754 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
755 if (jcr->SD_msg_chan) {
756 pthread_cancel(jcr->SD_msg_chan);
761 term_msg = _("Inappropriate %s term code");
764 bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
765 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
766 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
767 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
771 kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
773 if (!db_get_job_volume_names(tjcr, tjcr->db, tjcr->jr.JobId, &tjcr->VolumeName)) {
775 * Note, if the job has erred, most likely it did not write any
776 * tape, so suppress this "error" message since in that case
777 * it is normal. Or look at it the other way, only for a
778 * normal exit should we complain about this error.
780 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
781 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(tjcr->db));
783 tjcr->VolumeName[0] = 0; /* none */
786 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
788 // bmicrosleep(15, 0); /* for debugging SIGHUP */
790 Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
791 " Old Backup JobId: %u\n"
792 " New Backup JobId: %u\n"
795 " Backup Level: %s%s\n"
797 " FileSet: \"%s\" %s\n"
801 " Elapsed time: %s\n"
803 " SD Files Written: %s\n"
804 " SD Bytes Written: %s (%sB)\n"
806 " Volume name(s): %s\n"
807 " Volume Session Id: %d\n"
808 " Volume Session Time: %d\n"
809 " Last Volume Bytes: %s (%sB)\n"
811 " SD termination status: %s\n"
812 " Termination: %s\n\n"),
816 jcr->previous_jr.JobId,
820 level_to_str(jcr->JobLevel), jcr->since,
821 jcr->client->hdr.name,
822 jcr->fileset->hdr.name, jcr->FSCreateTime,
826 edit_utime(RunTime, elapsed, sizeof(elapsed)),
828 edit_uint64_with_commas(jcr->SDJobFiles, ec1),
829 edit_uint64_with_commas(jcr->SDJobBytes, ec2),
830 edit_uint64_with_suffix(jcr->jr.JobBytes, ec3),
835 edit_uint64_with_commas(mr.VolBytes, ec4),
836 edit_uint64_with_suffix(mr.VolBytes, ec5),
841 Dmsg1(100, "Leave migrate_cleanup() previous_jcr=0x%x\n", jcr->previous_jcr);
842 if (jcr->previous_jcr) {
843 free_jcr(jcr->previous_jcr);