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