]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/mac.c
Massive SD calling sequence reorganization
[bacula/bacula] / bacula / src / dird / mac.c
1 /*
2  *
3  *   Bacula Director -- mac.c -- responsible for doing 
4  *     migration, archive, and copy jobs.
5  *
6  *     Kern Sibbald, September MMIV
7  *
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
12  *       to do the backup.
13  *     When the File daemon finishes the job, update the DB.
14  *
15  *   Version $Id$
16  */
17
18 /*
19    Copyright (C) 2004 Kern Sibbald and John Walker
20
21    This program is free software; you can redistribute it and/or
22    modify it under the terms of the GNU General Public License as
23    published by the Free Software Foundation; either version 2 of
24    the License, or (at your option) any later version.
25
26    This program is distributed in the hope that it will be useful,
27    but WITHOUT ANY WARRANTY; without even the implied warranty of
28    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29    General Public License for more details.
30
31    You should have received a copy of the GNU General Public
32    License along with this program; if not, write to the Free
33    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
34    MA 02111-1307, USA.
35
36  */
37
38 #include "bacula.h"
39 #include "dird.h"
40 #include "ua.h"
41
42 /* Forward referenced functions */
43 static void mac_cleanup(JCR *jcr, int TermCode, char *since, FILESET_DBR *fsr,
44                         const char *Type);
45
46 /* External functions */
47
48 /* 
49  * Do a Migration, Archive, or Copy of a previous job
50  *    
51  *  Returns:  false on failure
52  *            true  on success
53  */
54 bool do_mac(JCR *jcr) 
55 {
56    char since[MAXSTRING];
57    int stat;
58    POOL_DBR pr;
59    JOB_DBR jr;
60    FILESET_DBR fsr;
61    JobId_t input_jobid;
62    char *Name;
63    const char *Type;
64
65    switch(jcr->JobType) {
66    case JT_MIGRATION:
67       Type = "Migration";
68       break;
69    case JT_ARCHIVE:
70       Type = "Archive";
71       break;
72    case JT_COPY:
73       Type = "Copy";
74       break;
75    default:
76       Type = "Unknown";
77       break;
78    }
79
80    if (!get_or_create_client_record(jcr)) {
81       goto bail_out;
82    }
83
84    if (!get_or_create_fileset_record(jcr, &fsr)) {
85       goto bail_out;
86    }
87
88    /*
89     * Find JobId of last job that ran.
90     */
91    memcpy(&jr, &jcr->jr, sizeof(jr));
92    Name = jcr->job->hdr.name;
93    Dmsg1(100, "find last jobid for: %s\n", NPRT(Name));
94    if (!db_find_last_jobid(jcr, jcr->db, Name, &jr)) {
95       Jmsg(jcr, M_FATAL, 0, _(
96            "Unable to find JobId of previous Job for this client.\n"));
97       goto bail_out;
98    }
99    input_jobid = jr.JobId;
100    jcr->JobLevel = jr.JobLevel;
101    Dmsg1(100, "Last jobid=%d\n", input_jobid);
102
103    jcr->fname = get_pool_memory(PM_FNAME);
104
105    /* 
106     * Get the Pool record -- first apply any level defined pools  
107     */
108    switch (jcr->JobLevel) {
109    case L_FULL:
110       if (jcr->full_pool) {
111          jcr->pool = jcr->full_pool;   
112       }
113       break;
114    case L_INCREMENTAL:
115       if (jcr->inc_pool) {
116          jcr->pool = jcr->inc_pool;   
117       }
118       break;
119    case L_DIFFERENTIAL:
120       if (jcr->dif_pool) {
121          jcr->pool = jcr->dif_pool;   
122       }
123       break;
124    }
125    memset(&pr, 0, sizeof(pr));
126    bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
127
128    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
129       /* Try to create the pool */
130       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
131          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name, 
132             db_strerror(jcr->db));
133          goto bail_out;
134       } else {
135          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
136       }
137    }
138    jcr->PoolId = pr.PoolId;               /****FIXME**** this can go away */
139    jcr->jr.PoolId = pr.PoolId;
140
141
142    /* Print Job Start message */
143    Jmsg(jcr, M_INFO, 0, _("Start %s JobId %u, Job=%s\n"),
144         Type, jcr->JobId, jcr->Job);
145
146    set_jcr_job_status(jcr, JS_Running);
147    Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
148    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
149       Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
150       goto bail_out;
151    }
152
153
154    /*
155     * Open a message channel connection with the Storage
156     * daemon. This is to let him know that our client
157     * will be contacting him for a backup  session.
158     *
159     */
160    Dmsg0(110, "Open connection with storage daemon\n");
161    set_jcr_job_status(jcr, JS_WaitSD);
162    /*
163     * Start conversation with Storage daemon  
164     */
165    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
166       goto bail_out;
167    }
168    /*
169     * Now start a job with the Storage daemon
170     */
171    if (!start_storage_daemon_job(jcr)) {
172       goto bail_out;
173    }
174    /*
175     * Now start a Storage daemon message thread
176     */
177    if (!start_storage_daemon_message_thread(jcr)) {
178       goto bail_out;
179    }
180    Dmsg0(150, "Storage daemon connection OK\n");
181
182    /* Pickup Job termination data */        
183    set_jcr_job_status(jcr, JS_Running);
184
185    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
186    wait_for_storage_daemon_termination(jcr);
187
188    if (jcr->JobStatus != JS_Terminated) {
189       stat = jcr->JobStatus;
190    } else {
191       stat = jcr->SDJobStatus;
192    }
193    mac_cleanup(jcr, stat, since, &fsr, Type);
194    return true;
195
196 bail_out:
197    mac_cleanup(jcr, JS_ErrorTerminated, since, &fsr, Type);
198    return false;
199 }
200
201
202 /*
203  * Release resources allocated during backup.
204  */
205 static void mac_cleanup(JCR *jcr, int TermCode, char *since, FILESET_DBR *fsr,
206                        const char *Type)
207 {
208    char sdt[50], edt[50];
209    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
210    char term_code[100], fd_term_msg[100], sd_term_msg[100];
211    const char *term_msg;
212    int msg_type;
213    MEDIA_DBR mr;
214    double kbps, compression;
215    utime_t RunTime;
216
217    Dmsg2(100, "Enter mac_cleanup %d %c\n", TermCode, TermCode);
218    dequeue_messages(jcr);             /* display any queued messages */
219    memset(&mr, 0, sizeof(mr));
220    set_jcr_job_status(jcr, TermCode);
221
222    update_job_end_record(jcr);        /* update database */
223    
224    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
225       Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"), 
226          db_strerror(jcr->db));
227       set_jcr_job_status(jcr, JS_ErrorTerminated);
228    }
229
230    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
231    if (!db_get_media_record(jcr, jcr->db, &mr)) {
232       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"), 
233          mr.VolumeName, db_strerror(jcr->db));
234       set_jcr_job_status(jcr, JS_ErrorTerminated);
235    }
236
237    /* Now update the bootstrap file if any */
238    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes && 
239        jcr->job->WriteBootstrap) {
240       FILE *fd;
241       BPIPE *bpipe = NULL;
242       int got_pipe = 0;
243       char *fname = jcr->job->WriteBootstrap;
244       VOL_PARAMS *VolParams = NULL;
245       int VolCount;
246
247       if (*fname == '|') {
248          fname++;
249          got_pipe = 1;
250          bpipe = open_bpipe(fname, 0, "w");
251          fd = bpipe ? bpipe->wfd : NULL;
252       } else {
253          /* ***FIXME*** handle BASE */
254          fd = fopen(fname, jcr->JobLevel==L_FULL?"w+":"a+");
255       }
256       if (fd) {
257          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
258                     &VolParams);
259          if (VolCount == 0) {
260             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "      
261                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
262              if (jcr->SDJobFiles != 0) {
263                 set_jcr_job_status(jcr, JS_ErrorTerminated);
264              }
265
266          }
267          for (int i=0; i < VolCount; i++) {
268             /* Write the record */
269             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
270             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
271             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
272             fprintf(fd, "VolFile=%u-%u\n", VolParams[i].StartFile,
273                          VolParams[i].EndFile);
274             fprintf(fd, "VolBlock=%u-%u\n", VolParams[i].StartBlock,
275                          VolParams[i].EndBlock);
276             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
277                          VolParams[i].LastIndex);
278          }
279          if (VolParams) {
280             free(VolParams);
281          }
282          if (got_pipe) {
283             close_bpipe(bpipe);
284          } else {
285             fclose(fd);
286          }
287       } else {
288          berrno be;
289          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
290               "%s: ERR=%s\n"), fname, be.strerror());
291          set_jcr_job_status(jcr, JS_ErrorTerminated);
292       }
293    }
294
295    msg_type = M_INFO;                 /* by default INFO message */
296    switch (jcr->JobStatus) {
297       case JS_Terminated:
298          if (jcr->Errors || jcr->SDErrors) {
299             term_msg = _("Backup OK -- with warnings");
300          } else {
301             term_msg = _("Backup OK");
302          }
303          break;
304       case JS_FatalError:
305       case JS_ErrorTerminated:
306          term_msg = _("*** Backup Error ***"); 
307          msg_type = M_ERROR;          /* Generate error message */
308          if (jcr->store_bsock) {
309             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
310             if (jcr->SD_msg_chan) {
311                pthread_cancel(jcr->SD_msg_chan);
312             }
313          }
314          break;
315       case JS_Canceled:
316          term_msg = _("Backup Canceled");
317          if (jcr->store_bsock) {
318             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
319             if (jcr->SD_msg_chan) {
320                pthread_cancel(jcr->SD_msg_chan);
321             }
322          }
323          break;
324       default:
325          term_msg = term_code;
326          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
327          break;
328    }
329    bstrftime(sdt, sizeof(sdt), jcr->jr.StartTime);
330    bstrftime(edt, sizeof(edt), jcr->jr.EndTime);
331    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
332    if (RunTime <= 0) {
333       kbps = 0;
334    } else {
335       kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
336    }
337    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
338       /*
339        * Note, if the job has erred, most likely it did not write any
340        *  tape, so suppress this "error" message since in that case
341        *  it is normal.  Or look at it the other way, only for a
342        *  normal exit should we complain about this error.
343        */
344       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {                                
345          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
346       }
347       jcr->VolumeName[0] = 0;         /* none */
348    }
349
350    if (jcr->ReadBytes == 0) {
351       bstrncpy(compress, "None", sizeof(compress));
352    } else {
353       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
354       if (compression < 0.5) {
355          bstrncpy(compress, "None", sizeof(compress));
356       } else {
357          bsnprintf(compress, sizeof(compress), "%.1f %%", (float)compression);
358       }
359    }
360    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
361    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
362
363 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
364
365    Jmsg(jcr, msg_type, 0, _("Bacula " VERSION " (" LSMDATE "): %s\n\
366 JobId:                  %d\n\
367 Job:                    %s\n\
368 Backup Level:           %s%s\n\
369 Client:                 %s\n\
370 FileSet:                \"%s\" %s\n\
371 Pool:                   \"%s\"\n\
372 Start time:             %s\n\
373 End time:               %s\n\
374 FD Files Written:       %s\n\
375 SD Files Written:       %s\n\
376 FD Bytes Written:       %s\n\
377 SD Bytes Written:       %s\n\
378 Rate:                   %.1f KB/s\n\
379 Software Compression:   %s\n\
380 Volume name(s):         %s\n\
381 Volume Session Id:      %d\n\
382 Volume Session Time:    %d\n\
383 Last Volume Bytes:      %s\n\
384 Non-fatal FD errors:    %d\n\
385 SD Errors:              %d\n\
386 FD termination status:  %s\n\
387 SD termination status:  %s\n\
388 Termination:            %s\n\n"),
389         edt,
390         jcr->jr.JobId,
391         jcr->jr.Job,
392         level_to_str(jcr->JobLevel), since,
393         jcr->client->hdr.name,
394         jcr->fileset->hdr.name, fsr->cCreateTime,
395         jcr->pool->hdr.name,
396         sdt,
397         edt,
398         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
399         edit_uint64_with_commas(jcr->SDJobFiles, ec4),
400         edit_uint64_with_commas(jcr->jr.JobBytes, ec2),
401         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
402         (float)kbps,
403         compress,
404         jcr->VolumeName,
405         jcr->VolSessionId,
406         jcr->VolSessionTime,
407         edit_uint64_with_commas(mr.VolBytes, ec3),
408         jcr->Errors,
409         jcr->SDErrors,
410         fd_term_msg,
411         sd_term_msg,
412         term_msg);
413
414    Dmsg0(100, "Leave mac_cleanup()\n");
415 }