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