#include "cpdup.h"
#include "hclink.h"
#include "hcproto.h"
static void hcc_start_reply(hctransaction_t trans, struct HCHead *rhead);
static int hcc_finish_reply(hctransaction_t trans, struct HCHead *head);
int
hcc_connect(struct HostConf *hc, int readonly)
{
int fdin[2];
int fdout[2];
const char *av[32];
if (hc == NULL || hc->host == NULL)
return(0);
if (pipe(fdin) < 0)
return(-1);
if (pipe(fdout) < 0) {
close(fdin[0]);
close(fdin[1]);
return(-1);
}
if ((hc->pid = fork()) == 0) {
int n, m;
dup2(fdin[1], 1);
close(fdin[0]);
close(fdin[1]);
dup2(fdout[0], 0);
close(fdout[0]);
close(fdout[1]);
n = 0;
av[n++] = "ssh";
if (CompressOpt)
av[n++] = "-C";
for (m = 0; m < ssh_argc; m++)
av[n++] = ssh_argv[m];
av[n++] = "-T";
av[n++] = hc->host;
av[n++] = "cpdup";
av[n++] = (readonly ? "-RS" : "-S");
av[n++] = NULL;
execv("/usr/bin/ssh", (void *)av);
_exit(1);
} else if (hc->pid < 0) {
return(-1);
} else {
close(fdin[1]);
hc->fdin = fdin[0];
close(fdout[0]);
hc->fdout = fdout[1];
return(0);
}
}
static int
rc_badop(hctransaction_t trans __unused, struct HCHead *head)
{
head->error = EOPNOTSUPP;
return(0);
}
int
hcc_slave(int fdin, int fdout, struct HCDesc *descs, int count)
{
struct HostConf hcslave;
struct HCHead *head;
struct HCTransaction trans;
int (*dispatch[256])(hctransaction_t, struct HCHead *);
int i;
int r;
bzero(&hcslave, sizeof(hcslave));
bzero(&trans, sizeof(trans));
bzero(dispatch, sizeof(dispatch));
for (i = 0; i < count; ++i) {
struct HCDesc *desc = &descs[i];
assert(desc->cmd >= 0 && desc->cmd < 256);
dispatch[desc->cmd] = desc->func;
}
for (i = 0; i < 256; ++i) {
if (dispatch[i] == NULL)
dispatch[i] = rc_badop;
}
hcslave.fdin = fdin;
hcslave.fdout = fdout;
trans.hc = &hcslave;
for (;;) {
head = hcc_read_command(trans.hc, &trans);
if (head == NULL)
break;
head->error = 0;
hcc_start_reply(&trans, head);
r = dispatch[head->cmd & 255](&trans, head);
switch(r) {
case -2:
head->error = EINVAL;
break;
case -1:
head->error = errno;
break;
case 0:
break;
default:
assert(0);
break;
}
if (!hcc_finish_reply(&trans, head))
break;
}
return(0);
}
struct HCHead *
hcc_read_command(struct HostConf *hc, hctransaction_t trans)
{
struct HCHead tmp;
int aligned_bytes;
int need_swap;
int n;
int r;
if (trans == NULL)
fatal("cpdup hlink protocol error with %s", hc->host);
n = 0;
while (n < (int)sizeof(struct HCHead)) {
r = read(hc->fdin, (char *)&tmp + n, sizeof(struct HCHead) - n);
if (r <= 0)
goto fail;
n += r;
}
if (tmp.magic == HCMAGIC) {
need_swap = 0;
} else {
tmp.magic = hc_bswap32(tmp.magic);
if (tmp.magic != HCMAGIC)
fatal("magic mismatch with %s (%04x)", hc->host, tmp.id);
need_swap = 1;
tmp.bytes = hc_bswap32(tmp.bytes);
tmp.cmd = hc_bswap16(tmp.cmd);
tmp.id = hc_bswap16(tmp.id);
tmp.error = hc_bswap32(tmp.error);
}
assert(tmp.bytes >= (int)sizeof(tmp) && tmp.bytes < HC_BUFSIZE);
trans->swap = need_swap;
bcopy(&tmp, trans->rbuf, n);
aligned_bytes = HCC_ALIGN(tmp.bytes);
while (n < aligned_bytes) {
r = read(hc->fdin, trans->rbuf + n, aligned_bytes - n);
if (r <= 0)
goto fail;
n += r;
}
#ifdef DEBUG
hcc_debug_dump(trans, head);
#endif
trans->state = HCT_REPLIED;
return((void *)trans->rbuf);
fail:
trans->state = HCT_FAIL;
return(NULL);
}
hctransaction_t
hcc_start_command(struct HostConf *hc, int16_t cmd)
{
struct HCHead *whead;
hctransaction_t trans;
trans = &hc->trans;
whead = (void *)trans->wbuf;
whead->magic = HCMAGIC;
whead->bytes = 0;
whead->cmd = cmd;
whead->id = trans->id;
whead->error = 0;
trans->windex = sizeof(*whead);
trans->hc = hc;
trans->state = HCT_IDLE;
return(trans);
}
static void
hcc_start_reply(hctransaction_t trans, struct HCHead *rhead)
{
struct HCHead *whead = (void *)trans->wbuf;
whead->magic = HCMAGIC;
whead->bytes = 0;
whead->cmd = rhead->cmd | HCF_REPLY;
whead->id = rhead->id;
whead->error = 0;
trans->windex = sizeof(*whead);
}
struct HCHead *
hcc_finish_command(hctransaction_t trans)
{
struct HostConf *hc;
struct HCHead *whead;
struct HCHead *rhead;
int aligned_bytes;
int16_t wcmd;
hc = trans->hc;
whead = (void *)trans->wbuf;
whead->bytes = trans->windex;
aligned_bytes = HCC_ALIGN(trans->windex);
trans->windex = 0;
trans->state = HCT_SENT;
if (write(hc->fdout, whead, aligned_bytes) != aligned_bytes) {
#ifdef __error
*__error = EIO;
#else
errno = EIO;
#endif
if (whead->cmd < 0x0010)
return(NULL);
fatal("cpdup lost connection to %s", hc->host);
}
wcmd = whead->cmd;
rhead = hcc_read_command(hc, trans);
if (trans->state != HCT_REPLIED || rhead->id != trans->id) {
#ifdef __error
*__error = EIO;
#else
errno = EIO;
#endif
if (wcmd < 0x0010)
return(NULL);
fatal("cpdup lost connection to %s", hc->host);
}
trans->state = HCT_DONE;
if (rhead->error) {
#ifdef __error
*__error = rhead->error;
#else
errno = rhead->error;
#endif
}
return (rhead);
}
int
hcc_finish_reply(hctransaction_t trans, struct HCHead *head)
{
struct HCHead *whead;
int aligned_bytes;
whead = (void *)trans->wbuf;
whead->bytes = trans->windex;
whead->error = head->error;
aligned_bytes = HCC_ALIGN(trans->windex);
#ifdef DEBUG
hcc_debug_dump(trans, whead);
#endif
return (write(trans->hc->fdout, whead, aligned_bytes) == aligned_bytes);
}
void
hcc_leaf_string(hctransaction_t trans, int16_t leafid, const char *str)
{
struct HCLeaf *item;
int bytes = strlen(str) + 1;
item = (void *)(trans->wbuf + trans->windex);
assert(trans->windex + sizeof(*item) + bytes < HC_BUFSIZE);
item->leafid = leafid;
item->reserved = 0;
item->bytes = sizeof(*item) + bytes;
bcopy(str, item + 1, bytes);
trans->windex = HCC_ALIGN(trans->windex + item->bytes);
}
void
hcc_leaf_data(hctransaction_t trans, int16_t leafid, const void *ptr, int bytes)
{
struct HCLeaf *item;
item = (void *)(trans->wbuf + trans->windex);
assert(trans->windex + sizeof(*item) + bytes < HC_BUFSIZE);
item->leafid = leafid;
item->reserved = 0;
item->bytes = sizeof(*item) + bytes;
bcopy(ptr, item + 1, bytes);
trans->windex = HCC_ALIGN(trans->windex + item->bytes);
}
void
hcc_leaf_int32(hctransaction_t trans, int16_t leafid, int32_t value)
{
struct HCLeaf *item;
item = (void *)(trans->wbuf + trans->windex);
assert(trans->windex + sizeof(*item) + sizeof(value) < HC_BUFSIZE);
item->leafid = leafid;
item->reserved = 0;
item->bytes = sizeof(*item) + sizeof(value);
*(int32_t *)(item + 1) = value;
trans->windex = HCC_ALIGN(trans->windex + item->bytes);
}
void
hcc_leaf_int64(hctransaction_t trans, int16_t leafid, int64_t value)
{
struct HCLeaf *item;
item = (void *)(trans->wbuf + trans->windex);
assert(trans->windex + sizeof(*item) + sizeof(value) < HC_BUFSIZE);
item->leafid = leafid;
item->reserved = 0;
item->bytes = sizeof(*item) + sizeof(value);
*(int64_t *)(item + 1) = value;
trans->windex = HCC_ALIGN(trans->windex + item->bytes);
}
int
hcc_check_space(hctransaction_t trans, struct HCHead *head, int n, int size)
{
size = HCC_ALIGN(size) + n * sizeof(struct HCLeaf);
if (size >= HC_BUFSIZE - trans->windex) {
struct HCHead *whead = (void *)trans->wbuf;
whead->cmd |= HCF_CONTINUE;
if (!hcc_finish_reply(trans, head))
return (0);
hcc_start_reply(trans, head);
}
return (1);
}
intptr_t
hcc_alloc_descriptor(struct HostConf *hc, void *ptr, int type)
{
struct HCHostDesc *hd;
struct HCHostDesc *hnew;
hnew = malloc(sizeof(struct HCHostDesc));
hnew->type = type;
hnew->data = ptr;
if ((hd = hc->hostdescs) != NULL) {
hnew->desc = hd->desc + 1;
} else {
hnew->desc = 2;
}
hnew->next = hd;
hc->hostdescs = hnew;
return(hnew->desc);
}
void *
hcc_get_descriptor(struct HostConf *hc, intptr_t desc, int type)
{
struct HCHostDesc *hd;
for (hd = hc->hostdescs; hd; hd = hd->next) {
if (hd->desc == desc && hd->type == type)
return(hd->data);
}
return(NULL);
}
void
hcc_set_descriptor(struct HostConf *hc, intptr_t desc, void *ptr, int type)
{
struct HCHostDesc *hd;
struct HCHostDesc **hdp;
for (hdp = &hc->hostdescs; (hd = *hdp) != NULL; hdp = &hd->next) {
if (hd->desc == desc) {
if (ptr) {
hd->data = ptr;
hd->type = type;
} else {
*hdp = hd->next;
free(hd);
}
return;
}
}
if (ptr) {
hd = malloc(sizeof(*hd));
hd->desc = desc;
hd->type = type;
hd->data = ptr;
hd->next = hc->hostdescs;
hc->hostdescs = hd;
}
}
struct HCLeaf *
hcc_nextitem(hctransaction_t trans, struct HCHead *head, struct HCLeaf *item)
{
int offset;
if (item == NULL)
item = (void *)(head + 1);
else
item = (void *)((char *)item + HCC_ALIGN(item->bytes));
offset = (char *)item - (char *)head;
if (offset == head->bytes)
return(NULL);
if (trans->swap) {
int64_t *i64ptr;
int32_t *i32ptr;
item->leafid = hc_bswap16(item->leafid);
item->bytes = hc_bswap32(item->bytes);
switch (item->leafid & LCF_TYPEMASK) {
case LCF_INT32:
i32ptr = (void *)(item + 1);
*i32ptr = hc_bswap32(*i32ptr);
break;
case LCF_INT64:
i64ptr = (void *)(item + 1);
*i64ptr = hc_bswap64(*i64ptr);
break;
}
}
assert(head->bytes >= offset + (int)sizeof(*item));
assert(head->bytes >= offset + item->bytes);
assert(item->bytes >= (int)sizeof(*item) && item->bytes < HC_BUFSIZE);
return (item);
}
struct HCLeaf *
hcc_nextchaineditem(struct HostConf *hc, struct HCHead *head)
{
hctransaction_t trans = &hc->trans;
struct HCLeaf *item = hcc_currentchaineditem(hc, head);
while ((item = hcc_nextitem(trans, head, item)) == NULL) {
if (!(head->cmd & HCF_CONTINUE))
return (NULL);
head = hcc_read_command(hc, trans);
if (trans->state != HCT_REPLIED || head->id != trans->id)
return (NULL);
}
trans->windex = (char *)item - (char *)head;
return (item);
}
struct HCLeaf *
hcc_currentchaineditem(struct HostConf *hc, struct HCHead *head)
{
hctransaction_t trans = &hc->trans;
if (trans->windex == 0)
return (NULL);
else
return ((void *) ((char *)head + trans->windex));
}
#ifdef DEBUG
void
hcc_debug_dump(hctransaction_t trans, struct HCHead *head)
{
struct HCLeaf *item;
int aligned_bytes = HCC_ALIGN(head->bytes);
fprintf(stderr, "DUMP %04x (%d)", (uint16_t)head->cmd, aligned_bytes);
if (head->cmd & HCF_REPLY)
fprintf(stderr, " error %d", head->error);
fprintf(stderr, "\n");
FOR_EACH_ITEM(item, trans, head) {
fprintf(stderr, " ITEM %04x DATA ", item->leafid);
switch(item->leafid & LCF_TYPEMASK) {
case LCF_INT32:
fprintf(stderr, "int32 %d\n", HCC_INT32(item));
break;
case LCF_INT64:
fprintf(stderr, "int64 %lld\n", HCC_INT64(item));
break;
case LCF_STRING:
fprintf(stderr, "\"%s\"\n", HCC_STRING(item));
break;
case LCF_BINARY:
fprintf(stderr, "(binary)\n");
break;
default:
printf("?\n");
}
}
}
#endif