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