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);
45 * Called here before the job is run to do the job
48 bool do_migration_init(JCR *jcr)
52 /* If we find a job to migrate it is previous_jr.JobId */
53 if (!get_job_to_migrate(jcr)) {
57 if (jcr->previous_jr.JobId == 0) {
58 return true; /* no work */
61 if (!get_or_create_fileset_record(jcr)) {
66 * Get the Pool record -- first apply any level defined pools
68 switch (jcr->previous_jr.JobLevel) {
71 jcr->pool = jcr->full_pool;
76 jcr->pool = jcr->inc_pool;
81 jcr->pool = jcr->dif_pool;
85 memset(&pr, 0, sizeof(pr));
86 bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
88 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
89 /* Try to create the pool */
90 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
91 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
92 db_strerror(jcr->db));
95 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
98 jcr->jr.PoolId = pr.PoolId;
100 /* If pool storage specified, use it instead of job storage */
101 copy_storage(jcr, jcr->pool->storage);
104 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
108 if (!create_restore_bootstrap_file(jcr)) {
115 * Do a Migration of a previous job
117 * Returns: false on failure
120 bool do_migration(JCR *jcr)
129 if (jcr->previous_jr.JobId == 0) {
130 jcr->JobStatus = JS_Terminated;
131 migration_cleanup(jcr, jcr->JobStatus);
132 return true; /* no work */
135 Dmsg4(000, "Previous:: Name=%s JobId=%d Type=%c Level=%c\n",
136 jcr->previous_jr.Name, jcr->previous_jr.JobId,
137 jcr->previous_jr.JobType, jcr->previous_jr.JobLevel);
139 Dmsg4(000, "Current: Name=%s JobId=%d Type=%c Level=%c\n",
140 jcr->jr.Name, jcr->jr.JobId,
141 jcr->jr.JobType, jcr->jr.JobLevel);
144 job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
145 prev_job = (JOB *)GetResWithName(R_JOB, jcr->previous_jr.Name);
147 if (!job || !prev_job) {
152 * prev_jcr is the new Job that corresponds to the original
153 * job. It "runs" at the same time as the current
154 * migration job and becomes a new backup job that replaces
155 * the original backup job. Most operations on the current
156 * migration jcr are also done on the prev_jcr.
158 prev_jcr = jcr->previous_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
159 memcpy(&prev_jcr->previous_jr, &jcr->previous_jr, sizeof(prev_jcr->previous_jr));
161 /* Turn the prev_jcr into a "real" job */
162 set_jcr_defaults(prev_jcr, prev_job);
163 if (!setup_job(prev_jcr)) {
166 /* Set output PoolId and FileSetId. */
167 prev_jcr->jr.PoolId = jcr->jr.PoolId;
168 prev_jcr->jr.FileSetId = jcr->jr.FileSetId;
171 * Get the PoolId used with the original job. Then
172 * find the pool name from the database record.
174 memset(&pr, 0, sizeof(pr));
175 pr.PoolId = prev_jcr->previous_jr.PoolId;
176 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
177 Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
178 edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
181 /* Get the pool resource corresponding to the original job */
182 pool = (POOL *)GetResWithName(R_POOL, pr.Name);
184 Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
188 /* Check Migration time and High/Low water marks */
191 /* If pool storage specified, use it for restore */
192 copy_storage(prev_jcr, pool->storage);
194 /* If the original backup pool has a NextPool, make sure a
195 * record exists in the database.
197 if (pool->NextPool) {
198 memset(&pr, 0, sizeof(pr));
199 bstrncpy(pr.Name, pool->NextPool->hdr.name, sizeof(pr.Name));
201 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
202 /* Try to create the pool */
203 if (create_pool(jcr, jcr->db, pool->NextPool, POOL_OP_CREATE) < 0) {
204 Jmsg(jcr, M_FATAL, 0, _("Pool \"%s\" not in database. %s"), pr.Name,
205 db_strerror(jcr->db));
208 Jmsg(jcr, M_INFO, 0, _("Pool \"%s\" created in database.\n"), pr.Name);
212 * put the "NextPool" resource pointer in our jcr so that we
213 * can pull the Storage reference from it.
215 prev_jcr->pool = jcr->pool = pool->NextPool;
216 prev_jcr->jr.PoolId = jcr->jr.PoolId = pr.PoolId;
219 /* If pool storage specified, use it instead of job storage for backup */
220 copy_storage(jcr, jcr->pool->storage);
222 /* Print Job Start message */
223 Jmsg(jcr, M_INFO, 0, _("Start Migration JobId %s, Job=%s\n"),
224 edit_uint64(jcr->JobId, ed1), jcr->Job);
226 set_jcr_job_status(jcr, JS_Running);
227 set_jcr_job_status(prev_jcr, JS_Running);
228 Dmsg2(000, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
229 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
230 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
234 if (!db_update_job_start_record(prev_jcr, prev_jcr->db, &prev_jcr->jr)) {
235 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(prev_jcr->db));
241 * Open a message channel connection with the Storage
242 * daemon. This is to let him know that our client
243 * will be contacting him for a backup session.
246 Dmsg0(110, "Open connection with storage daemon\n");
247 set_jcr_job_status(jcr, JS_WaitSD);
248 set_jcr_job_status(prev_jcr, JS_WaitSD);
250 * Start conversation with Storage daemon
252 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
255 sd = jcr->store_bsock;
257 * Now start a job with the Storage daemon
259 Dmsg2(000, "Read store=%s, write store=%s\n",
260 ((STORE *)prev_jcr->storage->first())->hdr.name,
261 ((STORE *)jcr->storage->first())->hdr.name);
262 if (!start_storage_daemon_job(jcr, prev_jcr->storage, jcr->storage)) {
265 Dmsg0(150, "Storage daemon connection OK\n");
267 if (!send_bootstrap_file(jcr, sd) ||
268 !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
273 * Now start a Storage daemon message thread
275 if (!start_storage_daemon_message_thread(jcr)) {
279 if (!bnet_fsend(sd, "run")) {
283 set_jcr_job_status(jcr, JS_Running);
284 set_jcr_job_status(prev_jcr, JS_Running);
286 /* Pickup Job termination data */
287 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
288 wait_for_storage_daemon_termination(jcr);
290 jcr->JobStatus = jcr->SDJobStatus;
291 if (jcr->JobStatus == JS_Terminated) {
292 migration_cleanup(jcr, jcr->JobStatus);
299 * Callback handler make list of JobIds
301 static int jobid_handler(void *ctx, int num_fields, char **row)
303 POOLMEM *JobIds = (POOLMEM *)ctx;
305 if (JobIds[0] != 0) {
306 pm_strcat(JobIds, ",");
308 pm_strcat(JobIds, row[0]);
318 static int item_compare(void *item1, void *item2)
320 uitem *i1 = (uitem *)item1;
321 uitem *i2 = (uitem *)item2;
322 return strcmp(i1->item, i2->item);
325 static int unique_name_handler(void *ctx, int num_fields, char **row)
327 dlist *list = (dlist *)ctx;
329 uitem *new_item = (uitem *)malloc(sizeof(uitem));
332 memset(new_item, 0, sizeof(uitem));
333 new_item->item = bstrdup(row[0]);
334 Dmsg1(000, "Item=%s\n", row[0]);
335 item = (uitem *)list->binary_insert((void *)new_item, item_compare);
336 if (item != new_item) { /* already in list */
337 free(new_item->item);
338 free((char *)new_item);
344 const char *sql_smallest_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 VolBytes ASC LIMIT 1";
350 const char *sql_oldest_vol =
351 "SELECT MediaId FROM Media,Pool WHERE"
352 " VolStatus in ('Full','Used','Error') AND"
353 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
354 " ORDER BY LastWritten ASC LIMIT 1";
356 const char *sql_jobids_from_mediaid =
357 "SELECT DISTINCT Job.JobId FROM JobMedia,Job"
358 " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId=%s"
359 " ORDER by Job.StartTime";
361 const char *sql_pool_bytes =
362 "SELECT SUM(VolBytes) FROM Media,Pool WHERE"
363 " VolStatus in ('Full','Used','Error','Append') AND"
364 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
366 const char *sql_vol_bytes =
367 "SELECT MediaId FROM Media,Pool WHERE"
368 " VolStatus in ('Full','Used','Error') AND"
369 " Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
370 " VolBytes<%s ORDER BY LastWritten ASC LIMIT 1";
372 const char *sql_client =
373 "SELECT DISTINCT Client.Name from Client,Pool,Media,Job,JobMedia "
374 " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
375 " JobMedia.JobId=Job.JobId AND Job.ClientId=Client.ClientId AND"
376 " Job.PoolId=Media.PoolId";
378 const char *sql_job =
379 "SELECT DISTINCT Job.Name from Job,Pool"
380 " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
382 const char *sql_jobids_from_job =
383 "SELECT DISTINCT Job.JobId FROM Job,Pool"
384 " WHERE Job.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
385 " ORDER by Job.StartTime";
388 const char *sql_ujobid =
389 "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
390 " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
391 " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
393 const char *sql_vol =
394 "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
395 " VolStatus in ('Full','Used','Error') AND"
396 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
400 * Returns: false on error
401 * true if OK and jcr->previous_jr filled in
403 static bool get_job_to_migrate(JCR *jcr)
406 POOL_MEM query(PM_MESSAGE);
407 POOLMEM *JobIds = get_pool_memory(PM_MESSAGE);
413 uitem *last_item = NULL;
418 if (jcr->MigrateJobId != 0) {
419 jcr->previous_jr.JobId = jcr->MigrateJobId;
420 Dmsg1(000, "previous jobid=%u\n", jcr->MigrateJobId);
422 switch (jcr->job->selection_type) {
424 if (!jcr->job->selection_pattern) {
425 Jmsg(jcr, M_FATAL, 0, _("No Migration Job selection pattern specified.\n"));
428 Dmsg1(000, "Job regex=%s\n", jcr->job->selection_pattern);
429 /* Complie regex expression */
430 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
432 regerror(rc, &preg, prbuf, sizeof(prbuf));
433 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
434 jcr->job->selection_pattern, prbuf);
437 item_chain = New(dlist(item, &item->link));
438 /* Basic query for Job names */
439 Mmsg(query, sql_job, jcr->pool->hdr.name);
440 Dmsg1(000, "query=%s\n", query.c_str());
441 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
442 (void *)item_chain)) {
443 Jmsg(jcr, M_FATAL, 0,
444 _("SQL to get Job failed. ERR=%s\n"), db_strerror(jcr->db));
447 /* Now apply the regex to the job names and remove any item not matched */
448 foreach_dlist(item, item_chain) {
449 const int nmatch = 30;
450 regmatch_t pmatch[nmatch];
452 free(last_item->item);
453 Dmsg1(000, "Remove item %s\n", last_item->item);
454 item_chain->remove(last_item);
456 Dmsg1(000, "Jobitem=%s\n", item->item);
457 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
459 last_item = NULL; /* keep this one */
465 free(last_item->item);
466 Dmsg1(000, "Remove item %s\n", last_item->item);
467 item_chain->remove(last_item);
471 * At this point, we have a list of items in item_chain
472 * that have been matched by the regex, so now we need
473 * to look up their jobids.
476 foreach_dlist(item, item_chain) {
477 Dmsg1(000, "Got Job: %s\n", item->item);
478 Mmsg(query, sql_jobids_from_job, item->item, jcr->pool->hdr.name);
479 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
480 Jmsg(jcr, M_FATAL, 0,
481 _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
485 if (JobIds[0] == 0) {
486 Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
489 Dmsg1(000, "Job Jobids=%s\n", JobIds);
492 case MT_SMALLEST_VOL:
493 Mmsg(query, sql_smallest_vol, jcr->pool->hdr.name);
495 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
496 Jmsg(jcr, M_FATAL, 0,
497 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
500 if (JobIds[0] == 0) {
501 Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
504 /* ***FIXME*** must loop over JobIds */
505 Mmsg(query, sql_jobids_from_mediaid, JobIds);
507 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
508 Jmsg(jcr, M_FATAL, 0,
509 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
512 Dmsg1(000, "Smallest Vol Jobids=%s\n", JobIds);
515 Mmsg(query, sql_oldest_vol, jcr->pool->hdr.name);
517 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
518 Jmsg(jcr, M_FATAL, 0,
519 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
522 if (JobIds[0] == 0) {
523 Jmsg(jcr, M_INFO, 0, _("No Volume found to migrate.\n"));
526 Mmsg(query, sql_jobids_from_mediaid, JobIds);
528 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
529 Jmsg(jcr, M_FATAL, 0,
530 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
533 Dmsg1(000, "Oldest Vol Jobids=%s\n", JobIds);
535 case MT_POOL_OCCUPANCY:
536 Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
538 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
539 Jmsg(jcr, M_FATAL, 0,
540 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
543 if (JobIds[0] == 0) {
544 Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
547 Dmsg1(000, "Pool Occupancy Jobids=%s\n", JobIds);
550 Dmsg0(000, "Pool time not implemented\n");
553 if (!jcr->job->selection_pattern) {
554 Jmsg(jcr, M_FATAL, 0, _("No Migration Client selection pattern specified.\n"));
557 Dmsg1(000, "Client regex=%s\n", jcr->job->selection_pattern);
558 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
560 regerror(rc, &preg, prbuf, sizeof(prbuf));
561 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
562 jcr->job->selection_pattern, prbuf);
564 item_chain = New(dlist(item, &item->link));
565 Mmsg(query, sql_client, jcr->pool->hdr.name);
566 Dmsg1(100, "query=%s\n", query.c_str());
567 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
568 (void *)item_chain)) {
569 Jmsg(jcr, M_FATAL, 0,
570 _("SQL to get Client failed. ERR=%s\n"), db_strerror(jcr->db));
573 /* Now apply the regex and create the jobs */
574 foreach_dlist(item, item_chain) {
575 const int nmatch = 30;
576 regmatch_t pmatch[nmatch];
577 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
579 Dmsg1(000, "Do Client=%s\n", item->item);
587 if (!jcr->job->selection_pattern) {
588 Jmsg(jcr, M_FATAL, 0, _("No Migration Volume selection pattern specified.\n"));
591 Dmsg1(000, "Volume regex=%s\n", jcr->job->selection_pattern);
592 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
594 regerror(rc, &preg, prbuf, sizeof(prbuf));
595 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
596 jcr->job->selection_pattern, prbuf);
598 item_chain = New(dlist(item, &item->link));
599 Mmsg(query, sql_vol, jcr->pool->hdr.name);
600 Dmsg1(100, "query=%s\n", query.c_str());
601 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
602 (void *)item_chain)) {
603 Jmsg(jcr, M_FATAL, 0,
604 _("SQL to get Job failed. ERR=%s\n"), db_strerror(jcr->db));
607 /* Now apply the regex and create the jobs */
608 foreach_dlist(item, item_chain) {
609 const int nmatch = 30;
610 regmatch_t pmatch[nmatch];
611 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
613 Dmsg1(000, "Do Vol=%s\n", item->item);
622 if (!jcr->job->selection_pattern) {
623 Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
626 Dmsg1(000, "SQL=%s\n", jcr->job->selection_pattern);
627 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
628 Jmsg(jcr, M_FATAL, 0,
629 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
632 if (JobIds[0] == 0) {
633 Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
636 Dmsg1(000, "Jobids=%s\n", JobIds);
640 Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
647 stat = get_next_jobid_from_list(&p, &JobId);
648 Dmsg2(000, "get_next_jobid stat=%d JobId=%u\n", stat, JobId);
650 Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
652 } else if (stat == 0) {
653 Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
657 jcr->previous_jr.JobId = JobId;
658 Dmsg1(000, "Last jobid=%d\n", jcr->previous_jr.JobId);
660 if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
661 Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
662 edit_int64(jcr->previous_jr.JobId, ed1),
663 db_strerror(jcr->db));
666 Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
667 jcr->previous_jr.JobId, jcr->previous_jr.Job);
670 free_pool_memory(JobIds);
674 free_pool_memory(JobIds);
680 * Release resources allocated during backup.
682 void migration_cleanup(JCR *jcr, int TermCode)
684 char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
685 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
686 char term_code[100], sd_term_msg[100];
687 const char *term_msg;
692 JCR *prev_jcr = jcr->previous_jcr;
693 POOL_MEM query(PM_MESSAGE);
695 Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
696 dequeue_messages(jcr); /* display any queued messages */
697 memset(&mr, 0, sizeof(mr));
698 set_jcr_job_status(jcr, TermCode);
699 update_job_end_record(jcr); /* update database */
701 /* Check if we actually did something */
703 prev_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
704 prev_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
705 prev_jcr->VolSessionId = jcr->VolSessionId;
706 prev_jcr->VolSessionTime = jcr->VolSessionTime;
708 set_jcr_job_status(prev_jcr, TermCode);
710 update_job_end_record(prev_jcr);
712 Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
713 "JobTDate=%s WHERE JobId=%s",
714 jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime,
715 edit_uint64(jcr->previous_jr.JobTDate, ec1),
716 edit_uint64(prev_jcr->jr.JobId, ec2));
717 db_sql_query(prev_jcr->db, query.c_str(), NULL, NULL);
719 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
720 Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
721 db_strerror(jcr->db));
722 set_jcr_job_status(jcr, JS_ErrorTerminated);
725 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
726 if (!db_get_media_record(jcr, jcr->db, &mr)) {
727 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
728 mr.VolumeName, db_strerror(jcr->db));
729 set_jcr_job_status(jcr, JS_ErrorTerminated);
732 update_bootstrap_file(prev_jcr);
734 if (!db_get_job_volume_names(prev_jcr, prev_jcr->db, prev_jcr->jr.JobId, &prev_jcr->VolumeName)) {
736 * Note, if the job has erred, most likely it did not write any
737 * tape, so suppress this "error" message since in that case
738 * it is normal. Or look at it the other way, only for a
739 * normal exit should we complain about this error.
741 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
742 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(prev_jcr->db));
744 prev_jcr->VolumeName[0] = 0; /* none */
748 msg_type = M_INFO; /* by default INFO message */
749 switch (jcr->JobStatus) {
751 if (jcr->Errors || jcr->SDErrors) {
752 term_msg = _("%s OK -- with warnings");
754 term_msg = _("%s OK");
758 case JS_ErrorTerminated:
759 term_msg = _("*** %s Error ***");
760 msg_type = M_ERROR; /* Generate error message */
761 if (jcr->store_bsock) {
762 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
763 if (jcr->SD_msg_chan) {
764 pthread_cancel(jcr->SD_msg_chan);
769 term_msg = _("%s Canceled");
770 if (jcr->store_bsock) {
771 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
772 if (jcr->SD_msg_chan) {
773 pthread_cancel(jcr->SD_msg_chan);
778 term_msg = _("Inappropriate %s term code");
781 bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
782 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
783 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
784 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
788 kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
792 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
794 // bmicrosleep(15, 0); /* for debugging SIGHUP */
796 Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
797 " Old Backup JobId: %u\n"
798 " New Backup JobId: %u\n"
801 " Backup Level: %s%s\n"
803 " FileSet: \"%s\" %s\n"
807 " Elapsed time: %s\n"
809 " SD Files Written: %s\n"
810 " SD Bytes Written: %s (%sB)\n"
812 " Volume name(s): %s\n"
813 " Volume Session Id: %d\n"
814 " Volume Session Time: %d\n"
815 " Last Volume Bytes: %s (%sB)\n"
817 " SD termination status: %s\n"
818 " Termination: %s\n\n"),
822 prev_jcr ? jcr->previous_jr.JobId : 0,
823 prev_jcr ? prev_jcr->jr.JobId : 0,
826 level_to_str(jcr->JobLevel), jcr->since,
827 jcr->client->hdr.name,
828 jcr->fileset->hdr.name, jcr->FSCreateTime,
832 edit_utime(RunTime, elapsed, sizeof(elapsed)),
834 edit_uint64_with_commas(jcr->SDJobFiles, ec1),
835 edit_uint64_with_commas(jcr->SDJobBytes, ec2),
836 edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
838 prev_jcr ? prev_jcr->VolumeName : "",
841 edit_uint64_with_commas(mr.VolBytes, ec4),
842 edit_uint64_with_suffix(mr.VolBytes, ec5),
847 Dmsg1(000, "migrate_cleanup() previous_jcr=0x%x\n", jcr->previous_jcr);
848 if (jcr->previous_jcr) {
849 // free_jcr(jcr->previous_jcr);
850 // jcr->previous_jcr = NULL;
852 Dmsg0(000, "Leave migrate_cleanup()\n");