/* $Cambridge: hermes/src/prayer/accountd/os_bsd.c,v 1.3 2004/10/01 13:25:51 dpc22 Exp $ */
/************************************************
* Prayer - a Webmail Interface *
************************************************/
/* Copyright (c) University of Cambridge 2000 - 2002 */
/* See the file NOTICE for conditions of use and distribution. */
#include "accountd.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <netdb.h>
#ifdef USE_SSL
#include <openssl/rand.h>
#endif
#include <libutil.h>
/* ====================================================================== */
BOOL os_socketpair(int *sockfd)
{
int rc;
do {
rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd);
} while ((rc < 0) && (errno == EINTR));
return ((rc == 0) ? T : NIL);
}
/* ====================================================================== */
int os_connect_unix_socket(char *name)
{
struct sockaddr_un serv_addr;
int sockfd, servlen;
/* Open the socket */
if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
return (-1);
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sun_family = AF_UNIX;
strcpy(serv_addr.sun_path, name);
servlen = sizeof(serv_addr);
if (connect(sockfd, (struct sockaddr *) &serv_addr, servlen) < 0) {
close(sockfd);
return (-1);
}
return (sockfd);
}
int os_connect_inet_socket(char *host, unsigned long port)
{
struct hostent *hostent;
struct sockaddr_in serv_addr;
int sockfd;
/* Open the socket */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return (-1);
/* Set up the socket */
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
if ((hostent = gethostbyname(host)) == NIL) {
close(sockfd);
return (-1);
}
bcopy(hostent->h_addr, (char *) &serv_addr.sin_addr,
hostent->h_length);
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))
< 0) {
close(sockfd);
return (-1);
}
return (sockfd);
}
/* ====================================================================== */
int os_bind_unix_socket(char *name)
{
struct sockaddr_un serv_addr;
int sockfd, servlen;
int i;
/* Generate well known connect address for frontend servers */
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sun_family = AF_UNIX;
strcpy(serv_addr.sun_path, name);
servlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family);
unlink(serv_addr.sun_path);
/* Open the socket */
if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
log_panic("[os_bind_unix_socket()] socket() failed, %s",
strerror(errno));
return (-1);
}
/* Set socket reuseaddr, otherwise bind will fail after fast stop/start */
i = 1;
if (setsockopt
(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *) &i, sizeof(int))) {
log_panic(("[os_bind_unix_socket()] setsockopt() failed: "
"couldn't set SO_REUSEADDR on init port, %s"),
strerror(errno));
close(sockfd);
return (-1);
}
/* bind() as UNIX domain socket */
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) <
0) {
log_panic("[os_bind_unix_socket()] bind() failed: %s",
strerror(errno));
close(sockfd);
return (-1);
}
/* Requests should queue on sockfd until we are ready to serve them */
if (listen(sockfd, 10) < 0) {
log_panic("[os_bind_unix_socket()] listen() failed, %s",
strerror(errno));
close(sockfd);
return (-1);
}
return (sockfd);
}
/* ====================================================================== */
int os_bind_inet_socket(unsigned long port)
{
int i, sockfd;
struct sockaddr_in serv_addr;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
log_panic("[os_bind_unix_socket()] socket() failed, %s",
strerror(errno));
return (-1);
}
/* Set Reuseaddr on this socket */
i = 1;
if (setsockopt
(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *) &i, sizeof(int))) {
log_panic(("[os_bind_inet_socket()] setsockopt() failed: "
"couldn't set SO_REUSEADDR on init port, %s"),
strerror(errno));
close(sockfd);
return (-1);
}
/* bind() as Internet domain socket */
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(port);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) <
0) {
log_panic("[os_bind_inet_socket()] bind() failed: %s",
strerror(errno));
close(sockfd);
return (-1);
}
if (listen(sockfd, 10) < 0) {
log_panic("[os_bind_inet_socket()] listen() failed, %s",
strerror(errno));
close(sockfd);
return (-1);
}
return (sockfd);
}
/* ====================================================================== */
int os_accept_unix(int sockfd)
{
struct sockaddr_un addr;
socklen_t len = (socklen_t) sizeof(struct sockaddr_un);
int newsockfd;
do {
newsockfd = accept(sockfd, (struct sockaddr *) &addr, &len);
} while ((newsockfd < 0) && (errno == EINTR));
if (newsockfd < 0) {
log_panic("[os_accept_unix()] accept() failed: %s",
strerror(errno));
close(newsockfd);
return (-1);
}
/* Set close on exec so subprocesses can't interfere */
if (fcntl(newsockfd, F_SETFD, FD_CLOEXEC) < 0) {
log_panic("[os_accept_unix()] fcntl() (close-on-exec) failed: %s",
strerror(errno));
close(newsockfd);
return (-1);
}
return (newsockfd);
}
int os_accept_inet(int sockfd, struct ipaddr *ipaddr)
{
struct sockaddr_in addr;
socklen_t len = (socklen_t) sizeof(struct sockaddr_in);
int newsockfd;
do {
newsockfd = accept(sockfd, (struct sockaddr *) &addr, &len);
} while ((newsockfd < 0) && (errno == EINTR));
if (newsockfd < 0) {
log_panic("[os_accept_inet()] accept() failed: %s",
strerror(errno));
close(newsockfd);
return (-1);
}
if (ipaddr)
ipaddr_set(ipaddr, 4, (unsigned char *) &addr.sin_addr);
/* Set close on exec so subprocesses can't interfere */
if (fcntl(newsockfd, F_SETFD, FD_CLOEXEC) < 0) {
log_panic("[os_accept_inet()] fcntl() (close-on-exec) failed: %s",
strerror(errno));
close(newsockfd);
return (-1);
}
return (newsockfd);
}
/* ====================================================================== */
/* Run child outside PTY */
BOOL os_run(char *cmdline, int *fdp, int *childpidp)
{
int fd[2];
int pid;
if (!os_socketpair(fd))
return(NIL);
if ((pid = fork()) < 0)
return (NIL);
if (pid == 0) {
dup2(fd[1], 0);
dup2(fd[1], 1);
dup2(fd[1], 2);
close(fd[0]);
close(fd[1]);
process_exec(cmdline);
/* NOTREACHED */
exit(1);
}
/* Parent process */
close(fd[1]);
*fdp = fd[0];
*childpidp = pid;
return (T);
}
/* BSD Psuedo-Terminal support */
BOOL os_run_pty(char *cmdline, int *fdp, int *childpidp)
{
int fd;
int pid;
if ((pid = forkpty(&fd, NIL, NIL, NIL)) < 0)
return (NIL);
if (pid == 0) {
/* Child process */
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
process_exec(cmdline);
/* NOTREACHED */
exit(1);
}
/* Parent process */
*fdp = fd;
*childpidp = pid;
return (T);
}
/* ====================================================================== */
/* Trivial SIG_CLD handler to prevent zombies from hanging around */
void os_child_reaper()
{
int status;
pid_t child;
do {
child = waitpid(0, &status, WNOHANG);
} while (child > 0);
}
pid_t os_waitpid_nohang()
{
int status;
return (waitpid(0, &status, WNOHANG));
}
BOOL os_signal_child_init(void (*fn) ())
{
struct sigaction act, oact;
sigemptyset(&act.sa_mask);
act.sa_handler = fn;
act.sa_flags = 0;
if (sigaction(SIGCHLD, &act, &oact) == 0)
return (T);
log_panic("[os_signal_child_init()] sigaction() failed: %s",
strerror(errno));
return (NIL);
}
BOOL os_signal_child_clear(void)
{
struct sigaction act, oact;
sigemptyset(&act.sa_mask);
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
if (sigaction(SIGCHLD, &act, &oact) == 0)
return (T);
log_panic("[os_signal_child_clear()] sigaction() failed: %s",
strerror(errno));
return (NIL);
}
/* ====================================================================== */
BOOL os_signal_alarm_init(void (*fn) ())
{
struct sigaction act, oact;
sigemptyset(&act.sa_mask);
act.sa_handler = fn;
act.sa_flags = 0;
if (sigaction(SIGALRM, &act, &oact) == 0)
return (T);
log_panic("[os_signal_alarm_init()] sigaction() failed: %s",
strerror(errno));
return (NIL);
}
BOOL os_signal_alarm_clear(void)
{
struct sigaction act, oact;
sigemptyset(&act.sa_mask);
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
if (sigaction(SIGALRM, &act, &oact) == 0)
return (T);
log_panic("[os_signal_alarm_clear()] sigaction() failed: %s",
strerror(errno));
return (NIL);
}
/* ====================================================================== */
BOOL os_lock_exclusive(int fd)
{
int rc;
do {
rc = flock(fd, LOCK_EX);
} while ((rc < 0) && (errno == EINTR));
return ((rc >= 0) ? T : NIL);
}
BOOL os_lock_shared(int fd)
{
int rc;
do {
rc = flock(fd, LOCK_SH);
} while ((rc < 0) && (errno == EINTR));
return ((rc >= 0) ? T : NIL);
}
BOOL os_lock_release(int fd)
{
int rc;
do {
rc = flock(fd, LOCK_UN);
} while ((rc < 0) && (errno == EINTR));
return ((rc >= 0) ? T : NIL);
}
BOOL os_lock_exclusive_allow_break(int fd)
{
return ((flock(fd, LOCK_EX) >= 0) ? T : NIL);
}
BOOL os_lock_shared_allow_break(int fd)
{
return ((flock(fd, LOCK_SH) >= 0) ? T : NIL);
}
BOOL os_lock_release_allow_break(int fd)
{
return ((flock(fd, LOCK_UN) >= 0) ? T : NIL);
}
/* ====================================================================== */
static int os_read(int fd, char *buf, unsigned long count)
{
int rc;
while (count > 0) {
rc = read(fd, buf, count);
if (rc > 0) {
buf += rc;
count -= rc;
} else if (rc == 0) /* EOF */
break;
else if (errno != EINTR) /* read() failed */
break;
}
return ((count == 0) ? T : NIL);
}
BOOL os_random(struct config * config, void *buffer, unsigned long count)
{
int fd;
if (config->egd_socket) {
if ((fd = os_connect_unix_socket(config->egd_socket)) < 0)
return (NIL);
while (count > 0) {
unsigned char buf[2];
unsigned long bytes = (count > 255) ? (count % 256) : count;
buf[0] = 0x02;
buf[1] = (unsigned char) bytes;
if (!((write(fd, buf, 2) == 2) && os_read(fd, buffer, bytes))) {
close(fd);
return (NIL);
}
count -= bytes;
}
close(fd);
return (T);
}
if ((fd = open("/dev/urandom", O_RDONLY)) < 0)
return (NIL);
if (!os_read(fd, buffer, count)) {
close(fd);
return (NIL);
}
close(fd);
return (T);
}
syntax highlighted by Code2HTML, v. 0.9.1