* file named AUTHORS, in the root directory of this distribution.
*/
/*
- * $Id: protocol.c,v 1.39 2006/02/28 16:36:13 martinea Exp $
+ * $Id: protocol.c,v 1.45 2006/05/25 17:07:31 martinea Exp $
*
* implements amanda protocol
*/
#include "amanda.h"
+#include "conffile.h"
#include "event.h"
#include "packet.h"
#include "security.h"
#include "protocol.h"
-/*#define PROTO_DEBUG*/
+#define proto_debug(i, ...) do { \
+ if ((i) <= debug_protocol) { \
+ dbprintf(__VA_ARGS__); \
+ } \
+} while (0)
/*
* Valid actions that can be passed to the state machine
*/
typedef enum {
- A_START, A_TIMEOUT, A_ERROR, A_RCVDATA, A_CONTPEND, A_PENDING,
- A_CONTINUE, A_FINISH, A_ABORT
-} action_t;
+ PA_START,
+ PA_TIMEOUT,
+ PA_ERROR,
+ PA_RCVDATA,
+ PA_CONTPEND,
+ PA_PENDING,
+ PA_CONTINUE,
+ PA_FINISH,
+ PA_ABORT
+} p_action_t;
/*
* The current state type. States are represented as function
* vectors.
*/
struct proto;
-typedef action_t (*pstate_t) P((struct proto *, action_t, pkt_t *));
+typedef p_action_t (*pstate_t)(struct proto *, p_action_t, pkt_t *);
/*
* This is a request structure that is wrapped around a packet while it
time_t origtime; /* orig start time of this request */
time_t curtime; /* time when this attempt started */
int connecttries; /* times we'll retry a connect */
- int reqtries; /* times we'll resend a REQ */
- int acktries; /* times we'll wait for an a ACK */
+ int resettries; /* times we'll resend a REQ */
+ int reqtries; /* times we'll wait for an a ACK */
pkt_t req; /* the actual wire request */
protocol_sendreq_callback continuation; /* call when req dies/finishes */
void *datap; /* opaque cookie passed to above */
- char *(*conf_fn) P((char *, void *));/* configuration function */
+ char *(*conf_fn)(char *, void *); /* configuration function */
} proto_t;
-#define CONNECT_TRIES 3 /* num retries after connect errors */
#define CONNECT_WAIT 5 /* secs between connect attempts */
#define ACK_WAIT 10 /* time (secs) to wait for ACK - keep short */
-#define ACK_TRIES 3 /* num retries after ACK_WAIT timeout */
-#define REQ_TRIES 2 /* num restarts (reboot/crash) */
+#define RESET_TRIES 2 /* num restarts (reboot/crash) */
#define CURTIME (time(0) - proto_init_time) /* time relative to start */
/* if no reply in an hour, just forget it */
#define DROP_DEAD_TIME(t) (CURTIME - (t) > (60 * 60))
/* get the size of an array */
-#define ASIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#define ASIZE(arr) (int)(sizeof(arr) / sizeof((arr)[0]))
/*
* Initialization time
/* local functions */
-#ifdef PROTO_DEBUG
-static const char *action2str P((action_t));
-static const char *pstate2str P((pstate_t));
-#endif
+static const char *action2str(p_action_t);
+static const char *pstate2str(pstate_t);
-static void connect_callback P((void *, security_handle_t *,
- security_status_t));
-static void connect_wait_callback P((void *));
-static void recvpkt_callback P((void *, pkt_t *, security_status_t));
+static void connect_callback(void *, security_handle_t *, security_status_t);
+static void connect_wait_callback(void *);
+static void recvpkt_callback(void *, pkt_t *, security_status_t);
-static action_t s_sendreq P((proto_t *, action_t, pkt_t *));
-static action_t s_ackwait P((proto_t *, action_t, pkt_t *));
-static action_t s_repwait P((proto_t *, action_t, pkt_t *));
-static void state_machine P((proto_t *, action_t, pkt_t *));
+static p_action_t s_sendreq(proto_t *, p_action_t, pkt_t *);
+static p_action_t s_ackwait(proto_t *, p_action_t, pkt_t *);
+static p_action_t s_repwait(proto_t *, p_action_t, pkt_t *);
+static void state_machine(proto_t *, p_action_t, pkt_t *);
/*
* -------------------
* Initialize globals.
*/
void
-protocol_init()
+protocol_init(void)
{
proto_init_time = time(NULL);
* for transmission.
*/
void
-protocol_sendreq(hostname, security_driver, conf_fn, req, repwait, continuation, datap)
- const char *hostname;
- const security_driver_t *security_driver;
- char *(*conf_fn) P((char *, void *));
- const char *req;
- time_t repwait;
- protocol_sendreq_callback continuation;
- void *datap;
+protocol_sendreq(
+ const char * hostname,
+ const security_driver_t * security_driver,
+ char * (*conf_fn)(char *, void *),
+ const char * req,
+ time_t repwait,
+ protocol_sendreq_callback continuation,
+ void * datap)
{
proto_t *p;
- p = alloc(sizeof(proto_t));
+ p = alloc(SIZEOF(proto_t));
p->state = s_sendreq;
p->hostname = stralloc(hostname);
p->security_driver = security_driver;
p->repwait = repwait;
p->origtime = CURTIME;
/* p->curtime set in the sendreq state */
- p->connecttries = CONNECT_TRIES;
- p->reqtries = REQ_TRIES;
- p->acktries = ACK_TRIES;
+ p->connecttries = getconf_int(CNF_CONNECT_TRIES);
+ p->resettries = RESET_TRIES;
+ p->reqtries = getconf_int(CNF_REQ_TRIES);
p->conf_fn = conf_fn;
- pkt_init(&p->req, P_REQ, req);
+ pkt_init(&p->req, P_REQ, "%s", req);
/*
* These are here for the caller
p->continuation = continuation;
p->datap = datap;
-#ifdef PROTO_DEBUG
- dbprintf(("%s: security_connect: host %s -> p %X\n",
- debug_prefix_time(": protocol"), hostname, (int)p));
-#endif
+ proto_debug(1, _("protocol: security_connect: host %s -> p %p\n"),
+ hostname, p);
- security_connect(p->security_driver, p->hostname, conf_fn, connect_callback, p);
+ security_connect(p->security_driver, p->hostname, conf_fn, connect_callback,
+ p, p->datap);
}
/*
* be had via security_geterror on the handle.
*/
static void
-connect_callback(cookie, security_handle, status)
- void *cookie;
- security_handle_t *security_handle;
- security_status_t status;
+connect_callback(
+ void * cookie,
+ security_handle_t * security_handle,
+ security_status_t status)
{
proto_t *p = cookie;
assert(p != NULL);
p->security_handle = security_handle;
-#ifdef PROTO_DEBUG
- dbprintf(("%s: connect_callback: p %X\n",
- debug_prefix_time(": protocol"), (int)p));
-#endif
+ proto_debug(1, _("protocol: connect_callback: p %p\n"), p);
switch (status) {
case S_OK:
- state_machine(p, A_START, NULL);
+ state_machine(p, PA_START, NULL);
break;
case S_TIMEOUT:
- security_seterror(p->security_handle, "timeout during connect");
+ security_seterror(p->security_handle, _("timeout during connect"));
/* FALLTHROUGH */
case S_ERROR:
* an error back to the caller.
*/
if (--p->connecttries == 0) {
- state_machine(p, A_ABORT, NULL);
+ state_machine(p, PA_ABORT, NULL);
} else {
-#ifdef PROTO_DEBUG
- dbprintf(("%s: connect_callback: p %X: retrying %s\n",
- debug_prefix_time(": protocol"), (int)p, p->hostname));
-#endif
+ proto_debug(1, _("protocol: connect_callback: p %p: retrying %s\n"),
+ p, p->hostname);
security_close(p->security_handle);
/* XXX overload p->security handle to hold the event handle */
p->security_handle =
* initial connection attempts failed.
*/
static void
-connect_wait_callback(cookie)
- void *cookie;
+connect_wait_callback(
+ void * cookie)
{
proto_t *p = cookie;
event_release((event_handle_t *)p->security_handle);
security_connect(p->security_driver, p->hostname, p->conf_fn,
- connect_callback, p);
+ connect_callback, p, p->datap);
}
* requests if they plan on doing a lot of work.
*/
void
-protocol_check()
+protocol_check(void)
{
/* arg == 1 means don't block */
* and are just waiting for all of the answers to come back.
*/
void
-protocol_run()
+protocol_run(void)
{
/* arg == 0 means block forever until no more events are left */
* with timeouts and successfull replies.
*/
static void
-state_machine(p, action, pkt)
- proto_t *p;
- action_t action;
- pkt_t *pkt;
+state_machine(
+ proto_t * p,
+ p_action_t action,
+ pkt_t * pkt)
{
pstate_t curstate;
- action_t retaction;
+ p_action_t retaction;
-#ifdef PROTO_DEBUG
- dbprintf(("%s: state_machine: initial: p %X action %s pkt %X\n",
- debug_prefix_time(": protocol"),
- (int)p, action2str(action), NULL));
-#endif
+ proto_debug(1, _("protocol: state_machine: initial: p %p action %s pkt %p\n"),
+ p, action2str(action), (void *)NULL);
assert(p != NULL);
- assert(action == A_RCVDATA || pkt == NULL);
+ assert(action == PA_RCVDATA || pkt == NULL);
assert(p->state != NULL);
for (;;) {
-#ifdef PROTO_DEBUG
- dbprintf(("%s: state_machine: p %X state %s action %s\n",
- debug_prefix_time(": protocol"),
- (int)p, pstate2str(p->state), action2str(action)));
+ proto_debug(1, _("protocol: state_machine: p %p state %s action %s\n"),
+ p, pstate2str(p->state), action2str(action));
if (pkt != NULL) {
- dbprintf(("%s: pkt: %s (t %d) orig REQ (t %d cur %d)\n",
- debug_prefix(": protocol"),
- pkt_type2str(pkt->type), (int)CURTIME,
- (int)p->origtime, (int)p->curtime));
- dbprintf(("%s: pkt contents:\n-----\n%s-----\n",
- debug_prefix(": protocol"), pkt->body));
+ proto_debug(1, _("protocol: pkt: %s (t %d) orig REQ (t %d cur %d)\n"),
+ pkt_type2str(pkt->type), (int)CURTIME,
+ (int)p->origtime, (int)p->curtime);
+ proto_debug(1, _("protocol: pkt contents:\n-----\n%s-----\n"),
+ pkt->body);
}
-#endif
/*
* p->state is a function pointer to the current state a request
* is in.
*
* We keep track of the last state we were in so we can make
- * sure states which return A_CONTINUE really have transitioned
+ * sure states which return PA_CONTINUE really have transitioned
* the request to a new state.
*/
curstate = p->state;
- if (action == A_ABORT)
+ if (action == PA_ABORT)
/*
* If the passed action indicates a terminal error, then we
* need to move to abort right away.
*/
- retaction = A_ABORT;
+ retaction = PA_ABORT;
else
/*
* Else we run the state and perform the action it
*/
retaction = (*curstate)(p, action, pkt);
-#ifdef PROTO_DEBUG
- dbprintf(("%s: state_machine: p %X state %s returned %s\n",
- debug_prefix_time(": protocol"),
- (int)p, pstate2str(p->state), action2str(retaction)));
-#endif
+ proto_debug(1, _("protocol: state_machine: p %p state %s returned %s\n"),
+ p, pstate2str(p->state), action2str(retaction));
/*
* The state function is expected to return one of the following
- * action_t's.
+ * p_action_t's.
*/
switch (retaction) {
* Setup to receive another pkt, and wait for the recv event
* to occur.
*/
- case A_CONTPEND:
+ case PA_CONTPEND:
(*p->continuation)(p->datap, pkt, p->security_handle);
/* FALLTHROUGH */
- case A_PENDING:
-#ifdef PROTO_DEBUG
- dbprintf(("%s: state_machine: p %X state %s: timeout %d\n",
- debug_prefix_time(": protocol"),
- (int)p, pstate2str(p->state), (int)p->timeout));
-#endif
+ case PA_PENDING:
+ proto_debug(1, _("protocol: state_machine: p %p state %s: timeout %d\n"),
+ p, pstate2str(p->state), (int)p->timeout);
/*
* Get the security layer to register a receive event for this
* security handle on our behalf. Have it timeout in p->timeout
* seconds.
*/
security_recvpkt(p->security_handle, recvpkt_callback, p,
- p->timeout);
+ (int)p->timeout);
return;
/*
* Request has moved to another state. Loop and run it again.
*/
- case A_CONTINUE:
+ case PA_CONTINUE:
assert(p->state != curstate);
-#ifdef PROTO_DEBUG
- dbprintf(("%s: state_machine: p %X: moved from %s to %s\n",
- debug_prefix_time(": protocol"),
- (unsigned int)p, pstate2str(curstate),
- pstate2str(p->state)));
-#endif
+ proto_debug(1, _("protocol: state_machine: p %p: moved from %s to %s\n"),
+ p, pstate2str(curstate),
+ pstate2str(p->state));
continue;
/*
* pkt to NULL to indicate failure to the callback, and then
* fall through to the common finish code.
*
- * Note that remote failures finish via A_FINISH, because they did
+ * Note that remote failures finish via PA_FINISH, because they did
* complete successfully locally.
*/
- case A_ABORT:
+ case PA_ABORT:
pkt = NULL;
/* FALLTHROUGH */
* Free up resources the request has used, call the continuation
* function specified by the caller and quit.
*/
- case A_FINISH:
+ case PA_FINISH:
(*p->continuation)(p->datap, pkt, p->security_handle);
security_close(p->security_handle);
amfree(p->hostname);
+ amfree(p->req.body);
amfree(p);
return;
assert(0);
break; /* in case asserts are turned off */
}
- /* NOTREACHED */
+ /*NOTREACHED*/
}
- /* NOTREACHED */
+ /*NOTREACHED*/
}
/*
* moves to the acknowledgement wait state. We return from the state
* machine at this point, and let the request be received from the network.
*/
-static action_t
-s_sendreq(p, action, pkt)
- proto_t *p;
- action_t action;
- pkt_t *pkt;
+static p_action_t
+s_sendreq(
+ proto_t * p,
+ p_action_t action,
+ pkt_t * pkt)
{
assert(p != NULL);
+ (void)action; /* Quiet unused parameter warning */
+ (void)pkt; /* Quiet unused parameter warning */
if (security_sendpkt(p->security_handle, &p->req) < 0) {
/* XXX should retry */
- security_seterror(p->security_handle, "error sending REQ: %s",
+ security_seterror(p->security_handle, _("error sending REQ: %s"),
security_geterror(p->security_handle));
- return (A_ABORT);
+ return (PA_ABORT);
}
/*
*/
p->state = s_ackwait;
p->timeout = ACK_WAIT;
- return (A_PENDING);
+ return (PA_PENDING);
}
/*
* The acknowledge wait state. We can enter here two ways:
*
* - the caller has received a packet, located the request for
- * that packet, and called us with an action of A_RCVDATA.
+ * that packet, and called us with an action of PA_RCVDATA.
*
* - the caller has determined that a request has timed out,
- * and has called us with A_TIMEOUT.
+ * and has called us with PA_TIMEOUT.
*
* Here we process the acknowledgment, which usually means that
* the client has agreed to our request and is working on it.
* It will later send a reply when finished.
*/
-static action_t
-s_ackwait(p, action, pkt)
- proto_t *p;
- action_t action;
- pkt_t *pkt;
+static p_action_t
+s_ackwait(
+ proto_t * p,
+ p_action_t action,
+ pkt_t * pkt)
{
assert(p != NULL);
* fail this request. Otherwise, move to the send state
* to retry the request.
*/
- if (action == A_TIMEOUT) {
+ if (action == PA_TIMEOUT) {
assert(pkt == NULL);
- if (--p->acktries == 0) {
- security_seterror(p->security_handle, "timeout waiting for ACK");
- return (A_ABORT);
+ if (--p->reqtries == 0) {
+ security_seterror(p->security_handle, _("timeout waiting for ACK"));
+ return (PA_ABORT);
}
p->state = s_sendreq;
- return (A_CONTINUE);
+ return (PA_CONTINUE);
}
- assert(action == A_RCVDATA);
+ assert(action == PA_RCVDATA);
assert(pkt != NULL);
/*
case P_ACK:
p->state = s_repwait;
p->timeout = p->repwait;
- return (A_PENDING);
+ return (PA_PENDING);
/*
* Received a NAK. The request failed, so free up the
* resources associated with it and return.
*
- * This should NOT return A_ABORT because it is not a local failure.
+ * This should NOT return PA_ABORT because it is not a local failure.
*/
case P_NAK:
- return (A_FINISH);
+ return (PA_FINISH);
/*
* The client skipped the ACK, and replied right away.
case P_REP:
case P_PREP:
p->state = s_repwait;
- return (A_CONTINUE);
+ return (PA_CONTINUE);
/*
* Unexpected packet. Requeue this request and hope
* we get what we want later.
*/
default:
- return (A_PENDING);
+ return (PA_PENDING);
}
}
/*
* The reply wait state. We enter here much like we do with s_ackwait.
*/
-static action_t
-s_repwait(p, action, pkt)
- proto_t *p;
- action_t action;
- pkt_t *pkt;
+static p_action_t
+s_repwait(
+ proto_t * p,
+ p_action_t action,
+ pkt_t * pkt)
{
pkt_t ack;
/*
* Timeout waiting for a reply.
*/
- if (action == A_TIMEOUT) {
+ if (action == PA_TIMEOUT) {
assert(pkt == NULL);
/*
* If we've blown our timeout limit, free up this packet and
* return.
*/
- if (p->reqtries == 0 || DROP_DEAD_TIME(p->origtime)) {
- security_seterror(p->security_handle, "timeout waiting for REP");
- return (A_ABORT);
+ if (p->resettries == 0 || DROP_DEAD_TIME(p->origtime)) {
+ security_seterror(p->security_handle, _("timeout waiting for REP"));
+ return (PA_ABORT);
}
/*
* We still have some tries left. Resend the request.
*/
- p->reqtries--;
+ p->resettries--;
p->state = s_sendreq;
- p->acktries = ACK_TRIES;
- return (A_CONTINUE);
+ p->reqtries = getconf_int(CNF_REQ_TRIES);
+ return (PA_CONTINUE);
}
- assert(action == A_RCVDATA);
+ assert(action == PA_RCVDATA);
+
+ /* Finish if we get a NAK */
+ if (pkt->type == P_NAK)
+ return (PA_FINISH);
/*
* We've received some data. If we didn't get a reply,
* the reply, cleanup this packet, and return.
*/
if (pkt->type != P_REP && pkt->type != P_PREP)
- return (A_PENDING);
+ return (PA_PENDING);
if(pkt->type == P_REP) {
- pkt_init(&ack, P_ACK, "");
+ pkt_init_empty(&ack, P_ACK);
if (security_sendpkt(p->security_handle, &ack) < 0) {
/* XXX should retry */
- security_seterror(p->security_handle, "error sending ACK: %s",
+ amfree(ack.body);
+ security_seterror(p->security_handle, _("error sending ACK: %s"),
security_geterror(p->security_handle));
- return (A_ABORT);
+ return (PA_ABORT);
}
- return (A_FINISH);
+ amfree(ack.body);
+ return (PA_FINISH);
}
else if(pkt->type == P_PREP) {
p->timeout = p->repwait - CURTIME + p->curtime + 1;
- return (A_CONTPEND);
+ return (PA_CONTPEND);
}
/* should never go here, shut up compiler warning */
- return (A_FINISH);
+ return (PA_FINISH);
}
/*
* event callback that receives a packet
*/
static void
-recvpkt_callback(cookie, pkt, status)
- void *cookie;
- pkt_t *pkt;
- security_status_t status;
+recvpkt_callback(
+ void * cookie,
+ pkt_t * pkt,
+ security_status_t status)
{
proto_t *p = cookie;
switch (status) {
case S_OK:
- state_machine(p, A_RCVDATA, pkt);
+ state_machine(p, PA_RCVDATA, pkt);
break;
case S_TIMEOUT:
- state_machine(p, A_TIMEOUT, NULL);
+ state_machine(p, PA_TIMEOUT, NULL);
break;
case S_ERROR:
- state_machine(p, A_ABORT, NULL);
+ state_machine(p, PA_ABORT, NULL);
break;
default:
assert(0);
* Misc functions
*/
-#ifdef PROTO_DEBUG
/*
* Convert a pstate_t into a printable form.
*/
static const char *
-pstate2str(pstate)
- pstate_t pstate;
+pstate2str(
+ pstate_t pstate)
{
static const struct {
pstate_t type;
for (i = 0; i < ASIZE(pstates); i++)
if (pstate == pstates[i].type)
return (pstates[i].name);
- return ("BOGUS PSTATE");
+ return (_("BOGUS PSTATE"));
}
/*
- * Convert an action_t into a printable form
+ * Convert an p_action_t into a printable form
*/
static const char *
-action2str(action)
- action_t action;
+action2str(
+ p_action_t action)
{
static const struct {
- action_t type;
+ p_action_t type;
const char name[12];
} actions[] = {
#define X(s) { s, stringize(s) }
- X(A_START),
- X(A_TIMEOUT),
- X(A_ERROR),
- X(A_RCVDATA),
- X(A_CONTPEND),
- X(A_PENDING),
- X(A_CONTINUE),
- X(A_FINISH),
- X(A_ABORT),
+ X(PA_START),
+ X(PA_TIMEOUT),
+ X(PA_ERROR),
+ X(PA_RCVDATA),
+ X(PA_CONTPEND),
+ X(PA_PENDING),
+ X(PA_CONTINUE),
+ X(PA_FINISH),
+ X(PA_ABORT),
#undef X
};
int i;
for (i = 0; i < ASIZE(actions); i++)
if (action == actions[i].type)
return (actions[i].name);
- return ("BOGUS ACTION");
+ return (_("BOGUS ACTION"));
}
-#endif /* PROTO_DEBUG */