2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 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 Bacula® is a registered trademark of Kern Sibbald.
18 * Bacula Director -- routines to receive network data and
19 * handle network signals. These routines handle the connections
20 * to the Storage daemon and the File daemon.
22 * Kern Sibbald, August MM
24 * This routine runs as a thread and must be thread reentrant.
26 * Basic tasks done here:
27 * Handle network signals (signals).
28 * Signals always have return status 0 from bnet_recv() and
29 * a zero or negative message length.
30 * Pass appropriate messages back to the caller (responses).
31 * Responses always have a digit as the first character.
32 * Handle requests for message and catalog services (requests).
33 * Requests are any message that does not begin with a digit.
34 * In affect, they are commands.
41 /* Forward referenced functions */
42 static char *find_msg_start(char *msg);
44 static char Job_status[] = "Status Job=%127s JobStatus=%d\n";
46 static char Device_update[] = "DevUpd Job=%127s "
48 "append=%d read=%d num_writers=%d "
49 "open=%d labeled=%d offline=%d "
50 "reserved=%d max_writers=%d "
51 "autoselect=%d autochanger=%d "
52 "changer_name=%127s media_type=%127s volume_name=%127s "
53 "DevReadTime=%d DevWriteTime=%d DevReadBytes=%d "
58 static char OK_msg[] = "1000 OK\n";
61 static void set_jcr_sd_job_status(JCR *jcr, int SDJobStatus)
63 bool set_waittime=false;
64 Dmsg2(800, "set_jcr_sd_job_status(%s, %c)\n", jcr->Job, SDJobStatus);
65 /* if wait state is new, we keep current time for watchdog MaxWaitTime */
66 switch (SDJobStatus) {
75 if (job_waiting(jcr)) {
80 /* set it before JobStatus */
81 Dmsg0(800, "Setting wait_time\n");
82 jcr->wait_time = time(NULL);
84 jcr->SDJobStatus = SDJobStatus;
89 * Call appropriate processing routine
90 * If it is not a Jmsg or a ReqCat message,
91 * return it to the caller.
93 * This routine is called to get the next message from
94 * another daemon. If the message is in canonical message
95 * format and the type is known, it will be dispatched
96 * to the appropriate handler. If the message is
97 * in any other format, it will be returned.
99 * E.g. any message beginning with a digit will be passed
100 * through to the caller.
101 * All other messages are expected begin with some identifier
102 * -- for the moment only the first character is checked, but
103 * at a later time, the whole identifier (e.g. Jmsg, CatReq, ...)
104 * could be checked. This is followed by Job=Jobname <user-defined>
105 * info. The identifier is used to dispatch the message to the right
106 * place (Job message, catalog request, ...). The Job is used to lookup
107 * the JCR so that the action is performed on the correct jcr, and
108 * the rest of the message is up to the user. Note, DevUpd uses
109 * *System* for the Job name, and hence no JCR is obtained. This
110 * is a *rare* case where a jcr is not really needed.
113 int bget_dirmsg(BSOCK *bs)
115 int32_t n = BNET_TERMINATE;
116 char Job[MAX_NAME_LENGTH];
119 utime_t mtime; /* message time */
120 JCR *jcr = bs->jcr();
123 for ( ; !bs->is_stop() && !bs->is_timed_out(); ) {
125 Dmsg2(200, "bget_dirmsg %d: %s\n", n, bs->msg);
127 if (bs->is_stop() || bs->is_timed_out()) {
128 return n; /* error or terminate */
130 if (n == BNET_SIGNAL) { /* handle signal */
131 /* BNET_SIGNAL (-1) return from bnet_recv() => network signal */
132 switch (bs->msglen) {
133 case BNET_EOD: /* end of data */
136 bs->fsend(OK_msg);/* send response */
137 return n; /* end of data */
139 bs->set_terminated();
142 bs->fsend(OK_msg); /* send response */
145 // encode_time(time(NULL), Job);
146 // Dmsg1(100, "%s got heartbeat.\n", Job);
148 case BNET_HB_RESPONSE:
151 /* *****FIXME***** Implement more completely */
152 bs->fsend("Status OK\n");
153 bs->signal(BNET_EOD);
155 case BNET_BTIME: /* send Bacula time */
157 bs->fsend("btime %s\n", edit_uint64(get_current_btime(),ed1));
160 Jmsg1(jcr, M_WARNING, 0, _("bget_dirmsg: unknown bnet signal %d\n"), bs->msglen);
166 /* Handle normal data */
168 if (n > 0 && B_ISDIGIT(bs->msg[0])) { /* response? */
169 return n; /* yes, return it */
173 * If we get here, it must be a request. Either
174 * a message to dispatch, or a catalog request.
177 if (sscanf(bs->msg, "%020s Job=%127s ", MsgType, Job) != 2) {
178 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
182 /* Skip past "Jmsg Job=nnn" */
183 if (!(msg=find_msg_start(bs->msg))) {
184 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
189 * Here we are expecting a message of the following format:
190 * Jmsg Job=nnn type=nnn level=nnn Message-string
191 * Note, level should really be mtime, but that changes
194 if (bs->msg[0] == 'J') { /* Job message */
195 if (sscanf(bs->msg, "Jmsg Job=%127s type=%d level=%lld",
196 Job, &type, &mtime) != 3) {
197 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
200 Dmsg1(900, "Got msg: %s\n", bs->msg);
202 skip_nonspaces(&msg); /* skip type=nnn */
204 skip_nonspaces(&msg); /* skip level=nnn */
206 msg++; /* skip leading space */
208 Dmsg1(900, "Dispatch msg: %s", msg);
209 dispatch_message(jcr, type, mtime, msg);
213 * Here we expact a CatReq message
214 * CatReq Job=nn Catalog-Request-Message
216 if (bs->msg[0] == 'C') { /* Catalog request */
217 Dmsg2(900, "Catalog req jcr 0x%x: %s", jcr, bs->msg);
218 catalog_request(jcr, bs);
221 if (bs->msg[0] == 'U') { /* SD sending attributes */
222 Dmsg2(900, "Catalog upd jcr 0x%x: %s", jcr, bs->msg);
223 catalog_update(jcr, bs);
226 if (bs->msg[0] == 'B') { /* SD sending file spool attributes */
227 Dmsg2(100, "Blast attributes jcr 0x%x: %s", jcr, bs->msg);
229 if (sscanf(bs->msg, "BlastAttr Job=%127s File=%255s",
230 Job, filename) != 2) {
231 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
234 unbash_spaces(filename);
235 if (despool_attributes_from_file(jcr, filename)) {
236 bs->fsend("1000 OK BlastAttr\n");
238 bs->fsend("1990 ERROR BlastAttr\n");
242 if (bs->msg[0] == 'M') { /* Mount request */
243 Dmsg1(900, "Mount req: %s", bs->msg);
244 mount_request(jcr, bs, msg);
247 /* Get Progress: files, bytes, bytes/sec */
248 if (bs->msg[0] == 'P') { /* Progress report */
251 if (sscanf(bs->msg, "Progress Job=x files=%ld bytes=%lld bps=%ld\n",
252 &files, &bytes, &bps) == 3) {
253 Dmsg2(900, "JobId=%d %s", jcr->JobId, bs->msg);
254 /* Save progress data */
255 jcr->JobFiles = files;
256 jcr->JobBytes = bytes;
261 if (bs->msg[0] == 'S') { /* Status change */
263 char Job[MAX_NAME_LENGTH];
264 if (sscanf(bs->msg, Job_status, &Job, &JobStatus) == 2) {
265 set_jcr_sd_job_status(jcr, JobStatus); /* current status */
267 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
272 /* No JCR for Device Updates! */
273 if (bs->msg[0] = 'D') { /* Device update */
275 POOL_MEM dev_name, changer_name, media_type, volume_name;
276 int dev_open, dev_append, dev_read, dev_labeled;
277 int dev_offline, dev_autochanger, dev_autoselect;
278 int dev_num_writers, dev_max_writers, dev_reserved;
279 uint64_t dev_read_time, dev_write_time, dev_write_bytes, dev_read_bytes;
281 Dmsg1(100, "<stored: %s", bs->msg);
282 if (sscanf(bs->msg, Device_update,
283 &Job, dev_name.c_str(),
284 &dev_append, &dev_read,
285 &dev_num_writers, &dev_open,
286 &dev_labeled, &dev_offline, &dev_reserved,
287 &dev_max_writers, &dev_autoselect,
289 changer_name.c_str(), media_type.c_str(),
291 &dev_read_time, &dev_write_time, &dev_read_bytes,
292 &dev_write_bytes) != 19) {
293 Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
295 unbash_spaces(dev_name);
296 dev = (DEVICE *)GetResWithName(R_DEVICE, dev_name.c_str());
300 unbash_spaces(changer_name);
301 unbash_spaces(media_type);
302 unbash_spaces(volume_name);
303 bstrncpy(dev->ChangerName, changer_name.c_str(), sizeof(dev->ChangerName));
304 bstrncpy(dev->MediaType, media_type.c_str(), sizeof(dev->MediaType));
305 bstrncpy(dev->VolumeName, volume_name.c_str(), sizeof(dev->VolumeName));
306 /* Note, these are copied because they are boolean rather than
309 dev->open = dev_open;
310 dev->append = dev_append;
311 dev->read = dev_read;
312 dev->labeled = dev_labeled;
313 dev->offline = dev_offline;
314 dev->autoselect = dev_autoselect;
315 dev->autochanger = dev_autochanger > 0;
316 dev->num_drives = dev_autochanger; /* does double duty */
317 dev->PoolId = dev_PoolId;
318 dev->num_writers = dev_num_writers;
319 dev->max_writers = dev_max_writers;
320 dev->reserved = dev_reserved;
322 dev->DevReadTime = dev_read_time; /* TODO : have to update database */
323 dev->DevWriteTime = dev_write_time;
324 dev->DevReadBytes = dev_read_bytes;
325 dev->DevWriteBytes = dev_write_bytes;
335 static char *find_msg_start(char *msg)
339 skip_nonspaces(&p); /* skip message type */
341 skip_nonspaces(&p); /* skip Job */
342 skip_spaces(&p); /* after spaces come the message */
347 * Get response from FD or SD to a command we
348 * sent. Check that the response agrees with what we expect.
350 * Returns: false on failure
353 bool response(JCR *jcr, BSOCK *bs, char *resp, const char *cmd, e_prtmsg prtmsg)
357 if (bs->is_error()) {
360 if ((n = bget_dirmsg(bs)) >= 0) {
361 if (strcmp(bs->msg, resp) == 0) {
364 if (prtmsg == DISPLAY_ERROR) {
365 Jmsg(jcr, M_FATAL, 0, _("Bad response to %s command: wanted %s, got %s\n"),
370 Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"),
371 cmd, bs->bstrerror());