1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2004-2012 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2016 Planets Communications B.V.
6    Copyright (C) 2013-2019 Bareos GmbH & Co. KG
7 
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    Affero 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 /*
24  * BAREOS Director -- migrate.c -- responsible for doing migration and copy jobs.
25  * Also handles Copy jobs (March 2008)
26  * Kern Sibbald, September 2004
27  * SD-SD Migration by Marco van Wieringen, November 2012
28  */
29 /**
30  * @file
31  * responsible for doing migration and copy jobs.
32  *
33  * Also handles Copy jobs
34  *
35  * Basic tasks done here:
36  *    Open DB and create records for this job.
37  *    Open Message Channel with Storage daemon to tell him a job will be starting.
38  *    Open connection with Storage daemon and pass him commands to do the backup.
39  *    When the Storage daemon finishes the job, update the DB.
40  */
41 
42 #include "include/bareos.h"
43 #include "dird.h"
44 #include "dird/dird_globals.h"
45 #include "dird/backup.h"
46 #include "dird/job.h"
47 #include "dird/migration.h"
48 #include "dird/msgchan.h"
49 #include "dird/sd_cmds.h"
50 #include "dird/storage.h"
51 #include "dird/ua_input.h"
52 #include "dird/ua_server.h"
53 #include "dird/ua_purge.h"
54 #include "dird/ua_run.h"
55 #include "lib/edit.h"
56 
57 #include "cats/sql.h"
58 
59 #ifndef HAVE_REGEX_H
60 #include "lib/bregex.h"
61 #else
62 #include <regex.h>
63 #endif
64 
65 namespace directordaemon {
66 
67 /* Commands sent to other storage daemon */
68 static char replicatecmd[]  =
69    "replicate JobId=%d Job=%s address=%s port=%d ssl=%d Authorization=%s\n";
70 
71 /**
72  * Get Job names in Pool
73  */
74 static const char *sql_job =
75    "SELECT DISTINCT Job.Name from Job,Pool"
76    " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
77 
78 /**
79  * Get JobIds from regex'ed Job names
80  */
81 static const char *sql_jobids_from_job =
82    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
83    " WHERE Job.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
84    " ORDER by Job.StartTime";
85 
86 /**
87  * Get Client names in Pool
88  */
89 static const char *sql_client =
90    "SELECT DISTINCT Client.Name from Client,Pool,Job"
91    " WHERE Pool.Name='%s' AND Job.ClientId=Client.ClientId AND"
92    " Job.PoolId=Pool.PoolId";
93 
94 /**
95  * Get JobIds from regex'ed Client names
96  */
97 static const char *sql_jobids_from_client =
98    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool,Client"
99    " WHERE Client.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
100    " AND Job.ClientId=Client.ClientId AND Job.Type IN ('B','C')"
101    " AND Job.JobStatus IN ('T','W')"
102    " ORDER by Job.StartTime";
103 
104 /**
105  * Get Volume names in Pool
106  */
107 static const char *sql_vol =
108    "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
109    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
110    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
111 
112 /**
113  * Get JobIds from regex'ed Volume names
114  */
115 static const char *sql_jobids_from_vol =
116    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Media,JobMedia,Job"
117    " WHERE Media.VolumeName='%s' AND Media.MediaId=JobMedia.MediaId"
118    " AND JobMedia.JobId=Job.JobId AND Job.Type IN ('B','C')"
119    " AND Job.JobStatus IN ('T','W') AND Media.Enabled=1"
120    " ORDER by Job.StartTime";
121 
122 /**
123  * Get JobIds from the smallest volume
124  */
125 static const char *sql_smallest_vol =
126    "SELECT Media.MediaId FROM Media,Pool,JobMedia WHERE"
127    " Media.MediaId in (SELECT DISTINCT MediaId from JobMedia) AND"
128    " Media.VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
129    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
130    " ORDER BY VolBytes ASC LIMIT 1";
131 
132 /**
133  * Get JobIds from the oldest volume
134  */
135 static const char *sql_oldest_vol =
136    "SELECT Media.MediaId FROM Media,Pool,JobMedia WHERE"
137    " Media.MediaId in (SELECT DISTINCT MediaId from JobMedia) AND"
138    " Media.VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
139    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
140    " ORDER BY LastWritten ASC LIMIT 1";
141 
142 /**
143  * Get JobIds when we have selected MediaId
144  */
145 static const char *sql_jobids_from_mediaid =
146    "SELECT DISTINCT Job.JobId,Job.StartTime FROM JobMedia,Job"
147    " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId IN (%s)"
148    " AND Job.Type IN ('B','C') AND Job.JobStatus IN ('T','W')"
149    " ORDER by Job.StartTime";
150 
151 /**
152  * Get the number of bytes in the pool
153  */
154 static const char *sql_pool_bytes =
155    "SELECT SUM(JobBytes) FROM Job WHERE JobId IN"
156    " (SELECT DISTINCT Job.JobId from Pool,Job,Media,JobMedia WHERE"
157    " Pool.Name='%s' AND Media.PoolId=Pool.PoolId AND"
158    " VolStatus in ('Full','Used','Error','Append') AND Media.Enabled=1 AND"
159    " Job.Type IN ('B','C') AND Job.JobStatus IN ('T','W') AND"
160    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId)";
161 
162 /**
163  * Get the number of bytes in the Jobs
164  */
165 static const char *sql_job_bytes =
166    "SELECT SUM(JobBytes) FROM Job WHERE JobId IN (%s)";
167 
168 /**
169  * Get Media Ids in Pool
170  */
171 static const char *sql_mediaids =
172    "SELECT MediaId FROM Media,Pool WHERE"
173    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
174    " Media.PoolId=Pool.PoolId AND Pool.Name='%s' ORDER BY LastWritten ASC";
175 
176 /**
177  * Get JobIds in Pool longer than specified time
178  */
179 static const char *sql_pool_time =
180    "SELECT DISTINCT Job.JobId FROM Pool,Job,Media,JobMedia WHERE"
181    " Pool.Name='%s' AND Media.PoolId=Pool.PoolId AND"
182    " VolStatus IN ('Full','Used','Error') AND Media.Enabled=1 AND"
183    " Job.Type IN ('B','C') AND Job.JobStatus IN ('T','W') AND"
184    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId"
185    " AND Job.RealEndTime<='%s'";
186 
187 /**
188  * Get JobIds from successfully completed backup jobs which have not been copied before
189  */
190 static const char *sql_jobids_of_pool_uncopied_jobs =
191    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
192    " WHERE Pool.Name = '%s' AND Pool.PoolId = Job.PoolId"
193    " AND Job.Type = 'B' AND Job.JobStatus IN ('T','W')"
194    " AND Job.jobBytes > 0"
195    " AND Job.JobId NOT IN"
196    " (SELECT PriorJobId FROM Job WHERE"
197    " Type IN ('B','C') AND Job.JobStatus IN ('T','W')"
198    " AND PriorJobId != 0)"
199    " ORDER by Job.StartTime";
200 
201 /**
202  * Migrate NDMP Job MetaData.
203  */
204 static const char *sql_migrate_ndmp_metadata =
205    "UPDATE File SET JobId=%s "
206    "WHERE JobId=%s "
207    "AND Name NOT IN ("
208    "SELECT Name "
209    "FROM File "
210    "WHERE JobId=%s)";
211 
212 /**
213  * Copy NDMP Job MetaData.
214  */
215 static const char *sql_copy_ndmp_metadata =
216    "INSERT INTO File (FileIndex, JobId, PathId, Name, DeltaSeq, MarkId, LStat, MD5) "
217    "SELECT FileIndex, %s, PathId, Name, DeltaSeq, MarkId, LStat, MD5 "
218    "FROM File "
219    "WHERE JobId=%s "
220    "AND Name NOT IN ("
221    "SELECT Name "
222    "FROM File "
223    "WHERE JobId=%s)";
224 
225 static const int dbglevel = 10;
226 
227 struct idpkt {
228    POOLMEM *list;
229    uint32_t count;
230 };
231 
232 /**
233  * See if two storage definitions point to the same Storage Daemon.
234  *
235  * We compare:
236  *  - address
237  *  - SDport
238  *  - password
239  */
IsSameStorageDaemon(StorageResource * read_storage,StorageResource * write_storage)240 static inline bool IsSameStorageDaemon(StorageResource *read_storage, StorageResource *write_storage)
241 {
242    return read_storage->SDport == write_storage->SDport &&
243           Bstrcasecmp(read_storage->address, write_storage->address) &&
244           Bstrcasecmp(read_storage->password_.value, write_storage->password_.value);
245 }
246 
SetMigrationWstorage(JobControlRecord * jcr,PoolResource * pool,PoolResource * next_pool,const char * where)247 bool SetMigrationWstorage(JobControlRecord *jcr, PoolResource *pool, PoolResource *next_pool, const char *where)
248 {
249    if (!next_pool) {
250       Jmsg(jcr, M_FATAL, 0, _("No Next Pool specification found in Pool \"%s\".\n"),
251            pool->hdr.name);
252       return false;
253    }
254 
255    if (!next_pool->storage || next_pool->storage->size() == 0) {
256       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Next Pool \"%s\".\n"),
257            next_pool->name());
258       return false;
259    }
260 
261    CopyWstorage(jcr, next_pool->storage, where);
262 
263    return true;
264 }
265 
266 /**
267  * SetMigrationNextPool() called by DoMigrationInit()
268  * at differents stages.
269  *
270  * The idea here is to make a common subroutine for the
271  * NextPool's search code and to permit DoMigrationInit()
272  * to return with NextPool set in jcr struct.
273  */
SetMigrationNextPool(JobControlRecord * jcr,PoolResource ** retpool)274 static inline bool SetMigrationNextPool(JobControlRecord *jcr, PoolResource **retpool)
275 {
276    PoolDbRecord pr;
277    char ed1[100];
278    PoolResource *pool;
279    const char *storage_source;
280 
281    /*
282     * Get the PoolId used with the original job. Then
283     * find the pool name from the database record.
284     */
285    memset(&pr, 0, sizeof(pr));
286    pr.PoolId = jcr->jr.PoolId;
287    if (!jcr->db->GetPoolRecord(jcr, &pr)) {
288       Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
289            edit_int64(pr.PoolId, ed1), jcr->db->strerror());
290          return false;
291    }
292 
293    /*
294     * Get the pool resource corresponding to the original job
295     */
296    pool = (PoolResource *)my_config->GetResWithName(R_POOL, pr.Name);
297    *retpool = pool;
298    if (!pool) {
299       Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
300       return false;
301    }
302 
303    /*
304     * See if there is a next pool override.
305     */
306    if (jcr->res.run_next_pool_override) {
307       PmStrcpy(jcr->res.npool_source, _("Run NextPool override"));
308       PmStrcpy(jcr->res.pool_source, _("Run NextPool override"));
309       storage_source = _("Storage from Run NextPool override");
310    } else {
311       /*
312        * See if there is a next pool override in the Job definition.
313        */
314       if (jcr->res.job->next_pool) {
315          jcr->res.next_pool = jcr->res.job->next_pool;
316          PmStrcpy(jcr->res.npool_source, _("Job's NextPool resource"));
317          PmStrcpy(jcr->res.pool_source, _("Job's NextPool resource"));
318          storage_source = _("Storage from Job's NextPool resource");
319       } else {
320          /*
321           * Fall back to the pool's NextPool definition.
322           */
323          jcr->res.next_pool = pool->NextPool;
324          PmStrcpy(jcr->res.npool_source, _("Job Pool's NextPool resource"));
325          PmStrcpy(jcr->res.pool_source, _("Job Pool's NextPool resource"));
326          storage_source = _("Storage from Pool's NextPool resource");
327       }
328    }
329 
330    /*
331     * If the original backup pool has a NextPool, make sure a
332     * record exists in the database. Note, in this case, we
333     * will be migrating from pool to pool->NextPool.
334     */
335    if (jcr->res.next_pool) {
336       jcr->jr.PoolId = GetOrCreatePoolRecord(jcr, jcr->res.next_pool->name());
337       if (jcr->jr.PoolId == 0) {
338          return false;
339       }
340    }
341 
342    if (!SetMigrationWstorage(jcr, pool, jcr->res.next_pool, storage_source)) {
343       return false;
344    }
345 
346    jcr->res.pool = jcr->res.next_pool;
347    Dmsg2(dbglevel, "Write pool=%s read rpool=%s\n", jcr->res.pool->name(), jcr->res.rpool->name());
348 
349    return true;
350 }
351 
352 /**
353  * Sanity check that we are not using the same storage for reading and writing.
354  */
SameStorage(JobControlRecord * jcr)355 static inline bool SameStorage(JobControlRecord *jcr)
356 {
357    StorageResource *read_store, *write_store;
358 
359    read_store = (StorageResource *)jcr->res.read_storage_list->first();
360    write_store = (StorageResource *)jcr->res.write_storage_list->first();
361 
362    if (!read_store->autochanger && !write_store->autochanger &&
363        bstrcmp(read_store->name(), write_store->name())) {
364       return true;
365    }
366 
367    return false;
368 }
369 
StartNewMigrationJob(JobControlRecord * jcr)370 static inline void StartNewMigrationJob(JobControlRecord *jcr)
371 {
372    char ed1[50];
373    JobId_t jobid;
374    UaContext *ua;
375    PoolMem cmd(PM_MESSAGE);
376 
377    ua = new_ua_context(jcr);
378    ua->batch = true;
379    Mmsg(ua->cmd, "run job=\"%s\" jobid=%s ignoreduplicatecheck=yes",
380         jcr->res.job->name(), edit_uint64(jcr->MigrateJobId, ed1));
381 
382    /*
383     * Make sure we have something to compare against.
384     */
385    if (jcr->res.pool) {
386       /*
387        * See if there was actually a pool override.
388        */
389       if (jcr->res.pool != jcr->res.job->pool) {
390          Mmsg(cmd, " pool=\"%s\"", jcr->res.pool->name());
391          PmStrcat(ua->cmd, cmd.c_str());
392       }
393 
394       /*
395        * See if there was actually a next pool override.
396        */
397       if (jcr->res.next_pool && jcr->res.next_pool != jcr->res.pool->NextPool) {
398          Mmsg(cmd, " nextpool=\"%s\"", jcr->res.next_pool->name());
399          PmStrcat(ua->cmd, cmd.c_str());
400       }
401    }
402 
403    Dmsg2(dbglevel, "=============== %s cmd=%s\n", jcr->get_OperationName(), ua->cmd);
404    ParseUaArgs(ua);                 /* parse command */
405 
406    jobid = DoRunCmd(ua, ua->cmd);
407    if (jobid == 0) {
408       Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
409    } else {
410       Jmsg(jcr, M_INFO, 0, _("%s JobId %d started.\n"), jcr->get_OperationName(), (int)jobid);
411    }
412 
413    FreeUaContext(ua);
414 }
415 
416 /**
417  * Return next DBId from comma separated list
418  *
419  * Returns:
420  *   1 if next DBId returned
421  *   0 if no more DBIds are in list
422  *  -1 there is an error
423  */
GetNextDbidFromList(char ** p,DBId_t * DBId)424 static inline int GetNextDbidFromList(char **p, DBId_t *DBId)
425 {
426    int i;
427    const int maxlen = 30;
428    char id[maxlen+1];
429    char *q = *p;
430 
431    id[0] = 0;
432    for (i = 0; i < maxlen; i++) {
433       if (*q == 0) {
434          break;
435       } else if (*q == ',') {
436          q++;
437          break;
438       }
439       id[i] = *q++;
440       id[i+1] = 0;
441    }
442 
443    if (id[0] == 0) {
444       return 0;
445    } else if (!Is_a_number(id)) {
446       return -1;                      /* error */
447    }
448 
449    *p = q;
450    *DBId = str_to_int64(id);
451 
452    return 1;
453 }
454 
455 /**
456  * Add an item to the list if it is unique
457  */
AddUniqueId(idpkt * ids,char * item)458 static void AddUniqueId(idpkt *ids, char *item)
459 {
460    const int maxlen = 30;
461    char id[maxlen+1];
462    char *q = ids->list;
463 
464    /*
465     * Walk through current list to see if each item is the same as item
466     */
467    while (*q) {
468        id[0] = 0;
469        for (int i=0; i<maxlen; i++) {
470           if (*q == 0) {
471              break;
472           } else if (*q == ',') {
473              q++;
474              break;
475           }
476           id[i] = *q++;
477           id[i+1] = 0;
478        }
479        if (bstrcmp(item, id)) {
480           return;
481        }
482    }
483 
484    /*
485     * Did not find item, so add it to list
486     */
487    if (ids->count == 0) {
488       ids->list[0] = 0;
489    } else {
490       PmStrcat(ids->list, ",");
491    }
492 
493    PmStrcat(ids->list, item);
494    ids->count++;
495 
496    return;
497 }
498 
499 /**
500  * Callback handler make list of DB Ids
501  */
UniqueDbidHandler(void * ctx,int num_fields,char ** row)502 static int UniqueDbidHandler(void *ctx, int num_fields, char **row)
503 {
504    idpkt *ids = (idpkt *)ctx;
505 
506    /*
507     * Sanity check
508     */
509    if (!row || !row[0]) {
510       Dmsg0(dbglevel, "dbid_hdlr error empty row\n");
511       return 1;              /* stop calling us */
512    }
513 
514    AddUniqueId(ids, row[0]);
515    Dmsg3(dbglevel, "dbid_hdlr count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
516    return 0;
517 }
518 
519 struct uitem {
520    dlink link;
521    char *item;
522 };
523 
ItemCompare(void * item1,void * item2)524 static int ItemCompare(void *item1, void *item2)
525 {
526    uitem *i1 = (uitem *)item1;
527    uitem *i2 = (uitem *)item2;
528 
529    return strcmp(i1->item, i2->item);
530 }
531 
UniqueNameHandler(void * ctx,int num_fields,char ** row)532 static int UniqueNameHandler(void *ctx, int num_fields, char **row)
533 {
534    dlist *list = (dlist *)ctx;
535 
536    uitem *new_item = (uitem *)malloc(sizeof(uitem));
537    uitem *item;
538 
539    memset(new_item, 0, sizeof(uitem));
540    new_item->item = bstrdup(row[0]);
541    Dmsg1(dbglevel, "Unique_name_hdlr Item=%s\n", row[0]);
542 
543    item = (uitem *)list->binary_insert((void *)new_item, ItemCompare);
544    if (item != new_item) {            /* already in list */
545       free(new_item->item);
546       free((char *)new_item);
547       return 0;
548    }
549 
550    return 0;
551 }
552 
553 /**
554  * This routine returns:
555  *    false       if an error occurred
556  *    true        otherwise
557  *    ids.count   number of jobids found (may be zero)
558  */
FindJobidsFromMediaidList(JobControlRecord * jcr,idpkt * ids,const char * type)559 static bool FindJobidsFromMediaidList(JobControlRecord *jcr, idpkt *ids, const char *type)
560 {
561    bool ok = false;
562    PoolMem query(PM_MESSAGE);
563 
564    Mmsg(query, sql_jobids_from_mediaid, ids->list);
565    ids->count = 0;
566    if (!jcr->db->SqlQuery(query.c_str(), UniqueDbidHandler, (void *)ids)) {
567       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), jcr->db->strerror());
568       goto bail_out;
569    }
570    if (ids->count == 0) {
571       Jmsg(jcr, M_INFO, 0, _("No %ss found to %s.\n"), type, jcr->get_ActionName());
572    }
573    ok = true;
574 
575 bail_out:
576    return ok;
577 }
578 
find_mediaid_then_jobids(JobControlRecord * jcr,idpkt * ids,const char * query1,const char * type)579 static bool find_mediaid_then_jobids(JobControlRecord *jcr, idpkt *ids,
580                                      const char *query1,
581                                      const char *type)
582 {
583    bool ok = false;
584    PoolMem query(PM_MESSAGE);
585 
586    ids->count = 0;
587 
588    /*
589     * Basic query for MediaId
590     */
591    Mmsg(query, query1, jcr->res.rpool->name());
592    if (!jcr->db->SqlQuery(query.c_str(), UniqueDbidHandler, (void *)ids)) {
593       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), jcr->db->strerror());
594       goto bail_out;
595    }
596 
597    if (ids->count == 0) {
598       Jmsg(jcr, M_INFO, 0, _("No %s found to %s.\n"), type, jcr->get_ActionName());
599       ok = true;         /* Not an error */
600       goto bail_out;
601    } else if (ids->count != 1) {
602       Jmsg(jcr, M_FATAL, 0, _("SQL error. Expected 1 MediaId got %d\n"), ids->count);
603       goto bail_out;
604    }
605 
606    Dmsg2(dbglevel, "%s MediaIds=%s\n", type, ids->list);
607 
608    ok = FindJobidsFromMediaidList(jcr, ids, type);
609 
610 bail_out:
611    return ok;
612 }
613 
614 /**
615  * This routine returns:
616  *    false       if an error occurred
617  *    true        otherwise
618  *    ids.count   number of jobids found (may be zero)
619  */
FindJobidsOfPoolUncopiedJobs(JobControlRecord * jcr,idpkt * ids)620 static inline bool FindJobidsOfPoolUncopiedJobs(JobControlRecord *jcr, idpkt *ids)
621 {
622    bool ok = false;
623    PoolMem query(PM_MESSAGE);
624 
625    /*
626     * Only a copy job is allowed
627     */
628    if (!jcr->is_JobType(JT_COPY)) {
629       Jmsg(jcr, M_FATAL, 0,
630            _("Selection Type 'pooluncopiedjobs' only applies to Copy Jobs"));
631       goto bail_out;
632    }
633 
634    Dmsg1(dbglevel, "copy selection pattern=%s\n", jcr->res.rpool->name());
635    Mmsg(query, sql_jobids_of_pool_uncopied_jobs, jcr->res.rpool->name());
636    Dmsg1(dbglevel, "get uncopied jobs query=%s\n", query.c_str());
637    if (!jcr->db->SqlQuery(query.c_str(), UniqueDbidHandler, (void *)ids)) {
638       Jmsg(jcr, M_FATAL, 0,
639            _("SQL to get uncopied jobs failed. ERR=%s\n"), jcr->db->strerror());
640       goto bail_out;
641    }
642    ok = true;
643 
644 bail_out:
645    return ok;
646 }
647 
regex_find_jobids(JobControlRecord * jcr,idpkt * ids,const char * query1,const char * query2,const char * type)648 static bool regex_find_jobids(JobControlRecord *jcr, idpkt *ids,
649                               const char *query1,
650                               const char *query2,
651                               const char *type)
652 {
653    dlist *item_chain;
654    uitem *item = NULL;
655    uitem *last_item = NULL;
656    regex_t preg;
657    char prbuf[500];
658    int rc;
659    bool ok = false;
660    PoolMem query(PM_MESSAGE);
661 
662    item_chain = New(dlist(item, &item->link));
663    if (!jcr->res.job->selection_pattern) {
664       Jmsg(jcr, M_FATAL, 0, _("No %s %s selection pattern specified.\n"),
665          jcr->get_OperationName(), type);
666       goto bail_out;
667    }
668    Dmsg1(dbglevel, "regex-sel-pattern=%s\n", jcr->res.job->selection_pattern);
669 
670    /*
671     * Basic query for names
672     */
673    Mmsg(query, query1, jcr->res.rpool->name());
674    Dmsg1(dbglevel, "get name query1=%s\n", query.c_str());
675    if (!jcr->db->SqlQuery(query.c_str(), UniqueNameHandler,
676         (void *)item_chain)) {
677       Jmsg(jcr, M_FATAL, 0,
678            _("SQL to get %s failed. ERR=%s\n"), type, jcr->db->strerror());
679       goto bail_out;
680    }
681    Dmsg1(dbglevel, "query1 returned %d names\n", item_chain->size());
682    if (item_chain->size() == 0) {
683       Jmsg(jcr, M_INFO, 0, _("Query of Pool \"%s\" returned no Jobs to %s.\n"),
684            jcr->res.rpool->name(), jcr->get_ActionName());
685       ok = true;
686       goto bail_out;               /* skip regex match */
687    } else {
688       /*
689        * Compile regex expression
690        */
691       rc = regcomp(&preg, jcr->res.job->selection_pattern, REG_EXTENDED);
692       if (rc != 0) {
693          regerror(rc, &preg, prbuf, sizeof(prbuf));
694          Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
695               jcr->res.job->selection_pattern, prbuf);
696          goto bail_out;
697       }
698 
699       /*
700        * Now apply the regex to the names and remove any item not matched
701        */
702       foreach_dlist(item, item_chain) {
703          if (last_item) {
704             Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
705             free(last_item->item);
706             item_chain->remove(last_item);
707          }
708          Dmsg1(dbglevel, "get name Item=%s\n", item->item);
709          rc = regexec(&preg, item->item, 0, NULL, 0);
710          if (rc == 0) {
711             last_item = NULL;   /* keep this one */
712          } else {
713             last_item = item;
714          }
715       }
716 
717       if (last_item) {
718          free(last_item->item);
719          Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
720          item_chain->remove(last_item);
721       }
722 
723       regfree(&preg);
724    }
725 
726    if (item_chain->size() == 0) {
727       Jmsg(jcr, M_INFO, 0, _("Regex pattern matched no Jobs to %s.\n"), jcr->get_ActionName());
728       ok = true;
729       goto bail_out;               /* skip regex match */
730    }
731 
732    /*
733     * At this point, we have a list of items in item_chain
734     * that have been matched by the regex, so now we need
735     * to look up their jobids.
736     */
737    ids->count = 0;
738    foreach_dlist(item, item_chain) {
739       Dmsg2(dbglevel, "Got %s: %s\n", type, item->item);
740       Mmsg(query, query2, item->item, jcr->res.rpool->name());
741       Dmsg1(dbglevel, "get id from name query2=%s\n", query.c_str());
742       if (!jcr->db->SqlQuery(query.c_str(), UniqueDbidHandler, (void *)ids)) {
743          Jmsg(jcr, M_FATAL, 0,
744               _("SQL failed. ERR=%s\n"), jcr->db->strerror());
745          goto bail_out;
746       }
747    }
748 
749    if (ids->count == 0) {
750       Jmsg(jcr, M_INFO, 0, _("No %ss found to %s.\n"), type, jcr->get_ActionName());
751    }
752 
753    ok = true;
754 
755 bail_out:
756    Dmsg2(dbglevel, "Count=%d Jobids=%s\n", ids->count, ids->list);
757    foreach_dlist(item, item_chain) {
758       free(item->item);
759    }
760    delete item_chain;
761    return ok;
762 }
763 
764 /**
765  * This is the central piece of code that finds jobs actually JobIds to migrate.
766  * It examines the Selection Type to see what kind of migration we are doing
767  * (Volume, Job, Client, ...) and applies any Selection Pattern if appropriate
768  * to obtain a list of JobIds.
769  *
770  * Finally, it will loop over all the JobIds found, starting a new job with
771  * MigrationJobId set to that JobId.
772  *
773  * Returns: false - On error
774  *          true - If OK
775  */
getJobs_to_migrate(JobControlRecord * jcr)776 static inline bool getJobs_to_migrate(JobControlRecord *jcr)
777 {
778    char *p;
779    int status;
780    int limit = -1;
781    bool apply_limit = false;
782    bool retval = false;
783    JobId_t JobId;
784    db_int64_ctx ctx;
785    idpkt ids, mid, jids;
786    char ed1[30], ed2[30];
787    PoolMem query(PM_MESSAGE);
788 
789    ids.list = GetPoolMemory(PM_MESSAGE);
790    ids.list[0] = 0;
791    ids.count = 0;
792    mid.list = NULL;
793    jids.list = NULL;
794 
795    switch (jcr->res.job->selection_type) {
796    case MT_JOB:
797       if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
798          goto bail_out;
799       }
800       break;
801    case MT_CLIENT:
802       if (!regex_find_jobids(jcr, &ids, sql_client, sql_jobids_from_client, "Client")) {
803          goto bail_out;
804       }
805       break;
806    case MT_VOLUME:
807       if (!regex_find_jobids(jcr, &ids, sql_vol, sql_jobids_from_vol, "Volume")) {
808          goto bail_out;
809       }
810       break;
811    case MT_SQLQUERY:
812       if (!jcr->res.job->selection_pattern) {
813          Jmsg(jcr, M_FATAL, 0, _("No %s SQL selection pattern specified.\n"), jcr->get_OperationName());
814          goto bail_out;
815       }
816       Dmsg1(dbglevel, "SQL=%s\n", jcr->res.job->selection_pattern);
817       if (!jcr->db->SqlQuery(jcr->res.job->selection_pattern,
818            UniqueDbidHandler, (void *)&ids)) {
819          Jmsg(jcr, M_FATAL, 0,
820               _("SQL failed. ERR=%s\n"), jcr->db->strerror());
821          goto bail_out;
822       }
823       break;
824    case MT_SMALLEST_VOL:
825       if (!find_mediaid_then_jobids(jcr, &ids, sql_smallest_vol, "Smallest Volume")) {
826          goto bail_out;
827       }
828       break;
829    case MT_OLDEST_VOL:
830       if (!find_mediaid_then_jobids(jcr, &ids, sql_oldest_vol, "Oldest Volume")) {
831          goto bail_out;
832       }
833       break;
834    case MT_POOL_OCCUPANCY: {
835       int64_t pool_bytes;
836       DBId_t DBId = 0;
837 
838       mid.list = GetPoolMemory(PM_MESSAGE);
839       mid.list[0] = 0;
840       mid.count = 0;
841       jids.list = GetPoolMemory(PM_MESSAGE);
842       jids.list[0] = 0;
843       jids.count = 0;
844       ctx.count = 0;
845 
846       /*
847        * Find count of bytes in pool
848        */
849       Mmsg(query, sql_pool_bytes, jcr->res.rpool->name());
850 
851       if (!jcr->db->SqlQuery(query.c_str(), db_int64_handler, (void *)&ctx)) {
852          Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), jcr->db->strerror());
853          goto bail_out;
854       }
855 
856       if (ctx.count == 0) {
857          Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName());
858          retval = true;
859          goto bail_out;
860       }
861 
862       pool_bytes = ctx.value;
863       Dmsg2(dbglevel, "highbytes=%lld pool=%lld\n", jcr->res.rpool->MigrationHighBytes, pool_bytes);
864 
865       if (pool_bytes < (int64_t)jcr->res.rpool->MigrationHighBytes) {
866          Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName());
867          retval = true;
868          goto bail_out;
869       }
870 
871       Dmsg0(dbglevel, "We should do Occupation migration.\n");
872 
873       ids.count = 0;
874       /*
875        * Find a list of MediaIds that could be migrated
876        */
877       Mmsg(query, sql_mediaids, jcr->res.rpool->name());
878       Dmsg1(dbglevel, "query=%s\n", query.c_str());
879 
880       if (!jcr->db->SqlQuery(query.c_str(), UniqueDbidHandler, (void *)&ids)) {
881          Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), jcr->db->strerror());
882          goto bail_out;
883       }
884 
885       if (ids.count == 0) {
886          Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName());
887          retval = true;
888          goto bail_out;
889       }
890 
891       Dmsg2(dbglevel, "Pool Occupancy ids=%d MediaIds=%s\n", ids.count, ids.list);
892 
893       if (!FindJobidsFromMediaidList(jcr, &ids, "Volume")) {
894          goto bail_out;
895       }
896 
897       /*
898        * ids == list of jobs
899        */
900       p = ids.list;
901       for (int i = 0; i < (int)ids.count; i++) {
902          status = GetNextDbidFromList(&p, &DBId);
903          Dmsg2(dbglevel, "get_next_dbid status=%d JobId=%u\n", status, (uint32_t)DBId);
904          if (status < 0) {
905             Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
906             goto bail_out;
907          } else if (status == 0) {
908             break;
909          }
910 
911          mid.count = 1;
912          Mmsg(mid.list, "%s", edit_int64(DBId, ed1));
913          if (jids.count > 0) {
914             PmStrcat(jids.list, ",");
915          }
916          PmStrcat(jids.list, mid.list);
917          jids.count += mid.count;
918 
919          /*
920           * Find count of bytes from Jobs
921           */
922          Mmsg(query, sql_job_bytes, mid.list);
923          Dmsg1(dbglevel, "Jobbytes query: %s\n", query.c_str());
924          if (!jcr->db->SqlQuery(query.c_str(), db_int64_handler, (void *)&ctx)) {
925             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), jcr->db->strerror());
926             goto bail_out;
927          }
928          pool_bytes -= ctx.value;
929          Dmsg2(dbglevel, "Total %s Job bytes=%s\n", jcr->get_ActionName(),
930                edit_int64_with_commas(ctx.value, ed1));
931          Dmsg2(dbglevel, "lowbytes=%s poolafter=%s\n",
932                edit_int64_with_commas(jcr->res.rpool->MigrationLowBytes, ed1),
933                edit_int64_with_commas(pool_bytes, ed2));
934          if (pool_bytes <= (int64_t)jcr->res.rpool->MigrationLowBytes) {
935             Dmsg0(dbglevel, "We should be done.\n");
936             break;
937          }
938       }
939 
940       /*
941        * Transfer jids to ids, where the jobs list is expected
942        */
943       ids.count = jids.count;
944       PmStrcpy(ids.list, jids.list);
945       Dmsg2(dbglevel, "Pool Occupancy ids=%d JobIds=%s\n", ids.count, ids.list);
946       break;
947    }
948    case MT_POOL_TIME: {
949       time_t ttime;
950       char dt[MAX_TIME_LENGTH];
951 
952       ttime = time(NULL) - (time_t)jcr->res.rpool->MigrationTime;
953       bstrutime(dt, sizeof(dt), ttime);
954 
955       ids.count = 0;
956       Mmsg(query, sql_pool_time, jcr->res.rpool->name(), dt);
957       Dmsg1(dbglevel, "query=%s\n", query.c_str());
958 
959       if (!jcr->db->SqlQuery(query.c_str(), UniqueDbidHandler, (void *)&ids)) {
960          Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), jcr->db->strerror());
961          goto bail_out;
962       }
963 
964       if (ids.count == 0) {
965          Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName());
966          retval = true;
967          goto bail_out;
968       }
969 
970       Dmsg2(dbglevel, "PoolTime ids=%d JobIds=%s\n", ids.count, ids.list);
971       break;
972    }
973    case MT_POOL_UNCOPIED_JOBS:
974       if (!FindJobidsOfPoolUncopiedJobs(jcr, &ids)) {
975          goto bail_out;
976       }
977       break;
978    default:
979       Jmsg(jcr, M_FATAL, 0, _("Unknown %s Selection Type.\n"), jcr->get_OperationName());
980       goto bail_out;
981    }
982 
983    if (ids.count == 0) {
984       Jmsg(jcr, M_INFO, 0, _("No JobIds found to %s.\n"), jcr->get_ActionName());
985       retval = true;
986       goto bail_out;
987    }
988 
989    Jmsg(jcr, M_INFO, 0, _("The following %u JobId%s chosen to be %s: %s\n"),
990         ids.count, (ids.count < 2) ? _(" was") : _("s were"),
991         jcr->get_ActionName(true), ids.list);
992 
993    Dmsg2(dbglevel, "Before loop count=%d ids=%s\n", ids.count, ids.list);
994 
995    /*
996     * Note: to not over load the system, limit the number of new jobs started.
997     */
998    if (jcr->res.job->MaxConcurrentCopies) {
999       limit = jcr->res.job->MaxConcurrentCopies;
1000       apply_limit = true;
1001    }
1002 
1003    p = ids.list;
1004    for (int i = 0; i < (int)ids.count; i++) {
1005       JobId = 0;
1006       status = GetNextJobidFromList(&p, &JobId);
1007       Dmsg3(dbglevel, "getJobid_no=%d status=%d JobId=%u\n", i, status, JobId);
1008       if (status < 0) {
1009          Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
1010          goto bail_out;
1011       } else if (status == 0) {
1012          Jmsg(jcr, M_INFO, 0, _("No JobIds found to %s.\n"), jcr->get_ActionName());
1013          retval = true;
1014          goto bail_out;
1015       }
1016       jcr->MigrateJobId = JobId;
1017 
1018       if (apply_limit) {
1019          /*
1020           * Don't start any more when limit reaches zero
1021           */
1022          limit--;
1023          if (limit < 0) {
1024             continue;
1025          }
1026       }
1027 
1028       StartNewMigrationJob(jcr);
1029       Dmsg0(dbglevel, "Back from StartNewMigrationJob\n");
1030    }
1031 
1032    jcr->HasSelectedJobs = true;
1033    retval = true;
1034 
1035 bail_out:
1036    FreePoolMemory(ids.list);
1037 
1038    if (mid.list) {
1039       FreePoolMemory(mid.list);
1040    }
1041 
1042    if (jids.list) {
1043       FreePoolMemory(jids.list);
1044    }
1045 
1046    return retval;
1047 }
1048 
1049 /**
1050  * Called here before the job is run to do the job
1051  * specific setup. Note, one of the important things to
1052  * complete in this init code is to make the definitive
1053  * choice of input and output storage devices.  This is
1054  * because immediately after the init, the job is queued
1055  * in the jobq.c code, and it checks that all the resources
1056  * (storage resources in particular) are available, so these
1057  * must all be properly defined.
1058  */
DoMigrationInit(JobControlRecord * jcr)1059 bool DoMigrationInit(JobControlRecord *jcr)
1060 {
1061    char ed1[100];
1062    PoolResource *pool = NULL;
1063    JobResource *job, *prev_job;
1064    JobControlRecord *mig_jcr = NULL;            /* newly migrated job */
1065 
1066    ApplyPoolOverrides(jcr);
1067 
1068    if (!AllowDuplicateJob(jcr)) {
1069       return false;
1070    }
1071 
1072    jcr->jr.PoolId = GetOrCreatePoolRecord(jcr, jcr->res.pool->name());
1073    if (jcr->jr.PoolId == 0) {
1074       Dmsg1(dbglevel, "JobId=%d no PoolId\n", (int)jcr->JobId);
1075       Jmsg(jcr, M_FATAL, 0, _("Could not get or create a Pool record.\n"));
1076       return false;
1077    }
1078 
1079    /*
1080     * Note, at this point, pool is the pool for this job.
1081     * We transfer it to rpool (read pool), and a bit later,
1082     * pool will be changed to point to the write pool,
1083     * which comes from pool->NextPool.
1084     */
1085    jcr->res.rpool = jcr->res.pool;    /* save read pool */
1086    PmStrcpy(jcr->res.rpool_source, jcr->res.pool_source);
1087    Dmsg2(dbglevel, "Read pool=%s (From %s)\n", jcr->res.rpool->name(), jcr->res.rpool_source);
1088 
1089    /*
1090     * See if this is a control job e.g. the one that selects the Jobs to Migrate or Copy or
1091     * one of the worker Jobs that do the actual Migration or Copy. If jcr->MigrateJobId is
1092     * set we know that its an actual Migration or Copy Job.
1093     */
1094    if (jcr->MigrateJobId != 0) {
1095       Dmsg1(dbglevel, "At Job start previous jobid=%u\n", jcr->MigrateJobId);
1096 
1097       jcr->previous_jr.JobId = jcr->MigrateJobId;
1098       Dmsg1(dbglevel, "Previous jobid=%d\n", (int)jcr->previous_jr.JobId);
1099 
1100       if (!jcr->db->GetJobRecord(jcr, &jcr->previous_jr)) {
1101          Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to %s. ERR=%s"),
1102               edit_int64(jcr->previous_jr.JobId, ed1),
1103               jcr->get_ActionName(), jcr->db->strerror());
1104          return false;
1105       }
1106 
1107       Jmsg(jcr, M_INFO, 0, _("%s using JobId=%s Job=%s\n"),
1108            jcr->get_OperationName(), edit_int64(jcr->previous_jr.JobId, ed1),
1109            jcr->previous_jr.Job);
1110       Dmsg4(dbglevel, "%s JobId=%d  using JobId=%s Job=%s\n",
1111            jcr->get_OperationName(), jcr->JobId,
1112            edit_int64(jcr->previous_jr.JobId, ed1), jcr->previous_jr.Job);
1113 
1114       if (CreateRestoreBootstrapFile(jcr) < 0) {
1115          Jmsg(jcr, M_FATAL, 0, _("Create bootstrap file failed.\n"));
1116          return false;
1117       }
1118 
1119       if (jcr->previous_jr.JobId == 0 || jcr->ExpectedFiles == 0) {
1120          jcr->setJobStatus(JS_Terminated);
1121          Dmsg1(dbglevel, "JobId=%d expected files == 0\n", (int)jcr->JobId);
1122          if (jcr->previous_jr.JobId == 0) {
1123             Jmsg(jcr, M_INFO, 0, _("No previous Job found to %s.\n"), jcr->get_ActionName());
1124          } else {
1125             Jmsg(jcr, M_INFO, 0, _("Previous Job has no data to %s.\n"), jcr->get_ActionName());
1126          }
1127          SetMigrationNextPool(jcr, &pool);
1128          return true;                    /* no work */
1129       }
1130 
1131       Dmsg5(dbglevel, "JobId=%d: Current: Name=%s JobId=%d Type=%c Level=%c\n",
1132             (int)jcr->JobId, jcr->jr.Name, (int)jcr->jr.JobId, jcr->jr.JobType, jcr->jr.JobLevel);
1133 
1134       job = (JobResource *)my_config->GetResWithName(R_JOB, jcr->jr.Name);
1135       prev_job = (JobResource *)my_config->GetResWithName(R_JOB, jcr->previous_jr.Name);
1136 
1137       if (!job) {
1138          Jmsg(jcr, M_FATAL, 0, _("Job resource not found for \"%s\".\n"), jcr->jr.Name);
1139          return false;
1140       }
1141 
1142       if (!prev_job) {
1143          Jmsg(jcr, M_FATAL, 0, _("Previous Job resource not found for \"%s\".\n"),
1144               jcr->previous_jr.Name);
1145          return false;
1146       }
1147 
1148       /*
1149        * Copy the actual level setting of the previous Job to this Job.
1150        * This overrides the dummy backup level given to the migrate/copy Job and replaces it
1151        * with the actual level the backup run at.
1152        */
1153       jcr->setJobLevel(prev_job->JobLevel);
1154 
1155       /*
1156        * If the current Job has no explicit client set use the client setting of the previous Job.
1157        */
1158       if (!jcr->res.client && prev_job->client) {
1159          jcr->res.client = prev_job->client;
1160          if (!jcr->client_name) {
1161             jcr->client_name = GetPoolMemory(PM_NAME);
1162          }
1163          PmStrcpy(jcr->client_name, jcr->res.client->hdr.name);
1164       }
1165 
1166       /*
1167        * If the current Job has no explicit fileset set use the client setting of the previous Job.
1168        */
1169       if (!jcr->res.fileset) {
1170          jcr->res.fileset = prev_job->fileset;
1171       }
1172 
1173       /*
1174        * See if spooling data is not enabled yet. If so turn on spooling if requested in job
1175        */
1176       if (!jcr->spool_data) {
1177          jcr->spool_data = job->spool_data;
1178       }
1179 
1180       /*
1181        * Create a migration jcr
1182        */
1183       mig_jcr = new_jcr(sizeof(JobControlRecord), DirdFreeJcr);
1184       jcr->mig_jcr = mig_jcr;
1185       memcpy(&mig_jcr->previous_jr, &jcr->previous_jr, sizeof(mig_jcr->previous_jr));
1186 
1187       /*
1188        * Turn the mig_jcr into a "real" job that takes on the aspects of
1189        * the previous backup job "prev_job". We only don't want it to
1190        * ever send any messages to the database or mail messages when
1191        * we are doing a migrate or copy to a remote storage daemon. When
1192        * doing such operations the mig_jcr is used for tracking some of
1193        * the remote state and it might want to send some captured state
1194        * info on tear down of the mig_jcr so we call SetupJob with the
1195        * suppress_output argument set to true (e.g. don't init messages
1196        * and set the jcr suppress_output boolean to true).
1197        */
1198       SetJcrDefaults(mig_jcr, prev_job);
1199 
1200       /*
1201        * Don't let Watchdog checks Max*Time value on this Job
1202        */
1203       mig_jcr->no_maxtime = true;
1204 
1205       /*
1206        * Don't check for duplicates on migration and copy jobs
1207        */
1208       mig_jcr->IgnoreDuplicateJobChecking = true;
1209 
1210       /*
1211        * Copy some overwrites back from the Control Job to the migration and copy job.
1212        */
1213       mig_jcr->spool_data = jcr->spool_data;
1214       mig_jcr->spool_size = jcr->spool_size;
1215 
1216 
1217       if (!SetupJob(mig_jcr, true)) {
1218          Jmsg(jcr, M_FATAL, 0, _("setup job failed.\n"));
1219          return false;
1220       }
1221 
1222       /*
1223        * Keep track that the mig_jcr has a controlling JobControlRecord.
1224        */
1225       mig_jcr->cjcr = jcr;
1226 
1227       /*
1228        * Now reset the job record from the previous job
1229        */
1230       memcpy(&mig_jcr->jr, &jcr->previous_jr, sizeof(mig_jcr->jr));
1231 
1232       /*
1233        * Update the jr to reflect the new values of PoolId and JobId.
1234        */
1235       mig_jcr->jr.PoolId = jcr->jr.PoolId;
1236       mig_jcr->jr.JobId = mig_jcr->JobId;
1237 
1238       if (SetMigrationNextPool(jcr, &pool)) {
1239          /*
1240           * If pool storage specified, use it as source
1241           */
1242          CopyRstorage(mig_jcr, pool->storage, _("Pool resource"));
1243          CopyRstorage(jcr, pool->storage, _("Pool resource"));
1244 
1245          mig_jcr->res.pool = jcr->res.pool;
1246          mig_jcr->res.next_pool = jcr->res.next_pool;
1247          mig_jcr->jr.PoolId = jcr->jr.PoolId;
1248       }
1249 
1250       /*
1251        * Get the storage that was used for the original Job.
1252        * This only happens when the original pool used doesn't have an explicit storage.
1253        */
1254       if (!jcr->res.read_storage_list) {
1255          CopyRstorage(jcr, prev_job->storage, _("previous Job"));
1256       }
1257 
1258       /*
1259        * See if the read and write storage is the same.
1260        * When they are we do the migrate/copy over one SD connection
1261        * otherwise we open a connection to the reading SD and a second
1262        * one to the writing SD.
1263        */
1264       jcr->remote_replicate = !IsSameStorageDaemon(jcr->res.read_storage, jcr->res.write_storage);
1265 
1266       /*
1267        * set the JobLevel to what the original job was
1268        */
1269       mig_jcr->setJobLevel(mig_jcr->previous_jr.JobLevel);
1270 
1271 
1272       Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
1273             mig_jcr->jr.Name, (int)mig_jcr->jr.JobId, mig_jcr->jr.JobType,
1274             mig_jcr->jr.JobLevel);
1275 
1276    }
1277 
1278    return true;
1279 }
1280 
1281 /**
1282  * Do a Migration of a previous job
1283  *
1284  * - previous_jr refers to the job DB record of the Job that is
1285  *   going to be migrated.
1286  * - prev_job refers to the job resource of the Job that is
1287  *   going to be migrated.
1288  * - jcr is the jcr for the current "migration" job. It is a
1289  *   control job that is put in the DB as a migration job, which
1290  *   means that this job migrated a previous job to a new job.
1291  *   No Volume or File data is associated with this control
1292  *   job.
1293  * - mig_jcr refers to the newly migrated job that is run by
1294  *   the current jcr. It is a backup job that moves (migrates) the
1295  *   data written for the previous_jr into the new pool. This
1296  *   job (mig_jcr) becomes the new backup job that replaces
1297  *   the original backup job. Note, when this is a migration
1298  *   on a single storage daemon this jcr is not really run. It
1299  *   is simply attached to the current jcr. It will show up in
1300  *   the Director's status output, but not in the SD or FD, both of
1301  *   which deal only with the current migration job (i.e. jcr).
1302  *   When this is is a migration between two storage daemon this
1303  *   mig_jcr is used to control the second connection to the
1304  *   remote storage daemon.
1305  *
1306  * Returns:  false on failure
1307  *           true  on success
1308  */
DoActualMigration(JobControlRecord * jcr)1309 static inline bool DoActualMigration(JobControlRecord *jcr)
1310 {
1311    char ed1[100];
1312    bool retval = false;
1313    JobControlRecord *mig_jcr = jcr->mig_jcr;
1314 
1315    ASSERT(mig_jcr);
1316 
1317    /*
1318     * Make sure this job was not already migrated
1319     */
1320    if (jcr->previous_jr.JobType != JT_BACKUP &&
1321        jcr->previous_jr.JobType != JT_JOB_COPY) {
1322       Jmsg(jcr, M_INFO, 0, _("JobId %s already %s probably by another Job. %s stopped.\n"),
1323            edit_int64(jcr->previous_jr.JobId, ed1),
1324            jcr->get_ActionName(true),
1325            jcr->get_OperationName());
1326       jcr->setJobStatus(JS_Terminated);
1327       MigrationCleanup(jcr, jcr->JobStatus);
1328       return true;
1329    }
1330 
1331    if (SameStorage(jcr)) {
1332       Jmsg(jcr, M_FATAL, 0, _("JobId %s cannot %s using the same read and write storage.\n"),
1333            edit_int64(jcr->previous_jr.JobId, ed1),
1334            jcr->get_OperationName());
1335       jcr->setJobStatus(JS_Terminated);
1336       MigrationCleanup(jcr, jcr->JobStatus);
1337       return true;
1338    }
1339 
1340    /*
1341     * Print Job Start message
1342     */
1343    Jmsg(jcr, M_INFO, 0, _("Start %s JobId %s, Job=%s\n"),
1344         jcr->get_OperationName(), edit_uint64(jcr->JobId, ed1), jcr->Job);
1345 
1346    /*
1347     * See if the read storage is paired NDMP storage, if so setup
1348     * the Job to use the native storage instead.
1349     */
1350    if (HasPairedStorage(jcr)) {
1351       SetPairedStorage(jcr);
1352    }
1353 
1354    Dmsg2(dbglevel, "Read store=%s, write store=%s\n",
1355          ((StorageResource *)jcr->res.read_storage_list->first())->name(),
1356          ((StorageResource *)jcr->res.write_storage_list->first())->name());
1357 
1358    if (jcr->remote_replicate) {
1359 
1360       alist *write_storage_list;
1361 
1362       /*
1363        * See if we need to apply any bandwidth limiting.
1364        * We search the bandwidth limiting in the following way:
1365        * - Job bandwidth limiting
1366        * - Writing Storage Daemon bandwidth limiting
1367        * - Reading Storage Daemon bandwidth limiting
1368        */
1369       if (jcr->res.job->max_bandwidth > 0) {
1370          jcr->max_bandwidth = jcr->res.job->max_bandwidth;
1371       } else if (jcr->res.write_storage->max_bandwidth > 0) {
1372          jcr->max_bandwidth = jcr->res.write_storage->max_bandwidth;
1373       } else if (jcr->res.read_storage->max_bandwidth > 0) {
1374          jcr->max_bandwidth = jcr->res.read_storage->max_bandwidth;
1375       }
1376 
1377       /*
1378        * Open a message channel connection to the Reading Storage daemon.
1379        */
1380       Dmsg0(110, "Open connection with reading storage daemon\n");
1381 
1382       /*
1383        * Clear the write_storage of the jcr and assign it to the mig_jcr so
1384        * the jcr is connected to the reading storage daemon and the
1385        * mig_jcr to the writing storage daemon.
1386        */
1387       mig_jcr->res.write_storage = jcr->res.write_storage;
1388       jcr->res.write_storage = NULL;
1389 
1390       /*
1391        * Swap the write_storage_list between the jcr and the mig_jcr.
1392        */
1393       write_storage_list = mig_jcr->res.write_storage_list;
1394       mig_jcr->res.write_storage_list = jcr->res.write_storage_list;
1395       jcr->res.write_storage_list = write_storage_list;
1396 
1397       /*
1398        * Start conversation with Reading Storage daemon
1399        */
1400       jcr->setJobStatus(JS_WaitSD);
1401       if (!ConnectToStorageDaemon(jcr, 10, me->SDConnectTimeout, true)) {
1402          goto bail_out;
1403       }
1404 
1405       /*
1406        * Open a message channel connection with the Writing Storage daemon.
1407        */
1408       Dmsg0(110, "Open connection with writing storage daemon\n");
1409 
1410       /*
1411        * Start conversation with Writing Storage daemon
1412        */
1413       mig_jcr->setJobStatus(JS_WaitSD);
1414       if (!ConnectToStorageDaemon(mig_jcr, 10, me->SDConnectTimeout, true)) {
1415          goto bail_out;
1416       }
1417 
1418       /*
1419        * Now start a job with the Reading Storage daemon
1420        */
1421       if (!StartStorageDaemonJob(jcr, jcr->res.read_storage_list, NULL, /* send_bsr */ true)) {
1422          goto bail_out;
1423       }
1424 
1425       Dmsg0(150, "Reading Storage daemon connection OK\n");
1426 
1427       /*
1428        * Now start a job with the Writing Storage daemon
1429        */
1430 
1431       if (!StartStorageDaemonJob(mig_jcr, NULL, mig_jcr->res.write_storage_list, /* send_bsr */ false)) {
1432          goto bail_out;
1433       }
1434 
1435       Dmsg0(150, "Writing Storage daemon connection OK\n");
1436 
1437    } else { /* local replicate */
1438 
1439       /*
1440        * Open a message channel connection with the Storage daemon.
1441        */
1442       Dmsg0(110, "Open connection with storage daemon\n");
1443       jcr->setJobStatus(JS_WaitSD);
1444       mig_jcr->setJobStatus(JS_WaitSD);
1445 
1446       /*
1447        * Start conversation with Storage daemon
1448        */
1449       if (!ConnectToStorageDaemon(jcr, 10, me->SDConnectTimeout, true)) {
1450          FreePairedStorage(jcr);
1451          return false;
1452       }
1453 
1454       /*
1455        * Now start a job with the Storage daemon
1456        */
1457       if (!StartStorageDaemonJob(jcr, jcr->res.read_storage_list, jcr->res.write_storage_list, /* send_bsr */ true)) {
1458          FreePairedStorage(jcr);
1459          return false;
1460       }
1461 
1462       Dmsg0(150, "Storage daemon connection OK\n");
1463    }
1464 
1465    /*
1466     * We re-update the job start record so that the start
1467     * time is set after the run before job.  This avoids
1468     * that any files created by the run before job will
1469     * be saved twice.  They will be backed up in the current
1470     * job, but not in the next one unless they are changed.
1471     * Without this, they will be backed up in this job and
1472     * in the next job run because in that case, their date
1473     * is after the start of this run.
1474     */
1475    jcr->start_time = time(NULL);
1476    jcr->jr.StartTime = jcr->start_time;
1477    jcr->jr.JobTDate = jcr->start_time;
1478    jcr->setJobStatus(JS_Running);
1479 
1480    /*
1481     * Update job start record for this migration control job
1482     */
1483    if (!jcr->db->UpdateJobStartRecord(jcr, &jcr->jr)) {
1484       Jmsg(jcr, M_FATAL, 0, "%s", jcr->db->strerror());
1485       goto bail_out;
1486    }
1487 
1488    /*
1489     * Declare the job started to start the MaxRunTime check
1490     */
1491    jcr->setJobStarted();
1492 
1493    mig_jcr->start_time = time(NULL);
1494    mig_jcr->jr.StartTime = mig_jcr->start_time;
1495    mig_jcr->jr.JobTDate = mig_jcr->start_time;
1496    mig_jcr->setJobStatus(JS_Running);
1497 
1498    /*
1499     * Update job start record for the real migration backup job
1500     */
1501    if (!mig_jcr->db->UpdateJobStartRecord(mig_jcr, &mig_jcr->jr)) {
1502       Jmsg(jcr, M_FATAL, 0, "%s", mig_jcr->db->strerror());
1503       goto bail_out;
1504    }
1505 
1506    Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
1507          mig_jcr->jr.Name, (int)mig_jcr->jr.JobId,
1508          mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
1509 
1510    /*
1511     * If we are connected to two different SDs tell the writing one
1512     * to be ready to receive the data and tell the reading one
1513     * to replicate to the other.
1514     */
1515    if (jcr->remote_replicate) {
1516       StorageResource *write_storage = mig_jcr->res.write_storage;
1517       StorageResource *read_storage = jcr->res.read_storage;
1518       PoolMem command(PM_MESSAGE);
1519       uint32_t tls_need = 0;
1520 
1521       if (jcr->max_bandwidth > 0) {
1522          SendBwlimitToSd(jcr, jcr->Job);
1523       }
1524 
1525       /*
1526        * Start the job prior to starting the message thread below
1527        * to avoid two threads from using the BareosSocket structure at
1528        * the same time.
1529        */
1530       if (!mig_jcr->store_bsock->fsend("listen")) {
1531          goto bail_out;
1532       }
1533 
1534       if (!StartStorageDaemonMessageThread(mig_jcr)) {
1535          goto bail_out;
1536       }
1537 
1538       /*
1539        * Send Storage daemon address to the other Storage daemon
1540        */
1541       if (write_storage->SDDport == 0) {
1542          write_storage->SDDport = write_storage->SDport;
1543       }
1544 
1545       /*
1546        * TLS Requirement
1547        */
1548       tls_need = write_storage->IsTlsConfigured() ? TlsPolicy::kBnetTlsAuto : TlsPolicy::kBnetTlsNone;
1549 
1550       char *connection_target_address = StorageAddressToContact(read_storage, write_storage);
1551 
1552       Mmsg(command, replicatecmd, mig_jcr->JobId, mig_jcr->Job, connection_target_address,
1553            write_storage->SDDport, tls_need, mig_jcr->sd_auth_key);
1554 
1555       if (!jcr->store_bsock->fsend(command.c_str())) {
1556          goto bail_out;
1557       }
1558 
1559       if (jcr->store_bsock->recv() <= 0) {
1560          goto bail_out;
1561       }
1562 
1563       std::string OK_replicate {"3000 OK replicate\n"};
1564       std::string received = jcr->store_bsock->msg;
1565       if (received != OK_replicate) {
1566          goto bail_out;
1567       }
1568   }
1569 
1570    /*
1571     * Start the job prior to starting the message thread below
1572     * to avoid two threads from using the BareosSocket structure at
1573     * the same time.
1574     */
1575    if (!jcr->store_bsock->fsend("run")) {
1576       goto bail_out;
1577    }
1578 
1579    /*
1580     * Now start a Storage daemon message thread
1581     */
1582    if (!StartStorageDaemonMessageThread(jcr)) {
1583       goto bail_out;
1584    }
1585 
1586    jcr->setJobStatus(JS_Running);
1587    mig_jcr->setJobStatus(JS_Running);
1588 
1589    /*
1590     * Pickup Job termination data
1591     * Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors or
1592     * mig_jcr->JobFiles/ReadBytes/JobBytes/JobErrors when replicating to
1593     * a remote storage daemon.
1594     */
1595    if (jcr->remote_replicate) {
1596       WaitForStorageDaemonTermination(jcr);
1597       WaitForStorageDaemonTermination(mig_jcr);
1598       jcr->setJobStatus(jcr->SDJobStatus);
1599       mig_jcr->db_batch->WriteBatchFileRecords(mig_jcr);
1600    } else {
1601       WaitForStorageDaemonTermination(jcr);
1602       jcr->setJobStatus(jcr->SDJobStatus);
1603       jcr->db_batch->WriteBatchFileRecords(jcr);
1604    }
1605 
1606 bail_out:
1607    if (jcr->remote_replicate && mig_jcr) {
1608       alist *write_storage_list;
1609 
1610       /*
1611        * Swap the write_storage_list between the jcr and the mig_jcr.
1612        */
1613       write_storage_list = mig_jcr->res.write_storage_list;
1614       mig_jcr->res.write_storage_list = jcr->res.write_storage_list;
1615       jcr->res.write_storage_list = write_storage_list;
1616 
1617       /*
1618        * Undo the clear of the write_storage in the jcr and assign the mig_jcr write_storage
1619        * back to the jcr. This is an undo of the clearing we did earlier
1620        * as we want the jcr connected to the reading storage daemon and the
1621        * mig_jcr to the writing jcr. By clearing the write_storage of the jcr the
1622        * ConnectToStorageDaemon function will do the right thing e.g. connect
1623        * the jcrs in the way we want them to.
1624        */
1625       jcr->res.write_storage = mig_jcr->res.write_storage;
1626       mig_jcr->res.write_storage = NULL;
1627    }
1628 
1629    FreePairedStorage(jcr);
1630 
1631    if (jcr->is_JobStatus(JS_Terminated)) {
1632       MigrationCleanup(jcr, jcr->JobStatus);
1633       retval = true;
1634    }
1635 
1636    return retval;
1637 }
1638 
1639 /**
1640  * Select the Jobs to Migrate/Copy using the getJobs_to_migrate function and then exit.
1641  */
DoMigrationSelection(JobControlRecord * jcr)1642 static inline bool DoMigrationSelection(JobControlRecord *jcr)
1643 {
1644    bool retval;
1645 
1646    retval = getJobs_to_migrate(jcr);
1647    if (retval) {
1648       jcr->setJobStatus(JS_Terminated);
1649       MigrationCleanup(jcr, jcr->JobStatus);
1650    } else {
1651       jcr->setJobStatus(JS_ErrorTerminated);
1652    }
1653 
1654    return retval;
1655 }
1656 
DoMigration(JobControlRecord * jcr)1657 bool DoMigration(JobControlRecord *jcr)
1658 {
1659    /*
1660     * See if this is a control job e.g. the one that selects the Jobs to Migrate or Copy or
1661     * one of the worker Jobs that do the actual Migration or Copy. If jcr->MigrateJobId is
1662     * unset we know that its the control job.
1663     */
1664    if (jcr->MigrateJobId == 0) {
1665       return DoMigrationSelection(jcr);
1666    } else {
1667       return DoActualMigration(jcr);
1668    }
1669 }
1670 
GenerateMigrateSummary(JobControlRecord * jcr,MediaDbRecord * mr,int msg_type,const char * TermMsg)1671 static inline void GenerateMigrateSummary(JobControlRecord *jcr, MediaDbRecord *mr, int msg_type, const char *TermMsg)
1672 {
1673    double kbps;
1674    utime_t RunTime;
1675    JobControlRecord *mig_jcr = jcr->mig_jcr;
1676    char term_code[100], sd_term_msg[100];
1677    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
1678    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
1679    char ec6[50], ec7[50], ec8[50];
1680 
1681    Bsnprintf(term_code, sizeof(term_code), TermMsg, jcr->get_OperationName(), jcr->get_ActionName());
1682    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
1683    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
1684    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
1685 
1686    JobstatusToAscii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
1687    if (jcr->previous_jr.JobId != 0) {
1688       /*
1689        * Copy/Migrate worker Job.
1690        */
1691       if (RunTime <= 0) {
1692          kbps = 0;
1693       } else {
1694          kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
1695       }
1696 
1697       Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
1698            "  Build OS:               %s %s %s\n"
1699            "  Prev Backup JobId:      %s\n"
1700            "  Prev Backup Job:        %s\n"
1701            "  New Backup JobId:       %s\n"
1702            "  Current JobId:          %s\n"
1703            "  Current Job:            %s\n"
1704            "  Backup Level:           %s\n"
1705            "  Client:                 %s\n"
1706            "  FileSet:                \"%s\"\n"
1707            "  Read Pool:              \"%s\" (From %s)\n"
1708            "  Read Storage:           \"%s\" (From %s)\n"
1709            "  Write Pool:             \"%s\" (From %s)\n"
1710            "  Write Storage:          \"%s\" (From %s)\n"
1711            "  Next Pool:              \"%s\" (From %s)\n"
1712            "  Catalog:                \"%s\" (From %s)\n"
1713            "  Start time:             %s\n"
1714            "  End time:               %s\n"
1715            "  Elapsed time:           %s\n"
1716            "  Priority:               %d\n"
1717            "  SD Files Written:       %s\n"
1718            "  SD Bytes Written:       %s (%sB)\n"
1719            "  Rate:                   %.1f KB/s\n"
1720            "  Volume name(s):         %s\n"
1721            "  Volume Session Id:      %d\n"
1722            "  Volume Session Time:    %d\n"
1723            "  Last Volume Bytes:      %s (%sB)\n"
1724            "  SD Errors:              %d\n"
1725            "  SD termination status:  %s\n"
1726            "  Bareos binary info:     %s\n"
1727            "  Termination:            %s\n\n"),
1728            BAREOS, my_name, VERSION, LSMDATE,
1729            HOST_OS, DISTNAME, DISTVER,
1730            edit_uint64(jcr->previous_jr.JobId, ec6),
1731            jcr->previous_jr.Job,
1732            mig_jcr ? edit_uint64(mig_jcr->jr.JobId, ec7) : _("*None*"),
1733            edit_uint64(jcr->jr.JobId, ec8),
1734            jcr->jr.Job,
1735            level_to_str(jcr->getJobLevel()),
1736            jcr->res.client ? jcr->res.client->name() :  _("*None*"),
1737            jcr->res.fileset ? jcr->res.fileset->name() :  _("*None*"),
1738            jcr->res.rpool->name(), jcr->res.rpool_source,
1739            jcr->res.read_storage ? jcr->res.read_storage->name() : _("*None*"),
1740            NPRT(jcr->res.rstore_source),
1741            jcr->res.pool->name(), jcr->res.pool_source,
1742            jcr->res.write_storage ? jcr->res.write_storage->name() : _("*None*"),
1743            NPRT(jcr->res.wstore_source),
1744            jcr->res.next_pool ? jcr->res.next_pool->name() : _("*None*"),
1745            NPRT(jcr->res.npool_source),
1746            jcr->res.catalog->name(), jcr->res.catalog_source,
1747            sdt,
1748            edt,
1749            edit_utime(RunTime, elapsed, sizeof(elapsed)),
1750            jcr->JobPriority,
1751            edit_uint64_with_commas(jcr->SDJobFiles, ec1),
1752            edit_uint64_with_commas(jcr->SDJobBytes, ec2),
1753            edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
1754            (float)kbps,
1755            mig_jcr ? mig_jcr->VolumeName : _("*None*"),
1756            jcr->VolSessionId,
1757            jcr->VolSessionTime,
1758            edit_uint64_with_commas(mr->VolBytes, ec4),
1759            edit_uint64_with_suffix(mr->VolBytes, ec5),
1760            jcr->SDErrors,
1761            sd_term_msg,
1762            BAREOS_JOBLOG_MESSAGE,
1763            term_code);
1764    } else {
1765       /*
1766        * Copy/Migrate selection only Job.
1767        */
1768       Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
1769            "  Build OS:               %s %s %s\n"
1770            "  Current JobId:          %s\n"
1771            "  Current Job:            %s\n"
1772            "  Catalog:                \"%s\" (From %s)\n"
1773            "  Start time:             %s\n"
1774            "  End time:               %s\n"
1775            "  Elapsed time:           %s\n"
1776            "  Priority:               %d\n"
1777            "  Bareos binary info:     %s\n"
1778            "  Termination:            %s\n\n"),
1779            BAREOS, my_name, VERSION, LSMDATE,
1780            HOST_OS, DISTNAME, DISTVER,
1781            edit_uint64(jcr->jr.JobId, ec8),
1782            jcr->jr.Job,
1783            jcr->res.catalog->name(), jcr->res.catalog_source,
1784            sdt,
1785            edt,
1786            edit_utime(RunTime, elapsed, sizeof(elapsed)),
1787            jcr->JobPriority,
1788            BAREOS_JOBLOG_MESSAGE,
1789            term_code);
1790    }
1791 }
1792 
1793 /**
1794  * Release resources allocated during backup.
1795  */
MigrationCleanup(JobControlRecord * jcr,int TermCode)1796 void MigrationCleanup(JobControlRecord *jcr, int TermCode)
1797 {
1798    char ec1[30];
1799    const char *TermMsg;
1800    int msg_type = M_INFO;
1801    MediaDbRecord mr;
1802    JobControlRecord *mig_jcr = jcr->mig_jcr;
1803    PoolMem query(PM_MESSAGE);
1804 
1805    memset(&mr, 0, sizeof(mr));
1806    Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
1807    UpdateJobEnd(jcr, TermCode);
1808 
1809    /*
1810     * Check if we actually did something.
1811     * mig_jcr is jcr of the newly migrated job.
1812     */
1813    if (mig_jcr) {
1814       char old_jobid[50], new_jobid[50];
1815 
1816       edit_uint64(jcr->previous_jr.JobId, old_jobid);
1817       edit_uint64(mig_jcr->jr.JobId, new_jobid);
1818 
1819       /*
1820        * See if we used a remote SD if so the mig_jcr contains
1821        * the jobfiles and jobbytes and the new volsessionid
1822        * and volsessiontime as the writing SD generates this info.
1823        */
1824       if (jcr->remote_replicate) {
1825          mig_jcr->JobFiles = jcr->JobFiles = mig_jcr->SDJobFiles;
1826          mig_jcr->JobBytes = jcr->JobBytes = mig_jcr->SDJobBytes;
1827       } else {
1828          mig_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
1829          mig_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
1830          mig_jcr->VolSessionId = jcr->VolSessionId;
1831          mig_jcr->VolSessionTime = jcr->VolSessionTime;
1832       }
1833       mig_jcr->jr.RealEndTime = 0;
1834       mig_jcr->jr.PriorJobId = jcr->previous_jr.JobId;
1835 
1836       if (jcr->is_JobStatus(JS_Terminated) &&
1837          (jcr->JobErrors || jcr->SDErrors)) {
1838          TermCode = JS_Warnings;
1839       }
1840 
1841       UpdateJobEnd(mig_jcr, TermCode);
1842 
1843       /*
1844        * Update final items to set them to the previous job's values
1845        */
1846       Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
1847                   "JobTDate=%s WHERE JobId=%s",
1848            jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime,
1849            edit_uint64(jcr->previous_jr.JobTDate, ec1),
1850            new_jobid);
1851       jcr->db->SqlQuery(query.c_str());
1852 
1853       if (jcr->IsTerminatedOk()) {
1854          UaContext *ua;
1855 
1856          switch (jcr->getJobType()) {
1857          case JT_MIGRATE:
1858             /*
1859              * If we terminated a Migration Job successfully we should:
1860              * - Mark the previous job as migrated
1861              * - Move any Log records to the new JobId
1862              * - Move any MetaData of a NDMP backup
1863              * - Purge the File records from the previous job
1864              */
1865             Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1866                  (char)JT_MIGRATED_JOB, old_jobid);
1867             mig_jcr->db->SqlQuery(query.c_str());
1868 
1869             /*
1870              * Move JobLog to new JobId
1871              */
1872             Mmsg(query, "UPDATE Log SET JobId=%s WHERE JobId=%s",
1873                  new_jobid, old_jobid);
1874             mig_jcr->db->SqlQuery(query.c_str());
1875 
1876             /*
1877              * If we just migrated a NDMP job, we need to move the file MetaData
1878              * to the new job. The file MetaData is stored as hardlinks to the
1879              * NDMP archive itself. And as we only clone the actual data in the
1880              * storage daemon we need to add data normally send to the director
1881              * via the FHDB interface here.
1882              */
1883             switch (jcr->res.client->Protocol) {
1884             case APT_NDMPV2:
1885             case APT_NDMPV3:
1886             case APT_NDMPV4:
1887                Mmsg(query, sql_migrate_ndmp_metadata, new_jobid, old_jobid, new_jobid);
1888                mig_jcr->db->SqlQuery(query.c_str());
1889                break;
1890             default:
1891                break;
1892             }
1893 
1894             ua = new_ua_context(jcr);
1895             if (jcr->res.job->PurgeMigrateJob) {
1896                /*
1897                 * Purge old Job record
1898                 */
1899                PurgeJobsFromCatalog(ua, old_jobid);
1900             } else {
1901                /*
1902                 * Purge all old file records, but leave Job record
1903                 */
1904                PurgeFilesFromJobs(ua, old_jobid);
1905             }
1906 
1907             FreeUaContext(ua);
1908             break;
1909          case JT_COPY:
1910             /*
1911              * If we terminated a Copy Job successfully we should:
1912              * - Copy any Log records to the new JobId
1913              * - Copy any MetaData of a NDMP backup
1914              * - Set type="Job Copy" for the new job
1915              */
1916             Mmsg(query, "INSERT INTO Log (JobId, Time, LogText ) "
1917                         "SELECT %s, Time, LogText FROM Log WHERE JobId=%s",
1918                  new_jobid, old_jobid);
1919             mig_jcr->db->SqlQuery(query.c_str());
1920 
1921             /*
1922              * If we just copied a NDMP job, we need to copy the file MetaData
1923              * to the new job. The file MetaData is stored as hardlinks to the
1924              * NDMP archive itself. And as we only clone the actual data in the
1925              * storage daemon we need to add data normally send to the director
1926              * via the FHDB interface here.
1927              */
1928             switch (jcr->res.client->Protocol) {
1929             case APT_NDMPV2:
1930             case APT_NDMPV3:
1931             case APT_NDMPV4:
1932                Mmsg(query, sql_copy_ndmp_metadata, new_jobid, old_jobid, new_jobid);
1933                mig_jcr->db->SqlQuery(query.c_str());
1934                break;
1935             default:
1936                break;
1937             }
1938 
1939             Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1940                  (char)JT_JOB_COPY, new_jobid);
1941             mig_jcr->db->SqlQuery(query.c_str());
1942             break;
1943          default:
1944             break;
1945          }
1946       }
1947 
1948       if (!jcr->db->GetJobRecord(jcr, &jcr->jr)) {
1949          Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"), jcr->db->strerror());
1950          jcr->setJobStatus(JS_ErrorTerminated);
1951       }
1952 
1953       UpdateBootstrapFile(mig_jcr);
1954 
1955       if (!mig_jcr->db->GetJobVolumeNames(mig_jcr, mig_jcr->jr.JobId, mig_jcr->VolumeName)) {
1956          /*
1957           * Note, if the job has failed, most likely it did not write any
1958           * tape, so suppress this "error" message since in that case
1959           * it is normal. Or look at it the other way, only for a
1960           * normal exit should we complain about this error.
1961           */
1962          if (jcr->IsTerminatedOk() && jcr->jr.JobBytes) {
1963             Jmsg(jcr, M_ERROR, 0, "%s", mig_jcr->db->strerror());
1964          }
1965          mig_jcr->VolumeName[0] = 0;         /* none */
1966       }
1967 
1968       if (mig_jcr->VolumeName[0]) {
1969          /*
1970           * Find last volume name. Multiple vols are separated by |
1971           */
1972          char *p = strrchr(mig_jcr->VolumeName, '|');
1973          if (p) {
1974             p++;                         /* skip | */
1975          } else {
1976             p = mig_jcr->VolumeName;     /* no |, take full name */
1977          }
1978          bstrncpy(mr.VolumeName, p, sizeof(mr.VolumeName));
1979          if (!jcr->db->GetMediaRecord(jcr, &mr)) {
1980             Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
1981                  mr.VolumeName, jcr->db->strerror());
1982          }
1983       }
1984 
1985       switch (jcr->JobStatus) {
1986       case JS_Terminated:
1987          TermMsg = _("%s OK");
1988          break;
1989       case JS_Warnings:
1990          TermMsg = _("%s OK -- with warnings");
1991          break;
1992       case JS_FatalError:
1993       case JS_ErrorTerminated:
1994       case JS_Canceled:
1995          /*
1996           * We catch any error here as the close of the SD sessions is mandatory for each
1997           * failure path. The termination message and the message type can be different
1998           * so that is why we do a second switch inside the switch on the JobStatus.
1999           */
2000          switch (jcr->JobStatus) {
2001          case JS_Canceled:
2002             TermMsg = _("%s Canceled");
2003             break;
2004          default:
2005             TermMsg = _("*** %s Error ***");
2006             msg_type = M_ERROR;          /* Generate error message */
2007             break;
2008          }
2009 
2010          /*
2011           * Close connection to Reading SD.
2012           */
2013          if (jcr->store_bsock) {
2014             jcr->store_bsock->signal(BNET_TERMINATE);
2015             if (jcr->SD_msg_chan_started) {
2016                pthread_cancel(jcr->SD_msg_chan);
2017             }
2018          }
2019 
2020          /*
2021           * Close connection to Writing SD (if SD-SD replication)
2022           */
2023          if (mig_jcr->store_bsock) {
2024             mig_jcr->store_bsock->signal(BNET_TERMINATE);
2025             if (mig_jcr->SD_msg_chan_started) {
2026                pthread_cancel(mig_jcr->SD_msg_chan);
2027             }
2028          }
2029          break;
2030       default:
2031          TermMsg = _("Inappropriate %s term code");
2032          break;
2033       }
2034    } else if (jcr->HasSelectedJobs) {
2035       switch (jcr->JobStatus) {
2036       case JS_Terminated:
2037          TermMsg = _("%s OK");
2038          break;
2039       case JS_Warnings:
2040          TermMsg = _("%s OK -- with warnings");
2041          break;
2042       case JS_FatalError:
2043       case JS_ErrorTerminated:
2044          TermMsg = _("*** %s Error ***");
2045          msg_type = M_ERROR;          /* Generate error message */
2046          break;
2047       case JS_Canceled:
2048          TermMsg = _("%s Canceled");
2049          break;
2050       default:
2051          TermMsg = _("Inappropriate %s term code");
2052          break;
2053       }
2054    } else {
2055       TermMsg = _("%s -- no files to %s");
2056    }
2057 
2058    GenerateMigrateSummary(jcr, &mr, msg_type, TermMsg);
2059 
2060    Dmsg0(100, "Leave migrate_cleanup()\n");
2061 }
2062 } /* namespace directordaemon */
2063