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