/* This file is part of gacopyz.
Copyright (C) 2006 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 2, 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301 USA */
#include <gacopyz_priv.h>
int
gacopyz_init(gacopyz_conn_t *pconn, struct smfiDesc *desc)
{
size_t len;
char *name;
gacopyz_conn_t conn;
name = desc->xxfi_name ? desc->xxfi_name : "Unknown";
len = strlen(name);
if (desc->xxfi_version != SMFI_VERSION) {
gacopyz_logmsg(SMI_LOG_ERR,
"smfi_register: %s: version mismatch; "
"application %d != implementation %d",
name, desc->xxfi_version, SMFI_VERSION);
return MI_FAILURE;
}
conn = malloc(sizeof(*conn) + len + 1);
if (!conn) {
gacopyz_logmsg(SMI_LOG_ERR,
"smfi_register: %s: not enough memory", name);
return MI_FAILURE;
}
memset(conn, 0, sizeof(*conn));
conn->sd = -1;
conn->master_timeout.tv_usec = 0;
conn->master_timeout.tv_sec = 5;
conn->ctx_timeout.tv_usec = 0;
conn->ctx_timeout.tv_sec = GACOPYZ_TIMEOUT;
conn->logmask = SMI_DEFAULT_LOG_MASK;
conn->desc = *desc;
conn->desc.xxfi_name = (char*)(conn + 1);
strcpy(conn->desc.xxfi_name, name);
*pconn = conn;
return MI_SUCCESS;
}
void
gacopyz_free(gacopyz_conn_t conn)
{
free(conn);
}
static int
parse_connection(gacopyz_conn_t conn,
const char *cstr,
char **pproto, char **pport, char **ppath)
{
const char *p;
size_t len;
p = strchr(cstr, ':');
if (!p)
*pproto = NULL;
else {
len = p - cstr;
*pproto = malloc(len + 1);
if (!*pproto) {
gacopyz_log(conn, SMI_LOG_ERR,
"parse_connection: not enough memory");
return MI_FAILURE;
}
memcpy(*pproto, cstr, len);
(*pproto)[len] = 0;
cstr = p + 1;
}
p = strchr(cstr, '@');
if (!p)
*pport = NULL;
else {
len = p - cstr;
*pport = malloc(len + 1);
if (!*pport) {
free(*pproto);
gacopyz_log(conn, SMI_LOG_ERR,
"parse_connection: not enough memory");
return MI_FAILURE;
}
memcpy(*pport, cstr, len);
(*pport)[len] = 0;
cstr = p + 1;
}
*ppath = strdup(cstr);
if (!*ppath) {
free(*pproto);
free(*pport);
free(*ppath);
gacopyz_log(conn, SMI_LOG_ERR,
"parse_connection: not enough memory");
return MI_FAILURE;
}
return MI_SUCCESS;
}
static int
do_connect(gacopyz_conn_t conn,
const char *cstr, char *proto, char *port, char *path,
int backlog, int rmsocket)
{
union {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_un sun;
} addr;
int socklen;
int fd, flags;
int yes = 1;
if (!proto
|| strcmp(proto, "unix") == 0 || strcmp(proto, "local") == 0) {
struct stat st;
if (port) {
gacopyz_log(conn, SMI_LOG_ERR,
"%s: invalid connection type: %s; "
"port is meaningless for UNIX sockets",
conn->desc.xxfi_name, cstr);
return -1;
}
if (strlen(path) > sizeof addr.sun.sun_path) {
errno = EINVAL;
gacopyz_log(conn, SMI_LOG_ERR,
"%s: %s: UNIX socket name too long",
conn->desc.xxfi_name, path);
return -1;
}
addr.sa.sa_family = PF_UNIX;
socklen = sizeof(addr.sun);
strcpy(addr.sun.sun_path, path);
if (stat(path, &st)) {
if (errno != ENOENT) {
gacopyz_log(conn, SMI_LOG_ERR,
"%s: %s: cannot stat socket: %s",
conn->desc.xxfi_name, path,
strerror(errno));
return -1;
}
} else {
/* FIXME: Check permissions? */
if (!S_ISSOCK(st.st_mode)) {
gacopyz_log(conn, SMI_LOG_ERR,
"%s: %s: not a socket",
conn->desc.xxfi_name, path);
return -1;
}
if (rmsocket && unlink(path)) {
gacopyz_log(conn, SMI_LOG_ERR,
"%s: %s: cannot unlink: %s",
conn->desc.xxfi_name, path,
strerror(errno));
return -1;
}
}
} else if (strcmp(proto, "inet") == 0) {
short pnum;
long num;
char *p;
addr.sa.sa_family = PF_INET;
socklen = sizeof(addr.sin);
if (!port) {
gacopyz_log(conn, SMI_LOG_ERR,
"%s: invalid connection type: %s; "
"missing port number",
conn->desc.xxfi_name, cstr);
return -1;
}
num = pnum = strtol(port, &p, 0);
if (*p == 0) {
if (num != pnum) {
gacopyz_log(conn, SMI_LOG_ERR,
"%s: invalid connection type: %s; "
"bad port number",
conn->desc.xxfi_name, cstr);
return -1;
}
pnum = htons(pnum);
} else {
struct servent *sp = getservbyname(path, "tcp");
if (!sp) {
gacopyz_log(conn, SMI_LOG_ERR,
"%s: invalid connection type: %s; "
"unknown port name",
conn->desc.xxfi_name, cstr);
return -1;
}
pnum = sp->s_port;
}
if (!path)
addr.sin.sin_addr.s_addr = INADDR_ANY;
else {
struct hostent *hp = gethostbynam
|