NetBSD/sbin/nfsd/nfsd.c

377 lines
9.9 KiB
C

/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Rick Macklem at The University of Guelph.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1989 Regents of the University of California.\n\
All rights reserved.\n";
#endif not lint
#ifndef lint
/*static char sccsid[] = "from: @(#)nfsd.c 5.10 (Berkeley) 4/24/91";*/
static char rcsid[] = "$Id: nfsd.c,v 1.9 1993/12/05 13:41:07 deraadt Exp $";
#endif not lint
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <stdio.h>
#include <syslog.h>
#include <fcntl.h>
#include <string.h>
#include <netdb.h>
#include <rpc/rpc.h>
#include <rpc/pmap_clnt.h>
#include <rpc/pmap_prot.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsv2.h>
#include <machine/vmparam.h> /* these are for PS_STRINGS */
#include <sys/exec.h>
/* Global defs */
#ifdef DEBUG
#define syslog(e, s) fprintf(stderr,(s))
int debug = 1;
#else
int debug = 0;
#endif
struct hadr {
u_long ha_sad;
struct hadr *ha_next;
};
struct hadr hphead;
char **Argv = NULL; /* pointer to argument vector */
char *LastArg = NULL; /* end of argv */
void reapchild(),not_nfsserver();;
/*
* Nfs server daemon mostly just a user context for nfssvc()
* 1 - do file descriptor and signal cleanup
* 2 - create server socket
* 3 - register socket with portmap
* For SOCK_DGRAM, just fork children and send them into the kernel
* by calling nfssvc()
* For connection based sockets, loop doing accepts. When you get a new socket
* from accept, fork a child that drops into the kernel via. nfssvc.
* This child will return from nfssvc when the connection is closed, so
* just shutdown() and exit().
* The arguments are:
* -t - support tcp nfs clients
* -u - support udp nfs clients
*/
main(argc, argv, envp)
int argc;
char *argv[], *envp[];
{
register int i;
register char *cp, *cp2;
register struct hadr *hp;
int udpcnt, sock, msgsock, tcpflag = 0, udpflag = 0, ret, len;
int reregister = 0;
char opt;
extern int optind;
extern char *optarg;
struct sockaddr_in saddr, msk, mtch, peername;
bzero((char *)&saddr, sizeof saddr);
bzero((char *)&msk, sizeof msk);
bzero((char *)&mtch, sizeof mtch);
/*
* Save start and extent of argv for setproctitle.
*/
Argv = argv;
if (envp == 0 || *envp == 0)
envp = argv;
while (*envp)
envp++;
LastArg = envp[-1] + strlen(envp[-1]);
while ((opt = getopt(argc, argv, "rt:u:")) != EOF)
switch (opt) {
case 'r':
reregister++;
break;
case 't':
tcpflag++;
if (cp = index(optarg, ',')) {
*cp++ = '\0';
msk.sin_addr.s_addr = inet_addr(optarg);
if (msk.sin_addr.s_addr == -1)
usage();
if (cp2 = index(cp, ','))
*cp2++ = '\0';
mtch.sin_addr.s_addr = inet_addr(cp);
if (mtch.sin_addr.s_addr == -1)
usage();
cp = cp2;
hphead.ha_next = (struct hadr *)0;
while (cp) {
if (cp2 = index(cp, ','))
*cp2++ = '\0';
hp = (struct hadr *)
malloc(sizeof (struct hadr));
hp->ha_sad = inet_addr(cp);
if (hp->ha_sad == -1)
usage();
hp->ha_next = hphead.ha_next;
hphead.ha_next = hp;
cp = cp2;
}
} else
usage();
break;
case 'u':
udpflag++;
if (cp = index(optarg, ',')) {
*cp++ = '\0';
msk.sin_addr.s_addr = inet_addr(optarg);
if (msk.sin_addr.s_addr == -1)
usage();
if (cp2 = index(cp, ','))
*cp2++ = '\0';
mtch.sin_addr.s_addr = inet_addr(cp);
if (mtch.sin_addr.s_addr == -1)
usage();
if (cp2)
udpcnt = atoi(cp2);
if (udpcnt < 1 || udpcnt > 20)
udpcnt = 1;
} else
usage();
break;
default:
case '?':
usage();
}
/*
* Default, if neither UDP nor TCP is specified,
* is to support UDP only; a numeric argument indicates
* the number of server daemons to run.
*/
if (udpflag == 0 && tcpflag == 0) {
if (argc > 1)
udpcnt = atoi(*++argv);
if (udpcnt < 1 || udpcnt > 20)
udpcnt = 1;
msk.sin_addr.s_addr = mtch.sin_addr.s_addr = 0;
udpflag++;
}
if (debug == 0) {
daemon(0, 0);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
signal(SIGHUP, SIG_IGN);
}
signal(SIGCHLD, reapchild);
signal(SIGSYS, not_nfsserver);
if (nfssvc(-1, NULL, 0, NULL, 0) >= 0) {
syslog(LOG_ERR, "bad arguments didn't cause error");
exit(1);
}
if (reregister) {
if (udpflag && !pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP,
NFS_PORT)) {
fprintf(stderr,
"Can't register with portmap for UDP\n");
exit(1);
}
if (tcpflag && !pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP,
NFS_PORT)) {
fprintf(stderr,
"Can't register with portmap for TCP\n");
exit(1);
}
exit(0);
}
openlog("nfsd:", LOG_PID, LOG_DAEMON);
#ifdef notdef
/* why? unregisters both protocols even if we restart only one */
pmap_unset(RPCPROG_NFS, NFS_VER2);
#endif
if (udpflag) {
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
syslog(LOG_ERR, "Can't create socket");
exit(1);
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = INADDR_ANY;
saddr.sin_port = htons(NFS_PORT);
if (bind(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
syslog(LOG_ERR, "Can't bind addr");
exit(1);
}
if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT)) {
syslog(LOG_ERR, "Can't register with portmap");
exit(1);
}
/*
* Send the nfs datagram servers
* right down into the kernel
*/
for (i = 0; i < udpcnt; i++)
if (fork() == 0) {
setproctitle("nfsd-udp",
(struct sockaddr_in *)NULL);
ret = nfssvc(sock, &msk, sizeof(msk),
&mtch, sizeof(mtch));
if (ret < 0)
syslog(LOG_ERR, "nfssvc() failed %m");
exit(1);
}
close(sock);
}
/*
* Now set up the master STREAM server waiting for tcp connections.
*/
if (tcpflag) {
int on = 1;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
syslog(LOG_ERR, "Can't create socket");
exit(1);
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(char *) &on, sizeof(on)) < 0)
syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = INADDR_ANY;
saddr.sin_port = htons(NFS_PORT);
if (bind(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
syslog(LOG_ERR, "Can't bind addr");
exit(1);
}
if (listen(sock, 5) < 0) {
syslog(LOG_ERR, "Listen failed");
exit(1);
}
if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) {
syslog(LOG_ERR, "Can't register with portmap");
exit(1);
}
setproctitle("nfsd-listen", (struct sockaddr_in *)NULL);
/*
* Loop forever accepting connections and sending the children
* into the kernel to service the mounts.
*/
for (;;) {
len = sizeof(peername);
if ((msgsock = accept(sock,
(struct sockaddr *)&peername, &len)) < 0) {
syslog(LOG_ERR, "Accept failed: %m");
exit(1);
}
if ((peername.sin_addr.s_addr & msk.sin_addr.s_addr) !=
mtch.sin_addr.s_addr) {
hp = hphead.ha_next;
while (hp) {
if (peername.sin_addr.s_addr ==
hp->ha_sad)
break;
hp = hp->ha_next;
}
if (hp == NULL) {
shutdown(msgsock, 2);
close(msgsock);
continue;
}
}
if (fork() == 0) {
close(sock);
setproctitle("nfsd-tcp", &peername);
if (setsockopt(msgsock, SOL_SOCKET,
SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0)
syslog(LOG_ERR,
"setsockopt SO_KEEPALIVE: %m");
ret = nfssvc(msgsock, &msk, sizeof(msk),
&mtch, sizeof(mtch));
shutdown(msgsock, 2);
if (ret < 0)
syslog(LOG_NOTICE,
"Nfssvc STREAM Failed");
exit(1);
}
close(msgsock);
}
}
}
usage()
{
fprintf(stderr, "nfsd [-t msk,mtch[,addrs]] [-u msk,mtch,numprocs]\n");
exit(1);
}
void
reapchild()
{
while (wait3((int *) NULL, WNOHANG, (struct rusage *) NULL))
;
}
setproctitle(a, sin)
char *a;
struct sockaddr_in *sin;
{
register char *cp;
static char buf[80];
if (sin)
(void) sprintf(buf, "%s [%s]", a, inet_ntoa(sin->sin_addr));
else
(void) sprintf(buf, "%s", a);
PS_STRINGS->ps_nargvstr = 1;
PS_STRINGS->ps_argvstr = buf;
}
void not_nfsserver()
{
syslog(LOG_ERR, "not configured as NFS server");
exit(1);
}