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