rpcbind.c revision 104592
1/*	$NetBSD: rpcbind.c,v 1.1 2000/06/02 23:15:42 fvdl Exp $	*/
2/*	$FreeBSD: head/usr.sbin/rpcbind/rpcbind.c 104592 2002-10-07 02:56:59Z alfred $ */
3
4/*
5 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
6 * unrestricted use provided that this legend is included on all tape
7 * media and as a part of the software program in whole or part.  Users
8 * may copy or modify Sun RPC without charge, but are not authorized
9 * to license or distribute it to anyone else except as part of a product or
10 * program developed by the user.
11 *
12 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
13 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
14 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
15 *
16 * Sun RPC is provided with no support and without any obligation on the
17 * part of Sun Microsystems, Inc. to assist in its use, correction,
18 * modification or enhancement.
19 *
20 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
21 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
22 * OR ANY PART THEREOF.
23 *
24 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
25 * or profits or other special, indirect and consequential damages, even if
26 * Sun has been advised of the possibility of such damages.
27 *
28 * Sun Microsystems, Inc.
29 * 2550 Garcia Avenue
30 * Mountain View, California  94043
31 */
32/*
33 * Copyright (c) 1984 - 1991 by Sun Microsystems, Inc.
34 */
35
36/* #ident	"@(#)rpcbind.c	1.19	94/04/25 SMI" */
37
38#if 0
39#ifndef lint
40static	char sccsid[] = "@(#)rpcbind.c 1.35 89/04/21 Copyr 1984 Sun Micro";
41#endif
42#endif
43
44/*
45 * rpcbind.c
46 * Implements the program, version to address mapping for rpc.
47 *
48 */
49
50#include <sys/types.h>
51#include <sys/stat.h>
52#include <sys/errno.h>
53#include <sys/time.h>
54#include <sys/resource.h>
55#include <sys/wait.h>
56#include <sys/signal.h>
57#include <sys/socket.h>
58#include <sys/un.h>
59#include <rpc/rpc.h>
60#ifdef PORTMAP
61#include <netinet/in.h>
62#endif
63#include <arpa/inet.h>
64#include <netdb.h>
65#include <stdio.h>
66#include <netconfig.h>
67#include <stdlib.h>
68#include <unistd.h>
69#include <syslog.h>
70#include <err.h>
71#include <libutil.h>
72#include <pwd.h>
73#include <string.h>
74#include <errno.h>
75#include "rpcbind.h"
76
77/* Global variables */
78int debugging = 0;	/* Tell me what's going on */
79int doabort = 0;	/* When debugging, do an abort on errors */
80rpcblist_ptr list_rbl;	/* A list of version 3/4 rpcbind services */
81
82/* who to suid to if -s is given */
83#define RUN_AS  "daemon"
84
85int runasdaemon = 0;
86int insecure = 0;
87int oldstyle_local = 0;
88int verboselog = 0;
89
90char **hosts = NULL;
91int nhosts = 0;
92int on = 1;
93
94#ifdef WARMSTART
95/* Local Variable */
96static int warmstart = 0;	/* Grab a old copy of registrations */
97#endif
98
99#ifdef PORTMAP
100struct pmaplist *list_pml;	/* A list of version 2 rpcbind services */
101char *udptrans;		/* Name of UDP transport */
102char *tcptrans;		/* Name of TCP transport */
103char *udp_uaddr;	/* Universal UDP address */
104char *tcp_uaddr;	/* Universal TCP address */
105#endif
106static char servname[] = "rpcbind";
107static char superuser[] = "superuser";
108
109int main __P((int, char *[]));
110
111static int init_transport __P((struct netconfig *));
112static void rbllist_add __P((rpcprog_t, rpcvers_t, struct netconfig *,
113			     struct netbuf *));
114static void terminate __P((int));
115static void parseargs __P((int, char *[]));
116
117int
118main(int argc, char *argv[])
119{
120	struct netconfig *nconf;
121	void *nc_handle;	/* Net config handle */
122	struct rlimit rl;
123
124	parseargs(argc, argv);
125
126	getrlimit(RLIMIT_NOFILE, &rl);
127	if (rl.rlim_cur < 128) {
128		if (rl.rlim_max <= 128)
129			rl.rlim_cur = rl.rlim_max;
130		else
131			rl.rlim_cur = 128;
132		setrlimit(RLIMIT_NOFILE, &rl);
133	}
134	openlog("rpcbind", LOG_CONS, LOG_DAEMON);
135	if (geteuid()) { /* This command allowed only to root */
136		fprintf(stderr, "Sorry. You are not superuser\n");
137		exit(1);
138	}
139	nc_handle = setnetconfig(); 	/* open netconfig file */
140	if (nc_handle == NULL) {
141		syslog(LOG_ERR, "could not read /etc/netconfig");
142		exit(1);
143	}
144#ifdef PORTMAP
145	udptrans = "";
146	tcptrans = "";
147#endif
148
149	nconf = getnetconfigent("unix");
150	if (nconf == NULL) {
151		syslog(LOG_ERR, "%s: can't find local transport\n", argv[0]);
152		exit(1);
153	}
154	init_transport(nconf);
155
156	while ((nconf = getnetconfig(nc_handle))) {
157		if (nconf->nc_flag & NC_VISIBLE)
158			init_transport(nconf);
159	}
160	endnetconfig(nc_handle);
161
162	/* catch the usual termination signals for graceful exit */
163	(void) signal(SIGCHLD, reap);
164	(void) signal(SIGINT, terminate);
165	(void) signal(SIGTERM, terminate);
166	(void) signal(SIGQUIT, terminate);
167	/* ignore others that could get sent */
168	(void) signal(SIGPIPE, SIG_IGN);
169	(void) signal(SIGHUP, SIG_IGN);
170	(void) signal(SIGUSR1, SIG_IGN);
171	(void) signal(SIGUSR2, SIG_IGN);
172#ifdef WARMSTART
173	if (warmstart) {
174		read_warmstart();
175	}
176#endif
177	if (debugging) {
178		printf("rpcbind debugging enabled.");
179		if (doabort) {
180			printf("  Will abort on errors!\n");
181		} else {
182			printf("\n");
183		}
184	} else {
185		if (daemon(0, 0))
186			err(1, "fork failed");
187	}
188
189	if (runasdaemon) {
190		struct passwd *p;
191
192		if((p = getpwnam(RUN_AS)) == NULL) {
193			syslog(LOG_ERR, "cannot get uid of daemon: %m");
194			exit(1);
195		}
196		if (setuid(p->pw_uid) == -1) {
197			syslog(LOG_ERR, "setuid to daemon failed: %m");
198			exit(1);
199		}
200	}
201
202	network_init();
203
204	my_svc_run();
205	syslog(LOG_ERR, "svc_run returned unexpectedly");
206	rpcbind_abort();
207	/* NOTREACHED */
208
209	return 0;
210}
211
212/*
213 * Adds the entry into the rpcbind database.
214 * If PORTMAP, then for UDP and TCP, it adds the entries for version 2 also
215 * Returns 0 if succeeds, else fails
216 */
217static int
218init_transport(struct netconfig *nconf)
219{
220	int fd;
221	struct t_bind taddr;
222	struct addrinfo hints, *res = NULL;
223	struct __rpc_sockinfo si;
224	SVCXPRT	*my_xprt;
225	int status;	/* bound checking ? */
226	int aicode;
227	int addrlen;
228	int nhostsbak;
229	int checkbind;
230	struct sockaddr *sa;
231	u_int32_t host_addr[4];  /* IPv4 or IPv6 */
232	struct sockaddr_un sun;
233	mode_t oldmask;
234
235	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
236		(nconf->nc_semantics != NC_TPI_COTS) &&
237		(nconf->nc_semantics != NC_TPI_COTS_ORD))
238		return (1);	/* not my type */
239#ifdef ND_DEBUG
240	if (debugging) {
241		int i;
242		char **s;
243
244		(void) fprintf(stderr, "%s: %ld lookup routines :\n",
245			nconf->nc_netid, nconf->nc_nlookups);
246		for (i = 0, s = nconf->nc_lookups; i < nconf->nc_nlookups;
247		     i++, s++)
248			fprintf(stderr, "[%d] - %s\n", i, *s);
249	}
250#endif
251
252	/*
253	 * XXX - using RPC library internal functions. For NC_TPI_CLTS
254	 * we call this later, for each socket we like to bind.
255	 */
256	if (nconf->nc_semantics != NC_TPI_CLTS) {
257		if ((fd = __rpc_nconf2fd(nconf)) < 0) {
258			syslog(LOG_ERR, "cannot create socket for %s",
259			    nconf->nc_netid);
260			return (1);
261		}
262	}
263
264	if (!__rpc_nconf2sockinfo(nconf, &si)) {
265		syslog(LOG_ERR, "cannot get information for %s",
266		    nconf->nc_netid);
267		return (1);
268	}
269
270	if (!strcmp(nconf->nc_netid, "unix")) {
271		memset(&sun, 0, sizeof sun);
272		sun.sun_family = AF_LOCAL;
273		unlink(_PATH_RPCBINDSOCK);
274		strcpy(sun.sun_path, _PATH_RPCBINDSOCK);
275		sun.sun_len = SUN_LEN(&sun);
276		addrlen = sizeof (struct sockaddr_un);
277		sa = (struct sockaddr *)&sun;
278	} else {
279		/* Get rpcbind's address on this transport */
280
281		memset(&hints, 0, sizeof hints);
282		hints.ai_flags = AI_PASSIVE;
283		hints.ai_family = si.si_af;
284		hints.ai_socktype = si.si_socktype;
285		hints.ai_protocol = si.si_proto;
286	}
287	if (nconf->nc_semantics == NC_TPI_CLTS) {
288		/*
289		 * If no hosts were specified, just bind to INADDR_ANY.  Otherwise
290		 * make sure 127.0.0.1 is added to the list.
291		 */
292		nhostsbak = nhosts;
293		nhostsbak++;
294		hosts = realloc(hosts, nhostsbak * sizeof(char *));
295		if (nhostsbak == 1)
296			hosts[0] = "*";
297		else {
298			if (hints.ai_family == AF_INET) {
299				hosts[nhostsbak - 1] = "127.0.0.1";
300			} else if (hints.ai_family == AF_INET6) {
301				hosts[nhostsbak - 1] = "::1";
302			} else
303				return 1;
304		}
305
306	       /*
307		* Bind to specific IPs if asked to
308		*/
309		checkbind = 1;
310		while (nhostsbak > 0) {
311			--nhostsbak;
312			/*
313			 * XXX - using RPC library internal functions.
314			 */
315			if ((fd = __rpc_nconf2fd(nconf)) < 0) {
316				syslog(LOG_ERR, "cannot create socket for %s",
317				    nconf->nc_netid);
318				return (1);
319			}
320			switch (hints.ai_family) {
321			case AF_INET:
322				if (inet_pton(AF_INET, hosts[nhostsbak],
323				    host_addr) == 1) {
324					hints.ai_flags &= AI_NUMERICHOST;
325				} else {
326					/*
327					 * Skip if we have a AF_INET6 adress
328					 */
329					if (inet_pton(AF_INET6,
330					    hosts[nhostsbak], host_addr) == 1)
331						continue;
332				}
333				break;
334			case AF_INET6:
335				if (inet_pton(AF_INET6, hosts[nhostsbak],
336				    host_addr) == 1) {
337					hints.ai_flags &= AI_NUMERICHOST;
338				} else {
339					/*
340					 * Skip if we have a AF_INET adress
341					 */
342					if (inet_pton(AF_INET, hosts[nhostsbak],
343					    host_addr) == 1)
344						continue;
345				}
346				if (setsockopt(fd, IPPROTO_IPV6,
347                                    IPV6_V6ONLY, &on, sizeof on) < 0) {
348                                        syslog(LOG_ERR,
349					    "can't set v6-only binding for "
350                                            "udp6 socket: %m");
351					continue;
352				}
353				break;
354			default:
355				break;
356			}
357
358			/*
359			 * If no hosts were specified, just bind to INADDR_ANY
360			 */
361			if (strcmp("*", hosts[nhostsbak]) == 0)
362				hosts[nhostsbak] = NULL;
363
364			if ((aicode = getaddrinfo(hosts[nhostsbak],
365			    servname, &hints, &res)) != 0) {
366				syslog(LOG_ERR,
367				    "cannot get local address for %s: %s",
368				    nconf->nc_netid, gai_strerror(aicode));
369				continue;
370			}
371			addrlen = res->ai_addrlen;
372			sa = (struct sockaddr *)res->ai_addr;
373			oldmask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
374			if (bind(fd, sa, addrlen) != 0) {
375				syslog(LOG_ERR, "cannot bind %s on %s: %m",
376					(hosts[nhostsbak] == NULL) ? "*" :
377					hosts[nhostsbak], nconf->nc_netid);
378				if (res != NULL)
379					freeaddrinfo(res);
380				continue;
381			} else
382				checkbind++;
383			(void) umask(oldmask);
384
385			/* Copy the address */
386			taddr.addr.len = taddr.addr.maxlen = addrlen;
387			taddr.addr.buf = malloc(addrlen);
388			if (taddr.addr.buf == NULL) {
389				syslog(LOG_ERR,
390				    "cannot allocate memory for %s address",
391				    nconf->nc_netid);
392				if (res != NULL)
393					freeaddrinfo(res);
394				return 1;
395			}
396			memcpy(taddr.addr.buf, sa, addrlen);
397#ifdef ND_DEBUG
398			if (debugging) {
399				/*
400				 * for debugging print out our universal
401				 * address
402				 */
403				char *uaddr;
404				struct netbuf nb;
405
406				nb.buf = sa;
407				nb.len = nb.maxlen = sa->sa_len;
408				uaddr = taddr2uaddr(nconf, &nb);
409				(void) fprintf(stderr,
410				    "rpcbind : my address is %s\n", uaddr);
411				(void) free(uaddr);
412			}
413#endif
414
415			if (nconf->nc_semantics != NC_TPI_CLTS)
416				listen(fd, SOMAXCONN);
417
418			my_xprt = (SVCXPRT *)svc_tli_create(fd, nconf, &taddr,
419			    0, 0);
420			if (my_xprt == (SVCXPRT *)NULL) {
421				syslog(LOG_ERR, "%s: could not create service",
422					nconf->nc_netid);
423				goto error;
424			}
425		}
426		if (!checkbind)
427			return 1;
428	} else {
429		if (strcmp(nconf->nc_netid, "unix") != 0) {
430			if ((aicode = getaddrinfo(NULL, servname, &hints, &res))
431			    != 0) {
432				syslog(LOG_ERR,
433				    "cannot get local address for %s: %s",
434				    nconf->nc_netid, gai_strerror(aicode));
435				return 1;
436			}
437			addrlen = res->ai_addrlen;
438			sa = (struct sockaddr *)res->ai_addr;
439		}
440		oldmask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
441		if (bind(fd, sa, addrlen) < 0) {
442			syslog(LOG_ERR, "cannot bind %s: %m", nconf->nc_netid);
443			if (res != NULL)
444				freeaddrinfo(res);
445			return 1;
446		}
447		(void) umask(oldmask);
448
449		/* Copy the address */
450		taddr.addr.len = taddr.addr.maxlen = addrlen;
451		taddr.addr.buf = malloc(addrlen);
452		if (taddr.addr.buf == NULL) {
453			syslog(LOG_ERR, "cannot allocate memory for %s address",
454			    nconf->nc_netid);
455			if (res != NULL)
456				freeaddrinfo(res);
457			return 1;
458		}
459		memcpy(taddr.addr.buf, sa, addrlen);
460#ifdef ND_DEBUG
461		if (debugging) {
462			/* for debugging print out our universal address */
463			char *uaddr;
464			struct netbuf nb;
465
466			nb.buf = sa;
467			nb.len = nb.maxlen = sa->sa_len;
468			uaddr = taddr2uaddr(nconf, &nb);
469			(void) fprintf(stderr, "rpcbind : my address is %s\n",
470			    uaddr);
471			(void) free(uaddr);
472		}
473#endif
474
475		if (nconf->nc_semantics != NC_TPI_CLTS)
476			listen(fd, SOMAXCONN);
477
478		my_xprt = (SVCXPRT *)svc_tli_create(fd, nconf, &taddr, 0, 0);
479		if (my_xprt == (SVCXPRT *)NULL) {
480			syslog(LOG_ERR, "%s: could not create service",
481					nconf->nc_netid);
482			goto error;
483		}
484	}
485
486#ifdef PORTMAP
487	/*
488	 * Register both the versions for tcp/ip, udp/ip and local.
489	 */
490	if ((strcmp(nconf->nc_protofmly, NC_INET) == 0 &&
491		(strcmp(nconf->nc_proto, NC_TCP) == 0 ||
492		strcmp(nconf->nc_proto, NC_UDP) == 0)) ||
493		strcmp(nconf->nc_netid, "unix") == 0) {
494		struct pmaplist *pml;
495
496		if (!svc_register(my_xprt, PMAPPROG, PMAPVERS,
497			pmap_service, NULL)) {
498			syslog(LOG_ERR, "could not register on %s",
499					nconf->nc_netid);
500			goto error;
501		}
502		pml = malloc(sizeof (struct pmaplist));
503		if (pml == NULL) {
504			syslog(LOG_ERR, "no memory!");
505			exit(1);
506		}
507		pml->pml_map.pm_prog = PMAPPROG;
508		pml->pml_map.pm_vers = PMAPVERS;
509		pml->pml_map.pm_port = PMAPPORT;
510		if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
511			if (tcptrans[0]) {
512				syslog(LOG_ERR,
513				"cannot have more than one TCP transport");
514				goto error;
515			}
516			tcptrans = strdup(nconf->nc_netid);
517			pml->pml_map.pm_prot = IPPROTO_TCP;
518
519			/* Let's snarf the universal address */
520			/* "h1.h2.h3.h4.p1.p2" */
521			tcp_uaddr = taddr2uaddr(nconf, &taddr.addr);
522		} else if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
523			if (udptrans[0]) {
524				syslog(LOG_ERR,
525				"cannot have more than one UDP transport");
526				goto error;
527			}
528			udptrans = strdup(nconf->nc_netid);
529			pml->pml_map.pm_prot = IPPROTO_UDP;
530
531			/* Let's snarf the universal address */
532			/* "h1.h2.h3.h4.p1.p2" */
533			udp_uaddr = taddr2uaddr(nconf, &taddr.addr);
534		} else if (strcmp(nconf->nc_netid, "unix") == 0)
535			pml->pml_map.pm_prot = IPPROTO_ST;
536		pml->pml_next = list_pml;
537		list_pml = pml;
538
539		/* Add version 3 information */
540		pml = malloc(sizeof (struct pmaplist));
541		if (pml == NULL) {
542			syslog(LOG_ERR, "no memory!");
543			exit(1);
544		}
545		pml->pml_map = list_pml->pml_map;
546		pml->pml_map.pm_vers = RPCBVERS;
547		pml->pml_next = list_pml;
548		list_pml = pml;
549
550		/* Add version 4 information */
551		pml = malloc (sizeof (struct pmaplist));
552		if (pml == NULL) {
553			syslog(LOG_ERR, "no memory!");
554			exit(1);
555		}
556		pml->pml_map = list_pml->pml_map;
557		pml->pml_map.pm_vers = RPCBVERS4;
558		pml->pml_next = list_pml;
559		list_pml = pml;
560
561		/* Also add version 2 stuff to rpcbind list */
562		rbllist_add(PMAPPROG, PMAPVERS, nconf, &taddr.addr);
563	}
564#endif
565
566	/* version 3 registration */
567	if (!svc_reg(my_xprt, RPCBPROG, RPCBVERS, rpcb_service_3, NULL)) {
568		syslog(LOG_ERR, "could not register %s version 3",
569				nconf->nc_netid);
570		goto error;
571	}
572	rbllist_add(RPCBPROG, RPCBVERS, nconf, &taddr.addr);
573
574	/* version 4 registration */
575	if (!svc_reg(my_xprt, RPCBPROG, RPCBVERS4, rpcb_service_4, NULL)) {
576		syslog(LOG_ERR, "could not register %s version 4",
577				nconf->nc_netid);
578		goto error;
579	}
580	rbllist_add(RPCBPROG, RPCBVERS4, nconf, &taddr.addr);
581
582	/* decide if bound checking works for this transport */
583	status = add_bndlist(nconf, &taddr.addr);
584#ifdef BIND_DEBUG
585	if (debugging) {
586		if (status < 0) {
587			fprintf(stderr, "Error in finding bind status for %s\n",
588				nconf->nc_netid);
589		} else if (status == 0) {
590			fprintf(stderr, "check binding for %s\n",
591				nconf->nc_netid);
592		} else if (status > 0) {
593			fprintf(stderr, "No check binding for %s\n",
594				nconf->nc_netid);
595		}
596	}
597#endif
598	/*
599	 * rmtcall only supported on CLTS transports for now.
600	 */
601	if (nconf->nc_semantics == NC_TPI_CLTS) {
602		status = create_rmtcall_fd(nconf);
603
604#ifdef BIND_DEBUG
605		if (debugging) {
606			if (status < 0) {
607				fprintf(stderr,
608				    "Could not create rmtcall fd for %s\n",
609					nconf->nc_netid);
610			} else {
611				fprintf(stderr, "rmtcall fd for %s is %d\n",
612					nconf->nc_netid, status);
613			}
614		}
615#endif
616	}
617	return (0);
618error:
619	close(fd);
620	return (1);
621}
622
623static void
624rbllist_add(rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf,
625	    struct netbuf *addr)
626{
627	rpcblist_ptr rbl;
628
629	rbl = malloc(sizeof (rpcblist));
630	if (rbl == NULL) {
631		syslog(LOG_ERR, "no memory!");
632		exit(1);
633	}
634
635	rbl->rpcb_map.r_prog = prog;
636	rbl->rpcb_map.r_vers = vers;
637	rbl->rpcb_map.r_netid = strdup(nconf->nc_netid);
638	rbl->rpcb_map.r_addr = taddr2uaddr(nconf, addr);
639	rbl->rpcb_map.r_owner = strdup(superuser);
640	rbl->rpcb_next = list_rbl;	/* Attach to global list */
641	list_rbl = rbl;
642}
643
644/*
645 * Catch the signal and die
646 */
647static void
648terminate(int dummy __unused)
649{
650#ifdef WARMSTART
651	syslog(LOG_ERR,
652		"rpcbind terminating on signal. Restart with \"rpcbind -w\"");
653	write_warmstart();	/* Dump yourself */
654#endif
655	exit(2);
656}
657
658void
659rpcbind_abort()
660{
661#ifdef WARMSTART
662	write_warmstart();	/* Dump yourself */
663#endif
664	abort();
665}
666
667/* get command line options */
668static void
669parseargs(int argc, char *argv[])
670{
671	int c;
672
673	while ((c = getopt(argc, argv, "dwah:ilLs")) != -1) {
674		switch (c) {
675		case 'a':
676			doabort = 1;	/* when debugging, do an abort on */
677			break;		/* errors; for rpcbind developers */
678					/* only! */
679		case 'd':
680			debugging = 1;
681			break;
682		case 'h':
683			++nhosts;
684			hosts = realloc(hosts, nhosts * sizeof(char *));
685			if (hosts == NULL)
686				errx(1, "Out of memory");
687			hosts[nhosts - 1] = strdup(optarg);
688			if (hosts[nhosts - 1] == NULL)
689				errx(1, "Out of memory");
690			break;
691		case 'i':
692			insecure = 1;
693			break;
694		case 'L':
695			oldstyle_local = 1;
696			break;
697		case 'l':
698			verboselog = 1;
699			break;
700		case 's':
701			runasdaemon = 1;
702			break;
703#ifdef WARMSTART
704		case 'w':
705			warmstart = 1;
706			break;
707#endif
708		default:	/* error */
709			fprintf(stderr,	"usage: rpcbind [-Idwils]\n");
710			exit (1);
711		}
712	}
713	if (doabort && !debugging) {
714	    fprintf(stderr,
715		"-a (abort) specified without -d (debugging) -- ignored.\n");
716	    doabort = 0;
717	}
718}
719
720void
721reap(int dummy __unused)
722{
723	int save_errno = errno;
724
725	while (wait3(NULL, WNOHANG, NULL) > 0)
726		;
727	errno = save_errno;
728}
729
730void
731toggle_verboselog(int dummy __unused)
732{
733	verboselog = !verboselog;
734}
735