]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/migrate.c
======================= Warning ==========================
[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 const int dbglevel = 100;
42
43 static char OKbootstrap[] = "3000 OK bootstrap\n";
44 static bool get_job_to_migrate(JCR *jcr);
45 struct idpkt;
46 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
47                  const char *query2, const char *type);
48 static void start_migration_job(JCR *jcr);
49
50 /* 
51  * Called here before the job is run to do the job
52  *   specific setup.
53  */
54 bool do_migration_init(JCR *jcr)
55 {
56    /* If we find a job to migrate it is previous_jr.JobId */
57    if (!get_job_to_migrate(jcr)) {
58       return false;
59    }
60
61    if (jcr->previous_jr.JobId == 0) {
62       return true;                    /* no work */
63    }
64
65    if (!get_or_create_fileset_record(jcr)) {
66       return false;
67    }
68
69    apply_pool_overrides(jcr);
70
71    jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->hdr.name);
72    if (jcr->jr.PoolId == 0) {
73       return false;
74    }
75
76    /* If pool storage specified, use it instead of job storage */
77    copy_storage(jcr, jcr->pool->storage, _("Pool resource"));
78
79    if (!jcr->storage) {
80       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
81       return false;
82    }
83
84    create_restore_bootstrap_file(jcr);
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 || jcr->ExpectedFiles == 0) {
104       set_jcr_job_status(jcr, JS_Terminated);
105       migration_cleanup(jcr, jcr->JobStatus);
106       return true;                    /* no work */
107    }
108
109    Dmsg4(dbglevel, "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(dbglevel, "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(dbglevel, "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(dbglevel, "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(dbglevel, "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(dbglevel, "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(dbglevel, "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(dbglevel, "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(dbglevel, "list=%p\n", ids.list);
427    ids.list[0] = 0;
428    ids.count = 0;
429
430    if (jcr->MigrateJobId != 0) {
431       Dmsg1(000, "At Job start 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, sql_jobids_from_client, "Client")) {
443             goto bail_out;
444          } 
445          break;
446       case MT_VOLUME:
447          if (!regex_find_jobids(jcr, &ids, sql_vol, sql_jobids_from_vol, "Volume")) {
448             goto bail_out;
449          } 
450          break;
451       case MT_SQLQUERY:
452          if (!jcr->job->selection_pattern) {
453             Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
454             goto bail_out;
455          }
456          Dmsg1(dbglevel, "SQL=%s\n", jcr->job->selection_pattern);
457          if (!db_sql_query(jcr->db, jcr->job->selection_pattern,
458               dbid_handler, (void *)&ids)) {
459             Jmsg(jcr, M_FATAL, 0,
460                  _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
461             goto bail_out;
462          }
463          break;
464
465
466 /***** Below not implemented yet *********/
467       case MT_SMALLEST_VOL:
468          Mmsg(query, sql_smallest_vol, jcr->pool->hdr.name);
469 //       Mmsg(query2, sql_jobids_from_mediaid, JobIds);
470 //       Dmsg1(000, "Smallest Vol Jobids=%s\n", JobIds);
471          break;
472       case MT_OLDEST_VOL:
473          Mmsg(query, sql_oldest_vol, jcr->pool->hdr.name);
474 //       Mmsg(query2, sql_jobids_from_mediaid, JobIds);
475 //       Dmsg1(000, "Oldest Vol Jobids=%s\n", JobIds);
476          break;
477       case MT_POOL_OCCUPANCY:
478          Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
479 //       Dmsg1(000, "Pool Occupancy Jobids=%s\n", JobIds);
480          break;
481       case MT_POOL_TIME:
482          Dmsg0(000, "Pool time not implemented\n");
483          break;
484       default:
485          Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
486          goto bail_out;
487       }
488    }
489
490    /*
491     * Loop over all jobids except the last one, sending
492     *  them to start_migration_job(), which will start a job
493     *  for each of them.  For the last JobId, we handle it below.
494     */
495    p = ids.list;
496    for (int i=1; i < (int)ids.count; i++) {
497       JobId = 0;
498       stat = get_next_jobid_from_list(&p, &JobId);
499       Dmsg2(000, "get_next_jobid stat=%d JobId=%u\n", stat, JobId);
500       jcr->MigrateJobId = JobId;
501       start_migration_job(jcr);
502       if (stat < 0) {
503          Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
504          goto bail_out;
505       } else if (stat == 0) {
506          Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
507          goto ok_out;
508       }
509    }
510    
511    /* Now get the last JobId and handle it in the current job */
512    JobId = 0;
513    stat = get_next_jobid_from_list(&p, &JobId);
514    Dmsg2(000, "Last get_next_jobid stat=%d JobId=%u\n", stat, JobId);
515    if (stat < 0) {
516       Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
517       goto bail_out;
518    } else if (stat == 0) {
519       Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
520       goto ok_out;
521    }
522
523    jcr->previous_jr.JobId = JobId;
524    Dmsg1(100, "Previous jobid=%d\n", jcr->previous_jr.JobId);
525
526    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
527       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
528            edit_int64(jcr->previous_jr.JobId, ed1),
529            db_strerror(jcr->db));
530       goto bail_out;
531    }
532    Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
533       jcr->previous_jr.JobId, jcr->previous_jr.Job);
534
535 ok_out:
536    free_pool_memory(ids.list);
537    return true;
538
539 bail_out:
540    free_pool_memory(ids.list);
541    return false;
542 }
543
544 static void start_migration_job(JCR *jcr)
545 {
546    UAContext *ua = new_ua_context(jcr);
547    char ed1[50];
548    ua->batch = true;
549    Mmsg(ua->cmd, "run %s jobid=%s", jcr->job->hdr.name, 
550         edit_uint64(jcr->MigrateJobId, ed1));
551    Dmsg1(dbglevel, "=============== Migration cmd=%s\n", ua->cmd);
552    parse_ua_args(ua);                 /* parse command */
553    int stat = run_cmd(ua, ua->cmd);
554 // int stat = (int)jcr->MigrateJobId;
555    if (stat == 0) {
556       Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
557    } else {
558       Jmsg(jcr, M_INFO, 0, _("Migration JobId %d started.\n"), stat);
559    }
560    free_ua_context(ua);
561 }
562
563
564 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
565                  const char *query2, const char *type) {
566    dlist *item_chain;
567    uitem *item = NULL;
568    uitem *last_item = NULL;
569    regex_t preg;
570    char prbuf[500];
571    int rc;
572    bool ok = false;
573    POOL_MEM query(PM_MESSAGE);
574
575    item_chain = New(dlist(item, &item->link));
576    if (!jcr->job->selection_pattern) {
577       Jmsg(jcr, M_FATAL, 0, _("No Migration %s selection pattern specified.\n"),
578          type);
579       goto bail_out;
580    }
581    Dmsg1(dbglevel, "regex=%s\n", jcr->job->selection_pattern);
582    /* Compile regex expression */
583    rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
584    if (rc != 0) {
585       regerror(rc, &preg, prbuf, sizeof(prbuf));
586       Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
587            jcr->job->selection_pattern, prbuf);
588       goto bail_out;
589    }
590    /* Basic query for names */
591    Mmsg(query, query1, jcr->pool->hdr.name);
592    Dmsg1(dbglevel, "query1=%s\n", query.c_str());
593    if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
594         (void *)item_chain)) {
595       Jmsg(jcr, M_FATAL, 0,
596            _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
597       goto bail_out;
598    }
599    /* Now apply the regex to the names and remove any item not matched */
600    foreach_dlist(item, item_chain) {
601       const int nmatch = 30;
602       regmatch_t pmatch[nmatch];
603       if (last_item) {
604          Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
605          free(last_item->item);
606          item_chain->remove(last_item);
607       }
608       Dmsg1(dbglevel, "Item=%s\n", item->item);
609       rc = regexec(&preg, item->item, nmatch, pmatch,  0);
610       if (rc == 0) {
611          last_item = NULL;   /* keep this one */
612       } else {   
613          last_item = item;
614       }
615    }
616    if (last_item) {
617       free(last_item->item);
618       Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
619       item_chain->remove(last_item);
620    }
621    regfree(&preg);
622    /* 
623     * At this point, we have a list of items in item_chain
624     *  that have been matched by the regex, so now we need
625     *  to look up their jobids.
626     */
627    ids->count = 0;
628    foreach_dlist(item, item_chain) {
629       Dmsg2(dbglevel, "Got %s: %s\n", type, item->item);
630       Mmsg(query, query2, item->item, jcr->pool->hdr.name);
631       Dmsg1(dbglevel, "query2=%s\n", query.c_str());
632       if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
633          Jmsg(jcr, M_FATAL, 0,
634               _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
635          goto bail_out;
636       }
637    }
638    if (ids->count == 0) {
639       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
640    }
641    ok = true;
642 bail_out:
643    Dmsg2(dbglevel, "Count=%d Jobids=%s\n", ids->count, ids->list);
644    delete item_chain;
645    Dmsg0(dbglevel, "After delete item_chain\n");
646    return ok;
647 }
648
649
650 /*
651  * Release resources allocated during backup.
652  */
653 void migration_cleanup(JCR *jcr, int TermCode)
654 {
655    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
656    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
657    char ec6[50], ec7[50], ec8[50];
658    char term_code[100], sd_term_msg[100];
659    const char *term_msg;
660    int msg_type;
661    MEDIA_DBR mr;
662    double kbps;
663    utime_t RunTime;
664    JCR *prev_jcr = jcr->previous_jcr;
665    POOL_MEM query(PM_MESSAGE);
666
667    Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
668    dequeue_messages(jcr);             /* display any queued messages */
669    memset(&mr, 0, sizeof(mr));
670    set_jcr_job_status(jcr, TermCode);
671    update_job_end_record(jcr);        /* update database */
672
673    /* 
674     * Check if we actually did something.  
675     *  prev_jcr is jcr of the newly migrated job.
676     */
677    if (prev_jcr) {
678       prev_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
679       prev_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
680       prev_jcr->VolSessionId = jcr->VolSessionId;
681       prev_jcr->VolSessionTime = jcr->VolSessionTime;
682       prev_jcr->jr.RealEndTime = 0; 
683       prev_jcr->jr.PriorJobId = jcr->previous_jr.JobId;
684
685       set_jcr_job_status(prev_jcr, TermCode);
686
687   
688       update_job_end_record(prev_jcr);
689      
690       /* Update final items to set them to the previous job's values */
691       Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
692                   "JobTDate=%s WHERE JobId=%s", 
693          jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
694          edit_uint64(jcr->previous_jr.JobTDate, ec1),
695          edit_uint64(prev_jcr->jr.JobId, ec2));
696       db_sql_query(prev_jcr->db, query.c_str(), NULL, NULL);
697
698       /* Now marke the previous job as migrated */
699       Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
700            (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
701       db_sql_query(prev_jcr->db, query.c_str(), NULL, NULL);
702
703       if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
704          Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
705             db_strerror(jcr->db));
706          set_jcr_job_status(jcr, JS_ErrorTerminated);
707       }
708
709       bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
710       if (!db_get_media_record(jcr, jcr->db, &mr)) {
711          Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
712             mr.VolumeName, db_strerror(jcr->db));
713          set_jcr_job_status(jcr, JS_ErrorTerminated);
714       }
715
716       update_bootstrap_file(prev_jcr);
717
718       if (!db_get_job_volume_names(prev_jcr, prev_jcr->db, prev_jcr->jr.JobId, &prev_jcr->VolumeName)) {
719          /*
720           * Note, if the job has erred, most likely it did not write any
721           *  tape, so suppress this "error" message since in that case
722           *  it is normal.  Or look at it the other way, only for a
723           *  normal exit should we complain about this error.
724           */
725          if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
726             Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(prev_jcr->db));
727          }
728          prev_jcr->VolumeName[0] = 0;         /* none */
729       }
730   }
731
732    msg_type = M_INFO;                 /* by default INFO message */
733    switch (jcr->JobStatus) {
734    case JS_Terminated:
735       if (jcr->Errors || jcr->SDErrors) {
736          term_msg = _("%s OK -- with warnings");
737       } else {
738          term_msg = _("%s OK");
739       }
740       break;
741    case JS_FatalError:
742    case JS_ErrorTerminated:
743       term_msg = _("*** %s Error ***");
744       msg_type = M_ERROR;          /* Generate error message */
745       if (jcr->store_bsock) {
746          bnet_sig(jcr->store_bsock, BNET_TERMINATE);
747          if (jcr->SD_msg_chan) {
748             pthread_cancel(jcr->SD_msg_chan);
749          }
750       }
751       break;
752    case JS_Canceled:
753       term_msg = _("%s Canceled");
754       if (jcr->store_bsock) {
755          bnet_sig(jcr->store_bsock, BNET_TERMINATE);
756          if (jcr->SD_msg_chan) {
757             pthread_cancel(jcr->SD_msg_chan);
758          }
759       }
760       break;
761    default:
762       term_msg = _("Inappropriate %s term code");
763       break;
764    }
765    bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
766    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
767    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
768    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
769    if (RunTime <= 0) {
770       kbps = 0;
771    } else {
772       kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
773    }
774
775
776    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
777
778    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
779 "  Prev Backup JobId:      %s\n"
780 "  New Backup JobId:       %s\n"
781 "  Migration JobId:        %s\n"
782 "  Migration Job:          %s\n"
783 "  Backup Level:           %s%s\n"
784 "  Client:                 %s\n"
785 "  FileSet:                \"%s\" %s\n"
786 "  Pool:                   \"%s\" (From %s)\n"
787 "  Storage:                \"%s\" (From %s)\n"
788 "  Start time:             %s\n"
789 "  End time:               %s\n"
790 "  Elapsed time:           %s\n"
791 "  Priority:               %d\n"
792 "  SD Files Written:       %s\n"
793 "  SD Bytes Written:       %s (%sB)\n"
794 "  Rate:                   %.1f KB/s\n"
795 "  Volume name(s):         %s\n"
796 "  Volume Session Id:      %d\n"
797 "  Volume Session Time:    %d\n"
798 "  Last Volume Bytes:      %s (%sB)\n"
799 "  SD Errors:              %d\n"
800 "  SD termination status:  %s\n"
801 "  Termination:            %s\n\n"),
802    VERSION,
803    LSMDATE,
804         edt, 
805         prev_jcr ? edit_uint64(jcr->previous_jr.JobId, ec6) : "0", 
806         prev_jcr ? edit_uint64(prev_jcr->jr.JobId, ec7) : "0",
807         edit_uint64(jcr->jr.JobId, ec8),
808         jcr->jr.Job,
809         level_to_str(jcr->JobLevel), jcr->since,
810         jcr->client->hdr.name,
811         jcr->fileset->hdr.name, jcr->FSCreateTime,
812         jcr->pool->hdr.name, jcr->pool_source,
813         jcr->store->hdr.name, jcr->storage_source,
814         sdt,
815         edt,
816         edit_utime(RunTime, elapsed, sizeof(elapsed)),
817         jcr->JobPriority,
818         edit_uint64_with_commas(jcr->SDJobFiles, ec1),
819         edit_uint64_with_commas(jcr->SDJobBytes, ec2),
820         edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
821         (float)kbps,
822         prev_jcr ? prev_jcr->VolumeName : "",
823         jcr->VolSessionId,
824         jcr->VolSessionTime,
825         edit_uint64_with_commas(mr.VolBytes, ec4),
826         edit_uint64_with_suffix(mr.VolBytes, ec5),
827         jcr->SDErrors,
828         sd_term_msg,
829         term_code);
830
831    Dmsg1(100, "migrate_cleanup() previous_jcr=0x%x\n", jcr->previous_jcr);
832    if (jcr->previous_jcr) {
833       free_jcr(jcr->previous_jcr);
834       jcr->previous_jcr = NULL;
835    }
836    Dmsg0(100, "Leave migrate_cleanup()\n");
837 }