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