#include <sys/param.h>
#include <sys/syslog.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/uio.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <rpc/rpc.h>
#include <rpc/pmap_clnt.h>
#include <rpc/pmap_prot.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <pthread.h>
#include "common.h"
int nfstcpsock;
static void *
nfsd_server_thread(__unused void *arg)
{
set_thread_sigmask();
if (nfssvc(NFSSVC_NFSD, NULL) < 0)
log(LOG_ERR, "nfssvc: %s (%d)", strerror(errno), errno);
DEBUG(1, "nfsd thread exiting.");
return (NULL);
}
void *
nfsd_accept_thread(__unused void *arg)
{
struct nfsd_args nfsdargs;
fd_set ready, sockbits;
struct sockaddr_in inetpeer;
socklen_t len;
int msgsock, on = 1;
struct timeval tv;
set_thread_sigmask();
FD_ZERO(&sockbits);
if (config.tcp)
FD_SET(nfstcpsock, &sockbits);
while (!gotterm) {
tv.tv_sec = 3600;
tv.tv_usec = 0;
ready = sockbits;
if (select(nfstcpsock+1, ((nfstcpsock < 0) ? NULL : &ready), NULL, NULL, &tv) < 0) {
if (errno == EINTR)
continue;
log(LOG_ERR, "select failed: %s (%d)", strerror(errno), errno);
break;
}
if (config.tcp && FD_ISSET(nfstcpsock, &ready)) {
len = sizeof(inetpeer);
if ((msgsock = accept(nfstcpsock, (struct sockaddr *)&inetpeer, &len)) < 0) {
log(LOG_WARNING, "accept failed: %s (%d)", strerror(errno), errno);
continue;
}
DEBUG(1, "NFS socket accepted");
memset(inetpeer.sin_zero, 0, sizeof(inetpeer.sin_zero));
if (setsockopt(msgsock, SOL_SOCKET,
SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
log(LOG_NOTICE, "setsockopt SO_KEEPALIVE: %s (%d)", strerror(errno), errno);
nfsdargs.sock = msgsock;
nfsdargs.name = (caddr_t)&inetpeer;
nfsdargs.namelen = sizeof(inetpeer);
nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
close(msgsock);
}
}
return (NULL);
}
void
nfsd_start_server_threads(int count)
{
int threadcnt, i, rv;
pthread_t thd;
threadcnt = 0;
for (i=0; i < count; i++) {
rv = pthread_create(&thd, &pattr, nfsd_server_thread, NULL);
if (rv) {
log(LOG_ERR, "pthread_create: %s (%d)", strerror(rv), rv);
continue;
}
threadcnt++;
}
DEBUG(1, "Started %d of %d new nfsd threads", threadcnt, count);
if (!threadcnt)
log(LOG_ERR, "unable to start any nfsd threads");
if (threadcnt != count)
log(LOG_WARNING, "only able to create %d of %d nfsd threads", threadcnt, count);
}
void
nfsd(void)
{
struct nfsd_args nfsdargs;
struct sockaddr_in inetaddr;
int rv;
int on, sock;
pthread_t thd;
nfstcpsock = -1;
if (config.udp) {
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
log(LOG_ERR, "can't create NFS/UDP socket");
exit(1);
}
inetaddr.sin_family = AF_INET;
inetaddr.sin_addr.s_addr = INADDR_ANY;
inetaddr.sin_port = htons(config.port);
inetaddr.sin_len = sizeof(inetaddr);
if (bind(sock, (struct sockaddr *)&inetaddr, sizeof(inetaddr)) < 0) {
sleep(6);
if (bind(sock, (struct sockaddr *)&inetaddr, sizeof(inetaddr)) < 0) {
log(LOG_ERR, "can't bind NFS/UDP addr");
exit(1);
}
}
nfsdargs.sock = sock;
nfsdargs.name = NULL;
nfsdargs.namelen = 0;
if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) {
log(LOG_ERR, "can't add NFS/UDP socket");
exit(1);
}
close(sock);
}
on = 1;
if (config.tcp) {
if ((nfstcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
log(LOG_ERR, "can't create NFS/TCP socket");
exit(1);
}
if (setsockopt(nfstcpsock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
log(LOG_WARNING, "setsockopt SO_REUSEADDR: %s (%d)", strerror(errno), errno);
inetaddr.sin_family = AF_INET;
inetaddr.sin_addr.s_addr = INADDR_ANY;
inetaddr.sin_port = htons(config.port);
inetaddr.sin_len = sizeof(inetaddr);
if (bind(nfstcpsock, (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) {
log(LOG_ERR, "can't bind NFS/TCP addr");
exit(1);
}
if (listen(nfstcpsock, 128) < 0) {
log(LOG_ERR, "NFS listen failed");
exit(1);
}
}
sysctl_set("vfs.generic.nfs.server.nfsd_thread_max", config.nfsd_threads);
nfsd_start_server_threads(config.nfsd_threads);
rv = pthread_create(&thd, &pattr, nfsd_accept_thread, NULL);
if (rv) {
log(LOG_ERR, "pthread_create: %s (%d)", strerror(rv), rv);
exit(1);
}
}