]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/migrate.c
88a5189d3a860be6ba8575da8c8e1e9b610f8a18
[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 AND Job.Type='B'"
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 AND Job.Type='B'"
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    " AND Job.Type='B'"
510    " ORDER by Job.StartTime";
511
512 /* Get tne number of bytes in the pool */
513 const char *sql_pool_bytes =
514    "SELECT SUM(VolBytes) FROM Media,Pool WHERE"
515    " VolStatus in ('Full','Used','Error','Append') AND Media.Enabled=1 AND"
516    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
517
518 /* Get tne number of bytes in the Jobs */
519 const char *sql_job_bytes =
520    "SELECT SUM(JobBytes) FROM Job WHERE JobId IN (%s)";
521
522
523 /* Get Media Ids in Pool */
524 const char *sql_mediaids =
525    "SELECT MediaId FROM Media,Pool WHERE"
526    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
527    " Media.PoolId=Pool.PoolId AND Pool.Name='%s' ORDER BY LastWritten ASC";
528
529 /* Get JobIds in Pool longer than specified time */
530 const char *sql_pool_time = 
531    "SELECT DISTINCT Job.JobId from Pool,Job,Media,JobMedia WHERE"
532    " Pool.Name='%s' AND Media.PoolId=Pool.PoolId AND"
533    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
534    " Job.Type='B' AND"
535    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId"
536    " AND Job.RealEndTime<='%s'";
537
538 /*
539 * const char *sql_ujobid =
540 *   "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
541 *   " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
542 *   " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
543 */
544
545
546
547 /*
548  *
549  * This is the central piece of code that finds a job or jobs 
550  *   actually JobIds to migrate.  It first looks to see if one
551  *   has been "manually" specified in jcr->MigrateJobId, and if
552  *   so, it returns that JobId to be run.  Otherwise, it
553  *   examines the Selection Type to see what kind of migration
554  *   we are doing (Volume, Job, Client, ...) and applies any
555  *   Selection Pattern if appropriate to obtain a list of JobIds.
556  *   Finally, it will loop over all the JobIds found, except the last
557  *   one starting a new job with MigrationJobId set to that JobId, and
558  *   finally, it returns the last JobId to the caller.
559  *
560  * Returns: -1  on error
561  *           0  if no jobs to migrate
562  *           1  if OK and jcr->previous_jr filled in
563  */
564 static int get_job_to_migrate(JCR *jcr)
565 {
566    char ed1[30];
567    POOL_MEM query(PM_MESSAGE);
568    JobId_t JobId;
569    DBId_t  MediaId = 0;
570    int stat;
571    char *p;
572    idpkt ids, mid, jids;
573    db_int64_ctx ctx;
574    int64_t pool_bytes;
575    time_t ttime;
576    struct tm tm;
577    char dt[MAX_TIME_LENGTH];
578    int count = 0;
579
580    ids.list = get_pool_memory(PM_MESSAGE);
581    ids.list[0] = 0;
582    ids.count = 0;
583    mid.list = get_pool_memory(PM_MESSAGE);
584    mid.list[0] = 0;
585    mid.count = 0;
586    jids.list = get_pool_memory(PM_MESSAGE);
587    jids.list[0] = 0;
588    jids.count = 0;
589
590
591    /*
592     * If MigrateJobId is set, then we migrate only that Job,
593     *  otherwise, we go through the full selection of jobs to
594     *  migrate.
595     */
596    if (jcr->MigrateJobId != 0) {
597       Dmsg1(dbglevel, "At Job start previous jobid=%u\n", jcr->MigrateJobId);
598       edit_uint64(jcr->MigrateJobId, ids.list);
599       ids.count = 1;
600    } else {
601       switch (jcr->job->selection_type) {
602       case MT_JOB:
603          if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
604             goto bail_out;
605          } 
606          break;
607       case MT_CLIENT:
608          if (!regex_find_jobids(jcr, &ids, sql_client, sql_jobids_from_client, "Client")) {
609             goto bail_out;
610          } 
611          break;
612       case MT_VOLUME:
613          if (!regex_find_jobids(jcr, &ids, sql_vol, sql_jobids_from_vol, "Volume")) {
614             goto bail_out;
615          } 
616          break;
617       case MT_SQLQUERY:
618          if (!jcr->job->selection_pattern) {
619             Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
620             goto bail_out;
621          }
622          Dmsg1(dbglevel, "SQL=%s\n", jcr->job->selection_pattern);
623          if (!db_sql_query(jcr->db, jcr->job->selection_pattern,
624               unique_dbid_handler, (void *)&ids)) {
625             Jmsg(jcr, M_FATAL, 0,
626                  _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
627             goto bail_out;
628          }
629          break;
630       case MT_SMALLEST_VOL:
631          if (!find_mediaid_then_jobids(jcr, &ids, sql_smallest_vol, "Smallest Volume")) {
632             goto bail_out;
633          }
634          break;
635       case MT_OLDEST_VOL:
636          if (!find_mediaid_then_jobids(jcr, &ids, sql_oldest_vol, "Oldest Volume")) {
637             goto bail_out;
638          }
639          break;
640
641       case MT_POOL_OCCUPANCY:
642          ctx.count = 0;
643          /* Find count of bytes in pool */
644          Mmsg(query, sql_pool_bytes, jcr->rpool->name());
645          if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
646             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
647             goto bail_out;
648          }
649          if (ctx.count == 0) {
650             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
651             goto ok_out;
652          }
653          pool_bytes = ctx.value;
654          Dmsg2(dbglevel, "highbytes=%d pool=%d\n", (int)jcr->rpool->MigrationHighBytes,
655                (int)pool_bytes);
656          if (pool_bytes < (int64_t)jcr->rpool->MigrationHighBytes) {
657             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
658             goto ok_out;
659          }
660          Dmsg0(dbglevel, "We should do Occupation migration.\n");
661
662          ids.count = 0;
663          /* Find a list of MediaIds that could be migrated */
664          Mmsg(query, sql_mediaids, jcr->rpool->name());
665          Dmsg1(dbglevel, "query=%s\n", query.c_str());
666          if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)&ids)) {
667             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
668             goto bail_out;
669          }
670          if (ids.count == 0) {
671             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
672             goto ok_out;
673          }
674          Dmsg2(dbglevel, "Pool Occupancy ids=%d MediaIds=%s\n", ids.count, ids.list);
675
676          /*
677           * Now loop over MediaIds getting more JobIds to migrate until
678           *  we reduce the pool occupancy below the low water mark.
679           */
680          p = ids.list;
681          for (int i=0; i < (int)ids.count; i++) {
682             stat = get_next_dbid_from_list(&p, &MediaId);
683             Dmsg2(dbglevel, "get_next_dbid stat=%d MediaId=%u\n", stat, MediaId);
684             if (stat < 0) {
685                Jmsg(jcr, M_FATAL, 0, _("Invalid MediaId found.\n"));
686                goto bail_out;
687             } else if (stat == 0) {
688                break;
689             }
690             mid.count = 1;
691             Mmsg(mid.list, "%s", edit_int64(MediaId, ed1));
692             if (!find_jobids_from_mediaid_list(jcr, &mid, "Volumes")) {
693                continue;
694             }
695             if (i != 0) {
696                pm_strcat(jids.list, ",");
697             }
698             pm_strcat(jids.list, mid.list);
699             jids.count += mid.count;
700
701             /* Now get the count of bytes added */
702             ctx.count = 0;
703             /* Find count of bytes from Jobs */
704             Mmsg(query, sql_job_bytes, mid.list);
705             if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
706                Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
707                goto bail_out;
708             }
709             pool_bytes -= ctx.value;
710             Dmsg1(dbglevel, "Job bytes=%d\n", (int)ctx.value);
711             Dmsg2(dbglevel, "lowbytes=%d pool=%d\n", (int)jcr->rpool->MigrationLowBytes,
712                   (int)pool_bytes);
713             if (pool_bytes <= (int64_t)jcr->rpool->MigrationLowBytes) {
714                Dmsg0(dbglevel, "We should be done.\n");
715                break;
716             }
717
718          }
719          Dmsg2(dbglevel, "Pool Occupancy ids=%d JobIds=%s\n", jids.count, jids.list);
720
721          break;
722
723       case MT_POOL_TIME:
724          ttime = time(NULL) - (time_t)jcr->rpool->MigrationTime;
725          (void)localtime_r(&ttime, &tm);
726          strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
727
728          ids.count = 0;
729          Mmsg(query, sql_pool_time, jcr->rpool->name(), dt);
730          Dmsg1(dbglevel, "query=%s\n", query.c_str());
731          if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)&ids)) {
732             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
733             goto bail_out;
734          }
735          if (ids.count == 0) {
736             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
737             goto ok_out;
738          }
739          Dmsg2(dbglevel, "PoolTime ids=%d JobIds=%s\n", ids.count, ids.list);
740          break;
741
742       default:
743          Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
744          goto bail_out;
745       }
746    }
747
748    /*
749     * Loop over all jobids except the last one, sending
750     *  them to start_migration_job(), which will start a job
751     *  for each of them.  For the last JobId, we handle it below.
752     */
753    p = ids.list;
754    if (ids.count == 0) {
755       Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
756       goto ok_out;
757    }
758    Jmsg(jcr, M_INFO, 0, _("The following %u JobId%s will be migrated: %s\n"),
759       ids.count, ids.count==0?"":"s", ids.list);
760    Dmsg2(dbglevel, "Before loop count=%d ids=%s\n", ids.count, ids.list);
761    for (int i=1; i < (int)ids.count; i++) {
762       JobId = 0;
763       stat = get_next_jobid_from_list(&p, &JobId);
764       Dmsg3(dbglevel, "get_jobid_no=%d stat=%d JobId=%u\n", i, stat, JobId);
765       jcr->MigrateJobId = JobId;
766       start_migration_job(jcr);
767       Dmsg0(dbglevel, "Back from start_migration_job\n");
768       if (stat < 0) {
769          Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
770          goto bail_out;
771       } else if (stat == 0) {
772          Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
773          goto ok_out;
774       }
775    }
776    
777    /* Now get the last JobId and handle it in the current job */
778    JobId = 0;
779    stat = get_next_jobid_from_list(&p, &JobId);
780    Dmsg2(dbglevel, "Last get_next_jobid stat=%d JobId=%u\n", stat, (int)JobId);
781    if (stat < 0) {
782       Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
783       goto bail_out;
784    } else if (stat == 0) {
785       Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
786       goto ok_out;
787    }
788
789    jcr->previous_jr.JobId = JobId;
790    Dmsg1(dbglevel, "Previous jobid=%d\n", (int)jcr->previous_jr.JobId);
791
792    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
793       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
794            edit_int64(jcr->previous_jr.JobId, ed1),
795            db_strerror(jcr->db));
796       goto bail_out;
797    }
798    Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%s Job=%s\n"),
799       edit_int64(jcr->previous_jr.JobId, ed1), jcr->previous_jr.Job);
800    Dmsg3(dbglevel, "Migration JobId=%d  using JobId=%s Job=%s\n",
801       jcr->JobId,
802       edit_int64(jcr->previous_jr.JobId, ed1), jcr->previous_jr.Job);
803    count = 1;
804
805 ok_out:
806    goto out;
807
808 bail_out:
809    count = -1;
810            
811 out:
812    free_pool_memory(ids.list);
813    free_pool_memory(mid.list);
814    free_pool_memory(jids.list);
815    return count;
816 }
817
818 static void start_migration_job(JCR *jcr)
819 {
820    UAContext *ua = new_ua_context(jcr);
821    char ed1[50];
822    ua->batch = true;
823    Mmsg(ua->cmd, "run %s jobid=%s", jcr->job->hdr.name, 
824         edit_uint64(jcr->MigrateJobId, ed1));
825    Dmsg1(dbglevel, "=============== Migration cmd=%s\n", ua->cmd);
826    parse_ua_args(ua);                 /* parse command */
827    int stat = run_cmd(ua, ua->cmd);
828    if (stat == 0) {
829       Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
830    } else {
831       Jmsg(jcr, M_INFO, 0, _("Migration JobId %d started.\n"), stat);
832    }
833    free_ua_context(ua);
834 }
835
836 static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
837                  const char *type) 
838 {
839    bool ok = false;
840    POOL_MEM query(PM_MESSAGE);
841
842    ids->count = 0;
843    /* Basic query for MediaId */
844    Mmsg(query, query1, jcr->rpool->name());
845    if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
846       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
847       goto bail_out;
848    }
849    if (ids->count == 0) {
850       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
851    }
852    if (ids->count != 1) {
853       Jmsg(jcr, M_FATAL, 0, _("SQL logic error. Count should be 1 but is %d\n"), 
854          ids->count);
855       goto bail_out;
856    }
857    Dmsg1(dbglevel, "Smallest Vol Jobids=%s\n", ids->list);
858
859    ok = find_jobids_from_mediaid_list(jcr, ids, type);
860
861 bail_out:
862    return ok;
863 }
864
865 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type) 
866 {
867    bool ok = false;
868    POOL_MEM query(PM_MESSAGE);
869
870    Mmsg(query, sql_jobids_from_mediaid, ids->list);
871    ids->count = 0;
872    if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
873       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
874       goto bail_out;
875    }
876    if (ids->count == 0) {
877       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
878    }
879    ok = true;
880 bail_out:
881    return ok;
882 }
883
884 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
885                  const char *query2, const char *type) 
886 {
887    dlist *item_chain;
888    uitem *item = NULL;
889    uitem *last_item = NULL;
890    regex_t preg;
891    char prbuf[500];
892    int rc;
893    bool ok = false;
894    POOL_MEM query(PM_MESSAGE);
895
896    item_chain = New(dlist(item, &item->link));
897    if (!jcr->job->selection_pattern) {
898       Jmsg(jcr, M_FATAL, 0, _("No Migration %s selection pattern specified.\n"),
899          type);
900       goto bail_out;
901    }
902    Dmsg1(dbglevel, "regex-sel-pattern=%s\n", jcr->job->selection_pattern);
903    /* Basic query for names */
904    Mmsg(query, query1, jcr->rpool->name());
905    Dmsg1(dbglevel, "get name query1=%s\n", query.c_str());
906    if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
907         (void *)item_chain)) {
908       Jmsg(jcr, M_FATAL, 0,
909            _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
910       goto bail_out;
911    }
912    Dmsg1(dbglevel, "query1 returned %d names\n", item_chain->size());
913    if (item_chain->size() == 0) {
914       Jmsg(jcr, M_INFO, 0, _("Query of Pool \"%s\" returned no Jobs to migrate.\n"),
915            jcr->rpool->name());
916       ok = true;
917       goto bail_out;               /* skip regex match */
918    } else {
919       /* Compile regex expression */
920       rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
921       if (rc != 0) {
922          regerror(rc, &preg, prbuf, sizeof(prbuf));
923          Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
924               jcr->job->selection_pattern, prbuf);
925          goto bail_out;
926       }
927       /* Now apply the regex to the names and remove any item not matched */
928       foreach_dlist(item, item_chain) {
929          const int nmatch = 30;
930          regmatch_t pmatch[nmatch];
931          if (last_item) {
932             Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
933             free(last_item->item);
934             item_chain->remove(last_item);
935          }
936          Dmsg1(dbglevel, "get name Item=%s\n", item->item);
937          rc = regexec(&preg, item->item, nmatch, pmatch,  0);
938          if (rc == 0) {
939             last_item = NULL;   /* keep this one */
940          } else {   
941             last_item = item;
942          }
943       }
944       if (last_item) {
945          free(last_item->item);
946          Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
947          item_chain->remove(last_item);
948       }
949       regfree(&preg);
950    }
951    if (item_chain->size() == 0) {
952       Jmsg(jcr, M_INFO, 0, _("Regex pattern matched no Jobs to migrate.\n"));
953       ok = true;
954       goto bail_out;               /* skip regex match */
955    }
956
957    /* 
958     * At this point, we have a list of items in item_chain
959     *  that have been matched by the regex, so now we need
960     *  to look up their jobids.
961     */
962    ids->count = 0;
963    foreach_dlist(item, item_chain) {
964       Dmsg2(dbglevel, "Got %s: %s\n", type, item->item);
965       Mmsg(query, query2, item->item, jcr->rpool->name());
966       Dmsg1(dbglevel, "get id from name query2=%s\n", query.c_str());
967       if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
968          Jmsg(jcr, M_FATAL, 0,
969               _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
970          goto bail_out;
971       }
972    }
973    if (ids->count == 0) {
974       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
975    }
976    ok = true;
977
978 bail_out:
979    Dmsg2(dbglevel, "Count=%d Jobids=%s\n", ids->count, ids->list);
980    delete item_chain;
981    return ok;
982 }
983
984
985 /*
986  * Release resources allocated during backup.
987  */
988 void migration_cleanup(JCR *jcr, int TermCode)
989 {
990    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
991    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
992    char ec6[50], ec7[50], ec8[50];
993    char term_code[100], sd_term_msg[100];
994    const char *term_msg;
995    int msg_type = M_INFO;
996    MEDIA_DBR mr;
997    double kbps;
998    utime_t RunTime;
999    JCR *mig_jcr = jcr->mig_jcr;
1000    POOL_MEM query(PM_MESSAGE);
1001
1002    Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
1003    update_job_end(jcr, TermCode);
1004    memset(&mr, 0, sizeof(mr));
1005
1006    /* 
1007     * Check if we actually did something.  
1008     *  mig_jcr is jcr of the newly migrated job.
1009     */
1010    if (mig_jcr) {
1011       mig_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
1012       mig_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
1013       mig_jcr->VolSessionId = jcr->VolSessionId;
1014       mig_jcr->VolSessionTime = jcr->VolSessionTime;
1015       mig_jcr->jr.RealEndTime = 0; 
1016       mig_jcr->jr.PriorJobId = jcr->previous_jr.JobId;
1017
1018       update_job_end(mig_jcr, TermCode);
1019      
1020       /* Update final items to set them to the previous job's values */
1021       Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
1022                   "JobTDate=%s WHERE JobId=%s", 
1023          jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
1024          edit_uint64(jcr->previous_jr.JobTDate, ec1),
1025          edit_uint64(mig_jcr->jr.JobId, ec2));
1026       db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1027
1028       /* Now mark the previous job as migrated if it terminated normally */
1029       if (jcr->JobStatus == JS_Terminated) {
1030          Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1031               (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
1032          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1033       } 
1034
1035       if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
1036          Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
1037             db_strerror(jcr->db));
1038          set_jcr_job_status(jcr, JS_ErrorTerminated);
1039       }
1040
1041       bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
1042       if (!db_get_media_record(jcr, jcr->db, &mr)) {
1043          Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
1044             mr.VolumeName, db_strerror(jcr->db));
1045          set_jcr_job_status(jcr, JS_ErrorTerminated);
1046       }
1047
1048       update_bootstrap_file(mig_jcr);
1049
1050       if (!db_get_job_volume_names(mig_jcr, mig_jcr->db, mig_jcr->jr.JobId, &mig_jcr->VolumeName)) {
1051          /*
1052           * Note, if the job has failed, most likely it did not write any
1053           *  tape, so suppress this "error" message since in that case
1054           *  it is normal.  Or look at it the other way, only for a
1055           *  normal exit should we complain about this error.
1056           */
1057          if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
1058             Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(mig_jcr->db));
1059          }
1060          mig_jcr->VolumeName[0] = 0;         /* none */
1061       }
1062       switch (jcr->JobStatus) {
1063       case JS_Terminated:
1064          if (jcr->Errors || jcr->SDErrors) {
1065             term_msg = _("%s OK -- with warnings");
1066          } else {
1067             term_msg = _("%s OK");
1068          }
1069          break;
1070       case JS_FatalError:
1071       case JS_ErrorTerminated:
1072          term_msg = _("*** %s Error ***");
1073          msg_type = M_ERROR;          /* Generate error message */
1074          if (jcr->store_bsock) {
1075             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
1076             if (jcr->SD_msg_chan) {
1077                pthread_cancel(jcr->SD_msg_chan);
1078             }
1079          }
1080          break;
1081       case JS_Canceled:
1082          term_msg = _("%s Canceled");
1083          if (jcr->store_bsock) {
1084             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
1085             if (jcr->SD_msg_chan) {
1086                pthread_cancel(jcr->SD_msg_chan);
1087             }
1088          }
1089          break;
1090       default:
1091          term_msg = _("Inappropriate %s term code");
1092          break;
1093       }
1094   } else {
1095      if (jcr->previous_jr.JobId != 0) {
1096         /* Mark previous job as migrated */
1097         Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1098              (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
1099         Dmsg1(000, "Mark: %s\n", query.c_str());
1100         db_sql_query(jcr->db, query.c_str(), NULL, NULL);
1101      }
1102      term_msg = _("%s -- no files to migrate");
1103   }
1104
1105    bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
1106    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
1107    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
1108    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
1109    if (RunTime <= 0) {
1110       kbps = 0;
1111    } else {
1112       kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
1113    }
1114
1115
1116    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
1117
1118    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
1119 "  Prev Backup JobId:      %s\n"
1120 "  New Backup JobId:       %s\n"
1121 "  Migration JobId:        %s\n"
1122 "  Migration Job:          %s\n"
1123 "  Backup Level:           %s%s\n"
1124 "  Client:                 %s\n"
1125 "  FileSet:                \"%s\" %s\n"
1126 "  Read Pool:              \"%s\" (From %s)\n"
1127 "  Read Storage:           \"%s\" (From %s)\n"
1128 "  Write Pool:             \"%s\" (From %s)\n"
1129 "  Write Storage:          \"%s\" (From %s)\n"
1130 "  Start time:             %s\n"
1131 "  End time:               %s\n"
1132 "  Elapsed time:           %s\n"
1133 "  Priority:               %d\n"
1134 "  SD Files Written:       %s\n"
1135 "  SD Bytes Written:       %s (%sB)\n"
1136 "  Rate:                   %.1f KB/s\n"
1137 "  Volume name(s):         %s\n"
1138 "  Volume Session Id:      %d\n"
1139 "  Volume Session Time:    %d\n"
1140 "  Last Volume Bytes:      %s (%sB)\n"
1141 "  SD Errors:              %d\n"
1142 "  SD termination status:  %s\n"
1143 "  Termination:            %s\n\n"),
1144    VERSION,
1145    LSMDATE,
1146         edt, 
1147         edit_uint64(jcr->previous_jr.JobId, ec6),
1148         mig_jcr ? edit_uint64(mig_jcr->jr.JobId, ec7) : "0",
1149         edit_uint64(jcr->jr.JobId, ec8),
1150         jcr->jr.Job,
1151         level_to_str(jcr->JobLevel), jcr->since,
1152         jcr->client->name(),
1153         jcr->fileset->name(), jcr->FSCreateTime,
1154         jcr->rpool->name(), jcr->rpool_source,
1155         jcr->rstore?jcr->rstore->name():"*None*", 
1156         NPRT(jcr->rstore_source), 
1157         jcr->pool->name(), jcr->pool_source,
1158         jcr->wstore?jcr->wstore->name():"*None*", 
1159         NPRT(jcr->wstore_source),
1160         sdt,
1161         edt,
1162         edit_utime(RunTime, elapsed, sizeof(elapsed)),
1163         jcr->JobPriority,
1164         edit_uint64_with_commas(jcr->SDJobFiles, ec1),
1165         edit_uint64_with_commas(jcr->SDJobBytes, ec2),
1166         edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
1167         (float)kbps,
1168         mig_jcr ? mig_jcr->VolumeName : "",
1169         jcr->VolSessionId,
1170         jcr->VolSessionTime,
1171         edit_uint64_with_commas(mr.VolBytes, ec4),
1172         edit_uint64_with_suffix(mr.VolBytes, ec5),
1173         jcr->SDErrors,
1174         sd_term_msg,
1175         term_code);
1176
1177    Dmsg1(100, "migrate_cleanup() mig_jcr=0x%x\n", jcr->mig_jcr);
1178    if (jcr->mig_jcr) {
1179       free_jcr(jcr->mig_jcr);
1180       jcr->mig_jcr = NULL;
1181    }
1182    Dmsg0(100, "Leave migrate_cleanup()\n");
1183 }
1184
1185 /* 
1186  * Return next DBId from comma separated list   
1187  *
1188  * Returns:
1189  *   1 if next DBId returned
1190  *   0 if no more DBIds are in list
1191  *  -1 there is an error
1192  */
1193 static int get_next_dbid_from_list(char **p, DBId_t *DBId)
1194 {
1195    char id[30];
1196    char *q = *p;
1197
1198    id[0] = 0;
1199    for (int i=0; i<(int)sizeof(id); i++) {
1200       if (*q == 0) {
1201          break;
1202       } else if (*q == ',') {
1203          q++;
1204          break;
1205       }
1206       id[i] = *q++;
1207       id[i+1] = 0;
1208    }
1209    if (id[0] == 0) {
1210       return 0;
1211    } else if (!is_a_number(id)) {
1212       return -1;                      /* error */
1213    }
1214    *p = q;
1215    *DBId = str_to_int64(id);
1216    return 1;
1217 }
1218
1219 bool set_migration_wstorage(JCR *jcr, POOL *pool)
1220 {
1221    POOL *wpool = pool->NextPool;
1222
1223    if (!wpool) {
1224       Jmsg(jcr, M_FATAL, 0, _("No Next Pool specification found in Pool \"%s\".\n"),
1225          pool->hdr.name);
1226       return false;
1227    }
1228
1229    if (!wpool->storage || wpool->storage->size() == 0) {
1230       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Next Pool \"%s\".\n"),
1231          wpool->name());
1232       return false;
1233    }
1234
1235    /* If pool storage specified, use it instead of job storage for backup */
1236    copy_wstorage(jcr, wpool->storage, _("Storage from Pool's NextPool resource"));
1237    return true;
1238 }