1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20 * Bacula Director -- mac.c -- responsible for doing
21 * migration and copy jobs.
22 *
23 * Also handles Copy jobs (March MMVIII)
24 *
25 * Written by Kern Sibbald, September MMIV
26 *
27 * Basic tasks done here:
28 * Open DB and create records for this job.
29 * Open Message Channel with Storage daemon to tell him a job will be starting.
30 * Open connection with Storage daemon and pass him commands
31 * to do the backup.
32 * When the Storage daemon finishes the job, update the DB.
33 */
34
35 #include "bacula.h"
36 #include "dird.h"
37 #include "ua.h"
38
39 static const int dbglevel = 10;
40 static char storaddr[] = "storage address=%s port=%d ssl=%d Job=%s Authentication=%s\n";
41 static char OKstore[] = "2000 OK storage\n";
42
43 /* Imported subroutines */
44 extern int getJob_to_migrate(JCR *jcr);
45 extern bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
46 const char *query2, const char *type);
47 extern bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
48 const char *type);
49 extern bool find_jobids_of_pool_uncopied_jobs(JCR *jcr, idpkt *ids);
50
51 static bool set_mac_next_pool(JCR *jcr, POOL **pool);
52
53 /*
54 * Called here before the job is run to do the job
55 * specific setup. Note, one of the important things to
56 * complete in this init code is to make the definitive
57 * choice of input and output storage devices. This is
58 * because immediately after the init, the job is queued
59 * in the jobq.c code, and it checks that all the resources
60 * (storage resources in particular) are available, so these
61 * must all be properly defined.
62 *
63 * previous_jr refers to the job DB record of the Job that is
64 * going to be migrated.
65 * prev_job refers to the job resource of the Job that is
66 * going to be migrated.
67 * jcr is the jcr for the current "migration" job. It is a
68 * control job that is put in the DB as a migration job, which
69 * means that this job migrated a previous job to a new job.
70 * No Volume or File data is associated with this control
71 * job.
72 * wjcr refers to the migrate/copy job that is writing and is run by
73 * the current jcr. It is a backup job that writes the
74 * data written for the previous_jr into the new pool. This
75 * job (wjcr) becomes the new backup job that replaces
76 * the original backup job. Note, this jcr is not really run. It
77 * is simply attached to the current jcr. It will show up in
78 * the Director's status output, but not in the SD or FD, both of
79 * which deal only with the current migration job (i.e. jcr).
80 */
do_mac_init(JCR * jcr)81 bool do_mac_init(JCR *jcr)
82 {
83 POOL *pool = NULL;
84 JOB *job, *prev_job;
85 JCR *wjcr; /* jcr of writing job */
86 int count;
87
88
89 apply_pool_overrides(jcr);
90
91 if (!allow_duplicate_job(jcr)) {
92 return false;
93 }
94
95 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
96 if (jcr->jr.PoolId == 0) {
97 Dmsg1(dbglevel, "JobId=%d no PoolId\n", (int)jcr->JobId);
98 Jmsg(jcr, M_FATAL, 0, _("Could not get or create a Pool record.\n"));
99 return false;
100 }
101 /*
102 * Note, at this point, pool is the pool for this job. We
103 * transfer it to rpool (read pool), and a bit later,
104 * pool will be changed to point to the write pool,
105 * which comes from pool->NextPool.
106 */
107 jcr->rpool = jcr->pool; /* save read pool */
108 pm_strcpy(jcr->rpool_source, jcr->pool_source);
109 Dmsg2(dbglevel, "Read pool=%s (From %s)\n", jcr->rpool->name(), jcr->rpool_source);
110
111 if (!get_or_create_fileset_record(jcr)) {
112 Dmsg1(dbglevel, "JobId=%d no FileSet\n", (int)jcr->JobId);
113 Jmsg(jcr, M_FATAL, 0, _("Could not get or create the FileSet record.\n"));
114 return false;
115 }
116
117 /* If we find a job or jobs to migrate it is previous_jr.JobId */
118 count = getJob_to_migrate(jcr);
119 if (count < 0) {
120 return false;
121 }
122 if (count == 0) {
123 set_mac_next_pool(jcr, &pool);
124 return true; /* no work */
125 }
126
127 Dmsg1(dbglevel, "Back from getJob_to_migrate JobId=%d\n", (int)jcr->JobId);
128
129 if (jcr->previous_jr.JobId == 0) {
130 Dmsg1(dbglevel, "JobId=%d no previous JobId\n", (int)jcr->JobId);
131 Jmsg(jcr, M_INFO, 0, _("No previous Job found to %s.\n"), jcr->get_ActionName(0));
132 set_mac_next_pool(jcr, &pool);
133 return true; /* no work */
134 }
135
136 if (create_restore_bootstrap_file(jcr) < 0) {
137 Jmsg(jcr, M_FATAL, 0, _("Create bootstrap file failed.\n"));
138 return false;
139 }
140
141 if (jcr->previous_jr.JobId == 0 || jcr->ExpectedFiles == 0) {
142 jcr->setJobStatus(JS_Terminated);
143 Dmsg1(dbglevel, "JobId=%d expected files == 0\n", (int)jcr->JobId);
144 if (jcr->previous_jr.JobId == 0) {
145 Jmsg(jcr, M_INFO, 0, _("No previous Job found to %s.\n"), jcr->get_ActionName(0));
146 } else {
147 Jmsg(jcr, M_INFO, 0, _("Previous Job has no data to %s.\n"), jcr->get_ActionName(0));
148 }
149 set_mac_next_pool(jcr, &pool);
150 return true; /* no work */
151 }
152
153
154 Dmsg5(dbglevel, "JobId=%d: Current: Name=%s JobId=%d Type=%c Level=%c\n",
155 (int)jcr->JobId,
156 jcr->jr.Name, (int)jcr->jr.JobId,
157 jcr->jr.JobType, jcr->jr.JobLevel);
158
159 LockRes();
160 job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
161 prev_job = (JOB *)GetResWithName(R_JOB, jcr->previous_jr.Name);
162 UnlockRes();
163 if (!job) {
164 Jmsg(jcr, M_FATAL, 0, _("Job resource not found for \"%s\".\n"), jcr->jr.Name);
165 return false;
166 }
167 if (!prev_job) {
168 Jmsg(jcr, M_FATAL, 0, _("Previous Job resource not found for \"%s\".\n"),
169 jcr->previous_jr.Name);
170 return false;
171 }
172
173
174 /* Create a write jcr */
175 wjcr = jcr->wjcr = new_jcr(sizeof(JCR), dird_free_jcr);
176 memcpy(&wjcr->previous_jr, &jcr->previous_jr, sizeof(wjcr->previous_jr));
177
178 /*
179 * Turn the wjcr into a "real" job that takes on the aspects of
180 * the previous backup job "prev_job".
181 */
182 set_jcr_defaults(wjcr, prev_job);
183 /* fix MA 987 cannot copy/migrate jobs with a Level=VF in the job resource
184 * If the prev_job level definition is VirtualFull,
185 * change it to Incremental, otherwise the writing SD would do a VF
186 */
187 if (wjcr->getJobLevel() == L_VIRTUAL_FULL) {
188 wjcr->setJobLevel(L_INCREMENTAL);
189 }
190
191 /* Don't check for duplicates on this jobs. We do it before setup_job(),
192 * because we check allow_duplicate_job() here.
193 */
194 wjcr->IgnoreDuplicateJobChecking = true;
195
196 if (!setup_job(wjcr)) {
197 Jmsg(jcr, M_FATAL, 0, _("setup job failed.\n"));
198 return false;
199 }
200
201 /* Now reset the job record from the previous job */
202 memcpy(&wjcr->jr, &jcr->previous_jr, sizeof(wjcr->jr));
203 /* Update the jr to reflect the new values of PoolId and JobId. */
204 wjcr->jr.PoolId = jcr->jr.PoolId;
205 wjcr->jr.JobId = wjcr->JobId;
206 wjcr->sd_client = true;
207 //wjcr->setJobType(jcr->getJobType());
208 wjcr->setJobLevel(jcr->getJobLevel());
209 wjcr->spool_data = job->spool_data; /* turn on spooling if requested in job */
210 wjcr->spool_size = jcr->spool_size;
211 jcr->spool_size = 0;
212
213 /* Don't let WatchDog checks Max*Time value on this Job */
214 wjcr->no_maxtime = true;
215 Dmsg4(dbglevel, "wjcr: Name=%s JobId=%d Type=%c Level=%c\n",
216 wjcr->jr.Name, (int)wjcr->jr.JobId,
217 wjcr->jr.JobType, wjcr->jr.JobLevel);
218
219 if (set_mac_next_pool(jcr, &pool)) {
220 /* If pool storage specified, use it for restore */
221 copy_rstorage(wjcr, pool->storage, _("Pool resource"));
222 copy_rstorage(jcr, pool->storage, _("Pool resource"));
223
224 wjcr->pool = jcr->pool;
225 wjcr->next_pool = jcr->next_pool;
226 wjcr->jr.PoolId = jcr->jr.PoolId;
227 }
228
229 return true;
230 }
231
232 /*
233 * set_mac_next_pool() called by do_mac_init()
234 * at differents stages.
235 * The idea here is to make a common subroutine for the
236 * NextPool's search code and to permit do_mac_init()
237 * to return with NextPool set in jcr struct.
238 */
set_mac_next_pool(JCR * jcr,POOL ** retpool)239 static bool set_mac_next_pool(JCR *jcr, POOL **retpool)
240 {
241 POOL_DBR pr;
242 POOL *pool;
243 char ed1[100];
244
245 /*
246 * Get the PoolId used with the original job. Then
247 * find the pool name from the database record.
248 */
249 bmemset(&pr, 0, sizeof(pr));
250 pr.PoolId = jcr->jr.PoolId;
251 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
252 Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
253 edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
254 return false;
255 }
256 /* Get the pool resource corresponding to the original job */
257 pool = (POOL *)GetResWithName(R_POOL, pr.Name);
258 *retpool = pool;
259 if (!pool) {
260 Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
261 return false;
262 }
263
264 if (!apply_wstorage_overrides(jcr, pool)) {
265 return false;
266 }
267
268 Dmsg2(dbglevel, "Write pool=%s read rpool=%s\n", jcr->pool->name(), jcr->rpool->name());
269
270 return true;
271 }
272
273 /*
274 * Send storage address and authentication to deblock the other
275 * job.
276 */
send_store_addr_to_sd(JCR * jcr,char * Job,char * sd_auth_key,STORE * store,char * store_address,uint32_t store_port)277 static bool send_store_addr_to_sd(JCR *jcr, char *Job, char *sd_auth_key,
278 STORE *store, char *store_address, uint32_t store_port)
279 {
280 int tls_need = BNET_TLS_NONE;
281
282 /* TLS Requirement */
283 if (store->tls_enable) {
284 if (store->tls_require) {
285 tls_need = BNET_TLS_REQUIRED;
286 } else {
287 tls_need = BNET_TLS_OK;
288 }
289 }
290
291 /*
292 * Send Storage address to the SD client
293 */
294 Dmsg2(200, "=== Job=%s sd auth key=%s\n", Job, sd_auth_key);
295 jcr->store_bsock->fsend(storaddr, store_address, store_port,
296 tls_need, Job, sd_auth_key);
297 if (!response(jcr, jcr->store_bsock, OKstore, "Storage", DISPLAY_ERROR)) {
298 Dmsg4(050, "Response fail for: JobId=%d storeaddr=%s:%d Job=%s\n",
299 jcr->JobId, store_address, store_port, Job);
300 Jmsg3(jcr, M_FATAL, 0, "Response failure: storeddr=%s:%d Job=%s\n",
301 store_address, store_port, Job);
302
303 return false;
304 }
305 return true;
306 }
307
308 /*
309 * Do a Migration and Copy of a previous job
310 *
311 * Returns: false on failure
312 * true on success
313 */
do_mac(JCR * jcr)314 bool do_mac(JCR *jcr)
315 {
316 char ed1[100];
317 BSOCK *sd, *wsd;
318 JCR *wjcr = jcr->wjcr; /* newly migrated job */
319 bool ok = false;
320 STORE *store;
321 char *store_address;
322 uint32_t store_port;
323
324 /*
325 * If wjcr is NULL, there is nothing to do for this job,
326 * so set a normal status, cleanup and return OK.
327 */
328 if (!wjcr) {
329 jcr->setJobStatus(JS_Terminated);
330 mac_cleanup(jcr, JS_Terminated, JS_Terminated);
331 return true;
332 }
333
334 if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
335 Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to %s. ERR=%s"),
336 edit_int64(jcr->previous_jr.JobId, ed1),
337 jcr->get_ActionName(0),
338 db_strerror(jcr->db));
339 jcr->setJobStatus(JS_Terminated);
340 mac_cleanup(jcr, JS_Terminated, JS_Terminated);
341 return true;
342 }
343 /* Make sure this job was not already migrated */
344 if (jcr->previous_jr.JobType != JT_BACKUP &&
345 jcr->previous_jr.JobType != JT_JOB_COPY) {
346 Jmsg(jcr, M_INFO, 0, _("JobId %s already %s probably by another Job. %s stopped.\n"),
347 edit_int64(jcr->previous_jr.JobId, ed1),
348 jcr->get_ActionName(1),
349 jcr->get_OperationName());
350 jcr->setJobStatus(JS_Terminated);
351 mac_cleanup(jcr, JS_Terminated, JS_Terminated);
352 return true;
353 }
354
355 /* Print Job Start message */
356 Jmsg(jcr, M_INFO, 0, _("Start %s JobId %s, Job=%s\n"),
357 jcr->get_OperationName(), edit_uint64(jcr->JobId, ed1), jcr->Job);
358
359 Dmsg3(200, "Start %s JobId %s, Job=%s\n",
360 jcr->get_OperationName(), edit_uint64(jcr->JobId, ed1), jcr->Job);
361
362
363 /*
364 * Now separate the read and write storages. jcr has no wstor...
365 * they all go into wjcr.
366 */
367 free_rwstorage(wjcr);
368 wjcr->rstore = NULL;
369 wjcr->wstore = jcr->wstore;
370 jcr->wstore = NULL;
371 wjcr->wstorage = jcr->wstorage;
372 jcr->wstorage = NULL;
373
374 /* TODO: See priority with bandwidth parameter */
375 if (jcr->job->max_bandwidth > 0) {
376 jcr->max_bandwidth = jcr->job->max_bandwidth;
377 } else if (jcr->client && jcr->client->max_bandwidth > 0) {
378 jcr->max_bandwidth = jcr->client->max_bandwidth;
379 }
380
381 if (jcr->max_bandwidth > 0) {
382 send_bwlimit(jcr, jcr->Job); /* Old clients don't have this command */
383 }
384
385 /*
386 * Open a message channel connection with the Storage
387 * daemon. This is to let him know that our client
388 * will be contacting him for a backup session.
389 *
390 */
391 jcr->setJobStatus(JS_WaitSD);
392 wjcr->setJobStatus(JS_WaitSD);
393
394 /*
395 * Start conversation with write Storage daemon
396 */
397 Dmsg0(200, "Connect to write (wjcr) storage daemon.\n");
398 if (!connect_to_storage_daemon(wjcr, 10, SDConnectTimeout, 1)) {
399 goto bail_out;
400 }
401 wsd = wjcr->store_bsock;
402
403 /*
404 * Start conversation with read Storage daemon
405 */
406 Dmsg1(200, "Connect to read (jcr) storage daemon. Jid=%d\n", jcr->JobId);
407 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
408 goto bail_out;
409 }
410 sd = jcr->store_bsock;
411 if (jcr->client) {
412 jcr->sd_calls_client = jcr->client->sd_calls_client;
413 }
414
415 Dmsg2(dbglevel, "Read store=%s, write store=%s\n",
416 ((STORE *)jcr->rstorage->first())->name(),
417 ((STORE *)wjcr->wstorage->first())->name());
418
419 /*
420 * Now start a job with the read Storage daemon sending the bsr.
421 * This call returns the sd_auth_key
422 */
423 Dmsg1(200, "Start job with read (jcr) storage daemon. Jid=%d\n", jcr->JobId);
424 if (!start_storage_daemon_job(jcr, jcr->rstorage, NULL, /*send_bsr*/true)) {
425 goto bail_out;
426 }
427 Dmsg0(150, "Read storage daemon connection OK\n");
428
429 if (jcr->sd_calls_client) {
430 wjcr->sd_calls_client = true;
431 wjcr->sd_client = false;
432 } else {
433 wjcr->sd_calls_client = true;
434 wjcr->sd_client = true;
435 }
436
437 /*
438 * Now start a job with the write Storage daemon sending.
439 */
440 Dmsg1(200, "Start Job with write (wjcr) storage daemon. Jid=%d\n", jcr->JobId);
441 if (!start_storage_daemon_job(wjcr, NULL, wjcr->wstorage, /*no_send_bsr*/false)) {
442 goto bail_out;
443 }
444 Dmsg0(150, "Write storage daemon connection OK\n");
445
446
447 /* Declare the job started to start the MaxRunTime check */
448 jcr->setJobStarted();
449
450 /*
451 * We re-update the job start record so that the start
452 * time is set after the run before job. This avoids
453 * that any files created by the run before job will
454 * be saved twice. They will be backed up in the current
455 * job, but not in the next one unless they are changed.
456 * Without this, they will be backed up in this job and
457 * in the next job run because in that case, their date
458 * is after the start of this run.
459 */
460 jcr->start_time = time(NULL);
461 jcr->jr.StartTime = jcr->start_time;
462 jcr->jr.JobTDate = jcr->start_time;
463 jcr->setJobStatus(JS_Running);
464
465 /* Update job start record for this mac control job */
466 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
467 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
468 goto bail_out;
469 }
470
471 /* Declare the job started to start the MaxRunTime check */
472 jcr->setJobStarted();
473
474 wjcr->start_time = time(NULL);
475 wjcr->jr.StartTime = wjcr->start_time;
476 wjcr->jr.JobTDate = wjcr->start_time;
477 wjcr->setJobStatus(JS_Running);
478
479
480 /* Update job start record for the real mac backup job */
481 if (!db_update_job_start_record(wjcr, wjcr->db, &wjcr->jr)) {
482 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(wjcr->db));
483 goto bail_out;
484 }
485
486 Dmsg4(dbglevel, "wjcr: Name=%s JobId=%d Type=%c Level=%c\n",
487 wjcr->jr.Name, (int)wjcr->jr.JobId,
488 wjcr->jr.JobType, wjcr->jr.JobLevel);
489
490
491 if (jcr->sd_calls_client) {
492 /*
493 * Reading SD must call the "client" i.e. the writing SD
494 */
495 if (jcr->SDVersion < 3) {
496 Jmsg(jcr, M_FATAL, 0, _("The Storage daemon does not support SDCallsClient.\n"));
497 goto bail_out;
498 }
499
500 /* Setup the storage address and port */
501 store = wjcr->wstore;
502 if (store->SDDport == 0) {
503 store->SDDport = store->SDport;
504 }
505 store_address = store->address; /* note: store points to wstore */
506
507 Dmsg2(200, "Start write message thread jid=%d Job=%s\n", wjcr->JobId, wjcr->Job);
508 if (!run_storage_and_start_message_thread(wjcr, wsd)) {
509 goto bail_out;
510 }
511
512 store_port = store->SDDport;
513
514 /*
515 * Send writing SD address to the reading SD
516 */
517 /* Send and wait for connection */
518 /* ***FIXME*** this should probably be jcr->rstore, store_address, ...
519 * to get TLS right */
520 if (!send_store_addr_to_sd(jcr, wjcr->Job, wjcr->sd_auth_key,
521 store, store_address, store_port)) {
522 goto bail_out;
523 }
524
525 /* Start read message thread */
526 Dmsg2(200, "Start read message thread jid=%d Job=%s\n", jcr->JobId, jcr->Job);
527 if (!run_storage_and_start_message_thread(jcr, sd)) {
528 goto bail_out;
529 }
530
531 } else {
532 /*
533 * Writing SD must simulate an FD and call the reading SD
534 *
535 * Send Storage daemon address to the writing SD
536 */
537 store = jcr->rstore;
538 if (store->SDDport == 0) {
539 store->SDDport = store->SDport;
540 }
541 store_address = get_storage_address(jcr->client, store);
542 store_port = store->SDDport;
543
544 /* Start read message thread */
545 Dmsg2(200, "Start read message thread jid=%d Job=%s\n", jcr->JobId, jcr->Job);
546 if (!run_storage_and_start_message_thread(jcr, sd)) {
547 goto bail_out;
548 }
549
550 /* Attempt connection for one hour */
551 if (!send_store_addr_to_sd(wjcr, jcr->Job, jcr->sd_auth_key,
552 store, store_address, store_port)) {
553 goto bail_out;
554 }
555 /* Start write message thread */
556 Dmsg2(200, "Start write message thread jid=%d Job=%s\n", wjcr->JobId, wjcr->Job);
557 if (!run_storage_and_start_message_thread(wjcr, wsd)) {
558 goto bail_out;
559 }
560 }
561
562 jcr->setJobStatus(JS_Running);
563 wjcr->setJobStatus(JS_Running);
564
565 /* Pickup Job termination data */
566 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
567 wait_for_storage_daemon_termination(wjcr);
568 wjcr->setJobStatus(wjcr->SDJobStatus);
569 wait_for_storage_daemon_termination(jcr);
570 jcr->setJobStatus(jcr->SDJobStatus);
571
572 flush_file_records(wjcr); /* cached attribute + batch insert */
573
574 ok = jcr->is_JobStatus(JS_Terminated) && wjcr->is_JobStatus(JS_Terminated);
575
576 bail_out:
577 /* Put back jcr write storages for proper cleanup */
578 jcr->wstorage = wjcr->wstorage;
579 jcr->wstore = wjcr->wstore;
580 wjcr->wstore = NULL;
581 wjcr->wstorage = NULL;
582 wjcr->file_bsock = NULL;
583
584 if (ok) {
585 mac_cleanup(jcr, jcr->JobStatus, wjcr->JobStatus);
586 }
587 return ok;
588 }
589
590 /*
591 * Called from mac_sql.c for each migration/copy job to start
592 */
start_mac_job(JCR * jcr)593 void start_mac_job(JCR *jcr)
594 {
595 UAContext *ua = new_ua_context(jcr);
596 char ed1[50];
597 char args[MAX_NAME_LENGTH + 50];
598
599 ua->batch = true;
600 Mmsg(ua->cmd, "run job=\"%s\" jobid=%s ignoreduplicatecheck=yes pool=\"%s\"",
601 jcr->job->name(), edit_uint64(jcr->MigrateJobId, ed1),
602 jcr->pool->name());
603 if (jcr->next_pool) {
604 bsnprintf(args, sizeof(args), " nextpool=\"%s\"", jcr->next_pool->name());
605 pm_strcat(ua->cmd, args);
606 }
607 Dmsg2(dbglevel, "=============== %s cmd=%s\n", jcr->get_OperationName(), ua->cmd);
608 parse_ua_args(ua); /* parse command */
609 JobId_t jobid = run_cmd(ua, ua->cmd);
610 if (jobid == 0) {
611 Jmsg(jcr, M_ERROR, 0, _("Could not start migration/copy job.\n"));
612 } else {
613 Jmsg(jcr, M_INFO, 0, _("%s JobId %d started.\n"), jcr->get_OperationName(), (int)jobid);
614 }
615 free_ua_context(ua);
616 }
617
618 /*
619 * Release resources allocated during backup.
620 */
621 /* ***FIXME*** implement writeTermCode */
mac_cleanup(JCR * jcr,int TermCode,int writeTermCode)622 void mac_cleanup(JCR *jcr, int TermCode, int writeTermCode)
623 {
624 char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
625 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
626 char ec6[50], ec7[50], ec8[50], ec9[30], ec10[30], edl[50];
627 char sd_term_msg[100];
628 POOL_MEM term_code;
629 POOL_MEM term_msg;
630 int msg_type = M_INFO;
631 MEDIA_DBR mr;
632 double kbps;
633 utime_t RunTime;
634 bool goterrors=false;
635 JCR *wjcr = jcr->wjcr;
636 POOL_MEM query(PM_MESSAGE);
637 POOL_MEM vol_info;
638
639 remove_dummy_jobmedia_records(jcr);
640
641 Dmsg2(100, "Enter mac_cleanup %d %c\n", TermCode, TermCode);
642 update_job_end(jcr, TermCode);
643
644 /*
645 * Check if we actually did something.
646 * wjcr is jcr of the newly migrated job.
647 */
648 if (wjcr) {
649 char old_jobid[50], new_jobid[50];
650
651 edit_uint64(jcr->previous_jr.JobId, old_jobid);
652 edit_uint64(wjcr->jr.JobId, new_jobid);
653
654 wjcr->JobFiles = jcr->JobFiles = wjcr->SDJobFiles;
655 wjcr->JobBytes = jcr->JobBytes = wjcr->SDJobBytes;
656 wjcr->jr.RealEndTime = 0;
657 wjcr->jr.PriorJobId = jcr->previous_jr.JobId;
658 if (jcr->previous_jr.PriorJob[0]) {
659 bstrncpy(wjcr->jr.PriorJob, jcr->previous_jr.PriorJob, sizeof(wjcr->jr.PriorJob));
660 } else {
661 bstrncpy(wjcr->jr.PriorJob, jcr->previous_jr.Job, sizeof(wjcr->jr.PriorJob));
662 }
663 wjcr->JobErrors += wjcr->SDErrors;
664 update_job_end(wjcr, TermCode);
665
666 /* Update final items to set them to the previous job's values */
667 Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
668 "JobTDate=%s WHERE JobId=%s",
669 jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime,
670 edit_uint64(jcr->previous_jr.JobTDate, ec1),
671 new_jobid);
672 db_sql_query(wjcr->db, query.c_str(), NULL, NULL);
673
674 goterrors = jcr->SDErrors > 0 || jcr->JobErrors > 0 ||
675 jcr->SDJobStatus == JS_Canceled ||
676 jcr->SDJobStatus == JS_ErrorTerminated ||
677 jcr->SDJobStatus == JS_FatalError ||
678 jcr->JobStatus == JS_FatalError;
679
680 if (goterrors && jcr->getJobType() == JT_MIGRATE && jcr->JobStatus == JS_Terminated) {
681 Jmsg(jcr, M_WARNING, 0, _("Found errors during the migration process. "
682 "The original job %s will be kept in the catalog "
683 "and the Migration job will be marked in Error\n"), old_jobid);
684 }
685
686 /*
687 * If we terminated a migration normally:
688 * - mark the previous job as migrated
689 * - move any Log records to the new JobId
690 * - Purge the File records from the previous job
691 */
692 if (!goterrors && jcr->getJobType() == JT_MIGRATE && jcr->JobStatus == JS_Terminated) {
693 Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
694 (char)JT_MIGRATED_JOB, old_jobid);
695 db_sql_query(wjcr->db, query.c_str(), NULL, NULL);
696 UAContext *ua = new_ua_context(jcr);
697 /* Move JobLog to new JobId */
698 Mmsg(query, "UPDATE Log SET JobId=%s WHERE JobId=%s",
699 new_jobid, old_jobid);
700 db_sql_query(wjcr->db, query.c_str(), NULL, NULL);
701
702 /* Move RestoreObjects */
703 Mmsg(query, "UPDATE RestoreObject SET JobId=%s WHERE JobId=%s",
704 new_jobid, old_jobid);
705 db_sql_query(wjcr->db, query.c_str(), NULL, NULL);
706
707 if (jcr->job->PurgeMigrateJob) {
708 /* Purge old Job record */
709 purge_jobs_from_catalog(ua, old_jobid);
710 } else {
711 /* Purge all old file records, but leave Job record */
712 purge_files_from_jobs(ua, old_jobid);
713 }
714
715 free_ua_context(ua);
716 }
717
718 /*
719 * If we terminated a Copy (rather than a Migration) normally:
720 * - copy any Log records to the new JobId
721 * - set type="Job Copy" for the new job
722 */
723 if (goterrors || (jcr->getJobType() == JT_COPY && jcr->JobStatus == JS_Terminated)) {
724 /* Copy JobLog to new JobId */
725 Mmsg(query, "INSERT INTO Log (JobId, Time, LogText ) "
726 "SELECT %s, Time, LogText FROM Log WHERE JobId=%s",
727 new_jobid, old_jobid);
728 db_sql_query(wjcr->db, query.c_str(), NULL, NULL);
729
730 /* We are in a real copy job */
731 Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
732 (char)JT_JOB_COPY, new_jobid);
733 db_sql_query(wjcr->db, query.c_str(), NULL, NULL);
734
735 /* Copy RestoreObjects */
736 Mmsg(query, "INSERT INTO RestoreObject (ObjectName,PluginName,RestoreObject,"
737 "ObjectLength,ObjectFullLength,ObjectIndex,ObjectType,"
738 "ObjectCompression,FileIndex,JobId) "
739 "SELECT ObjectName,PluginName,RestoreObject,"
740 "ObjectLength,ObjectFullLength,ObjectIndex,ObjectType,"
741 "ObjectCompression,FileIndex,%s FROM RestoreObject WHERE JobId=%s",
742 new_jobid, old_jobid);
743 db_sql_query(wjcr->db, query.c_str(), NULL, NULL);
744 }
745
746 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
747 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
748 db_strerror(jcr->db));
749 jcr->setJobStatus(JS_ErrorTerminated);
750 }
751
752 update_bootstrap_file(wjcr);
753
754 if (!db_get_job_volume_names(wjcr, wjcr->db, wjcr->jr.JobId, &wjcr->VolumeName)) {
755 /*
756 * Note, if the job has failed, most likely it did not write any
757 * tape, so suppress this "error" message since in that case
758 * it is normal. Or look at it the other way, only for a
759 * normal exit should we complain about this error.
760 */
761 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
762 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(wjcr->db));
763 }
764 wjcr->VolumeName[0] = 0; /* none */
765 }
766
767 if (wjcr->VolumeName[0]) {
768 /* Find last volume name. Multiple vols are separated by | */
769 char *p = strrchr(wjcr->VolumeName, '|');
770 if (p) {
771 p++; /* skip | */
772 } else {
773 p = wjcr->VolumeName; /* no |, take full name */
774 }
775 bstrncpy(mr.VolumeName, p, sizeof(mr.VolumeName));
776 if (!db_get_media_record(jcr, jcr->db, &mr)) {
777 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
778 mr.VolumeName, db_strerror(jcr->db));
779 }
780 }
781
782 /* We keep all information in the catalog because most of the work is
783 * done, and users might restore things from what we have
784 */
785 if (goterrors) {
786 jcr->setJobStatus(JS_ErrorTerminated);
787 Mmsg(query, "UPDATE Job SET JobStatus='%c' WHERE JobId=%s",
788 JS_ErrorTerminated, new_jobid);
789 db_sql_query(wjcr->db, query.c_str(), NULL, NULL);
790 }
791 }
792
793 switch (jcr->JobStatus) {
794 case JS_Terminated:
795 if (jcr->JobErrors || jcr->SDErrors) {
796 Mmsg(term_msg, _("%%s OK -- %s"), jcr->StatusErrMsg[0] ? jcr->StatusErrMsg : _("with warnings"));
797 } else {
798 Mmsg(term_msg, _("%%s OK"));
799 }
800 break;
801 case JS_FatalError:
802 case JS_ErrorTerminated:
803 Mmsg(term_msg, _("*** %%s Error ***"));
804 msg_type = M_ERROR; /* Generate error message */
805 terminate_sd_msg_chan_thread(jcr);
806 terminate_sd_msg_chan_thread(wjcr);
807 break;
808 case JS_Canceled:
809 Mmsg(term_msg, _("%%s Canceled"));
810 terminate_sd_msg_chan_thread(jcr);
811 terminate_sd_msg_chan_thread(wjcr);
812 break;
813 default:
814 Mmsg(term_msg, _("Inappropriate %s term code"));
815 break;
816 }
817
818 if (!wjcr) { /* We did nothing */
819 goterrors = jcr->JobErrors > 0 || jcr->JobStatus == JS_FatalError;
820 if (!goterrors) {
821 if (jcr->getJobType() == JT_MIGRATE && jcr->previous_jr.JobId != 0) {
822 /* Mark previous job as migrated */
823 Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
824 (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
825 db_sql_query(jcr->db, query.c_str(), NULL, NULL);
826 }
827 Mmsg(term_msg, _("%%s -- no files to %%s"));
828 }
829 }
830
831 Mmsg(term_code, term_msg.c_str(), jcr->get_OperationName(), jcr->get_ActionName(0));
832 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
833 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
834 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
835 if (RunTime <= 0) {
836 RunTime = 1;
837 }
838 kbps = (double)jcr->SDJobBytes / (1000.0 * (double)RunTime);
839
840 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
841
842 /* Edit string for last volume size */
843 if (mr.VolABytes != 0) {
844 Mmsg(vol_info, _("meta: %s (%sB) aligned: %s (%sB)"),
845 edit_uint64_with_commas(mr.VolBytes, ec4),
846 edit_uint64_with_suffix(mr.VolBytes, ec5),
847 edit_uint64_with_commas(mr.VolABytes, ec9),
848 edit_uint64_with_suffix(mr.VolBytes, ec10));
849 } else {
850 Mmsg(vol_info, _("%s (%sB)"),
851 edit_uint64_with_commas(mr.VolBytes, ec4),
852 edit_uint64_with_suffix(mr.VolBytes, ec5));
853 }
854
855 Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
856 " Build OS: %s %s %s\n"
857 " Prev Backup JobId: %s\n"
858 " Prev Backup Job: %s\n"
859 " New Backup JobId: %s\n"
860 " Current JobId: %s\n"
861 " Current Job: %s\n"
862 " Backup Level: %s%s\n"
863 " Client: %s\n"
864 " FileSet: \"%s\" %s\n"
865 " Read Pool: \"%s\" (From %s)\n"
866 " Read Storage: \"%s\" (From %s)\n"
867 " Write Pool: \"%s\" (From %s)\n"
868 " Write Storage: \"%s\" (From %s)\n"
869 " Catalog: \"%s\" (From %s)\n"
870 " Start time: %s\n"
871 " End time: %s\n"
872 " Elapsed time: %s\n"
873 " Priority: %d\n"
874 " SD Files Written: %s\n"
875 " SD Bytes Written: %s (%sB)\n"
876 " Rate: %.1f KB/s\n"
877 " Volume name(s): %s\n"
878 " Volume Session Id: %d\n"
879 " Volume Session Time: %d\n"
880 " Last Volume Bytes: %s\n"
881 " SD Errors: %d\n"
882 " SD termination status: %s\n"
883 " Termination: %s\n\n"),
884 BACULA, my_name, VERSION, LSMDATE,
885 HOST_OS, DISTNAME, DISTVER,
886 edit_uint64(jcr->previous_jr.JobId, ec6),
887 jcr->previous_jr.Job,
888 wjcr ? edit_uint64(wjcr->jr.JobId, ec7) : "0",
889 edit_uint64(jcr->jr.JobId, ec8),
890 jcr->jr.Job,
891 level_to_str(edl, sizeof(edl), jcr->getJobLevel()), jcr->since,
892 jcr->client->name(),
893 jcr->fileset->name(), jcr->FSCreateTime,
894 jcr->rpool->name(), jcr->rpool_source,
895 jcr->rstore?jcr->rstore->name():"*None*",
896 NPRT(jcr->rstore_source),
897 jcr->pool->name(), jcr->pool_source,
898 jcr->wstore?jcr->wstore->name():"*None*",
899 NPRT(jcr->wstore_source),
900 jcr->catalog->name(), jcr->catalog_source,
901 sdt,
902 edt,
903 edit_utime(RunTime, elapsed, sizeof(elapsed)),
904 jcr->JobPriority,
905 edit_uint64_with_commas(jcr->SDJobFiles, ec1),
906 edit_uint64_with_commas(jcr->SDJobBytes, ec2),
907 edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
908 (float)kbps,
909 wjcr ? wjcr->VolumeName : "",
910 jcr->VolSessionId,
911 jcr->VolSessionTime,
912 vol_info.c_str(),
913 jcr->SDErrors,
914 sd_term_msg,
915 term_code.c_str());
916
917 Dmsg0(100, "Leave migrate_cleanup()\n");
918 }
919
set_mac_wstorage(UAContext * ua,JCR * jcr,POOL * pool,POOL * next_pool,const char * source)920 bool set_mac_wstorage(UAContext *ua, JCR *jcr, POOL *pool, POOL *next_pool,
921 const char *source)
922 {
923 if (!next_pool) {
924 if (ua) {
925 ua->error_msg(_("No Next Pool specification found in Pool \"%s\".\n"),
926 pool->hdr.name);
927 } else {
928 Jmsg(jcr, M_FATAL, 0, _("No Next Pool specification found in Pool \"%s\".\n"),
929 pool->hdr.name);
930 }
931 return false;
932 }
933
934 if (!next_pool->storage || next_pool->storage->size() == 0) {
935 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Next Pool \"%s\".\n"),
936 next_pool->name());
937 return false;
938 }
939
940 /* If pool storage specified, use it instead of job storage for backup */
941 copy_wstorage(jcr, next_pool->storage, source);
942
943 return true;
944 }
945