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 JobId=%ld JobStatus=%d\n";
49 static char Device_update[] = "DevUpd JobId=%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);
95 * See if we are pointing to a message id
98 static bool is_msgid(char *msg)
100 if (!msg) return false;
101 char *end = strchr(msg, ']');
102 if (!end) return false;
103 if ((end - msg) != 7) return false;
104 if (!B_ISUPPER(msg[1]) || !B_ISUPPER(msg[2])) return false;
105 for (int i=3; i<7; i++) {
106 if (!B_ISDIGIT(msg[i])) return false;
113 * Call appropriate processing routine
114 * If it is not a Jmsg or a ReqCat message,
115 * return it to the caller.
117 * This routine is called to get the next message from
118 * another daemon. If the message is in canonical message
119 * format and the type is known, it will be dispatched
120 * to the appropriate handler. If the message is
121 * in any other format, it will be returned.
123 * E.g. any message beginning with a digit will be passed
124 * through to the caller.
125 * All other messages are expected begin with some identifier
126 * -- for the moment only the first character is checked, but
127 * at a later time, the whole identifier (e.g. Jmsg, CatReq, ...)
129 * This is followed by JobId=nnn <user-defined>
130 * info. The identifier is used to dispatch the message to the right
131 * place (Job message, catalog request, ...). The Job is used to lookup
132 * the JCR so that the action is performed on the correct jcr, and
133 * the rest of the message is up to the user. Note, DevUpd uses
134 * *System* for the Job name, and hence no JCR is obtained. This
135 * is a *rare* case where a jcr is not really needed.
138 int bget_dirmsg(BSOCK *bs)
140 int32_t n = BNET_TERMINATE;
141 char Job[MAX_NAME_LENGTH];
145 utime_t mtime; /* message time */
146 JCR *jcr = bs->jcr();
149 for ( ; !bs->is_stop() && !bs->is_timed_out(); ) {
151 Dmsg2(200, "bget_dirmsg %d: %s\n", n, bs->msg);
153 if (bs->is_stop() || bs->is_timed_out()) {
154 return n; /* error or terminate */
156 if (n == BNET_SIGNAL) { /* handle signal */
157 /* BNET_SIGNAL (-1) return from bnet_recv() => network signal */
158 switch (bs->msglen) {
159 case BNET_EOD: /* end of data */
162 bs->fsend(OK_msg);/* send response */
163 return n; /* end of data */
165 bs->set_terminated();
168 bs->fsend(OK_msg); /* send response */
171 // encode_time(time(NULL), Job);
172 // Dmsg1(100, "%s got heartbeat.\n", Job);
174 case BNET_HB_RESPONSE:
177 /* *****FIXME***** Implement more completely */
178 bs->fsend("Status OK\n");
179 bs->signal(BNET_EOD);
181 case BNET_BTIME: /* send Bacula time */
183 bs->fsend("btime %s\n", edit_uint64(get_current_btime(),ed1));
186 Jmsg1(jcr, M_WARNING, 0, _("bget_dirmsg: unknown bnet signal %d\n"), bs->msglen);
192 /* Handle normal data */
194 if (n > 0 && B_ISDIGIT(bs->msg[0])) { /* response? */
195 return n; /* yes, return it */
199 * If we get here, it must be a request. Either
200 * a message to dispatch, or a catalog request.
203 if ((sscanf(bs->msg, "%020s JobId=%ld ", MsgType, &JobId) != 2) &&
204 (sscanf(bs->msg, "%020s Job=%127s ", MsgType, Job) != 2) &&
205 (sscanf(bs->msg, "%020s Job=x", MsgType) != 1)) {
206 if (is_msgid(strchr(bs->msg, '['))) {
209 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
213 /* Skip past first two fields: "Jmsg JobId=nnn" */
214 if (!(msg=find_msg_start(bs->msg))) {
215 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
220 * Here we are expecting a message of the following format:
221 * Jmsg JobId=nnn type=nnn level=nnn Message-string
222 * Note, level should really be mtime, but that changes
225 if (bs->msg[0] == 'J') { /* Job message */
226 if ((sscanf(bs->msg, "Jmsg JobId=%ld type=%d level=%lld",
227 &JobId, &type, &mtime) != 3) &&
228 (sscanf(bs->msg, "Jmsg Job=%127s type=%d level=%lld",
229 Job, &type, &mtime) != 3)) {
230 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
233 Dmsg1(900, "Got msg: %s\n", bs->msg);
235 skip_nonspaces(&msg); /* skip type=nnn */
237 skip_nonspaces(&msg); /* skip level=nnn */
239 msg++; /* skip leading space */
241 Dmsg1(900, "Dispatch msg: %s", msg);
242 dispatch_message(jcr, type, mtime, msg);
246 * Here we expact a CatReq message
247 * CatReq JobId=nn Catalog-Request-Message
249 if (bs->msg[0] == 'C') { /* Catalog request */
250 Dmsg2(900, "Catalog req jcr=%p: %s", jcr, bs->msg);
251 catalog_request(jcr, bs);
254 if (bs->msg[0] == 'U') { /* SD sending attributes */
255 Dmsg2(900, "Catalog upd jcr=%p: %s", jcr, bs->msg);
256 catalog_update(jcr, bs);
259 if (bs->msg[0] == 'B') { /* SD sending file spool attributes */
260 Dmsg2(100, "Blast attributes jcr=%p: %s", jcr, bs->msg);
262 if (sscanf(bs->msg, "BlastAttr JobId=%ld File=%255s",
263 &JobId, filename) != 2) {
264 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
267 unbash_spaces(filename);
268 if (despool_attributes_from_file(jcr, filename)) {
269 bs->fsend("1000 OK BlastAttr\n");
271 bs->fsend("1990 ERROR BlastAttr\n");
275 if (bs->msg[0] == 'M') { /* Mount request */
276 Dmsg1(900, "Mount req: %s", bs->msg);
277 mount_request(jcr, bs, msg);
280 /* Get Progress: files, bytes, bytes/sec */
281 if (bs->msg[0] == 'P') { /* Progress report */
284 if ((sscanf(bs->msg, "Progress JobId=%ld files=%ld bytes=%lld bps=%ld\n",
285 &JobId, &files, &bytes, &bps) == 4) ||
286 (sscanf(bs->msg, "Progress JobId=x files=%ld bytes=%lld bps=%ld\n",
287 &files, &bytes, &bps) == 3) ||
288 (sscanf(bs->msg, "Progress Job=x files=%ld bytes=%lld bps=%ld\n",
289 &files, &bytes, &bps) == 3)) {
290 Dmsg2(900, "JobId=%d %s", jcr->JobId, bs->msg);
291 /* Save progress data */
292 jcr->JobFiles = files;
293 jcr->JobBytes = bytes;
296 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
300 if (bs->msg[0] == 'S') { /* Status change */
302 if (sscanf(bs->msg, Job_status, &JobId, &JobStatus) == 2) {
303 set_jcr_sd_job_status(jcr, JobStatus); /* current status */
305 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
310 /* No JCR for Device Updates! */
311 if (bs->msg[0] = 'D') { /* Device update */
313 POOL_MEM dev_name, changer_name, media_type, volume_name;
314 int dev_open, dev_append, dev_read, dev_labeled;
315 int dev_offline, dev_autochanger, dev_autoselect;
316 int dev_num_writers, dev_max_writers, dev_reserved;
317 uint64_t dev_read_time, dev_write_time, dev_write_bytes, dev_read_bytes;
319 Dmsg1(100, "<stored: %s", bs->msg);
320 if (sscanf(bs->msg, Device_update,
321 &Job, dev_name.c_str(),
322 &dev_append, &dev_read,
323 &dev_num_writers, &dev_open,
324 &dev_labeled, &dev_offline, &dev_reserved,
325 &dev_max_writers, &dev_autoselect,
327 changer_name.c_str(), media_type.c_str(),
329 &dev_read_time, &dev_write_time, &dev_read_bytes,
330 &dev_write_bytes) != 19) {
331 Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
333 unbash_spaces(dev_name);
334 dev = (DEVICE *)GetResWithName(R_DEVICE, dev_name.c_str());
338 unbash_spaces(changer_name);
339 unbash_spaces(media_type);
340 unbash_spaces(volume_name);
341 bstrncpy(dev->ChangerName, changer_name.c_str(), sizeof(dev->ChangerName));
342 bstrncpy(dev->MediaType, media_type.c_str(), sizeof(dev->MediaType));
343 bstrncpy(dev->VolumeName, volume_name.c_str(), sizeof(dev->VolumeName));
344 /* Note, these are copied because they are boolean rather than
347 dev->open = dev_open;
348 dev->append = dev_append;
349 dev->read = dev_read;
350 dev->labeled = dev_labeled;
351 dev->offline = dev_offline;
352 dev->autoselect = dev_autoselect;
353 dev->autochanger = dev_autochanger > 0;
354 dev->num_drives = dev_autochanger; /* does double duty */
355 dev->PoolId = dev_PoolId;
356 dev->num_writers = dev_num_writers;
357 dev->max_writers = dev_max_writers;
358 dev->reserved = dev_reserved;
360 dev->DevReadTime = dev_read_time; /* TODO : have to update database */
361 dev->DevWriteTime = dev_write_time;
362 dev->DevReadBytes = dev_read_bytes;
363 dev->DevWriteBytes = dev_write_bytes;
373 static char *find_msg_start(char *msg)
377 skip_nonspaces(&p); /* skip message type */
379 skip_nonspaces(&p); /* skip Job */
380 skip_spaces(&p); /* after spaces come the message */
385 * Get response from FD or SD to a command we
386 * sent. Check that the response agrees with what we expect.
388 * Returns: false on failure
391 bool response(JCR *jcr, BSOCK *bs, char *resp, const char *cmd, e_prtmsg prtmsg)
395 if (bs->is_error()) {
398 if ((n = bget_dirmsg(bs)) >= 0) {
399 if (strcmp(bs->msg, resp) == 0) {
402 if (prtmsg == DISPLAY_ERROR) {
403 Jmsg(jcr, M_FATAL, 0, _("Bad response to %s command: wanted %s, got %s\n"),
408 Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"),
409 cmd, bs->bstrerror());