]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/migrate.c
ebl Fix runscript to always execute command after all job type.
[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    Bacula® - The Network Backup Solution
19
20    Copyright (C) 2004-2006 Free Software Foundation Europe e.V.
21
22    The main author of Bacula is Kern Sibbald, with contributions from
23    many others, a complete list can be found in the file AUTHORS.
24    This program is Free Software; you can redistribute it and/or
25    modify it under the terms of version two of the GNU General Public
26    License as published by the Free Software Foundation plus additions
27    that are listed in the file LICENSE.
28
29    This program is distributed in the hope that it will be useful, but
30    WITHOUT ANY WARRANTY; without even the implied warranty of
31    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
32    General Public License for more details.
33
34    You should have received a copy of the GNU General Public License
35    along with this program; if not, write to the Free Software
36    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
37    02110-1301, USA.
38
39    Bacula® is a registered trademark of John Walker.
40    The licensor of Bacula is the Free Software Foundation Europe
41    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
42    Switzerland, email:ftf@fsfeurope.org.
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    set_jcr_job_status(jcr, JS_Running);
281    set_jcr_job_status(mig_jcr, JS_Running);
282    Dmsg2(dbglevel, "JobId=%d JobLevel=%c\n", (int)jcr->jr.JobId, jcr->jr.JobLevel);
283
284    /* Update job start record for this migration control job */
285    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
286       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
287       return false;
288    }
289
290    Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
291       mig_jcr->jr.Name, (int)mig_jcr->jr.JobId, 
292       mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
293
294    /* Update job start record for the real migration backup job */
295    if (!db_update_job_start_record(mig_jcr, mig_jcr->db, &mig_jcr->jr)) {
296       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(mig_jcr->db));
297       return false;
298    }
299
300
301    /*
302     * Open a message channel connection with the Storage
303     * daemon. This is to let him know that our client
304     * will be contacting him for a backup  session.
305     *
306     */
307    Dmsg0(110, "Open connection with storage daemon\n");
308    set_jcr_job_status(jcr, JS_WaitSD);
309    set_jcr_job_status(mig_jcr, JS_WaitSD);
310    /*
311     * Start conversation with Storage daemon
312     */
313    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
314       return false;
315    }
316    sd = jcr->store_bsock;
317    /*
318     * Now start a job with the Storage daemon
319     */
320    Dmsg2(dbglevel, "Read store=%s, write store=%s\n", 
321       ((STORE *)jcr->rstorage->first())->name(),
322       ((STORE *)jcr->wstorage->first())->name());
323    if (((STORE *)jcr->rstorage->first())->name() == ((STORE *)jcr->wstorage->first())->name()) {
324       Jmsg(jcr, M_FATAL, 0, _("Read storage \"%s\" same as write storage.\n"),
325            ((STORE *)jcr->rstorage->first())->name());
326       return false;
327    }
328    if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage)) {
329       return false;
330    }
331    Dmsg0(150, "Storage daemon connection OK\n");
332
333    if (!send_bootstrap_file(jcr, sd) ||
334        !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
335       return false;
336    }
337
338    if (!bnet_fsend(sd, "run")) {
339       return false;
340    }
341
342    /*
343     * Now start a Storage daemon message thread
344     */
345    if (!start_storage_daemon_message_thread(jcr)) {
346       return false;
347    }
348
349
350    set_jcr_job_status(jcr, JS_Running);
351    set_jcr_job_status(mig_jcr, JS_Running);
352
353    /* Pickup Job termination data */
354    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
355    wait_for_storage_daemon_termination(jcr);
356
357    set_jcr_job_status(jcr, jcr->SDJobStatus);
358    if (jcr->JobStatus != JS_Terminated) {
359       return false;
360    }
361
362    migration_cleanup(jcr, jcr->JobStatus);
363    if (mig_jcr) {
364       UAContext *ua = new_ua_context(jcr);
365       purge_job_records_from_catalog(ua, jcr->previous_jr.JobId);
366       free_ua_context(ua);
367    }
368    return true;
369 }
370
371 struct idpkt {
372    POOLMEM *list;
373    uint32_t count;
374 };
375
376 /* Add an item to the list if it is unique */
377 static void add_unique_id(idpkt *ids, char *item) 
378 {
379    char id[30];
380    char *q = ids->list;
381
382    /* Walk through current list to see if each item is the same as item */
383    for ( ; *q; ) {
384        id[0] = 0;
385        for (int i=0; i<(int)sizeof(id); i++) {
386           if (*q == 0) {
387              break;
388           } else if (*q == ',') {
389              q++;
390              break;
391           }
392           id[i] = *q++;
393           id[i+1] = 0;
394        }
395        if (strcmp(item, id) == 0) {
396           return;
397        }
398    }
399    /* Did not find item, so add it to list */
400    if (ids->count == 0) {
401       ids->list[0] = 0;
402    } else {
403       pm_strcat(ids->list, ",");
404    }
405    pm_strcat(ids->list, item);
406    ids->count++;
407 // Dmsg3(0, "add_uniq count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
408    return;
409 }
410
411 /*
412  * Callback handler make list of DB Ids
413  */
414 static int unique_dbid_handler(void *ctx, int num_fields, char **row)
415 {
416    idpkt *ids = (idpkt *)ctx;
417
418    add_unique_id(ids, row[0]);
419    Dmsg3(dbglevel, "dbid_hdlr count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
420    return 0;
421 }
422
423
424 struct uitem {
425    dlink link;   
426    char *item;
427 };
428
429 static int item_compare(void *item1, void *item2)
430 {
431    uitem *i1 = (uitem *)item1;
432    uitem *i2 = (uitem *)item2;
433    return strcmp(i1->item, i2->item);
434 }
435
436 static int unique_name_handler(void *ctx, int num_fields, char **row)
437 {
438    dlist *list = (dlist *)ctx;
439
440    uitem *new_item = (uitem *)malloc(sizeof(uitem));
441    uitem *item;
442    
443    memset(new_item, 0, sizeof(uitem));
444    new_item->item = bstrdup(row[0]);
445    Dmsg1(dbglevel, "Unique_name_hdlr Item=%s\n", row[0]);
446    item = (uitem *)list->binary_insert((void *)new_item, item_compare);
447    if (item != new_item) {            /* already in list */
448       free(new_item->item);
449       free((char *)new_item);
450       return 0;
451    }
452    return 0;
453 }
454
455 /* Get Job names in Pool */
456 const char *sql_job =
457    "SELECT DISTINCT Job.Name from Job,Pool"
458    " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
459
460 /* Get JobIds from regex'ed Job names */
461 const char *sql_jobids_from_job =
462    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
463    " WHERE Job.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
464    " ORDER by Job.StartTime";
465
466 /* Get Client names in Pool */
467 const char *sql_client =
468    "SELECT DISTINCT Client.Name from Client,Pool,Job"
469    " WHERE Pool.Name='%s' AND Job.ClientId=Client.ClientId AND"
470    " Job.PoolId=Pool.PoolId";
471
472 /* Get JobIds from regex'ed Client names */
473 const char *sql_jobids_from_client =
474    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool,Client"
475    " WHERE Client.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
476    " AND Job.ClientId=Client.ClientId "
477    " ORDER by Job.StartTime";
478
479 /* Get Volume names in Pool */
480 const char *sql_vol = 
481    "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
482    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
483    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
484
485 /* Get JobIds from regex'ed Volume names */
486 const char *sql_jobids_from_vol =
487    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Media,JobMedia,Job"
488    " WHERE Media.VolumeName='%s' AND Media.MediaId=JobMedia.MediaId"
489    " AND JobMedia.JobId=Job.JobId" 
490    " ORDER by Job.StartTime";
491
492
493 const char *sql_smallest_vol = 
494    "SELECT MediaId FROM Media,Pool WHERE"
495    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
496    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
497    " ORDER BY VolBytes ASC LIMIT 1";
498
499 const char *sql_oldest_vol = 
500    "SELECT MediaId FROM Media,Pool WHERE"
501    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
502    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
503    " ORDER BY LastWritten ASC LIMIT 1";
504
505 /* Get JobIds when we have selected MediaId */
506 const char *sql_jobids_from_mediaid =
507    "SELECT DISTINCT Job.JobId,Job.StartTime FROM JobMedia,Job"
508    " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId=%s"
509    " ORDER by Job.StartTime";
510
511 /* Get tne number of bytes in the pool */
512 const char *sql_pool_bytes =
513    "SELECT SUM(VolBytes) FROM Media,Pool WHERE"
514    " VolStatus in ('Full','Used','Error','Append') AND Media.Enabled=1 AND"
515    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
516
517 /* Get tne number of bytes in the Jobs */
518 const char *sql_job_bytes =
519    "SELECT SUM(JobBytes) FROM Job WHERE JobId IN (%s)";
520
521
522 /* Get Media Ids in Pool */
523 const char *sql_mediaids =
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' ORDER BY LastWritten ASC";
527
528 /* Get JobIds in Pool longer than specified time */
529 const char *sql_pool_time = 
530    "SELECT DISTINCT Job.JobId from Pool,Job,Media,JobMedia WHERE"
531    " Pool.Name='%s' AND Media.PoolId=Pool.PoolId AND"
532    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
533    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId"
534    " AND Job.RealEndTime<='%s'";
535
536 /*
537 * const char *sql_ujobid =
538 *   "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
539 *   " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
540 *   " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
541 */
542
543
544
545 /*
546  *
547  * This is the central piece of code that finds a job or jobs 
548  *   actually JobIds to migrate.  It first looks to see if one
549  *   has been "manually" specified in jcr->MigrateJobId, and if
550  *   so, it returns that JobId to be run.  Otherwise, it
551  *   examines the Selection Type to see what kind of migration
552  *   we are doing (Volume, Job, Client, ...) and applies any
553  *   Selection Pattern if appropriate to obtain a list of JobIds.
554  *   Finally, it will loop over all the JobIds found, except the last
555  *   one starting a new job with MigrationJobId set to that JobId, and
556  *   finally, it returns the last JobId to the caller.
557  *
558  * Returns: -1  on error
559  *           0  if no jobs to migrate
560  *           1  if OK and jcr->previous_jr filled in
561  */
562 static int get_job_to_migrate(JCR *jcr)
563 {
564    char ed1[30];
565    POOL_MEM query(PM_MESSAGE);
566    JobId_t JobId;
567    DBId_t  MediaId = 0;
568    int stat;
569    char *p;
570    idpkt ids, mid, jids;
571    db_int64_ctx ctx;
572    int64_t pool_bytes;
573    time_t ttime;
574    struct tm tm;
575    char dt[MAX_TIME_LENGTH];
576    int count = 0;
577
578    ids.list = get_pool_memory(PM_MESSAGE);
579    ids.list[0] = 0;
580    ids.count = 0;
581    mid.list = get_pool_memory(PM_MESSAGE);
582    mid.list[0] = 0;
583    mid.count = 0;
584    jids.list = get_pool_memory(PM_MESSAGE);
585    jids.list[0] = 0;
586    jids.count = 0;
587
588
589    /*
590     * If MigrateJobId is set, then we migrate only that Job,
591     *  otherwise, we go through the full selection of jobs to
592     *  migrate.
593     */
594    if (jcr->MigrateJobId != 0) {
595       Dmsg1(dbglevel, "At Job start previous jobid=%u\n", jcr->MigrateJobId);
596       edit_uint64(jcr->MigrateJobId, ids.list);
597       ids.count = 1;
598    } else {
599       switch (jcr->job->selection_type) {
600       case MT_JOB:
601          if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
602             goto bail_out;
603          } 
604          break;
605       case MT_CLIENT:
606          if (!regex_find_jobids(jcr, &ids, sql_client, sql_jobids_from_client, "Client")) {
607             goto bail_out;
608          } 
609          break;
610       case MT_VOLUME:
611          if (!regex_find_jobids(jcr, &ids, sql_vol, sql_jobids_from_vol, "Volume")) {
612             goto bail_out;
613          } 
614          break;
615       case MT_SQLQUERY:
616          if (!jcr->job->selection_pattern) {
617             Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
618             goto bail_out;
619          }
620          Dmsg1(dbglevel, "SQL=%s\n", jcr->job->selection_pattern);
621          if (!db_sql_query(jcr->db, jcr->job->selection_pattern,
622               unique_dbid_handler, (void *)&ids)) {
623             Jmsg(jcr, M_FATAL, 0,
624                  _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
625             goto bail_out;
626          }
627          break;
628       case MT_SMALLEST_VOL:
629          if (!find_mediaid_then_jobids(jcr, &ids, sql_smallest_vol, "Smallest Volume")) {
630             goto bail_out;
631          }
632          break;
633       case MT_OLDEST_VOL:
634          if (!find_mediaid_then_jobids(jcr, &ids, sql_oldest_vol, "Oldest Volume")) {
635             goto bail_out;
636          }
637          break;
638
639       case MT_POOL_OCCUPANCY:
640          ctx.count = 0;
641          /* Find count of bytes in pool */
642          Mmsg(query, sql_pool_bytes, jcr->rpool->name());
643          if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
644             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
645             goto bail_out;
646          }
647          if (ctx.count == 0) {
648             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
649             goto ok_out;
650          }
651          pool_bytes = ctx.value;
652          Dmsg2(dbglevel, "highbytes=%d pool=%d\n", (int)jcr->rpool->MigrationHighBytes,
653                (int)pool_bytes);
654          if (pool_bytes < (int64_t)jcr->rpool->MigrationHighBytes) {
655             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
656             goto ok_out;
657          }
658          Dmsg0(dbglevel, "We should do Occupation migration.\n");
659
660          ids.count = 0;
661          /* Find a list of MediaIds that could be migrated */
662          Mmsg(query, sql_mediaids, jcr->rpool->name());
663          Dmsg1(dbglevel, "query=%s\n", query.c_str());
664          if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)&ids)) {
665             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
666             goto bail_out;
667          }
668          if (ids.count == 0) {
669             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
670             goto ok_out;
671          }
672          Dmsg2(dbglevel, "Pool Occupancy ids=%d MediaIds=%s\n", ids.count, ids.list);
673
674          /*
675           * Now loop over MediaIds getting more JobIds to migrate until
676           *  we reduce the pool occupancy below the low water mark.
677           */
678          p = ids.list;
679          for (int i=0; i < (int)ids.count; i++) {
680             stat = get_next_dbid_from_list(&p, &MediaId);
681             Dmsg2(dbglevel, "get_next_dbid stat=%d MediaId=%u\n", stat, MediaId);
682             if (stat < 0) {
683                Jmsg(jcr, M_FATAL, 0, _("Invalid MediaId found.\n"));
684                goto bail_out;
685             } else if (stat == 0) {
686                break;
687             }
688             mid.count = 1;
689             Mmsg(mid.list, "%s", edit_int64(MediaId, ed1));
690             if (!find_jobids_from_mediaid_list(jcr, &mid, "Volumes")) {
691                continue;
692             }
693             if (i != 0) {
694                pm_strcat(jids.list, ",");
695             }
696             pm_strcat(jids.list, mid.list);
697             jids.count += mid.count;
698
699             /* Now get the count of bytes added */
700             ctx.count = 0;
701             /* Find count of bytes from Jobs */
702             Mmsg(query, sql_job_bytes, mid.list);
703             if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
704                Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
705                goto bail_out;
706             }
707             pool_bytes -= ctx.value;
708             Dmsg1(dbglevel, "Job bytes=%d\n", (int)ctx.value);
709             Dmsg2(dbglevel, "lowbytes=%d pool=%d\n", (int)jcr->rpool->MigrationLowBytes,
710                   (int)pool_bytes);
711             if (pool_bytes <= (int64_t)jcr->rpool->MigrationLowBytes) {
712                Dmsg0(dbglevel, "We should be done.\n");
713                break;
714             }
715
716          }
717          Dmsg2(dbglevel, "Pool Occupancy ids=%d JobIds=%s\n", jids.count, jids.list);
718
719          break;
720
721       case MT_POOL_TIME:
722          ttime = time(NULL) - (time_t)jcr->rpool->MigrationTime;
723          (void)localtime_r(&ttime, &tm);
724          strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
725
726          ids.count = 0;
727          Mmsg(query, sql_pool_time, jcr->rpool->name(), dt);
728          Dmsg1(dbglevel, "query=%s\n", query.c_str());
729          if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)&ids)) {
730             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
731             goto bail_out;
732          }
733          if (ids.count == 0) {
734             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
735             goto ok_out;
736          }
737          Dmsg2(dbglevel, "PoolTime ids=%d JobIds=%s\n", ids.count, ids.list);
738          break;
739
740       default:
741          Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
742          goto bail_out;
743       }
744    }
745
746    /*
747     * Loop over all jobids except the last one, sending
748     *  them to start_migration_job(), which will start a job
749     *  for each of them.  For the last JobId, we handle it below.
750     */
751    p = ids.list;
752    if (ids.count == 0) {
753       Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
754       goto ok_out;
755    }
756    Jmsg(jcr, M_INFO, 0, _("The following %u JobId%s will be migrated: %s\n"),
757       ids.count, ids.count==0?"":"s", ids.list);
758    Dmsg2(dbglevel, "Before loop count=%d ids=%s\n", ids.count, ids.list);
759    for (int i=1; i < (int)ids.count; i++) {
760       JobId = 0;
761       stat = get_next_jobid_from_list(&p, &JobId);
762       Dmsg3(dbglevel, "get_jobid_no=%d stat=%d JobId=%u\n", i, stat, JobId);
763       jcr->MigrateJobId = JobId;
764       start_migration_job(jcr);
765       Dmsg0(dbglevel, "Back from start_migration_job\n");
766       if (stat < 0) {
767          Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
768          goto bail_out;
769       } else if (stat == 0) {
770          Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
771          goto ok_out;
772       }
773    }
774    
775    /* Now get the last JobId and handle it in the current job */
776    JobId = 0;
777    stat = get_next_jobid_from_list(&p, &JobId);
778    Dmsg2(dbglevel, "Last get_next_jobid stat=%d JobId=%u\n", stat, (int)JobId);
779    if (stat < 0) {
780       Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
781       goto bail_out;
782    } else if (stat == 0) {
783       Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
784       goto ok_out;
785    }
786
787    jcr->previous_jr.JobId = JobId;
788    Dmsg1(dbglevel, "Previous jobid=%d\n", (int)jcr->previous_jr.JobId);
789
790    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
791       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
792            edit_int64(jcr->previous_jr.JobId, ed1),
793            db_strerror(jcr->db));
794       goto bail_out;
795    }
796    Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%s Job=%s\n"),
797       edit_int64(jcr->previous_jr.JobId, ed1), jcr->previous_jr.Job);
798    Dmsg3(dbglevel, "Migration JobId=%d  using JobId=%s Job=%s\n",
799       jcr->JobId,
800       edit_int64(jcr->previous_jr.JobId, ed1), jcr->previous_jr.Job);
801    count = 1;
802
803 ok_out:
804    goto out;
805
806 bail_out:
807    count = -1;
808            
809 out:
810    free_pool_memory(ids.list);
811    free_pool_memory(mid.list);
812    free_pool_memory(jids.list);
813    return count;
814 }
815
816 static void start_migration_job(JCR *jcr)
817 {
818    UAContext *ua = new_ua_context(jcr);
819    char ed1[50];
820    ua->batch = true;
821    Mmsg(ua->cmd, "run %s jobid=%s", jcr->job->hdr.name, 
822         edit_uint64(jcr->MigrateJobId, ed1));
823    Dmsg1(dbglevel, "=============== Migration cmd=%s\n", ua->cmd);
824    parse_ua_args(ua);                 /* parse command */
825    int stat = run_cmd(ua, ua->cmd);
826    if (stat == 0) {
827       Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
828    } else {
829       Jmsg(jcr, M_INFO, 0, _("Migration JobId %d started.\n"), stat);
830    }
831    free_ua_context(ua);
832 }
833
834 static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
835                  const char *type) 
836 {
837    bool ok = false;
838    POOL_MEM query(PM_MESSAGE);
839
840    ids->count = 0;
841    /* Basic query for MediaId */
842    Mmsg(query, query1, jcr->rpool->name());
843    if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
844       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
845       goto bail_out;
846    }
847    if (ids->count == 0) {
848       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
849    }
850    if (ids->count != 1) {
851       Jmsg(jcr, M_FATAL, 0, _("SQL logic error. Count should be 1 but is %d\n"), 
852          ids->count);
853       goto bail_out;
854    }
855    Dmsg1(dbglevel, "Smallest Vol Jobids=%s\n", ids->list);
856
857    ok = find_jobids_from_mediaid_list(jcr, ids, type);
858
859 bail_out:
860    return ok;
861 }
862
863 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type) 
864 {
865    bool ok = false;
866    POOL_MEM query(PM_MESSAGE);
867
868    Mmsg(query, sql_jobids_from_mediaid, ids->list);
869    ids->count = 0;
870    if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
871       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
872       goto bail_out;
873    }
874    if (ids->count == 0) {
875       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
876    }
877    ok = true;
878 bail_out:
879    return ok;
880 }
881
882 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
883                  const char *query2, const char *type) 
884 {
885    dlist *item_chain;
886    uitem *item = NULL;
887    uitem *last_item = NULL;
888    regex_t preg;
889    char prbuf[500];
890    int rc;
891    bool ok = false;
892    POOL_MEM query(PM_MESSAGE);
893
894    item_chain = New(dlist(item, &item->link));
895    if (!jcr->job->selection_pattern) {
896       Jmsg(jcr, M_FATAL, 0, _("No Migration %s selection pattern specified.\n"),
897          type);
898       goto bail_out;
899    }
900    Dmsg1(dbglevel, "regex-sel-pattern=%s\n", jcr->job->selection_pattern);
901    /* Basic query for names */
902    Mmsg(query, query1, jcr->rpool->name());
903    Dmsg1(dbglevel, "get name query1=%s\n", query.c_str());
904    if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
905         (void *)item_chain)) {
906       Jmsg(jcr, M_FATAL, 0,
907            _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
908       goto bail_out;
909    }
910    Dmsg1(dbglevel, "query1 returned %d names\n", item_chain->size());
911    if (item_chain->size() == 0) {
912       Jmsg(jcr, M_INFO, 0, _("Query of Pool \"%s\" returned no Jobs to migrate.\n"),
913            jcr->rpool->name());
914       ok = true;
915       goto bail_out;               /* skip regex match */
916    } else {
917       /* Compile regex expression */
918       rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
919       if (rc != 0) {
920          regerror(rc, &preg, prbuf, sizeof(prbuf));
921          Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
922               jcr->job->selection_pattern, prbuf);
923          goto bail_out;
924       }
925       /* Now apply the regex to the names and remove any item not matched */
926       foreach_dlist(item, item_chain) {
927          const int nmatch = 30;
928          regmatch_t pmatch[nmatch];
929          if (last_item) {
930             Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
931             free(last_item->item);
932             item_chain->remove(last_item);
933          }
934          Dmsg1(dbglevel, "get name Item=%s\n", item->item);
935          rc = regexec(&preg, item->item, nmatch, pmatch,  0);
936          if (rc == 0) {
937             last_item = NULL;   /* keep this one */
938          } else {   
939             last_item = item;
940          }
941       }
942       if (last_item) {
943          free(last_item->item);
944          Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
945          item_chain->remove(last_item);
946       }
947       regfree(&preg);
948    }
949    if (item_chain->size() == 0) {
950       Jmsg(jcr, M_INFO, 0, _("Regex pattern matched no Jobs to migrate.\n"));
951       ok = true;
952       goto bail_out;               /* skip regex match */
953    }
954
955    /* 
956     * At this point, we have a list of items in item_chain
957     *  that have been matched by the regex, so now we need
958     *  to look up their jobids.
959     */
960    ids->count = 0;
961    foreach_dlist(item, item_chain) {
962       Dmsg2(dbglevel, "Got %s: %s\n", type, item->item);
963       Mmsg(query, query2, item->item, jcr->rpool->name());
964       Dmsg1(dbglevel, "get id from name query2=%s\n", query.c_str());
965       if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
966          Jmsg(jcr, M_FATAL, 0,
967               _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
968          goto bail_out;
969       }
970    }
971    if (ids->count == 0) {
972       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
973    }
974    ok = true;
975
976 bail_out:
977    Dmsg2(dbglevel, "Count=%d Jobids=%s\n", ids->count, ids->list);
978    delete item_chain;
979    return ok;
980 }
981
982
983 /*
984  * Release resources allocated during backup.
985  */
986 void migration_cleanup(JCR *jcr, int TermCode)
987 {
988    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
989    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
990    char ec6[50], ec7[50], ec8[50];
991    char term_code[100], sd_term_msg[100];
992    const char *term_msg;
993    int msg_type = M_INFO;
994    MEDIA_DBR mr;
995    double kbps;
996    utime_t RunTime;
997    JCR *mig_jcr = jcr->mig_jcr;
998    POOL_MEM query(PM_MESSAGE);
999
1000    Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
1001    update_job_end(jcr, TermCode);
1002    memset(&mr, 0, sizeof(mr));
1003
1004    /* 
1005     * Check if we actually did something.  
1006     *  mig_jcr is jcr of the newly migrated job.
1007     */
1008    if (mig_jcr) {
1009       mig_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
1010       mig_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
1011       mig_jcr->VolSessionId = jcr->VolSessionId;
1012       mig_jcr->VolSessionTime = jcr->VolSessionTime;
1013       mig_jcr->jr.RealEndTime = 0; 
1014       mig_jcr->jr.PriorJobId = jcr->previous_jr.JobId;
1015
1016       update_job_end(mig_jcr, TermCode);
1017      
1018       /* Update final items to set them to the previous job's values */
1019       Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
1020                   "JobTDate=%s WHERE JobId=%s", 
1021          jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
1022          edit_uint64(jcr->previous_jr.JobTDate, ec1),
1023          edit_uint64(mig_jcr->jr.JobId, ec2));
1024       db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1025
1026       /* Now mark the previous job as migrated if it terminated normally */
1027       if (jcr->JobStatus == JS_Terminated) {
1028          Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1029               (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
1030          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1031       } 
1032
1033       if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
1034          Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
1035             db_strerror(jcr->db));
1036          set_jcr_job_status(jcr, JS_ErrorTerminated);
1037       }
1038
1039       bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
1040       if (!db_get_media_record(jcr, jcr->db, &mr)) {
1041          Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
1042             mr.VolumeName, db_strerror(jcr->db));
1043          set_jcr_job_status(jcr, JS_ErrorTerminated);
1044       }
1045
1046       update_bootstrap_file(mig_jcr);
1047
1048       if (!db_get_job_volume_names(mig_jcr, mig_jcr->db, mig_jcr->jr.JobId, &mig_jcr->VolumeName)) {
1049          /*
1050           * Note, if the job has failed, most likely it did not write any
1051           *  tape, so suppress this "error" message since in that case
1052           *  it is normal.  Or look at it the other way, only for a
1053           *  normal exit should we complain about this error.
1054           */
1055          if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
1056             Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(mig_jcr->db));
1057          }
1058          mig_jcr->VolumeName[0] = 0;         /* none */
1059       }
1060       switch (jcr->JobStatus) {
1061       case JS_Terminated:
1062          if (jcr->Errors || jcr->SDErrors) {
1063             term_msg = _("%s OK -- with warnings");
1064          } else {
1065             term_msg = _("%s OK");
1066          }
1067          break;
1068       case JS_FatalError:
1069       case JS_ErrorTerminated:
1070          term_msg = _("*** %s Error ***");
1071          msg_type = M_ERROR;          /* Generate error message */
1072          if (jcr->store_bsock) {
1073             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
1074             if (jcr->SD_msg_chan) {
1075                pthread_cancel(jcr->SD_msg_chan);
1076             }
1077          }
1078          break;
1079       case JS_Canceled:
1080          term_msg = _("%s Canceled");
1081          if (jcr->store_bsock) {
1082             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
1083             if (jcr->SD_msg_chan) {
1084                pthread_cancel(jcr->SD_msg_chan);
1085             }
1086          }
1087          break;
1088       default:
1089          term_msg = _("Inappropriate %s term code");
1090          break;
1091       }
1092   } else {
1093      term_msg = _("%s -- no files to migrate");
1094   }
1095
1096    bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
1097    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
1098    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
1099    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
1100    if (RunTime <= 0) {
1101       kbps = 0;
1102    } else {
1103       kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
1104    }
1105
1106
1107    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
1108
1109    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
1110 "  Prev Backup JobId:      %s\n"
1111 "  New Backup JobId:       %s\n"
1112 "  Migration JobId:        %s\n"
1113 "  Migration Job:          %s\n"
1114 "  Backup Level:           %s%s\n"
1115 "  Client:                 %s\n"
1116 "  FileSet:                \"%s\" %s\n"
1117 "  Read Pool:              \"%s\" (From %s)\n"
1118 "  Read Storage:           \"%s\" (From %s)\n"
1119 "  Write Pool:             \"%s\" (From %s)\n"
1120 "  Write Storage:          \"%s\" (From %s)\n"
1121 "  Start time:             %s\n"
1122 "  End time:               %s\n"
1123 "  Elapsed time:           %s\n"
1124 "  Priority:               %d\n"
1125 "  SD Files Written:       %s\n"
1126 "  SD Bytes Written:       %s (%sB)\n"
1127 "  Rate:                   %.1f KB/s\n"
1128 "  Volume name(s):         %s\n"
1129 "  Volume Session Id:      %d\n"
1130 "  Volume Session Time:    %d\n"
1131 "  Last Volume Bytes:      %s (%sB)\n"
1132 "  SD Errors:              %d\n"
1133 "  SD termination status:  %s\n"
1134 "  Termination:            %s\n\n"),
1135    VERSION,
1136    LSMDATE,
1137         edt, 
1138         edit_uint64(jcr->previous_jr.JobId, ec6),
1139         mig_jcr ? edit_uint64(mig_jcr->jr.JobId, ec7) : "0",
1140         edit_uint64(jcr->jr.JobId, ec8),
1141         jcr->jr.Job,
1142         level_to_str(jcr->JobLevel), jcr->since,
1143         jcr->client->name(),
1144         jcr->fileset->name(), jcr->FSCreateTime,
1145         jcr->rpool->name(), jcr->rpool_source,
1146         jcr->rstore?jcr->rstore->name():"*None*", 
1147         NPRT(jcr->rstore_source), 
1148         jcr->pool->name(), jcr->pool_source,
1149         jcr->wstore?jcr->wstore->name():"*None*", 
1150         NPRT(jcr->wstore_source),
1151         sdt,
1152         edt,
1153         edit_utime(RunTime, elapsed, sizeof(elapsed)),
1154         jcr->JobPriority,
1155         edit_uint64_with_commas(jcr->SDJobFiles, ec1),
1156         edit_uint64_with_commas(jcr->SDJobBytes, ec2),
1157         edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
1158         (float)kbps,
1159         mig_jcr ? mig_jcr->VolumeName : "",
1160         jcr->VolSessionId,
1161         jcr->VolSessionTime,
1162         edit_uint64_with_commas(mr.VolBytes, ec4),
1163         edit_uint64_with_suffix(mr.VolBytes, ec5),
1164         jcr->SDErrors,
1165         sd_term_msg,
1166         term_code);
1167
1168    Dmsg1(100, "migrate_cleanup() mig_jcr=0x%x\n", jcr->mig_jcr);
1169    if (jcr->mig_jcr) {
1170       free_jcr(jcr->mig_jcr);
1171       jcr->mig_jcr = NULL;
1172    }
1173    Dmsg0(100, "Leave migrate_cleanup()\n");
1174 }
1175
1176 /* 
1177  * Return next DBId from comma separated list   
1178  *
1179  * Returns:
1180  *   1 if next DBId returned
1181  *   0 if no more DBIds are in list
1182  *  -1 there is an error
1183  */
1184 static int get_next_dbid_from_list(char **p, DBId_t *DBId)
1185 {
1186    char id[30];
1187    char *q = *p;
1188
1189    id[0] = 0;
1190    for (int i=0; i<(int)sizeof(id); i++) {
1191       if (*q == 0) {
1192          break;
1193       } else if (*q == ',') {
1194          q++;
1195          break;
1196       }
1197       id[i] = *q++;
1198       id[i+1] = 0;
1199    }
1200    if (id[0] == 0) {
1201       return 0;
1202    } else if (!is_a_number(id)) {
1203       return -1;                      /* error */
1204    }
1205    *p = q;
1206    *DBId = str_to_int64(id);
1207    return 1;
1208 }
1209
1210 bool set_migration_wstorage(JCR *jcr, POOL *pool)
1211 {
1212    POOL *wpool = pool->NextPool;
1213
1214    if (!wpool) {
1215       Jmsg(jcr, M_FATAL, 0, _("No Next Pool specification found in Pool \"%s\".\n"),
1216          pool->hdr.name);
1217       return false;
1218    }
1219
1220    if (!wpool->storage || wpool->storage->size() == 0) {
1221       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Next Pool \"%s\".\n"),
1222          wpool->name());
1223       return false;
1224    }
1225
1226    /* If pool storage specified, use it instead of job storage for backup */
1227    copy_wstorage(jcr, wpool->storage, _("Storage from Pool's NextPool resource"));
1228    return true;
1229 }