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/jcr_private.h"
49 #include "dird/msgchan.h"
50 #include "lib/bnet.h"
51 #include "lib/edit.h"
52 #include "lib/util.h"
53
54 namespace directordaemon {
55
56 /* Forward referenced functions */
57 static char* find_msg_start(char* msg);
58
59 static char Job_status[] = "Status Job=%127s JobStatus=%d\n";
60
61 static char OK_msg[] = "1000 OK\n";
62
SetJcrSdJobStatus(JobControlRecord * jcr,int SDJobStatus)63 static void SetJcrSdJobStatus(JobControlRecord* jcr, int SDJobStatus)
64 {
65 bool set_waittime = false;
66
67 Dmsg2(800, "SetJcrSdJobStatus(%s, %c)\n", jcr->Job, SDJobStatus);
68
69 /*
70 * If wait state is new, we keep current time for watchdog MaxWaitTime
71 */
72 switch (SDJobStatus) {
73 case JS_WaitMedia:
74 case JS_WaitMount:
75 case JS_WaitMaxJobs:
76 set_waittime = true;
77 default:
78 break;
79 }
80
81 if (JobWaiting(jcr)) { set_waittime = false; }
82
83 if (set_waittime) {
84 /*
85 * Set it before JobStatus
86 */
87 Dmsg0(800, "Setting wait_time\n");
88 jcr->wait_time = time(NULL);
89 }
90 jcr->impl->SDJobStatus = SDJobStatus;
91
92 /*
93 * Some SD Job status setting are propagated to the controlling Job.
94 */
95 switch (jcr->impl->SDJobStatus) {
96 case JS_Incomplete:
97 jcr->setJobStatus(JS_Incomplete);
98 break;
99 case JS_FatalError:
100 jcr->setJobStatus(JS_FatalError);
101 break;
102 default:
103 break;
104 }
105 }
106
107 /**
108 * Get a message
109 * Call appropriate processing routine
110 * If it is not a Jmsg or a ReqCat message,
111 * return it to the caller.
112 *
113 * This routine is called to get the next message from
114 * another daemon. If the message is in canonical message
115 * format and the type is known, it will be dispatched
116 * to the appropriate handler. If the message is
117 * in any other format, it will be returned.
118 *
119 * E.g. any message beginning with a digit will be passed
120 * through to the caller.
121 * All other messages are expected begin with some identifier
122 * -- for the moment only the first character is checked, but
123 * at a later time, the whole identifier (e.g. Jmsg, CatReq, ...)
124 * could be checked. This is followed by Job=Jobname <user-defined>
125 * info. The identifier is used to dispatch the message to the right
126 * place (Job message, catalog request, ...). The Job is used to lookup
127 * the JobControlRecord so that the action is performed on the correct jcr,
128 * and the rest of the message is up to the user. Note, DevUpd uses *System*
129 * for the Job name, and hence no JobControlRecord is obtained. This is a *rare*
130 * case where a jcr is not really needed.
131 *
132 */
BgetDirmsg(BareosSocket * bs,bool allow_any_message)133 int BgetDirmsg(BareosSocket* bs, bool allow_any_message)
134 {
135 int32_t n = BNET_TERMINATE;
136 char Job[MAX_NAME_LENGTH];
137 char MsgType[21];
138 int type;
139 utime_t mtime; /* message time */
140 JobControlRecord* jcr = bs->jcr();
141 char* msg;
142
143 for (; !bs->IsStop() && !bs->IsTimedOut();) {
144 n = bs->recv();
145 Dmsg2(200, "BgetDirmsg %d: %s\n", n, bs->msg);
146
147 if (bs->IsStop() || bs->IsTimedOut()) { return n; /* error or Terminate */ }
148 if (n == BNET_SIGNAL) { /* handle signal */
149 /* BNET_SIGNAL (-1) return from BnetRecv() => network signal */
150 switch (bs->message_length) {
151 case BNET_EOD: /* end of data */
152 return n;
153 case BNET_EOD_POLL:
154 bs->fsend(OK_msg); /* send response */
155 return n; /* end of data */
156 case BNET_TERMINATE:
157 bs->SetTerminated();
158 return n;
159 case BNET_POLL:
160 bs->fsend(OK_msg); /* send response */
161 break;
162 case BNET_HEARTBEAT:
163 // encode_time(time(NULL), Job);
164 // Dmsg1(100, "%s got heartbeat.\n", Job);
165 break;
166 case BNET_HB_RESPONSE:
167 break;
168 case BNET_STATUS:
169 /* *****FIXME***** Implement more completely */
170 bs->fsend("Status OK\n");
171 bs->signal(BNET_EOD);
172 break;
173 case BNET_BTIME: /* send BAREOS time */
174 char ed1[50];
175 bs->fsend("btime %s\n", edit_uint64(GetCurrentBtime(), ed1));
176 break;
177 default:
178 Jmsg1(jcr, M_WARNING, 0, _("BgetDirmsg: unknown bnet signal %d\n"),
179 bs->message_length);
180 return n;
181 }
182 continue;
183 }
184
185 /*
186 * Handle normal data
187 */
188 if (n > 0 && B_ISDIGIT(bs->msg[0])) { /* response? */
189 return n; /* yes, return it */
190 }
191
192 /*
193 * If we get here, it must be a request. Either
194 * a message to dispatch, or a catalog request.
195 * Try to fulfill it.
196 */
197 if (sscanf(bs->msg, "%020s Job=%127s ", MsgType, Job) != 2) {
198 /*
199 * If the special flag allow_any_message is given ignore
200 * the error and just return it as normal data.
201 */
202 if (allow_any_message) {
203 return n;
204 } else {
205 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
206 continue;
207 }
208 }
209
210 /*
211 * Skip past "Jmsg Job=nnn"
212 */
213 if (!(msg = find_msg_start(bs->msg))) {
214 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
215 continue;
216 }
217
218 /*
219 * Here we are expecting a message of the following format:
220 * Jmsg Job=nnn type=nnn level=nnn Message-string
221 * Note, level should really be mtime, but that changes
222 * the protocol.
223 */
224 if (bs->msg[0] == 'J') { /* Job message */
225 if (sscanf(bs->msg, "Jmsg Job=%127s type=%d level=%lld", Job, &type,
226 &mtime) != 3) {
227 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
228 continue;
229 }
230 Dmsg1(900, "Got msg: %s\n", bs->msg);
231 SkipSpaces(&msg);
232 SkipNonspaces(&msg); /* skip type=nnn */
233 SkipSpaces(&msg);
234 SkipNonspaces(&msg); /* skip level=nnn */
235 if (*msg == ' ') { msg++; /* skip leading space */ }
236 Dmsg1(900, "Dispatch msg: %s", msg);
237 DispatchMessage(jcr, type, mtime, msg);
238 continue;
239 }
240 /*
241 * Here we expact a CatReq message
242 * CatReq Job=nn Catalog-Request-Message
243 */
244 if (bs->msg[0] == 'C') { /* Catalog request */
245 Dmsg2(900, "Catalog req jcr 0x%x: %s", jcr, bs->msg);
246 CatalogRequest(jcr, bs);
247 continue;
248 }
249 if (bs->msg[0] == 'U') { /* SD sending attributes */
250 Dmsg2(900, "Catalog upd jcr 0x%x: %s", jcr, bs->msg);
251 CatalogUpdate(jcr, bs);
252 continue;
253 }
254 if (bs->msg[0] == 'B') { /* SD sending file spool attributes */
255 Dmsg2(100, "Blast attributes jcr 0x%x: %s", jcr, bs->msg);
256 char filename[256];
257 if (sscanf(bs->msg, "BlastAttr Job=%127s File=%255s", Job, filename) !=
258 2) {
259 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
260 continue;
261 }
262 UnbashSpaces(filename);
263 if (DespoolAttributesFromFile(jcr, filename)) {
264 bs->fsend("1000 OK BlastAttr\n");
265 } else {
266 bs->fsend("1990 ERROR BlastAttr\n");
267 }
268 continue;
269 }
270 if (bs->msg[0] == 'S') { /* Status change */
271 int JobStatus;
272 char Job[MAX_NAME_LENGTH];
273 if (sscanf(bs->msg, Job_status, &Job, &JobStatus) == 2) {
274 SetJcrSdJobStatus(jcr, JobStatus); /* current status */
275 } else {
276 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
277 }
278 continue;
279 }
280 return n;
281 }
282 return n;
283 }
284
find_msg_start(char * msg)285 static char* find_msg_start(char* msg)
286 {
287 char* p = msg;
288
289 SkipNonspaces(&p); /* skip message type */
290 SkipSpaces(&p);
291 SkipNonspaces(&p); /* skip Job */
292 SkipSpaces(&p); /* after spaces come the message */
293 return p;
294 }
295
296 /**
297 * Get response from FD or SD to a command we
298 * sent. Check that the response agrees with what we expect.
299 *
300 * Returns: false on failure
301 * true on success
302 */
response(JobControlRecord * jcr,BareosSocket * bs,char * resp,const char * cmd,e_prtmsg PrintMessage)303 bool response(JobControlRecord* jcr,
304 BareosSocket* bs,
305 char* resp,
306 const char* cmd,
307 e_prtmsg PrintMessage)
308 {
309 int n;
310
311 if (IsBnetError(bs)) { return false; }
312 if ((n = BgetDirmsg(bs)) >= 0) {
313 if (bstrcmp(bs->msg, resp)) { return true; }
314 if (PrintMessage == DISPLAY_ERROR) {
315 Jmsg(jcr, M_FATAL, 0,
316 _("Bad response to %s command: wanted %s, got %s\n"), cmd, resp,
317 bs->msg);
318 }
319 return false;
320 }
321 Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"), cmd,
322 BnetStrerror(bs));
323 return false;
324 }
325 } /* namespace directordaemon */
326