3 * Bacula Director -- mac.c -- responsible for doing
4 * migration, archive, and copy jobs.
6 * Kern Sibbald, September MMIV
8 * Basic tasks done here:
9 * Open DB and create records for this job.
10 * Open Message Channel with Storage daemon to tell him a job will be starting.
11 * Open connection with File daemon and pass him commands
13 * When the File daemon finishes the job, update the DB.
18 Copyright (C) 2004-2005 Kern Sibbald
20 This program is free software; you can redistribute it and/or
21 modify it under the terms of the GNU General Public License
22 version 2 as amended with additional clauses defined in the
23 file LICENSE in the main source directory.
25 This program is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 the file LICENSE for additional details.
37 * Called here before the job is run to do the job
40 bool do_mac_init(JCR *jcr)
47 if (!get_or_create_fileset_record(jcr)) {
52 * Find JobId of last job that ran.
54 memcpy(&jr, &jcr->jr, sizeof(jr));
55 Name = jcr->job->hdr.name;
56 Dmsg1(100, "find last jobid for: %s\n", NPRT(Name));
57 if (!db_find_last_jobid(jcr, jcr->db, Name, &jr)) {
58 Jmsg(jcr, M_FATAL, 0, _(
59 _("Unable to find JobId of previous Job for this client.\n")));
62 input_jobid = jr.JobId;
63 jcr->JobLevel = jr.JobLevel;
64 Dmsg1(100, "Last jobid=%d\n", input_jobid);
67 * Get the Pool record -- first apply any level defined pools
69 switch (jcr->JobLevel) {
72 jcr->pool = jcr->full_pool;
77 jcr->pool = jcr->inc_pool;
82 jcr->pool = jcr->dif_pool;
86 memset(&pr, 0, sizeof(pr));
87 bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
89 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
90 /* Try to create the pool */
91 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
92 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
93 db_strerror(jcr->db));
96 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
99 jcr->PoolId = pr.PoolId; /****FIXME**** this can go away */
100 jcr->jr.PoolId = pr.PoolId;
101 jcr->needs_sd = true;
106 * Do a Migration, Archive, or Copy of a previous job
108 * Returns: false on failure
111 bool do_mac(JCR *jcr)
116 switch(jcr->JobType) {
132 /* Print Job Start message */
133 Jmsg(jcr, M_INFO, 0, _("Start %s JobId %u, Job=%s\n"),
134 Type, jcr->JobId, jcr->Job);
136 set_jcr_job_status(jcr, JS_Running);
137 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
138 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
139 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
144 * Open a message channel connection with the Storage
145 * daemon. This is to let him know that our client
146 * will be contacting him for a backup session.
149 Dmsg0(110, "Open connection with storage daemon\n");
150 set_jcr_job_status(jcr, JS_WaitSD);
152 * Start conversation with Storage daemon
154 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
158 * Now start a job with the Storage daemon
160 if (!start_storage_daemon_job(jcr, jcr->storage, SD_APPEND)) {
164 * Now start a Storage daemon message thread
166 if (!start_storage_daemon_message_thread(jcr)) {
169 Dmsg0(150, "Storage daemon connection OK\n");
171 /* Pickup Job termination data */
172 set_jcr_job_status(jcr, JS_Running);
174 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
175 wait_for_storage_daemon_termination(jcr);
177 if (jcr->JobStatus != JS_Terminated) {
178 stat = jcr->JobStatus;
180 stat = jcr->SDJobStatus;
182 if (stat == JS_Terminated) {
183 mac_cleanup(jcr, stat);
191 * Release resources allocated during backup.
193 void mac_cleanup(JCR *jcr, int TermCode)
195 char sdt[50], edt[50];
196 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
197 char term_code[100], fd_term_msg[100], sd_term_msg[100];
198 const char *term_msg;
201 double kbps, compression;
205 switch(jcr->JobType) {
220 Dmsg2(100, "Enter mac_cleanup %d %c\n", TermCode, TermCode);
221 dequeue_messages(jcr); /* display any queued messages */
222 memset(&mr, 0, sizeof(mr));
223 set_jcr_job_status(jcr, TermCode);
225 update_job_end_record(jcr); /* update database */
227 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
228 Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
229 db_strerror(jcr->db));
230 set_jcr_job_status(jcr, JS_ErrorTerminated);
233 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
234 if (!db_get_media_record(jcr, jcr->db, &mr)) {
235 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
236 mr.VolumeName, db_strerror(jcr->db));
237 set_jcr_job_status(jcr, JS_ErrorTerminated);
240 /* Now update the bootstrap file if any */
241 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
242 jcr->job->WriteBootstrap) {
246 char *fname = jcr->job->WriteBootstrap;
247 VOL_PARAMS *VolParams = NULL;
253 bpipe = open_bpipe(fname, 0, "w");
254 fd = bpipe ? bpipe->wfd : NULL;
256 /* ***FIXME*** handle BASE */
257 fd = fopen(fname, jcr->JobLevel==L_FULL?"w+":"a+");
260 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
263 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
264 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
265 if (jcr->SDJobFiles != 0) {
266 set_jcr_job_status(jcr, JS_ErrorTerminated);
270 for (int i=0; i < VolCount; i++) {
271 /* Write the record */
272 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
273 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
274 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
275 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
276 fprintf(fd, "VolFile=%u-%u\n", VolParams[i].StartFile,
277 VolParams[i].EndFile);
278 fprintf(fd, "VolBlock=%u-%u\n", VolParams[i].StartBlock,
279 VolParams[i].EndBlock);
280 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
281 VolParams[i].LastIndex);
293 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
294 "%s: ERR=%s\n"), fname, be.strerror());
295 set_jcr_job_status(jcr, JS_ErrorTerminated);
299 msg_type = M_INFO; /* by default INFO message */
300 switch (jcr->JobStatus) {
302 if (jcr->Errors || jcr->SDErrors) {
303 term_msg = _("Backup OK -- with warnings");
305 term_msg = _("Backup OK");
309 case JS_ErrorTerminated:
310 term_msg = _("*** Backup Error ***");
311 msg_type = M_ERROR; /* Generate error message */
312 if (jcr->store_bsock) {
313 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
314 if (jcr->SD_msg_chan) {
315 pthread_cancel(jcr->SD_msg_chan);
320 term_msg = _("Backup Canceled");
321 if (jcr->store_bsock) {
322 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
323 if (jcr->SD_msg_chan) {
324 pthread_cancel(jcr->SD_msg_chan);
329 term_msg = term_code;
330 sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
333 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
334 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
335 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
339 kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
341 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
343 * Note, if the job has erred, most likely it did not write any
344 * tape, so suppress this "error" message since in that case
345 * it is normal. Or look at it the other way, only for a
346 * normal exit should we complain about this error.
348 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
349 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
351 jcr->VolumeName[0] = 0; /* none */
354 if (jcr->ReadBytes == 0) {
355 bstrncpy(compress, "None", sizeof(compress));
357 compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
358 if (compression < 0.5) {
359 bstrncpy(compress, "None", sizeof(compress));
361 bsnprintf(compress, sizeof(compress), "%.1f %%", (float)compression);
364 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
365 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
367 // bmicrosleep(15, 0); /* for debugging SIGHUP */
369 Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
372 " Backup Level: %s%s\n"
374 " FileSet: \"%s\" %s\n"
378 " FD Files Written: %s\n"
379 " SD Files Written: %s\n"
380 " FD Bytes Written: %s\n"
381 " SD Bytes Written: %s\n"
383 " Software Compression: %s\n"
384 " Volume name(s): %s\n"
385 " Volume Session Id: %d\n"
386 " Volume Session Time: %d\n"
387 " Last Volume Bytes: %s\n"
388 " Non-fatal FD errors: %d\n"
390 " FD termination status: %s\n"
391 " SD termination status: %s\n"
392 " Termination: %s\n\n"),
398 level_to_str(jcr->JobLevel), jcr->since,
399 jcr->client->hdr.name,
400 jcr->fileset->hdr.name, jcr->FSCreateTime,
404 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
405 edit_uint64_with_commas(jcr->SDJobFiles, ec4),
406 edit_uint64_with_commas(jcr->jr.JobBytes, ec2),
407 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
413 edit_uint64_with_commas(mr.VolBytes, ec3),
420 Dmsg0(100, "Leave mac_cleanup()\n");