3 * Bacula Director -- routines to receive network data and
4 * handle network signals. These routines handle the connections
5 * to the Storage daemon and the File daemon.
7 * Kern Sibbald, August MM
9 * This routine runs as a thread and must be thread reentrant.
11 * Basic tasks done here:
12 * Handle network signals (signals).
13 * Signals always have return status 0 from bnet_recv() and
14 * a zero or negative message length.
15 * Pass appropriate messages back to the caller (responses).
16 * Responses always have a digit as the first character.
17 * Handle requests for message and catalog services (requests).
18 * Requests are any message that does not begin with a digit.
19 * In affect, they are commands.
24 Bacula® - The Network Backup Solution
26 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
28 The main author of Bacula is Kern Sibbald, with contributions from
29 many others, a complete list can be found in the file AUTHORS.
30 This program is Free Software; you can redistribute it and/or
31 modify it under the terms of version two of the GNU General Public
32 License as published by the Free Software Foundation and included
35 This program is distributed in the hope that it will be useful, but
36 WITHOUT ANY WARRANTY; without even the implied warranty of
37 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
38 General Public License for more details.
40 You should have received a copy of the GNU General Public License
41 along with this program; if not, write to the Free Software
42 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
45 Bacula® is a registered trademark of John Walker.
46 The licensor of Bacula is the Free Software Foundation Europe
47 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
48 Switzerland, email:ftf@fsfeurope.org.
54 /* Forward referenced functions */
55 static char *find_msg_start(char *msg);
57 static char Job_status[] = "Status Job=%127s JobStatus=%d\n";
59 static char Device_update[] = "DevUpd Job=%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 "
71 static char OK_msg[] = "1000 OK\n";
75 * Call appropriate processing routine
76 * If it is not a Jmsg or a ReqCat message,
77 * return it to the caller.
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.
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.
99 int bget_dirmsg(BSOCK *bs)
102 char Job[MAX_NAME_LENGTH];
110 Dmsg2(900, "bget_dirmsg %d: %s", n, bs->msg);
112 if (is_bnet_stop(bs)) {
113 return n; /* error or terminate */
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 */
121 bs->fsend(OK_msg);/* send response */
122 return n; /* end of data */
124 bs->set_terminated();
127 bs->fsend(OK_msg); /* send response */
130 // encode_time(time(NULL), Job);
131 // Dmsg1(100, "%s got heartbeat.\n", Job);
133 case BNET_HB_RESPONSE:
136 /* *****FIXME***** Implement more completely */
137 bs->fsend("Status OK\n");
138 bs->signal(BNET_EOD);
140 case BNET_BTIME: /* send Bacula time */
142 bs->fsend("btime %s\n", edit_uint64(get_current_btime(),ed1));
145 Emsg1(M_WARNING, 0, _("bget_dirmsg: unknown bnet signal %d\n"), bs->msglen);
151 /* Handle normal data */
153 if (n > 0 && B_ISDIGIT(bs->msg[0])) { /* response? */
154 return n; /* yes, return it */
158 * If we get here, it must be a request. Either
159 * a message to dispatch, or a catalog request.
162 if (sscanf(bs->msg, "%020s Job=%127s ", MsgType, Job) != 2) {
163 Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
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);
172 Dmsg1(900, "Getmsg got jcr 0x%x\n", jcr);
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);
182 * Here we are expecting a message of the following format:
183 * Jmsg Job=nnn type=nnn level=nnn Message-string
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);
192 Dmsg1(900, "Got msg: %s\n", bs->msg);
194 skip_nonspaces(&msg); /* skip type=nnn */
196 skip_nonspaces(&msg); /* skip level=nnn */
198 msg++; /* skip leading space */
200 Dmsg1(900, "Dispatch msg: %s", msg);
201 dispatch_message(jcr, type, level, msg);
206 * Here we expact a CatReq message
207 * CatReq Job=nn Catalog-Request-Message
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);
216 if (bs->msg[0] == 'U') { /* Catalog update */
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);
223 if (bs->msg[0] == 'M') { /* Mount request */
224 Dmsg1(900, "Mount req: %s", bs->msg);
225 mount_request(jcr, bs, msg);
229 if (bs->msg[0] == 'S') { /* Status change */
231 char Job[MAX_NAME_LENGTH];
232 if (sscanf(bs->msg, Job_status, &Job, &JobStatus) == 2) {
233 jcr->SDJobStatus = JobStatus; /* current status */
235 Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
241 /* No JCR for Device Updates! */
242 if (bs->msg[0] = 'D') { /* Device update */
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;
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,
258 changer_name.c_str(), media_type.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);
264 unbash_spaces(dev_name);
265 dev = (DEVICE *)GetResWithName(R_DEVICE, dev_name.c_str());
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
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;
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;
303 static char *find_msg_start(char *msg)
307 skip_nonspaces(&p); /* skip message type */
309 skip_nonspaces(&p); /* skip Job */
310 skip_spaces(&p); /* after spaces come the message */
315 * Get response from FD or SD to a command we
316 * sent. Check that the response agrees with what we expect.
318 * Returns: false on failure
321 bool response(JCR *jcr, BSOCK *bs, char *resp, const char *cmd, e_prtmsg prtmsg)
325 if (is_bnet_error(bs)) {
328 if ((n = bget_dirmsg(bs)) >= 0) {
329 if (strcmp(bs->msg, resp) == 0) {
332 if (prtmsg == DISPLAY_ERROR) {
333 Jmsg(jcr, M_FATAL, 0, _("Bad response to %s command: wanted %s, got %s\n"),
338 Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"),
339 cmd, bnet_strerror(bs));