Add two new utilities and two new daemons to /usr/src/usr.sbin that

are specifically used by the experimental nfsv4 subsystem.
  nfscbd - The NFSv4 client callback daemon.
  nfsuserd - The NFSv4 daemon that maps between user and group name
           and their corresponding uid/gid numbers.
  nfsdumpstate - A utility that dumps out the NFSv4 Open/Lock state.
  nfsrevoke - Administratively revokes an NFSv4 client, releasing all
           NFSv4 Open/Lock state it holds on the server.

Approved by:	kib (mentor)
This commit is contained in:
Rick Macklem 2009-05-26 15:19:04 +00:00
parent 4147dd02cd
commit 03914b0bb2
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=192811
13 changed files with 1813 additions and 0 deletions

View File

@ -110,7 +110,11 @@ SUBDIR= ${_ac} \
${_ndiscvt} \
${_ndp} \
newsyslog \
nfscbd \
nfsd \
nfsdumpstate \
nfsrevoke \
nfsuserd \
${_ngctl} \
${_nghook} \
nologin \

6
usr.sbin/nfscbd/Makefile Normal file
View File

@ -0,0 +1,6 @@
# $FreeBSD$
PROG= nfscbd
MAN= nfscbd.8
.include <bsd.prog.mk>

87
usr.sbin/nfscbd/nfscbd.8 Normal file
View File

@ -0,0 +1,87 @@
.\" Copyright (c) 2009 Rick Macklem, University of Guelph
.\" All rights reserved.
.\"
.\" 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.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
.\"
.\" $FreeBSD$
.\"
.Dd April 25, 2009
.Dt NFSCBD 8
.Os
.Sh NAME
.Nm nfscbd
.Tn NFSv4
client side callback daemon
.Sh SYNOPSIS
.Nm nfscbd
.Op Fl p Ar port_number
.Op Fl P Ar client_principal
.Sh DESCRIPTION
.Nm
runs on a client using
.Tn NFSv4
to handle callback requests from the NFSv4 server.
If no
.Nm
is running, NFSv4 mounts will still work, but the server will never issue
Open Delegations to the client.
.Pp
One callback server and one master server
are always started.
.Pp
The following options are available:
.Bl -tag -width Ds
.It Fl p Ar port_number
Specifies what port# the callback server should use.
.It Fl P Ar client_principal
Specifies the host based principal name to be used as the target for
callbacks over RPCSEC_GSS. For KerberosV, it must be in the client's
default keytab file.
This client_principal should be the same one specified by the
.Cm gssname
argument being used by nfsv4 mounts.
If you do not specify this argument, callbacks will still work over AUTH_SYS,
which is what many extant servers use even for RPCSEC_GSS mounts, as of 2009.
.El
.Pp
For example,
.Dq Li "nfscbd -p 7654 -P root"
starts the daemon to handle callbacks on port# 7654 and is using the host based
principal root@<client-host>.<dns-domain> as the callback target.
.Pp
.Nm
listens for service requests at the port
defined by NFSV4_CBPORT in /usr/include/fs/nfs/nfs.h, unless
.Fl p
has been specified.
For more information on what callbacks and Open Delegations do, see
.%T "Network File System (NFS) Version 4 Protocol" ,
RFC3530 .
.Pp
The
.Nm
utility exits 0 on success or >0 if an error occurred.
.Sh SEE ALSO
.Xr nfsv4 4 ,
.Xr mount_nfs 8
.Sh HISTORY
First introduced with the experimental nfs client for NFSv4 support in 2009.

380
usr.sbin/nfscbd/nfscbd.c Normal file
View File

