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(100, "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(100, "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(100, "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]);
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 Pool,Media,Job,JobMedia "
374 " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
375 " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
377 const char *sql_ujobid =
378 "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
379 " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
380 " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
382 const char *sql_vol =
383 "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
384 " VolStatus in ('Full','Used','Error') AND"
385 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
389 * Returns: false on error
390 * true if OK and jcr->previous_jr filled in
392 static bool get_job_to_migrate(JCR *jcr)
395 POOL_MEM query(PM_MESSAGE);
396 POOLMEM *JobIds = get_pool_memory(PM_MESSAGE);
405 if (jcr->MigrateJobId != 0) {
406 jcr->previous_jr.JobId = jcr->MigrateJobId;
408 switch (jcr->job->selection_type) {
409 case MT_SMALLEST_VOL:
410 Mmsg(query, sql_smallest_vol, jcr->pool->hdr.name);
411 JobIds = get_pool_memory(PM_MESSAGE);
413 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
414 Jmsg(jcr, M_FATAL, 0,
415 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
418 if (JobIds[0] == 0) {
419 Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
422 /* ***FIXME*** must loop over JobIds */
423 Mmsg(query, sql_jobids_from_mediaid, JobIds);
425 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
426 Jmsg(jcr, M_FATAL, 0,
427 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
430 Dmsg1(000, "Jobids=%s\n", JobIds);
433 Mmsg(query, sql_oldest_vol, jcr->pool->hdr.name);
434 JobIds = get_pool_memory(PM_MESSAGE);
436 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
437 Jmsg(jcr, M_FATAL, 0,
438 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
441 if (JobIds[0] == 0) {
442 Jmsg(jcr, M_INFO, 0, _("No Volume found to migrate.\n"));
445 Mmsg(query, sql_jobids_from_mediaid, JobIds);
447 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
448 Jmsg(jcr, M_FATAL, 0,
449 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
452 Dmsg1(000, "Jobids=%s\n", JobIds);
454 case MT_POOL_OCCUPANCY:
455 Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
456 JobIds = get_pool_memory(PM_MESSAGE);
458 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
459 Jmsg(jcr, M_FATAL, 0,
460 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
463 if (JobIds[0] == 0) {
464 Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
471 if (!jcr->job->selection_pattern) {
472 Jmsg(jcr, M_FATAL, 0, _("No selection pattern specified.\n"));
475 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
477 regerror(rc, &preg, prbuf, sizeof(prbuf));
478 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
479 jcr->job->selection_pattern, prbuf);
481 item_chain = New(dlist(item, &item->link));
482 Mmsg(query, sql_client, jcr->pool->hdr.name);
483 Dmsg1(100, "query=%s\n", query.c_str());
484 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
485 (void *)item_chain)) {
486 Jmsg(jcr, M_FATAL, 0,
487 _("SQL to get Client failed. ERR=%s\n"), db_strerror(jcr->db));
490 /* Now apply the regex and create the jobs */
491 foreach_dlist(item, item_chain) {
492 const int nmatch = 30;
493 regmatch_t pmatch[nmatch];
494 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
496 Dmsg1(000, "Do Client=%s\n", item->item);
504 if (!jcr->job->selection_pattern) {
505 Jmsg(jcr, M_FATAL, 0, _("No selection pattern specified.\n"));
508 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
510 regerror(rc, &preg, prbuf, sizeof(prbuf));
511 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
512 jcr->job->selection_pattern, prbuf);
514 item_chain = New(dlist(item, &item->link));
515 Mmsg(query, sql_vol, jcr->pool->hdr.name);
516 Dmsg1(100, "query=%s\n", query.c_str());
517 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
518 (void *)item_chain)) {
519 Jmsg(jcr, M_FATAL, 0,
520 _("SQL to get Job failed. ERR=%s\n"), db_strerror(jcr->db));
523 /* Now apply the regex and create the jobs */
524 foreach_dlist(item, item_chain) {
525 const int nmatch = 30;
526 regmatch_t pmatch[nmatch];
527 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
529 Dmsg1(000, "Do Vol=%s\n", item->item);
537 if (!jcr->job->selection_pattern) {
538 Jmsg(jcr, M_FATAL, 0, _("No selection pattern specified.\n"));
541 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
543 regerror(rc, &preg, prbuf, sizeof(prbuf));
544 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
545 jcr->job->selection_pattern, prbuf);
547 item_chain = New(dlist(item, &item->link));
548 Mmsg(query, sql_job, jcr->pool->hdr.name);
549 Dmsg1(100, "query=%s\n", query.c_str());
550 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
551 (void *)item_chain)) {
552 Jmsg(jcr, M_FATAL, 0,
553 _("SQL to get Job failed. ERR=%s\n"), db_strerror(jcr->db));
556 /* Now apply the regex and create the jobs */
557 foreach_dlist(item, item_chain) {
558 const int nmatch = 30;
559 regmatch_t pmatch[nmatch];
560 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
562 Dmsg1(000, "Do Job=%s\n", item->item);
571 if (!jcr->job->selection_pattern) {
572 Jmsg(jcr, M_FATAL, 0, _("No selection pattern specified.\n"));
575 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
576 Jmsg(jcr, M_FATAL, 0,
577 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
580 if (JobIds[0] == 0) {
581 Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
584 Dmsg1(000, "Jobids=%s\n", JobIds);
588 Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
594 stat = get_next_jobid_from_list(&p, &JobId);
595 Dmsg2(000, "get_next_jobid stat=%d JobId=%u\n", stat, JobId);
597 Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
599 } else if (stat == 0) {
600 Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
604 jcr->previous_jr.JobId = JobId;
605 Dmsg1(000, "Last jobid=%d\n", jcr->previous_jr.JobId);
607 if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
608 Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
609 edit_int64(jcr->previous_jr.JobId, ed1),
610 db_strerror(jcr->db));
613 Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
614 jcr->previous_jr.JobId, jcr->previous_jr.Job);
617 free_pool_memory(JobIds);
621 free_pool_memory(JobIds);
627 * Release resources allocated during backup.
629 void migration_cleanup(JCR *jcr, int TermCode)
631 char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
632 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
633 char term_code[100], sd_term_msg[100];
634 const char *term_msg;
639 JCR *tjcr = jcr->previous_jcr;
640 POOL_MEM query(PM_MESSAGE);
642 /* Ensure target is defined to avoid a lot of testing */
646 tjcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
647 tjcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
648 tjcr->VolSessionId = jcr->VolSessionId;
649 tjcr->VolSessionTime = jcr->VolSessionTime;
651 Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
652 dequeue_messages(jcr); /* display any queued messages */
653 memset(&mr, 0, sizeof(mr));
654 set_jcr_job_status(jcr, TermCode);
655 set_jcr_job_status(tjcr, TermCode);
658 update_job_end_record(jcr); /* update database */
659 update_job_end_record(tjcr);
661 Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
662 "JobTDate=%s WHERE JobId=%s",
663 jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime,
664 edit_uint64(jcr->previous_jr.JobTDate, ec1),
665 edit_uint64(tjcr->jr.JobId, ec2));
666 db_sql_query(tjcr->db, query.c_str(), NULL, NULL);
668 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
669 Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
670 db_strerror(jcr->db));
671 set_jcr_job_status(jcr, JS_ErrorTerminated);
674 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
675 if (!db_get_media_record(jcr, jcr->db, &mr)) {
676 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
677 mr.VolumeName, db_strerror(jcr->db));
678 set_jcr_job_status(jcr, JS_ErrorTerminated);
681 update_bootstrap_file(tjcr);
683 msg_type = M_INFO; /* by default INFO message */
684 switch (jcr->JobStatus) {
686 if (jcr->Errors || jcr->SDErrors) {
687 term_msg = _("%s OK -- with warnings");
689 term_msg = _("%s OK");
693 case JS_ErrorTerminated:
694 term_msg = _("*** %s Error ***");
695 msg_type = M_ERROR; /* Generate error message */
696 if (jcr->store_bsock) {
697 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
698 if (jcr->SD_msg_chan) {
699 pthread_cancel(jcr->SD_msg_chan);
704 term_msg = _("%s Canceled");
705 if (jcr->store_bsock) {
706 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
707 if (jcr->SD_msg_chan) {
708 pthread_cancel(jcr->SD_msg_chan);
713 term_msg = _("Inappropriate %s term code");
716 bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
717 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
718 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
719 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
723 kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
725 if (!db_get_job_volume_names(tjcr, tjcr->db, tjcr->jr.JobId, &tjcr->VolumeName)) {
727 * Note, if the job has erred, most likely it did not write any
728 * tape, so suppress this "error" message since in that case
729 * it is normal. Or look at it the other way, only for a
730 * normal exit should we complain about this error.
732 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
733 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(tjcr->db));
735 tjcr->VolumeName[0] = 0; /* none */
738 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
740 // bmicrosleep(15, 0); /* for debugging SIGHUP */
742 Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
743 " Old Backup JobId: %u\n"
744 " New Backup JobId: %u\n"
747 " Backup Level: %s%s\n"
749 " FileSet: \"%s\" %s\n"
753 " Elapsed time: %s\n"
755 " SD Files Written: %s\n"
756 " SD Bytes Written: %s (%sB)\n"
758 " Volume name(s): %s\n"
759 " Volume Session Id: %d\n"
760 " Volume Session Time: %d\n"
761 " Last Volume Bytes: %s (%sB)\n"
763 " SD termination status: %s\n"
764 " Termination: %s\n\n"),
768 jcr->previous_jr.JobId,
772 level_to_str(jcr->JobLevel), jcr->since,
773 jcr->client->hdr.name,
774 jcr->fileset->hdr.name, jcr->FSCreateTime,
778 edit_utime(RunTime, elapsed, sizeof(elapsed)),
780 edit_uint64_with_commas(jcr->SDJobFiles, ec1),
781 edit_uint64_with_commas(jcr->SDJobBytes, ec2),
782 edit_uint64_with_suffix(jcr->jr.JobBytes, ec3),
787 edit_uint64_with_commas(mr.VolBytes, ec4),
788 edit_uint64_with_suffix(mr.VolBytes, ec5),
793 Dmsg1(100, "Leave migrate_cleanup() previous_jcr=0x%x\n", jcr->previous_jcr);
794 if (jcr->previous_jcr) {
795 free_jcr(jcr->previous_jcr);