2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
22 * Bacula Director -- routines to receive network data and
23 * handle network signals. These routines handle the connections
24 * to the Storage daemon and the File daemon.
26 * Kern Sibbald, August MM
28 * This routine runs as a thread and must be thread reentrant.
30 * Basic tasks done here:
31 * Handle network signals (signals).
32 * Signals always have return status 0 from bnet_recv() and
33 * a zero or negative message length.
34 * Pass appropriate messages back to the caller (responses).
35 * Responses always have a digit as the first character.
36 * Handle requests for message and catalog services (requests).
37 * Requests are any message that does not begin with a digit.
38 * In affect, they are commands.
45 /* Forward referenced functions */
46 static char *find_msg_start(char *msg);
48 static char Job_status[] = "Status Job=%127s JobStatus=%d\n";
50 static char Device_update[] = "DevUpd Job=%127s "
52 "append=%d read=%d num_writers=%d "
53 "open=%d labeled=%d offline=%d "
54 "reserved=%d max_writers=%d "
55 "autoselect=%d autochanger=%d "
56 "changer_name=%127s media_type=%127s volume_name=%127s "
57 "DevReadTime=%d DevWriteTime=%d DevReadBytes=%d "
62 static char OK_msg[] = "1000 OK\n";
65 static void set_jcr_sd_job_status(JCR *jcr, int SDJobStatus)
67 bool set_waittime=false;
68 Dmsg2(800, "set_jcr_sd_job_status(%s, %c)\n", jcr->Job, SDJobStatus);
69 /* if wait state is new, we keep current time for watchdog MaxWaitTime */
70 switch (SDJobStatus) {
79 if (job_waiting(jcr)) {
84 /* set it before JobStatus */
85 Dmsg0(800, "Setting wait_time\n");
86 jcr->wait_time = time(NULL);
88 jcr->SDJobStatus = SDJobStatus;
89 if (jcr->SDJobStatus == JS_Incomplete) {
90 jcr->setJobStatus(JS_Incomplete);
97 * Call appropriate processing routine
98 * If it is not a Jmsg or a ReqCat message,
99 * return it to the caller.
101 * This routine is called to get the next message from
102 * another daemon. If the message is in canonical message
103 * format and the type is known, it will be dispatched
104 * to the appropriate handler. If the message is
105 * in any other format, it will be returned.
107 * E.g. any message beginning with a digit will be passed
108 * through to the caller.
109 * All other messages are expected begin with some identifier
110 * -- for the moment only the first character is checked, but
111 * at a later time, the whole identifier (e.g. Jmsg, CatReq, ...)
112 * could be checked. This is followed by Job=Jobname <user-defined>
113 * info. The identifier is used to dispatch the message to the right
114 * place (Job message, catalog request, ...). The Job is used to lookup
115 * the JCR so that the action is performed on the correct jcr, and
116 * the rest of the message is up to the user. Note, DevUpd uses
117 * *System* for the Job name, and hence no JCR is obtained. This
118 * is a *rare* case where a jcr is not really needed.
121 int bget_dirmsg(BSOCK *bs)
123 int32_t n = BNET_TERMINATE;
124 char Job[MAX_NAME_LENGTH];
127 utime_t mtime; /* message time */
128 JCR *jcr = bs->jcr();
131 for ( ; !bs->is_stop() && !bs->is_timed_out(); ) {
133 Dmsg2(200, "bget_dirmsg %d: %s\n", n, bs->msg);
135 if (bs->is_stop() || bs->is_timed_out()) {
136 return n; /* error or terminate */
138 if (n == BNET_SIGNAL) { /* handle signal */
139 /* BNET_SIGNAL (-1) return from bnet_recv() => network signal */
140 switch (bs->msglen) {
141 case BNET_EOD: /* end of data */
144 bs->fsend(OK_msg);/* send response */
145 return n; /* end of data */
147 bs->set_terminated();
150 bs->fsend(OK_msg); /* send response */
153 // encode_time(time(NULL), Job);
154 // Dmsg1(100, "%s got heartbeat.\n", Job);
156 case BNET_HB_RESPONSE:
159 /* *****FIXME***** Implement more completely */
160 bs->fsend("Status OK\n");
161 bs->signal(BNET_EOD);
163 case BNET_BTIME: /* send Bacula time */
165 bs->fsend("btime %s\n", edit_uint64(get_current_btime(),ed1));
168 Jmsg1(jcr, M_WARNING, 0, _("bget_dirmsg: unknown bnet signal %d\n"), bs->msglen);
174 /* Handle normal data */
176 if (n > 0 && B_ISDIGIT(bs->msg[0])) { /* response? */
177 return n; /* yes, return it */
181 * If we get here, it must be a request. Either
182 * a message to dispatch, or a catalog request.
185 if (sscanf(bs->msg, "%020s Job=%127s ", MsgType, Job) != 2) {
186 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
190 /* Skip past "Jmsg Job=nnn" */
191 if (!(msg=find_msg_start(bs->msg))) {
192 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
197 * Here we are expecting a message of the following format:
198 * Jmsg Job=nnn type=nnn level=nnn Message-string
199 * Note, level should really be mtime, but that changes
202 if (bs->msg[0] == 'J') { /* Job message */
203 if (sscanf(bs->msg, "Jmsg Job=%127s type=%d level=%lld",
204 Job, &type, &mtime) != 3) {
205 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
208 Dmsg1(900, "Got msg: %s\n", bs->msg);
210 skip_nonspaces(&msg); /* skip type=nnn */
212 skip_nonspaces(&msg); /* skip level=nnn */
214 msg++; /* skip leading space */
216 Dmsg1(900, "Dispatch msg: %s", msg);
217 dispatch_message(jcr, type, mtime, msg);
221 * Here we expact a CatReq message
222 * CatReq Job=nn Catalog-Request-Message
224 if (bs->msg[0] == 'C') { /* Catalog request */
225 Dmsg2(900, "Catalog req jcr=%p: %s", jcr, bs->msg);
226 catalog_request(jcr, bs);
229 if (bs->msg[0] == 'U') { /* SD sending attributes */
230 Dmsg2(900, "Catalog upd jcr=%p: %s", jcr, bs->msg);
231 catalog_update(jcr, bs);
234 if (bs->msg[0] == 'B') { /* SD sending file spool attributes */
235 Dmsg2(100, "Blast attributes jcr=%p: %s", jcr, bs->msg);
237 if (sscanf(bs->msg, "BlastAttr Job=%127s File=%255s",
238 Job, filename) != 2) {
239 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
242 unbash_spaces(filename);
243 if (despool_attributes_from_file(jcr, filename)) {
244 bs->fsend("1000 OK BlastAttr\n");
246 bs->fsend("1990 ERROR BlastAttr\n");
250 if (bs->msg[0] == 'M') { /* Mount request */
251 Dmsg1(900, "Mount req: %s", bs->msg);
252 mount_request(jcr, bs, msg);
255 /* Get Progress: files, bytes, bytes/sec */
256 if (bs->msg[0] == 'P') { /* Progress report */
259 if (sscanf(bs->msg, "Progress Job=x files=%ld bytes=%lld bps=%ld\n",
260 &files, &bytes, &bps) == 3) {
261 Dmsg2(900, "JobId=%d %s", jcr->JobId, bs->msg);
262 /* Save progress data */
263 jcr->JobFiles = files;
264 jcr->JobBytes = bytes;
269 if (bs->msg[0] == 'S') { /* Status change */
271 char Job[MAX_NAME_LENGTH];
272 if (sscanf(bs->msg, Job_status, &Job, &JobStatus) == 2) {
273 set_jcr_sd_job_status(jcr, JobStatus); /* current status */
275 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
280 /* No JCR for Device Updates! */
281 if (bs->msg[0] = 'D') { /* Device update */
283 POOL_MEM dev_name, changer_name, media_type, volume_name;
284 int dev_open, dev_append, dev_read, dev_labeled;
285 int dev_offline, dev_autochanger, dev_autoselect;
286 int dev_num_writers, dev_max_writers, dev_reserved;
287 uint64_t dev_read_time, dev_write_time, dev_write_bytes, dev_read_bytes;
289 Dmsg1(100, "<stored: %s", bs->msg);
290 if (sscanf(bs->msg, Device_update,
291 &Job, dev_name.c_str(),
292 &dev_append, &dev_read,
293 &dev_num_writers, &dev_open,
294 &dev_labeled, &dev_offline, &dev_reserved,
295 &dev_max_writers, &dev_autoselect,
297 changer_name.c_str(), media_type.c_str(),
299 &dev_read_time, &dev_write_time, &dev_read_bytes,
300 &dev_write_bytes) != 19) {
301 Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
303 unbash_spaces(dev_name);
304 dev = (DEVICE *)GetResWithName(R_DEVICE, dev_name.c_str());
308 unbash_spaces(changer_name);
309 unbash_spaces(media_type);
310 unbash_spaces(volume_name);
311 bstrncpy(dev->ChangerName, changer_name.c_str(), sizeof(dev->ChangerName));
312 bstrncpy(dev->MediaType, media_type.c_str(), sizeof(dev->MediaType));
313 bstrncpy(dev->VolumeName, volume_name.c_str(), sizeof(dev->VolumeName));
314 /* Note, these are copied because they are boolean rather than
317 dev->open = dev_open;
318 dev->append = dev_append;
319 dev->read = dev_read;
320 dev->labeled = dev_labeled;
321 dev->offline = dev_offline;
322 dev->autoselect = dev_autoselect;
323 dev->autochanger = dev_autochanger > 0;
324 dev->num_drives = dev_autochanger; /* does double duty */
325 dev->PoolId = dev_PoolId;
326 dev->num_writers = dev_num_writers;
327 dev->max_writers = dev_max_writers;
328 dev->reserved = dev_reserved;
330 dev->DevReadTime = dev_read_time; /* TODO : have to update database */
331 dev->DevWriteTime = dev_write_time;
332 dev->DevReadBytes = dev_read_bytes;
333 dev->DevWriteBytes = dev_write_bytes;
343 static char *find_msg_start(char *msg)
347 skip_nonspaces(&p); /* skip message type */
349 skip_nonspaces(&p); /* skip Job */
350 skip_spaces(&p); /* after spaces come the message */
355 * Get response from FD or SD to a command we
356 * sent. Check that the response agrees with what we expect.
358 * Returns: false on failure
361 bool response(JCR *jcr, BSOCK *bs, char *resp, const char *cmd, e_prtmsg prtmsg)
365 if (bs->is_error()) {
368 if ((n = bget_dirmsg(bs)) >= 0) {
369 if (strcmp(bs->msg, resp) == 0) {
372 if (prtmsg == DISPLAY_ERROR) {
373 Jmsg(jcr, M_FATAL, 0, _("Bad response to %s command: wanted %s, got %s\n"),
378 Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"),
379 cmd, bs->bstrerror());