]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/migrate.c
kes Correct return status on db_batch... routines.
[bacula/bacula] / bacula / src / dird / migrate.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2004-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation plus additions
11    that are listed in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- migrate.c -- responsible for doing
31  *     migration jobs.
32  *
33  *     Kern Sibbald, September MMIV
34  *
35  *  Basic tasks done here:
36  *     Open DB and create records for this job.
37  *     Open Message Channel with Storage daemon to tell him a job will be starting.
38  *     Open connection with Storage daemon and pass him commands
39  *       to do the backup.
40  *     When the Storage daemon finishes the job, update the DB.
41  *
42  *   Version $Id$
43  */
44
45 #include "bacula.h"
46 #include "dird.h"
47 #include "ua.h"
48 #ifndef HAVE_REGEX_H
49 #include "lib/bregex.h"
50 #else
51 #include <regex.h>
52 #endif
53
54 static const int dbglevel = 10;
55
56 static char OKbootstrap[] = "3000 OK bootstrap\n";
57 static int get_job_to_migrate(JCR *jcr);
58 struct idpkt;
59 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
60                  const char *query2, const char *type);
61 static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
62                  const char *type);
63 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type);
64 static void start_migration_job(JCR *jcr);
65 static int get_next_dbid_from_list(char **p, DBId_t *DBId);
66
67 /* 
68  * Called here before the job is run to do the job
69  *   specific setup.  Note, one of the important things to
70  *   complete in this init code is to make the definitive
71  *   choice of input and output storage devices.  This is
72  *   because immediately after the init, the job is queued
73  *   in the jobq.c code, and it checks that all the resources
74  *   (storage resources in particular) are available, so these
75  *   must all be properly defined.
76  *
77  *  previous_jr refers to the job DB record of the Job that is
78  *    going to be migrated.
79  *  prev_job refers to the job resource of the Job that is
80  *    going to be migrated.
81  *  jcr is the jcr for the current "migration" job.  It is a
82  *    control job that is put in the DB as a migration job, which
83  *    means that this job migrated a previous job to a new job.
84  *    No Volume or File data is associated with this control
85  *    job.
86  *  mig_jcr refers to the newly migrated job that is run by
87  *    the current jcr.  It is a backup job that moves (migrates) the
88  *    data written for the previous_jr into the new pool.  This
89  *    job (mig_jcr) becomes the new backup job that replaces
90  *    the original backup job. Note, this jcr is not really run. It
91  *    is simply attached to the current jcr.  It will show up in
92  *    the Director's status output, but not in the SD or FD, both of
93  *    which deal only with the current migration job (i.e. jcr).
94  */
95 bool do_migration_init(JCR *jcr)
96 {
97    POOL_DBR pr;
98    POOL *pool;
99    char ed1[100];
100    JOB *job, *prev_job;
101    JCR *mig_jcr;                   /* newly migrated job */
102    int count;
103
104
105    apply_pool_overrides(jcr);
106
107    jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
108    if (jcr->jr.PoolId == 0) {
109       Dmsg1(dbglevel, "JobId=%d no PoolId\n", (int)jcr->JobId);
110       Jmsg(jcr, M_FATAL, 0, _("Could not get or create a Pool record.\n"));
111       return false;
112    }
113    /*
114     * Note, at this point, pool is the pool for this job.  We
115     *  transfer it to rpool (read pool), and a bit later,
116     *  pool will be changed to point to the write pool, 
117     *  which comes from pool->NextPool.
118     */
119    jcr->rpool = jcr->pool;            /* save read pool */
120    pm_strcpy(jcr->rpool_source, jcr->pool_source);
121
122
123    Dmsg2(dbglevel, "Read pool=%s (From %s)\n", jcr->rpool->name(), jcr->rpool_source);
124
125    /* If we find a job or jobs to migrate it is previous_jr.JobId */
126    count = get_job_to_migrate(jcr);
127    if (count < 0) {
128       return false;
129    }
130    if (count == 0) {
131       return true;
132    }
133
134    Dmsg1(dbglevel, "Back from get_job_to_migrate JobId=%d\n", (int)jcr->JobId);
135
136    if (jcr->previous_jr.JobId == 0) {
137       Dmsg1(dbglevel, "JobId=%d no previous JobId\n", (int)jcr->JobId);
138       Jmsg(jcr, M_INFO, 0, _("No previous Job found to migrate.\n"));
139       return true;                    /* no work */
140    }
141
142    if (!get_or_create_fileset_record(jcr)) {
143       Dmsg1(dbglevel, "JobId=%d no FileSet\n", (int)jcr->JobId);
144       Jmsg(jcr, M_FATAL, 0, _("Could not get or create the FileSet record.\n"));
145       return false;
146    }
147
148    create_restore_bootstrap_file(jcr);
149
150    if (jcr->previous_jr.JobId == 0 || jcr->ExpectedFiles == 0) {
151       set_jcr_job_status(jcr, JS_Terminated);
152       Dmsg1(dbglevel, "JobId=%d expected files == 0\n", (int)jcr->JobId);
153       if (jcr->previous_jr.JobId == 0) {
154          Jmsg(jcr, M_INFO, 0, _("No previous Job found to migrate.\n"));
155       } else {
156          Jmsg(jcr, M_INFO, 0, _("Previous Job has no data to migrate.\n"));
157       }
158       return true;                    /* no work */
159    }
160
161    Dmsg5(dbglevel, "JobId=%d: Previous: Name=%s JobId=%d Type=%c Level=%c\n",
162       (int)jcr->JobId,
163       jcr->previous_jr.Name, (int)jcr->previous_jr.JobId, 
164       jcr->previous_jr.JobType, jcr->previous_jr.JobLevel);
165
166    Dmsg5(dbglevel, "JobId=%d: Current: Name=%s JobId=%d Type=%c Level=%c\n",
167       (int)jcr->JobId,
168       jcr->jr.Name, (int)jcr->jr.JobId, 
169       jcr->jr.JobType, jcr->jr.JobLevel);
170
171    LockRes();
172    job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
173    prev_job = (JOB *)GetResWithName(R_JOB, jcr->previous_jr.Name);
174    UnlockRes();
175    if (!job) {
176       Jmsg(jcr, M_FATAL, 0, _("Job resource not found for \"%s\".\n"), jcr->jr.Name);
177       return false;
178    }
179    if (!prev_job) {
180       Jmsg(jcr, M_FATAL, 0, _("Previous Job resource not found for \"%s\".\n"), 
181            jcr->previous_jr.Name);
182       return false;
183    }
184
185    /* Create a migation jcr */
186    mig_jcr = jcr->mig_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
187    memcpy(&mig_jcr->previous_jr, &jcr->previous_jr, sizeof(mig_jcr->previous_jr));
188
189    /*
190     * Turn the mig_jcr into a "real" job that takes on the aspects of
191     *   the previous backup job "prev_job".
192     */
193    set_jcr_defaults(mig_jcr, prev_job);
194    if (!setup_job(mig_jcr)) {
195       Jmsg(jcr, M_FATAL, 0, _("setup job failed.\n"));
196       return false;
197    }
198
199    /* Now reset the job record from the previous job */
200    memcpy(&mig_jcr->jr, &jcr->previous_jr, sizeof(mig_jcr->jr));
201    /* Update the jr to reflect the new values of PoolId, FileSetId, and JobId. */
202    mig_jcr->jr.PoolId = jcr->jr.PoolId;
203    mig_jcr->jr.FileSetId = jcr->jr.FileSetId;
204    mig_jcr->jr.JobId = mig_jcr->JobId;
205
206    Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
207       mig_jcr->jr.Name, (int)mig_jcr->jr.JobId, 
208       mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
209
210    /*
211     * Get the PoolId used with the original job. Then
212     *  find the pool name from the database record.
213     */
214    memset(&pr, 0, sizeof(pr));
215    pr.PoolId = mig_jcr->previous_jr.PoolId;
216    if (!db_get_pool_record(jcr, jcr->db, &pr)) {
217       Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
218             edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
219          return false;
220    }
221    /* Get the pool resource corresponding to the original job */
222    pool = (POOL *)GetResWithName(R_POOL, pr.Name);
223    if (!pool) {
224       Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
225       return false;
226    }
227
228    /* If pool storage specified, use it for restore */
229    copy_rstorage(mig_jcr, pool->storage, _("Pool resource"));
230    copy_rstorage(jcr, pool->storage, _("Pool resource"));
231
232    /*
233     * If the original backup pool has a NextPool, make sure a 
234     *  record exists in the database. Note, in this case, we
235     *  will be migrating from pool to pool->NextPool.
236     */
237    if (pool->NextPool) {
238       jcr->jr.PoolId = get_or_create_pool_record(jcr, pool->NextPool->name());
239       if (jcr->jr.PoolId == 0) {
240          return false;
241       }
242    }
243    if (!set_migration_wstorage(jcr, pool)) {
244       return false;
245    }
246    mig_jcr->pool = jcr->pool = pool->NextPool;
247    pm_strcpy(jcr->pool_source, _("Job Pool's NextPool resource"));
248    mig_jcr->jr.PoolId = jcr->jr.PoolId;
249
250    Dmsg2(dbglevel, "Write pool=%s read rpool=%s\n", jcr->pool->name(), jcr->rpool->name());
251    return true;
252 }
253
254 /*
255  * Do a Migration of a previous job
256  *
257  *  Returns:  false on failure
258  *            true  on success
259  */
260 bool do_migration(JCR *jcr)
261 {
262    char ed1[100];
263    BSOCK *sd;
264    JCR *mig_jcr = jcr->mig_jcr;    /* newly migrated job */
265
266    /*
267     * If mig_jcr is NULL, there is nothing to do for this job,
268     *  so set a normal status, cleanup and return OK.
269     */
270    if (!mig_jcr) {
271       set_jcr_job_status(jcr, JS_Terminated);
272       migration_cleanup(jcr, jcr->JobStatus);
273       return true;
274    }
275
276    /* Print Job Start message */
277    Jmsg(jcr, M_INFO, 0, _("Start Migration JobId %s, Job=%s\n"),
278         edit_uint64(jcr->JobId, ed1), jcr->Job);
279
280
281
282    /*
283     * Open a message channel connection with the Storage
284     * daemon. This is to let him know that our client
285     * will be contacting him for a backup  session.
286     *
287     */
288    Dmsg0(110, "Open connection with storage daemon\n");
289    set_jcr_job_status(jcr, JS_WaitSD);
290    set_jcr_job_status(mig_jcr, JS_WaitSD);
291    /*
292     * Start conversation with Storage daemon
293     */
294    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
295       return false;
296    }
297    sd = jcr->store_bsock;
298    /*
299     * Now start a job with the Storage daemon
300     */
301    Dmsg2(dbglevel, "Read store=%s, write store=%s\n", 
302       ((STORE *)jcr->rstorage->first())->name(),
303       ((STORE *)jcr->wstorage->first())->name());
304    if (((STORE *)jcr->rstorage->first())->name() == ((STORE *)jcr->wstorage->first())->name()) {
305       Jmsg(jcr, M_FATAL, 0, _("Read storage \"%s\" same as write storage.\n"),
306            ((STORE *)jcr->rstorage->first())->name());
307       return false;
308    }
309    if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage)) {
310       return false;
311    }
312    Dmsg0(150, "Storage daemon connection OK\n");
313
314    if (!send_bootstrap_file(jcr, sd) ||
315        !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
316       return false;
317    }
318
319    /*    
320     * We re-update the job start record so that the start
321     *  time is set after the run before job.  This avoids 
322     *  that any files created by the run before job will
323     *  be saved twice.  They will be backed up in the current
324     *  job, but not in the next one unless they are changed.
325     *  Without this, they will be backed up in this job and
326     *  in the next job run because in that case, their date 
327     *   is after the start of this run.
328     */
329    jcr->start_time = time(NULL);
330    jcr->jr.StartTime = jcr->start_time;
331    jcr->jr.JobTDate = jcr->start_time;
332    set_jcr_job_status(jcr, JS_Running);
333
334    /* Update job start record for this migration control job */
335    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
336       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
337       return false;
338    }
339
340
341    mig_jcr->start_time = time(NULL);
342    mig_jcr->jr.StartTime = mig_jcr->start_time;
343    mig_jcr->jr.JobTDate = mig_jcr->start_time;
344    set_jcr_job_status(mig_jcr, JS_Running);
345
346    /* Update job start record for the real migration backup job */
347    if (!db_update_job_start_record(mig_jcr, mig_jcr->db, &mig_jcr->jr)) {
348       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(mig_jcr->db));
349       return false;
350    }
351
352    Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
353       mig_jcr->jr.Name, (int)mig_jcr->jr.JobId, 
354       mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
355
356
357    /*
358     * Start the job prior to starting the message thread below
359     * to avoid two threads from using the BSOCK structure at
360     * the same time.
361     */
362    if (!bnet_fsend(sd, "run")) {
363       return false;
364    }
365
366    /*
367     * Now start a Storage daemon message thread
368     */
369    if (!start_storage_daemon_message_thread(jcr)) {
370       return false;
371    }
372
373
374    set_jcr_job_status(jcr, JS_Running);
375    set_jcr_job_status(mig_jcr, JS_Running);
376
377    /* Pickup Job termination data */
378    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
379    wait_for_storage_daemon_termination(jcr);
380    set_jcr_job_status(jcr, jcr->SDJobStatus);
381    db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
382    if (jcr->JobStatus != JS_Terminated) {
383       return false;
384    }
385
386    migration_cleanup(jcr, jcr->JobStatus);
387    if (mig_jcr) {
388       UAContext *ua = new_ua_context(jcr);
389       purge_job_records_from_catalog(ua, jcr->previous_jr.JobId);
390       free_ua_context(ua);
391    }
392    return true;
393 }
394
395 struct idpkt {
396    POOLMEM *list;
397    uint32_t count;
398 };
399
400 /* Add an item to the list if it is unique */
401 static void add_unique_id(idpkt *ids, char *item) 
402 {
403    char id[30];
404    char *q = ids->list;
405
406    /* Walk through current list to see if each item is the same as item */
407    for ( ; *q; ) {
408        id[0] = 0;
409        for (int i=0; i<(int)sizeof(id); i++) {
410           if (*q == 0) {
411              break;
412           } else if (*q == ',') {
413              q++;
414              break;
415           }
416           id[i] = *q++;
417           id[i+1] = 0;
418        }
419        if (strcmp(item, id) == 0) {
420           return;
421        }
422    }
423    /* Did not find item, so add it to list */
424    if (ids->count == 0) {
425       ids->list[0] = 0;
426    } else {
427       pm_strcat(ids->list, ",");
428    }
429    pm_strcat(ids->list, item);
430    ids->count++;
431 // Dmsg3(0, "add_uniq count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
432    return;
433 }
434
435 /*
436  * Callback handler make list of DB Ids
437  */
438 static int unique_dbid_handler(void *ctx, int num_fields, char **row)
439 {
440    idpkt *ids = (idpkt *)ctx;
441
442    add_unique_id(ids, row[0]);
443    Dmsg3(dbglevel, "dbid_hdlr count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
444    return 0;
445 }
446
447
448 struct uitem {
449    dlink link;   
450    char *item;
451 };
452
453 static int item_compare(void *item1, void *item2)
454 {
455    uitem *i1 = (uitem *)item1;
456    uitem *i2 = (uitem *)item2;
457    return strcmp(i1->item, i2->item);
458 }
459
460 static int unique_name_handler(void *ctx, int num_fields, char **row)
461 {
462    dlist *list = (dlist *)ctx;
463
464    uitem *new_item = (uitem *)malloc(sizeof(uitem));
465    uitem *item;
466    
467    memset(new_item, 0, sizeof(uitem));
468    new_item->item = bstrdup(row[0]);
469    Dmsg1(dbglevel, "Unique_name_hdlr Item=%s\n", row[0]);
470    item = (uitem *)list->binary_insert((void *)new_item, item_compare);
471    if (item != new_item) {            /* already in list */
472       free(new_item->item);
473       free((char *)new_item);
474       return 0;
475    }
476    return 0;
477 }
478
479 /* Get Job names in Pool */
480 const char *sql_job =
481    "SELECT DISTINCT Job.Name from Job,Pool"
482    " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
483
484 /* Get JobIds from regex'ed Job names */
485 const char *sql_jobids_from_job =
486    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
487    " WHERE Job.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
488    " ORDER by Job.StartTime";
489
490 /* Get Client names in Pool */
491 const char *sql_client =
492    "SELECT DISTINCT Client.Name from Client,Pool,Job"
493    " WHERE Pool.Name='%s' AND Job.ClientId=Client.ClientId AND"
494    " Job.PoolId=Pool.PoolId";
495
496 /* Get JobIds from regex'ed Client names */
497 const char *sql_jobids_from_client =
498    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool,Client"
499    " WHERE Client.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
500    " AND Job.ClientId=Client.ClientId AND Job.Type='B'"
501    " ORDER by Job.StartTime";
502
503 /* Get Volume names in Pool */
504 const char *sql_vol = 
505    "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
506    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
507    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
508
509 /* Get JobIds from regex'ed Volume names */
510 const char *sql_jobids_from_vol =
511    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Media,JobMedia,Job"
512    " WHERE Media.VolumeName='%s' AND Media.MediaId=JobMedia.MediaId"
513    " AND JobMedia.JobId=Job.JobId AND Job.Type='B'"
514    " ORDER by Job.StartTime";
515
516
517 const char *sql_smallest_vol = 
518    "SELECT MediaId FROM Media,Pool WHERE"
519    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
520    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
521    " ORDER BY VolBytes ASC LIMIT 1";
522
523 const char *sql_oldest_vol = 
524    "SELECT MediaId FROM Media,Pool WHERE"
525    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
526    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
527    " ORDER BY LastWritten ASC LIMIT 1";
528
529 /* Get JobIds when we have selected MediaId */
530 const char *sql_jobids_from_mediaid =
531    "SELECT DISTINCT Job.JobId,Job.StartTime FROM JobMedia,Job"
532    " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId=%s"
533    " AND Job.Type='B'"
534    " ORDER by Job.StartTime";
535
536 /* Get tne number of bytes in the pool */
537 const char *sql_pool_bytes =
538    "SELECT SUM(VolBytes) FROM Media,Pool WHERE"
539    " VolStatus in ('Full','Used','Error','Append') AND Media.Enabled=1 AND"
540    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
541
542 /* Get tne number of bytes in the Jobs */
543 const char *sql_job_bytes =
544    "SELECT SUM(JobBytes) FROM Job WHERE JobId IN (%s)";
545
546
547 /* Get Media Ids in Pool */
548 const char *sql_mediaids =
549    "SELECT MediaId FROM Media,Pool WHERE"
550    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
551    " Media.PoolId=Pool.PoolId AND Pool.Name='%s' ORDER BY LastWritten ASC";
552
553 /* Get JobIds in Pool longer than specified time */
554 const char *sql_pool_time = 
555    "SELECT DISTINCT Job.JobId from Pool,Job,Media,JobMedia WHERE"
556    " Pool.Name='%s' AND Media.PoolId=Pool.PoolId AND"
557    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
558    " Job.Type='B' AND"
559    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId"
560    " AND Job.RealEndTime<='%s'";
561
562 /*
563 * const char *sql_ujobid =
564 *   "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
565 *   " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
566 *   " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
567 */
568
569
570
571 /*
572  *
573  * This is the central piece of code that finds a job or jobs 
574  *   actually JobIds to migrate.  It first looks to see if one
575  *   has been "manually" specified in jcr->MigrateJobId, and if
576  *   so, it returns that JobId to be run.  Otherwise, it
577  *   examines the Selection Type to see what kind of migration
578  *   we are doing (Volume, Job, Client, ...) and applies any
579  *   Selection Pattern if appropriate to obtain a list of JobIds.
580  *   Finally, it will loop over all the JobIds found, except the last
581  *   one starting a new job with MigrationJobId set to that JobId, and
582  *   finally, it returns the last JobId to the caller.
583  *
584  * Returns: -1  on error
585  *           0  if no jobs to migrate
586  *           1  if OK and jcr->previous_jr filled in
587  */
588 static int get_job_to_migrate(JCR *jcr)
589 {
590    char ed1[30], ed2[30];
591    POOL_MEM query(PM_MESSAGE);
592    JobId_t JobId;
593    DBId_t  MediaId = 0;
594    int stat;
595    char *p;
596    idpkt ids, mid, jids;
597    db_int64_ctx ctx;
598    int64_t pool_bytes;
599    time_t ttime;
600    struct tm tm;
601    char dt[MAX_TIME_LENGTH];
602    int count = 0;
603
604    ids.list = get_pool_memory(PM_MESSAGE);
605    ids.list[0] = 0;
606    ids.count = 0;
607    mid.list = get_pool_memory(PM_MESSAGE);
608    mid.list[0] = 0;
609    mid.count = 0;
610    jids.list = get_pool_memory(PM_MESSAGE);
611    jids.list[0] = 0;
612    jids.count = 0;
613
614
615    /*
616     * If MigrateJobId is set, then we migrate only that Job,
617     *  otherwise, we go through the full selection of jobs to
618     *  migrate.
619     */
620    if (jcr->MigrateJobId != 0) {
621       Dmsg1(dbglevel, "At Job start previous jobid=%u\n", jcr->MigrateJobId);
622       edit_uint64(jcr->MigrateJobId, ids.list);
623       ids.count = 1;
624    } else {
625       switch (jcr->job->selection_type) {
626       case MT_JOB:
627          if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
628             goto bail_out;
629          } 
630          break;
631       case MT_CLIENT:
632          if (!regex_find_jobids(jcr, &ids, sql_client, sql_jobids_from_client, "Client")) {
633             goto bail_out;
634          } 
635          break;
636       case MT_VOLUME:
637          if (!regex_find_jobids(jcr, &ids, sql_vol, sql_jobids_from_vol, "Volume")) {
638             goto bail_out;
639          } 
640          break;
641       case MT_SQLQUERY:
642          if (!jcr->job->selection_pattern) {
643             Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
644             goto bail_out;
645          }
646          Dmsg1(dbglevel, "SQL=%s\n", jcr->job->selection_pattern);
647          if (!db_sql_query(jcr->db, jcr->job->selection_pattern,
648               unique_dbid_handler, (void *)&ids)) {
649             Jmsg(jcr, M_FATAL, 0,
650                  _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
651             goto bail_out;
652          }
653          break;
654       case MT_SMALLEST_VOL:
655          if (!find_mediaid_then_jobids(jcr, &ids, sql_smallest_vol, "Smallest Volume")) {
656             goto bail_out;
657          }
658          break;
659       case MT_OLDEST_VOL:
660          if (!find_mediaid_then_jobids(jcr, &ids, sql_oldest_vol, "Oldest Volume")) {
661             goto bail_out;
662          }
663          break;
664
665       case MT_POOL_OCCUPANCY:
666          ctx.count = 0;
667          /* Find count of bytes in pool */
668          Mmsg(query, sql_pool_bytes, jcr->rpool->name());
669          if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
670             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
671             goto bail_out;
672          }
673          if (ctx.count == 0) {
674             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
675             goto ok_out;
676          }
677          pool_bytes = ctx.value;
678          Dmsg2(dbglevel, "highbytes=%d pool=%d\n", (int)jcr->rpool->MigrationHighBytes,
679                (int)pool_bytes);
680          if (pool_bytes < (int64_t)jcr->rpool->MigrationHighBytes) {
681             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
682             goto ok_out;
683          }
684          Dmsg0(dbglevel, "We should do Occupation migration.\n");
685
686          ids.count = 0;
687          /* Find a list of MediaIds that could be migrated */
688          Mmsg(query, sql_mediaids, jcr->rpool->name());
689          Dmsg1(dbglevel, "query=%s\n", query.c_str());
690          if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)&ids)) {
691             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
692             goto bail_out;
693          }
694          if (ids.count == 0) {
695             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
696             goto ok_out;
697          }
698          Dmsg2(dbglevel, "Pool Occupancy ids=%d MediaIds=%s\n", ids.count, ids.list);
699
700          /*
701           * Now loop over MediaIds getting more JobIds to migrate until
702           *  we reduce the pool occupancy below the low water mark.
703           */
704          p = ids.list;
705          for (int i=0; i < (int)ids.count; i++) {
706             stat = get_next_dbid_from_list(&p, &MediaId);
707             Dmsg2(dbglevel, "get_next_dbid stat=%d MediaId=%u\n", stat, MediaId);
708             if (stat < 0) {
709                Jmsg(jcr, M_FATAL, 0, _("Invalid MediaId found.\n"));
710                goto bail_out;
711             } else if (stat == 0) {
712                break;
713             }
714             mid.count = 1;
715             Mmsg(mid.list, "%s", edit_int64(MediaId, ed1));
716             if (!find_jobids_from_mediaid_list(jcr, &mid, "Volumes")) {
717                continue;
718             }
719             if (i != 0) {
720                pm_strcat(jids.list, ",");
721             }
722             pm_strcat(jids.list, mid.list);
723             jids.count += mid.count;
724
725             /* Now get the count of bytes added */
726             ctx.count = 0;
727             /* Find count of bytes from Jobs */
728             Mmsg(query, sql_job_bytes, mid.list);
729             Dmsg1(dbglevel, "Jobbytes query: %s\n", query.c_str());
730             if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
731                Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
732                goto bail_out;
733             }
734             pool_bytes -= ctx.value;
735             Dmsg1(dbglevel, "Total migrate Job bytes=%s\n", edit_int64(ctx.value, ed1));
736             Dmsg2(dbglevel, "lowbytes=%s poolafter=%s\n", 
737                   edit_int64(jcr->rpool->MigrationLowBytes, ed1),
738                   edit_int64(pool_bytes, ed2));
739             if (pool_bytes <= (int64_t)jcr->rpool->MigrationLowBytes) {
740                Dmsg0(dbglevel, "We should be done.\n");
741                break;
742             }
743
744          }
745          /* Transfer jids to ids, where the jobs list is expected */
746          ids.count = jids.count;
747          pm_strcpy(ids.list, jids.list);
748          Dmsg2(dbglevel, "Pool Occupancy ids=%d JobIds=%s\n", ids.count, ids.list);
749          break;
750
751       case MT_POOL_TIME:
752          ttime = time(NULL) - (time_t)jcr->rpool->MigrationTime;
753          (void)localtime_r(&ttime, &tm);
754          strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
755
756          ids.count = 0;
757          Mmsg(query, sql_pool_time, jcr->rpool->name(), dt);
758          Dmsg1(dbglevel, "query=%s\n", query.c_str());
759          if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)&ids)) {
760             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
761             goto bail_out;
762          }
763          if (ids.count == 0) {
764             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
765             goto ok_out;
766          }
767          Dmsg2(dbglevel, "PoolTime ids=%d JobIds=%s\n", ids.count, ids.list);
768          break;
769
770       default:
771          Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
772          goto bail_out;
773       }
774    }
775
776    /*
777     * Loop over all jobids except the last one, sending
778     *  them to start_migration_job(), which will start a job
779     *  for each of them.  For the last JobId, we handle it below.
780     */
781    p = ids.list;
782    if (ids.count == 0) {
783       Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
784       goto ok_out;
785    }
786
787    Jmsg(jcr, M_INFO, 0, _("The following %u JobId%s were chosen to be migrated: %s\n"),
788       ids.count, ids.count==0?"":"s", ids.list);
789
790    Dmsg2(dbglevel, "Before loop count=%d ids=%s\n", ids.count, ids.list);
791    for (int i=1; i < (int)ids.count; i++) {
792       JobId = 0;
793       stat = get_next_jobid_from_list(&p, &JobId);
794       Dmsg3(dbglevel, "get_jobid_no=%d stat=%d JobId=%u\n", i, stat, JobId);
795       jcr->MigrateJobId = JobId;
796       start_migration_job(jcr);
797       Dmsg0(dbglevel, "Back from start_migration_job\n");
798       if (stat < 0) {
799          Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
800          goto bail_out;
801       } else if (stat == 0) {
802          Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
803          goto ok_out;
804       }
805    }
806    
807    /* Now get the last JobId and handle it in the current job */
808    JobId = 0;
809    stat = get_next_jobid_from_list(&p, &JobId);
810    Dmsg2(dbglevel, "Last get_next_jobid stat=%d JobId=%u\n", stat, (int)JobId);
811    if (stat < 0) {
812       Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
813       goto bail_out;
814    } else if (stat == 0) {
815       Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
816       goto ok_out;
817    }
818
819    jcr->previous_jr.JobId = JobId;
820    Dmsg1(dbglevel, "Previous jobid=%d\n", (int)jcr->previous_jr.JobId);
821
822    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
823       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
824            edit_int64(jcr->previous_jr.JobId, ed1),
825            db_strerror(jcr->db));
826       goto bail_out;
827    }
828    Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%s Job=%s\n"),
829       edit_int64(jcr->previous_jr.JobId, ed1), jcr->previous_jr.Job);
830    Dmsg3(dbglevel, "Migration JobId=%d  using JobId=%s Job=%s\n",
831       jcr->JobId,
832       edit_int64(jcr->previous_jr.JobId, ed1), jcr->previous_jr.Job);
833    count = 1;
834
835 ok_out:
836    goto out;
837
838 bail_out:
839    count = -1;
840            
841 out:
842    free_pool_memory(ids.list);
843    free_pool_memory(mid.list);
844    free_pool_memory(jids.list);
845    return count;
846 }
847
848 static void start_migration_job(JCR *jcr)
849 {
850    UAContext *ua = new_ua_context(jcr);
851    char ed1[50];
852    ua->batch = true;
853    Mmsg(ua->cmd, "run %s jobid=%s", jcr->job->hdr.name, 
854         edit_uint64(jcr->MigrateJobId, ed1));
855    Dmsg1(dbglevel, "=============== Migration cmd=%s\n", ua->cmd);
856    parse_ua_args(ua);                 /* parse command */
857    int stat = run_cmd(ua, ua->cmd);
858    if (stat == 0) {
859       Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
860    } else {
861       Jmsg(jcr, M_INFO, 0, _("Migration JobId %d started.\n"), stat);
862    }
863    free_ua_context(ua);
864 }
865
866 static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
867                  const char *type) 
868 {
869    bool ok = false;
870    POOL_MEM query(PM_MESSAGE);
871
872    ids->count = 0;
873    /* Basic query for MediaId */
874    Mmsg(query, query1, jcr->rpool->name());
875    if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
876       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
877       goto bail_out;
878    }
879    if (ids->count == 0) {
880       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
881       ok = true;         /* Not an error */
882       goto bail_out;
883    } else if (ids->count != 1) {
884       Jmsg(jcr, M_FATAL, 0, _("SQL error. Expected 1 MediaId got %d\n"), 
885          ids->count);
886       goto bail_out;
887    }
888    Dmsg1(dbglevel, "Smallest Vol Jobids=%s\n", ids->list);
889
890    ok = find_jobids_from_mediaid_list(jcr, ids, type);
891
892 bail_out:
893    return ok;
894 }
895
896 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type) 
897 {
898    bool ok = false;
899    POOL_MEM query(PM_MESSAGE);
900
901    Mmsg(query, sql_jobids_from_mediaid, ids->list);
902    ids->count = 0;
903    if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
904       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
905       goto bail_out;
906    }
907    if (ids->count == 0) {
908       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
909    }
910    ok = true;
911 bail_out:
912    return ok;
913 }
914
915 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
916                  const char *query2, const char *type) 
917 {
918    dlist *item_chain;
919    uitem *item = NULL;
920    uitem *last_item = NULL;
921    regex_t preg;
922    char prbuf[500];
923    int rc;
924    bool ok = false;
925    POOL_MEM query(PM_MESSAGE);
926
927    item_chain = New(dlist(item, &item->link));
928    if (!jcr->job->selection_pattern) {
929       Jmsg(jcr, M_FATAL, 0, _("No Migration %s selection pattern specified.\n"),
930          type);
931       goto bail_out;
932    }
933    Dmsg1(dbglevel, "regex-sel-pattern=%s\n", jcr->job->selection_pattern);
934    /* Basic query for names */
935    Mmsg(query, query1, jcr->rpool->name());
936    Dmsg1(dbglevel, "get name query1=%s\n", query.c_str());
937    if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
938         (void *)item_chain)) {
939       Jmsg(jcr, M_FATAL, 0,
940            _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
941       goto bail_out;
942    }
943    Dmsg1(dbglevel, "query1 returned %d names\n", item_chain->size());
944    if (item_chain->size() == 0) {
945       Jmsg(jcr, M_INFO, 0, _("Query of Pool \"%s\" returned no Jobs to migrate.\n"),
946            jcr->rpool->name());
947       ok = true;
948       goto bail_out;               /* skip regex match */
949    } else {
950       /* Compile regex expression */
951       rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
952       if (rc != 0) {
953          regerror(rc, &preg, prbuf, sizeof(prbuf));
954          Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
955               jcr->job->selection_pattern, prbuf);
956          goto bail_out;
957       }
958       /* Now apply the regex to the names and remove any item not matched */
959       foreach_dlist(item, item_chain) {
960          const int nmatch = 30;
961          regmatch_t pmatch[nmatch];
962          if (last_item) {
963             Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
964             free(last_item->item);
965             item_chain->remove(last_item);
966          }
967          Dmsg1(dbglevel, "get name Item=%s\n", item->item);
968          rc = regexec(&preg, item->item, nmatch, pmatch,  0);
969          if (rc == 0) {
970             last_item = NULL;   /* keep this one */
971          } else {   
972             last_item = item;
973          }
974       }
975       if (last_item) {
976          free(last_item->item);
977          Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
978          item_chain->remove(last_item);
979       }
980       regfree(&preg);
981    }
982    if (item_chain->size() == 0) {
983       Jmsg(jcr, M_INFO, 0, _("Regex pattern matched no Jobs to migrate.\n"));
984       ok = true;
985       goto bail_out;               /* skip regex match */
986    }
987
988    /* 
989     * At this point, we have a list of items in item_chain
990     *  that have been matched by the regex, so now we need
991     *  to look up their jobids.
992     */
993    ids->count = 0;
994    foreach_dlist(item, item_chain) {
995       Dmsg2(dbglevel, "Got %s: %s\n", type, item->item);
996       Mmsg(query, query2, item->item, jcr->rpool->name());
997       Dmsg1(dbglevel, "get id from name query2=%s\n", query.c_str());
998       if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
999          Jmsg(jcr, M_FATAL, 0,
1000               _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
1001          goto bail_out;
1002       }
1003    }
1004    if (ids->count == 0) {
1005       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
1006    }
1007    ok = true;
1008
1009 bail_out:
1010    Dmsg2(dbglevel, "Count=%d Jobids=%s\n", ids->count, ids->list);
1011    foreach_dlist(item, item_chain) {
1012       free(item->item);
1013    }
1014    delete item_chain;
1015    return ok;
1016 }
1017
1018
1019 /*
1020  * Release resources allocated during backup.
1021  */
1022 void migration_cleanup(JCR *jcr, int TermCode)
1023 {
1024    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
1025    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
1026    char ec6[50], ec7[50], ec8[50];
1027    char term_code[100], sd_term_msg[100];
1028    const char *term_msg;
1029    int msg_type = M_INFO;
1030    MEDIA_DBR mr;
1031    double kbps;
1032    utime_t RunTime;
1033    JCR *mig_jcr = jcr->mig_jcr;
1034    POOL_MEM query(PM_MESSAGE);
1035
1036    Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
1037    update_job_end(jcr, TermCode);
1038    memset(&mr, 0, sizeof(mr));
1039
1040    /* 
1041     * Check if we actually did something.  
1042     *  mig_jcr is jcr of the newly migrated job.
1043     */
1044    if (mig_jcr) {
1045       mig_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
1046       mig_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
1047       mig_jcr->VolSessionId = jcr->VolSessionId;
1048       mig_jcr->VolSessionTime = jcr->VolSessionTime;
1049       mig_jcr->jr.RealEndTime = 0; 
1050       mig_jcr->jr.PriorJobId = jcr->previous_jr.JobId;
1051
1052       update_job_end(mig_jcr, TermCode);
1053      
1054       /* Update final items to set them to the previous job's values */
1055       Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
1056                   "JobTDate=%s WHERE JobId=%s", 
1057          jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
1058          edit_uint64(jcr->previous_jr.JobTDate, ec1),
1059          edit_uint64(mig_jcr->jr.JobId, ec2));
1060       db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1061
1062       /* Now mark the previous job as migrated if it terminated normally */
1063       if (jcr->JobStatus == JS_Terminated) {
1064          Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1065               (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
1066          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1067       } 
1068
1069       if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
1070          Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
1071             db_strerror(jcr->db));
1072          set_jcr_job_status(jcr, JS_ErrorTerminated);
1073       }
1074
1075       bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
1076       if (!db_get_media_record(jcr, jcr->db, &mr)) {
1077          Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
1078             mr.VolumeName, db_strerror(jcr->db));
1079          set_jcr_job_status(jcr, JS_ErrorTerminated);
1080       }
1081
1082       update_bootstrap_file(mig_jcr);
1083
1084       if (!db_get_job_volume_names(mig_jcr, mig_jcr->db, mig_jcr->jr.JobId, &mig_jcr->VolumeName)) {
1085          /*
1086           * Note, if the job has failed, most likely it did not write any
1087           *  tape, so suppress this "error" message since in that case
1088           *  it is normal.  Or look at it the other way, only for a
1089           *  normal exit should we complain about this error.
1090           */
1091          if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
1092             Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(mig_jcr->db));
1093          }
1094          mig_jcr->VolumeName[0] = 0;         /* none */
1095       }
1096       switch (jcr->JobStatus) {
1097       case JS_Terminated:
1098          if (jcr->Errors || jcr->SDErrors) {
1099             term_msg = _("%s OK -- with warnings");
1100          } else {
1101             term_msg = _("%s OK");
1102          }
1103          break;
1104       case JS_FatalError:
1105       case JS_ErrorTerminated:
1106          term_msg = _("*** %s Error ***");
1107          msg_type = M_ERROR;          /* Generate error message */
1108          if (jcr->store_bsock) {
1109             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
1110             if (jcr->SD_msg_chan) {
1111                pthread_cancel(jcr->SD_msg_chan);
1112             }
1113          }
1114          break;
1115       case JS_Canceled:
1116          term_msg = _("%s Canceled");
1117          if (jcr->store_bsock) {
1118             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
1119             if (jcr->SD_msg_chan) {
1120                pthread_cancel(jcr->SD_msg_chan);
1121             }
1122          }
1123          break;
1124       default:
1125          term_msg = _("Inappropriate %s term code");
1126          break;
1127       }
1128   } else {
1129      if (jcr->previous_jr.JobId != 0) {
1130         /* Mark previous job as migrated */
1131         Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1132              (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
1133         Dmsg1(000, "Mark: %s\n", query.c_str());
1134         db_sql_query(jcr->db, query.c_str(), NULL, NULL);
1135      }
1136      term_msg = _("%s -- no files to migrate");
1137   }
1138
1139    bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
1140    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
1141    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
1142    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
1143    if (RunTime <= 0) {
1144       kbps = 0;
1145    } else {
1146       kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
1147    }
1148
1149
1150    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
1151
1152    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
1153 "  Prev Backup JobId:      %s\n"
1154 "  New Backup JobId:       %s\n"
1155 "  Migration JobId:        %s\n"
1156 "  Migration Job:          %s\n"
1157 "  Backup Level:           %s%s\n"
1158 "  Client:                 %s\n"
1159 "  FileSet:                \"%s\" %s\n"
1160 "  Read Pool:              \"%s\" (From %s)\n"
1161 "  Read Storage:           \"%s\" (From %s)\n"
1162 "  Write Pool:             \"%s\" (From %s)\n"
1163 "  Write Storage:          \"%s\" (From %s)\n"
1164 "  Start time:             %s\n"
1165 "  End time:               %s\n"
1166 "  Elapsed time:           %s\n"
1167 "  Priority:               %d\n"
1168 "  SD Files Written:       %s\n"
1169 "  SD Bytes Written:       %s (%sB)\n"
1170 "  Rate:                   %.1f KB/s\n"
1171 "  Volume name(s):         %s\n"
1172 "  Volume Session Id:      %d\n"
1173 "  Volume Session Time:    %d\n"
1174 "  Last Volume Bytes:      %s (%sB)\n"
1175 "  SD Errors:              %d\n"
1176 "  SD termination status:  %s\n"
1177 "  Termination:            %s\n\n"),
1178    VERSION,
1179    LSMDATE,
1180         edt, 
1181         edit_uint64(jcr->previous_jr.JobId, ec6),
1182         mig_jcr ? edit_uint64(mig_jcr->jr.JobId, ec7) : "0",
1183         edit_uint64(jcr->jr.JobId, ec8),
1184         jcr->jr.Job,
1185         level_to_str(jcr->JobLevel), jcr->since,
1186         jcr->client->name(),
1187         jcr->fileset->name(), jcr->FSCreateTime,
1188         jcr->rpool->name(), jcr->rpool_source,
1189         jcr->rstore?jcr->rstore->name():"*None*", 
1190         NPRT(jcr->rstore_source), 
1191         jcr->pool->name(), jcr->pool_source,
1192         jcr->wstore?jcr->wstore->name():"*None*", 
1193         NPRT(jcr->wstore_source),
1194         sdt,
1195         edt,
1196         edit_utime(RunTime, elapsed, sizeof(elapsed)),
1197         jcr->JobPriority,
1198         edit_uint64_with_commas(jcr->SDJobFiles, ec1),
1199         edit_uint64_with_commas(jcr->SDJobBytes, ec2),
1200         edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
1201         (float)kbps,
1202         mig_jcr ? mig_jcr->VolumeName : "",
1203         jcr->VolSessionId,
1204         jcr->VolSessionTime,
1205         edit_uint64_with_commas(mr.VolBytes, ec4),
1206         edit_uint64_with_suffix(mr.VolBytes, ec5),
1207         jcr->SDErrors,
1208         sd_term_msg,
1209         term_code);
1210
1211    Dmsg1(100, "migrate_cleanup() mig_jcr=0x%x\n", jcr->mig_jcr);
1212    if (jcr->mig_jcr) {
1213       free_jcr(jcr->mig_jcr);
1214       jcr->mig_jcr = NULL;
1215    }
1216    Dmsg0(100, "Leave migrate_cleanup()\n");
1217 }
1218
1219 /* 
1220  * Return next DBId from comma separated list   
1221  *
1222  * Returns:
1223  *   1 if next DBId returned
1224  *   0 if no more DBIds are in list
1225  *  -1 there is an error
1226  */
1227 static int get_next_dbid_from_list(char **p, DBId_t *DBId)
1228 {
1229    char id[30];
1230    char *q = *p;
1231
1232    id[0] = 0;
1233    for (int i=0; i<(int)sizeof(id); i++) {
1234       if (*q == 0) {
1235          break;
1236       } else if (*q == ',') {
1237          q++;
1238          break;
1239       }
1240       id[i] = *q++;
1241       id[i+1] = 0;
1242    }
1243    if (id[0] == 0) {
1244       return 0;
1245    } else if (!is_a_number(id)) {
1246       return -1;                      /* error */
1247    }
1248    *p = q;
1249    *DBId = str_to_int64(id);
1250    return 1;
1251 }
1252
1253 bool set_migration_wstorage(JCR *jcr, POOL *pool)
1254 {
1255    POOL *wpool = pool->NextPool;
1256
1257    if (!wpool) {
1258       Jmsg(jcr, M_FATAL, 0, _("No Next Pool specification found in Pool \"%s\".\n"),
1259          pool->hdr.name);
1260       return false;
1261    }
1262
1263    if (!wpool->storage || wpool->storage->size() == 0) {
1264       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Next Pool \"%s\".\n"),
1265          wpool->name());
1266       return false;
1267    }
1268
1269    /* If pool storage specified, use it instead of job storage for backup */
1270    copy_wstorage(jcr, wpool->storage, _("Storage from Pool's NextPool resource"));
1271    return true;
1272 }