]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/migrate.c
kes Fix the Win32 build.
[bacula/bacula] / bacula / src / dird / migrate.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2004-2009 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 (!get_or_create_fileset_record(jcr)) {
133       Dmsg1(dbglevel, "JobId=%d no FileSet\n", (int)jcr->JobId);
134       Jmsg(jcr, M_FATAL, 0, _("Could not get or create the FileSet record.\n"));
135       return false;
136    }
137
138    /* If we find a job or jobs to migrate it is previous_jr.JobId */
139    count = get_job_to_migrate(jcr);
140    if (count < 0) {
141       return false;
142    }
143    if (count == 0) {
144       return true;
145    }
146
147    Dmsg1(dbglevel, "Back from get_job_to_migrate JobId=%d\n", (int)jcr->JobId);
148
149    if (jcr->previous_jr.JobId == 0) {
150       Dmsg1(dbglevel, "JobId=%d no previous JobId\n", (int)jcr->JobId);
151       Jmsg(jcr, M_INFO, 0, _("No previous Job found to %s.\n"), jcr->get_ActionName(0));
152       return true;                    /* no work */
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 migration 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/JobErrors */
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
410    return true;
411 }
412
413 struct idpkt {
414    POOLMEM *list;
415    uint32_t count;
416 };
417
418 /* Add an item to the list if it is unique */
419 static void add_unique_id(idpkt *ids, char *item) 
420 {
421    const int maxlen = 30;
422    char id[maxlen+1];
423    char *q = ids->list;
424
425    /* Walk through current list to see if each item is the same as item */
426    for ( ; *q; ) {
427        id[0] = 0;
428        for (int i=0; i<maxlen; i++) {
429           if (*q == 0) {
430              break;
431           } else if (*q == ',') {
432              q++;
433              break;
434           }
435           id[i] = *q++;
436           id[i+1] = 0;
437        }
438        if (strcmp(item, id) == 0) {
439           return;
440        }
441    }
442    /* Did not find item, so add it to list */
443    if (ids->count == 0) {
444       ids->list[0] = 0;
445    } else {
446       pm_strcat(ids->list, ",");
447    }
448    pm_strcat(ids->list, item);
449    ids->count++;
450 // Dmsg3(0, "add_uniq count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
451    return;
452 }
453
454 /*
455  * Callback handler make list of DB Ids
456  */
457 static int unique_dbid_handler(void *ctx, int num_fields, char **row)
458 {
459    idpkt *ids = (idpkt *)ctx;
460
461    add_unique_id(ids, row[0]);
462    Dmsg3(dbglevel, "dbid_hdlr count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
463    return 0;
464 }
465
466
467 struct uitem {
468    dlink link;   
469    char *item;
470 };
471
472 static int item_compare(void *item1, void *item2)
473 {
474    uitem *i1 = (uitem *)item1;
475    uitem *i2 = (uitem *)item2;
476    return strcmp(i1->item, i2->item);
477 }
478
479 static int unique_name_handler(void *ctx, int num_fields, char **row)
480 {
481    dlist *list = (dlist *)ctx;
482
483    uitem *new_item = (uitem *)malloc(sizeof(uitem));
484    uitem *item;
485    
486    memset(new_item, 0, sizeof(uitem));
487    new_item->item = bstrdup(row[0]);
488    Dmsg1(dbglevel, "Unique_name_hdlr Item=%s\n", row[0]);
489    item = (uitem *)list->binary_insert((void *)new_item, item_compare);
490    if (item != new_item) {            /* already in list */
491       free(new_item->item);
492       free((char *)new_item);
493       return 0;
494    }
495    return 0;
496 }
497
498 /* Get Job names in Pool */
499 const char *sql_job =
500    "SELECT DISTINCT Job.Name from Job,Pool"
501    " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
502
503 /* Get JobIds from regex'ed Job names */
504 const char *sql_jobids_from_job =
505    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
506    " WHERE Job.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
507    " ORDER by Job.StartTime";
508
509 /* Get Client names in Pool */
510 const char *sql_client =
511    "SELECT DISTINCT Client.Name from Client,Pool,Job"
512    " WHERE Pool.Name='%s' AND Job.ClientId=Client.ClientId AND"
513    " Job.PoolId=Pool.PoolId";
514
515 /* Get JobIds from regex'ed Client names */
516 const char *sql_jobids_from_client =
517    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool,Client"
518    " WHERE Client.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
519    " AND Job.ClientId=Client.ClientId AND Job.Type='B'"
520    " AND Job.JobStatus = 'T'"
521    " ORDER by Job.StartTime";
522
523 /* Get Volume names in Pool */
524 const char *sql_vol = 
525    "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
526    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
527    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
528
529 /* Get JobIds from regex'ed Volume names */
530 const char *sql_jobids_from_vol =
531    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Media,JobMedia,Job"
532    " WHERE Media.VolumeName='%s' AND Media.MediaId=JobMedia.MediaId"
533    " AND JobMedia.JobId=Job.JobId AND Job.Type='B'"
534    " AND Job.JobStatus = 'T' AND Media.Enabled=1"
535    " ORDER by Job.StartTime";
536
537 const char *sql_smallest_vol = 
538    "SELECT Media.MediaId FROM Media,Pool,JobMedia WHERE"
539    " Media.MediaId in (SELECT DISTINCT MediaId from JobMedia) AND"
540    " Media.VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
541    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
542    " ORDER BY VolBytes ASC LIMIT 1";
543
544 const char *sql_oldest_vol = 
545    "SELECT Media.MediaId FROM Media,Pool,JobMedia WHERE"
546    " Media.MediaId in (SELECT DISTINCT MediaId from JobMedia) AND"
547    " Media.VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
548    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
549    " ORDER BY LastWritten ASC LIMIT 1";
550
551 /* Get JobIds when we have selected MediaId */
552 const char *sql_jobids_from_mediaid =
553    "SELECT DISTINCT Job.JobId,Job.StartTime FROM JobMedia,Job"
554    " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId IN (%s)"
555    " AND Job.Type='B' AND Job.JobStatus = 'T'"
556    " ORDER by Job.StartTime";
557
558 /* Get the number of bytes in the pool */
559 const char *sql_pool_bytes =
560    "SELECT SUM(JobBytes) FROM Job WHERE JobId IN"
561    " (SELECT DISTINCT Job.JobId from Pool,Job,Media,JobMedia WHERE"
562    " Pool.Name='%s' AND Media.PoolId=Pool.PoolId AND"
563    " VolStatus in ('Full','Used','Error','Append') AND Media.Enabled=1 AND"
564    " Job.Type='B' AND Job.JobStatus = 'T' AND"
565    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId)";
566
567 /* Get the number of bytes in the Jobs */
568 const char *sql_job_bytes =
569    "SELECT SUM(JobBytes) FROM Job WHERE JobId IN (%s)";
570
571 /* Get Media Ids in Pool */
572 const char *sql_mediaids =
573    "SELECT MediaId FROM Media,Pool WHERE"
574    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
575    " Media.PoolId=Pool.PoolId AND Pool.Name='%s' ORDER BY LastWritten ASC";
576
577 /* Get JobIds in Pool longer than specified time */
578 const char *sql_pool_time = 
579    "SELECT DISTINCT Job.JobId FROM Pool,Job,Media,JobMedia WHERE"
580    " Pool.Name='%s' AND Media.PoolId=Pool.PoolId AND"
581    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
582    " Job.Type='B' AND Job.JobStatus = 'T' AND"
583    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId"
584    " AND Job.RealEndTime<='%s'";
585
586 /* Get JobIds from successfully completed backup jobs which have not been copied before */
587 const char *sql_jobids_of_pool_uncopied_jobs =
588    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
589    " WHERE Pool.Name = '%s' AND Pool.PoolId = Job.PoolId"
590    " AND Job.Type = 'B' AND Job.JobStatus = 'T'"
591    " AND Job.jobBytes > 0"
592    " AND Job.JobId NOT IN"
593    " (SELECT PriorJobId FROM Job WHERE"
594    " Type IN ('B','C') AND Job.JobStatus = 'T'"
595    " AND PriorJobId != 0)"
596    " ORDER by Job.StartTime";
597
598 /*
599 * const char *sql_ujobid =
600 *   "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
601 *   " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
602 *   " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
603 */
604
605 /*
606  *
607  * This is the central piece of code that finds a job or jobs 
608  *   actually JobIds to migrate.  It first looks to see if one
609  *   has been "manually" specified in jcr->MigrateJobId, and if
610  *   so, it returns that JobId to be run.  Otherwise, it
611  *   examines the Selection Type to see what kind of migration
612  *   we are doing (Volume, Job, Client, ...) and applies any
613  *   Selection Pattern if appropriate to obtain a list of JobIds.
614  *   Finally, it will loop over all the JobIds found, except the last
615  *   one starting a new job with MigrationJobId set to that JobId, and
616  *   finally, it returns the last JobId to the caller.
617  *
618  * Returns: -1  on error
619  *           0  if no jobs to migrate
620  *           1  if OK and jcr->previous_jr filled in
621  */
622 static int get_job_to_migrate(JCR *jcr)
623 {
624    char ed1[30], ed2[30];
625    POOL_MEM query(PM_MESSAGE);
626    JobId_t JobId;
627    DBId_t DBId = 0;
628    int stat;
629    char *p;
630    idpkt ids, mid, jids;
631    db_int64_ctx ctx;
632    int64_t pool_bytes;
633    time_t ttime;
634    struct tm tm;
635    char dt[MAX_TIME_LENGTH];
636    int count = 0;
637
638    ids.list = get_pool_memory(PM_MESSAGE);
639    ids.list[0] = 0;
640    ids.count = 0;
641    mid.list = get_pool_memory(PM_MESSAGE);
642    mid.list[0] = 0;
643    mid.count = 0;
644    jids.list = get_pool_memory(PM_MESSAGE);
645    jids.list[0] = 0;
646    jids.count = 0;
647
648
649    /*
650     * If MigrateJobId is set, then we migrate only that Job,
651     *  otherwise, we go through the full selection of jobs to
652     *  migrate.
653     */
654    if (jcr->MigrateJobId != 0) {
655       Dmsg1(dbglevel, "At Job start previous jobid=%u\n", jcr->MigrateJobId);
656       edit_uint64(jcr->MigrateJobId, ids.list);
657       ids.count = 1;
658    } else {
659       switch (jcr->job->selection_type) {
660       case MT_JOB:
661          if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
662             goto bail_out;
663          } 
664          break;
665       case MT_CLIENT:
666          if (!regex_find_jobids(jcr, &ids, sql_client, sql_jobids_from_client, "Client")) {
667             goto bail_out;
668          } 
669          break;
670       case MT_VOLUME:
671          if (!regex_find_jobids(jcr, &ids, sql_vol, sql_jobids_from_vol, "Volume")) {
672             goto bail_out;
673          } 
674          break;
675       case MT_SQLQUERY:
676          if (!jcr->job->selection_pattern) {
677             Jmsg(jcr, M_FATAL, 0, _("No %s SQL selection pattern specified.\n"), jcr->get_OperationName());
678             goto bail_out;
679          }
680          Dmsg1(dbglevel, "SQL=%s\n", jcr->job->selection_pattern);
681          if (!db_sql_query(jcr->db, jcr->job->selection_pattern,
682               unique_dbid_handler, (void *)&ids)) {
683             Jmsg(jcr, M_FATAL, 0,
684                  _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
685             goto bail_out;
686          }
687          break;
688       case MT_SMALLEST_VOL:
689          if (!find_mediaid_then_jobids(jcr, &ids, sql_smallest_vol, "Smallest Volume")) {
690             goto bail_out;
691          }
692          break;
693       case MT_OLDEST_VOL:
694          if (!find_mediaid_then_jobids(jcr, &ids, sql_oldest_vol, "Oldest Volume")) {
695             goto bail_out;
696          }
697          break;
698       case MT_POOL_OCCUPANCY:
699          ctx.count = 0;
700          /* Find count of bytes in pool */
701          Mmsg(query, sql_pool_bytes, jcr->rpool->name());
702          if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
703             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
704             goto bail_out;
705          }
706          if (ctx.count == 0) {
707             Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0));
708             goto ok_out;
709          }
710          pool_bytes = ctx.value;
711          Dmsg2(dbglevel, "highbytes=%lld pool=%lld\n", jcr->rpool->MigrationHighBytes,
712                pool_bytes);
713          if (pool_bytes < (int64_t)jcr->rpool->MigrationHighBytes) {
714             Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0));
715             goto ok_out;
716          }
717          Dmsg0(dbglevel, "We should do Occupation migration.\n");
718
719          ids.count = 0;
720          /* Find a list of MediaIds that could be migrated */
721          Mmsg(query, sql_mediaids, jcr->rpool->name());
722          Dmsg1(dbglevel, "query=%s\n", query.c_str());
723          if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)&ids)) {
724             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
725             goto bail_out;
726          }
727          if (ids.count == 0) {
728             Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0));
729             goto ok_out;
730          }
731          Dmsg2(dbglevel, "Pool Occupancy ids=%d MediaIds=%s\n", ids.count, ids.list);
732
733          if (!find_jobids_from_mediaid_list(jcr, &ids, "Volume")) {
734             goto bail_out;
735          }
736          /* ids == list of jobs  */
737          p = ids.list;
738          for (int i=0; i < (int)ids.count; i++) {
739             stat = get_next_dbid_from_list(&p, &DBId);
740             Dmsg2(dbglevel, "get_next_dbid stat=%d JobId=%u\n", stat, (uint32_t)DBId);
741             if (stat < 0) {
742                Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
743                goto bail_out;
744             } else if (stat == 0) {
745                break;
746             }
747
748             mid.count = 1;
749             Mmsg(mid.list, "%s", edit_int64(DBId, ed1));
750             if (jids.count > 0) {
751                pm_strcat(jids.list, ",");
752             }
753             pm_strcat(jids.list, mid.list);
754             jids.count += mid.count;
755
756             /* Find count of bytes from Jobs */
757             Mmsg(query, sql_job_bytes, mid.list);
758             Dmsg1(dbglevel, "Jobbytes query: %s\n", query.c_str());
759             if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
760                Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
761                goto bail_out;
762             }
763             pool_bytes -= ctx.value;
764             Dmsg2(dbglevel, "Total %s Job bytes=%s\n", jcr->get_ActionName(0), edit_int64_with_commas(ctx.value, ed1));
765             Dmsg2(dbglevel, "lowbytes=%s poolafter=%s\n", 
766                   edit_int64_with_commas(jcr->rpool->MigrationLowBytes, ed1),
767                   edit_int64_with_commas(pool_bytes, ed2));
768             if (pool_bytes <= (int64_t)jcr->rpool->MigrationLowBytes) {
769                Dmsg0(dbglevel, "We should be done.\n");
770                break;
771             }
772          }
773          /* Transfer jids to ids, where the jobs list is expected */
774          ids.count = jids.count;
775          pm_strcpy(ids.list, jids.list);
776          Dmsg2(dbglevel, "Pool Occupancy ids=%d JobIds=%s\n", ids.count, ids.list);
777          break;
778       case MT_POOL_TIME:
779          ttime = time(NULL) - (time_t)jcr->rpool->MigrationTime;
780          (void)localtime_r(&ttime, &tm);
781          strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
782
783          ids.count = 0;
784          Mmsg(query, sql_pool_time, jcr->rpool->name(), dt);
785          Dmsg1(dbglevel, "query=%s\n", query.c_str());
786          if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)&ids)) {
787             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
788             goto bail_out;
789          }
790          if (ids.count == 0) {
791             Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0));
792             goto ok_out;
793          }
794          Dmsg2(dbglevel, "PoolTime ids=%d JobIds=%s\n", ids.count, ids.list);
795          break;
796       case MT_POOL_UNCOPIED_JOBS:
797          if (!find_jobids_of_pool_uncopied_jobs(jcr, &ids)) {
798             goto bail_out;
799          } 
800          break;
801       default:
802          Jmsg(jcr, M_FATAL, 0, _("Unknown %s Selection Type.\n"), jcr->get_OperationName());
803          goto bail_out;
804       }
805    }
806
807    /*
808     * Loop over all jobids except the last one, sending
809     *  them to start_migration_job(), which will start a job
810     *  for each of them.  For the last JobId, we handle it below.
811     */
812    p = ids.list;
813    if (ids.count == 0) {
814       Jmsg(jcr, M_INFO, 0, _("No JobIds found to %s.\n"), jcr->get_ActionName(0));
815       goto ok_out;
816    }
817
818    Jmsg(jcr, M_INFO, 0, _("The following %u JobId%s chosen to be %s: %s\n"),
819       ids.count, (ids.count < 2) ? _(" was") : _("s were"),
820       jcr->get_ActionName(1), ids.list);
821
822    Dmsg2(dbglevel, "Before loop count=%d ids=%s\n", ids.count, ids.list);
823    for (int i=1; i < (int)ids.count; i++) {
824       JobId = 0;
825       stat = get_next_jobid_from_list(&p, &JobId);
826       Dmsg3(dbglevel, "get_jobid_no=%d stat=%d JobId=%u\n", i, stat, JobId);
827       jcr->MigrateJobId = JobId;
828       start_migration_job(jcr);
829       Dmsg0(dbglevel, "Back from start_migration_job\n");
830       if (stat < 0) {
831          Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
832          goto bail_out;
833       } else if (stat == 0) {
834          Jmsg(jcr, M_INFO, 0, _("No JobIds found to %s.\n"), jcr->get_ActionName(0));
835          goto ok_out;
836       }
837    }
838    
839    /* Now get the last JobId and handle it in the current job */
840    JobId = 0;
841    stat = get_next_jobid_from_list(&p, &JobId);
842    Dmsg2(dbglevel, "Last get_next_jobid stat=%d JobId=%u\n", stat, (int)JobId);
843    if (stat < 0) {
844       Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
845       goto bail_out;
846    } else if (stat == 0) {
847       Jmsg(jcr, M_INFO, 0, _("No JobIds found to %s.\n"), jcr->get_ActionName(0));
848       goto ok_out;
849    }
850
851    jcr->previous_jr.JobId = JobId;
852    Dmsg1(dbglevel, "Previous jobid=%d\n", (int)jcr->previous_jr.JobId);
853
854    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
855       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to %s. ERR=%s"),
856            edit_int64(jcr->previous_jr.JobId, ed1),
857            jcr->get_ActionName(0),
858            db_strerror(jcr->db));
859       goto bail_out;
860    }
861    Jmsg(jcr, M_INFO, 0, _("%s using JobId=%s Job=%s\n"),
862       jcr->get_OperationName(),
863       edit_int64(jcr->previous_jr.JobId, ed1), jcr->previous_jr.Job);
864    Dmsg4(dbglevel, "%s JobId=%d  using JobId=%s Job=%s\n",
865       jcr->get_OperationName(),
866       jcr->JobId,
867       edit_int64(jcr->previous_jr.JobId, ed1), jcr->previous_jr.Job);
868    count = 1;
869
870 ok_out:
871    goto out;
872
873 bail_out:
874    count = -1;
875            
876 out:
877    free_pool_memory(ids.list);
878    free_pool_memory(mid.list);
879    free_pool_memory(jids.list);
880    return count;
881 }
882
883 static void start_migration_job(JCR *jcr)
884 {
885    UAContext *ua = new_ua_context(jcr);
886    char ed1[50];
887    ua->batch = true;
888    Mmsg(ua->cmd, "run %s jobid=%s", jcr->job->hdr.name, 
889         edit_uint64(jcr->MigrateJobId, ed1));
890    Dmsg2(dbglevel, "=============== %s cmd=%s\n", jcr->get_OperationName(), ua->cmd);
891    parse_ua_args(ua);                 /* parse command */
892    JobId_t jobid = run_cmd(ua, ua->cmd);
893    if (jobid == 0) {
894       Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
895    } else {
896       Jmsg(jcr, M_INFO, 0, _("%s JobId %d started.\n"), jcr->get_OperationName(), (int)jobid);
897    }
898    free_ua_context(ua);
899 }
900
901 static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
902                  const char *type) 
903 {
904    bool ok = false;
905    POOL_MEM query(PM_MESSAGE);
906
907    ids->count = 0;
908    /* Basic query for MediaId */
909    Mmsg(query, query1, jcr->rpool->name());
910    if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
911       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
912       goto bail_out;
913    }
914    if (ids->count == 0) {
915       Jmsg(jcr, M_INFO, 0, _("No %s found to %s.\n"), type, jcr->get_ActionName(0));
916       ok = true;         /* Not an error */
917       goto bail_out;
918    } else if (ids->count != 1) {
919       Jmsg(jcr, M_FATAL, 0, _("SQL error. Expected 1 MediaId got %d\n"), ids->count);
920       goto bail_out;
921    }
922    Dmsg2(dbglevel, "%s MediaIds=%s\n", type, ids->list);
923
924    ok = find_jobids_from_mediaid_list(jcr, ids, type);
925
926 bail_out:
927    return ok;
928 }
929
930 /* 
931  * This routine returns:
932  *    false       if an error occurred
933  *    true        otherwise
934  *    ids.count   number of jobids found (may be zero)
935  */       
936 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type) 
937 {
938    bool ok = false;
939    POOL_MEM query(PM_MESSAGE);
940
941    Mmsg(query, sql_jobids_from_mediaid, ids->list);
942    ids->count = 0;
943    if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
944       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
945       goto bail_out;
946    }
947    if (ids->count == 0) {
948       Jmsg(jcr, M_INFO, 0, _("No %ss found to %s.\n"), type, jcr->get_ActionName(0));
949    }
950    ok = true;
951
952 bail_out:
953    return ok;
954 }
955
956 /* 
957  * This routine returns:
958  *    false       if an error occurred
959  *    true        otherwise
960  *    ids.count   number of jobids found (may be zero)
961  */       
962 static bool find_jobids_of_pool_uncopied_jobs(JCR *jcr, idpkt *ids) 
963 {
964    bool ok = false;
965    POOL_MEM query(PM_MESSAGE);
966
967    /* Only a copy job is allowed */
968    if (jcr->get_JobType() != JT_COPY) {
969       Jmsg(jcr, M_FATAL, 0,
970            _("Selection Type 'pooluncopiedjobs' only applies to Copy Jobs"));
971       goto bail_out;
972    }
973
974    Dmsg1(dbglevel, "copy selection pattern=%s\n", jcr->rpool->name());
975    Mmsg(query, sql_jobids_of_pool_uncopied_jobs, jcr->rpool->name());
976    Dmsg1(dbglevel, "get uncopied jobs query=%s\n", query.c_str());
977    if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
978       Jmsg(jcr, M_FATAL, 0,
979            _("SQL to get uncopied jobs failed. ERR=%s\n"), db_strerror(jcr->db));
980       goto bail_out;
981    }
982    ok = true;
983
984 bail_out:
985    return ok;
986 }
987
988 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
989                  const char *query2, const char *type) 
990 {
991    dlist *item_chain;
992    uitem *item = NULL;
993    uitem *last_item = NULL;
994    regex_t preg;
995    char prbuf[500];
996    int rc;
997    bool ok = false;
998    POOL_MEM query(PM_MESSAGE);
999
1000    item_chain = New(dlist(item, &item->link));
1001    if (!jcr->job->selection_pattern) {
1002       Jmsg(jcr, M_FATAL, 0, _("No %s %s selection pattern specified.\n"),
1003          jcr->get_OperationName(), type);
1004       goto bail_out;
1005    }
1006    Dmsg1(dbglevel, "regex-sel-pattern=%s\n", jcr->job->selection_pattern);
1007    /* Basic query for names */
1008    Mmsg(query, query1, jcr->rpool->name());
1009    Dmsg1(dbglevel, "get name query1=%s\n", query.c_str());
1010    if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
1011         (void *)item_chain)) {
1012       Jmsg(jcr, M_FATAL, 0,
1013            _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
1014       goto bail_out;
1015    }
1016    Dmsg1(dbglevel, "query1 returned %d names\n", item_chain->size());
1017    if (item_chain->size() == 0) {
1018       Jmsg(jcr, M_INFO, 0, _("Query of Pool \"%s\" returned no Jobs to %s.\n"),
1019            jcr->rpool->name(), jcr->get_ActionName(0));
1020       ok = true;
1021       goto bail_out;               /* skip regex match */
1022    } else {
1023       /* Compile regex expression */
1024       rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
1025       if (rc != 0) {
1026          regerror(rc, &preg, prbuf, sizeof(prbuf));
1027          Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
1028               jcr->job->selection_pattern, prbuf);
1029          goto bail_out;
1030       }
1031       /* Now apply the regex to the names and remove any item not matched */
1032       foreach_dlist(item, item_chain) {
1033          const int nmatch = 30;
1034          regmatch_t pmatch[nmatch];
1035          if (last_item) {
1036             Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
1037             free(last_item->item);
1038             item_chain->remove(last_item);
1039          }
1040          Dmsg1(dbglevel, "get name Item=%s\n", item->item);
1041          rc = regexec(&preg, item->item, nmatch, pmatch,  0);
1042          if (rc == 0) {
1043             last_item = NULL;   /* keep this one */
1044          } else {   
1045             last_item = item;
1046          }
1047       }
1048       if (last_item) {
1049          free(last_item->item);
1050          Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
1051          item_chain->remove(last_item);
1052       }
1053       regfree(&preg);
1054    }
1055    if (item_chain->size() == 0) {
1056       Jmsg(jcr, M_INFO, 0, _("Regex pattern matched no Jobs to %s.\n"), jcr->get_ActionName(0));
1057       ok = true;
1058       goto bail_out;               /* skip regex match */
1059    }
1060
1061    /* 
1062     * At this point, we have a list of items in item_chain
1063     *  that have been matched by the regex, so now we need
1064     *  to look up their jobids.
1065     */
1066    ids->count = 0;
1067    foreach_dlist(item, item_chain) {
1068       Dmsg2(dbglevel, "Got %s: %s\n", type, item->item);
1069       Mmsg(query, query2, item->item, jcr->rpool->name());
1070       Dmsg1(dbglevel, "get id from name query2=%s\n", query.c_str());
1071       if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
1072          Jmsg(jcr, M_FATAL, 0,
1073               _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
1074          goto bail_out;
1075       }
1076    }
1077    if (ids->count == 0) {
1078       Jmsg(jcr, M_INFO, 0, _("No %ss found to %s.\n"), type, jcr->get_ActionName(0));
1079    }
1080    ok = true;
1081
1082 bail_out:
1083    Dmsg2(dbglevel, "Count=%d Jobids=%s\n", ids->count, ids->list);
1084    foreach_dlist(item, item_chain) {
1085       free(item->item);
1086    }
1087    delete item_chain;
1088    return ok;
1089 }
1090
1091 /*
1092  * Release resources allocated during backup.
1093  */
1094 void migration_cleanup(JCR *jcr, int TermCode)
1095 {
1096    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
1097    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
1098    char ec6[50], ec7[50], ec8[50];
1099    char term_code[100], sd_term_msg[100];
1100    const char *term_msg;
1101    int msg_type = M_INFO;
1102    MEDIA_DBR mr;
1103    double kbps;
1104    utime_t RunTime;
1105    JCR *mig_jcr = jcr->mig_jcr;
1106    POOL_MEM query(PM_MESSAGE);
1107
1108    Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
1109    update_job_end(jcr, TermCode);
1110    memset(&mr, 0, sizeof(mr));
1111
1112    /* 
1113     * Check if we actually did something.  
1114     *  mig_jcr is jcr of the newly migrated job.
1115     */
1116    if (mig_jcr) {
1117       char old_jobid[50], new_jobid[50];
1118
1119       edit_uint64(jcr->previous_jr.JobId, old_jobid);
1120       edit_uint64(mig_jcr->jr.JobId, new_jobid);
1121
1122       mig_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
1123       mig_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
1124       mig_jcr->VolSessionId = jcr->VolSessionId;
1125       mig_jcr->VolSessionTime = jcr->VolSessionTime;
1126       mig_jcr->jr.RealEndTime = 0; 
1127       mig_jcr->jr.PriorJobId = jcr->previous_jr.JobId;
1128
1129       update_job_end(mig_jcr, TermCode);
1130      
1131       /* Update final items to set them to the previous job's values */
1132       Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
1133                   "JobTDate=%s WHERE JobId=%s", 
1134          jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
1135          edit_uint64(jcr->previous_jr.JobTDate, ec1),
1136          new_jobid);
1137       db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1138
1139       /*
1140        * If we terminated a migration normally:
1141        *   - mark the previous job as migrated
1142        *   - move any Log records to the new JobId
1143        *   - Purge the File records from the previous job
1144        */
1145       if (jcr->get_JobType() == JT_MIGRATE && jcr->JobStatus == JS_Terminated) {
1146          Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1147               (char)JT_MIGRATED_JOB, old_jobid);
1148          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1149          UAContext *ua = new_ua_context(jcr);
1150          /* Move JobLog to new JobId */
1151          Mmsg(query, "UPDATE Log SET JobId=%s WHERE JobId=%s",
1152            new_jobid, old_jobid);
1153          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1154          /* Purge all old file records, but leave Job record */
1155          purge_files_from_jobs(ua, old_jobid);
1156          free_ua_context(ua);
1157       } 
1158
1159       /*
1160        * If we terminated a copy normally:
1161        *   - copy any Log records to the new JobId
1162        *   - set type="Job Copy" for the new job
1163        */
1164       if (jcr->get_JobType() == JT_COPY && jcr->JobStatus == JS_Terminated) {
1165          /* Copy JobLog to new JobId */
1166          Mmsg(query, "INSERT INTO Log (JobId, Time, LogText ) " 
1167                       "SELECT %s, Time, LogText FROM Log WHERE JobId=%s",
1168               new_jobid, old_jobid);
1169          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1170          Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1171               (char)JT_JOB_COPY, new_jobid);
1172          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1173       } 
1174
1175       if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
1176          Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
1177             db_strerror(jcr->db));
1178          set_jcr_job_status(jcr, JS_ErrorTerminated);
1179       }
1180
1181       update_bootstrap_file(mig_jcr);
1182
1183       if (!db_get_job_volume_names(mig_jcr, mig_jcr->db, mig_jcr->jr.JobId, &mig_jcr->VolumeName)) {
1184          /*
1185           * Note, if the job has failed, most likely it did not write any
1186           *  tape, so suppress this "error" message since in that case
1187           *  it is normal.  Or look at it the other way, only for a
1188           *  normal exit should we complain about this error.
1189           */
1190          if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
1191             Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(mig_jcr->db));
1192          }
1193          mig_jcr->VolumeName[0] = 0;         /* none */
1194       }
1195
1196       if (mig_jcr->VolumeName[0]) {
1197          /* Find last volume name. Multiple vols are separated by | */
1198          char *p = strrchr(mig_jcr->VolumeName, '|');
1199          if (p) {
1200             p++;                         /* skip | */
1201          } else {
1202             p = mig_jcr->VolumeName;     /* no |, take full name */
1203          }
1204          bstrncpy(mr.VolumeName, p, sizeof(mr.VolumeName));
1205          if (!db_get_media_record(jcr, jcr->db, &mr)) {
1206             Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
1207                mr.VolumeName, db_strerror(jcr->db));
1208          }
1209       }
1210
1211       switch (jcr->JobStatus) {
1212       case JS_Terminated:
1213          if (jcr->JobErrors || jcr->SDErrors) {
1214             term_msg = _("%s OK -- with warnings");
1215          } else {
1216             term_msg = _("%s OK");
1217          }
1218          break;
1219       case JS_FatalError:
1220       case JS_ErrorTerminated:
1221          term_msg = _("*** %s Error ***");
1222          msg_type = M_ERROR;          /* Generate error message */
1223          if (jcr->store_bsock) {
1224             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
1225             if (jcr->SD_msg_chan) {
1226                pthread_cancel(jcr->SD_msg_chan);
1227             }
1228          }
1229          break;
1230       case JS_Canceled:
1231          term_msg = _("%s Canceled");
1232          if (jcr->store_bsock) {
1233             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
1234             if (jcr->SD_msg_chan) {
1235                pthread_cancel(jcr->SD_msg_chan);
1236             }
1237          }
1238          break;
1239       default:
1240          term_msg = _("Inappropriate %s term code");
1241          break;
1242       }
1243    } else {
1244       if (jcr->get_JobType() == JT_MIGRATE && jcr->previous_jr.JobId != 0) {
1245          /* Mark previous job as migrated */
1246          Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1247               (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
1248          db_sql_query(jcr->db, query.c_str(), NULL, NULL);
1249       }
1250       term_msg = _("%s -- no files to %s");
1251    }
1252
1253    bsnprintf(term_code, sizeof(term_code), term_msg, jcr->get_OperationName(), jcr->get_ActionName(0));
1254    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
1255    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
1256    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
1257    if (RunTime <= 0) {
1258       kbps = 0;
1259    } else {
1260       kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
1261    }
1262
1263    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
1264
1265    Jmsg(jcr, msg_type, 0, _("%s %s %s (%s): %s\n"
1266 "  Build OS:               %s %s %s\n"
1267 "  Prev Backup JobId:      %s\n"
1268 "  New Backup JobId:       %s\n"
1269 "  Current JobId:          %s\n"
1270 "  Current Job:            %s\n"
1271 "  Backup Level:           %s%s\n"
1272 "  Client:                 %s\n"
1273 "  FileSet:                \"%s\" %s\n"
1274 "  Read Pool:              \"%s\" (From %s)\n"
1275 "  Read Storage:           \"%s\" (From %s)\n"
1276 "  Write Pool:             \"%s\" (From %s)\n"
1277 "  Write Storage:          \"%s\" (From %s)\n"
1278 "  Catalog:                \"%s\" (From %s)\n"
1279 "  Start time:             %s\n"
1280 "  End time:               %s\n"
1281 "  Elapsed time:           %s\n"
1282 "  Priority:               %d\n"
1283 "  SD Files Written:       %s\n"
1284 "  SD Bytes Written:       %s (%sB)\n"
1285 "  Rate:                   %.1f KB/s\n"
1286 "  Volume name(s):         %s\n"
1287 "  Volume Session Id:      %d\n"
1288 "  Volume Session Time:    %d\n"
1289 "  Last Volume Bytes:      %s (%sB)\n"
1290 "  SD Errors:              %d\n"
1291 "  SD termination status:  %s\n"
1292 "  Termination:            %s\n\n"),
1293         BACULA, my_name, VERSION, LSMDATE, edt,
1294         HOST_OS, DISTNAME, DISTVER,
1295         edit_uint64(jcr->previous_jr.JobId, ec6),
1296         mig_jcr ? edit_uint64(mig_jcr->jr.JobId, ec7) : "0",
1297         edit_uint64(jcr->jr.JobId, ec8),
1298         jcr->jr.Job,
1299         level_to_str(jcr->get_JobLevel()), jcr->since,
1300         jcr->client->name(),
1301         jcr->fileset->name(), jcr->FSCreateTime,
1302         jcr->rpool->name(), jcr->rpool_source,
1303         jcr->rstore?jcr->rstore->name():"*None*", 
1304         NPRT(jcr->rstore_source), 
1305         jcr->pool->name(), jcr->pool_source,
1306         jcr->wstore?jcr->wstore->name():"*None*", 
1307         NPRT(jcr->wstore_source),
1308         jcr->catalog->name(), jcr->catalog_source,
1309         sdt,
1310         edt,
1311         edit_utime(RunTime, elapsed, sizeof(elapsed)),
1312         jcr->JobPriority,
1313         edit_uint64_with_commas(jcr->SDJobFiles, ec1),
1314         edit_uint64_with_commas(jcr->SDJobBytes, ec2),
1315         edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
1316         (float)kbps,
1317         mig_jcr ? mig_jcr->VolumeName : "",
1318         jcr->VolSessionId,
1319         jcr->VolSessionTime,
1320         edit_uint64_with_commas(mr.VolBytes, ec4),
1321         edit_uint64_with_suffix(mr.VolBytes, ec5),
1322         jcr->SDErrors,
1323         sd_term_msg,
1324         term_code);
1325
1326    Dmsg1(100, "migrate_cleanup() mig_jcr=0x%x\n", jcr->mig_jcr);
1327    if (jcr->mig_jcr) {
1328       free_jcr(jcr->mig_jcr);
1329       jcr->mig_jcr = NULL;
1330    }
1331    Dmsg0(100, "Leave migrate_cleanup()\n");
1332 }
1333
1334 /* 
1335  * Return next DBId from comma separated list   
1336  *
1337  * Returns:
1338  *   1 if next DBId returned
1339  *   0 if no more DBIds are in list
1340  *  -1 there is an error
1341  */
1342 static int get_next_dbid_from_list(char **p, DBId_t *DBId)
1343 {
1344    const int maxlen = 30;
1345    char id[maxlen+1];
1346    char *q = *p;
1347
1348    id[0] = 0;
1349    for (int i=0; i<maxlen; i++) {
1350       if (*q == 0) {
1351          break;
1352       } else if (*q == ',') {
1353          q++;
1354          break;
1355       }
1356       id[i] = *q++;
1357       id[i+1] = 0;
1358    }
1359    if (id[0] == 0) {
1360       return 0;
1361    } else if (!is_a_number(id)) {
1362       return -1;                      /* error */
1363    }
1364    *p = q;
1365    *DBId = str_to_int64(id);
1366    return 1;
1367 }
1368
1369 bool set_migration_wstorage(JCR *jcr, POOL *pool)
1370 {
1371    POOL *wpool = pool->NextPool;
1372
1373    if (!wpool) {
1374       Jmsg(jcr, M_FATAL, 0, _("No Next Pool specification found in Pool \"%s\".\n"),
1375          pool->hdr.name);
1376       return false;
1377    }
1378
1379    if (!wpool->storage || wpool->storage->size() == 0) {
1380       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Next Pool \"%s\".\n"),
1381          wpool->name());
1382       return false;
1383    }
1384
1385    /* If pool storage specified, use it instead of job storage for backup */
1386    copy_wstorage(jcr, wpool->storage, _("Storage from Pool's NextPool resource"));
1387    return true;
1388 }