#include <sys_defs.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <msg.h>
#include <mymalloc.h>
#include <split_at.h>
#include <vstream.h>
#include <connect.h>
#include <htable.h>
#include <attr.h>
#include <auto_clnt.h>
#include <attr_clnt.h>
struct ATTR_CLNT {
AUTO_CLNT *auto_clnt;
int (*connect) (const char *, int, int);
char *endpoint;
int timeout;
ATTR_CLNT_PRINT_FN print;
ATTR_CLNT_SCAN_FN scan;
};
static VSTREAM *attr_clnt_connect(void *context)
{
const char *myname = "attr_clnt_connect";
ATTR_CLNT *client = (ATTR_CLNT *) context;
VSTREAM *fp;
int fd;
fd = client->connect(client->endpoint, BLOCKING, client->timeout);
if (fd < 0) {
msg_warn("connect to %s: %m", client->endpoint);
return (0);
} else {
if (msg_verbose)
msg_info("%s: connected to %s", myname, client->endpoint);
fp = vstream_fdopen(fd, O_RDWR);
vstream_control(fp, VSTREAM_CTL_PATH, client->endpoint,
VSTREAM_CTL_TIMEOUT, client->timeout,
VSTREAM_CTL_END);
return (fp);
}
}
void attr_clnt_free(ATTR_CLNT *client)
{
myfree(client->endpoint);
auto_clnt_free(client->auto_clnt);
myfree((char *) client);
}
ATTR_CLNT *attr_clnt_create(const char *service, int timeout,
int max_idle, int max_ttl)
{
const char *myname = "attr_clnt_create";
char *transport = mystrdup(service);
char *endpoint;
ATTR_CLNT *client;
if ((endpoint = split_at(transport, ':')) == 0
|| *endpoint == 0 || *transport == 0)
msg_fatal("need service transport:endpoint instead of \"%s\"", service);
if (msg_verbose)
msg_info("%s: transport=%s endpoint=%s", myname, transport, endpoint);
client = (ATTR_CLNT *) mymalloc(sizeof(*client));
client->scan = attr_vscan_plain;
client->print = attr_vprint_plain;
client->endpoint = mystrdup(endpoint);
client->timeout = timeout;
if (strcmp(transport, "inet") == 0) {
client->connect = inet_connect;
} else if (strcmp(transport, "local") == 0) {
client->connect = LOCAL_CONNECT;
} else if (strcmp(transport, "unix") == 0) {
client->connect = unix_connect;
} else {
msg_fatal("invalid attribute transport name: %s", service);
}
client->auto_clnt = auto_clnt_create(max_idle, max_ttl,
attr_clnt_connect, (void *) client);
myfree(transport);
return (client);
}
int attr_clnt_request(ATTR_CLNT *client, int send_flags,...)
{
const char *myname = "attr_clnt_request";
VSTREAM *stream;
int count = 0;
va_list ap;
int type;
int recv_flags;
int err;
int ret;
#define SKIP_ARG(ap, type) { \
(void) va_arg(ap, char *); \
(void) va_arg(ap, type); \
}
for (;;) {
errno = 0;
if ((stream = auto_clnt_access(client->auto_clnt)) != 0
&& readable(vstream_fileno(stream)) == 0) {
errno = 0;
va_start(ap, send_flags);
err = (client->print(stream, send_flags, ap) != 0
|| vstream_fflush(stream) != 0);
va_end(ap);
if (err == 0) {
va_start(ap, send_flags);
while ((type = va_arg(ap, int)) != ATTR_TYPE_END) {
switch (type) {
case ATTR_TYPE_STR:
SKIP_ARG(ap, char *);
break;
case ATTR_TYPE_NUM:
SKIP_ARG(ap, int);
break;
case ATTR_TYPE_LONG:
SKIP_ARG(ap, long);
break;
case ATTR_TYPE_HASH:
SKIP_ARG(ap, HTABLE *);
break;
default:
msg_panic("%s: unexpected attribute type %d",
myname, type);
}
}
recv_flags = va_arg(ap, int);
ret = client->scan(stream, recv_flags, ap);
va_end(ap);
if (ret > 0)
return (ret);
}
}
if (++count >= 2
|| msg_verbose
|| (errno && errno != EPIPE && errno != ENOENT && errno != ECONNRESET))
msg_warn("problem talking to server %s: %m", client->endpoint);
if (count >= 2)
return (-1);
sleep(1);
auto_clnt_recover(client->auto_clnt);
}
}
void attr_clnt_control(ATTR_CLNT *client, int name,...)
{
char *myname = "attr_clnt_control";
va_list ap;
for (va_start(ap, name); name != ATTR_CLNT_CTL_END; name = va_arg(ap, int)) {
switch (name) {
case ATTR_CLNT_CTL_PROTO:
client->print = va_arg(ap, ATTR_CLNT_PRINT_FN);
client->scan = va_arg(ap, ATTR_CLNT_SCAN_FN);
break;
default:
msg_panic("%s: bad name %d", myname, name);
}
}
}