Home | History | Annotate | Line # | Download | only in nfsd
nfsd.c revision 1.11
      1 /*
      2  * Copyright (c) 1989, 1993, 1994
      3  *	The Regents of the University of California.  All rights reserved.
      4  *
      5  * This code is derived from software contributed to Berkeley by
      6  * Rick Macklem at The University of Guelph.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. All advertising materials mentioning features or use of this software
     17  *    must display the following acknowledgement:
     18  *	This product includes software developed by the University of
     19  *	California, Berkeley and its contributors.
     20  * 4. Neither the name of the University nor the names of its contributors
     21  *    may be used to endorse or promote products derived from this software
     22  *    without specific prior written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  */
     36 
     37 #ifndef lint
     38 static char copyright[] =
     39 "@(#) Copyright (c) 1989, 1993, 1994\n\
     40 	The Regents of the University of California.  All rights reserved.\n";
     41 #endif not lint
     42 
     43 #ifndef lint
     44 /*static char sccsid[] = "from: @(#)nfsd.c	8.7 (Berkeley) 2/22/94";*/
     45 static char *rcsid = "$Id: nfsd.c,v 1.11 1994/06/08 19:31:49 mycroft Exp $";
     46 #endif not lint
     47 
     48 #include <sys/param.h>
     49 #include <sys/syslog.h>
     50 #include <sys/ioctl.h>
     51 #include <sys/stat.h>
     52 #include <sys/wait.h>
     53 #include <sys/uio.h>
     54 #include <sys/ucred.h>
     55 #include <sys/mount.h>
     56 #include <sys/socket.h>
     57 #include <sys/socketvar.h>
     58 
     59 #include <rpc/rpc.h>
     60 #include <rpc/pmap_clnt.h>
     61 #include <rpc/pmap_prot.h>
     62 
     63 #ifdef ISO
     64 #include <netiso/iso.h>
     65 #endif
     66 #include <nfs/rpcv2.h>
     67 #include <nfs/nfsv2.h>
     68 #include <nfs/nfs.h>
     69 
     70 #ifdef KERBEROS
     71 #include <kerberosIV/des.h>
     72 #include <kerberosIV/krb.h>
     73 #endif
     74 
     75 #include <err.h>
     76 #include <errno.h>
     77 #include <fcntl.h>
     78 #include <grp.h>
     79 #include <pwd.h>
     80 #include <signal.h>
     81 #include <stdio.h>
     82 #include <stdlib.h>
     83 #include <strings.h>
     84 #include <unistd.h>
     85 
     86 /* Global defs */
     87 #ifdef DEBUG
     88 #define	syslog(e, s)	fprintf(stderr,(s))
     89 int	debug = 1;
     90 #else
     91 int	debug = 0;
     92 #endif
     93 
     94 struct	nfsd_srvargs nsd;
     95 
     96 #ifdef KERBEROS
     97 char		lnam[ANAME_SZ];
     98 KTEXT_ST	kt;
     99 AUTH_DAT	auth;
    100 char		inst[INST_SZ];
    101 #endif
    102 
    103 void	nonfs __P((int));
    104 void	reapchild __P((int));
    105 void	usage __P((void));
    106 
    107 /*
    108  * Nfs server daemon mostly just a user context for nfssvc()
    109  *
    110  * 1 - do file descriptor and signal cleanup
    111  * 2 - fork the nfsd(s)
    112  * 3 - create server socket(s)
    113  * 4 - register socket with portmap
    114  *
    115  * For connectionless protocols, just pass the socket into the kernel via.
    116  * nfssvc().
    117  * For connection based sockets, loop doing accepts. When you get a new
    118  * socket from accept, pass the msgsock into the kernel via. nfssvc().
    119  * The arguments are:
    120  *	-c - support iso cltp clients
    121  *	-r - reregister with portmapper
    122  *	-t - support tcp nfs clients
    123  *	-u - support udp nfs clients
    124  * followed by "n" which is the number of nfsds' to fork off
    125  */
    126 int
    127 main(argc, argv, envp)
    128 	int argc;
    129 	char *argv[], *envp[];
    130 {
    131 	extern int optind;
    132 	struct group *grp;
    133 	struct nfsd_args nfsdargs;
    134 	struct passwd *pwd;
    135 	struct ucred *cr;
    136 	struct sockaddr_in inetaddr, inetpeer;
    137 #ifdef ISO
    138 	struct sockaddr_iso isoaddr, isopeer;
    139 #endif
    140 	fd_set ready, sockbits;
    141 	int ch, cltpflag, connect_type_cnt, i, len, maxsock, msgsock;
    142 	int nfsdcnt, nfssvc_flag, on, reregister, sock, tcpflag, tcpsock;
    143 	int tp4cnt, tp4flag, tp4sock, tpipcnt, tpipflag, tpipsock, udpflag;
    144 	char *cp, **cpp;
    145 
    146 #define	MAXNFSDCNT	20
    147 #define	DEFNFSDCNT	 4
    148 	nfsdcnt = DEFNFSDCNT;
    149 	cltpflag = reregister = tcpflag = tp4cnt = tp4flag = tpipcnt = 0;
    150 	tpipflag = udpflag = 0;
    151 #ifdef ISO
    152 #define	GETOPT	"cn:rtu"
    153 #define	USAGE	"[-crtu] [-n num_servers]"
    154 #else
    155 #define	GETOPT	"n:rtu"
    156 #define	USAGE	"[-rtu] [-n num_servers]"
    157 #endif
    158 	while ((ch = getopt(argc, argv, GETOPT)) != EOF)
    159 		switch (ch) {
    160 		case 'n':
    161 			nfsdcnt = atoi(optarg);
    162 			if (nfsdcnt < 1 || nfsdcnt > MAXNFSDCNT) {
    163 				warnx("nfsd count %d; reset to %d", DEFNFSDCNT);
    164 				nfsdcnt = DEFNFSDCNT;
    165 			}
    166 			break;
    167 		case 'r':
    168 			reregister = 1;
    169 			break;
    170 		case 't':
    171 			tcpflag = 1;
    172 			break;
    173 		case 'u':
    174 			udpflag = 1;
    175 			break;
    176 #ifdef ISO
    177 		case 'c':
    178 			cltpflag = 1;
    179 			break;
    180 #ifdef notyet
    181 		case 'i':
    182 			tp4cnt = 1;
    183 			break;
    184 		case 'p':
    185 			tpipcnt = 1;
    186 			break;
    187 #endif /* notyet */
    188 #endif /* ISO */
    189 		default:
    190 		case '?':
    191 			usage();
    192 		};
    193 	argv += optind;
    194 	argc -= optind;
    195 
    196 	/*
    197 	 * XXX
    198 	 * Backward compatibility, trailing number is the count of daemons.
    199 	 */
    200 	if (argc > 1)
    201 		usage();
    202 	if (argc == 1) {
    203 		nfsdcnt = atoi(argv[0]);
    204 		if (nfsdcnt < 1 || nfsdcnt > MAXNFSDCNT) {
    205 			warnx("nfsd count %d; reset to %d", DEFNFSDCNT);
    206 			nfsdcnt = DEFNFSDCNT;
    207 		}
    208 	}
    209 
    210 	if (debug == 0) {
    211 		daemon(0, 0);
    212 		(void)signal(SIGHUP, SIG_IGN);
    213 		(void)signal(SIGINT, SIG_IGN);
    214 		(void)signal(SIGQUIT, SIG_IGN);
    215 		(void)signal(SIGSYS, nonfs);
    216 		(void)signal(SIGTERM, SIG_IGN);
    217 	}
    218 	(void)signal(SIGCHLD, reapchild);
    219 
    220 	if (reregister) {
    221 		if (udpflag &&
    222 		    !pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT))
    223 			err(1, "can't register with portmap for UDP.");
    224 		if (tcpflag &&
    225 		    !pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT))
    226 			err(1, "can't register with portmap for TCP.");
    227 		exit(0);
    228 	}
    229 	openlog("nfsd:", LOG_PID, LOG_DAEMON);
    230 
    231 	for (i = 0; i < nfsdcnt; i++) {
    232 		switch (fork()) {
    233 		case -1:
    234 			syslog(LOG_ERR, "fork: %m");
    235 			exit (1);
    236 		case 0:
    237 			break;
    238 		default:
    239 			continue;
    240 		}
    241 
    242 		setproctitle("nfsd-srv");
    243 		nfssvc_flag = NFSSVC_NFSD;
    244 		nsd.nsd_nfsd = NULL;
    245 #ifdef KERBEROS
    246 		nsd.nsd_authstr = (char *)kt.dat;
    247 #endif
    248 		while (nfssvc(nfssvc_flag, &nsd) < 0) {
    249 			if (errno != ENEEDAUTH) {
    250 				syslog(LOG_ERR, "nfssvc: %m");
    251 				exit(1);
    252 			}
    253 			nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHINFAIL;
    254 #ifdef KERBEROS
    255 			kt.length = nsd.nsd_authlen;
    256 			kt.mbz = 0;
    257 			(void)strcpy(inst, "*");
    258 			if (krb_rd_req(&kt, "rcmd",
    259 			    inst, nsd.nsd_haddr, &auth, "") == RD_AP_OK &&
    260 			    krb_kntoln(&auth, lnam) == KSUCCESS &&
    261 			    (pwd = getpwnam(lnam)) != NULL) {
    262 				cr = &nsd.nsd_cr;
    263 				cr->cr_uid = pwd->pw_uid;
    264 				cr->cr_groups[0] = pwd->pw_gid;
    265 				cr->cr_ngroups = 1;
    266 				setgrent();
    267 				while ((grp = getgrent()) != NULL) {
    268 					if (grp->gr_gid == cr->cr_groups[0])
    269 						continue;
    270 					for (cpp = grp->gr_mem;
    271 					    *cpp != NULL; ++cpp)
    272 						if (!strcmp(*cpp, lnam))
    273 							break;
    274 					if (*cpp == NULL)
    275 						continue;
    276 					cr->cr_groups[cr->cr_ngroups++]
    277 					    = grp->gr_gid;
    278 					if (cr->cr_ngroups == NGROUPS)
    279 						break;
    280 				}
    281 				endgrent();
    282 				nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHIN;
    283 			}
    284 #endif /* KERBEROS */
    285 		}
    286 		exit(0);
    287 	}
    288 
    289 	/* If we are serving udp, set up the socket. */
    290 	if (udpflag) {
    291 		if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    292 			syslog(LOG_ERR, "can't create udp socket");
    293 			exit(1);
    294 		}
    295 		inetaddr.sin_family = AF_INET;
    296 		inetaddr.sin_addr.s_addr = INADDR_ANY;
    297 		inetaddr.sin_port = htons(NFS_PORT);
    298 		inetaddr.sin_len = sizeof(inetaddr);
    299 		if (bind(sock,
    300 		    (struct sockaddr *)&inetaddr, sizeof(inetaddr)) < 0) {
    301 			syslog(LOG_ERR, "can't bind udp addr");
    302 			exit(1);
    303 		}
    304 		if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT)) {
    305 			syslog(LOG_ERR, "can't register with udp portmap");
    306 			exit(1);
    307 		}
    308 		nfsdargs.sock = sock;
    309 		nfsdargs.name = NULL;
    310 		nfsdargs.namelen = 0;
    311 		if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) {
    312 			syslog(LOG_ERR, "can't Add UDP socket");
    313 			exit(1);
    314 		}
    315 		(void)close(sock);
    316 	}
    317 
    318 #ifdef ISO
    319 	/* If we are serving cltp, set up the socket. */
    320 	if (cltpflag) {
    321 		if ((sock = socket(AF_ISO, SOCK_DGRAM, 0)) < 0) {
    322 			syslog(LOG_ERR, "can't create cltp socket");
    323 			exit(1);
    324 		}
    325 		memset(&isoaddr, 0, sizeof(isoaddr));
    326 		isoaddr.siso_family = AF_ISO;
    327 		isoaddr.siso_tlen = 2;
    328 		cp = TSEL(&isoaddr);
    329 		*cp++ = (NFS_PORT >> 8);
    330 		*cp = (NFS_PORT & 0xff);
    331 		isoaddr.siso_len = sizeof(isoaddr);
    332 		if (bind(sock,
    333 		    (struct sockaddr *)&isoaddr, sizeof(isoaddr)) < 0) {
    334 			syslog(LOG_ERR, "can't bind cltp addr");
    335 			exit(1);
    336 		}
    337 #ifdef notyet
    338 		/*
    339 		 * XXX
    340 		 * Someday this should probably use "rpcbind", the son of
    341 		 * portmap.
    342 		 */
    343 		if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT)) {
    344 			syslog(LOG_ERR, "can't register with udp portmap");
    345 			exit(1);
    346 		}
    347 #endif /* notyet */
    348 		nfsdargs.sock = sock;
    349 		nfsdargs.name = NULL;
    350 		nfsdargs.namelen = 0;
    351 		if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) {
    352 			syslog(LOG_ERR, "can't add UDP socket");
    353 			exit(1);
    354 		}
    355 		close(sock);
    356 	}
    357 #endif /* ISO */
    358 
    359 	/* Now set up the master server socket waiting for tcp connections. */
    360 	on = 1;
    361 	FD_ZERO(&sockbits);
    362 	connect_type_cnt = 0;
    363 	if (tcpflag) {
    364 		if ((tcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    365 			syslog(LOG_ERR, "can't create tcp socket");
    366 			exit(1);
    367 		}
    368 		if (setsockopt(tcpsock,
    369 		    SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
    370 			syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
    371 		inetaddr.sin_family = AF_INET;
    372 		inetaddr.sin_addr.s_addr = INADDR_ANY;
    373 		inetaddr.sin_port = htons(NFS_PORT);
    374 		inetaddr.sin_len = sizeof(inetaddr);
    375 		if (bind(tcpsock,
    376 		    (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) {
    377 			syslog(LOG_ERR, "can't bind tcp addr");
    378 			exit(1);
    379 		}
    380 		if (listen(tcpsock, 5) < 0) {
    381 			syslog(LOG_ERR, "listen failed");
    382 			exit(1);
    383 		}
    384 		if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) {
    385 			syslog(LOG_ERR, "can't register tcp with portmap");
    386 			exit(1);
    387 		}
    388 		FD_SET(tcpsock, &sockbits);
    389 		maxsock = tcpsock;
    390 		connect_type_cnt++;
    391 	}
    392 
    393 #ifdef notyet
    394 	/* Now set up the master server socket waiting for tp4 connections. */
    395 	if (tp4flag) {
    396 		if ((tp4sock = socket(AF_ISO, SOCK_SEQPACKET, 0)) < 0) {
    397 			syslog(LOG_ERR, "can't create tp4 socket");
    398 			exit(1);
    399 		}
    400 		if (setsockopt(tp4sock,
    401 		    SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
    402 			syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
    403 		memset(&isoaddr, 0, sizeof(isoaddr));
    404 		isoaddr.siso_family = AF_ISO;
    405 		isoaddr.siso_tlen = 2;
    406 		cp = TSEL(&isoaddr);
    407 		*cp++ = (NFS_PORT >> 8);
    408 		*cp = (NFS_PORT & 0xff);
    409 		isoaddr.siso_len = sizeof(isoaddr);
    410 		if (bind(tp4sock,
    411 		    (struct sockaddr *)&isoaddr, sizeof (isoaddr)) < 0) {
    412 			syslog(LOG_ERR, "can't bind tp4 addr");
    413 			exit(1);
    414 		}
    415 		if (listen(tp4sock, 5) < 0) {
    416 			syslog(LOG_ERR, "listen failed");
    417 			exit(1);
    418 		}
    419 		/*
    420 		 * XXX
    421 		 * Someday this should probably use "rpcbind", the son of
    422 		 * portmap.
    423 		 */
    424 		if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) {
    425 			syslog(LOG_ERR, "can't register tcp with portmap");
    426 			exit(1);
    427 		}
    428 		FD_SET(tp4sock, &sockbits);
    429 		maxsock = tp4sock;
    430 		connect_type_cnt++;
    431 	}
    432 
    433 	/* Now set up the master server socket waiting for tpip connections. */
    434 	if (tpipflag) {
    435 		if ((tpipsock = socket(AF_INET, SOCK_SEQPACKET, 0)) < 0) {
    436 			syslog(LOG_ERR, "can't create tpip socket");
    437 			exit(1);
    438 		}
    439 		if (setsockopt(tpipsock,
    440 		    SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
    441 			syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
    442 		inetaddr.sin_family = AF_INET;
    443 		inetaddr.sin_addr.s_addr = INADDR_ANY;
    444 		inetaddr.sin_port = htons(NFS_PORT);
    445 		inetaddr.sin_len = sizeof(inetaddr);
    446 		if (bind(tpipsock,
    447 		    (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) {
    448 			syslog(LOG_ERR, "can't bind tcp addr");
    449 			exit(1);
    450 		}
    451 		if (listen(tpipsock, 5) < 0) {
    452 			syslog(LOG_ERR, "listen failed");
    453 			exit(1);
    454 		}
    455 		/*
    456 		 * XXX
    457 		 * Someday this should probably use "rpcbind", the son of
    458 		 * portmap.
    459 		 */
    460 		if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) {
    461 			syslog(LOG_ERR, "can't register tcp with portmap");
    462 			exit(1);
    463 		}
    464 		FD_SET(tpipsock, &sockbits);
    465 		maxsock = tpipsock;
    466 		connect_type_cnt++;
    467 	}
    468 #endif /* notyet */
    469 
    470 	if (connect_type_cnt == 0)
    471 		exit(0);
    472 
    473 	setproctitle("nfsd-master");
    474 
    475 	/*
    476 	 * Loop forever accepting connections and passing the sockets
    477 	 * into the kernel for the mounts.
    478 	 */
    479 	for (;;) {
    480 		ready = sockbits;
    481 		if (connect_type_cnt > 1) {
    482 			if (select(maxsock + 1,
    483 			    &ready, NULL, NULL, NULL) < 1) {
    484 				syslog(LOG_ERR, "select failed: %m");
    485 				exit(1);
    486 			}
    487 		}
    488 		if (tcpflag && FD_ISSET(tcpsock, &ready)) {
    489 			len = sizeof(inetpeer);
    490 			if ((msgsock = accept(tcpsock,
    491 			    (struct sockaddr *)&inetpeer, &len)) < 0) {
    492 				syslog(LOG_ERR, "accept failed: %m");
    493 				exit(1);
    494 			}
    495 			memset(inetpeer.sin_zero, 0, sizeof(inetpeer.sin_zero));
    496 			if (setsockopt(msgsock, SOL_SOCKET,
    497 			    SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
    498 				syslog(LOG_ERR,
    499 				    "setsockopt SO_KEEPALIVE: %m");
    500 			nfsdargs.sock = msgsock;
    501 			nfsdargs.name = (caddr_t)&inetpeer;
    502 			nfsdargs.namelen = sizeof(inetpeer);
    503 			nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
    504 			(void)close(msgsock);
    505 		}
    506 #ifdef notyet
    507 		if (tp4flag && FD_ISSET(tp4sock, &ready)) {
    508 			len = sizeof(isopeer);
    509 			if ((msgsock = accept(tp4sock,
    510 			    (struct sockaddr *)&isopeer, &len)) < 0) {
    511 				syslog(LOG_ERR, "accept failed: %m");
    512 				exit(1);
    513 			}
    514 			if (setsockopt(msgsock, SOL_SOCKET,
    515 			    SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
    516 				syslog(LOG_ERR,
    517 				    "setsockopt SO_KEEPALIVE: %m");
    518 			nfsdargs.sock = msgsock;
    519 			nfsdargs.name = (caddr_t)&isopeer;
    520 			nfsdargs.namelen = len;
    521 			nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
    522 			(void)close(msgsock);
    523 		}
    524 		if (tpipflag && FD_ISSET(tpipsock, &ready)) {
    525 			len = sizeof(inetpeer);
    526 			if ((msgsock = accept(tpipsock,
    527 			    (struct sockaddr *)&inetpeer, &len)) < 0) {
    528 				syslog(LOG_ERR, "Accept failed: %m");
    529 				exit(1);
    530 			}
    531 			if (setsockopt(msgsock, SOL_SOCKET,
    532 			    SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
    533 				syslog(LOG_ERR, "setsockopt SO_KEEPALIVE: %m");
    534 			nfsdargs.sock = msgsock;
    535 			nfsdargs.name = (caddr_t)&inetpeer;
    536 			nfsdargs.namelen = len;
    537 			nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
    538 			(void)close(msgsock);
    539 		}
    540 #endif /* notyet */
    541 	}
    542 }
    543 
    544 void
    545 usage()
    546 {
    547 	(void)fprintf(stderr, "nfsd %s\n", USAGE);
    548 	exit(1);
    549 }
    550 
    551 void
    552 nonfs(signo)
    553 	int signo;
    554 {
    555 	syslog(LOG_ERR, "missing system call: NFS not available.");
    556 }
    557 
    558 void
    559 reapchild(signo)
    560 	int signo;
    561 {
    562 
    563 	while (wait3(NULL, WNOHANG, NULL));
    564 }
    565