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