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