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 Copyright (C) 2000-2006 Kern Sibbald
26 This program is free software; you can redistribute it and/or
27 modify it under the terms of the GNU General Public License
28 version 2 as amended with additional clauses defined in the
29 file LICENSE in the main source directory.
31 This program is distributed in the hope that it will be useful,
32 but WITHOUT ANY WARRANTY; without even the implied warranty of
33 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 the file LICENSE for additional details.
41 /* Forward referenced functions */
42 static char *find_msg_start(char *msg);
44 static char Job_status[] = "Status Job=%127s JobStatus=%d\n";
46 static char Device_update[] = "DevUpd Job=%127s "
48 "append=%d read=%d num_writers=%d "
49 "open=%d labeled=%d offline=%d "
50 "reserved=%d max_writers=%d "
51 "autoselect=%d autochanger=%d "
52 "changer_name=%127s media_type=%127s volume_name=%127s\n";
56 static char OK_msg[] = "1000 OK\n";
60 * Call appropriate processing routine
61 * If it is not a Jmsg or a ReqCat message,
62 * return it to the caller.
64 * This routine is called to get the next message from
65 * another daemon. If the message is in canonical message
66 * format and the type is known, it will be dispatched
67 * to the appropriate handler. If the message is
68 * in any other format, it will be returned.
70 * E.g. any message beginning with a digit will be passed
71 * through to the caller.
72 * All other messages are expected begin with some identifier
73 * -- for the moment only the first character is checked, but
74 * at a later time, the whole identifier (e.g. Jmsg, CatReq, ...)
75 * could be checked. This is followed by Job=Jobname <user-defined>
76 * info. The identifier is used to dispatch the message to the right
77 * place (Job message, catalog request, ...). The Job is used to lookup
78 * the JCR so that the action is performed on the correct jcr, and
79 * the rest of the message is up to the user. Note, DevUpd uses
80 * *System* for the Job name, and hence no JCR is obtained. This
81 * is a *rare* case where a jcr is not really needed.
84 int bget_dirmsg(BSOCK *bs)
87 char Job[MAX_NAME_LENGTH];
95 Dmsg2(900, "bget_dirmsg %d: %s", n, bs->msg);
97 if (is_bnet_stop(bs)) {
98 return n; /* error or terminate */
100 if (n == BNET_SIGNAL) { /* handle signal */
101 /* BNET_SIGNAL (-1) return from bnet_recv() => network signal */
102 switch (bs->msglen) {
103 case BNET_EOD: /* end of data */
106 bnet_fsend(bs, OK_msg);/* send response */
107 return n; /* end of data */
112 bnet_fsend(bs, OK_msg); /* send response */
115 // encode_time(time(NULL), Job);
116 // Dmsg1(100, "%s got heartbeat.\n", Job);
118 case BNET_HB_RESPONSE:
121 /* *****FIXME***** Implement more completely */
122 bnet_fsend(bs, "Status OK\n");
123 bnet_sig(bs, BNET_EOD);
125 case BNET_BTIME: /* send Bacula time */
127 bnet_fsend(bs, "btime %s\n", edit_uint64(get_current_btime(),ed1));
130 Emsg1(M_WARNING, 0, _("bget_dirmsg: unknown bnet signal %d\n"), bs->msglen);
136 /* Handle normal data */
138 if (n > 0 && B_ISDIGIT(bs->msg[0])) { /* response? */
139 return n; /* yes, return it */
143 * If we get here, it must be a request. Either
144 * a message to dispatch, or a catalog request.
147 if (sscanf(bs->msg, "%020s Job=%127s ", MsgType, Job) != 2) {
148 Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
151 if (strcmp(Job, "*System*") == 0) {
152 jcr = NULL; /* No jcr */
153 } else if (!(jcr=get_jcr_by_full_name(Job))) {
154 Emsg1(M_ERROR, 0, _("Job not found: %s\n"), bs->msg);
157 Dmsg1(900, "Getmsg got jcr 0x%x\n", jcr);
159 /* Skip past "Jmsg Job=nnn" */
160 if (!(msg=find_msg_start(bs->msg))) {
161 Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
167 * Here we are expecting a message of the following format:
168 * Jmsg Job=nnn type=nnn level=nnn Message-string
170 if (bs->msg[0] == 'J') { /* Job message */
171 if (sscanf(bs->msg, "Jmsg Job=%127s type=%d level=%d",
172 Job, &type, &level) != 3) {
173 Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
177 Dmsg1(900, "Got msg: %s\n", bs->msg);
179 skip_nonspaces(&msg); /* skip type=nnn */
181 skip_nonspaces(&msg); /* skip level=nnn */
183 msg++; /* skip leading space */
185 Dmsg1(900, "Dispatch msg: %s", msg);
186 dispatch_message(jcr, type, level, msg);
191 * Here we expact a CatReq message
192 * CatReq Job=nn Catalog-Request-Message
194 if (bs->msg[0] == 'C') { /* Catalog request */
195 Dmsg2(900, "Catalog req jcr 0x%x: %s", jcr, bs->msg);
196 catalog_request(jcr, bs);
197 Dmsg1(900, "Calling freejcr 0x%x\n", jcr);
201 if (bs->msg[0] == 'U') { /* Catalog update */
202 Dmsg2(900, "Catalog upd jcr 0x%x: %s", jcr, bs->msg);
203 catalog_update(jcr, bs);
204 Dmsg1(900, "Calling freejcr 0x%x\n", jcr);
208 if (bs->msg[0] == 'M') { /* Mount request */
209 Dmsg1(900, "Mount req: %s", bs->msg);
210 mount_request(jcr, bs, msg);
214 if (bs->msg[0] == 'S') { /* Status change */
216 char Job[MAX_NAME_LENGTH];
217 if (sscanf(bs->msg, Job_status, &Job, &JobStatus) == 2) {
218 jcr->SDJobStatus = JobStatus; /* current status */
220 Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
226 /* No JCR for Device Updates! */
227 if (bs->msg[0] = 'D') { /* Device update */
229 POOL_MEM dev_name, changer_name, media_type, volume_name;
230 int dev_open, dev_append, dev_read, dev_labeled;
231 int dev_offline, dev_autochanger, dev_autoselect;
232 int dev_num_writers, dev_max_writers, dev_reserved;
234 Dmsg1(100, "<stored: %s", bs->msg);
235 if (sscanf(bs->msg, Device_update,
236 &Job, dev_name.c_str(),
237 &dev_append, &dev_read,
238 &dev_num_writers, &dev_open,
239 &dev_labeled, &dev_offline, &dev_reserved,
240 &dev_max_writers, &dev_autoselect,
242 changer_name.c_str(), media_type.c_str(),
243 volume_name.c_str()) != 15) {
244 Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
246 unbash_spaces(dev_name);
247 dev = (DEVICE *)GetResWithName(R_DEVICE, dev_name.c_str());
251 unbash_spaces(changer_name);
252 unbash_spaces(media_type);
253 unbash_spaces(volume_name);
254 bstrncpy(dev->ChangerName, changer_name.c_str(), sizeof(dev->ChangerName));
255 bstrncpy(dev->MediaType, media_type.c_str(), sizeof(dev->MediaType));
256 bstrncpy(dev->VolumeName, volume_name.c_str(), sizeof(dev->VolumeName));
257 /* Note, these are copied because they are boolean rather than
260 dev->open = dev_open;
261 dev->append = dev_append;
262 dev->read = dev_read;
263 dev->labeled = dev_labeled;
264 dev->offline = dev_offline;
265 dev->autoselect = dev_autoselect;
266 dev->autochanger = dev_autochanger > 0;
267 dev->num_drives = dev_autochanger; /* does double duty */
268 dev->PoolId = dev_PoolId;
269 dev->num_writers = dev_num_writers;
270 dev->max_writers = dev_max_writers;
271 dev->reserved = dev_reserved;
281 static char *find_msg_start(char *msg)
285 skip_nonspaces(&p); /* skip message type */
287 skip_nonspaces(&p); /* skip Job */
288 skip_spaces(&p); /* after spaces come the message */
293 * Get response from FD or SD to a command we
294 * sent. Check that the response agrees with what we expect.
296 * Returns: false on failure
299 bool response(JCR *jcr, BSOCK *bs, char *resp, const char *cmd, e_prtmsg prtmsg)
303 if (is_bnet_error(bs)) {
306 if ((n = bget_dirmsg(bs)) >= 0) {
307 if (strcmp(bs->msg, resp) == 0) {
310 if (prtmsg == DISPLAY_ERROR) {
311 Jmsg(jcr, M_FATAL, 0, _("Bad response to %s command: wanted %s, got %s\n"),
316 Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"),
317 cmd, bnet_strerror(bs));