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