]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/migrate.c
Migration fixes
[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;                     /* newly migrated job */
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
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;
147
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);
151
152    /*
153     * Get the PoolId used with the original job. Then
154     *  find the pool name from the database record.
155     */
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));
161          return false;
162    }
163    /* Get the pool resource corresponding to the original job */
164    pool = (POOL *)GetResWithName(R_POOL, pr.Name);
165    if (!pool) {
166       Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
167       return false;
168    }
169
170    /* Check Migration time and High/Low water marks */
171    /* ***FIXME*** */
172
173    /* If pool storage specified, use it for restore */
174    copy_storage(prev_jcr, pool->storage, _("Pool resource"));
175
176    /* If the original backup pool has a NextPool, make sure a 
177     *  record exists in the database.
178     */
179    if (pool->NextPool) {
180       jcr->jr.PoolId = get_or_create_pool_record(jcr, pool->NextPool->hdr.name);
181       if (jcr->jr.PoolId == 0) {
182          return false;
183       }
184       /*
185        * put the "NextPool" resource pointer in our jcr so that we
186        * can pull the Storage reference from it.
187        */
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"));
191    }
192
193    /* If pool storage specified, use it instead of job storage for backup */
194    copy_storage(jcr, jcr->pool->storage, _("Pool resource"));
195
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);
199
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);
203
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));
207       return false;
208    }
209
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);
213
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));
217       return false;
218    }
219
220
221    /*
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.
225     *
226     */
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);
230    /*
231     * Start conversation with Storage daemon
232     */
233    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
234       return false;
235    }
236    sd = jcr->store_bsock;
237    /*
238     * Now start a job with the Storage daemon
239     */
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)) {
244       return false;
245    }
246    Dmsg0(150, "Storage daemon connection OK\n");
247
248    if (!send_bootstrap_file(jcr, sd) ||
249        !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
250       return false;
251    }
252
253    if (!bnet_fsend(sd, "run")) {
254       return false;
255    }
256
257    /*
258     * Now start a Storage daemon message thread
259     */
260    if (!start_storage_daemon_message_thread(jcr)) {
261       return false;
262    }
263
264
265    set_jcr_job_status(jcr, JS_Running);
266    set_jcr_job_status(prev_jcr, JS_Running);
267
268    /* Pickup Job termination data */
269    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
270    wait_for_storage_daemon_termination(jcr);
271
272    set_jcr_job_status(jcr, jcr->SDJobStatus);
273    if (jcr->JobStatus == JS_Terminated) {
274       migration_cleanup(jcr, jcr->JobStatus);
275       return true;
276    }
277    return false;
278 }
279
280 struct idpkt {
281    POOLMEM *list;
282    uint32_t count;
283 };
284
285 /*
286  * Callback handler make list of DB Ids
287  */
288 static int dbid_handler(void *ctx, int num_fields, char **row)
289 {
290    idpkt *ids = (idpkt *)ctx;
291
292    Dmsg3(000, "count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
293    if (ids->count == 0) {
294       ids->list[0] = 0;
295    } else {
296       pm_strcat(ids->list, ",");
297    }
298    pm_strcat(ids->list, row[0]);
299    ids->count++;
300    return 0;
301 }
302
303
304 struct uitem {
305    dlink link;   
306    char *item;
307 };
308
309 static int item_compare(void *item1, void *item2)
310 {
311    uitem *i1 = (uitem *)item1;
312    uitem *i2 = (uitem *)item2;
313    return strcmp(i1->item, i2->item);
314 }
315
316 static int unique_name_handler(void *ctx, int num_fields, char **row)
317 {
318    dlist *list = (dlist *)ctx;
319
320    uitem *new_item = (uitem *)malloc(sizeof(uitem));
321    uitem *item;
322    
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);
330       return 0;
331    }
332    return 0;
333 }
334
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";
339
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";
345
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";
351
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";
358
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'";
364
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";
371    
372
373
374
375
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";
381
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";
387
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";
392
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'";
397
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";
403
404
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";
409
410
411
412 /*
413  * Returns: false on error
414  *          true  if OK and jcr->previous_jr filled in
415  */
416 static bool get_job_to_migrate(JCR *jcr)
417 {
418    char ed1[30];
419    POOL_MEM query(PM_MESSAGE);
420    JobId_t JobId;
421    int stat;
422    char *p;
423    idpkt ids;
424
425    ids.list = get_pool_memory(PM_MESSAGE);
426    Dmsg1(000, "list=%p\n", ids.list);
427    ids.list[0] = 0;
428    ids.count = 0;
429
430    if (jcr->MigrateJobId != 0) {
431       Dmsg1(000, "previous jobid=%u\n", jcr->MigrateJobId);
432       edit_uint64(jcr->MigrateJobId, ids.list);
433       ids.count = 1;
434    } else {
435       switch (jcr->job->selection_type) {
436       case MT_JOB:
437          if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
438             goto bail_out;
439          } 
440          break;
441       case MT_CLIENT:
442          if (!regex_find_jobids(jcr, &ids, sql_client, 
443               sql_jobids_from_client, "Client")) {
444             goto bail_out;
445          } 
446          break;
447       case MT_VOLUME:
448          if (!regex_find_jobids(jcr, &ids, sql_vol, 
449              sql_jobids_from_vol, "Volume")) {
450             goto bail_out;
451          } 
452          break;
453       case MT_SQLQUERY:
454          if (!jcr->job->selection_pattern) {
455             Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
456             goto bail_out;
457          }
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));
463             goto bail_out;
464          }
465          break;
466
467
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);
473          break;
474       case MT_OLDEST_VOL:
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);
478          break;
479       case MT_POOL_OCCUPANCY:
480          Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
481 //       Dmsg1(000, "Pool Occupancy Jobids=%s\n", JobIds);
482          break;
483       case MT_POOL_TIME:
484          Dmsg0(000, "Pool time not implemented\n");
485          break;
486       default:
487          Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
488          goto bail_out;
489       }
490    }
491
492    p = ids.list;
493    JobId = 0;
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);
498    if (stat < 0) {
499       Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
500       goto bail_out;
501    } else if (stat == 0) {
502       Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
503       goto ok_out;
504    }
505    
506    jcr->previous_jr.JobId = JobId;
507    Dmsg1(000, "Previous jobid=%d\n", jcr->previous_jr.JobId);
508
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));
513       goto bail_out;
514    }
515    Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
516       jcr->previous_jr.JobId, jcr->previous_jr.Job);
517
518 ok_out:
519    free_pool_memory(ids.list);
520    return true;
521
522 bail_out:
523    free_pool_memory(ids.list);
524    return false;
525 }
526
527 static void start_migration_job(JCR *jcr)
528 {
529    UAContext *ua = new_ua_context(jcr);
530    char ed1[50];
531    ua->batch = true;
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;
538    if (stat == 0) {
539       Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
540    } else {
541       Jmsg(jcr, M_INFO, 0, _("Migration JobId %d started.\n"), stat);
542    }
543    free_ua_context(ua);
544 }
545
546
547 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
548                  const char *query2, const char *type) {
549    dlist *item_chain;
550    uitem *item = NULL;
551    uitem *last_item = NULL;
552    regex_t preg;
553    char prbuf[500];
554    int rc;
555    bool ok = false;
556    POOL_MEM query(PM_MESSAGE);
557
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"),
561          type);
562       goto bail_out;
563    }
564    Dmsg1(000, "regex=%s\n", jcr->job->selection_pattern);
565    /* Compile regex expression */
566    rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
567    if (rc != 0) {
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);
571       goto bail_out;
572    }
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));
580       goto bail_out;
581    }
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];
586       if (last_item) {
587          Dmsg1(000, "Remove item %s\n", last_item->item);
588          free(last_item->item);
589          item_chain->remove(last_item);
590       }
591       Dmsg1(000, "Jobitem=%s\n", item->item);
592       rc = regexec(&preg, item->item, nmatch, pmatch,  0);
593       if (rc == 0) {
594          last_item = NULL;   /* keep this one */
595       } else {   
596          last_item = item;
597       }
598    }
599    if (last_item) {
600       free(last_item->item);
601       Dmsg1(000, "Remove item %s\n", last_item->item);
602       item_chain->remove(last_item);
603    }
604    regfree(&preg);
605    /* 
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.
609     */
610    ids->count = 0;
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));
618          goto bail_out;
619       }
620    }
621    if (ids->count == 0) {
622       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
623    }
624    ok = true;
625 bail_out:
626    Dmsg2(000, "Count=%d Jobids=%s\n", ids->count, ids->list);
627    delete item_chain;
628    Dmsg0(000, "After delete item_chain\n");
629    return ok;
630 }
631
632
633 /*
634  * Release resources allocated during backup.
635  */
636 void migration_cleanup(JCR *jcr, int TermCode)
637 {
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;
643    int msg_type;
644    MEDIA_DBR mr;
645    double kbps;
646    utime_t RunTime;
647    JCR *prev_jcr = jcr->previous_jcr;
648    POOL_MEM query(PM_MESSAGE);
649
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 */
655
656    /* 
657     * Check if we actually did something.  
658     *  prev_jcr is jcr of the newly migrated job.
659     */
660    if (prev_jcr) {
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;
667
668       set_jcr_job_status(prev_jcr, TermCode);
669
670   
671       update_job_end_record(prev_jcr);
672      
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);
680
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);
685
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);
690       }
691
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);
697       }
698
699       update_bootstrap_file(prev_jcr);
700
701       if (!db_get_job_volume_names(prev_jcr, prev_jcr->db, prev_jcr->jr.JobId, &prev_jcr->VolumeName)) {
702          /*
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.
707           */
708          if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
709             Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(prev_jcr->db));
710          }
711          prev_jcr->VolumeName[0] = 0;         /* none */
712       }
713   }
714
715    msg_type = M_INFO;                 /* by default INFO message */
716    switch (jcr->JobStatus) {
717    case JS_Terminated:
718       if (jcr->Errors || jcr->SDErrors) {
719          term_msg = _("%s OK -- with warnings");
720       } else {
721          term_msg = _("%s OK");
722       }
723       break;
724    case JS_FatalError:
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);
732          }
733       }
734       break;
735    case JS_Canceled:
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);
741          }
742       }
743       break;
744    default:
745       term_msg = _("Inappropriate %s term code");
746       break;
747    }
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;
752    if (RunTime <= 0) {
753       kbps = 0;
754    } else {
755       kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
756    }
757
758
759    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
760
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"
767 "  Client:                 %s\n"
768 "  FileSet:                \"%s\" %s\n"
769 "  Pool:                   \"%s\" (From %s)\n"
770 "  Storage:                \"%s\" (From %s)\n"
771 "  Start time:             %s\n"
772 "  End time:               %s\n"
773 "  Elapsed time:           %s\n"
774 "  Priority:               %d\n"
775 "  SD Files Written:       %s\n"
776 "  SD Bytes Written:       %s (%sB)\n"
777 "  Rate:                   %.1f KB/s\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"
782 "  SD Errors:              %d\n"
783 "  SD termination status:  %s\n"
784 "  Termination:            %s\n\n"),
785    VERSION,
786    LSMDATE,
787         edt, 
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),
791         jcr->jr.Job,
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,
797         sdt,
798         edt,
799         edit_utime(RunTime, elapsed, sizeof(elapsed)),
800         jcr->JobPriority,
801         edit_uint64_with_commas(jcr->SDJobFiles, ec1),
802         edit_uint64_with_commas(jcr->SDJobBytes, ec2),
803         edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
804         (float)kbps,
805         prev_jcr ? prev_jcr->VolumeName : "",
806         jcr->VolSessionId,
807         jcr->VolSessionTime,
808         edit_uint64_with_commas(mr.VolBytes, ec4),
809         edit_uint64_with_suffix(mr.VolBytes, ec5),
810         jcr->SDErrors,
811         sd_term_msg,
812         term_code);
813
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;
818    }
819    Dmsg0(100, "Leave migrate_cleanup()\n");
820 }