/* Distributed Checksum Clearinghouse * * ask for and administrative operation * * --C-LICENSE-- * $Revision: 1.46 $ */ #include "dcc_clnt.h" /* ask for an administrative operation */ DCC_OPS /* DCC_OP_INVALID=failed, else result */ dcc_aop(DCC_EMSG *emsg, /* result if DCC_OP_ERROR or _INVALID */ DCC_CLNT_CTXT *ctxt, DCC_CLNT_FGS clnt_fgs, /* DCC_CLNT_FG_* */ SRVR_INX anum, /* server index or NO_SRVR */ time_t dsecs, /* fudge timestamp by this */ DCC_AOPS aop, u_int32_t val1, u_char val2, u_char val3, u_char val4, void *val5, u_int val5_len, DCC_OP_RESP *resp, DCC_SOCKU *resp_su) /* IP address of server used */ { DCC_ADMN_REQ req; DCC_EMSG loc_emsg; DCC_OP_RESP resp0; char resp_buf[DCC_OPBUF]; memset(&req, 0, sizeof(req)); req.date = htonl(time(0) + dsecs); req.aop = aop; req.val1 = ntohl(val1); req.val2 = val2; req.val3 = val3; req.val4 = val4; if (val5_len != 0) { if (val5_len > DCC_MAX_ADMN_REQ_VAL5) dcc_logbad(EX_SOFTWARE, "bogus aop val5 length %d", val5_len); memcpy(&req.val5, val5, val5_len); } if (!resp) { resp = &resp0; } else if (clnt_fgs & DCC_CLNT_FG_RETRANS) { req.hdr.op_nums.r = resp->hdr.op_nums.r; } memset(resp, 0, sizeof(*resp)); if (!dcc_clnt_op(&loc_emsg, ctxt, clnt_fgs | DCC_CLNT_FG_NO_FAIL, &anum, 0, resp_su, &req.hdr, sizeof(req)-DCC_MAX_ADMN_REQ_VAL5+val5_len, DCC_OP_ADMN, resp, sizeof(*resp))) { dcc_pemsg(emsg_ex_code(&loc_emsg), emsg, "%s: %s", dcc_aop2str(resp_buf, sizeof(resp_buf), aop, val1), loc_emsg.c); return DCC_OP_INVALID; } if (resp->hdr.op == DCC_OP_OK) return DCC_OP_OK; if (resp->hdr.op == DCC_OP_ADMN) { /* clear signature after possible string */ int len = (ntohs(resp->hdr.len) - (sizeof(resp->resp) - sizeof(resp->resp.val.string))); if (len < ISZ(resp->resp.val.string)) resp->resp.val.string[len] = '\0'; return DCC_OP_ADMN; } if (resp->hdr.op == DCC_OP_ERROR) { dcc_pemsg(emsg_ex_code(emsg), emsg, "%s: %s", dcc_aop2str(resp_buf, sizeof(resp_buf), aop, val1), resp->resp.val.string); return DCC_OP_ERROR; } dcc_pemsg(EX_PROTOCOL, emsg, "%s unexpected response: %s", dcc_aop2str(0, 0, aop, val1), dcc_hdr_op2str(resp_buf, sizeof(resp_buf), &resp->hdr)); return DCC_OP_INVALID; } /* try for a long time or until the server hears */ u_char /* 1=ok, 0=failed */ dcc_aop_persist(DCC_EMSG *emsg, DCC_CLNT_CTXT *ctxt, DCC_CLNT_FGS clnt_fgs, u_char debug, DCC_AOPS aop, u_int32_t val1, int secs, /* try for this long */ DCC_OP_RESP *aop_resp) /* results here */ { struct timeval begin, now, op_start; char buf1[DCC_OPBUF]; char buf2[DCC_OPBUF]; u_char complained, fast; time_t us; DCC_OPS result; gettimeofday(&begin, 0); complained = 0; fast = 0; for (;;) { gettimeofday(&op_start, 0); result = dcc_aop(emsg, ctxt, clnt_fgs, NO_SRVR, 0, aop, val1, 0, 0, 0, 0, 0, aop_resp, 0); /* finished if that worked */ if (result == DCC_OP_ADMN || result == DCC_OP_OK) return 1; /* use the same transaction ID next time. * This kludge on the transaction ID makes all of our * requests appear to be retransmissions of a single request. * This is nice for operations such as DCC_AOP_FLOD_SHUTDOWN * that are not idempotent when the server has been stalled by * the operating system. */ if (aop_resp) clnt_fgs |= DCC_CLNT_FG_RETRANS; /* invent an error message if we do not have one */ if (result != DCC_OP_ERROR && result != DCC_OP_INVALID) dcc_pemsg(EX_UNAVAILABLE, emsg, "%s: %s", dcc_aop2str(buf1, sizeof(buf1), aop, val1), aop_resp ? dcc_hdr_op2str(buf2, sizeof(buf2), &aop_resp->hdr) : ""); /* deal with time change */ gettimeofday(&now, 0); us = tv_diff2us(&now, &begin); if (us < 0) { begin = op_start = now; } else if (us/DCC_US > secs) { return 0; /* eventually give up */ } us = tv_diff2us(&now, &op_start); if (us >= DCC_US) { fast = 0; } else { /* assume the server is dead if it persistently fails * immediately */ if ((result == DCC_OP_INVALID || result == DCC_OP_INVALID) && ++fast > 4) { if (debug) quiet_trace_msg("%s: assume dccd dead", dcc_aop2str(buf1, sizeof(buf1), aop, val1)); return 0; } /* delay 50 ms since the request didn't delay */ usleep(50*1000); } /* sometimes emit a message if we are going to try again */ if ((result != DCC_OP_ERROR && result != DCC_OP_INVALID) && (debug || !complained)) { complained = 1; dcc_error_msg("%s", emsg->c); } } }