]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/migrate.c
Correct pool source setting
[bacula/bacula] / bacula / src / dird / migrate.c
1 /*
2  *
3  *   Bacula Director -- migrate.c -- responsible for doing
4  *     migration jobs.
5  *
6  *     Kern Sibbald, September MMIV
7  *
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
12  *       to do the backup.
13  *     When the Storage daemon finishes the job, update the DB.
14  *
15  *   Version $Id$
16  */
17 /*
18    Copyright (C) 2004-2006 Kern Sibbald
19
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.
24
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.
29
30  */
31
32 #include "bacula.h"
33 #include "dird.h"
34 #include "ua.h"
35 #ifndef HAVE_REGEX_H
36 #include "lib/bregex.h"
37 #else
38 #include <regex.h>
39 #endif
40
41 static char OKbootstrap[] = "3000 OK bootstrap\n";
42 static bool get_job_to_migrate(JCR *jcr);
43 struct idpkt;
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);
47
48 /* 
49  * Called here before the job is run to do the job
50  *   specific setup.
51  */
52 bool do_migration_init(JCR *jcr)
53 {
54    /* If we find a job to migrate it is previous_jr.JobId */
55    if (!get_job_to_migrate(jcr)) {
56       return false;
57    }
58
59    if (jcr->previous_jr.JobId == 0) {
60       return true;                    /* no work */
61    }
62
63    if (!get_or_create_fileset_record(jcr)) {
64       return false;
65    }
66
67    apply_pool_overrides(jcr);
68
69    jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->hdr.name);
70    if (jcr->jr.PoolId == 0) {
71       return false;
72    }
73
74    /* If pool storage specified, use it instead of job storage */
75    copy_storage(jcr, jcr->pool->storage, _("Pool resource"));
76
77    if (!jcr->storage) {
78       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
79       return false;
80    }
81
82    if (!create_restore_bootstrap_file(jcr)) {
83       return false;
84    }
85    return true;
86 }
87
88 /*
89  * Do a Migration of a previous job
90  *
91  *  Returns:  false on failure
92  *            true  on success
93  */
94 bool do_migration(JCR *jcr)
95 {
96    POOL_DBR pr;
97    POOL *pool;
98    char ed1[100];
99    BSOCK *sd;
100    JOB *job, *prev_job;
101    JCR *prev_jcr;
102
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 */
107    }
108
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);
112
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);
116
117    LockRes();
118    job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
119    prev_job = (JOB *)GetResWithName(R_JOB, jcr->previous_jr.Name);
120    UnlockRes();
121    if (!job || !prev_job) {
122       return false;
123    }
124
125    /* 
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.
131     */
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));
134
135    /* Turn the prev_jcr into a "real" job */
136    set_jcr_defaults(prev_jcr, prev_job);
137    if (!setup_job(prev_jcr)) {
138       return false;
139    }
140    /* Set output PoolId and FileSetId. */
141    prev_jcr->jr.PoolId = jcr->jr.PoolId;
142    prev_jcr->jr.FileSetId = jcr->jr.FileSetId;
143
144    /*
145     * Get the PoolId used with the original job. Then
146     *  find the pool name from the database record.
147     */
148    memset(&pr, 0, sizeof(pr));
149    pr.PoolId = prev_jcr->previous_jr.PoolId;
150    if (!db_get_pool_record(jcr, jcr->db, &pr)) {
151       Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
152             edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
153          return false;
154    }
155    /* Get the pool resource corresponding to the original job */
156    pool = (POOL *)GetResWithName(R_POOL, pr.Name);
157    if (!pool) {
158       Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
159       return false;
160    }
161
162    /* Check Migration time and High/Low water marks */
163    /* ***FIXME*** */
164
165    /* If pool storage specified, use it for restore */
166    copy_storage(prev_jcr, pool->storage, _("Pool resource"));
167
168    /* If the original backup pool has a NextPool, make sure a 
169     *  record exists in the database.
170     */
171    if (pool->NextPool) {
172       jcr->jr.PoolId = get_or_create_pool_record(jcr, pool->NextPool->hdr.name);
173       if (jcr->jr.PoolId == 0) {
174          return false;
175       }
176       /*
177        * put the "NextPool" resource pointer in our jcr so that we
178        * can pull the Storage reference from it.
179        */
180       prev_jcr->pool = jcr->pool = pool->NextPool;
181       prev_jcr->jr.PoolId = jcr->jr.PoolId;
182       pm_strcpy(jcr->pool_source, _("NextPool in Pool resource"));
183    }
184
185    /* If pool storage specified, use it instead of job storage for backup */
186    copy_storage(jcr, jcr->pool->storage, _("Pool resource"));
187
188    /* Print Job Start message */
189    Jmsg(jcr, M_INFO, 0, _("Start Migration JobId %s, Job=%s\n"),
190         edit_uint64(jcr->JobId, ed1), jcr->Job);
191
192    set_jcr_job_status(jcr, JS_Running);
193    set_jcr_job_status(prev_jcr, JS_Running);
194    Dmsg2(000, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
195    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
196       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
197       return false;
198    }
199
200    if (!db_update_job_start_record(prev_jcr, prev_jcr->db, &prev_jcr->jr)) {
201       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(prev_jcr->db));
202       return false;
203    }
204
205
206    /*
207     * Open a message channel connection with the Storage
208     * daemon. This is to let him know that our client
209     * will be contacting him for a backup  session.
210     *
211     */
212    Dmsg0(110, "Open connection with storage daemon\n");
213    set_jcr_job_status(jcr, JS_WaitSD);
214    set_jcr_job_status(prev_jcr, JS_WaitSD);
215    /*
216     * Start conversation with Storage daemon
217     */
218    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
219       return false;
220    }
221    sd = jcr->store_bsock;
222    /*
223     * Now start a job with the Storage daemon
224     */
225    Dmsg2(000, "Read store=%s, write store=%s\n", 
226       ((STORE *)prev_jcr->storage->first())->hdr.name,
227       ((STORE *)jcr->storage->first())->hdr.name);
228    if (!start_storage_daemon_job(jcr, prev_jcr->storage, jcr->storage)) {
229       return false;
230    }
231    Dmsg0(150, "Storage daemon connection OK\n");
232
233    if (!send_bootstrap_file(jcr, sd) ||
234        !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
235       return false;
236    }
237
238    if (!bnet_fsend(sd, "run")) {
239       return false;
240    }
241
242    /*
243     * Now start a Storage daemon message thread
244     */
245    if (!start_storage_daemon_message_thread(jcr)) {
246       return false;
247    }
248
249
250    set_jcr_job_status(jcr, JS_Running);
251    set_jcr_job_status(prev_jcr, JS_Running);
252
253    /* Pickup Job termination data */
254    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
255    wait_for_storage_daemon_termination(jcr);
256
257    set_jcr_job_status(jcr, jcr->SDJobStatus);
258    if (jcr->JobStatus == JS_Terminated) {
259       migration_cleanup(jcr, jcr->JobStatus);
260       return true;
261    }
262    return false;
263 }
264
265 struct idpkt {
266    POOLMEM *list;
267    uint32_t count;
268 };
269
270 /*
271  * Callback handler make list of DB Ids
272  */
273 static int dbid_handler(void *ctx, int num_fields, char **row)
274 {
275    idpkt *ids = (idpkt *)ctx;
276
277    Dmsg3(000, "count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
278    if (ids->count == 0) {
279       ids->list[0] = 0;
280    } else {
281       pm_strcat(ids->list, ",");
282    }
283    pm_strcat(ids->list, row[0]);
284    ids->count++;
285    return 0;
286 }
287
288
289 struct uitem {
290    dlink link;   
291    char *item;
292 };
293
294 static int item_compare(void *item1, void *item2)
295 {
296    uitem *i1 = (uitem *)item1;
297    uitem *i2 = (uitem *)item2;
298    return strcmp(i1->item, i2->item);
299 }
300
301 static int unique_name_handler(void *ctx, int num_fields, char **row)
302 {
303    dlist *list = (dlist *)ctx;
304
305    uitem *new_item = (uitem *)malloc(sizeof(uitem));
306    uitem *item;
307    
308    memset(new_item, 0, sizeof(uitem));
309    new_item->item = bstrdup(row[0]);
310    Dmsg1(000, "Item=%s\n", row[0]);
311    item = (uitem *)list->binary_insert((void *)new_item, item_compare);
312    if (item != new_item) {            /* already in list */
313       free(new_item->item);
314       free((char *)new_item);
315       return 0;
316    }
317    return 0;
318 }
319
320 /* Get Job names in Pool */
321 const char *sql_job =
322    "SELECT DISTINCT Job.Name from Job,Pool"
323    " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
324
325 /* Get JobIds from regex'ed Job names */
326 const char *sql_jobids_from_job =
327    "SELECT DISTINCT Job.JobId FROM Job,Pool"
328    " WHERE Job.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
329    " ORDER by Job.StartTime";
330
331 /* Get Client names in Pool */
332 const char *sql_client =
333    "SELECT DISTINCT Client.Name from Client,Pool,Job"
334    " WHERE Pool.Name='%s' AND Job.ClientId=Client.ClientId AND"
335    " Job.PoolId=Pool.PoolId";
336
337 /* Get JobIds from regex'ed Client names */
338 const char *sql_jobids_from_client =
339    "SELECT DISTINCT Job.JobId FROM Job,Pool"
340    " WHERE Client.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
341    " AND Job.ClientId=Client.ClientId "
342    " ORDER by Job.StartTime";
343
344 /* Get Volume names in Pool */
345 const char *sql_vol = 
346    "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
347    " VolStatus in ('Full','Used','Error') AND"
348    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
349
350 /* Get JobIds from regex'ed Volume names */
351 const char *sql_jobids_from_vol =
352    "SELECT DISTINCT Job.JobId FROM Media,JobMedia,Job"
353    " WHERE Media.VolumeName='%s' AND Media.MediaId=JobMedia.MediaId"
354    " AND JobMedia.JobId=Job.JobId" 
355    " ORDER by Job.StartTime";
356    
357
358
359
360
361 const char *sql_smallest_vol = 
362    "SELECT MediaId FROM Media,Pool WHERE"
363    " VolStatus in ('Full','Used','Error') AND"
364    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
365    " ORDER BY VolBytes ASC LIMIT 1";
366
367 const char *sql_oldest_vol = 
368    "SELECT MediaId FROM Media,Pool WHERE"
369    " VolStatus in ('Full','Used','Error') AND"
370    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
371    " ORDER BY LastWritten ASC LIMIT 1";
372
373 const char *sql_jobids_from_mediaid =
374    "SELECT DISTINCT Job.JobId FROM JobMedia,Job"
375    " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId=%s"
376    " ORDER by Job.StartTime";
377
378 const char *sql_pool_bytes =
379    "SELECT SUM(VolBytes) FROM Media,Pool WHERE"
380    " VolStatus in ('Full','Used','Error','Append') AND"
381    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
382
383 const char *sql_vol_bytes =
384    "SELECT MediaId FROM Media,Pool WHERE"
385    " VolStatus in ('Full','Used','Error') AND"
386    " Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
387    " VolBytes<%s ORDER BY LastWritten ASC LIMIT 1";
388
389
390 const char *sql_ujobid =
391    "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
392    " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
393    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
394
395
396
397 /*
398  * Returns: false on error
399  *          true  if OK and jcr->previous_jr filled in
400  */
401 static bool get_job_to_migrate(JCR *jcr)
402 {
403    char ed1[30];
404    POOL_MEM query(PM_MESSAGE);
405    JobId_t JobId;
406    int stat;
407    char *p;
408    idpkt ids;
409
410    ids.list = get_pool_memory(PM_MESSAGE);
411    Dmsg1(000, "list=%p\n", ids.list);
412    ids.list[0] = 0;
413    ids.count = 0;
414
415    if (jcr->MigrateJobId != 0) {
416       Dmsg1(000, "previous jobid=%u\n", jcr->MigrateJobId);
417       edit_uint64(jcr->MigrateJobId, ids.list);
418       ids.count = 1;
419    } else {
420       switch (jcr->job->selection_type) {
421       case MT_JOB:
422          if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
423             goto bail_out;
424          } 
425          break;
426       case MT_CLIENT:
427          if (!regex_find_jobids(jcr, &ids, sql_client, 
428               sql_jobids_from_client, "Client")) {
429             goto bail_out;
430          } 
431          break;
432       case MT_VOLUME:
433          if (!regex_find_jobids(jcr, &ids, sql_vol, 
434              sql_jobids_from_vol, "Volume")) {
435             goto bail_out;
436          } 
437          break;
438       case MT_SQLQUERY:
439          if (!jcr->job->selection_pattern) {
440             Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
441             goto bail_out;
442          }
443          Dmsg1(000, "SQL=%s\n", jcr->job->selection_pattern);
444          if (!db_sql_query(jcr->db, jcr->job->selection_pattern,
445               dbid_handler, (void *)&ids)) {
446             Jmsg(jcr, M_FATAL, 0,
447                  _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
448             goto bail_out;
449          }
450          break;
451
452
453 /***** Below not implemented yet *********/
454       case MT_SMALLEST_VOL:
455          Mmsg(query, sql_smallest_vol, jcr->pool->hdr.name);
456 //       Mmsg(query2, sql_jobids_from_mediaid, JobIds);
457 //       Dmsg1(000, "Smallest Vol Jobids=%s\n", JobIds);
458          break;
459       case MT_OLDEST_VOL:
460          Mmsg(query, sql_oldest_vol, jcr->pool->hdr.name);
461 //       Mmsg(query2, sql_jobids_from_mediaid, JobIds);
462 //       Dmsg1(000, "Oldest Vol Jobids=%s\n", JobIds);
463          break;
464       case MT_POOL_OCCUPANCY:
465          Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
466 //       Dmsg1(000, "Pool Occupancy Jobids=%s\n", JobIds);
467          break;
468       case MT_POOL_TIME:
469          Dmsg0(000, "Pool time not implemented\n");
470          break;
471       default:
472          Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
473          goto bail_out;
474       }
475    }
476
477    p = ids.list;
478    JobId = 0;
479    stat = get_next_jobid_from_list(&p, &JobId);
480    Dmsg2(000, "get_next_jobid stat=%d JobId=%u\n", stat, JobId);
481    jcr->MigrateJobId = JobId;
482    start_migration_job(jcr);
483    if (stat < 0) {
484       Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
485       goto bail_out;
486    } else if (stat == 0) {
487       Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
488       goto ok_out;
489    }
490    
491    jcr->previous_jr.JobId = JobId;
492    Dmsg1(000, "Previous jobid=%d\n", jcr->previous_jr.JobId);
493
494    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
495       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
496            edit_int64(jcr->previous_jr.JobId, ed1),
497            db_strerror(jcr->db));
498       goto bail_out;
499    }
500    Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
501       jcr->previous_jr.JobId, jcr->previous_jr.Job);
502
503 ok_out:
504    free_pool_memory(ids.list);
505    return true;
506
507 bail_out:
508    free_pool_memory(ids.list);
509    return false;
510 }
511
512 static void start_migration_job(JCR *jcr)
513 {
514    UAContext *ua = new_ua_context(jcr);
515    char ed1[50];
516    ua->batch = true;
517    Mmsg(ua->cmd, "run %s jobid=%s", jcr->job->hdr.name, 
518         edit_uint64(jcr->MigrateJobId, ed1));
519    Dmsg1(000, "=============== Migration cmd=%s\n", ua->cmd);
520    parse_ua_args(ua);                 /* parse command */
521 // int stat = run_cmd(ua, ua->cmd);
522    int stat = (int)jcr->MigrateJobId;
523    if (stat == 0) {
524       Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
525    } else {
526       Jmsg(jcr, M_INFO, 0, _("Migration JobId %d started.\n"), stat);
527    }
528    free_ua_context(ua);
529 }
530
531
532 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
533                  const char *query2, const char *type) {
534    dlist *item_chain;
535    uitem *item = NULL;
536    uitem *last_item = NULL;
537    regex_t preg;
538    char prbuf[500];
539    int rc;
540    bool ok = false;
541    POOL_MEM query(PM_MESSAGE);
542
543    item_chain = New(dlist(item, &item->link));
544    if (!jcr->job->selection_pattern) {
545       Jmsg(jcr, M_FATAL, 0, _("No Migration %s selection pattern specified.\n"),
546          type);
547       goto bail_out;
548    }
549    Dmsg1(000, "regex=%s\n", jcr->job->selection_pattern);
550    /* Compile regex expression */
551    rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
552    if (rc != 0) {
553       regerror(rc, &preg, prbuf, sizeof(prbuf));
554       Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
555            jcr->job->selection_pattern, prbuf);
556       goto bail_out;
557    }
558    /* Basic query for names */
559    Mmsg(query, query1, jcr->pool->hdr.name);
560    Dmsg1(000, "query1=%s\n", query.c_str());
561    if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
562         (void *)item_chain)) {
563       Jmsg(jcr, M_FATAL, 0,
564            _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
565       goto bail_out;
566    }
567    /* Now apply the regex to the names and remove any item not matched */
568    foreach_dlist(item, item_chain) {
569       const int nmatch = 30;
570       regmatch_t pmatch[nmatch];
571       if (last_item) {
572          Dmsg1(000, "Remove item %s\n", last_item->item);
573          free(last_item->item);
574          item_chain->remove(last_item);
575       }
576       Dmsg1(000, "Jobitem=%s\n", item->item);
577       rc = regexec(&preg, item->item, nmatch, pmatch,  0);
578       if (rc == 0) {
579          last_item = NULL;   /* keep this one */
580       } else {   
581          last_item = item;
582       }
583    }
584    if (last_item) {
585       free(last_item->item);
586       Dmsg1(000, "Remove item %s\n", last_item->item);
587       item_chain->remove(last_item);
588    }
589    regfree(&preg);
590    /* 
591     * At this point, we have a list of items in item_chain
592     *  that have been matched by the regex, so now we need
593     *  to look up their jobids.
594     */
595    ids->count = 0;
596    foreach_dlist(item, item_chain) {
597       Dmsg1(000, "Got Job: %s\n", item->item);
598       Mmsg(query, query2, item->item, jcr->pool->hdr.name);
599       Dmsg1(000, "query2=%s\n", query.c_str());
600       if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
601          Jmsg(jcr, M_FATAL, 0,
602               _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
603          goto bail_out;
604       }
605    }
606    if (ids->count == 0) {
607       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
608    }
609    ok = true;
610 bail_out:
611    Dmsg2(000, "Count=%d Jobids=%s\n", ids->count, ids->list);
612    delete item_chain;
613    Dmsg0(000, "After delete item_chain\n");
614    return ok;
615 }
616
617
618 /*
619  * Release resources allocated during backup.
620  */
621 void migration_cleanup(JCR *jcr, int TermCode)
622 {
623    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
624    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
625    char ec6[50], ec7[50], ec8[50];
626    char term_code[100], sd_term_msg[100];
627    const char *term_msg;
628    int msg_type;
629    MEDIA_DBR mr;
630    double kbps;
631    utime_t RunTime;
632    JCR *prev_jcr = jcr->previous_jcr;
633    POOL_MEM query(PM_MESSAGE);
634
635    Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
636    dequeue_messages(jcr);             /* display any queued messages */
637    memset(&mr, 0, sizeof(mr));
638    set_jcr_job_status(jcr, TermCode);
639    update_job_end_record(jcr);        /* update database */
640
641    /* 
642     * Check if we actually did something.  
643     *  prev_jcr is jcr of the newly migrated job.
644     */
645    if (prev_jcr) {
646       prev_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
647       prev_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
648       prev_jcr->VolSessionId = jcr->VolSessionId;
649       prev_jcr->VolSessionTime = jcr->VolSessionTime;
650       prev_jcr->jr.RealEndTime = 0; 
651       prev_jcr->jr.PriorJobId = jcr->previous_jr.JobId;
652
653       set_jcr_job_status(prev_jcr, TermCode);
654
655   
656       update_job_end_record(prev_jcr);
657
658       Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
659                   "JobTDate=%s WHERE JobId=%s", 
660          jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
661          edit_uint64(jcr->previous_jr.JobTDate, ec1),
662          edit_uint64(prev_jcr->jr.JobId, ec2));
663       db_sql_query(prev_jcr->db, query.c_str(), NULL, NULL);
664
665       if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
666          Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
667             db_strerror(jcr->db));
668          set_jcr_job_status(jcr, JS_ErrorTerminated);
669       }
670
671       bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
672       if (!db_get_media_record(jcr, jcr->db, &mr)) {
673          Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
674             mr.VolumeName, db_strerror(jcr->db));
675          set_jcr_job_status(jcr, JS_ErrorTerminated);
676       }
677
678       update_bootstrap_file(prev_jcr);
679
680       if (!db_get_job_volume_names(prev_jcr, prev_jcr->db, prev_jcr->jr.JobId, &prev_jcr->VolumeName)) {
681          /*
682           * Note, if the job has erred, most likely it did not write any
683           *  tape, so suppress this "error" message since in that case
684           *  it is normal.  Or look at it the other way, only for a
685           *  normal exit should we complain about this error.
686           */
687          if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
688             Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(prev_jcr->db));
689          }
690          prev_jcr->VolumeName[0] = 0;         /* none */
691       }
692   }
693
694    msg_type = M_INFO;                 /* by default INFO message */
695    switch (jcr->JobStatus) {
696    case JS_Terminated:
697       if (jcr->Errors || jcr->SDErrors) {
698          term_msg = _("%s OK -- with warnings");
699       } else {
700          term_msg = _("%s OK");
701       }
702       break;
703    case JS_FatalError:
704    case JS_ErrorTerminated:
705       term_msg = _("*** %s Error ***");
706       msg_type = M_ERROR;          /* Generate error message */
707       if (jcr->store_bsock) {
708          bnet_sig(jcr->store_bsock, BNET_TERMINATE);
709          if (jcr->SD_msg_chan) {
710             pthread_cancel(jcr->SD_msg_chan);
711          }
712       }
713       break;
714    case JS_Canceled:
715       term_msg = _("%s Canceled");
716       if (jcr->store_bsock) {
717          bnet_sig(jcr->store_bsock, BNET_TERMINATE);
718          if (jcr->SD_msg_chan) {
719             pthread_cancel(jcr->SD_msg_chan);
720          }
721       }
722       break;
723    default:
724       term_msg = _("Inappropriate %s term code");
725       break;
726    }
727    bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
728    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
729    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
730    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
731    if (RunTime <= 0) {
732       kbps = 0;
733    } else {
734       kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
735    }
736
737
738    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
739
740    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
741 "  Prev Backup JobId:      %s\n"
742 "  New Backup JobId:       %s\n"
743 "  Migration JobId:        %s\n"
744 "  Migration Job:          %s\n"
745 "  Backup Level:           %s%s\n"
746 "  Client:                 %s\n"
747 "  FileSet:                \"%s\" %s\n"
748 "  Pool:                   \"%s\" (From %s)\n"
749 "  Storage:                \"%s\" (From %s)\n"
750 "  Start time:             %s\n"
751 "  End time:               %s\n"
752 "  Elapsed time:           %s\n"
753 "  Priority:               %d\n"
754 "  SD Files Written:       %s\n"
755 "  SD Bytes Written:       %s (%sB)\n"
756 "  Rate:                   %.1f KB/s\n"
757 "  Volume name(s):         %s\n"
758 "  Volume Session Id:      %d\n"
759 "  Volume Session Time:    %d\n"
760 "  Last Volume Bytes:      %s (%sB)\n"
761 "  SD Errors:              %d\n"
762 "  SD termination status:  %s\n"
763 "  Termination:            %s\n\n"),
764    VERSION,
765    LSMDATE,
766         edt, 
767         prev_jcr ? edit_uint64(jcr->previous_jr.JobId, ec6) : "0", 
768         prev_jcr ? edit_uint64(prev_jcr->jr.JobId, ec7) : "0",
769         edit_uint64(jcr->jr.JobId, ec8),
770         jcr->jr.Job,
771         level_to_str(jcr->JobLevel), jcr->since,
772         jcr->client->hdr.name,
773         jcr->fileset->hdr.name, jcr->FSCreateTime,
774         jcr->pool->hdr.name, jcr->pool_source,
775         jcr->store->hdr.name, jcr->storage_source,
776         sdt,
777         edt,
778         edit_utime(RunTime, elapsed, sizeof(elapsed)),
779         jcr->JobPriority,
780         edit_uint64_with_commas(jcr->SDJobFiles, ec1),
781         edit_uint64_with_commas(jcr->SDJobBytes, ec2),
782         edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
783         (float)kbps,
784         prev_jcr ? prev_jcr->VolumeName : "",
785         jcr->VolSessionId,
786         jcr->VolSessionTime,
787         edit_uint64_with_commas(mr.VolBytes, ec4),
788         edit_uint64_with_suffix(mr.VolBytes, ec5),
789         jcr->SDErrors,
790         sd_term_msg,
791         term_code);
792
793    Dmsg1(100, "migrate_cleanup() previous_jcr=0x%x\n", jcr->previous_jcr);
794    if (jcr->previous_jcr) {
795       free_jcr(jcr->previous_jcr);
796       jcr->previous_jcr = NULL;
797    }
798    Dmsg0(100, "Leave migrate_cleanup()\n");
799 }