1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2012 Planets Communications B.V.
6 Copyright (C) 2013-2018 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
11 in the file LICENSE.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301, USA.
22 */
23 /*
24 * Kern Sibbald, August MM
25 */
26 /**
27 * @file
28 * routines to receive network data and
29 * handle network signals. These routines handle the connections
30 * to the Storage daemon and the File daemon.
31 *
32 * This routine runs as a thread and must be thread reentrant.
33 *
34 * Basic tasks done here:
35 * Handle network signals (signals).
36 * Signals always have return status 0 from BnetRecv() and
37 * a zero or negative message length.
38 * Pass appropriate messages back to the caller (responses).
39 * Responses always have a digit as the first character.
40 * Handle requests for message and catalog services (requests).
41 * Requests are any message that does not begin with a digit.
42 * In affect, they are commands.
43 */
44
45 #include "include/bareos.h"
46 #include "dird.h"
47 #include "dird/catreq.h"
48 #include "dird/mountreq.h"
49 #include "dird/msgchan.h"
50 #include "lib/bnet.h"
51 #include "lib/edit.h"
52
53 namespace directordaemon {
54
55 /* Forward referenced functions */
56 static char *find_msg_start(char *msg);
57
58 static char Job_status[] =
59 "Status Job=%127s JobStatus=%d\n";
60 #ifdef needed
61 static char Device_update[] =
62 "DevUpd Job=%127s "
63 "device=%127s "
64 "append=%d read=%d num_writers=%d "
65 "open=%d labeled=%d offline=%d "
66 "reserved=%d max_writers=%d "
67 "autoselect=%d autochanger=%d "
68 "changer_name=%127s media_type=%127s volume_name=%127s "
69 "DevReadTime=%d DevWriteTime=%d DevReadBytes=%d "
70 "DevWriteBytes=%d\n";
71 #endif
72
73 static char OK_msg[] =
74 "1000 OK\n";
75
SetJcrSdJobStatus(JobControlRecord * jcr,int SDJobStatus)76 static void SetJcrSdJobStatus(JobControlRecord *jcr, int SDJobStatus)
77 {
78 bool set_waittime = false;
79
80 Dmsg2(800, "SetJcrSdJobStatus(%s, %c)\n", jcr->Job, SDJobStatus);
81
82 /*
83 * If wait state is new, we keep current time for watchdog MaxWaitTime
84 */
85 switch (SDJobStatus) {
86 case JS_WaitMedia:
87 case JS_WaitMount:
88 case JS_WaitMaxJobs:
89 set_waittime = true;
90 default:
91 break;
92 }
93
94 if (JobWaiting(jcr)) {
95 set_waittime = false;
96 }
97
98 if (set_waittime) {
99 /*
100 * Set it before JobStatus
101 */
102 Dmsg0(800, "Setting wait_time\n");
103 jcr->wait_time = time(NULL);
104 }
105 jcr->SDJobStatus = SDJobStatus;
106
107 /*
108 * Some SD Job status setting are propagated to the controlling Job.
109 */
110 switch (jcr->SDJobStatus) {
111 case JS_Incomplete:
112 jcr->setJobStatus(JS_Incomplete);
113 break;
114 case JS_FatalError:
115 jcr->setJobStatus(JS_FatalError);
116 break;
117 default:
118 break;
119 }
120 }
121
122 /**
123 * Get a message
124 * Call appropriate processing routine
125 * If it is not a Jmsg or a ReqCat message,
126 * return it to the caller.
127 *
128 * This routine is called to get the next message from
129 * another daemon. If the message is in canonical message
130 * format and the type is known, it will be dispatched
131 * to the appropriate handler. If the message is
132 * in any other format, it will be returned.
133 *
134 * E.g. any message beginning with a digit will be passed
135 * through to the caller.
136 * All other messages are expected begin with some identifier
137 * -- for the moment only the first character is checked, but
138 * at a later time, the whole identifier (e.g. Jmsg, CatReq, ...)
139 * could be checked. This is followed by Job=Jobname <user-defined>
140 * info. The identifier is used to dispatch the message to the right
141 * place (Job message, catalog request, ...). The Job is used to lookup
142 * the JobControlRecord so that the action is performed on the correct jcr, and
143 * the rest of the message is up to the user. Note, DevUpd uses
144 * *System* for the Job name, and hence no JobControlRecord is obtained. This
145 * is a *rare* case where a jcr is not really needed.
146 *
147 */
BgetDirmsg(BareosSocket * bs,bool allow_any_message)148 int BgetDirmsg(BareosSocket *bs, bool allow_any_message)
149 {
150 int32_t n = BNET_TERMINATE;
151 char Job[MAX_NAME_LENGTH];
152 char MsgType[21];
153 int type;
154 utime_t mtime; /* message time */
155 JobControlRecord *jcr = bs->jcr();
156 char *msg;
157
158 for ( ; !bs->IsStop() && !bs->IsTimedOut(); ) {
159 n = bs->recv();
160 Dmsg2(200, "BgetDirmsg %d: %s\n", n, bs->msg);
161
162 if (bs->IsStop() || bs->IsTimedOut()) {
163 return n; /* error or Terminate */
164 }
165 if (n == BNET_SIGNAL) { /* handle signal */
166 /* BNET_SIGNAL (-1) return from BnetRecv() => network signal */
167 switch (bs->message_length) {
168 case BNET_EOD: /* end of data */
169 return n;
170 case BNET_EOD_POLL:
171 bs->fsend(OK_msg);/* send response */
172 return n; /* end of data */
173 case BNET_TERMINATE:
174 bs->SetTerminated();
175 return n;
176 case BNET_POLL:
177 bs->fsend(OK_msg); /* send response */
178 break;
179 case BNET_HEARTBEAT:
180 // encode_time(time(NULL), Job);
181 // Dmsg1(100, "%s got heartbeat.\n", Job);
182 break;
183 case BNET_HB_RESPONSE:
184 break;
185 case BNET_STATUS:
186 /* *****FIXME***** Implement more completely */
187 bs->fsend("Status OK\n");
188 bs->signal(BNET_EOD);
189 break;
190 case BNET_BTIME: /* send BAREOS time */
191 char ed1[50];
192 bs->fsend("btime %s\n", edit_uint64(GetCurrentBtime(),ed1));
193 break;
194 default:
195 Jmsg1(jcr, M_WARNING, 0, _("BgetDirmsg: unknown bnet signal %d\n"), bs->message_length);
196 return n;
197 }
198 continue;
199 }
200
201 /*
202 * Handle normal data
203 */
204 if (n > 0 && B_ISDIGIT(bs->msg[0])) { /* response? */
205 return n; /* yes, return it */
206 }
207
208 /*
209 * If we get here, it must be a request. Either
210 * a message to dispatch, or a catalog request.
211 * Try to fulfill it.
212 */
213 if (sscanf(bs->msg, "%020s Job=%127s ", MsgType, Job) != 2) {
214 /*
215 * If the special flag allow_any_message is given ignore
216 * the error and just return it as normal data.
217 */
218 if (allow_any_message) {
219 return n;
220 } else {
221 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
222 continue;
223 }
224 }
225
226 /*
227 * Skip past "Jmsg Job=nnn"
228 */
229 if (!(msg=find_msg_start(bs->msg))) {
230 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
231 continue;
232 }
233
234 /*
235 * Here we are expecting a message of the following format:
236 * Jmsg Job=nnn type=nnn level=nnn Message-string
237 * Note, level should really be mtime, but that changes
238 * the protocol.
239 */
240 if (bs->msg[0] == 'J') { /* Job message */
241 if (sscanf(bs->msg, "Jmsg Job=%127s type=%d level=%lld",
242 Job, &type, &mtime) != 3) {
243 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
244 continue;
245 }
246 Dmsg1(900, "Got msg: %s\n", bs->msg);
247 SkipSpaces(&msg);
248 SkipNonspaces(&msg); /* skip type=nnn */
249 SkipSpaces(&msg);
250 SkipNonspaces(&msg); /* skip level=nnn */
251 if (*msg == ' ') {
252 msg++; /* skip leading space */
253 }
254 Dmsg1(900, "Dispatch msg: %s", msg);
255 DispatchMessage(jcr, type, mtime, msg);
256 continue;
257 }
258 /*
259 * Here we expact a CatReq message
260 * CatReq Job=nn Catalog-Request-Message
261 */
262 if (bs->msg[0] == 'C') { /* Catalog request */
263 Dmsg2(900, "Catalog req jcr 0x%x: %s", jcr, bs->msg);
264 CatalogRequest(jcr, bs);
265 continue;
266 }
267 if (bs->msg[0] == 'U') { /* SD sending attributes */
268 Dmsg2(900, "Catalog upd jcr 0x%x: %s", jcr, bs->msg);
269 CatalogUpdate(jcr, bs);
270 continue;
271 }
272 if (bs->msg[0] == 'B') { /* SD sending file spool attributes */
273 Dmsg2(100, "Blast attributes jcr 0x%x: %s", jcr, bs->msg);
274 char filename[256];
275 if (sscanf(bs->msg, "BlastAttr Job=%127s File=%255s",
276 Job, filename) != 2) {
277 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
278 continue;
279 }
280 UnbashSpaces(filename);
281 if (DespoolAttributesFromFile(jcr, filename)) {
282 bs->fsend("1000 OK BlastAttr\n");
283 } else {
284 bs->fsend("1990 ERROR BlastAttr\n");
285 }
286 continue;
287 }
288 if (bs->msg[0] == 'M') { /* Mount request */
289 Dmsg1(900, "Mount req: %s", bs->msg);
290 MountRequest(jcr, bs, msg);
291 continue;
292 }
293 if (bs->msg[0] == 'S') { /* Status change */
294 int JobStatus;
295 char Job[MAX_NAME_LENGTH];
296 if (sscanf(bs->msg, Job_status, &Job, &JobStatus) == 2) {
297 SetJcrSdJobStatus(jcr, JobStatus); /* current status */
298 } else {
299 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
300 }
301 continue;
302 }
303 #ifdef needed
304 /* No JobControlRecord for Device Updates! */
305 if (bs->msg[0] = 'D') { /* Device update */
306 Device *dev;
307 PoolMem dev_name, changer_name, media_type, volume_name;
308 int dev_open, dev_append, dev_read, dev_labeled;
309 int dev_offline, dev_autochanger, dev_autoselect;
310 int dev_num_writers, dev_max_writers, dev_reserved;
311 uint64_t dev_read_time, dev_write_time, dev_write_bytes, dev_read_bytes;
312 uint64_t dev_PoolId = 0;
313 Dmsg1(100, "<stored: %s", bs->msg);
314 if (sscanf(bs->msg, Device_update,
315 &Job, dev_name.c_str(),
316 &dev_append, &dev_read,
317 &dev_num_writers, &dev_open,
318 &dev_labeled, &dev_offline, &dev_reserved,
319 &dev_max_writers, &dev_autoselect,
320 &dev_autochanger,
321 changer_name.c_str(), media_type.c_str(),
322 volume_name.c_str(),
323 &dev_read_time, &dev_write_time, &dev_read_bytes,
324 &dev_write_bytes) != 19) {
325 Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
326 } else {
327 UnbashSpaces(dev_name);
328 dev = (Device *)my_config->GetResWithName(R_DEVICE, dev_name.c_str());
329 if (!dev) {
330 continue;
331 }
332 UnbashSpaces(changer_name);
333 UnbashSpaces(media_type);
334 UnbashSpaces(volume_name);
335 bstrncpy(dev->ChangerName, changer_name.c_str(), sizeof(dev->ChangerName));
336 bstrncpy(dev->MediaType, media_type.c_str(), sizeof(dev->MediaType));
337 bstrncpy(dev->VolumeName, volume_name.c_str(), sizeof(dev->VolumeName));
338 /* Note, these are copied because they are boolean rather than
339 * integer.
340 */
341 dev->open = dev_open;
342 dev->append = dev_append;
343 dev->read = dev_read;
344 dev->labeled = dev_labeled;
345 dev->offline = dev_offline;
346 dev->autoselect = dev_autoselect;
347 dev->autochanger = dev_autochanger > 0;
348 dev->num_drives = dev_autochanger; /* does double duty */
349 dev->PoolId = dev_PoolId;
350 dev->num_writers = dev_num_writers;
351 dev->max_writers = dev_max_writers;
352 dev->reserved = dev_reserved;
353 dev->found = true;
354 dev->DevReadTime = dev_read_time; /* TODO : have to update database */
355 dev->DevWriteTime = dev_write_time;
356 dev->DevReadBytes = dev_read_bytes;
357 dev->DevWriteBytes = dev_write_bytes;
358 }
359 continue;
360 }
361 #endif
362 return n;
363 }
364 return n;
365 }
366
find_msg_start(char * msg)367 static char *find_msg_start(char *msg)
368 {
369 char *p = msg;
370
371 SkipNonspaces(&p); /* skip message type */
372 SkipSpaces(&p);
373 SkipNonspaces(&p); /* skip Job */
374 SkipSpaces(&p); /* after spaces come the message */
375 return p;
376 }
377
378 /**
379 * Get response from FD or SD to a command we
380 * sent. Check that the response agrees with what we expect.
381 *
382 * Returns: false on failure
383 * true on success
384 */
response(JobControlRecord * jcr,BareosSocket * bs,char * resp,const char * cmd,e_prtmsg PrintMessage)385 bool response(JobControlRecord *jcr, BareosSocket *bs, char *resp, const char *cmd, e_prtmsg PrintMessage)
386 {
387 int n;
388
389 if (IsBnetError(bs)) {
390 return false;
391 }
392 if ((n = BgetDirmsg(bs)) >= 0) {
393 if (bstrcmp(bs->msg, resp)) {
394 return true;
395 }
396 if (PrintMessage == DISPLAY_ERROR) {
397 Jmsg(jcr, M_FATAL, 0, _("Bad response to %s command: wanted %s, got %s\n"),
398 cmd, resp, bs->msg);
399 }
400 return false;
401 }
402 Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"),
403 cmd, BnetStrerror(bs));
404 return false;
405 }
406 } /* namespace directordaemon */
407