2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
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
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.
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
23 Bacula® is a registered trademark of Kern Sibbald.
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.
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.
34 * Kern Sibbald, August MM
36 * This routine runs as a thread and must be thread reentrant.
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.
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";
74 void set_jcr_sd_job_status(JCR *jcr, int SDJobStatus)
76 bool set_waittime=false;
77 Dmsg2(800, "set_jcr_sd_job_status(%s, %c)\n", jcr->Job, SDJobStatus);
78 /* if wait state is new, we keep current time for watchdog MaxWaitTime */
79 switch (SDJobStatus) {
88 if (job_waiting(jcr)) {
93 /* set it before JobStatus */
94 Dmsg0(800, "Setting wait_time\n");
95 jcr->wait_time = time(NULL);
97 jcr->SDJobStatus = SDJobStatus;
102 * Call appropriate processing routine
103 * If it is not a Jmsg or a ReqCat message,
104 * return it to the caller.
106 * This routine is called to get the next message from
107 * another daemon. If the message is in canonical message
108 * format and the type is known, it will be dispatched
109 * to the appropriate handler. If the message is
110 * in any other format, it will be returned.
112 * E.g. any message beginning with a digit will be passed
113 * through to the caller.
114 * All other messages are expected begin with some identifier
115 * -- for the moment only the first character is checked, but
116 * at a later time, the whole identifier (e.g. Jmsg, CatReq, ...)
117 * could be checked. This is followed by Job=Jobname <user-defined>
118 * info. The identifier is used to dispatch the message to the right
119 * place (Job message, catalog request, ...). The Job is used to lookup
120 * the JCR so that the action is performed on the correct jcr, and
121 * the rest of the message is up to the user. Note, DevUpd uses
122 * *System* for the Job name, and hence no JCR is obtained. This
123 * is a *rare* case where a jcr is not really needed.
126 int bget_dirmsg(BSOCK *bs)
129 char Job[MAX_NAME_LENGTH];
132 utime_t mtime; /* message time */
133 JCR *jcr = bs->jcr();
138 Dmsg2(300, "bget_dirmsg %d: %s\n", n, bs->msg);
140 if (is_bnet_stop(bs)) {
141 return n; /* error or terminate */
143 if (n == BNET_SIGNAL) { /* handle signal */
144 /* BNET_SIGNAL (-1) return from bnet_recv() => network signal */
145 switch (bs->msglen) {
146 case BNET_EOD: /* end of data */
149 bs->fsend(OK_msg);/* send response */
150 return n; /* end of data */
152 bs->set_terminated();
155 bs->fsend(OK_msg); /* send response */
158 // encode_time(time(NULL), Job);
159 // Dmsg1(100, "%s got heartbeat.\n", Job);
161 case BNET_HB_RESPONSE:
164 /* *****FIXME***** Implement more completely */
165 bs->fsend("Status OK\n");
166 bs->signal(BNET_EOD);
168 case BNET_BTIME: /* send Bacula time */
170 bs->fsend("btime %s\n", edit_uint64(get_current_btime(),ed1));
173 Jmsg1(jcr, M_WARNING, 0, _("bget_dirmsg: unknown bnet signal %d\n"), bs->msglen);
179 /* Handle normal data */
181 if (n > 0 && B_ISDIGIT(bs->msg[0])) { /* response? */
182 return n; /* yes, return it */
186 * If we get here, it must be a request. Either
187 * a message to dispatch, or a catalog request.
190 if (sscanf(bs->msg, "%020s Job=%127s ", MsgType, Job) != 2) {
191 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
195 /* Skip past "Jmsg Job=nnn" */
196 if (!(msg=find_msg_start(bs->msg))) {
197 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
202 * Here we are expecting a message of the following format:
203 * Jmsg Job=nnn type=nnn level=nnn Message-string
204 * Note, level should really be mtime, but that changes
207 if (bs->msg[0] == 'J') { /* Job message */
208 if (sscanf(bs->msg, "Jmsg Job=%127s type=%d level=%lld",
209 Job, &type, &mtime) != 3) {
210 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
213 Dmsg1(900, "Got msg: %s\n", bs->msg);
215 skip_nonspaces(&msg); /* skip type=nnn */
217 skip_nonspaces(&msg); /* skip level=nnn */
219 msg++; /* skip leading space */
221 Dmsg1(900, "Dispatch msg: %s", msg);
222 dispatch_message(jcr, type, mtime, msg);
226 * Here we expact a CatReq message
227 * CatReq Job=nn Catalog-Request-Message
229 if (bs->msg[0] == 'C') { /* Catalog request */
230 Dmsg2(900, "Catalog req jcr 0x%x: %s", jcr, bs->msg);
231 catalog_request(jcr, bs);
234 if (bs->msg[0] == 'U') { /* SD sending attributes */
235 Dmsg2(900, "Catalog upd jcr 0x%x: %s", jcr, bs->msg);
236 catalog_update(jcr, bs);
239 if (bs->msg[0] == 'M') { /* Mount request */
240 Dmsg1(900, "Mount req: %s", bs->msg);
241 mount_request(jcr, bs, msg);
244 if (bs->msg[0] == 'S') { /* Status change */
246 char Job[MAX_NAME_LENGTH];
247 if (sscanf(bs->msg, Job_status, &Job, &JobStatus) == 2) {
248 set_jcr_sd_job_status(jcr, JobStatus); /* current status */
250 Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
255 /* No JCR for Device Updates! */
256 if (bs->msg[0] = 'D') { /* Device update */
258 POOL_MEM dev_name, changer_name, media_type, volume_name;
259 int dev_open, dev_append, dev_read, dev_labeled;
260 int dev_offline, dev_autochanger, dev_autoselect;
261 int dev_num_writers, dev_max_writers, dev_reserved;
262 uint64_t dev_read_time, dev_write_time, dev_write_bytes, dev_read_bytes;
264 Dmsg1(100, "<stored: %s", bs->msg);
265 if (sscanf(bs->msg, Device_update,
266 &Job, dev_name.c_str(),
267 &dev_append, &dev_read,
268 &dev_num_writers, &dev_open,
269 &dev_labeled, &dev_offline, &dev_reserved,
270 &dev_max_writers, &dev_autoselect,
272 changer_name.c_str(), media_type.c_str(),
274 &dev_read_time, &dev_write_time, &dev_read_bytes,
275 &dev_write_bytes) != 19) {
276 Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
278 unbash_spaces(dev_name);
279 dev = (DEVICE *)GetResWithName(R_DEVICE, dev_name.c_str());
283 unbash_spaces(changer_name);
284 unbash_spaces(media_type);
285 unbash_spaces(volume_name);
286 bstrncpy(dev->ChangerName, changer_name.c_str(), sizeof(dev->ChangerName));
287 bstrncpy(dev->MediaType, media_type.c_str(), sizeof(dev->MediaType));
288 bstrncpy(dev->VolumeName, volume_name.c_str(), sizeof(dev->VolumeName));
289 /* Note, these are copied because they are boolean rather than
292 dev->open = dev_open;
293 dev->append = dev_append;
294 dev->read = dev_read;
295 dev->labeled = dev_labeled;
296 dev->offline = dev_offline;
297 dev->autoselect = dev_autoselect;
298 dev->autochanger = dev_autochanger > 0;
299 dev->num_drives = dev_autochanger; /* does double duty */
300 dev->PoolId = dev_PoolId;
301 dev->num_writers = dev_num_writers;
302 dev->max_writers = dev_max_writers;
303 dev->reserved = dev_reserved;
305 dev->DevReadTime = dev_read_time; /* TODO : have to update database */
306 dev->DevWriteTime = dev_write_time;
307 dev->DevReadBytes = dev_read_bytes;
308 dev->DevWriteBytes = dev_write_bytes;
317 static char *find_msg_start(char *msg)
321 skip_nonspaces(&p); /* skip message type */
323 skip_nonspaces(&p); /* skip Job */
324 skip_spaces(&p); /* after spaces come the message */
329 * Get response from FD or SD to a command we
330 * sent. Check that the response agrees with what we expect.
332 * Returns: false on failure
335 bool response(JCR *jcr, BSOCK *bs, char *resp, const char *cmd, e_prtmsg prtmsg)
339 if (is_bnet_error(bs)) {
342 if ((n = bget_dirmsg(bs)) >= 0) {
343 if (strcmp(bs->msg, resp) == 0) {
346 if (prtmsg == DISPLAY_ERROR) {
347 Jmsg(jcr, M_FATAL, 0, _("Bad response to %s command: wanted %s, got %s\n"),
352 Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"),
353 cmd, bnet_strerror(bs));