]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/getmsg.c
kes Make explicit exception to GPL in LICENSE to permit linking
[bacula/bacula] / bacula / src / dird / getmsg.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU 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    General Public License for more details.
17
18    You should have received a copy of the GNU 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    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- routines to receive network data and
31  *    handle network signals. These routines handle the connections
32  *    to the Storage daemon and the File daemon.
33  *
34  *     Kern Sibbald, August MM
35  *
36  *    This routine runs as a thread and must be thread reentrant.
37  *
38  *  Basic tasks done here:
39  *    Handle  network signals (signals).
40  *       Signals always have return status 0 from bnet_recv() and
41  *       a zero or negative message length.
42  *    Pass appropriate messages back to the caller (responses).
43  *       Responses always have a digit as the first character.
44  *    Handle requests for message and catalog services (requests).
45  *       Requests are any message that does not begin with a digit.
46  *       In affect, they are commands.
47  *
48  *   Version $Id$
49  */
50
51 #include "bacula.h"
52 #include "dird.h"
53
54 /* Forward referenced functions */
55 static char *find_msg_start(char *msg);
56
57 static char Job_status[] = "Status Job=%127s JobStatus=%d\n";
58 #ifdef needed
59 static char Device_update[]   = "DevUpd Job=%127s "
60    "device=%127s "
61    "append=%d read=%d num_writers=%d "
62    "open=%d labeled=%d offline=%d "
63    "reserved=%d max_writers=%d "
64    "autoselect=%d autochanger=%d "
65    "changer_name=%127s media_type=%127s volume_name=%127s "
66    "DevReadTime=%d DevWriteTime=%d DevReadBytes=%d "
67    "DevWriteBytes=%d\n";
68 #endif
69
70
71 static char OK_msg[] = "1000 OK\n";
72
73 /*
74  * Get a message
75  *  Call appropriate processing routine
76  *  If it is not a Jmsg or a ReqCat message,
77  *   return it to the caller.
78  *
79  *  This routine is called to get the next message from
80  *  another daemon. If the message is in canonical message
81  *  format and the type is known, it will be dispatched
82  *  to the appropriate handler.  If the message is
83  *  in any other format, it will be returned.
84  *
85  *  E.g. any message beginning with a digit will be passed   
86  *       through to the caller.
87  *  All other messages are expected begin with some identifier
88  *    -- for the moment only the first character is checked, but
89  *    at a later time, the whole identifier (e.g. Jmsg, CatReq, ...)
90  *    could be checked. This is followed by Job=Jobname <user-defined>
91  *    info. The identifier is used to dispatch the message to the right
92  *    place (Job message, catalog request, ...). The Job is used to lookup
93  *    the JCR so that the action is performed on the correct jcr, and
94  *    the rest of the message is up to the user.  Note, DevUpd uses
95  *    *System* for the Job name, and hence no JCR is obtained. This   
96  *    is a *rare* case where a jcr is not really needed.
97  *
98  */
99 int bget_dirmsg(BSOCK *bs)
100 {
101    int32_t n;
102    char Job[MAX_NAME_LENGTH];
103    char MsgType[20];
104    int type, level;
105    JCR *jcr;
106    char *msg;
107
108    for (;;) {
109       n = bs->recv();
110       Dmsg2(100, "bget_dirmsg %d: %s\n", n, bs->msg);
111
112       if (is_bnet_stop(bs)) {
113          return n;                    /* error or terminate */
114       }
115       if (n == BNET_SIGNAL) {          /* handle signal */
116          /* BNET_SIGNAL (-1) return from bnet_recv() => network signal */
117          switch (bs->msglen) {
118          case BNET_EOD:            /* end of data */
119             return n;
120          case BNET_EOD_POLL:
121             bs->fsend(OK_msg);/* send response */
122             return n;              /* end of data */
123          case BNET_TERMINATE:
124             bs->set_terminated();
125             return n;
126          case BNET_POLL:
127             bs->fsend(OK_msg); /* send response */
128             break;
129          case BNET_HEARTBEAT:
130 //          encode_time(time(NULL), Job);
131 //          Dmsg1(100, "%s got heartbeat.\n", Job);
132             break;
133          case BNET_HB_RESPONSE:
134             break;
135          case BNET_STATUS:
136             /* *****FIXME***** Implement more completely */
137             bs->fsend("Status OK\n");
138             bs->signal(BNET_EOD);
139             break;
140          case BNET_BTIME:             /* send Bacula time */
141             char ed1[50];
142             bs->fsend("btime %s\n", edit_uint64(get_current_btime(),ed1));
143             break;
144          default:
145             Emsg1(M_WARNING, 0, _("bget_dirmsg: unknown bnet signal %d\n"), bs->msglen);
146             return n;
147          }
148          continue;
149       }
150
151       /* Handle normal data */
152
153       if (n > 0 && B_ISDIGIT(bs->msg[0])) {      /* response? */
154          return n;                    /* yes, return it */
155       }
156
157       /*
158        * If we get here, it must be a request.  Either
159        *  a message to dispatch, or a catalog request.
160        *  Try to fulfill it.
161        */
162       if (sscanf(bs->msg, "%020s Job=%127s ", MsgType, Job) != 2) {
163          Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
164          continue;
165       }
166       if (strcmp(Job, "*System*") == 0) {
167          jcr = NULL;                  /* No jcr */
168       } else if (!(jcr=get_jcr_by_full_name(Job))) {
169          Emsg1(M_ERROR, 0, _("Job not found: %s\n"), bs->msg);
170          continue;
171       }
172       Dmsg1(900, "Getmsg got jcr 0x%x\n", jcr);
173
174       /* Skip past "Jmsg Job=nnn" */
175       if (!(msg=find_msg_start(bs->msg))) {
176          Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
177          free_jcr(jcr);
178          continue;
179       }
180
181       /*
182        * Here we are expecting a message of the following format:
183        *   Jmsg Job=nnn type=nnn level=nnn Message-string
184        */
185       if (bs->msg[0] == 'J') {           /* Job message */
186          if (sscanf(bs->msg, "Jmsg Job=%127s type=%d level=%d",
187                     Job, &type, &level) != 3) {
188             Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
189             free_jcr(jcr);
190             continue;
191          }
192          Dmsg1(900, "Got msg: %s\n", bs->msg);
193          skip_spaces(&msg);
194          skip_nonspaces(&msg);        /* skip type=nnn */
195          skip_spaces(&msg);
196          skip_nonspaces(&msg);        /* skip level=nnn */
197          if (*msg == ' ') {
198             msg++;                    /* skip leading space */
199          }
200          Dmsg1(900, "Dispatch msg: %s", msg);
201          dispatch_message(jcr, type, level, msg);
202          free_jcr(jcr);
203          continue;
204       }
205       /*
206        * Here we expact a CatReq message
207        *   CatReq Job=nn Catalog-Request-Message
208        */
209       if (bs->msg[0] == 'C') {        /* Catalog request */
210          Dmsg2(900, "Catalog req jcr 0x%x: %s", jcr, bs->msg);
211          catalog_request(jcr, bs);
212          Dmsg1(900, "Calling freejcr 0x%x\n", jcr);
213          free_jcr(jcr);
214          continue;
215       }
216       if (bs->msg[0] == 'U') {        /* SD sending attributes */
217          Dmsg2(900, "Catalog upd jcr 0x%x: %s", jcr, bs->msg);
218          catalog_update(jcr, bs);
219          Dmsg1(900, "Calling freejcr 0x%x\n", jcr);
220          free_jcr(jcr);
221          continue;
222       }
223       if (bs->msg[0] == 'M') {        /* Mount request */
224          Dmsg1(900, "Mount req: %s", bs->msg);
225          mount_request(jcr, bs, msg);
226          free_jcr(jcr);
227          continue;
228       }
229       if (bs->msg[0] == 'S') {       /* Status change */
230          int JobStatus;
231          char Job[MAX_NAME_LENGTH];
232          if (sscanf(bs->msg, Job_status, &Job, &JobStatus) == 2) {
233             jcr->SDJobStatus = JobStatus; /* current status */
234          } else {
235             Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
236          }
237          free_jcr(jcr);
238          continue;
239       }
240 #ifdef needed
241       /* No JCR for Device Updates! */
242       if (bs->msg[0] = 'D') {         /* Device update */
243          DEVICE *dev;
244          POOL_MEM dev_name, changer_name, media_type, volume_name;
245          int dev_open, dev_append, dev_read, dev_labeled;
246          int dev_offline, dev_autochanger, dev_autoselect;
247          int dev_num_writers, dev_max_writers, dev_reserved;
248          uint64_t dev_read_time, dev_write_time, dev_write_bytes, dev_read_bytes;
249          uint64_t dev_PoolId;
250          Dmsg1(100, "<stored: %s", bs->msg);
251          if (sscanf(bs->msg, Device_update,
252              &Job, dev_name.c_str(),
253              &dev_append, &dev_read,
254              &dev_num_writers, &dev_open,
255              &dev_labeled, &dev_offline, &dev_reserved,
256              &dev_max_writers, &dev_autoselect, 
257              &dev_autochanger, 
258              changer_name.c_str(), media_type.c_str(),
259              volume_name.c_str(),
260              &dev_read_time, &dev_write_time, &dev_read_bytes,
261              &dev_write_bytes) != 19) {
262             Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
263          } else {
264             unbash_spaces(dev_name);
265             dev = (DEVICE *)GetResWithName(R_DEVICE, dev_name.c_str());
266             if (!dev) {
267                continue;
268             }
269             unbash_spaces(changer_name);
270             unbash_spaces(media_type);
271             unbash_spaces(volume_name);
272             bstrncpy(dev->ChangerName, changer_name.c_str(), sizeof(dev->ChangerName));
273             bstrncpy(dev->MediaType, media_type.c_str(), sizeof(dev->MediaType));
274             bstrncpy(dev->VolumeName, volume_name.c_str(), sizeof(dev->VolumeName));
275             /* Note, these are copied because they are boolean rather than
276              *  integer.
277              */
278             dev->open = dev_open;
279             dev->append = dev_append;
280             dev->read = dev_read;
281             dev->labeled = dev_labeled;
282             dev->offline = dev_offline;
283             dev->autoselect = dev_autoselect;
284             dev->autochanger = dev_autochanger > 0;
285             dev->num_drives = dev_autochanger;    /* does double duty */
286             dev->PoolId = dev_PoolId;
287             dev->num_writers = dev_num_writers;
288             dev->max_writers = dev_max_writers;
289             dev->reserved = dev_reserved;
290             dev->found = true;
291             dev->DevReadTime = dev_read_time; /* TODO : have to update database */
292             dev->DevWriteTime = dev_write_time;
293             dev->DevReadBytes = dev_read_bytes;
294             dev->DevWriteBytes = dev_write_bytes;
295          }
296          continue;
297       }
298 #endif
299       return n;
300    }
301 }
302
303 static char *find_msg_start(char *msg)
304 {
305    char *p = msg;
306
307    skip_nonspaces(&p);                /* skip message type */
308    skip_spaces(&p);
309    skip_nonspaces(&p);                /* skip Job */
310    skip_spaces(&p);                   /* after spaces come the message */
311    return p;
312 }
313
314 /*
315  * Get response from FD or SD to a command we
316  * sent. Check that the response agrees with what we expect.
317  *
318  *  Returns: false on failure
319  *           true  on success
320  */
321 bool response(JCR *jcr, BSOCK *bs, char *resp, const char *cmd, e_prtmsg prtmsg)
322 {
323    int n;
324
325    if (is_bnet_error(bs)) {
326       return false;
327    }
328    if ((n = bget_dirmsg(bs)) >= 0) {
329       if (strcmp(bs->msg, resp) == 0) {
330          return true;
331       }
332       if (prtmsg == DISPLAY_ERROR) {
333          Jmsg(jcr, M_FATAL, 0, _("Bad response to %s command: wanted %s, got %s\n"),
334             cmd, resp, bs->msg);
335       }
336       return false;
337    }
338    Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"),
339          cmd, bnet_strerror(bs));
340    return false;
341 }