]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/fd_cmds.c
Improve mem pool garbage collection
[bacula/bacula] / bacula / src / stored / fd_cmds.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 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  * This file handles commands from the File daemon.
21  *
22  *   Written by Kern Sibbald, MM
23  *
24  * We get here because the Director has initiated a Job with
25  *  the Storage daemon, then done the same with the File daemon,
26  *  then when the Storage daemon receives a proper connection from
27  *  the File daemon, control is passed here to handle the
28  *  subsequent File daemon commands.
29  *
30  *
31  */
32
33 #include "bacula.h"
34 #include "stored.h"
35
36 /* Forward referenced functions */
37 static bool response(JCR *jcr, BSOCK *bs, const char *resp, const char *cmd);
38
39 /* Imported variables */
40 extern STORES *me;
41
42 /* Static variables */
43 static char ferrmsg[]      = "3900 Invalid command\n";
44
45 /* Imported functions */
46 extern bool do_append_data(JCR *jcr);
47 extern bool do_read_data(JCR *jcr);
48 extern bool do_backup_job(JCR *jcr);
49
50 /* Forward referenced FD commands */
51 static bool append_open_session(JCR *jcr);
52 static bool append_close_session(JCR *jcr);
53 static bool append_data_cmd(JCR *jcr);
54 static bool append_end_session(JCR *jcr);
55 static bool read_open_session(JCR *jcr);
56 static bool read_data_cmd(JCR *jcr);
57 static bool read_close_session(JCR *jcr);
58
59 /* Exported function */
60 bool get_bootstrap_file(JCR *jcr, BSOCK *bs);
61
62 struct s_cmds {
63    const char *cmd;
64    bool (*func)(JCR *jcr);
65 };
66
67 /*
68  * The following are the recognized commands from the File daemon
69  */
70 static struct s_cmds fd_cmds[] = {
71    {"append open",  append_open_session},
72    {"append data",  append_data_cmd},
73    {"append end",   append_end_session},
74    {"append close", append_close_session},
75    {"read open",    read_open_session},
76    {"read data",    read_data_cmd},
77    {"read close",   read_close_session},
78    {NULL,           NULL}                  /* list terminator */
79 };
80
81 /* Commands from the File daemon that require additional scanning */
82 static char read_open[]       = "read open session = %127s %ld %ld %ld %ld %ld %ld\n";
83
84 /* Responses sent to the File daemon */
85 static char NO_open[]         = "3901 Error session already open\n";
86 static char NOT_opened[]      = "3902 Error session not opened\n";
87 static char ERROR_open[]      = "3904 Error open session, bad parameters\n";
88 static char OK_end[]          = "3000 OK end\n";
89 static char OK_close[]        = "3000 OK close Status = %d\n";
90 static char OK_open[]         = "3000 OK open ticket = %d\n";
91 static char ERROR_append[]    = "3903 Error append data: %s\n";
92
93 /* Information sent to the Director */
94 static char Job_start[] = "3010 Job %s start\n";
95 char Job_end[] =
96    "3099 Job %s end JobStatus=%d JobFiles=%d JobBytes=%s JobErrors=%u\n";
97
98 /*
99  * Run a Client Job -- Client already authorized
100  *  Note: this can be either a backup or restore or
101  *    migrate/copy job.
102  *
103  * Basic task here is:
104  * - Read a command from the Client -- FD or SD
105  * - Execute it
106  *
107  */
108 void run_job(JCR *jcr)
109 {
110    BSOCK *dir = jcr->dir_bsock;
111    char ec1[30];
112
113    dir->set_jcr(jcr);
114    Dmsg1(120, "Start run Job=%s\n", jcr->Job);
115    dir->fsend(Job_start, jcr->Job);
116    jcr->start_time = time(NULL);
117    jcr->run_time = jcr->start_time;
118    jcr->sendJobStatus(JS_Running);
119    /*
120     * A migrate or copy job does both a restore (read_data) and
121     *   a backup (append_data).
122     * Otherwise we do the commands that the client sends
123     *   which are for normal backup or restore jobs.
124     */
125    Dmsg3(050, "==== JobType=%c run_job=%d sd_client=%d\n", jcr->getJobType(), jcr->JobId, jcr->sd_client);
126    if (jcr->is_JobType(JT_BACKUP) && jcr->sd_client) {
127       jcr->session_opened = true;
128       Dmsg0(050, "Do: receive for 3000 OK data then append\n");
129       if (!response(jcr, jcr->file_bsock, "3000 OK data\n", "Append data")) {
130          Dmsg1(050, "Expect: 3000 OK data, got: %s", jcr->file_bsock->msg);
131          Jmsg0(jcr, M_FATAL, 0, "Append data not accepted\n");
132          goto bail_out;
133       }
134       append_data_cmd(jcr);
135       append_end_session(jcr);
136    } else if (jcr->is_JobType(JT_MIGRATE) || jcr->is_JobType(JT_COPY)) {
137       jcr->session_opened = true;
138       read_data_cmd(jcr);
139       Dmsg0(050, "Do: receive for 3000 OK data then read\n");
140       if (!response(jcr, jcr->file_bsock, "3000 OK data\n", "Data received")) {
141          Dmsg1(050, "Expect 3000 OK data, got: %s", jcr->file_bsock->msg);
142          Jmsg0(jcr, M_FATAL, 0, "Read data not accepted\n");
143          jcr->file_bsock->signal(BNET_EOD);
144          goto bail_out;
145       }
146       jcr->file_bsock->signal(BNET_EOD);
147    } else {
148       /* Either a Backup or Restore job */
149       Dmsg0(050, "Do: do_client_commands\n");
150       do_client_commands(jcr);
151    }
152 bail_out:
153    jcr->end_time = time(NULL);
154    flush_jobmedia_queue(jcr);
155    dequeue_messages(jcr);             /* send any queued messages */
156    jcr->setJobStatus(JS_Terminated);
157    generate_daemon_event(jcr, "JobEnd");
158    generate_plugin_event(jcr, bsdEventJobEnd);
159    dir->fsend(Job_end, jcr->Job, jcr->JobStatus, jcr->JobFiles,
160       edit_uint64(jcr->JobBytes, ec1), jcr->JobErrors);
161    Dmsg1(100, "==== %s", dir->msg);
162    dir->signal(BNET_EOD);             /* send EOD to Director daemon */
163    free_plugins(jcr);                 /* release instantiated plugins */
164    garbage_collect_memory_pool();
165    return;
166 }
167
168 /*
169  * Now talk to the Client (FD/SD) and do what he says
170  */
171 void do_client_commands(JCR *jcr)
172 {
173    int i;
174    bool found, quit;
175    BSOCK *fd = jcr->file_bsock;
176
177    fd->set_jcr(jcr);
178    for (quit=false; !quit;) {
179       int stat;
180
181       /* Read command coming from the File daemon */
182       stat = fd->recv();
183       if (fd->is_stop()) {            /* hard eof or error */
184          break;                       /* connection terminated */
185       }
186       if (stat <= 0) {
187          continue;                    /* ignore signals and zero length msgs */
188       }
189       Dmsg1(110, "<filed: %s", fd->msg);
190       found = false;
191       for (i=0; fd_cmds[i].cmd; i++) {
192          if (strncmp(fd_cmds[i].cmd, fd->msg, strlen(fd_cmds[i].cmd)) == 0) {
193             found = true;               /* indicate command found */
194             jcr->errmsg[0] = 0;
195             if (!fd_cmds[i].func(jcr)) {    /* do command */
196                /* Note fd->msg command may be destroyed by comm activity */
197                if (!job_canceled(jcr)) {
198                   if (jcr->errmsg[0]) {
199                      Jmsg1(jcr, M_FATAL, 0, _("Command error with FD, hanging up. ERR=%s\n"),
200                            jcr->errmsg);
201                   } else {
202                      Jmsg0(jcr, M_FATAL, 0, _("Command error with FD, hanging up.\n"));
203                   }
204                   jcr->setJobStatus(JS_ErrorTerminated);
205                }
206                quit = true;
207             }
208             break;
209          }
210       }
211       if (!found) {                   /* command not found */
212          if (!job_canceled(jcr)) {
213             Jmsg1(jcr, M_FATAL, 0, _("FD command not found: %s\n"), fd->msg);
214             Dmsg1(110, "<filed: Command not found: %s\n", fd->msg);
215          }
216          fd->fsend(ferrmsg);
217          break;
218       }
219    }
220    fd->signal(BNET_TERMINATE);        /* signal to FD job is done */
221 }
222
223 /*
224  *   Append Data command
225  *     Open Data Channel and receive Data for archiving
226  *     Write the Data to the archive device
227  */
228 static bool append_data_cmd(JCR *jcr)
229 {
230    BSOCK *fd = jcr->file_bsock;
231
232    Dmsg1(120, "Append data: %s", fd->msg);
233    if (jcr->session_opened) {
234       Dmsg1(110, "<bfiled: %s", fd->msg);
235       jcr->setJobType(JT_BACKUP);
236       jcr->errmsg[0] = 0;
237       if (do_append_data(jcr)) {
238          return true;
239       } else {
240          fd->suppress_error_messages(true); /* ignore errors at this point */
241          fd->fsend(ERROR_append, jcr->errmsg);
242       }
243    } else {
244       pm_strcpy(jcr->errmsg, _("Attempt to append on non-open session.\n"));
245       fd->fsend(NOT_opened);
246    }
247    return false;
248 }
249
250 static bool append_end_session(JCR *jcr)
251 {
252    BSOCK *fd = jcr->file_bsock;
253
254    Dmsg1(120, ">filed: %s", fd->msg);
255    if (!jcr->session_opened) {
256       pm_strcpy(jcr->errmsg, _("Attempt to close non-open session.\n"));
257       fd->fsend(NOT_opened);
258       return false;
259    }
260    return fd->fsend(OK_end);
261 }
262
263
264 /*
265  * Append Open session command
266  *
267  */
268 static bool append_open_session(JCR *jcr)
269 {
270    BSOCK *fd = jcr->file_bsock;
271
272    Dmsg1(120, "Append open session: %s", fd->msg);
273    if (jcr->session_opened) {
274       pm_strcpy(jcr->errmsg, _("Attempt to open already open session.\n"));
275       fd->fsend(NO_open);
276       return false;
277    }
278
279    jcr->session_opened = true;
280
281    /* Send "Ticket" to File Daemon */
282    fd->fsend(OK_open, jcr->VolSessionId);
283    Dmsg1(110, ">filed: %s", fd->msg);
284
285    return true;
286 }
287
288 /*
289  *   Append Close session command
290  *      Close the append session and send back Statistics
291  *         (need to fix statistics)
292  */
293 static bool append_close_session(JCR *jcr)
294 {
295    BSOCK *fd = jcr->file_bsock;
296
297    Dmsg1(120, "<filed: %s", fd->msg);
298    if (!jcr->session_opened) {
299       pm_strcpy(jcr->errmsg, _("Attempt to close non-open session.\n"));
300       fd->fsend(NOT_opened);
301       return false;
302    }
303    /* Send final statistics to File daemon */
304    fd->fsend(OK_close, jcr->JobStatus);
305    Dmsg1(120, ">filed: %s", fd->msg);
306
307    fd->signal(BNET_EOD);              /* send EOD to File daemon */
308    jcr->session_opened = false;
309    return true;
310 }
311
312 /*
313  *   Read Data command
314  *     Open Data Channel, read the data from
315  *     the archive device and send to File
316  *     daemon.
317  */
318 static bool read_data_cmd(JCR *jcr)
319 {
320    BSOCK *fd = jcr->file_bsock;
321
322    Dmsg1(120, "Read data: %s", fd->msg);
323    if (jcr->session_opened) {
324       Dmsg1(120, "<bfiled: %s", fd->msg);
325       return do_read_data(jcr);
326    } else {
327       pm_strcpy(jcr->errmsg, _("Attempt to read on non-open session.\n"));
328       fd->fsend(NOT_opened);
329       return false;
330    }
331 }
332
333 /*
334  * Read Open session command
335  *
336  *  We need to scan for the parameters of the job
337  *    to be restored.
338  */
339 static bool read_open_session(JCR *jcr)
340 {
341    BSOCK *fd = jcr->file_bsock;
342
343    Dmsg1(120, "%s", fd->msg);
344    if (jcr->session_opened) {
345       pm_strcpy(jcr->errmsg, _("Attempt to open an already open session.\n"));
346       fd->fsend(NO_open);
347       return false;
348    }
349
350    if (sscanf(fd->msg, read_open, jcr->read_dcr->VolumeName, &jcr->read_VolSessionId,
351          &jcr->read_VolSessionTime, &jcr->read_StartFile, &jcr->read_EndFile,
352          &jcr->read_StartBlock, &jcr->read_EndBlock) == 7) {
353       Dmsg4(100, "read_open_session got: JobId=%d Vol=%s VolSessId=%ld VolSessT=%ld\n",
354          jcr->JobId, jcr->read_dcr->VolumeName, jcr->read_VolSessionId,
355          jcr->read_VolSessionTime);
356       Dmsg4(100, "  StartF=%ld EndF=%ld StartB=%ld EndB=%ld\n",
357          jcr->read_StartFile, jcr->read_EndFile, jcr->read_StartBlock,
358          jcr->read_EndBlock);
359
360    } else {
361       pm_strcpy(jcr->errmsg, _("Cannot open session, received bad parameters.\n"));
362       fd->fsend(ERROR_open);
363       return false;
364    }
365
366    jcr->session_opened = true;
367    jcr->setJobType(JT_RESTORE);
368
369    /* Send "Ticket" to File Daemon */
370    fd->fsend(OK_open, jcr->VolSessionId);
371    Dmsg1(110, ">filed: %s", fd->msg);
372
373    return true;
374 }
375
376 /*
377  *   Read Close session command
378  *      Close the read session
379  */
380 static bool read_close_session(JCR *jcr)
381 {
382    BSOCK *fd = jcr->file_bsock;
383
384    Dmsg1(120, "Read close session: %s\n", fd->msg);
385    if (!jcr->session_opened) {
386       fd->fsend(NOT_opened);
387       return false;
388    }
389    /* Send final close msg to File daemon */
390    fd->fsend(OK_close, jcr->JobStatus);
391    Dmsg1(160, ">filed: %s\n", fd->msg);
392
393    fd->signal(BNET_EOD);            /* send EOD to File daemon */
394
395    jcr->session_opened = false;
396    return true;
397 }
398
399 /*
400  * Get response from FD or SD
401  * sent. Check that the response agrees with what we expect.
402  *
403  *  Returns: false on failure
404  *           true  on success
405  */
406 static bool response(JCR *jcr, BSOCK *bs, const char *resp, const char *cmd)
407 {
408    int n;
409
410    if (bs->is_error()) {
411       return false;
412    }
413    if ((n = bs->recv()) >= 0) {
414       if (strcmp(bs->msg, resp) == 0) {
415          return true;
416       }
417       Jmsg(jcr, M_FATAL, 0, _("Bad response to %s command: wanted %s, got %s\n"),
418             cmd, resp, bs->msg);
419       return false;
420    }
421    Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"),
422          cmd, bs->bstrerror());
423    return false;
424 }