@ -0,0 +1,380 @@
/*-
* Copyright (c) 2009 Rick Macklem, University of Guelph
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/linker.h>
#include <sys/module.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/stat.h>
#include <sys/ucred.h>
#include <sys/uio.h>
#include <sys/vnode.h>
#include <sys/wait.h>
#include <nfs/nfssvc.h>
#include <rpc/rpc.h>
#include <fs/nfs/nfsproto.h>
#include <fs/nfs/nfskpiport.h>
#include <fs/nfs/nfs.h>
#include <fs/nfs/rpcv2.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <netdb.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
/* Global defs */
#ifdef DEBUG
#define syslog(e, s) fprintf(stderr,(s))
int debug = 1;
#else
int debug = 0;
#endif
pid_t children;
void nonfs(int);
void reapchild(int);
void usage(void);
void cleanup(int);
void child_cleanup(int);
void nfscbd_exit(int);
void killchildren(void);
/*
* Nfs callback server daemon.
*
* 1 - do file descriptor and signal cleanup
* 2 - fork the nfscbd(s)
* 4 - create callback server socket(s)
* 5 - set up server socket for rpc
*
* For connectionless protocols, just pass the socket into the kernel via.
* nfssvc().
* For connection based sockets, loop doing accepts. When you get a new
* socket from accept, pass the msgsock into the kernel via. nfssvc().
*/
int
main(int argc, char *argv[], char **envp)
{
struct group *grp;
struct nfscbd_args nfscbdargs;
struct nfsd_nfscbd_args nfscbdargs2;
struct passwd *pwd;
struct ucred *cr;
struct sockaddr_in inetaddr, inetpeer;
struct timeval ktv;
fd_set ready, sockbits;
int ch, connect_type_cnt, i, len, maxsock, msgsock, error;
int nfssvc_flag, on, sock, tcpsock, ret, mustfreeai = 0;
char *cp, **cpp, princname[128];
char myname[MAXHOSTNAMELEN], *myfqdnname = NULL;
struct addrinfo *aip, hints;
pid_t pid;
sigset_t signew;
short myport = NFSV4_CBPORT;
if (modfind("nfscl") < 0) {
/* Not present in kernel, try loading it */
if (kldload("nfscl") < 0 ||
modfind("nfscl") < 0)
errx(1, "nfscl is not available");
}
/*
* First, get our fully qualified host name, if possible.
*/
if (gethostname(myname, MAXHOSTNAMELEN) >= 0) {
cp = strchr(myname, '.');
if (cp != NULL && *(cp + 1) != '\0') {
cp = myname;
} else {
/*
* No domain on myname, so try looking it up.
*/
cp = NULL;
memset((void *)&hints, 0, sizeof (hints));
hints.ai_flags = AI_CANONNAME;
error = getaddrinfo(myname, NULL, &hints, &aip);
if (error == 0) {
if (aip->ai_canonname != NULL &&
(cp = strchr(aip->ai_canonname, '.')) != NULL
&& *(cp + 1) != '\0') {
cp = aip->ai_canonname;
mustfreeai = 1;
} else {
freeaddrinfo(aip);
}
}
}
if (cp == NULL)
warnx("Can't get fully qualified host name");
myfqdnname = cp;
}
princname[0] = '\0';
#define GETOPT "p:P:"
#define USAGE "[ -p port_num ] [ -P client_principal ]"
while ((ch = getopt(argc, argv, GETOPT)) != -1)
switch (ch) {
case 'p':
myport = atoi(optarg);
if (myport < 1) {
warnx("port# non-positive, reset to %d",
NFSV4_CBPORT);
myport = NFSV4_CBPORT;
}
break;
case 'P':
cp = optarg;
if (cp != NULL && strlen(cp) > 0 &&
strlen(cp) < sizeof (princname)) {
if (strchr(cp, '@') == NULL &&
myfqdnname != NULL)
snprintf(princname, sizeof (princname),
"%s@%s", cp, myfqdnname);
else
strlcpy(princname, cp,
sizeof (princname));
} else {
warnx("client princ invalid. ignored\n");
}
break;
default:
case '?':
usage();
};
argv += optind;
argc -= optind;
if (argc > 0)
usage();
if (mustfreeai)
freeaddrinfo(aip);
nfscbdargs2.principal = (const char *)princname;
if (debug == 0) {
daemon(0, 0);
(void)signal(SIGTERM, SIG_IGN);
(void)signal(SIGHUP, SIG_IGN);
(void)signal(SIGINT, SIG_IGN);
(void)signal(SIGQUIT, SIG_IGN);
}
(void)signal(SIGSYS, nonfs);
(void)signal(SIGCHLD, reapchild);
openlog("nfscbd:", LOG_PID, LOG_DAEMON);
pid = fork();
if (pid < 0) {
syslog(LOG_ERR, "fork: %m");
nfscbd_exit(1);
} else if (pid > 0) {
children = pid;
} else {
(void)signal(SIGUSR1, child_cleanup);
setproctitle("server");
nfssvc_flag = NFSSVC_NFSCBD;
if (nfssvc(nfssvc_flag, &nfscbdargs2) < 0) {
syslog(LOG_ERR, "nfssvc: %m");
nfscbd_exit(1);
}
exit(0);
}
(void)signal(SIGUSR1, cleanup);
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
syslog(LOG_ERR, "can't create udp socket");
nfscbd_exit(1);
}
memset(&inetaddr, 0, sizeof inetaddr);
inetaddr.sin_family = AF_INET;
inetaddr.sin_addr.s_addr = INADDR_ANY;
inetaddr.sin_port = htons(myport);
inetaddr.sin_len = sizeof(inetaddr);
ret = bind(sock, (struct sockaddr *)&inetaddr, sizeof(inetaddr));
/* If bind() fails, this is a restart, so just skip UDP. */
if (ret == 0) {
len = sizeof(inetaddr);
if (getsockname(sock, (struct sockaddr *)&inetaddr, &len) < 0){
syslog(LOG_ERR, "can't get bound addr");
nfscbd_exit(1);
}
nfscbdargs.port = ntohs(inetaddr.sin_port);
if (nfscbdargs.port != myport) {
syslog(LOG_ERR, "BAD PORT#");
nfscbd_exit(1);
}
nfscbdargs.sock = sock;
nfscbdargs.name = NULL;
nfscbdargs.namelen = 0;
if (nfssvc(NFSSVC_CBADDSOCK, &nfscbdargs) < 0) {
syslog(LOG_ERR, "can't Add UDP socket");
nfscbd_exit(1);
}
}
(void)close(sock);
/* Now set up the master server socket waiting for tcp connections. */
on = 1;
FD_ZERO(&sockbits);
connect_type_cnt = 0;
if ((tcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
syslog(LOG_ERR, "can't create tcp socket");
nfscbd_exit(1);
}
if (setsockopt(tcpsock,
SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
/* sin_port is already set */
inetaddr.sin_family = AF_INET;
inetaddr.sin_addr.s_addr = INADDR_ANY;
inetaddr.sin_port = htons(myport);
inetaddr.sin_len = sizeof(inetaddr);
if (bind(tcpsock,
(struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) {
syslog(LOG_ERR, "can't bind tcp addr");
nfscbd_exit(1);
}
if (listen(tcpsock, 5) < 0) {
syslog(LOG_ERR, "listen failed");
nfscbd_exit(1);
}
FD_SET(tcpsock, &sockbits);
maxsock = tcpsock;
connect_type_cnt++;
setproctitle("master");
/*
* Loop forever accepting connections and passing the sockets
* into the kernel for the mounts.
*/
for (;;) {
ready = sockbits;
if (connect_type_cnt > 1) {
if (select(maxsock + 1,
&ready, NULL, NULL, NULL) < 1) {
syslog(LOG_ERR, "select failed: %m");
nfscbd_exit(1);
}
}
if (FD_ISSET(tcpsock, &ready)) {
len = sizeof(inetpeer);
if ((msgsock = accept(tcpsock,
(struct sockaddr *)&inetpeer, &len)) < 0) {
syslog(LOG_ERR, "accept failed: %m");
nfscbd_exit(1);
}
memset(inetpeer.sin_zero, 0,
sizeof (inetpeer.sin_zero));
if (setsockopt(msgsock, SOL_SOCKET,
SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
syslog(LOG_ERR,
"setsockopt SO_KEEPALIVE: %m");
nfscbdargs.sock = msgsock;
nfscbdargs.name = (caddr_t)&inetpeer;
nfscbdargs.namelen = sizeof(inetpeer);
nfssvc(NFSSVC_CBADDSOCK, &nfscbdargs);
(void)close(msgsock);
}
}
}
void
usage(void)
{
errx(1, "usage: nfscbd %s", USAGE);
}
void
nonfs(int signo)
{
syslog(LOG_ERR, "missing system call: NFS not available");
}
void
reapchild(int signo)
{
pid_t pid;
int i;
while ((pid = wait3(NULL, WNOHANG, NULL)) > 0) {
if (pid == children)
children = -1;
}
}
void
killchildren(void)
{
int i;
if (children > 0)
kill(children, SIGKILL);
}
/*
* Cleanup master after SIGUSR1.
*/
void
cleanup(int signo)
{
nfscbd_exit(0);
}
/*
* Cleanup child after SIGUSR1.
*/
void
child_cleanup(int signo)
{
exit(0);
}
void
nfscbd_exit(int status)
{
killchildren();
exit(status);
}

View File

@ -0,0 +1,6 @@
# $FreeBSD$
PROG= nfsdumpstate
MAN= nfsdumpstate.8
.include <bsd.prog.mk>

View File

@ -0,0 +1,71 @@
.\" Copyright (c) 2009 Rick Macklem, University of Guelph
.\" All rights reserved.
.\"
.\" 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.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
.\"
.\" $FreeBSD$
.\"
.Dd April 25, 2009
.Dt NFSDUMPSTATE 8
.Os
.Sh NAME
.Nm nfsdumpstate
.Nd display
.Tn NFSv4
open/lock state
.Sh SYNOPSIS
.Nm nfsdumpstate
.Op Fl o
.Op Fl l Ar filename
.Sh DESCRIPTION
.Nm
displays open/lock state for the
.Tn NFSv4
client and server in the experimental nfs subsystem.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl o
Displays a summary of Clients for NFSv4. Each line lists a Client with
the ClientID being the last field of the line.
.sp
.nf
The following are the client flag values displayed:
NC - Needs Confirmation
CB - Callbacks are enabled
GSS - Using RPCSEC_GSS
REV - Administratively Revoked, via nfsrevoke(8)
.fi
.sp
.It Fl l Ar filename
Displays a list of all NFSv4 Opens and Locks on the file specified by
the
.Ar filename .
The ClientID is the last field of each line.
.El
.Sh SEE ALSO
.Xr nfsv4 4 ,
.Xr nfsrevoke 8
.Sh HISTORY
The
.Nm
utility was introduced with the NFSv4 experimental subsystem in 2009.

View File

@ -0,0 +1,280 @@
/*-
* Copyright (c) 2009 Rick Macklem, University of Guelph
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/linker.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <nfs/nfssvc.h>
#include <fs/nfs/rpcv2.h>
#include <fs/nfs/nfsproto.h>
#include <fs/nfs/nfskpiport.h>
#include <fs/nfs/nfs.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define DUMPSIZE 10000
static void dump_lockstate(char *);
static void dump_openstate(void);
static void usage(void);
static char *open_flags(uint32_t);
static char *deleg_flags(uint32_t);
static char *lock_flags(uint32_t);
static char *client_flags(uint32_t);
static struct nfsd_dumpclients dp[DUMPSIZE];
static struct nfsd_dumplocks lp[DUMPSIZE];
static char flag_string[20];
int
main(int argc, char **argv)
{
int ch, openstate;
char *lockfile;
if (modfind("nfsd") < 0)
errx(1, "nfsd not loaded - self terminating");
openstate = 0;
lockfile = NULL;
while ((ch = getopt(argc, argv, "ol")) != -1)
switch (ch) {
case 'o':
openstate = 1;
break;
case 'l':
lockfile = optarg;
break;
default:
usage();
}
argc -= optind;
argv += optind;
if (openstate == 0 && lockfile == NULL)
openstate = 1;
else if (openstate != 0 && lockfile != NULL)
errx(1, "-o and -l cannot both be specified");
/*
* For -o, dump all open/lock state.
* For -l, dump lock state for that file.
*/
if (openstate != 0)
dump_openstate();
else
dump_lockstate(lockfile);
exit(0);
}
static void
usage(void)
{
errx(1, "usage: nfsdumpstate [-o] [-l]");
}
/*
* Dump all open/lock state.
*/
static void
dump_openstate(void)
{
struct nfsd_dumplist dumplist;
int cnt, i;
dumplist.ndl_size = DUMPSIZE;
dumplist.ndl_list = (void *)dp;
if (nfssvc(NFSSVC_DUMPCLIENTS, &dumplist) < 0)
errx(1, "Can't perform dump clients syscall");
printf("%-13s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %-15s %s\n",
"Flags", "OpenOwner", "Open", "LockOwner",
"Lock", "Deleg", "OldDeleg", "Clientaddr", "ClientID");
/*
* Loop through results, printing them out.
*/
cnt = 0;
while (dp[cnt].ndcl_clid.nclid_idlen > 0 && cnt < DUMPSIZE) {
printf("%-13s ", client_flags(dp[cnt].ndcl_flags));
printf("%9d %9d %9d %9d %9d %9d ",
dp[cnt].ndcl_nopenowners,
dp[cnt].ndcl_nopens,
dp[cnt].ndcl_nlockowners,
dp[cnt].ndcl_nlocks,
dp[cnt].ndcl_ndelegs,
dp[cnt].ndcl_nolddelegs);
if (dp[cnt].ndcl_addrfam == AF_INET)
printf("%-15s ",
inet_ntoa(dp[cnt].ndcl_cbaddr.sin_addr));
for (i = 0; i < dp[cnt].ndcl_clid.nclid_idlen; i++)
printf("%02x", dp[cnt].ndcl_clid.nclid_id[i]);
printf("\n");
cnt++;
}
}
/*
* Dump the lock state for a file.
*/
static void
dump_lockstate(char *fname)
{
struct nfsd_dumplocklist dumplocklist;
int cnt, i;
dumplocklist.ndllck_size = DUMPSIZE;
dumplocklist.ndllck_list = (void *)lp;
dumplocklist.ndllck_fname = fname;
if (nfssvc(NFSSVC_DUMPLOCKS, &dumplocklist) < 0)
errx(1, "Can't dump locks for %s\n", fname);
printf("%-11s %-36s %-15s %s\n",
"Open/Lock",
" Stateid or Lock Range",
"Clientaddr",
"Owner and ClientID");
/*
* Loop through results, printing them out.
*/
cnt = 0;
while (lp[cnt].ndlck_clid.nclid_idlen > 0 && cnt < DUMPSIZE) {
if (lp[cnt].ndlck_flags & NFSLCK_OPEN)
printf("%-11s %9d %08x %08x %08x ",
open_flags(lp[cnt].ndlck_flags),
lp[cnt].ndlck_stateid.seqid,
lp[cnt].ndlck_stateid.other[0],
lp[cnt].ndlck_stateid.other[1],
lp[cnt].ndlck_stateid.other[2]);
else if (lp[cnt].ndlck_flags & (NFSLCK_DELEGREAD |
NFSLCK_DELEGWRITE))
printf("%-11s %9d %08x %08x %08x ",
deleg_flags(lp[cnt].ndlck_flags),
lp[cnt].ndlck_stateid.seqid,
lp[cnt].ndlck_stateid.other[0],
lp[cnt].ndlck_stateid.other[1],
lp[cnt].ndlck_stateid.other[2]);
else
printf("%-11s %17lld %17lld ",
lock_flags(lp[cnt].ndlck_flags),
lp[cnt].ndlck_first,
lp[cnt].ndlck_end);
if (lp[cnt].ndlck_addrfam == AF_INET)
printf("%-15s ",
inet_ntoa(lp[cnt].ndlck_cbaddr.sin_addr));
else
printf("%-15s ", " ");
for (i = 0; i < lp[cnt].ndlck_owner.nclid_idlen; i++)
printf("%02x", lp[cnt].ndlck_owner.nclid_id[i]);
printf(" ");
for (i = 0; i < lp[cnt].ndlck_clid.nclid_idlen; i++)
printf("%02x", lp[cnt].ndlck_clid.nclid_id[i]);
printf("\n");
cnt++;
}
}
/*
* Parse the Open/Lock flag bits and create a string to be printed.
*/
static char *
open_flags(uint32_t flags)
{
int i, j;
strlcpy(flag_string, "Open ", sizeof (flag_string));
i = 5;
if (flags & NFSLCK_READACCESS)
flag_string[i++] = 'R';
if (flags & NFSLCK_WRITEACCESS)
flag_string[i++] = 'W';
flag_string[i++] = ' ';
flag_string[i++] = 'D';
flag_string[i] = 'N';
j = i;
if (flags & NFSLCK_READDENY)
flag_string[i++] = 'R';
if (flags & NFSLCK_WRITEDENY)
flag_string[i++] = 'W';
if (i == j)
i++;
flag_string[i] = '\0';
return (flag_string);
}
static char *
deleg_flags(uint32_t flags)
{
if (flags & NFSLCK_DELEGREAD)
strlcpy(flag_string, "Deleg R", sizeof (flag_string));
else
strlcpy(flag_string, "Deleg W", sizeof (flag_string));
return (flag_string);
}
static char *
lock_flags(uint32_t flags)
{
if (flags & NFSLCK_READ)
strlcpy(flag_string, "Lock R", sizeof (flag_string));
else
strlcpy(flag_string, "Lock W", sizeof (flag_string));
return (flag_string);
}
static char *
client_flags(uint32_t flags)
{
flag_string[0] = '\0';
if (flags & LCL_NEEDSCONFIRM)
strlcat(flag_string, "NC ", sizeof (flag_string));
if (flags & LCL_CALLBACKSON)
strlcat(flag_string, "CB ", sizeof (flag_string));
if (flags & LCL_GSS)
strlcat(flag_string, "GSS ", sizeof (flag_string));
if (flags & LCL_ADMINREVOKED)
strlcat(flag_string, "REV", sizeof (flag_string));
return (flag_string);
}

View File

@ -0,0 +1,6 @@
# $FreeBSD$
PROG= nfsrevoke
MAN= nfsrevoke.8
.include <bsd.prog.mk>

View File

@ -0,0 +1,64 @@
.\" Copyright (c) 2009 Rick Macklem, University of Guelph
.\" All rights reserved.
.\"
.\" 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.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
.\"
.\" $FreeBSD$
.\"
.Dd April 25, 2009
.Dt NFSREVOKE 8
.Os
.Sh NAME
.Nm nfsrevoke
.Nd revoke
.Tn NFS
V4 client
.Sh SYNOPSIS
.Nm nfsrevoke
.Ar ClientId
.Sh DESCRIPTION
.Nm
This command is used by a system administrator to revoke a client's access
to the NFS Version 4 server. All Open/Lock state held by the client will
be released.
After revocation, the client will no longer be able to use state on the server
until it does a fresh SetClientID/SetClientIDConfirm operations sequence.
THIS SHOULD BE DONE AS A LAST RESORT ONLY, when clients are holding state
that must be released on the server.
.Pp
The
.Ar ClientId
argument is a hexadecimal string, which is the last field
of the
.Xr nfsdumpstate 8
command's
.Fl o
and
.Fl l
options output.
.Sh SEE ALSO
.Xr nfsv4 4 ,
.Xr nfsdumpstate 8
.Sh HISTORY
The
.Nm
command was introduced as a part of the experimental nfs server subsystem.

View File

@ -0,0 +1,124 @@
/*-
* Copyright (c) 2009 Rick Macklem, University of Guelph
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/linker.h>
#include <sys/module.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/vnode.h>
#include <netinet/in.h>
#include <nfs/nfssvc.h>
#include <fs/nfs/rpcv2.h>
#include <fs/nfs/nfsproto.h>
#include <fs/nfs/nfskpiport.h>
#include <fs/nfs/nfs.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void usage(void);
extern int errno;
int
main(int argc, char **argv)
{
char *cp;
u_char val;
int cnt, even;
struct nfsd_clid revoke;
if (modfind("nfsd") < 0)
errx(1, "nfsd not loaded - self terminating");
if (argc != 2)
usage();
cnt = 0;
cp = argv[1];
if (strlen(cp) % 2)
even = 0;
else
even = 1;
val = 0;
while (*cp) {
if (*cp >= '0' & *cp <= '9')
val += (u_char)(*cp - '0');
else if (*cp >= 'A' && *cp <= 'F')
val += ((u_char)(*cp - 'A')) + 0xa;
else if (*cp >= 'a' && *cp <= 'f')
val += ((u_char)(*cp - 'a')) + 0xa;
else
errx(1, "Non hexadecimal digit in %s", argv[1]);
if (even) {
val <<= 4;
even = 0;
} else {
revoke.nclid_id[cnt++] = val;
if (cnt > NFSV4_OPAQUELIMIT)
errx(1, "Clientid %s, loo long", argv[1]);
val = 0;
even = 1;
}
cp++;
}
/*
* Do the revocation system call.
*/
revoke.nclid_idlen = cnt;
#ifdef DEBUG
printf("Idlen=%d\n", revoke.nclid_idlen);
for (cnt = 0; cnt < revoke.nclid_idlen; cnt++)
printf("%02x", revoke.nclid_id[cnt]);
printf("\n");
#else
if (nfssvc(NFSSVC_ADMINREVOKE, &revoke) < 0)
err(1, "Admin revoke failed");
#endif
}
void
usage(void)
{
errx(1, "Usage: nfsrevoke <ClientID>");
}

View File

@ -0,0 +1,6 @@
# $FreeBSD$
PROG= nfsuserd
MAN= nfsuserd.8
.include <bsd.prog.mk>

View File

@ -0,0 +1,114 @@
.\" Copyright (c) 2009 Rick Macklem, University of Guelph
.\" All rights reserved.
.\"
.\" 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.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
.\"
.\" $FreeBSD$
.\"
.Dd April 25, 2009
.Dt NFSUSERD 8
.Os
.Sh NAME
.Nm nfsuserd
.Nd load user and group information into the kernel for
.Tn NFSv4
services
.Sh SYNOPSIS
.Nm nfsuserd
.Op Fl domain Ar domain_name
.Op Fl usertimeout Ar minutes
.Op Fl usermax Ar max_cache_size
.Op Fl verbose
.Op Fl force
.Op Ar num_servers
.Sh DESCRIPTION
.Nm
loads user and group information into the kernel for NFSv4.
It must be running for NFSv4 to function correctly, either client or server.
.Pp
Upon startup, it loads the machines DNS domain name, plus timeout and
cache size limit into the kernel. It then preloads the cache with group
and user information, up to the cache size limit and forks off N children
(default 4), that service requests from the kernel for cache misses. The
master server is there for the sole purpose of killing off the slaves.
To stop the nfsuserd, send a SIGUSR1 to the master server.
.Pp
The following options are available:
.Bl -tag -width Ds
.It Fl domain Ar domain_name
This option allows you to override the default DNS domain name, which
is acquired by taking either the suffix on the machine's hostname or,
if that name is not a fully qualified host name, the cannonical name as
reported by
.Xr getaddrinfo 3 .
.It Fl usertimeout Ar minutes
Overrides the default timeout for cache entries, in minutes. If the
timeout is specified as 0, cache entries never time out. The longer the
time out, the better the performance, but the longer it takes for replaced
entries to be seen. If your user/group database management system almost
never re-uses the same names or id numbers, a large timeout is recommended.
The default is 1 minute.
.It Fl usermax Ar max_cache_size
Overrides the default upper bound on the cache size. The larger the cache,
the more kernel memory is used, but the better the performance. If your
system can afford the memory use, make this the sum of the number of
entries in your group and password databases.
The default is 200 entries.
.It Fl verbose
When set, the server logs a bunch of information to syslog.
.It Fl force
This flag option must be set to restart the daemon after it has gone away
abnormally and refuses to start, because it thinks nfsuserd is already
running.
.It Ar num_servers
Specifies how many servers to create (max 20).
The default of 4 may be sufficient. You should run enough servers, so that
.Xr ps 1
shows almost no running time for one or two of the slaves after the system
has been running for a long period. Running too few will have a major
performance impact, whereas running too many will only tie up some resources,
such as a process table entry and swap space.
.El
.Sh SEE ALSO
.Xr getpwent 3 ,
.Xr getgrent 3 ,
.Xr nfsv4 4 ,
.Xr group 5 ,
.Xr passwd 5 ,
.Xr nfsd 8 .
.Sh HISTORY
The
.Nm
utility was introduced with the NFSv4 experimental subsystem in 2009.
.Sh BUGS
The
.Nm
use
.Xr getgrent 3
and
.Xr getpwent 3
library calls to resolve requests and will hang if the servers handling
those requests fail and the library functions don't return. See
.Xr group 5
and
.Xr passwd 5
for more information on how the databases are accessed.

View File

@ -0,0 +1,665 @@
/*-
* Copyright (c) 2009 Rick Macklem, University of Guelph
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/linker.h>
#include <sys/module.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/time.h>
#include <sys/ucred.h>
#include <sys/vnode.h>
#include <sys/wait.h>
#include <nfs/nfssvc.h>
#include <rpc/rpc.h>
#include <fs/nfs/rpcv2.h>
#include <fs/nfs/nfsproto.h>
#include <fs/nfs/nfskpiport.h>
#include <fs/nfs/nfs.h>
#include <ctype.h>
#include <err.h>
#include <grp.h>
#include <netdb.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
/*
* This program loads the password and group databases into the kernel
* for NFS V4.
*/
void cleanup_term(int);
void usage(void);
void nfsuserdsrv(struct svc_req *, SVCXPRT *);
bool_t xdr_getid(XDR *, caddr_t);
bool_t xdr_getname(XDR *, caddr_t);
bool_t xdr_retval(XDR *, caddr_t);
#define MAXNAME 1024
#define MAXNFSUSERD 20
#define DEFNFSUSERD 4
#define DEFUSERMAX 200
#define DEFUSERTIMEOUT (1 * 60)
struct info {
long id;
long retval;
char name[MAXNAME + 1];
};
u_char *dnsname = "default.domain";
u_char *defaultuser = "nobody";
uid_t defaultuid = (uid_t)32767;
u_char *defaultgroup = "nogroup";
gid_t defaultgid = (gid_t)32767;
int verbose = 0, im_a_slave = 0, nfsuserdcnt = -1, forcestart = 0;
int defusertimeout = DEFUSERTIMEOUT;
pid_t slaves[MAXNFSUSERD];
int
main(int argc, char *argv[], char *envp[])
{
int i;
int error, len, mustfreeai = 0;
struct nfsd_idargs nid;
struct passwd *pwd;
struct group *grp;
int sock, one = 1;
SVCXPRT *udptransp, *tcptransp;
struct passwd *pw;
u_short portnum;
sigset_t signew;
char hostname[MAXHOSTNAMELEN + 1], *cp, **aliases;
struct addrinfo *aip, hints;
if (modfind("nfscommon") < 0) {
/* Not present in kernel, try loading it */
if (kldload("nfscommon") < 0 ||
modfind("nfscommon") < 0)
errx(1, "Experimental nfs subsystem is not available");
}
/*
* First, figure out what our domain name and Kerberos Realm
* seem to be. Command line args may override these later.
*/
if (gethostname(hostname, MAXHOSTNAMELEN) == 0) {
if ((cp = strchr(hostname, '.')) != NULL &&
*(cp + 1) != '\0') {
dnsname = cp + 1;
} else {
memset((void *)&hints, 0, sizeof (hints));
hints.ai_flags = AI_CANONNAME;
error = getaddrinfo(hostname, NULL, &hints, &aip);
if (error == 0) {
if (aip->ai_canonname != NULL &&
(cp = strchr(aip->ai_canonname, '.')) != NULL
&& *(cp + 1) != '\0') {
dnsname = cp + 1;
mustfreeai = 1;
} else {
freeaddrinfo(aip);
}
}
}
}
nid.nid_usermax = DEFUSERMAX;
nid.nid_usertimeout = defusertimeout;
argc--;
argv++;
while (argc >= 1) {
if (!strcmp(*argv, "-domain")) {
if (argc == 1)
usage();
argc--;
argv++;
strncpy(hostname, *argv, MAXHOSTNAMELEN);
hostname[MAXHOSTNAMELEN] = '\0';
dnsname = hostname;
} else if (!strcmp(*argv, "-verbose")) {
verbose = 1;
} else if (!strcmp(*argv, "-force")) {
forcestart = 1;
} else if (!strcmp(*argv, "-usermax")) {
if (argc == 1)
usage();
argc--;
argv++;
i = atoi(*argv);
if (i < 10 || i > 100000) {
fprintf(stderr,
"usermax out of range 10<->100000\n", i);
usage();
}
nid.nid_usermax = i;
} else if (!strcmp(*argv, "-usertimeout")) {
if (argc == 1)
usage();
argc--;
argv++;
i = atoi(*argv);
if (i < 0 || i > 100000) {
fprintf(stderr,
"usertimeout out of range 0<->100000\n",
i);
usage();
}
nid.nid_usertimeout = defusertimeout = i * 60;
} else if (nfsuserdcnt == -1) {
nfsuserdcnt = atoi(*argv);
if (nfsuserdcnt < 1)
usage();
if (nfsuserdcnt > MAXNFSUSERD) {
warnx("nfsuserd count %d; reset to %d",
nfsuserdcnt, DEFNFSUSERD);
nfsuserdcnt = DEFNFSUSERD;
}
} else {
usage();
}
argc--;
argv++;
}
if (nfsuserdcnt < 1)
nfsuserdcnt = DEFNFSUSERD;
/*
* Strip off leading and trailing '.'s in domain name and map
* alphabetics to lower case.
*/
while (*dnsname == '.')
dnsname++;
if (*dnsname == '\0')
errx(1, "Domain name all '.'");
len = strlen(dnsname);
cp = dnsname + len - 1;
while (*cp == '.') {
*cp = '\0';
len--;
cp--;
}
for (i = 0; i < len; i++) {
if (!isascii(dnsname[i]))
errx(1, "Domain name has non-ascii char");
if (isupper(dnsname[i]))
dnsname[i] = tolower(dnsname[i]);
}
/*
* If the nfsuserd died off ungracefully, this is necessary to
* get them to start again.
*/
if (forcestart && nfssvc(NFSSVC_NFSUSERDDELPORT, NULL) < 0)
errx(1, "Can't do nfssvc() to delete the port");
if (verbose)
fprintf(stderr,
"nfsuserd: domain=%s usermax=%d usertimeout=%d\n",
dnsname, nid.nid_usermax, nid.nid_usertimeout);
for (i = 0; i < nfsuserdcnt; i++)
slaves[i] = (pid_t)-1;
/*
* Set up the service port to accept requests via UDP from
* localhost (127.0.0.1).
*/
if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
err(1, "cannot create udp socket");
/*
* Not sure what this does, so I'll leave it here for now.
*/
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if ((udptransp = svcudp_create(sock)) == NULL)
err(1, "Can't set up socket");
/*
* By not specifying a protocol, it is linked into the
* dispatch queue, but not registered with portmapper,
* which is just what I want.
*/
if (!svc_register(udptransp, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS,
nfsuserdsrv, 0))
err(1, "Can't register nfsuserd");
/*
* Tell the kernel what my port# is.
*/
portnum = htons(udptransp->xp_port);
#ifdef DEBUG
printf("portnum=0x%x\n", portnum);
#else
if (nfssvc(NFSSVC_NFSUSERDPORT, (caddr_t)&portnum) < 0) {
if (errno == EPERM) {
fprintf(stderr,
"Can't start nfsuserd when already running");
fprintf(stderr,
" If not running, use the -force option.\n");
} else {
fprintf(stderr, "Can't do nfssvc() to add port\n");
}
exit(1);
}
#endif
pwd = getpwnam(defaultuser);
if (pwd)
nid.nid_uid = pwd->pw_uid;
else
nid.nid_uid = defaultuid;
grp = getgrnam(defaultgroup);
if (grp)
nid.nid_gid = grp->gr_gid;
else
nid.nid_gid = defaultgid;
nid.nid_name = dnsname;
nid.nid_namelen = strlen(nid.nid_name);
nid.nid_flag = NFSID_INITIALIZE;
#ifdef DEBUG
printf("Initialize uid=%d gid=%d dns=%s\n", nid.nid_uid, nid.nid_gid,
nid.nid_name);
#else
error = nfssvc(NFSSVC_IDNAME, &nid);
if (error)
errx(1, "Can't initialize nfs user/groups");
#endif
i = 0;
/*
* Loop around adding all groups.
*/
setgrent();
while (i < nid.nid_usermax && (grp = getgrent())) {
nid.nid_gid = grp->gr_gid;
nid.nid_name = grp->gr_name;
nid.nid_namelen = strlen(grp->gr_name);
nid.nid_flag = NFSID_ADDGID;
#ifdef DEBUG
printf("add gid=%d name=%s\n", nid.nid_gid, nid.nid_name);
#else
error = nfssvc(NFSSVC_IDNAME, &nid);
if (error)
errx(1, "Can't add group %s", grp->gr_name);
#endif
i++;
}
/*
* Loop around adding all users.
*/
setpwent();
while (i < nid.nid_usermax && (pwd = getpwent())) {
nid.nid_uid = pwd->pw_uid;
nid.nid_name = pwd->pw_name;
nid.nid_namelen = strlen(pwd->pw_name);
nid.nid_flag = NFSID_ADDUID;
#ifdef DEBUG
printf("add uid=%d name=%s\n", nid.nid_uid, nid.nid_name);
#else
error = nfssvc(NFSSVC_IDNAME, &nid);
if (error)
errx(1, "Can't add user %s", pwd->pw_name);
#endif
i++;
}
/*
* I should feel guilty for not calling this for all the above exit()
* upon error cases, but I don't.
*/
if (mustfreeai)
freeaddrinfo(aip);
#ifdef DEBUG
exit(0);
#endif
/*
* Temporarily block SIGUSR1 and SIGCHLD, so slaves[] can't
* end up bogus.
*/
sigemptyset(&signew);
sigaddset(&signew, SIGUSR1);
sigaddset(&signew, SIGCHLD);
sigprocmask(SIG_BLOCK, &signew, NULL);
daemon(0, 0);
(void)signal(SIGHUP, SIG_IGN);
(void)signal(SIGINT, SIG_IGN);
(void)signal(SIGQUIT, SIG_IGN);
(void)signal(SIGTERM, SIG_IGN);
(void)signal(SIGUSR1, cleanup_term);
(void)signal(SIGCHLD, cleanup_term);
openlog("nfsuserd:", LOG_PID, LOG_DAEMON);
/*
* Fork off the slave daemons that do the work. All the master
* does is kill them off and cleanup.
*/
for (i = 0; i < nfsuserdcnt; i++) {
slaves[i] = fork();
if (slaves[i] == 0) {
im_a_slave = 1;
setproctitle("slave");
sigemptyset(&signew);
sigaddset(&signew, SIGUSR1);
sigprocmask(SIG_UNBLOCK, &signew, NULL);
/*
* and away we go.
*/
svc_run();
syslog(LOG_ERR, "nfsuserd died: %m");
exit(1);
} else if (slaves[i] < 0) {
syslog(LOG_ERR, "fork: %m");
}
}
/*
* Just wait for SIGUSR1 or a child to die and then...
* As the Governor of California would say, "Terminate them".
*/
setproctitle("master");
sigemptyset(&signew);
while (1)
sigsuspend(&signew);
}
/*
* The nfsuserd rpc service
*/
void
nfsuserdsrv(struct svc_req *rqstp, SVCXPRT *transp)
{
int i;
char *cp;
struct passwd *pwd;
struct group *grp;
int error;
u_short sport;
struct info info;
struct nfsd_idargs nid;
u_int32_t saddr;
/*
* Only handle requests from 127.0.0.1 on a reserved port number.
* (Since a reserved port # at localhost implies a client with
* local root, there won't be a security breach. This is about
* the only case I can think of where a reserved port # means
* something.)
*/
sport = ntohs(transp->xp_raddr.sin_port);
saddr = ntohl(transp->xp_raddr.sin_addr.s_addr);
if ((rqstp->rq_proc != NULLPROC && sport >= IPPORT_RESERVED) ||
saddr != 0x7f000001) {
syslog(LOG_ERR, "req from ip=0x%x port=%d\n", saddr, sport);
svcerr_weakauth(transp);
return;
}
switch (rqstp->rq_proc) {
case NULLPROC:
if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
syslog(LOG_ERR, "Can't send reply");
return;
case RPCNFSUSERD_GETUID:
if (!svc_getargs(transp, (xdrproc_t)xdr_getid,
(caddr_t)&info)) {
svcerr_decode(transp);
return;
}
pwd = getpwuid((uid_t)info.id);
info.retval = 0;
if (pwd != NULL) {
nid.nid_usertimeout = defusertimeout;
nid.nid_uid = pwd->pw_uid;
nid.nid_name = pwd->pw_name;
} else {
nid.nid_usertimeout = 5;
nid.nid_uid = (uid_t)info.id;
nid.nid_name = defaultuser;
}
nid.nid_namelen = strlen(nid.nid_name);
nid.nid_flag = NFSID_ADDUID;
error = nfssvc(NFSSVC_IDNAME, &nid);
if (error) {
info.retval = error;
syslog(LOG_ERR, "Can't add user %s\n", pwd->pw_name);
} else if (verbose) {
syslog(LOG_ERR,"Added uid=%d name=%s\n",
nid.nid_uid, nid.nid_name);
}
if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
(caddr_t)&info))
syslog(LOG_ERR, "Can't send reply");
return;
case RPCNFSUSERD_GETGID:
if (!svc_getargs(transp, (xdrproc_t)xdr_getid,
(caddr_t)&info)) {
svcerr_decode(transp);
return;
}
grp = getgrgid((gid_t)info.id);
info.retval = 0;
if (grp != NULL) {
nid.nid_usertimeout = defusertimeout;
nid.nid_gid = grp->gr_gid;
nid.nid_name = grp->gr_name;
} else {
nid.nid_usertimeout = 5;
nid.nid_gid = (gid_t)info.id;
nid.nid_name = defaultgroup;
}
nid.nid_namelen = strlen(nid.nid_name);
nid.nid_flag = NFSID_ADDGID;
error = nfssvc(NFSSVC_IDNAME, &nid);
if (error) {
info.retval = error;
syslog(LOG_ERR, "Can't add group %s\n",
grp->gr_name);
} else if (verbose) {
syslog(LOG_ERR,"Added gid=%d name=%s\n",
nid.nid_gid, nid.nid_name);
}
if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
(caddr_t)&info))
syslog(LOG_ERR, "Can't send reply");
return;
case RPCNFSUSERD_GETUSER:
if (!svc_getargs(transp, (xdrproc_t)xdr_getname,
(caddr_t)&info)) {
svcerr_decode(transp);
return;
}
pwd = getpwnam(info.name);
info.retval = 0;
if (pwd != NULL) {
nid.nid_usertimeout = defusertimeout;
nid.nid_uid = pwd->pw_uid;
nid.nid_name = pwd->pw_name;
} else {
nid.nid_usertimeout = 5;
nid.nid_uid = defaultuid;
nid.nid_name = info.name;
}
nid.nid_namelen = strlen(nid.nid_name);
nid.nid_flag = NFSID_ADDUSERNAME;
error = nfssvc(NFSSVC_IDNAME, &nid);
if (error) {
info.retval = error;
syslog(LOG_ERR, "Can't add user %s\n", pwd->pw_name);
} else if (verbose) {
syslog(LOG_ERR,"Added uid=%d name=%s\n",
nid.nid_uid, nid.nid_name);
}
if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
(caddr_t)&info))
syslog(LOG_ERR, "Can't send reply");
return;
case RPCNFSUSERD_GETGROUP:
if (!svc_getargs(transp, (xdrproc_t)xdr_getname,
(caddr_t)&info)) {
svcerr_decode(transp);
return;
}
grp = getgrnam(info.name);
info.retval = 0;
if (grp != NULL) {
nid.nid_usertimeout = defusertimeout;
nid.nid_gid = grp->gr_gid;
nid.nid_name = grp->gr_name;
} else {
nid.nid_usertimeout = 5;
nid.nid_gid = defaultgid;
nid.nid_name = info.name;
}
nid.nid_namelen = strlen(nid.nid_name);
nid.nid_flag = NFSID_ADDGROUPNAME;
error = nfssvc(NFSSVC_IDNAME, &nid);
if (error) {
info.retval = error;
syslog(LOG_ERR, "Can't add group %s\n",
grp->gr_name);
} else if (verbose) {
syslog(LOG_ERR,"Added gid=%d name=%s\n",
nid.nid_gid, nid.nid_name);
}
if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
(caddr_t)&info))
syslog(LOG_ERR, "Can't send reply");
return;
default:
svcerr_noproc(transp);
return;
};
}
/*
* Xdr routine to get an id number
*/
bool_t
xdr_getid(XDR *xdrsp, caddr_t cp)
{
struct info *ifp = (struct info *)cp;
return (xdr_long(xdrsp, &ifp->id));
}
/*
* Xdr routine to get a user name
*/
bool_t
xdr_getname(XDR *xdrsp, caddr_t cp)
{
struct info *ifp = (struct info *)cp;
long len;
if (!xdr_long(xdrsp, &len))
return (0);
if (len > MAXNAME)
return (0);
if (!xdr_opaque(xdrsp, ifp->name, len))
return (0);
ifp->name[len] = '\0';
return (1);
}
/*
* Xdr routine to return the value.
*/
bool_t
xdr_retval(XDR *xdrsp, caddr_t cp)
{
struct info *ifp = (struct info *)cp;
long val;
val = ifp->retval;
return (xdr_long(xdrsp, &val));
}
/*
* cleanup_term() called via SIGUSR1.
*/
void
cleanup_term(int signo)
{
int i, cnt;
if (im_a_slave)
exit(0);
/*
* Ok, so I'm the master.
* As the Governor of California might say, "Terminate them".
*/
cnt = 0;
for (i = 0; i < nfsuserdcnt; i++) {
if (slaves[i] != (pid_t)-1) {
cnt++;
kill(slaves[i], SIGUSR1);
}
}
/*
* and wait for them to die
*/
for (i = 0; i < cnt; i++)
wait3(NULL, 0, NULL);
/*
* Finally, get rid of the socket
*/
if (nfssvc(NFSSVC_NFSUSERDDELPORT, NULL) < 0) {
syslog(LOG_ERR, "Can't do nfssvc() to delete the port\n");
exit(1);
}
exit(0);
}
void
usage(void)
{
errx(1,
"usage: nfsuserd [-usermax cache_size] [-usertimeout minutes] [-verbose] [-domain domain_name] [n]");
}