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.
36 #include "lib/bregex.h"
41 static char OKbootstrap[] = "3000 OK bootstrap\n";
42 static bool get_job_to_migrate(JCR *jcr);
44 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
45 const char *query2, const char *type);
46 static void start_migration_job(JCR *jcr);
49 * Called here before the job is run to do the job
52 bool do_migration_init(JCR *jcr)
54 /* If we find a job to migrate it is previous_jr.JobId */
55 if (!get_job_to_migrate(jcr)) {
59 if (jcr->previous_jr.JobId == 0) {
60 return true; /* no work */
63 if (!get_or_create_fileset_record(jcr)) {
67 apply_pool_overrides(jcr);
69 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->hdr.name);
70 if (jcr->jr.PoolId == 0) {
74 /* If pool storage specified, use it instead of job storage */
75 copy_storage(jcr, jcr->pool->storage, _("Pool resource"));
78 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
82 if (!create_restore_bootstrap_file(jcr)) {
89 * Do a Migration of a previous job
91 * Returns: false on failure
94 bool do_migration(JCR *jcr)
101 JCR *prev_jcr; /* newly migrated job */
103 if (jcr->previous_jr.JobId == 0) {
104 set_jcr_job_status(jcr, JS_Terminated);
105 migration_cleanup(jcr, jcr->JobStatus);
106 return true; /* no work */
109 Dmsg4(000, "Previous: Name=%s JobId=%d Type=%c Level=%c\n",
110 jcr->previous_jr.Name, jcr->previous_jr.JobId,
111 jcr->previous_jr.JobType, jcr->previous_jr.JobLevel);
113 Dmsg4(000, "Current: Name=%s JobId=%d Type=%c Level=%c\n",
114 jcr->jr.Name, jcr->jr.JobId,
115 jcr->jr.JobType, jcr->jr.JobLevel);
118 job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
119 prev_job = (JOB *)GetResWithName(R_JOB, jcr->previous_jr.Name);
121 if (!job || !prev_job) {
126 * prev_jcr is the new Job that corresponds to the original
127 * job. It "runs" at the same time as the current
128 * migration job and becomes a new backup job that replaces
129 * the original backup job. Most operations on the current
130 * migration jcr are also done on the prev_jcr.
132 prev_jcr = jcr->previous_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
133 memcpy(&prev_jcr->previous_jr, &jcr->previous_jr, sizeof(prev_jcr->previous_jr));
135 /* Turn the prev_jcr into a "real" job */
136 set_jcr_defaults(prev_jcr, prev_job);
137 if (!setup_job(prev_jcr)) {
141 /* Now reset the job record from the previous job */
142 memcpy(&prev_jcr->jr, &jcr->previous_jr, sizeof(prev_jcr->jr));
143 /* Update the jr to reflect the new values of PoolId, FileSetId, and JobId. */
144 prev_jcr->jr.PoolId = jcr->jr.PoolId;
145 prev_jcr->jr.FileSetId = jcr->jr.FileSetId;
146 prev_jcr->jr.JobId = prev_jcr->JobId;
148 Dmsg4(000, "Prev_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
149 prev_jcr->jr.Name, prev_jcr->jr.JobId,
150 prev_jcr->jr.JobType, prev_jcr->jr.JobLevel);
153 * Get the PoolId used with the original job. Then
154 * find the pool name from the database record.
156 memset(&pr, 0, sizeof(pr));
157 pr.PoolId = prev_jcr->previous_jr.PoolId;
158 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
159 Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
160 edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
163 /* Get the pool resource corresponding to the original job */
164 pool = (POOL *)GetResWithName(R_POOL, pr.Name);
166 Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
170 /* Check Migration time and High/Low water marks */
173 /* If pool storage specified, use it for restore */
174 copy_storage(prev_jcr, pool->storage, _("Pool resource"));
176 /* If the original backup pool has a NextPool, make sure a
177 * record exists in the database.
179 if (pool->NextPool) {
180 jcr->jr.PoolId = get_or_create_pool_record(jcr, pool->NextPool->hdr.name);
181 if (jcr->jr.PoolId == 0) {
185 * put the "NextPool" resource pointer in our jcr so that we
186 * can pull the Storage reference from it.
188 prev_jcr->pool = jcr->pool = pool->NextPool;
189 prev_jcr->jr.PoolId = jcr->jr.PoolId;
190 pm_strcpy(jcr->pool_source, _("NextPool in Pool resource"));
193 /* If pool storage specified, use it instead of job storage for backup */
194 copy_storage(jcr, jcr->pool->storage, _("Pool resource"));
196 /* Print Job Start message */
197 Jmsg(jcr, M_INFO, 0, _("Start Migration JobId %s, Job=%s\n"),
198 edit_uint64(jcr->JobId, ed1), jcr->Job);
200 set_jcr_job_status(jcr, JS_Running);
201 set_jcr_job_status(prev_jcr, JS_Running);
202 Dmsg2(000, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
204 /* Update job start record for this migration job */
205 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
206 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
210 Dmsg4(000, "Prev_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
211 prev_jcr->jr.Name, prev_jcr->jr.JobId,
212 prev_jcr->jr.JobType, prev_jcr->jr.JobLevel);
214 /* Update job start record for migrated job */
215 if (!db_update_job_start_record(prev_jcr, prev_jcr->db, &prev_jcr->jr)) {
216 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(prev_jcr->db));
222 * Open a message channel connection with the Storage
223 * daemon. This is to let him know that our client
224 * will be contacting him for a backup session.
227 Dmsg0(110, "Open connection with storage daemon\n");
228 set_jcr_job_status(jcr, JS_WaitSD);
229 set_jcr_job_status(prev_jcr, JS_WaitSD);
231 * Start conversation with Storage daemon
233 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
236 sd = jcr->store_bsock;
238 * Now start a job with the Storage daemon
240 Dmsg2(000, "Read store=%s, write store=%s\n",
241 ((STORE *)prev_jcr->storage->first())->hdr.name,
242 ((STORE *)jcr->storage->first())->hdr.name);
243 if (!start_storage_daemon_job(jcr, prev_jcr->storage, jcr->storage)) {
246 Dmsg0(150, "Storage daemon connection OK\n");
248 if (!send_bootstrap_file(jcr, sd) ||
249 !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
253 if (!bnet_fsend(sd, "run")) {
258 * Now start a Storage daemon message thread
260 if (!start_storage_daemon_message_thread(jcr)) {
265 set_jcr_job_status(jcr, JS_Running);
266 set_jcr_job_status(prev_jcr, JS_Running);
268 /* Pickup Job termination data */
269 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
270 wait_for_storage_daemon_termination(jcr);
272 set_jcr_job_status(jcr, jcr->SDJobStatus);
273 if (jcr->JobStatus == JS_Terminated) {
274 migration_cleanup(jcr, jcr->JobStatus);
286 * Callback handler make list of DB Ids
288 static int dbid_handler(void *ctx, int num_fields, char **row)
290 idpkt *ids = (idpkt *)ctx;
292 Dmsg3(000, "count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
293 if (ids->count == 0) {
296 pm_strcat(ids->list, ",");
298 pm_strcat(ids->list, row[0]);
309 static int item_compare(void *item1, void *item2)
311 uitem *i1 = (uitem *)item1;
312 uitem *i2 = (uitem *)item2;
313 return strcmp(i1->item, i2->item);
316 static int unique_name_handler(void *ctx, int num_fields, char **row)
318 dlist *list = (dlist *)ctx;
320 uitem *new_item = (uitem *)malloc(sizeof(uitem));
323 memset(new_item, 0, sizeof(uitem));
324 new_item->item = bstrdup(row[0]);
325 Dmsg1(000, "Item=%s\n", row[0]);
326 item = (uitem *)list->binary_insert((void *)new_item, item_compare);
327 if (item != new_item) { /* already in list */
328 free(new_item->item);
329 free((char *)new_item);
335 /* Get Job names in Pool */
336 const char *sql_job =
337 "SELECT DISTINCT Job.Name from Job,Pool"
338 " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
340 /* Get JobIds from regex'ed Job names */
341 const char *sql_jobids_from_job =
342 "SELECT DISTINCT Job.JobId FROM Job,Pool"
343 " WHERE Job.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
344 " ORDER by Job.StartTime";
346 /* Get Client names in Pool */
347 const char *sql_client =
348 "SELECT DISTINCT Client.Name from Client,Pool,Job"
349 " WHERE Pool.Name='%s' AND Job.ClientId=Client.ClientId AND"
350 " Job.PoolId=Pool.PoolId";
352 /* Get JobIds from regex'ed Client names */
353 const char *sql_jobids_from_client =
354 "SELECT DISTINCT Job.JobId FROM Job,Pool"
355 " WHERE Client.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
356 " AND Job.ClientId=Client.ClientId "
357 " ORDER by Job.StartTime";
359 /* Get Volume names in Pool */
360 const char *sql_vol =
361 "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
362 " VolStatus in ('Full','Used','Error') AND"
363 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
365 /* Get JobIds from regex'ed Volume names */
366 const char *sql_jobids_from_vol =
367 "SELECT DISTINCT Job.JobId FROM Media,JobMedia,Job"
368 " WHERE Media.VolumeName='%s' AND Media.MediaId=JobMedia.MediaId"
369 " AND JobMedia.JobId=Job.JobId"
370 " ORDER by Job.StartTime";
376 const char *sql_smallest_vol =
377 "SELECT MediaId FROM Media,Pool WHERE"
378 " VolStatus in ('Full','Used','Error') AND"
379 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
380 " ORDER BY VolBytes ASC LIMIT 1";
382 const char *sql_oldest_vol =
383 "SELECT MediaId FROM Media,Pool WHERE"
384 " VolStatus in ('Full','Used','Error') AND"
385 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
386 " ORDER BY LastWritten ASC LIMIT 1";
388 const char *sql_jobids_from_mediaid =
389 "SELECT DISTINCT Job.JobId FROM JobMedia,Job"
390 " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId=%s"
391 " ORDER by Job.StartTime";
393 const char *sql_pool_bytes =
394 "SELECT SUM(VolBytes) FROM Media,Pool WHERE"
395 " VolStatus in ('Full','Used','Error','Append') AND"
396 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
398 const char *sql_vol_bytes =
399 "SELECT MediaId FROM Media,Pool WHERE"
400 " VolStatus in ('Full','Used','Error') AND"
401 " Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
402 " VolBytes<%s ORDER BY LastWritten ASC LIMIT 1";
405 const char *sql_ujobid =
406 "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
407 " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
408 " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
413 * Returns: false on error
414 * true if OK and jcr->previous_jr filled in
416 static bool get_job_to_migrate(JCR *jcr)
419 POOL_MEM query(PM_MESSAGE);
425 ids.list = get_pool_memory(PM_MESSAGE);
426 Dmsg1(000, "list=%p\n", ids.list);
430 if (jcr->MigrateJobId != 0) {
431 Dmsg1(000, "previous jobid=%u\n", jcr->MigrateJobId);
432 edit_uint64(jcr->MigrateJobId, ids.list);
435 switch (jcr->job->selection_type) {
437 if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
442 if (!regex_find_jobids(jcr, &ids, sql_client,
443 sql_jobids_from_client, "Client")) {
448 if (!regex_find_jobids(jcr, &ids, sql_vol,
449 sql_jobids_from_vol, "Volume")) {
454 if (!jcr->job->selection_pattern) {
455 Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
458 Dmsg1(000, "SQL=%s\n", jcr->job->selection_pattern);
459 if (!db_sql_query(jcr->db, jcr->job->selection_pattern,
460 dbid_handler, (void *)&ids)) {
461 Jmsg(jcr, M_FATAL, 0,
462 _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
468 /***** Below not implemented yet *********/
469 case MT_SMALLEST_VOL:
470 Mmsg(query, sql_smallest_vol, jcr->pool->hdr.name);
471 // Mmsg(query2, sql_jobids_from_mediaid, JobIds);
472 // Dmsg1(000, "Smallest Vol Jobids=%s\n", JobIds);
475 Mmsg(query, sql_oldest_vol, jcr->pool->hdr.name);
476 // Mmsg(query2, sql_jobids_from_mediaid, JobIds);
477 // Dmsg1(000, "Oldest Vol Jobids=%s\n", JobIds);
479 case MT_POOL_OCCUPANCY:
480 Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
481 // Dmsg1(000, "Pool Occupancy Jobids=%s\n", JobIds);
484 Dmsg0(000, "Pool time not implemented\n");
487 Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
494 stat = get_next_jobid_from_list(&p, &JobId);
495 Dmsg2(000, "get_next_jobid stat=%d JobId=%u\n", stat, JobId);
496 jcr->MigrateJobId = JobId;
497 start_migration_job(jcr);
499 Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
501 } else if (stat == 0) {
502 Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
506 jcr->previous_jr.JobId = JobId;
507 Dmsg1(000, "Previous jobid=%d\n", jcr->previous_jr.JobId);
509 if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
510 Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
511 edit_int64(jcr->previous_jr.JobId, ed1),
512 db_strerror(jcr->db));
515 Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
516 jcr->previous_jr.JobId, jcr->previous_jr.Job);
519 free_pool_memory(ids.list);
523 free_pool_memory(ids.list);
527 static void start_migration_job(JCR *jcr)
529 UAContext *ua = new_ua_context(jcr);
532 Mmsg(ua->cmd, "run %s jobid=%s", jcr->job->hdr.name,
533 edit_uint64(jcr->MigrateJobId, ed1));
534 Dmsg1(000, "=============== Migration cmd=%s\n", ua->cmd);
535 parse_ua_args(ua); /* parse command */
536 // int stat = run_cmd(ua, ua->cmd);
537 int stat = (int)jcr->MigrateJobId;
539 Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
541 Jmsg(jcr, M_INFO, 0, _("Migration JobId %d started.\n"), stat);
547 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
548 const char *query2, const char *type) {
551 uitem *last_item = NULL;
556 POOL_MEM query(PM_MESSAGE);
558 item_chain = New(dlist(item, &item->link));
559 if (!jcr->job->selection_pattern) {
560 Jmsg(jcr, M_FATAL, 0, _("No Migration %s selection pattern specified.\n"),
564 Dmsg1(000, "regex=%s\n", jcr->job->selection_pattern);
565 /* Compile regex expression */
566 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
568 regerror(rc, &preg, prbuf, sizeof(prbuf));
569 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
570 jcr->job->selection_pattern, prbuf);
573 /* Basic query for names */
574 Mmsg(query, query1, jcr->pool->hdr.name);
575 Dmsg1(000, "query1=%s\n", query.c_str());
576 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
577 (void *)item_chain)) {
578 Jmsg(jcr, M_FATAL, 0,
579 _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
582 /* Now apply the regex to the names and remove any item not matched */
583 foreach_dlist(item, item_chain) {
584 const int nmatch = 30;
585 regmatch_t pmatch[nmatch];
587 Dmsg1(000, "Remove item %s\n", last_item->item);
588 free(last_item->item);
589 item_chain->remove(last_item);
591 Dmsg1(000, "Jobitem=%s\n", item->item);
592 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
594 last_item = NULL; /* keep this one */
600 free(last_item->item);
601 Dmsg1(000, "Remove item %s\n", last_item->item);
602 item_chain->remove(last_item);
606 * At this point, we have a list of items in item_chain
607 * that have been matched by the regex, so now we need
608 * to look up their jobids.
611 foreach_dlist(item, item_chain) {
612 Dmsg1(000, "Got Job: %s\n", item->item);
613 Mmsg(query, query2, item->item, jcr->pool->hdr.name);
614 Dmsg1(000, "query2=%s\n", query.c_str());
615 if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
616 Jmsg(jcr, M_FATAL, 0,
617 _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
621 if (ids->count == 0) {
622 Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
626 Dmsg2(000, "Count=%d Jobids=%s\n", ids->count, ids->list);
628 Dmsg0(000, "After delete item_chain\n");
634 * Release resources allocated during backup.
636 void migration_cleanup(JCR *jcr, int TermCode)
638 char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
639 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
640 char ec6[50], ec7[50], ec8[50];
641 char term_code[100], sd_term_msg[100];
642 const char *term_msg;
647 JCR *prev_jcr = jcr->previous_jcr;
648 POOL_MEM query(PM_MESSAGE);
650 Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
651 dequeue_messages(jcr); /* display any queued messages */
652 memset(&mr, 0, sizeof(mr));
653 set_jcr_job_status(jcr, TermCode);
654 update_job_end_record(jcr); /* update database */
657 * Check if we actually did something.
658 * prev_jcr is jcr of the newly migrated job.
661 prev_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
662 prev_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
663 prev_jcr->VolSessionId = jcr->VolSessionId;
664 prev_jcr->VolSessionTime = jcr->VolSessionTime;
665 prev_jcr->jr.RealEndTime = 0;
666 prev_jcr->jr.PriorJobId = jcr->previous_jr.JobId;
668 set_jcr_job_status(prev_jcr, TermCode);
671 update_job_end_record(prev_jcr);
673 /* Update final items to set them to the previous job's values */
674 Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
675 "JobTDate=%s WHERE JobId=%s",
676 jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime,
677 edit_uint64(jcr->previous_jr.JobTDate, ec1),
678 edit_uint64(prev_jcr->jr.JobId, ec2));
679 db_sql_query(prev_jcr->db, query.c_str(), NULL, NULL);
681 /* Now marke the previous job as migrated */
682 Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
683 (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
684 db_sql_query(prev_jcr->db, query.c_str(), NULL, NULL);
686 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
687 Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
688 db_strerror(jcr->db));
689 set_jcr_job_status(jcr, JS_ErrorTerminated);
692 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
693 if (!db_get_media_record(jcr, jcr->db, &mr)) {
694 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
695 mr.VolumeName, db_strerror(jcr->db));
696 set_jcr_job_status(jcr, JS_ErrorTerminated);
699 update_bootstrap_file(prev_jcr);
701 if (!db_get_job_volume_names(prev_jcr, prev_jcr->db, prev_jcr->jr.JobId, &prev_jcr->VolumeName)) {
703 * Note, if the job has erred, most likely it did not write any
704 * tape, so suppress this "error" message since in that case
705 * it is normal. Or look at it the other way, only for a
706 * normal exit should we complain about this error.
708 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
709 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(prev_jcr->db));
711 prev_jcr->VolumeName[0] = 0; /* none */
715 msg_type = M_INFO; /* by default INFO message */
716 switch (jcr->JobStatus) {
718 if (jcr->Errors || jcr->SDErrors) {
719 term_msg = _("%s OK -- with warnings");
721 term_msg = _("%s OK");
725 case JS_ErrorTerminated:
726 term_msg = _("*** %s Error ***");
727 msg_type = M_ERROR; /* Generate error message */
728 if (jcr->store_bsock) {
729 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
730 if (jcr->SD_msg_chan) {
731 pthread_cancel(jcr->SD_msg_chan);
736 term_msg = _("%s Canceled");
737 if (jcr->store_bsock) {
738 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
739 if (jcr->SD_msg_chan) {
740 pthread_cancel(jcr->SD_msg_chan);
745 term_msg = _("Inappropriate %s term code");
748 bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
749 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
750 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
751 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
755 kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
759 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
761 Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
762 " Prev Backup JobId: %s\n"
763 " New Backup JobId: %s\n"
764 " Migration JobId: %s\n"
765 " Migration Job: %s\n"
766 " Backup Level: %s%s\n"
768 " FileSet: \"%s\" %s\n"
769 " Pool: \"%s\" (From %s)\n"
770 " Storage: \"%s\" (From %s)\n"
773 " Elapsed time: %s\n"
775 " SD Files Written: %s\n"
776 " SD Bytes Written: %s (%sB)\n"
778 " Volume name(s): %s\n"
779 " Volume Session Id: %d\n"
780 " Volume Session Time: %d\n"
781 " Last Volume Bytes: %s (%sB)\n"
783 " SD termination status: %s\n"
784 " Termination: %s\n\n"),
788 prev_jcr ? edit_uint64(jcr->previous_jr.JobId, ec6) : "0",
789 prev_jcr ? edit_uint64(prev_jcr->jr.JobId, ec7) : "0",
790 edit_uint64(jcr->jr.JobId, ec8),
792 level_to_str(jcr->JobLevel), jcr->since,
793 jcr->client->hdr.name,
794 jcr->fileset->hdr.name, jcr->FSCreateTime,
795 jcr->pool->hdr.name, jcr->pool_source,
796 jcr->store->hdr.name, jcr->storage_source,
799 edit_utime(RunTime, elapsed, sizeof(elapsed)),
801 edit_uint64_with_commas(jcr->SDJobFiles, ec1),
802 edit_uint64_with_commas(jcr->SDJobBytes, ec2),
803 edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
805 prev_jcr ? prev_jcr->VolumeName : "",
808 edit_uint64_with_commas(mr.VolBytes, ec4),
809 edit_uint64_with_suffix(mr.VolBytes, ec5),
814 Dmsg1(100, "migrate_cleanup() previous_jcr=0x%x\n", jcr->previous_jcr);
815 if (jcr->previous_jcr) {
816 free_jcr(jcr->previous_jcr);
817 jcr->previous_jcr = NULL;
819 Dmsg0(100, "Leave migrate_cleanup()\n");