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