]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/nfsuserd/nfsuserd.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.sbin / nfsuserd / nfsuserd.c
1 /*-
2  * Copyright (c) 2009 Rick Macklem, University of Guelph
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/errno.h>
33 #include <sys/linker.h>
34 #include <sys/module.h>
35 #include <sys/mount.h>
36 #include <sys/socket.h>
37 #include <sys/socketvar.h>
38 #include <sys/time.h>
39 #include <sys/ucred.h>
40 #include <sys/vnode.h>
41 #include <sys/wait.h>
42
43 #include <nfs/nfssvc.h>
44
45 #include <rpc/rpc.h>
46
47 #include <fs/nfs/rpcv2.h>
48 #include <fs/nfs/nfsproto.h>
49 #include <fs/nfs/nfskpiport.h>
50 #include <fs/nfs/nfs.h>
51
52 #include <ctype.h>
53 #include <err.h>
54 #include <grp.h>
55 #include <netdb.h>
56 #include <pwd.h>
57 #include <signal.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <syslog.h>
62 #include <unistd.h>
63
64 /*
65  * This program loads the password and group databases into the kernel
66  * for NFS V4.
67  */
68
69 static void     cleanup_term(int);
70 static void     usage(void);
71 static void     nfsuserdsrv(struct svc_req *, SVCXPRT *);
72 static bool_t   xdr_getid(XDR *, caddr_t);
73 static bool_t   xdr_getname(XDR *, caddr_t);
74 static bool_t   xdr_retval(XDR *, caddr_t);
75
76 #define MAXNAME         1024
77 #define MAXNFSUSERD     20
78 #define DEFNFSUSERD     4
79 #define MAXUSERMAX      100000
80 #define MINUSERMAX      10
81 #define DEFUSERMAX      200
82 #define DEFUSERTIMEOUT  (1 * 60)
83 struct info {
84         long    id;
85         long    retval;
86         char    name[MAXNAME + 1];
87 };
88
89 u_char *dnsname = "default.domain";
90 u_char *defaultuser = "nobody";
91 uid_t defaultuid = (uid_t)32767;
92 u_char *defaultgroup = "nogroup";
93 gid_t defaultgid = (gid_t)32767;
94 int verbose = 0, im_a_slave = 0, nfsuserdcnt = -1, forcestart = 0;
95 int defusertimeout = DEFUSERTIMEOUT;
96 pid_t slaves[MAXNFSUSERD];
97
98 int
99 main(int argc, char *argv[])
100 {
101         int i, j;
102         int error, fnd_dup, len, mustfreeai = 0, start_uidpos;
103         struct nfsd_idargs nid;
104         struct passwd *pwd;
105         struct group *grp;
106         int sock, one = 1;
107         SVCXPRT *udptransp;
108         u_short portnum;
109         sigset_t signew;
110         char hostname[MAXHOSTNAMELEN + 1], *cp;
111         struct addrinfo *aip, hints;
112         static uid_t check_dups[MAXUSERMAX];
113
114         if (modfind("nfscommon") < 0) {
115                 /* Not present in kernel, try loading it */
116                 if (kldload("nfscommon") < 0 ||
117                     modfind("nfscommon") < 0)
118                         errx(1, "Experimental nfs subsystem is not available");
119         }
120
121         /*
122          * First, figure out what our domain name and Kerberos Realm
123          * seem to be. Command line args may override these later.
124          */
125         if (gethostname(hostname, MAXHOSTNAMELEN) == 0) {
126                 if ((cp = strchr(hostname, '.')) != NULL &&
127                     *(cp + 1) != '\0') {
128                         dnsname = cp + 1;
129                 } else {
130                         memset((void *)&hints, 0, sizeof (hints));
131                         hints.ai_flags = AI_CANONNAME;
132                         error = getaddrinfo(hostname, NULL, &hints, &aip);
133                         if (error == 0) {
134                             if (aip->ai_canonname != NULL &&
135                                 (cp = strchr(aip->ai_canonname, '.')) != NULL
136                                 && *(cp + 1) != '\0') {
137                                         dnsname = cp + 1;
138                                         mustfreeai = 1;
139                                 } else {
140                                         freeaddrinfo(aip);
141                                 }
142                         }
143                 }
144         }
145         nid.nid_usermax = DEFUSERMAX;
146         nid.nid_usertimeout = defusertimeout;
147
148         argc--;
149         argv++;
150         while (argc >= 1) {
151                 if (!strcmp(*argv, "-domain")) {
152                         if (argc == 1)
153                                 usage();
154                         argc--;
155                         argv++;
156                         strncpy(hostname, *argv, MAXHOSTNAMELEN);
157                         hostname[MAXHOSTNAMELEN] = '\0';
158                         dnsname = hostname;
159                 } else if (!strcmp(*argv, "-verbose")) {
160                         verbose = 1;
161                 } else if (!strcmp(*argv, "-force")) {
162                         forcestart = 1;
163                 } else if (!strcmp(*argv, "-usermax")) {
164                         if (argc == 1)
165                                 usage();
166                         argc--;
167                         argv++;
168                         i = atoi(*argv);
169                         if (i < MINUSERMAX || i > MAXUSERMAX) {
170                                 fprintf(stderr,
171                                     "usermax %d out of range %d<->%d\n", i,
172                                     MINUSERMAX, MAXUSERMAX);
173                                 usage();
174                         }
175                         nid.nid_usermax = i;
176                 } else if (!strcmp(*argv, "-usertimeout")) {
177                         if (argc == 1)
178                                 usage();
179                         argc--;
180                         argv++;
181                         i = atoi(*argv);
182                         if (i < 0 || i > 100000) {
183                                 fprintf(stderr,
184                                     "usertimeout %d out of range 0<->100000\n",
185                                     i);
186                                 usage();
187                         }
188                         nid.nid_usertimeout = defusertimeout = i * 60;
189                 } else if (nfsuserdcnt == -1) {
190                         nfsuserdcnt = atoi(*argv);
191                         if (nfsuserdcnt < 1)
192                                 usage();
193                         if (nfsuserdcnt > MAXNFSUSERD) {
194                                 warnx("nfsuserd count %d; reset to %d",
195                                     nfsuserdcnt, DEFNFSUSERD);
196                                 nfsuserdcnt = DEFNFSUSERD;
197                         }
198                 } else {
199                         usage();
200                 }
201                 argc--;
202                 argv++;
203         }
204         if (nfsuserdcnt < 1)
205                 nfsuserdcnt = DEFNFSUSERD;
206
207         /*
208          * Strip off leading and trailing '.'s in domain name and map
209          * alphabetics to lower case.
210          */
211         while (*dnsname == '.')
212                 dnsname++;
213         if (*dnsname == '\0')
214                 errx(1, "Domain name all '.'");
215         len = strlen(dnsname);
216         cp = dnsname + len - 1;
217         while (*cp == '.') {
218                 *cp = '\0';
219                 len--;
220                 cp--;
221         }
222         for (i = 0; i < len; i++) {
223                 if (!isascii(dnsname[i]))
224                         errx(1, "Domain name has non-ascii char");
225                 if (isupper(dnsname[i]))
226                         dnsname[i] = tolower(dnsname[i]);
227         }
228
229         /*
230          * If the nfsuserd died off ungracefully, this is necessary to
231          * get them to start again.
232          */
233         if (forcestart && nfssvc(NFSSVC_NFSUSERDDELPORT, NULL) < 0)
234                 errx(1, "Can't do nfssvc() to delete the port");
235
236         if (verbose)
237                 fprintf(stderr,
238                     "nfsuserd: domain=%s usermax=%d usertimeout=%d\n",
239                     dnsname, nid.nid_usermax, nid.nid_usertimeout);
240
241         for (i = 0; i < nfsuserdcnt; i++)
242                 slaves[i] = (pid_t)-1;
243
244         /*
245          * Set up the service port to accept requests via UDP from
246          * localhost (127.0.0.1).
247          */
248         if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
249                 err(1, "cannot create udp socket");
250
251         /*
252          * Not sure what this does, so I'll leave it here for now.
253          */
254         setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
255         
256         if ((udptransp = svcudp_create(sock)) == NULL)
257                 err(1, "Can't set up socket");
258
259         /*
260          * By not specifying a protocol, it is linked into the
261          * dispatch queue, but not registered with portmapper,
262          * which is just what I want.
263          */
264         if (!svc_register(udptransp, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS,
265             nfsuserdsrv, 0))
266                 err(1, "Can't register nfsuserd");
267
268         /*
269          * Tell the kernel what my port# is.
270          */
271         portnum = htons(udptransp->xp_port);
272 #ifdef DEBUG
273         printf("portnum=0x%x\n", portnum);
274 #else
275         if (nfssvc(NFSSVC_NFSUSERDPORT, (caddr_t)&portnum) < 0) {
276                 if (errno == EPERM) {
277                         fprintf(stderr,
278                             "Can't start nfsuserd when already running");
279                         fprintf(stderr,
280                             " If not running, use the -force option.\n");
281                 } else {
282                         fprintf(stderr, "Can't do nfssvc() to add port\n");
283                 }
284                 exit(1);
285         }
286 #endif
287
288         pwd = getpwnam(defaultuser);
289         if (pwd)
290                 nid.nid_uid = pwd->pw_uid;
291         else
292                 nid.nid_uid = defaultuid;
293         grp = getgrnam(defaultgroup);
294         if (grp)
295                 nid.nid_gid = grp->gr_gid;
296         else
297                 nid.nid_gid = defaultgid;
298         nid.nid_name = dnsname;
299         nid.nid_namelen = strlen(nid.nid_name);
300         nid.nid_flag = NFSID_INITIALIZE;
301 #ifdef DEBUG
302         printf("Initialize uid=%d gid=%d dns=%s\n", nid.nid_uid, nid.nid_gid, 
303             nid.nid_name);
304 #else
305         error = nfssvc(NFSSVC_IDNAME, &nid);
306         if (error)
307                 errx(1, "Can't initialize nfs user/groups");
308 #endif
309
310         i = 0;
311         /*
312          * Loop around adding all groups.
313          */
314         setgrent();
315         while (i < nid.nid_usermax && (grp = getgrent())) {
316                 nid.nid_gid = grp->gr_gid;
317                 nid.nid_name = grp->gr_name;
318                 nid.nid_namelen = strlen(grp->gr_name);
319                 nid.nid_flag = NFSID_ADDGID;
320 #ifdef DEBUG
321                 printf("add gid=%d name=%s\n", nid.nid_gid, nid.nid_name);
322 #else
323                 error = nfssvc(NFSSVC_IDNAME, &nid);
324                 if (error)
325                         errx(1, "Can't add group %s", grp->gr_name);
326 #endif
327                 i++;
328         }
329
330         /*
331          * Loop around adding all users.
332          */
333         start_uidpos = i;
334         setpwent();
335         while (i < nid.nid_usermax && (pwd = getpwent())) {
336                 fnd_dup = 0;
337                 /*
338                  * Yes, this is inefficient, but it is only done once when
339                  * the daemon is started and will run in a fraction of a second
340                  * for nid_usermax at 10000. If nid_usermax is cranked up to
341                  * 100000, it will take several seconds, depending on the CPU.
342                  */
343                 for (j = 0; j < (i - start_uidpos); j++)
344                         if (check_dups[j] == pwd->pw_uid) {
345                                 /* Found another entry for uid, so skip it */
346                                 fnd_dup = 1;
347                                 break;
348                         }
349                 if (fnd_dup != 0)
350                         continue;
351                 check_dups[i - start_uidpos] = pwd->pw_uid;
352                 nid.nid_uid = pwd->pw_uid;
353                 nid.nid_name = pwd->pw_name;
354                 nid.nid_namelen = strlen(pwd->pw_name);
355                 nid.nid_flag = NFSID_ADDUID;
356 #ifdef DEBUG
357                 printf("add uid=%d name=%s\n", nid.nid_uid, nid.nid_name);
358 #else
359                 error = nfssvc(NFSSVC_IDNAME, &nid);
360                 if (error)
361                         errx(1, "Can't add user %s", pwd->pw_name);
362 #endif
363                 i++;
364         }
365
366         /*
367          * I should feel guilty for not calling this for all the above exit()
368          * upon error cases, but I don't.
369          */
370         if (mustfreeai)
371                 freeaddrinfo(aip);
372
373 #ifdef DEBUG
374         exit(0);
375 #endif
376         /*
377          * Temporarily block SIGUSR1 and SIGCHLD, so slaves[] can't
378          * end up bogus.
379          */
380         sigemptyset(&signew);
381         sigaddset(&signew, SIGUSR1);
382         sigaddset(&signew, SIGCHLD);
383         sigprocmask(SIG_BLOCK, &signew, NULL);
384
385         daemon(0, 0);
386         (void)signal(SIGHUP, SIG_IGN);
387         (void)signal(SIGINT, SIG_IGN);
388         (void)signal(SIGQUIT, SIG_IGN);
389         (void)signal(SIGTERM, SIG_IGN);
390         (void)signal(SIGUSR1, cleanup_term);
391         (void)signal(SIGCHLD, cleanup_term);
392
393         openlog("nfsuserd:", LOG_PID, LOG_DAEMON);
394
395         /*
396          * Fork off the slave daemons that do the work. All the master
397          * does is kill them off and cleanup.
398          */
399         for (i = 0; i < nfsuserdcnt; i++) {
400                 slaves[i] = fork();
401                 if (slaves[i] == 0) {
402                         im_a_slave = 1;
403                         setproctitle("slave");
404                         sigemptyset(&signew);
405                         sigaddset(&signew, SIGUSR1);
406                         sigprocmask(SIG_UNBLOCK, &signew, NULL);
407
408                         /*
409                          * and away we go.
410                          */
411                         svc_run();
412                         syslog(LOG_ERR, "nfsuserd died: %m");
413                         exit(1);
414                 } else if (slaves[i] < 0) {
415                         syslog(LOG_ERR, "fork: %m");
416                 }
417         }
418
419         /*
420          * Just wait for SIGUSR1 or a child to die and then...
421          * As the Governor of California would say, "Terminate them".
422          */
423         setproctitle("master");
424         sigemptyset(&signew);
425         while (1)
426                 sigsuspend(&signew);
427 }
428
429 /*
430  * The nfsuserd rpc service
431  */
432 static void
433 nfsuserdsrv(struct svc_req *rqstp, SVCXPRT *transp)
434 {
435         struct passwd *pwd;
436         struct group *grp;
437         int error;
438         u_short sport;
439         struct info info;
440         struct nfsd_idargs nid;
441         u_int32_t saddr;
442
443         /*
444          * Only handle requests from 127.0.0.1 on a reserved port number.
445          * (Since a reserved port # at localhost implies a client with
446          *  local root, there won't be a security breach. This is about
447          *  the only case I can think of where a reserved port # means
448          *  something.)
449          */
450         sport = ntohs(transp->xp_raddr.sin_port);
451         saddr = ntohl(transp->xp_raddr.sin_addr.s_addr);
452         if ((rqstp->rq_proc != NULLPROC && sport >= IPPORT_RESERVED) ||
453             saddr != 0x7f000001) {
454                 syslog(LOG_ERR, "req from ip=0x%x port=%d\n", saddr, sport);
455                 svcerr_weakauth(transp);
456                 return;
457         }
458         switch (rqstp->rq_proc) {
459         case NULLPROC:
460                 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
461                         syslog(LOG_ERR, "Can't send reply");
462                 return;
463         case RPCNFSUSERD_GETUID:
464                 if (!svc_getargs(transp, (xdrproc_t)xdr_getid,
465                     (caddr_t)&info)) {
466                         svcerr_decode(transp);
467                         return;
468                 }
469                 pwd = getpwuid((uid_t)info.id);
470                 info.retval = 0;
471                 if (pwd != NULL) {
472                         nid.nid_usertimeout = defusertimeout;
473                         nid.nid_uid = pwd->pw_uid;
474                         nid.nid_name = pwd->pw_name;
475                 } else {
476                         nid.nid_usertimeout = 5;
477                         nid.nid_uid = (uid_t)info.id;
478                         nid.nid_name = defaultuser;
479                 }
480                 nid.nid_namelen = strlen(nid.nid_name);
481                 nid.nid_flag = NFSID_ADDUID;
482                 error = nfssvc(NFSSVC_IDNAME, &nid);
483                 if (error) {
484                         info.retval = error;
485                         syslog(LOG_ERR, "Can't add user %s\n", pwd->pw_name);
486                 } else if (verbose) {
487                         syslog(LOG_ERR,"Added uid=%d name=%s\n",
488                             nid.nid_uid, nid.nid_name);
489                 }
490                 if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
491                     (caddr_t)&info))
492                         syslog(LOG_ERR, "Can't send reply");
493                 return;
494         case RPCNFSUSERD_GETGID:
495                 if (!svc_getargs(transp, (xdrproc_t)xdr_getid,
496                     (caddr_t)&info)) {
497                         svcerr_decode(transp);
498                         return;
499                 }
500                 grp = getgrgid((gid_t)info.id);
501                 info.retval = 0;
502                 if (grp != NULL) {
503                         nid.nid_usertimeout = defusertimeout;
504                         nid.nid_gid = grp->gr_gid;
505                         nid.nid_name = grp->gr_name;
506                 } else {
507                         nid.nid_usertimeout = 5;
508                         nid.nid_gid = (gid_t)info.id;
509                         nid.nid_name = defaultgroup;
510                 }
511                 nid.nid_namelen = strlen(nid.nid_name);
512                 nid.nid_flag = NFSID_ADDGID;
513                 error = nfssvc(NFSSVC_IDNAME, &nid);
514                 if (error) {
515                         info.retval = error;
516                         syslog(LOG_ERR, "Can't add group %s\n",
517                             grp->gr_name);
518                 } else if (verbose) {
519                         syslog(LOG_ERR,"Added gid=%d name=%s\n",
520                             nid.nid_gid, nid.nid_name);
521                 }
522                 if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
523                     (caddr_t)&info))
524                         syslog(LOG_ERR, "Can't send reply");
525                 return;
526         case RPCNFSUSERD_GETUSER:
527                 if (!svc_getargs(transp, (xdrproc_t)xdr_getname,
528                     (caddr_t)&info)) {
529                         svcerr_decode(transp);
530                         return;
531                 }
532                 pwd = getpwnam(info.name);
533                 info.retval = 0;
534                 if (pwd != NULL) {
535                         nid.nid_usertimeout = defusertimeout;
536                         nid.nid_uid = pwd->pw_uid;
537                         nid.nid_name = pwd->pw_name;
538                 } else {
539                         nid.nid_usertimeout = 5;
540                         nid.nid_uid = defaultuid;
541                         nid.nid_name = info.name;
542                 }
543                 nid.nid_namelen = strlen(nid.nid_name);
544                 nid.nid_flag = NFSID_ADDUSERNAME;
545                 error = nfssvc(NFSSVC_IDNAME, &nid);
546                 if (error) {
547                         info.retval = error;
548                         syslog(LOG_ERR, "Can't add user %s\n", pwd->pw_name);
549                 } else if (verbose) {
550                         syslog(LOG_ERR,"Added uid=%d name=%s\n",
551                             nid.nid_uid, nid.nid_name);
552                 }
553                 if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
554                     (caddr_t)&info))
555                         syslog(LOG_ERR, "Can't send reply");
556                 return;
557         case RPCNFSUSERD_GETGROUP:
558                 if (!svc_getargs(transp, (xdrproc_t)xdr_getname,
559                     (caddr_t)&info)) {
560                         svcerr_decode(transp);
561                         return;
562                 }
563                 grp = getgrnam(info.name);
564                 info.retval = 0;
565                 if (grp != NULL) {
566                         nid.nid_usertimeout = defusertimeout;
567                         nid.nid_gid = grp->gr_gid;
568                         nid.nid_name = grp->gr_name;
569                 } else {
570                         nid.nid_usertimeout = 5;
571                         nid.nid_gid = defaultgid;
572                         nid.nid_name = info.name;
573                 }
574                 nid.nid_namelen = strlen(nid.nid_name);
575                 nid.nid_flag = NFSID_ADDGROUPNAME;
576                 error = nfssvc(NFSSVC_IDNAME, &nid);
577                 if (error) {
578                         info.retval = error;
579                         syslog(LOG_ERR, "Can't add group %s\n",
580                             grp->gr_name);
581                 } else if (verbose) {
582                         syslog(LOG_ERR,"Added gid=%d name=%s\n",
583                             nid.nid_gid, nid.nid_name);
584                 }
585                 if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
586                     (caddr_t)&info))
587                         syslog(LOG_ERR, "Can't send reply");
588                 return;
589         default:
590                 svcerr_noproc(transp);
591                 return;
592         };
593 }
594
595 /*
596  * Xdr routine to get an id number
597  */
598 static bool_t
599 xdr_getid(XDR *xdrsp, caddr_t cp)
600 {
601         struct info *ifp = (struct info *)cp;
602
603         return (xdr_long(xdrsp, &ifp->id));
604 }
605
606 /*
607  * Xdr routine to get a user name
608  */
609 static bool_t
610 xdr_getname(XDR *xdrsp, caddr_t cp)
611 {
612         struct info *ifp = (struct info *)cp;
613         long len;
614
615         if (!xdr_long(xdrsp, &len))
616                 return (0);
617         if (len > MAXNAME)
618                 return (0);
619         if (!xdr_opaque(xdrsp, ifp->name, len))
620                 return (0);
621         ifp->name[len] = '\0';
622         return (1);
623 }
624
625 /*
626  * Xdr routine to return the value.
627  */
628 static bool_t
629 xdr_retval(XDR *xdrsp, caddr_t cp)
630 {
631         struct info *ifp = (struct info *)cp;
632         long val;
633
634         val = ifp->retval;
635         return (xdr_long(xdrsp, &val));
636 }
637
638 /*
639  * cleanup_term() called via SIGUSR1.
640  */
641 static void
642 cleanup_term(int signo __unused)
643 {
644         int i, cnt;
645
646         if (im_a_slave)
647                 exit(0);
648
649         /*
650          * Ok, so I'm the master.
651          * As the Governor of California might say, "Terminate them".
652          */
653         cnt = 0;
654         for (i = 0; i < nfsuserdcnt; i++) {
655                 if (slaves[i] != (pid_t)-1) {
656                         cnt++;
657                         kill(slaves[i], SIGUSR1);
658                 }
659         }
660
661         /*
662          * and wait for them to die
663          */
664         for (i = 0; i < cnt; i++)
665                 wait3(NULL, 0, NULL);
666
667         /*
668          * Finally, get rid of the socket
669          */
670         if (nfssvc(NFSSVC_NFSUSERDDELPORT, NULL) < 0) {
671                 syslog(LOG_ERR, "Can't do nfssvc() to delete the port\n");
672                 exit(1);
673         }
674         exit(0);
675 }
676
677 static void
678 usage(void)
679 {
680
681         errx(1,
682             "usage: nfsuserd [-usermax cache_size] [-usertimeout minutes] [-verbose] [-domain domain_name] [n]");
683 }