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