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 (!get_job_to_migrate(jcr)) {
56 if (jcr->previous_jr.JobId == 0) {
57 return true; /* no work */
60 if (!get_or_create_fileset_record(jcr)) {
65 * Get the Pool record -- first apply any level defined pools
67 switch (jcr->previous_jr.JobLevel) {
70 jcr->pool = jcr->full_pool;
75 jcr->pool = jcr->inc_pool;
80 jcr->pool = jcr->dif_pool;
84 memset(&pr, 0, sizeof(pr));
85 bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
87 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
88 /* Try to create the pool */
89 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
90 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
91 db_strerror(jcr->db));
94 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
97 jcr->jr.PoolId = pr.PoolId;
99 /* If pool storage specified, use it instead of job storage */
100 copy_storage(jcr, jcr->pool->storage);
103 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
107 if (!create_restore_bootstrap_file(jcr)) {
114 * Do a Migration of a previous job
116 * Returns: false on failure
119 bool do_migration(JCR *jcr)
128 if (jcr->previous_jr.JobId == 0) {
129 jcr->JobStatus = JS_Terminated;
130 migration_cleanup(jcr, jcr->JobStatus);
131 return true; /* no work */
133 Dmsg4(000, "Target: Name=%s JobId=%d Type=%c Level=%c\n",
134 jcr->previous_jr.Name, jcr->previous_jr.JobId,
135 jcr->previous_jr.JobType, jcr->previous_jr.JobLevel);
137 Dmsg4(000, "Current: Name=%s JobId=%d Type=%c Level=%c\n",
138 jcr->jr.Name, jcr->jr.JobId,
139 jcr->jr.JobType, jcr->jr.JobLevel);
142 job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
143 tjob = (JOB *)GetResWithName(R_JOB, jcr->previous_jr.Name);
150 * Target jcr is the new Job that corresponds to the original
151 * target job. It "runs" at the same time as the current
152 * migration job and becomes a new backup job that replaces
153 * the original backup job. Most operations on the current
154 * migration jcr are also done on the target jcr.
156 tjcr = jcr->previous_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
157 memcpy(&tjcr->previous_jr, &jcr->previous_jr, sizeof(tjcr->previous_jr));
159 /* Turn the tjcr into a "real" job */
160 set_jcr_defaults(tjcr, tjob);
161 if (!setup_job(tjcr)) {
164 /* Set output PoolId and FileSetId. */
165 tjcr->jr.PoolId = jcr->jr.PoolId;
166 tjcr->jr.FileSetId = jcr->jr.FileSetId;
169 * Get the PoolId used with the original job. Then
170 * find the pool name from the database record.
172 memset(&pr, 0, sizeof(pr));
173 pr.PoolId = tjcr->previous_jr.PoolId;
174 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
175 Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
176 edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
179 /* Get the pool resource corresponding to the original job */
180 pool = (POOL *)GetResWithName(R_POOL, pr.Name);
182 Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
186 /* Check Migration time and High/Low water marks */
189 /* If pool storage specified, use it for restore */
190 copy_storage(tjcr, pool->storage);
192 /* If the original backup pool has a NextPool, make sure a
193 * record exists in the database.
195 if (pool->NextPool) {
196 memset(&pr, 0, sizeof(pr));
197 bstrncpy(pr.Name, pool->NextPool->hdr.name, sizeof(pr.Name));
199 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
200 /* Try to create the pool */
201 if (create_pool(jcr, jcr->db, pool->NextPool, POOL_OP_CREATE) < 0) {
202 Jmsg(jcr, M_FATAL, 0, _("Pool \"%s\" not in database. %s"), pr.Name,
203 db_strerror(jcr->db));
206 Jmsg(jcr, M_INFO, 0, _("Pool \"%s\" created in database.\n"), pr.Name);
210 * put the "NextPool" resource pointer in our jcr so that we
211 * can pull the Storage reference from it.
213 tjcr->pool = jcr->pool = pool->NextPool;
214 tjcr->jr.PoolId = jcr->jr.PoolId = pr.PoolId;
217 /* If pool storage specified, use it instead of job storage for backup */
218 copy_storage(jcr, jcr->pool->storage);
220 /* Print Job Start message */
221 Jmsg(jcr, M_INFO, 0, _("Start Migration JobId %s, Job=%s\n"),
222 edit_uint64(jcr->JobId, ed1), jcr->Job);
224 set_jcr_job_status(jcr, JS_Running);
225 set_jcr_job_status(tjcr, JS_Running);
226 Dmsg2(000, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
227 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
228 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
232 if (!db_update_job_start_record(tjcr, tjcr->db, &tjcr->jr)) {
233 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(tjcr->db));
239 * Open a message channel connection with the Storage
240 * daemon. This is to let him know that our client
241 * will be contacting him for a backup session.
244 Dmsg0(110, "Open connection with storage daemon\n");
245 set_jcr_job_status(jcr, JS_WaitSD);
246 set_jcr_job_status(tjcr, JS_WaitSD);
248 * Start conversation with Storage daemon
250 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
253 sd = jcr->store_bsock;
255 * Now start a job with the Storage daemon
257 Dmsg2(000, "Read store=%s, write store=%s\n",
258 ((STORE *)tjcr->storage->first())->hdr.name,
259 ((STORE *)jcr->storage->first())->hdr.name);
260 if (!start_storage_daemon_job(jcr, tjcr->storage, jcr->storage)) {
263 Dmsg0(150, "Storage daemon connection OK\n");
265 if (!send_bootstrap_file(jcr, sd) ||
266 !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
271 * Now start a Storage daemon message thread
273 if (!start_storage_daemon_message_thread(jcr)) {
277 if (!bnet_fsend(sd, "run")) {
281 set_jcr_job_status(jcr, JS_Running);
282 set_jcr_job_status(tjcr, JS_Running);
284 /* Pickup Job termination data */
285 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
286 wait_for_storage_daemon_termination(jcr);
288 jcr->JobStatus = jcr->SDJobStatus;
289 if (jcr->JobStatus == JS_Terminated) {
290 migration_cleanup(jcr, jcr->JobStatus);
297 * Callback handler make list of JobIds
299 static int jobid_handler(void *ctx, int num_fields, char **row)
301 POOLMEM *JobIds = (POOLMEM *)ctx;
303 if (JobIds[0] != 0) {
304 pm_strcat(JobIds, ",");
306 pm_strcat(JobIds, row[0]);
316 static int item_compare(void *item1, void *item2)
318 uitem *i1 = (uitem *)item1;
319 uitem *i2 = (uitem *)item2;
320 return strcmp(i1->item, i2->item);
323 static int unique_name_handler(void *ctx, int num_fields, char **row)
325 dlist *list = (dlist *)ctx;
327 uitem *new_item = (uitem *)malloc(sizeof(uitem));
330 memset(new_item, 0, sizeof(uitem));
331 new_item->item = bstrdup(row[0]);
332 Dmsg1(000, "Item=%s\n", row[0]);
333 item = (uitem *)list->binary_insert((void *)new_item, item_compare);
334 if (item != new_item) { /* already in list */
335 free(new_item->item);
336 free((char *)new_item);
342 const char *sql_smallest_vol =
343 "SELECT MediaId FROM Media,Pool WHERE"
344 " VolStatus in ('Full','Used','Error') AND"
345 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
346 " ORDER BY VolBytes ASC LIMIT 1";
348 const char *sql_oldest_vol =
349 "SELECT MediaId FROM Media,Pool WHERE"
350 " VolStatus in ('Full','Used','Error') AND"
351 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
352 " ORDER BY LastWritten ASC LIMIT 1";
354 const char *sql_jobids_from_mediaid =
355 "SELECT DISTINCT Job.JobId FROM JobMedia,Job"
356 " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId=%s"
357 " ORDER by Job.StartTime";
359 const char *sql_pool_bytes =
360 "SELECT SUM(VolBytes) FROM Media,Pool WHERE"
361 " VolStatus in ('Full','Used','Error','Append') AND"
362 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
364 const char *sql_vol_bytes =
365 "SELECT MediaId FROM Media,Pool WHERE"
366 " VolStatus in ('Full','Used','Error') AND"
367 " Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
368 " VolBytes<%s ORDER BY LastWritten ASC LIMIT 1";
370 const char *sql_client =
371 "SELECT DISTINCT Client.Name from Client,Pool,Media,Job,JobMedia "
372 " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
373 " JobMedia.JobId=Job.JobId AND Job.ClientId=Client.ClientId AND"
374 " Job.PoolId=Media.PoolId";
376 const char *sql_job =
377 "SELECT DISTINCT Job.Name from Job,Pool"
378 " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
380 const char *sql_jobids_from_job =
381 "SELECT DISTINCT Job.JobId FROM Job,Pool"
382 " WHERE Job.Name=%s AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
383 " ORDER by Job.StartTime";
386 const char *sql_ujobid =
387 "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
388 " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
389 " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
391 const char *sql_vol =
392 "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
393 " VolStatus in ('Full','Used','Error') AND"
394 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
398 * Returns: false on error
399 * true if OK and jcr->previous_jr filled in
401 static bool get_job_to_migrate(JCR *jcr)
404 POOL_MEM query(PM_MESSAGE);
405 POOLMEM *JobIds = get_pool_memory(PM_MESSAGE);
411 uitem *last_item = NULL;
416 if (jcr->MigrateJobId != 0) {
417 jcr->previous_jr.JobId = jcr->MigrateJobId;
418 Dmsg1(000, "previous jobid=%u\n", jcr->MigrateJobId);
420 switch (jcr->job->selection_type) {
422 if (!jcr->job->selection_pattern) {
423 Jmsg(jcr, M_FATAL, 0, _("No Migration Job selection pattern specified.\n"));
426 Dmsg1(000, "Job regex=%s\n", jcr->job->selection_pattern);
427 /* Complie regex expression */
428 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
430 regerror(rc, &preg, prbuf, sizeof(prbuf));
431 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
432 jcr->job->selection_pattern, prbuf);
435 item_chain = New(dlist(item, &item->link));
436 /* Basic query for Job names */
437 Mmsg(query, sql_job, jcr->pool->hdr.name);
438 Dmsg1(000, "query=%s\n", query.c_str());
439 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
440 (void *)item_chain)) {
441 Jmsg(jcr, M_FATAL, 0,
442 _("SQL to get Job failed. ERR=%s\n"), db_strerror(jcr->db));
445 /* Now apply the regex to the job names and remove any item not matched */
446 foreach_dlist(item, item_chain) {
447 const int nmatch = 30;
448 regmatch_t pmatch[nmatch];
450 free(last_item->item);
451 Dmsg1(000, "Remove item %s\n", last_item->item);
452 item_chain->remove(last_item);
454 Dmsg1(000, "Jobitem=%s\n", item->item);
455 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
457 last_item = NULL; /* keep this one */
463 free(last_item->item);
464 Dmsg1(000, "Remove item %s\n", last_item->item);
465 item_chain->remove(last_item);
469 * At this point, we have a list of items in item_chain
470 * that have been matched by the regex, so now we need
471 * to look up their jobids.
473 JobIds = get_pool_memory(PM_MESSAGE);
475 foreach_dlist(item, item_chain) {
476 Dmsg1(000, "Got Job: %s\n", item->item);
477 Mmsg(query, sql_jobids_from_job, item->item, jcr->pool->hdr.name);
478 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
479 Jmsg(jcr, M_FATAL, 0,
480 _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
484 if (JobIds[0] == 0) {
485 Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
488 Dmsg1(000, "Job Jobids=%s\n", JobIds);
489 free_pool_memory(JobIds);
492 case MT_SMALLEST_VOL:
493 Mmsg(query, sql_smallest_vol, jcr->pool->hdr.name);
494 JobIds = get_pool_memory(PM_MESSAGE);
496 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
497 Jmsg(jcr, M_FATAL, 0,
498 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
501 if (JobIds[0] == 0) {
502 Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
505 /* ***FIXME*** must loop over JobIds */
506 Mmsg(query, sql_jobids_from_mediaid, JobIds);
508 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
509 Jmsg(jcr, M_FATAL, 0,
510 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
513 Dmsg1(000, "Smallest Vol Jobids=%s\n", JobIds);
516 Mmsg(query, sql_oldest_vol, jcr->pool->hdr.name);
517 JobIds = get_pool_memory(PM_MESSAGE);
519 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
520 Jmsg(jcr, M_FATAL, 0,
521 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
524 if (JobIds[0] == 0) {
525 Jmsg(jcr, M_INFO, 0, _("No Volume found to migrate.\n"));
528 Mmsg(query, sql_jobids_from_mediaid, JobIds);
530 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
531 Jmsg(jcr, M_FATAL, 0,
532 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
535 Dmsg1(000, "Oldest Vol Jobids=%s\n", JobIds);
537 case MT_POOL_OCCUPANCY:
538 Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
539 JobIds = get_pool_memory(PM_MESSAGE);
541 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
542 Jmsg(jcr, M_FATAL, 0,
543 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
546 if (JobIds[0] == 0) {
547 Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
550 Dmsg1(000, "Pool Occupancy Jobids=%s\n", JobIds);
553 Dmsg0(000, "Pool time not implemented\n");
556 if (!jcr->job->selection_pattern) {
557 Jmsg(jcr, M_FATAL, 0, _("No Migration Client selection pattern specified.\n"));
560 Dmsg1(000, "Client regex=%s\n", jcr->job->selection_pattern);
561 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
563 regerror(rc, &preg, prbuf, sizeof(prbuf));
564 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
565 jcr->job->selection_pattern, prbuf);
567 item_chain = New(dlist(item, &item->link));
568 Mmsg(query, sql_client, jcr->pool->hdr.name);
569 Dmsg1(100, "query=%s\n", query.c_str());
570 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
571 (void *)item_chain)) {
572 Jmsg(jcr, M_FATAL, 0,
573 _("SQL to get Client failed. ERR=%s\n"), db_strerror(jcr->db));
576 /* Now apply the regex and create the jobs */
577 foreach_dlist(item, item_chain) {
578 const int nmatch = 30;
579 regmatch_t pmatch[nmatch];
580 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
582 Dmsg1(000, "Do Client=%s\n", item->item);
590 if (!jcr->job->selection_pattern) {
591 Jmsg(jcr, M_FATAL, 0, _("No Migration Volume selection pattern specified.\n"));
594 Dmsg1(000, "Volume regex=%s\n", jcr->job->selection_pattern);
595 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
597 regerror(rc, &preg, prbuf, sizeof(prbuf));
598 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
599 jcr->job->selection_pattern, prbuf);
601 item_chain = New(dlist(item, &item->link));
602 Mmsg(query, sql_vol, jcr->pool->hdr.name);
603 Dmsg1(100, "query=%s\n", query.c_str());
604 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
605 (void *)item_chain)) {
606 Jmsg(jcr, M_FATAL, 0,
607 _("SQL to get Job failed. ERR=%s\n"), db_strerror(jcr->db));
610 /* Now apply the regex and create the jobs */
611 foreach_dlist(item, item_chain) {
612 const int nmatch = 30;
613 regmatch_t pmatch[nmatch];
614 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
616 Dmsg1(000, "Do Vol=%s\n", item->item);
625 if (!jcr->job->selection_pattern) {
626 Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
629 Dmsg1(000, "SQL=%s\n", jcr->job->selection_pattern);
630 if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
631 Jmsg(jcr, M_FATAL, 0,
632 _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
635 if (JobIds[0] == 0) {
636 Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
639 Dmsg1(000, "Jobids=%s\n", JobIds);
643 Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
650 stat = get_next_jobid_from_list(&p, &JobId);
651 Dmsg2(000, "get_next_jobid stat=%d JobId=%u\n", stat, JobId);
653 Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
655 } else if (stat == 0) {
656 Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
660 jcr->previous_jr.JobId = JobId;
661 Dmsg1(000, "Last jobid=%d\n", jcr->previous_jr.JobId);
663 if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
664 Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
665 edit_int64(jcr->previous_jr.JobId, ed1),
666 db_strerror(jcr->db));
669 Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
670 jcr->previous_jr.JobId, jcr->previous_jr.Job);
673 free_pool_memory(JobIds);
677 free_pool_memory(JobIds);
683 * Release resources allocated during backup.
685 void migration_cleanup(JCR *jcr, int TermCode)
687 char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
688 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
689 char term_code[100], sd_term_msg[100];
690 const char *term_msg;
695 JCR *prev_jcr = jcr->previous_jcr;
696 POOL_MEM query(PM_MESSAGE);
698 Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
699 dequeue_messages(jcr); /* display any queued messages */
700 memset(&mr, 0, sizeof(mr));
701 set_jcr_job_status(jcr, TermCode);
702 update_job_end_record(jcr); /* update database */
704 /* Check if we actually did something */
706 prev_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
707 prev_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
708 prev_jcr->VolSessionId = jcr->VolSessionId;
709 prev_jcr->VolSessionTime = jcr->VolSessionTime;
711 set_jcr_job_status(prev_jcr, TermCode);
714 update_job_end_record(prev_jcr);
716 Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
717 "JobTDate=%s WHERE JobId=%s",
718 jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime,
719 edit_uint64(jcr->previous_jr.JobTDate, ec1),
720 edit_uint64(prev_jcr->jr.JobId, ec2));
721 db_sql_query(prev_jcr->db, query.c_str(), NULL, NULL);
723 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
724 Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
725 db_strerror(jcr->db));
726 set_jcr_job_status(jcr, JS_ErrorTerminated);
729 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
730 if (!db_get_media_record(jcr, jcr->db, &mr)) {
731 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
732 mr.VolumeName, db_strerror(jcr->db));
733 set_jcr_job_status(jcr, JS_ErrorTerminated);
736 update_bootstrap_file(prev_jcr);
738 if (!db_get_job_volume_names(prev_jcr, prev_jcr->db, prev_jcr->jr.JobId, &prev_jcr->VolumeName)) {
740 * Note, if the job has erred, most likely it did not write any
741 * tape, so suppress this "error" message since in that case
742 * it is normal. Or look at it the other way, only for a
743 * normal exit should we complain about this error.
745 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
746 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(prev_jcr->db));
748 prev_jcr->VolumeName[0] = 0; /* none */
752 msg_type = M_INFO; /* by default INFO message */
753 switch (jcr->JobStatus) {
755 if (jcr->Errors || jcr->SDErrors) {
756 term_msg = _("%s OK -- with warnings");
758 term_msg = _("%s OK");
762 case JS_ErrorTerminated:
763 term_msg = _("*** %s Error ***");
764 msg_type = M_ERROR; /* Generate error message */
765 if (jcr->store_bsock) {
766 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
767 if (jcr->SD_msg_chan) {
768 pthread_cancel(jcr->SD_msg_chan);
773 term_msg = _("%s Canceled");
774 if (jcr->store_bsock) {
775 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
776 if (jcr->SD_msg_chan) {
777 pthread_cancel(jcr->SD_msg_chan);
782 term_msg = _("Inappropriate %s term code");
785 bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
786 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
787 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
788 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
792 kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
796 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
798 // bmicrosleep(15, 0); /* for debugging SIGHUP */
800 Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
801 " Old Backup JobId: %u\n"
802 " New Backup JobId: %u\n"
805 " Backup Level: %s%s\n"
807 " FileSet: \"%s\" %s\n"
811 " Elapsed time: %s\n"
813 " SD Files Written: %s\n"
814 " SD Bytes Written: %s (%sB)\n"
816 " Volume name(s): %s\n"
817 " Volume Session Id: %d\n"
818 " Volume Session Time: %d\n"
819 " Last Volume Bytes: %s (%sB)\n"
821 " SD termination status: %s\n"
822 " Termination: %s\n\n"),
826 prev_jcr ? jcr->previous_jr.JobId : 0,
827 prev_jcr ? prev_jcr->jr.JobId : 0,
830 level_to_str(jcr->JobLevel), jcr->since,
831 jcr->client->hdr.name,
832 jcr->fileset->hdr.name, jcr->FSCreateTime,
836 edit_utime(RunTime, elapsed, sizeof(elapsed)),
838 edit_uint64_with_commas(jcr->SDJobFiles, ec1),
839 edit_uint64_with_commas(jcr->SDJobBytes, ec2),
840 edit_uint64_with_suffix(jcr->jr.JobBytes, ec3),
842 prev_jcr ? prev_jcr->VolumeName : "",
845 edit_uint64_with_commas(mr.VolBytes, ec4),
846 edit_uint64_with_suffix(mr.VolBytes, ec5),
851 Dmsg1(100, "Leave migrate_cleanup() previous_jcr=0x%x\n", jcr->previous_jcr);
852 if (jcr->previous_jcr) {
853 free_jcr(jcr->previous_jcr);