2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
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.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Bacula Director -- routines to receive network data and
22 * handle network signals. These routines handle the connections
23 * to the Storage daemon and the File daemon.
25 * Kern Sibbald, August MM
27 * This routine runs as a thread and must be thread reentrant.
29 * Basic tasks done here:
30 * Handle network signals (signals).
31 * Signals always have return status 0 from bnet_recv() and
32 * a zero or negative message length.
33 * Pass appropriate messages back to the caller (responses).
34 * Responses always have a digit as the first character.
35 * Handle requests for message and catalog services (requests).
36 * Requests are any message that does not begin with a digit.
37 * In affect, they are commands.
44 /* Forward referenced functions */
45 static char *find_msg_start(char *msg);
47 static char Job_status[] = "Status Job=%127s JobStatus=%d\n";
49 static char Device_update[] = "DevUpd Job=%127s "
51 "append=%d read=%d num_writers=%d "
52 "open=%d labeled=%d offline=%d "
53 "reserved=%d max_writers=%d "
54 "autoselect=%d autochanger=%d "
55 "changer_name=%127s media_type=%127s volume_name=%127s "
56 "DevReadTime=%d DevWriteTime=%d DevReadBytes=%d "
61 static char OK_msg[] = "1000 OK\n";
64 static void set_jcr_sd_job_status(JCR *jcr, int SDJobStatus)
66 bool set_waittime=false;
67 Dmsg2(800, "set_jcr_sd_job_status(%s, %c)\n", jcr->Job, SDJobStatus);
68 /* if wait state is new, we keep current time for watchdog MaxWaitTime */
69 switch (SDJobStatus) {
78 if (job_waiting(jcr)) {
83 /* set it before JobStatus */
84 Dmsg0(800, "Setting wait_time\n");
85 jcr->wait_time = time(NULL);
87 jcr->SDJobStatus = SDJobStatus;
88 if (jcr->SDJobStatus == JS_Incomplete) {
89 jcr->setJobStatus(JS_Incomplete);
96 * Call appropriate processing routine
97 * If it is not a Jmsg or a ReqCat message,
98 * return it to the caller.
100 * This routine is called to get the next message from
101 * another daemon. If the message is in canonical message
102 * format and the type is known, it will be dispatched
103 * to the appropriate handler. If the message is
104 * in any other format, it will be returned.
106 * E.g. any message beginning with a digit will be passed
107 * through to the caller.
108 * All other messages are expected begin with some identifier
109 * -- for the moment only the first character is checked, but
110 * at a later time, the whole identifier (e.g. Jmsg, CatReq, ...)
111 * could be checked. This is followed by Job=Jobname <user-defined>
112 * info. The identifier is used to dispatch the message to the right
113 * place (Job message, catalog request, ...). The Job is used to lookup
114 * the JCR so that the action is performed on the correct jcr, and
115 * the rest of the message is up to the user. Note, DevUpd uses
116 * *System* for the Job name, and hence no JCR is obtained. This
117 * is a *rare* case where a jcr is not really needed.
120 int bget_dirmsg(BSOCK *bs)
122 int32_t n = BNET_TERMINATE;
123 char Job[MAX_NAME_LENGTH];
126 utime_t mtime; /* message time */
127 JCR *jcr = bs->jcr();
130 for ( ; !bs->is_stop() && !bs->is_timed_out(); ) {
132 Dmsg2(200, "bget_dirmsg %d: %s\n", n, bs->msg);
134 if (bs->is_stop() || bs->is_timed_out()) {
135 return n; /* error or terminate */
137 if (n == BNET_SIGNAL) { /* handle signal */
138 /* BNET_SIGNAL (-1) return from bnet_recv() => network signal */
139 switch (bs->msglen) {
140 case BNET_EOD: /* end of data */
143 bs->fsend(OK_msg);/* send response */
144 return n; /* end of data */
146 bs->set_terminated();
149 bs->fsend(OK_msg); /* send response */
152 // encode_time(time(NULL), Job);
153 // Dmsg1(100, "%s got heartbeat.\n", Job);
155 case BNET_HB_RESPONSE:
158 /* *****FIXME***** Implement more completely */
159 bs->fsend("Status OK\n");
160 bs->signal(BNET_EOD);
162 case BNET_BTIME: /* send Bacula time */
164 bs->fsend("btime %s\n", edit_uint64(get_current_btime(),ed1));
167 Jmsg1(jcr, M_WARNING, 0, _("bget_dirmsg: unknown bnet signal %d\n"), bs->msglen);
173 /* Handle normal data */
175 if (n > 0 && B_ISDIGIT(bs->msg[0])) { /* response? */
176 return n; /* yes, return it */
180 * If we get here, it must be a request. Either
181 * a message to dispatch, or a catalog request.
184 if (sscanf(bs->msg, "%020s Job=%127s ", MsgType, Job) != 2) {
185 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
189 /* Skip past "Jmsg Job=nnn" */
190 if (!(msg=find_msg_start(bs->msg))) {
191 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
196 * Here we are expecting a message of the following format:
197 * Jmsg Job=nnn type=nnn level=nnn Message-string
198 * Note, level should really be mtime, but that changes
201 if (bs->msg[0] == 'J') { /* Job message */
202 if (sscanf(bs->msg, "Jmsg Job=%127s type=%d level=%lld",
203 Job, &type, &mtime) != 3) {
204 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
207 Dmsg1(900, "Got msg: %s\n", bs->msg);
209 skip_nonspaces(&msg); /* skip type=nnn */
211 skip_nonspaces(&msg); /* skip level=nnn */
213 msg++; /* skip leading space */
215 Dmsg1(900, "Dispatch msg: %s", msg);
216 dispatch_message(jcr, type, mtime, msg);
220 * Here we expact a CatReq message
221 * CatReq Job=nn Catalog-Request-Message
223 if (bs->msg[0] == 'C') { /* Catalog request */
224 Dmsg2(900, "Catalog req jcr=%p: %s", jcr, bs->msg);
225 catalog_request(jcr, bs);
228 if (bs->msg[0] == 'U') { /* SD sending attributes */
229 Dmsg2(900, "Catalog upd jcr=%p: %s", jcr, bs->msg);
230 catalog_update(jcr, bs);
233 if (bs->msg[0] == 'B') { /* SD sending file spool attributes */
234 Dmsg2(100, "Blast attributes jcr=%p: %s", jcr, bs->msg);
236 if (sscanf(bs->msg, "BlastAttr Job=%127s File=%255s",
237 Job, filename) != 2) {
238 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
241 unbash_spaces(filename);
242 if (despool_attributes_from_file(jcr, filename)) {
243 bs->fsend("1000 OK BlastAttr\n");
245 bs->fsend("1990 ERROR BlastAttr\n");
249 if (bs->msg[0] == 'M') { /* Mount request */
250 Dmsg1(900, "Mount req: %s", bs->msg);
251 mount_request(jcr, bs, msg);
254 /* Get Progress: files, bytes, bytes/sec */
255 if (bs->msg[0] == 'P') { /* Progress report */
258 if (sscanf(bs->msg, "Progress Job=x files=%ld bytes=%lld bps=%ld\n",
259 &files, &bytes, &bps) == 3) {
260 Dmsg2(900, "JobId=%d %s", jcr->JobId, bs->msg);
261 /* Save progress data */
262 jcr->JobFiles = files;
263 jcr->JobBytes = bytes;
268 if (bs->msg[0] == 'S') { /* Status change */
270 char Job[MAX_NAME_LENGTH];
271 if (sscanf(bs->msg, Job_status, &Job, &JobStatus) == 2) {
272 set_jcr_sd_job_status(jcr, JobStatus); /* current status */
274 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
279 /* No JCR for Device Updates! */
280 if (bs->msg[0] = 'D') { /* Device update */
282 POOL_MEM dev_name, changer_name, media_type, volume_name;
283 int dev_open, dev_append, dev_read, dev_labeled;
284 int dev_offline, dev_autochanger, dev_autoselect;
285 int dev_num_writers, dev_max_writers, dev_reserved;
286 uint64_t dev_read_time, dev_write_time, dev_write_bytes, dev_read_bytes;
288 Dmsg1(100, "<stored: %s", bs->msg);
289 if (sscanf(bs->msg, Device_update,
290 &Job, dev_name.c_str(),
291 &dev_append, &dev_read,
292 &dev_num_writers, &dev_open,
293 &dev_labeled, &dev_offline, &dev_reserved,
294 &dev_max_writers, &dev_autoselect,
296 changer_name.c_str(), media_type.c_str(),
298 &dev_read_time, &dev_write_time, &dev_read_bytes,
299 &dev_write_bytes) != 19) {
300 Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
302 unbash_spaces(dev_name);
303 dev = (DEVICE *)GetResWithName(R_DEVICE, dev_name.c_str());
307 unbash_spaces(changer_name);
308 unbash_spaces(media_type);
309 unbash_spaces(volume_name);
310 bstrncpy(dev->ChangerName, changer_name.c_str(), sizeof(dev->ChangerName));
311 bstrncpy(dev->MediaType, media_type.c_str(), sizeof(dev->MediaType));
312 bstrncpy(dev->VolumeName, volume_name.c_str(), sizeof(dev->VolumeName));
313 /* Note, these are copied because they are boolean rather than
316 dev->open = dev_open;
317 dev->append = dev_append;
318 dev->read = dev_read;
319 dev->labeled = dev_labeled;
320 dev->offline = dev_offline;
321 dev->autoselect = dev_autoselect;
322 dev->autochanger = dev_autochanger > 0;
323 dev->num_drives = dev_autochanger; /* does double duty */
324 dev->PoolId = dev_PoolId;
325 dev->num_writers = dev_num_writers;
326 dev->max_writers = dev_max_writers;
327 dev->reserved = dev_reserved;
329 dev->DevReadTime = dev_read_time; /* TODO : have to update database */
330 dev->DevWriteTime = dev_write_time;
331 dev->DevReadBytes = dev_read_bytes;
332 dev->DevWriteBytes = dev_write_bytes;
342 static char *find_msg_start(char *msg)
346 skip_nonspaces(&p); /* skip message type */
348 skip_nonspaces(&p); /* skip Job */
349 skip_spaces(&p); /* after spaces come the message */
354 * Get response from FD or SD to a command we
355 * sent. Check that the response agrees with what we expect.
357 * Returns: false on failure
360 bool response(JCR *jcr, BSOCK *bs, char *resp, const char *cmd, e_prtmsg prtmsg)
364 if (bs->is_error()) {
367 if ((n = bget_dirmsg(bs)) >= 0) {
368 if (strcmp(bs->msg, resp) == 0) {
371 if (prtmsg == DISPLAY_ERROR) {
372 Jmsg(jcr, M_FATAL, 0, _("Bad response to %s command: wanted %s, got %s\n"),
377 Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"),
378 cmd, bs->bstrerror());