#include "am.h"
#include "amq.h"
#include <signal.h>
#include <unistd.h>
#include <setjmp.h>
extern jmp_buf select_intr;
extern int select_intr_valid;
#ifdef HAS_TFS
#define svcudp_create svcudp2_create
extern SVCXPRT *svcudp2_create(int);
#endif
unsigned short nfs_port;
SVCXPRT *nfsxprt, *lnfsxprt;
SVCXPRT *amqp, *lamqp;
extern int fwd_sock;
int max_fds = -1;
#ifdef DEBUG
static void
checkup(void)
{
static int max_fd = 0;
static char *max_mem = 0;
int next_fd = dup(0);
extern caddr_t sbrk(int);
caddr_t next_mem = sbrk(0);
close(next_fd);
if (max_fd < next_fd) {
dlog("%d new fds allocated; total is %d",
next_fd - max_fd, next_fd);
max_fd = next_fd;
}
if (max_mem < next_mem) {
dlog("%#lx bytes of memory allocated; total is %#lx (%ld pages)",
(unsigned long)(next_mem - max_mem),
(unsigned long)next_mem,
((unsigned long)next_mem+getpagesize()-1)/getpagesize());
max_mem = next_mem;
}
}
#endif
static int
do_select(sigset_t *mask, sigset_t *omask, int fds, fd_set *fdp,
struct timeval *tvp)
{
int sig;
int nsel;
if ((sig = setjmp(select_intr))) {
select_intr_valid = 0;
switch (sig) {
case SIGINT:
case SIGTERM:
amd_state = Finishing;
reschedule_timeout_mp();
break;
}
nsel = -1;
errno = EINTR;
} else {
select_intr_valid = 1;
clock_valid = 0;
sigprocmask(SIG_SETMASK, omask, NULL);
nsel = select(fds, fdp, NULL, NULL,
tvp->tv_sec ? tvp : (struct timeval *) 0);
}
sigprocmask(SIG_BLOCK, mask, NULL);
if (do_mapc_reload < clocktime()) {
mapc_reload();
do_mapc_reload = clocktime() + ONE_HOUR;
}
return nsel;
}
static int
rpc_pending_now(void)
{
struct timeval tvv;
int nsel;
fd_set *fdsp;
int fdsn;
fdsn = howmany(max_fds+1, NFDBITS) * sizeof(fd_mask);
if ((fdsp = malloc(fdsn)) == NULL)
return(0);
memset(fdsp, 0, fdsn);
FD_SET(fwd_sock, fdsp);
tvv.tv_sec = tvv.tv_usec = 0;
nsel = select(max_fds+1, fdsp, NULL, NULL, &tvv);
if (nsel < 1) {
free(fdsp);
return(0);
}
if (FD_ISSET(fwd_sock, fdsp)) {
free(fdsp);
return(1);
}
free(fdsp);
return(0);
}
static serv_state
run_rpc(void)
{
sigset_t mask, omask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGHUP);
sigprocmask(SIG_BLOCK, &mask, &omask);
next_softclock = clocktime();
amd_state = Run;
while ((int)amd_state <= (int)Finishing) {
struct timeval tvv;
int nsel;
time_t now;
#ifdef __OpenBSD__
extern int __svc_fdsetsize;
extern fd_set *__svc_fdset;
fd_set *fdsp;
int fdsn = __svc_fdsetsize;
int bytes;
if (fwd_sock > fdsn)
fdsn = fwd_sock;
bytes = howmany(fdsn, NFDBITS) * sizeof(fd_mask);
fdsp = malloc(bytes);
memset(fdsp, 0, bytes);
memcpy(fdsp, __svc_fdset, bytes);
FD_SET(fwd_sock, fdsp);
#else
fd_set *fdsp;
int fdsn = FDSETSIZE;
bytes = howmany(fdsn, NFDBITS) * sizeof(fd_mask);
fdsp = malloc(bytes);
memcpy(fdsp, &svc_fdset, bytes);
FD_SET(fwd_sock, fdsp);
#endif
#ifdef DEBUG
checkup();
#endif
now = clocktime();
if (next_softclock <= now) {
if (amd_state == Finishing)
umount_exported();
tvv.tv_sec = softclock();
} else {
tvv.tv_sec = next_softclock - now;
}
tvv.tv_usec = 0;
if (amd_state == Finishing && last_used_map < 0) {
flush_mntfs();
amd_state = Quit;
break;
}
#ifdef DEBUG
if (tvv.tv_sec)
dlog("Select waits for %llds", (long long)tvv.tv_sec);
else
dlog("Select waits for Godot");
#endif
nsel = do_select(&mask, &omask, fdsn + 1, fdsp, &tvv);
switch (nsel) {
case -1:
if (errno == EINTR) {
#ifdef DEBUG
dlog("select interrupted");
#endif
continue;
}
perror("select");
break;
case 0:
#ifdef DEBUG
#endif
break;
default:
if (FD_ISSET(fwd_sock, fdsp)) {
FD_CLR(fwd_sock, fdsp);
--nsel;
do {
fwd_reply();
} while (rpc_pending_now() > 0);
}
if (nsel) {
#ifdef __OpenBSD__
svc_getreqset2(fdsp, fdsn);
#else
svc_getreqset(fdsp);
#endif
}
break;
}
free(fdsp);
}
sigprocmask(SIG_SETMASK, &omask, NULL);
if (amd_state == Quit)
amd_state = Done;
return amd_state;
}
static int
bindnfs_port(int so)
{
unsigned short port;
int error = bind_resv_port(so, &port);
if (error == 0)
nfs_port = port;
return error;
}
void
unregister_amq(void)
{
#ifdef DEBUG
Debug(D_AMQ)
#endif
(void) pmap_unset(AMQ_PROGRAM, AMQ_VERSION);
}
int
mount_automounter(pid_t ppid)
{
struct sockaddr_in sin;
int so, so2, nmount;
int sinlen;
int on = 1;
so = socket(AF_INET, SOCK_DGRAM, 0);
if (so < 0 || bindnfs_port(so) < 0) {
perror("Can't create privileged nfs port");
return 1;
}
if ((nfsxprt = svcudp_create(so)) == NULL ||
(amqp = svcudp_create(so)) == NULL) {
plog(XLOG_FATAL, "cannot create rpc/udp service");
return 2;
}
sinlen = sizeof sin;
if (getsockname(so, (struct sockaddr *)&sin, &sinlen) == -1) {
perror("Can't get information on socket");
return 1;
}
so2 = socket(AF_INET, SOCK_DGRAM, 0);
if (so2 < 0) {
perror("Can't create 2nd socket");
return 1;
}
setsockopt(so2, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(so2, (struct sockaddr *)&sin, sizeof sin) == -1) {
perror("Can't bind 2nd socket");
return 1;
}
if ((lnfsxprt = svcudp_create(so2)) == NULL ||
(lamqp = svcudp_create(so2)) == NULL) {
plog(XLOG_FATAL, "cannot create rpc/udp service");
return 2;
}
if (!svc_register(nfsxprt, NFS_PROGRAM, NFS_VERSION, nfs_program_2, 0)) {
plog(XLOG_FATAL, "unable to register (NFS_PROGRAM, NFS_VERSION, 0)");
return 3;
}
if (fwd_init() != 0)
return 3;
if (so > max_fds)
max_fds = so;
if (so2 > max_fds)
max_fds = so2;
if (fwd_sock > max_fds)
max_fds = fwd_sock;
make_root_node();
if (restart_existing_mounts)
restart();
nmount = mount_exported();
if (ppid)
kill(ppid, SIGQUIT);
if (nmount == 0) {
plog(XLOG_FATAL, "No work to do - quitting");
amd_state = Done;
return 0;
}
#ifdef DEBUG
Debug(D_AMQ) {
#endif
unregister_amq();
if (!svc_register(amqp, AMQ_PROGRAM, AMQ_VERSION, amq_program_57, IPPROTO_UDP)) {
plog(XLOG_FATAL, "unable to register (AMQ_PROGRAM, AMQ_VERSION, udp)");
return 3;
}
#ifdef DEBUG
}
#endif
reschedule_timeout_mp();
if (run_rpc() != Done) {
plog(XLOG_FATAL, "run_rpc failed");
amd_state = Done;
}
return 0;
}