]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/fd_cmds.c
Change copyright as per agreement with FSFE
[bacula/bacula] / bacula / src / stored / fd_cmds.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2016 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    return;
165 }
166
167 /*
168  * Now talk to the Client (FD/SD) and do what he says
169  */
170 void do_client_commands(JCR *jcr)
171 {
172    int i;
173    bool found, quit;
174    BSOCK *fd = jcr->file_bsock;
175
176    fd->set_jcr(jcr);
177    for (quit=false; !quit;) {
178       int stat;
179
180       /* Read command coming from the File daemon */
181       stat = fd->recv();
182       if (fd->is_stop()) {            /* hard eof or error */
183          break;                       /* connection terminated */
184       }
185       if (stat <= 0) {
186          continue;                    /* ignore signals and zero length msgs */
187       }
188       Dmsg1(110, "<filed: %s", fd->msg);
189       found = false;
190       for (i=0; fd_cmds[i].cmd; i++) {
191          if (strncmp(fd_cmds[i].cmd, fd->msg, strlen(fd_cmds[i].cmd)) == 0) {
192             found = true;               /* indicate command found */
193             jcr->errmsg[0] = 0;
194             if (!fd_cmds[i].func(jcr)) {    /* do command */
195                /* Note fd->msg command may be destroyed by comm activity */
196                if (!job_canceled(jcr)) {
197                   if (jcr->errmsg[0]) {
198                      Jmsg1(jcr, M_FATAL, 0, _("Command error with FD, hanging up. ERR=%s\n"),
199                            jcr->errmsg);
200                   } else {
201                      Jmsg0(jcr, M_FATAL, 0, _("Command error with FD, hanging up.\n"));
202                   }
203                   jcr->setJobStatus(JS_ErrorTerminated);
204                }
205                quit = true;
206             }
207             break;
208          }
209       }
210       if (!found) {                   /* command not found */
211          if (!job_canceled(jcr)) {
212             Jmsg1(jcr, M_FATAL, 0, _("FD command not found: %s\n"), fd->msg);
213             Dmsg1(110, "<filed: Command not found: %s\n", fd->msg);
214          }
215          fd->fsend(ferrmsg);
216          break;
217       }
218    }
219    fd->signal(BNET_TERMINATE);        /* signal to FD job is done */
220 }
221
222 /*
223  *   Append Data command
224  *     Open Data Channel and receive Data for archiving
225  *     Write the Data to the archive device
226  */
227 static bool append_data_cmd(JCR *jcr)
228 {
229    BSOCK *fd = jcr->file_bsock;
230
231    Dmsg1(120, "Append data: %s", fd->msg);
232    if (jcr->session_opened) {
233       Dmsg1(110, "<bfiled: %s", fd->msg);
234       jcr->setJobType(JT_BACKUP);
235       jcr->errmsg[0] = 0;
236       if (do_append_data(jcr)) {
237          return true;
238       } else {
239          fd->suppress_error_messages(true); /* ignore errors at this point */
240          fd->fsend(ERROR_append, jcr->errmsg);
241       }
242    } else {
243       pm_strcpy(jcr->errmsg, _("Attempt to append on non-open session.\n"));
244       fd->fsend(NOT_opened);
245    }
246    return false;
247 }
248
249 static bool append_end_session(JCR *jcr)
250 {
251    BSOCK *fd = jcr->file_bsock;
252
253    Dmsg1(120, ">filed: %s", fd->msg);
254    if (!jcr->session_opened) {
255       pm_strcpy(jcr->errmsg, _("Attempt to close non-open session.\n"));
256       fd->fsend(NOT_opened);
257       return false;
258    }
259    return fd->fsend(OK_end);
260 }
261
262
263 /*
264  * Append Open session command
265  *
266  */
267 static bool append_open_session(JCR *jcr)
268 {
269    BSOCK *fd = jcr->file_bsock;
270
271    Dmsg1(120, "Append open session: %s", fd->msg);
272    if (jcr->session_opened) {
273       pm_strcpy(jcr->errmsg, _("Attempt to open already open session.\n"));
274       fd->fsend(NO_open);
275       return false;
276    }
277
278    jcr->session_opened = true;
279
280    /* Send "Ticket" to File Daemon */
281    fd->fsend(OK_open, jcr->VolSessionId);
282    Dmsg1(110, ">filed: %s", fd->msg);
283
284    return true;
285 }
286
287 /*
288  *   Append Close session command
289  *      Close the append session and send back Statistics
290  *         (need to fix statistics)
291  */
292 static bool append_close_session(JCR *jcr)
293 {
294    BSOCK *fd = jcr->file_bsock;
295
296    Dmsg1(120, "<filed: %s", fd->msg);
297    if (!jcr->session_opened) {
298       pm_strcpy(jcr->errmsg, _("Attempt to close non-open session.\n"));
299       fd->fsend(NOT_opened);
300       return false;
301    }
302    /* Send final statistics to File daemon */
303    fd->fsend(OK_close, jcr->JobStatus);
304    Dmsg1(120, ">filed: %s", fd->msg);
305
306    fd->signal(BNET_EOD);              /* send EOD to File daemon */
307    jcr->session_opened = false;
308    return true;
309 }
310
311 /*
312  *   Read Data command
313  *     Open Data Channel, read the data from
314  *     the archive device and send to File
315  *     daemon.
316  */
317 static bool read_data_cmd(JCR *jcr)
318 {
319    BSOCK *fd = jcr->file_bsock;
320
321    Dmsg1(120, "Read data: %s", fd->msg);
322    if (jcr->session_opened) {
323       Dmsg1(120, "<bfiled: %s", fd->msg);
324       return do_read_data(jcr);
325    } else {
326       pm_strcpy(jcr->errmsg, _("Attempt to read on non-open session.\n"));
327       fd->fsend(NOT_opened);
328       return false;
329    }
330 }
331
332 /*
333  * Read Open session command
334  *
335  *  We need to scan for the parameters of the job
336  *    to be restored.
337  */
338 static bool read_open_session(JCR *jcr)
339 {
340    BSOCK *fd = jcr->file_bsock;
341
342    Dmsg1(120, "%s", fd->msg);
343    if (jcr->session_opened) {
344       pm_strcpy(jcr->errmsg, _("Attempt to open an already open session.\n"));
345       fd->fsend(NO_open);
346       return false;
347    }
348
349    if (sscanf(fd->msg, read_open, jcr->read_dcr->VolumeName, &jcr->read_VolSessionId,
350          &jcr->read_VolSessionTime, &jcr->read_StartFile, &jcr->read_EndFile,
351          &jcr->read_StartBlock, &jcr->read_EndBlock) == 7) {
352       Dmsg4(100, "read_open_session got: JobId=%d Vol=%s VolSessId=%ld VolSessT=%ld\n",
353          jcr->JobId, jcr->read_dcr->VolumeName, jcr->read_VolSessionId,
354          jcr->read_VolSessionTime);
355       Dmsg4(100, "  StartF=%ld EndF=%ld StartB=%ld EndB=%ld\n",
356          jcr->read_StartFile, jcr->read_EndFile, jcr->read_StartBlock,
357          jcr->read_EndBlock);
358
359    } else {
360       pm_strcpy(jcr->errmsg, _("Cannot open session, received bad parameters.\n"));
361       fd->fsend(ERROR_open);
362       return false;
363    }
364
365    jcr->session_opened = true;
366    jcr->setJobType(JT_RESTORE);
367
368    /* Send "Ticket" to File Daemon */
369    fd->fsend(OK_open, jcr->VolSessionId);
370    Dmsg1(110, ">filed: %s", fd->msg);
371
372    return true;
373 }
374
375 /*
376  *   Read Close session command
377  *      Close the read session
378  */
379 static bool read_close_session(JCR *jcr)
380 {
381    BSOCK *fd = jcr->file_bsock;
382
383    Dmsg1(120, "Read close session: %s\n", fd->msg);
384    if (!jcr->session_opened) {
385       fd->fsend(NOT_opened);
386       return false;
387    }
388    /* Send final close msg to File daemon */
389    fd->fsend(OK_close, jcr->JobStatus);
390    Dmsg1(160, ">filed: %s\n", fd->msg);
391
392    fd->signal(BNET_EOD);            /* send EOD to File daemon */
393
394    jcr->session_opened = false;
395    return true;
396 }
397
398 /*
399  * Get response from FD or SD
400  * sent. Check that the response agrees with what we expect.
401  *
402  *  Returns: false on failure
403  *           true  on success
404  */
405 static bool response(JCR *jcr, BSOCK *bs, const char *resp, const char *cmd)
406 {
407    int n;
408
409    if (bs->is_error()) {
410       return false;
411    }
412    if ((n = bs->recv()) >= 0) {
413       if (strcmp(bs->msg, resp) == 0) {
414          return true;
415       }
416       Jmsg(jcr, M_FATAL, 0, _("Bad response to %s command: wanted %s, got %s\n"),
417             cmd, resp, bs->msg);
418       return false;
419    }
420    Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"),
421          cmd, bs->bstrerror());
422    return false;
423 }