/* This file is part of gacopyz.
Copyright (C) 2007-2020 Sergey Poznyakoff
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include
int
_gacopyz_read_tm(gacopyz_iod_t iod, int tm, char *buf, size_t size)
{
int rc = MI_SUCCESS;
while (size) {
fd_set rset;
fd_set xset;
int res;
struct timeval to;
FD_ZERO(&rset);
FD_ZERO(&xset);
FD_SET(iod->sd, &rset);
FD_SET(iod->sd, &xset);
to = iod->timeout[tm];
res = select(iod->sd + 1, &rset, NULL, &xset, &to);
if (res == 0) {
gacopyz_io_log(iod,
SMI_LOG_ERR,
_("%s: connection timed out"),
"_gacopyz_read");
errno = ETIMEDOUT;
rc = MI_FAILURE;
break;
} else if (res < 0) {
if (errno == EINTR)
continue;
rc = MI_FAILURE;
break;
} else if (rc > 0) {
if (FD_ISSET(iod->sd, &xset)) {
gacopyz_io_log(iod,
SMI_LOG_ERR,
_("%s: exception on control fd"),
"_gacopyz_read");
rc = MI_FAILURE;
break;
}
/* Otherwise, FD_ISSET(iod->sd, &rset) is true */
}
res = read(iod->sd, buf, size);
if (res == -1) {
gacopyz_io_log(iod,
SMI_LOG_ERR,
_("%s: read failed: %s"),
"_gacopyz_read",
strerror(errno));
rc = MI_FAILURE;
break;
} else if (res == 0) {
gacopyz_io_log(iod,
SMI_LOG_ERR,
_("%s: end of file"),
"_gacopyz_read");
rc = MI_FAILURE;
break;
}
buf += res;
size -= res;
}
return rc;
}
int
_gacopyz_read(gacopyz_iod_t iod, char *buf, size_t size)
{
return _gacopyz_read_tm(iod, GACOPYZ_TO_READ, buf, size);
}
int
_gacopyz_write(gacopyz_iod_t iod, const char *buf, size_t size)
{
int rc = MI_SUCCESS;
while (size) {
fd_set wset;
fd_set xset;
int res;
struct timeval to;
FD_ZERO(&wset);
FD_ZERO(&xset);
FD_SET(iod->sd, &wset);
FD_SET(iod->sd, &xset);
to = iod->timeout[GACOPYZ_TO_READ];
res = select(iod->sd + 1, NULL, &wset, &xset, &to);
if (res == 0) {
gacopyz_io_log(iod,
SMI_LOG_ERR,
_("%s: connection timed out"),
"_gacopyz_write");
errno = ETIMEDOUT;
rc = MI_FAILURE;
break;
} else if (res < 0) {
if (errno == EINTR)
continue;
rc = MI_FAILURE;
break;
} else if (rc > 0) {
if (FD_ISSET(iod->sd, &xset)) {
gacopyz_io_log(iod,
SMI_LOG_ERR,
_("%s: exception on control fd"),
"_gacopyz_write");
rc = MI_FAILURE;
break;
}
/* Otherwise, FD_ISSET(iod->sd, &wset) is true */
}
res = write(iod->sd, buf, size);
if (res == -1) {
gacopyz_io_log(iod,
SMI_LOG_ERR,
_("%s: write failed: %s"),
"_gacopyz_write",
strerror(errno));
rc = MI_FAILURE;
break;
} else if (res == 0) {
gacopyz_io_log(iod,
SMI_LOG_ERR,
_("%s: wrote 0 bytes"),
"_gacopyz_write");
rc = MI_FAILURE;
break;
}
buf += res;
size -= res;
}
return rc;
}
union header {
struct {
gacopyz_uint32_t size;
unsigned char cmd;
} hdr;
char buf[5];
};
int
gacopyz_send_command(gacopyz_iod_t iod, int cmd, const void *data, size_t size)
{
int rc;
union header header;
gacopyz_io_log(iod, SMI_LOG_DEBUG,
_("send header: size=%lu, cmd=%c"),
size, cmd);
gacopyz_io_logdump(iod, _("send data"), data, size);
header.hdr.size = htonl(size + 1);
header.hdr.cmd = cmd;
rc = _gacopyz_write(iod, header.buf, sizeof header.buf);
if (rc != MI_SUCCESS)
return rc;
if (size)
rc = _gacopyz_write(iod, data, size);
return rc;
}
int
gacopyz_read_command(gacopyz_iod_t iod, int tm,
unsigned char *cmd, size_t *pcount,
char **pbuf, size_t *psize)
{
union header header;
size_t size;
int rc;
if ((rc = _gacopyz_read_tm(iod, tm, header.buf, sizeof header.buf))
!= MI_SUCCESS)
return rc;
size = ntohl(header.hdr.size) - 1;
if (size + 1 > *psize) {
char *p = realloc(*pbuf, size + 1);
if (!p) {
gacopyz_io_log(iod,
SMI_LOG_ERR, "%s", strerror(errno));
return MI_FAILURE;
}
*pbuf = p;
*psize = size + 1;
}
gacopyz_io_log(iod, SMI_LOG_DEBUG,
_("read header: size=%lu, cmd=%c"),
size, header.hdr.cmd);
if ((rc = _gacopyz_read(iod, *pbuf, size)) != MI_SUCCESS)
return rc;
(*pbuf)[size] = 0;
gacopyz_io_logdump(iod, _("read data"), *pbuf, size);
*pcount = size;
*cmd = header.hdr.cmd;
return MI_SUCCESS;
}