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