/*
** Copyright 2000-2004 University of Illinois Board of Trustees
** Copyright 2000-2004 Mark D. Roth
** All rights reserved.
**
** handle.c - libphclient code to initialize a PH handle
**
** Mark D. Roth <roth@feep.net>
*/
#include <internal.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/param.h>
#ifdef STDC_HEADERS
# include <string.h>
# include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_ARPA_AIXRCMDS_H
# include <arpa/aixrcmds.h>
#endif
const char libphclient_version[] = PACKAGE_VERSION;
const int libphclient_is_thread_safe =
#if defined(HAVE_GETHOSTBYNAME_R) && defined(HAVE_GETSERVBYNAME_R) && defined(HAVE_GETPWUID_R)
1;
#else
0;
#endif
/*
** ph_open_local() - opens a pipe to a local qi process.
** returns: 0 on success, -1 on error (and sets errno)
*/
static int
ph_open_local(PH *ph, char *qi_path)
{
pid_t pid;
if (pipe(ph->ph_rfd) == -1)
return -1;
if (pipe(ph->ph_wfd) == -1)
{
close(ph->ph_rfd[0]);
ph->ph_rfd[0] = -1;
close(ph->ph_rfd[1]);
ph->ph_rfd[1] = -1;
return -1;
}
if ((pid = fork()) == -1)
{
close(ph->ph_rfd[0]);
ph->ph_rfd[0] = -1;
close(ph->ph_rfd[1]);
ph->ph_rfd[1] = -1;
close(ph->ph_wfd[0]);
ph->ph_wfd[0] = -1;
close(ph->ph_wfd[1]);
ph->ph_wfd[1] = -1;
return -1;
}
/* child sets its I/O to the pipe and exec's qi */
if (pid == 0)
{
if (dup2(ph->ph_wfd[0], 0) == -1
|| dup2(ph->ph_rfd[1], 1) == -1
|| dup2(ph->ph_rfd[1], 2) == -1
|| close(ph->ph_rfd[1]) == -1
|| close(ph->ph_rfd[0]) == -1
|| close(ph->ph_wfd[1]) == -1
|| close(ph->ph_wfd[0]) == -1)
_exit(1);
if (strchr(qi_path, '/') != NULL)
execl(qi_path, "qi", "-q", NULL);
else
execlp(qi_path, "qi", "-q", NULL);
/* NOTREACHED */
_exit(1);
}
/* parent closes the extra file descriptors and returns */
close(ph->ph_wfd[0]);
ph->ph_wfd[0] = -1;
close(ph->ph_rfd[1]);
ph->ph_rfd[1] = -1;
return 0;
}
#if MAXPATHLEN > MAXHOSTNAMELEN
# define PH_SERVER_LEN MAXPATHLEN
#else
# define PH_SERVER_LEN MAXHOSTNAMELEN
#endif
/*
** ph_rresvport() - thread-safe alternative to rresvport()
*/
static int
ph_rresvport(int sock)
{
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = INADDR_ANY;
for (local_addr.sin_port = IPPORT_RESERVED - 1;
local_addr.sin_port > 0;
local_addr.sin_port--)
{
if (bind(sock, (struct sockaddr *)&local_addr,
sizeof(local_addr)) == 0)
return 0;
}
errno = EADDRINUSE;
return -1;
}
/*
** ph_open_remote() - opens a socket to a remote PH server.
** returns: 0 on success, -1 on error (and sets errno)
*/
static int
ph_open_remote(PH *ph, char *server, int flags)
{
struct sockaddr_in addr;
int i;
char serverbuf[PH_SERVER_LEN];
char *cp;
struct hostent *hp;
#ifdef HAVE_GETHOSTBYNAME_R
struct hostent hent;
int h_errno_r;
#endif
struct servent *sp;
#ifdef HAVE_GETSERVBYNAME_R
struct servent sent;
#endif
#if defined(HAVE_GETHOSTBYNAME_R) || defined(HAVE_GETSERVBYNAME_R)
char buf[10240];
#endif
addr.sin_family = AF_INET;
strlcpy(serverbuf, server, sizeof(serverbuf));
/* set the server's port */
if ((cp = strchr(server, ':')) != NULL)
{
if ((cp - server) < sizeof(serverbuf))
serverbuf[cp - server] = '\0';
addr.sin_port = htons(atoi(++cp));
}
#ifdef HAVE_GETSERVBYNAME_R
else if (getservbyname_r(PH_SERVICE, "tcp", &sent,
buf, sizeof(buf), &sp) == 0
|| sp == NULL)
#else /* ! HAVE_GETSERVBYNAME_R */
else if ((sp = getservbyname(PH_SERVICE, "tcp")) != NULL)
#endif /* HAVE_GETSERVBYNAME_R */
addr.sin_port = sp->s_port;
else
addr.sin_port = htons(PH_PORT);
/* lookup the server's address */
#ifdef HAVE_GETHOSTBYNAME_R
if (gethostbyname_r(serverbuf, &hent, buf, sizeof(buf),
&hp, &h_errno_r) != 0
|| hp == NULL)
#else /* ! HAVE_GETHOSTBYNAME_R */
hp = gethostbyname(serverbuf);
if (hp == NULL)
#endif /* HAVE_GETHOSTBYNAME_R */
{
errno = EINVAL;
return -1;
}
for (i = 0; hp->h_addr_list[i] != NULL; i++)
{
memmove(&(addr.sin_addr.s_addr), hp->h_addr_list[i],
hp->h_length);
if ((ph->ph_rfd[0] = socket(PF_INET, SOCK_STREAM, 0)) == -1)
return -1;
/* bind to a reserved port if appropriate */
if (BIT_ISSET(flags, PH_OPEN_PRIVPORT)
&& ph_rresvport(ph->ph_rfd[0]) == -1)
return -1;
/* connect to server */
if (connect(ph->ph_rfd[0], (struct sockaddr *)&addr,
sizeof(addr)) == -1)
{
close(ph->ph_rfd[0]);
ph->ph_rfd[0] = -1;
if (BIT_ISSET(flags, PH_OPEN_ROUNDROBIN))
continue;
return -1;
}
return 0;
}
return -1;
}
/*
** ph_open() - opens a connection to the PH server.
** returns: 0 on success, -1 on error (and sets errno)
*/
int
ph_open(PH **ph, char *server, int flags,
ph_debug_func_t send_hook, ph_debug_func_t recv_hook,
void *hook_data)
{
int i;
char buf[PH_BUF_SIZE];
/* initialize PH data structure */
*ph = (PH *)calloc(1, sizeof(PH));
if (*ph == NULL)
return -1;
(*ph)->ph_rfd[0] = -1;
(*ph)->ph_rfd[1] = -1;
(*ph)->ph_wfd[0] = -1;
(*ph)->ph_wfd[1] = -1;
(*ph)->ph_sendhook = send_hook;
(*ph)->ph_recvhook = recv_hook;
(*ph)->ph_hook_data = hook_data;
/* create network buffer */
if (ph_buffer_new(&((*ph)->ph_buf), PH_BUF_SIZE,
ph_buffer_read, *ph) == -1)
return -1;
/* create mmgr handle */
if (ph_mmgr_init(&((*ph)->ph_mmgr)) == -1)
return -1;
/* connect to the server */
if (BIT_ISSET(flags, PH_OPEN_LOCAL))
i = ph_open_local(*ph, server);
else
i = ph_open_remote(*ph, server, flags);
if (i == 0
&& !BIT_ISSET(flags, PH_OPEN_DONTID))
{
snprintf(buf, sizeof(buf), "libphclient-%s",
libphclient_version);
if (ph_id(*ph, buf) == -1)
return -1;
}
return i;
}
/*
** ph_close() - closes a connection to the PH server.
** returns: 0 on success, -1 on error (and sets errno)
*/
int
ph_close(PH *ph, int flags)
{
int i, code;
if (!BIT_ISSET(flags, PH_CLOSE_FAST))
{
if (ph_send_command(ph, "quit") == -1)
return -1;
while (ph_get_response(ph, &code, NULL, 0) != -1
&& code != LR_OK);
}
if (ph->ph_wfd[1] >= 0)
{
if (close(ph->ph_wfd[1]) == -1)
return -1;
ph->ph_wfd[1] = -1;
}
if (close(ph->ph_rfd[0]) == -1)
return -1;
ph->ph_rfd[0] = -1;
if (ph_is_fieldinfo_cached(ph))
ph_free_fieldinfo(ph);
if (ph_is_optionlist_cached(ph))
ph_free_options(ph);
if (ph_is_siteinfo_cached(ph))
ph_free_siteinfo(ph);
if (ph->ph_auth != NULL)
ph_free_auth(ph->ph_auth);
if (ph->ph_mmgr != NULL)
ph_mmgr_cleanup(ph->ph_mmgr);
free(ph);
return 0;
}
/*
** ph_rfd() - return the read file descriptor associated with the PH handle.
*/
int
ph_rfd(PH *ph)
{
return ph->ph_rfd[0];
}
/*
** ph_wfd() - return the write file descriptor associated with the PH handle.
*/
int
ph_wfd(PH *ph)
{
return (ph->ph_wfd[1] >= 0
? ph->ph_wfd[1]
: ph->ph_rfd[0]);
}
syntax highlighted by Code2HTML, v. 0.9.1