]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/backup.c
631a971c5db00808c9263870dd3e2324a0781366
[bacula/bacula] / bacula / src / dird / backup.c
1 /*
2  *
3  *   Bacula Director -- backup.c -- responsible for doing backup jobs
4  *
5  *     Kern Sibbald, March MM
6  *
7  *    This routine is called as a thread. It may not yet be totally
8  *      thread reentrant!!!
9  *
10  *  Basic tasks done here:
11  *     Open DB and create records for this job.
12  *     Open Message Channel with Storage daemon to tell him a job will be starting.
13  *     Open connection with File daemon and pass him commands
14  *       to do the backup.
15  *     When the File daemon finishes the job, update the DB.
16  *
17  */
18
19 /*
20    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
21
22    This program is free software; you can redistribute it and/or
23    modify it under the terms of the GNU General Public License as
24    published by the Free Software Foundation; either version 2 of
25    the License, or (at your option) any later version.
26
27    This program is distributed in the hope that it will be useful,
28    but WITHOUT ANY WARRANTY; without even the implied warranty of
29    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30    General Public License for more details.
31
32    You should have received a copy of the GNU General Public
33    License along with this program; if not, write to the Free
34    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
35    MA 02111-1307, USA.
36
37  */
38
39 #include "bacula.h"
40 #include "dird.h"
41 #include "ua.h"
42
43 /* Commands sent to File daemon */
44 static char backupcmd[] = "backup\n";
45 static char storaddr[]  = "storage address=%s port=%d\n";
46 static char levelcmd[]  = "level = %s%s\n";
47
48 /* Responses received from File daemon */
49 static char OKbackup[] = "2000 OK backup\n";
50 static char OKstore[]  = "2000 OK storage\n";
51 static char OKlevel[]  = "2000 OK level\n";
52
53 /* Forward referenced functions */
54 static void backup_cleanup(JCR *jcr, int TermCode, char *since);
55 static int wait_for_job_termination(JCR *jcr);               
56
57 /* External functions */
58
59 /* 
60  * Do a backup of the specified FileSet
61  *    
62  *  Returns:  0 on failure
63  *            1 on success
64  */
65 int do_backup(JCR *jcr) 
66 {
67    char since[MAXSTRING];
68    int stat;
69    BSOCK   *fd;
70    POOL_DBR pr;
71    FILESET_DBR fsr;
72
73    since[0] = 0;
74
75    if (!get_or_create_client_record(jcr)) {
76       backup_cleanup(jcr, JS_ErrorTerminated, since);
77    }
78
79
80    /*
81     * Get or Create FileSet record
82     */
83    memset(&fsr, 0, sizeof(fsr));
84    strcpy(fsr.FileSet, jcr->fileset->hdr.name);
85    if (jcr->fileset->have_MD5) {
86       struct MD5Context md5c;
87       unsigned char signature[16];
88       memcpy(&md5c, &jcr->fileset->md5c, sizeof(md5c));
89       MD5Final(signature, &md5c);
90       bin_to_base64(fsr.MD5, (char *)signature, 16); /* encode 16 bytes */
91    } else {
92       Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 signature not found.\n"));
93    }
94    if (!db_create_fileset_record(jcr->db, &fsr)) {
95       Jmsg(jcr, M_ERROR, 0, _("Could not create FileSet record. %s"), 
96          db_strerror(jcr->db));
97       backup_cleanup(jcr, JS_ErrorTerminated, since);
98       return 0;
99    }   
100    jcr->jr.FileSetId = fsr.FileSetId;
101    Dmsg2(9, "Created FileSet %s record %d\n", jcr->fileset->hdr.name, 
102       jcr->jr.FileSetId);
103
104
105    /* Look up the last
106     * FULL backup job to get the time/date for a 
107     * differential or incremental save.
108     */
109    jcr->stime = (char *) get_pool_memory(PM_MESSAGE);
110    jcr->stime[0] = 0;
111    since[0] = 0;
112    switch (jcr->level) {
113       case L_DIFFERENTIAL:
114       case L_INCREMENTAL:
115          /* Look up start time of last job */
116          jcr->jr.JobId = 0;
117          if (!db_find_job_start_time(jcr->db, &jcr->jr, jcr->stime)) {
118             Jmsg(jcr, M_INFO, 0, _("Last FULL backup time not found. Doing FULL backup.\n"));
119             jcr->level = L_FULL;
120             jcr->jr.Level = L_FULL;
121          } else {
122             strcpy(since, ", since=");
123             strcat(since, jcr->stime);
124          }
125          Dmsg1(15, "Last start time = %s\n", jcr->stime);
126          break;
127    }
128
129    jcr->jr.JobId = jcr->JobId;
130    jcr->jr.StartTime = jcr->start_time;
131    if (!db_update_job_start_record(jcr->db, &jcr->jr)) {
132       Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
133       backup_cleanup(jcr, JS_ErrorTerminated, since);
134       return 0;
135    }
136
137    jcr->fname = (char *) get_pool_memory(PM_FNAME);
138
139    /* Print Job Start message */
140    Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %d, Job=%s\n"),
141         jcr->JobId, jcr->Job);
142
143    /* 
144     * Get the Pool record  
145     */
146    memset(&pr, 0, sizeof(pr));
147    strcpy(pr.Name, jcr->pool->hdr.name);
148    while (!db_get_pool_record(jcr->db, &pr)) { /* get by Name */
149       /* Try to create the pool */
150       if (create_pool(jcr->db, jcr->pool) < 0) {
151          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name, 
152             db_strerror(jcr->db));
153          backup_cleanup(jcr, JS_ErrorTerminated, since);
154          return 0;
155       } else {
156          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
157       }
158    }
159    jcr->PoolId = pr.PoolId;               /****FIXME**** this can go away */
160    jcr->jr.PoolId = pr.PoolId;
161
162 #ifdef needed
163    /* NOTE, THIS IS NOW DONE BY THE STORAGE DAEMON
164     *
165     * Find at least one Volume associated with this Pool
166     *  It must be marked Append, and be of the correct Media Type
167     *  for the storage type.
168     */
169    memset(&mr, 0, sizeof(mr));
170    mr.PoolId = pr.PoolId;
171    strcpy(mr.VolStatus, "Append");
172    strcpy(mr.MediaType, jcr->store->media_type);
173    if (!db_find_next_volume(jcr->db, 1, &mr)) {
174       if (!newVolume(jcr)) {
175          Jmsg(jcr, M_FATAL, 0, _("No writable %s media in Pool %s.\n\
176       Please use the Console program to add available Volumes.\n"), mr.MediaType, pr.Name);
177          backup_cleanup(jcr, JS_ErrorTerminated, since);
178          return 0;
179       }
180    }
181 #endif
182
183    /*
184     * Open a message channel connection with the Storage
185     * daemon. This is to let him know that our client
186     * will be contacting him for a backup  session.
187     *
188     */
189    Dmsg0(10, "Open connection with storage daemon\n");
190    jcr->JobStatus = JS_Blocked;
191    /*
192     * Start conversation with Storage daemon  
193     */
194    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
195       backup_cleanup(jcr, JS_ErrorTerminated, since);
196       return 0;
197    }
198    /*
199     * Now start a job with the Storage daemon
200     */
201    if (!start_storage_daemon_job(jcr)) {
202       backup_cleanup(jcr, JS_ErrorTerminated, since);
203       return 0;
204    }
205    /*
206     * Now start a Storage daemon message thread
207     */
208    if (!start_storage_daemon_message_thread(jcr)) {
209       backup_cleanup(jcr, JS_ErrorTerminated, since);
210       return 0;
211    }
212
213    Dmsg0(50, "Storage daemon connection OK\n");
214
215    if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
216       backup_cleanup(jcr, JS_ErrorTerminated, since);
217       return 0;
218    }
219
220    jcr->JobStatus = JS_Running;
221    fd = jcr->file_bsock;
222
223    if (!send_include_list(jcr)) {
224       backup_cleanup(jcr, JS_ErrorTerminated, since);
225       return 0;
226    }
227
228    if (!send_exclude_list(jcr)) {
229       backup_cleanup(jcr, JS_ErrorTerminated, since);
230       return 0;
231    }
232
233    /* 
234     * send Storage daemon address to the File daemon
235     */
236    if (jcr->store->SDDport == 0) {
237       jcr->store->SDDport = jcr->store->SDport;
238    }
239    bnet_fsend(fd, storaddr, jcr->store->address, jcr->store->SDDport);
240    if (!response(fd, OKstore, "Storage")) {
241       backup_cleanup(jcr, JS_ErrorTerminated, since);
242       return 0;
243    }
244
245    /* 
246     * Send Level command to File daemon
247     */
248    switch (jcr->level) {
249       case L_FULL:
250          bnet_fsend(fd, levelcmd, "full", " ");
251          break;
252       case L_DIFFERENTIAL:
253       case L_INCREMENTAL:
254          bnet_fsend(fd, levelcmd, "since ", jcr->stime);
255          free_pool_memory(jcr->stime);
256          jcr->stime = NULL;
257          break;
258       case L_SINCE:
259       default:
260          Emsg1(M_FATAL, 0, _("Unimplemented backup level %d\n"), jcr->level);
261          backup_cleanup(jcr, JS_ErrorTerminated, since);
262          return 0;
263    }
264    Dmsg1(20, ">filed: %s", fd->msg);
265    if (!response(fd, OKlevel, "Level")) {
266       backup_cleanup(jcr, JS_ErrorTerminated, since);
267       return 0;
268    }
269
270    /* Send backup command */
271    bnet_fsend(fd, backupcmd);
272    if (!response(fd, OKbackup, "backup")) {
273       backup_cleanup(jcr, JS_ErrorTerminated, since);
274       return 0;
275    }
276
277    /* Pickup Job termination data */        
278    stat = wait_for_job_termination(jcr);
279    backup_cleanup(jcr, stat, since);
280    return 1;
281 }
282
283 /*
284  *  NOTE! This is no longer really needed as the Storage
285  *        daemon now passes this information directly
286  *        back to us.   
287  */
288 static int wait_for_job_termination(JCR *jcr)
289 {
290    int32_t n = 0;
291    BSOCK *fd = jcr->file_bsock;
292
293    jcr->JobStatus = JS_WaitFD;
294    /* Wait for Client to terminate */
295    while ((n = bget_msg(fd, 0)) > 0 && !job_cancelled(jcr)) {
296       /* get and discard Client output */
297    }
298    bnet_sig(fd, BNET_TERMINATE);      /* tell Client we are terminating */
299    if (n < 0) {
300       Jmsg(jcr, M_FATAL, 0, _("<filed: network error during BACKUP command. ERR=%s\n"),
301           bnet_strerror(fd));
302    }
303
304    /* Now wait for Storage daemon to terminate our message thread */
305    P(jcr->mutex);
306    jcr->JobStatus = JS_WaitSD;
307    while (!jcr->msg_thread_done && !job_cancelled(jcr)) {
308       struct timeval tv;
309       struct timezone tz;
310       struct timespec timeout;
311
312       gettimeofday(&tv, &tz);
313       timeout.tv_nsec = 0;
314       timeout.tv_sec = tv.tv_sec + 10; /* wait 10 seconds */
315       Dmsg0(300, "I'm waiting for message thread termination.\n");
316       pthread_cond_timedwait(&jcr->term_wait, &jcr->mutex, &timeout);
317    }
318    V(jcr->mutex);
319    if (n < 0) {                                     
320       return JS_ErrorTerminated;
321    }
322    return jcr->SDJobStatus;
323 }
324
325 /*
326  * Release resources allocated during backup.
327  */
328 static void backup_cleanup(JCR *jcr, int TermCode, char *since)
329 {
330    char sdt[50], edt[50];
331    char ec1[30], ec2[30], ec3[30];
332    char term_code[100];
333    char *term_msg;
334    int msg_type;
335    MEDIA_DBR mr;
336
337    Dmsg0(100, "Enter backup_cleanup()\n");
338    memset(&mr, 0, sizeof(mr));
339    jcr->JobStatus = TermCode;
340
341    update_job_end_record(jcr);        /* update database */
342    
343    if (!db_get_job_record(jcr->db, &jcr->jr)) {
344       Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"), 
345          db_strerror(jcr->db));
346    }
347
348    strcpy(mr.VolumeName, jcr->VolumeName);
349    if (!db_get_media_record(jcr->db, &mr)) {
350       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for stats: %s"), 
351          db_strerror(jcr->db));
352    }
353
354       
355    msg_type = M_INFO;                 /* by default INFO message */
356    switch (TermCode) {
357       case JS_Terminated:
358          term_msg = _("Backup OK");
359          break;
360       case JS_Errored:
361          term_msg = _("*** Backup Error ***"); 
362          msg_type = M_ERROR;          /* Generate error message */
363          if (jcr->store_bsock) {
364             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
365             pthread_cancel(jcr->SD_msg_chan);
366          }
367          break;
368       case JS_Cancelled:
369          term_msg = _("Backup Cancelled");
370          if (jcr->store_bsock) {
371             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
372             pthread_cancel(jcr->SD_msg_chan);
373          }
374          break;
375       default:
376          term_msg = term_code;
377          sprintf(term_code, _("Inappropriate term code: %c\n"), TermCode);
378          break;
379    }
380    bstrftime(sdt, sizeof(sdt), jcr->jr.StartTime);
381    bstrftime(edt, sizeof(edt), jcr->jr.EndTime);
382    if (!db_get_job_volume_names(jcr->db, jcr->jr.JobId, jcr->VolumeName)) {
383       jcr->VolumeName[0] = 0;         /* none */
384    }
385
386    Jmsg(jcr, msg_type, 0, _("%s\n\
387 JobId:                  %d\n\
388 Job:                    %s\n\
389 FileSet:                %s\n\
390 Backup Level:           %s%s\n\
391 Client:                 %s\n\
392 Start time:             %s\n\
393 End time:               %s\n\
394 Bytes Written:          %s\n\
395 Files Written:          %s\n\
396 Volume names(s):        %s\n\
397 Volume Session Id:      %d\n\
398 Volume Session Time:    %d\n\
399 Volume Bytes:           %s\n\
400 Termination:            %s\n"),
401         edt,
402         jcr->jr.JobId,
403         jcr->jr.Job,
404         jcr->fileset->hdr.name,
405         level_to_str(jcr->level), since,
406         jcr->client->hdr.name,
407         sdt,
408         edt,
409         edit_uint64_with_commas(jcr->jr.JobBytes, ec1),
410         edit_uint64_with_commas(jcr->jr.JobFiles, ec2),
411         jcr->VolumeName,
412         jcr->VolSessionId,
413         jcr->VolSessionTime,
414         edit_uint64_with_commas(mr.VolBytes, ec3),
415         term_msg);
416
417    Dmsg0(100, "Leave backup_cleanup()\n");
418 }