1 1.31 nia /* $NetBSD: rpcbind.c,v 1.31 2021/10/30 11:04:48 nia Exp $ */ 2 1.1 fvdl 3 1.24 christos /*- 4 1.24 christos * Copyright (c) 2009, Sun Microsystems, Inc. 5 1.24 christos * All rights reserved. 6 1.1 fvdl * 7 1.24 christos * Redistribution and use in source and binary forms, with or without 8 1.24 christos * modification, are permitted provided that the following conditions are met: 9 1.24 christos * - Redistributions of source code must retain the above copyright notice, 10 1.24 christos * this list of conditions and the following disclaimer. 11 1.24 christos * - Redistributions in binary form must reproduce the above copyright notice, 12 1.24 christos * this list of conditions and the following disclaimer in the documentation 13 1.24 christos * and/or other materials provided with the distribution. 14 1.24 christos * - Neither the name of Sun Microsystems, Inc. nor the names of its 15 1.24 christos * contributors may be used to endorse or promote products derived 16 1.24 christos * from this software without specific prior written permission. 17 1.1 fvdl * 18 1.24 christos * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 1.24 christos * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 1.24 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 1.24 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 1.24 christos * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 1.24 christos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 1.24 christos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 1.24 christos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 1.24 christos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 1.24 christos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 1.24 christos * POSSIBILITY OF SUCH DAMAGE. 29 1.1 fvdl */ 30 1.1 fvdl /* 31 1.1 fvdl * Copyright (c) 1984 - 1991 by Sun Microsystems, Inc. 32 1.1 fvdl */ 33 1.1 fvdl 34 1.1 fvdl /* #ident "@(#)rpcbind.c 1.19 94/04/25 SMI" */ 35 1.1 fvdl 36 1.1 fvdl #if 0 37 1.1 fvdl #ifndef lint 38 1.1 fvdl static char sccsid[] = "@(#)rpcbind.c 1.35 89/04/21 Copyr 1984 Sun Micro"; 39 1.1 fvdl #endif 40 1.1 fvdl #endif 41 1.1 fvdl 42 1.1 fvdl /* 43 1.1 fvdl * rpcbind.c 44 1.1 fvdl * Implements the program, version to address mapping for rpc. 45 1.1 fvdl * 46 1.1 fvdl */ 47 1.1 fvdl 48 1.1 fvdl #include <sys/types.h> 49 1.1 fvdl #include <sys/stat.h> 50 1.1 fvdl #include <sys/errno.h> 51 1.1 fvdl #include <sys/time.h> 52 1.1 fvdl #include <sys/resource.h> 53 1.1 fvdl #include <sys/wait.h> 54 1.1 fvdl #include <sys/signal.h> 55 1.1 fvdl #include <sys/socket.h> 56 1.1 fvdl #include <sys/un.h> 57 1.1 fvdl #include <rpc/rpc.h> 58 1.24 christos #include <rpc/rpc_com.h> 59 1.1 fvdl #ifdef PORTMAP 60 1.1 fvdl #include <netinet/in.h> 61 1.1 fvdl #endif 62 1.24 christos #include <arpa/inet.h> 63 1.24 christos #include <fcntl.h> 64 1.1 fvdl #include <netdb.h> 65 1.1 fvdl #include <stdio.h> 66 1.1 fvdl #include <netconfig.h> 67 1.1 fvdl #include <stdlib.h> 68 1.1 fvdl #include <unistd.h> 69 1.1 fvdl #include <syslog.h> 70 1.1 fvdl #include <err.h> 71 1.1 fvdl #include <util.h> 72 1.1 fvdl #include <pwd.h> 73 1.1 fvdl #include <string.h> 74 1.1 fvdl #include <errno.h> 75 1.1 fvdl #include "rpcbind.h" 76 1.1 fvdl 77 1.23 christos #ifdef RPCBIND_RUMP 78 1.23 christos #include <semaphore.h> 79 1.23 christos 80 1.23 christos #include <rump/rump.h> 81 1.29 kamil #include <rump/rump_syscallshotgun.h> 82 1.23 christos #include <rump/rump_syscalls.h> 83 1.23 christos 84 1.23 christos #include "svc_fdset.h" 85 1.23 christos 86 1.23 christos extern sem_t gensem; 87 1.23 christos #define DEBUGGING 1 88 1.23 christos #else 89 1.23 christos #define DEBUGGING 0 90 1.23 christos #endif 91 1.23 christos 92 1.1 fvdl /* Global variables */ 93 1.23 christos int debugging = DEBUGGING; /* Tell me what's going on */ 94 1.1 fvdl int doabort = 0; /* When debugging, do an abort on errors */ 95 1.1 fvdl rpcblist_ptr list_rbl; /* A list of version 3/4 rpcbind services */ 96 1.1 fvdl 97 1.1 fvdl /* who to suid to if -s is given */ 98 1.1 fvdl #define RUN_AS "daemon" 99 1.1 fvdl 100 1.24 christos #define RPCBINDDLOCK "/var/run/rpcbind.lock" 101 1.24 christos 102 1.24 christos static int runasdaemon = 0; 103 1.1 fvdl int insecure = 0; 104 1.1 fvdl int oldstyle_local = 0; 105 1.24 christos #ifdef LIBWRAP 106 1.24 christos int libwrap = 0; 107 1.24 christos #endif 108 1.1 fvdl int verboselog = 0; 109 1.1 fvdl 110 1.24 christos static char **hosts = NULL; 111 1.24 christos static struct sockaddr **bound_sa; 112 1.24 christos static int ipv6_only = 0; 113 1.24 christos static int nhosts = 0; 114 1.24 christos static int on = 1; 115 1.25 christos #ifndef RPCBIND_RUMP 116 1.24 christos static int rpcbindlockfd; 117 1.25 christos #endif 118 1.24 christos 119 1.1 fvdl #ifdef WARMSTART 120 1.1 fvdl /* Local Variable */ 121 1.24 christos static int warmstart = 0; /* Grab an old copy of registrations */ 122 1.1 fvdl #endif 123 1.1 fvdl 124 1.1 fvdl #ifdef PORTMAP 125 1.1 fvdl struct pmaplist *list_pml; /* A list of version 2 rpcbind services */ 126 1.11 christos const char *udptrans; /* Name of UDP transport */ 127 1.11 christos const char *tcptrans; /* Name of TCP transport */ 128 1.11 christos const char *udp_uaddr; /* Universal UDP address */ 129 1.11 christos const char *tcp_uaddr; /* Universal TCP address */ 130 1.1 fvdl #endif 131 1.10 christos static const char servname[] = "sunrpc"; 132 1.1 fvdl 133 1.13 christos const char rpcbind_superuser[] = "superuser"; 134 1.13 christos const char rpcbind_unknown[] = "unknown"; 135 1.1 fvdl 136 1.11 christos static int init_transport(struct netconfig *); 137 1.11 christos static void rbllist_add(rpcprog_t, rpcvers_t, struct netconfig *, 138 1.11 christos struct netbuf *); 139 1.17 joerg __dead static void terminate(int); 140 1.24 christos static void update_bound_sa(void); 141 1.23 christos #ifndef RPCBIND_RUMP 142 1.11 christos static void parseargs(int, char *[]); 143 1.1 fvdl 144 1.1 fvdl int 145 1.1 fvdl main(int argc, char *argv[]) 146 1.23 christos #else 147 1.23 christos int rpcbind_main(void *); 148 1.23 christos int 149 1.23 christos rpcbind_main(void *arg) 150 1.23 christos #endif 151 1.1 fvdl { 152 1.1 fvdl struct netconfig *nconf; 153 1.1 fvdl void *nc_handle; /* Net config handle */ 154 1.1 fvdl struct rlimit rl; 155 1.3 fvdl int maxrec = RPC_MAXDATASIZE; 156 1.1 fvdl 157 1.23 christos #ifdef RPCBIND_RUMP 158 1.23 christos svc_fdset_init(SVC_FDSET_MT); 159 1.23 christos #else 160 1.1 fvdl parseargs(argc, argv); 161 1.23 christos #endif 162 1.1 fvdl 163 1.22 christos if (getrlimit(RLIMIT_NOFILE, &rl) == -1) 164 1.22 christos err(EXIT_FAILURE, "getrlimit(RLIMIT_NOFILE)"); 165 1.22 christos 166 1.22 christos if (rl.rlim_cur < 128) { 167 1.1 fvdl if (rl.rlim_max <= 128) 168 1.1 fvdl rl.rlim_cur = rl.rlim_max; 169 1.1 fvdl else 170 1.1 fvdl rl.rlim_cur = 128; 171 1.22 christos if (setrlimit(RLIMIT_NOFILE, &rl) < 0) 172 1.21 dholland err(EXIT_FAILURE, "setrlimit(RLIMIT_NOFILE)"); 173 1.1 fvdl } 174 1.24 christos update_bound_sa(); 175 1.24 christos 176 1.25 christos #ifndef RPCBIND_RUMP 177 1.24 christos /* Check that another rpcbind isn't already running. */ 178 1.24 christos if ((rpcbindlockfd = open(RPCBINDDLOCK, O_RDONLY|O_CREAT, 0444)) == -1) 179 1.26 christos err(EXIT_FAILURE, "%s", RPCBINDDLOCK); 180 1.24 christos 181 1.24 christos if (flock(rpcbindlockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK) 182 1.26 christos errx(EXIT_FAILURE, 183 1.26 christos "another rpcbind is already running. Aborting"); 184 1.24 christos 185 1.24 christos if (geteuid()) /* This command allowed only to root */ 186 1.24 christos errx(EXIT_FAILURE, "Sorry. You are not superuser\n"); 187 1.24 christos #endif 188 1.1 fvdl nc_handle = setnetconfig(); /* open netconfig file */ 189 1.11 christos if (nc_handle == NULL) 190 1.22 christos errx(EXIT_FAILURE, "could not read /etc/netconfig"); 191 1.24 christos 192 1.1 fvdl #ifdef PORTMAP 193 1.1 fvdl udptrans = ""; 194 1.1 fvdl tcptrans = ""; 195 1.1 fvdl #endif 196 1.1 fvdl 197 1.1 fvdl nconf = getnetconfigent("local"); 198 1.11 christos if (nconf == NULL) 199 1.22 christos errx(EXIT_FAILURE, "can't find local transport"); 200 1.3 fvdl 201 1.3 fvdl rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec); 202 1.3 fvdl 203 1.1 fvdl init_transport(nconf); 204 1.1 fvdl 205 1.1 fvdl while ((nconf = getnetconfig(nc_handle))) { 206 1.24 christos if (nconf->nc_flag & NC_VISIBLE) { 207 1.24 christos if (ipv6_only == 1 && strcmp(nconf->nc_protofmly, 208 1.24 christos "inet") == 0) { 209 1.24 christos /* DO NOTHING */ 210 1.24 christos } else 211 1.24 christos init_transport(nconf); 212 1.24 christos } 213 1.1 fvdl } 214 1.1 fvdl endnetconfig(nc_handle); 215 1.1 fvdl 216 1.1 fvdl /* catch the usual termination signals for graceful exit */ 217 1.1 fvdl (void) signal(SIGCHLD, reap); 218 1.1 fvdl (void) signal(SIGINT, terminate); 219 1.1 fvdl (void) signal(SIGTERM, terminate); 220 1.1 fvdl (void) signal(SIGQUIT, terminate); 221 1.1 fvdl /* ignore others that could get sent */ 222 1.1 fvdl (void) signal(SIGPIPE, SIG_IGN); 223 1.23 christos #ifndef RPCBIND_RUMP 224 1.1 fvdl (void) signal(SIGHUP, SIG_IGN); 225 1.23 christos #endif 226 1.1 fvdl (void) signal(SIGUSR1, SIG_IGN); 227 1.1 fvdl (void) signal(SIGUSR2, SIG_IGN); 228 1.1 fvdl #ifdef WARMSTART 229 1.1 fvdl if (warmstart) { 230 1.1 fvdl read_warmstart(); 231 1.1 fvdl } 232 1.1 fvdl #endif 233 1.1 fvdl if (debugging) { 234 1.1 fvdl printf("rpcbind debugging enabled."); 235 1.1 fvdl if (doabort) { 236 1.1 fvdl printf(" Will abort on errors!\n"); 237 1.1 fvdl } else { 238 1.1 fvdl printf("\n"); 239 1.1 fvdl } 240 1.1 fvdl } else { 241 1.1 fvdl if (daemon(0, 0)) 242 1.22 christos err(EXIT_FAILURE, "fork failed"); 243 1.1 fvdl } 244 1.11 christos 245 1.11 christos openlog("rpcbind", 0, LOG_DAEMON); 246 1.1 fvdl pidfile(NULL); 247 1.1 fvdl 248 1.1 fvdl if (runasdaemon) { 249 1.1 fvdl struct passwd *p; 250 1.1 fvdl 251 1.1 fvdl if((p = getpwnam(RUN_AS)) == NULL) { 252 1.1 fvdl syslog(LOG_ERR, "cannot get uid of daemon: %m"); 253 1.22 christos exit(EXIT_FAILURE); 254 1.1 fvdl } 255 1.1 fvdl if (setuid(p->pw_uid) == -1) { 256 1.1 fvdl syslog(LOG_ERR, "setuid to daemon failed: %m"); 257 1.22 christos exit(EXIT_FAILURE); 258 1.1 fvdl } 259 1.1 fvdl } 260 1.1 fvdl 261 1.1 fvdl network_init(); 262 1.1 fvdl 263 1.23 christos #ifdef RPCBIND_RUMP 264 1.23 christos sem_post(&gensem); 265 1.23 christos #endif 266 1.1 fvdl my_svc_run(); 267 1.1 fvdl syslog(LOG_ERR, "svc_run returned unexpectedly"); 268 1.1 fvdl rpcbind_abort(); 269 1.1 fvdl /* NOTREACHED */ 270 1.1 fvdl 271 1.22 christos return EXIT_SUCCESS; 272 1.1 fvdl } 273 1.1 fvdl 274 1.1 fvdl /* 275 1.1 fvdl * Adds the entry into the rpcbind database. 276 1.1 fvdl * If PORTMAP, then for UDP and TCP, it adds the entries for version 2 also 277 1.1 fvdl * Returns 0 if succeeds, else fails 278 1.1 fvdl */ 279 1.1 fvdl static int 280 1.1 fvdl init_transport(struct netconfig *nconf) 281 1.1 fvdl { 282 1.1 fvdl int fd; 283 1.1 fvdl struct t_bind taddr; 284 1.1 fvdl struct addrinfo hints, *res = NULL; 285 1.1 fvdl struct __rpc_sockinfo si; 286 1.1 fvdl SVCXPRT *my_xprt; 287 1.1 fvdl int status; /* bound checking ? */ 288 1.1 fvdl int aicode; 289 1.1 fvdl int addrlen; 290 1.24 christos int nhostsbak; 291 1.24 christos int bound; 292 1.24 christos u_int32_t host_addr[4]; /* IPv4 or IPv6 */ 293 1.1 fvdl struct sockaddr *sa; 294 1.1 fvdl struct sockaddr_un sun; 295 1.24 christos #ifndef RPCBIND_RUMP 296 1.24 christos mode_t oldmask; 297 1.24 christos #endif 298 1.1 fvdl 299 1.1 fvdl if ((nconf->nc_semantics != NC_TPI_CLTS) && 300 1.1 fvdl (nconf->nc_semantics != NC_TPI_COTS) && 301 1.1 fvdl (nconf->nc_semantics != NC_TPI_COTS_ORD)) 302 1.11 christos return 1; /* not my type */ 303 1.15 dsl #ifdef RPCBIND_DEBUG 304 1.1 fvdl if (debugging) { 305 1.30 christos unsigned int i; 306 1.1 fvdl char **s; 307 1.1 fvdl 308 1.11 christos (void)fprintf(stderr, "%s: %ld lookup routines :\n", 309 1.11 christos nconf->nc_netid, nconf->nc_nlookups); 310 1.1 fvdl for (i = 0, s = nconf->nc_lookups; i < nconf->nc_nlookups; 311 1.1 fvdl i++, s++) 312 1.30 christos (void)fprintf(stderr, "[%u] - %s\n", i, *s); 313 1.1 fvdl } 314 1.1 fvdl #endif 315 1.1 fvdl 316 1.1 fvdl /* 317 1.1 fvdl * XXX - using RPC library internal functions. 318 1.1 fvdl */ 319 1.24 christos if (strcmp(nconf->nc_netid, "local") == 0) { 320 1.24 christos /* 321 1.24 christos * For other transports we call this later, for each socket we 322 1.24 christos * like to bind. 323 1.24 christos */ 324 1.24 christos if ((fd = __rpc_nconf2fd(nconf)) < 0) { 325 1.24 christos int non_fatal = 0; 326 1.24 christos if (errno == EAFNOSUPPORT) 327 1.24 christos non_fatal = 1; 328 1.24 christos syslog(non_fatal ? LOG_DEBUG : LOG_ERR, 329 1.24 christos "Cannot create socket for `%s'", nconf->nc_netid); 330 1.14 christos return 1; 331 1.24 christos } 332 1.24 christos } else 333 1.24 christos fd = -1; 334 1.1 fvdl 335 1.1 fvdl if (!__rpc_nconf2sockinfo(nconf, &si)) { 336 1.24 christos syslog(LOG_ERR, "Cannot get information for `%s'", 337 1.24 christos nconf->nc_netid); 338 1.11 christos return 1; 339 1.1 fvdl } 340 1.6 fvdl 341 1.24 christos if (strcmp(nconf->nc_netid, "local") == 0) { 342 1.11 christos (void)memset(&sun, 0, sizeof sun); 343 1.1 fvdl sun.sun_family = AF_LOCAL; 344 1.23 christos #ifdef RPCBIND_RUMP 345 1.23 christos (void)rump_sys_unlink(_PATH_RPCBINDSOCK); 346 1.23 christos #else 347 1.11 christos (void)unlink(_PATH_RPCBINDSOCK); 348 1.23 christos #endif 349 1.11 christos (void)strlcpy(sun.sun_path, _PATH_RPCBINDSOCK, 350 1.11 christos sizeof(sun.sun_path)); 351 1.1 fvdl sun.sun_len = SUN_LEN(&sun); 352 1.11 christos addrlen = sizeof(struct sockaddr_un); 353 1.1 fvdl sa = (struct sockaddr *)&sun; 354 1.1 fvdl } else { 355 1.1 fvdl /* Get rpcbind's address on this transport */ 356 1.1 fvdl 357 1.11 christos (void)memset(&hints, 0, sizeof hints); 358 1.1 fvdl hints.ai_flags = AI_PASSIVE; 359 1.1 fvdl hints.ai_family = si.si_af; 360 1.1 fvdl hints.ai_socktype = si.si_socktype; 361 1.1 fvdl hints.ai_protocol = si.si_proto; 362 1.24 christos } 363 1.24 christos 364 1.24 christos if (strcmp(nconf->nc_netid, "local") != 0) { 365 1.24 christos /* 366 1.24 christos * If no hosts were specified, just bind to INADDR_ANY. 367 1.24 christos * Otherwise make sure 127.0.0.1 is added to the list. 368 1.24 christos */ 369 1.24 christos nhostsbak = nhosts + 1; 370 1.31 nia if (reallocarr(&hosts, nhostsbak, sizeof(*hosts)) != 0) { 371 1.26 christos syslog(LOG_ERR, "Can't grow hosts array"); 372 1.26 christos return 1; 373 1.26 christos } 374 1.24 christos if (nhostsbak == 1) 375 1.24 christos hosts[0] = __UNCONST("*"); 376 1.24 christos else { 377 1.24 christos if (hints.ai_family == AF_INET) { 378 1.24 christos hosts[nhostsbak - 1] = __UNCONST("127.0.0.1"); 379 1.24 christos } else if (hints.ai_family == AF_INET6) { 380 1.24 christos hosts[nhostsbak - 1] = __UNCONST("::1"); 381 1.24 christos } else 382 1.24 christos return 1; 383 1.24 christos } 384 1.24 christos 385 1.24 christos /* 386 1.24 christos * Bind to specific IPs if asked to 387 1.24 christos */ 388 1.24 christos bound = 0; 389 1.24 christos while (nhostsbak > 0) { 390 1.24 christos --nhostsbak; 391 1.24 christos /* 392 1.24 christos * XXX - using RPC library internal functions. 393 1.24 christos */ 394 1.24 christos if ((fd = __rpc_nconf2fd(nconf)) < 0) { 395 1.24 christos int non_fatal = 0; 396 1.24 christos if (errno == EAFNOSUPPORT && 397 1.24 christos nconf->nc_semantics != NC_TPI_CLTS) 398 1.24 christos non_fatal = 1; 399 1.24 christos syslog(non_fatal ? LOG_DEBUG : LOG_ERR, 400 1.24 christos "cannot create socket for %s", 401 1.24 christos nconf->nc_netid); 402 1.24 christos return 1; 403 1.24 christos } 404 1.24 christos switch (hints.ai_family) { 405 1.24 christos case AF_INET: 406 1.24 christos if (inet_pton(AF_INET, hosts[nhostsbak], 407 1.24 christos host_addr) == 1) { 408 1.24 christos hints.ai_flags &= AI_NUMERICHOST; 409 1.24 christos } else { 410 1.24 christos /* 411 1.24 christos * Skip if we have an AF_INET6 address. 412 1.24 christos */ 413 1.24 christos if (inet_pton(AF_INET6, 414 1.24 christos hosts[nhostsbak], host_addr) == 1) { 415 1.24 christos close(fd); 416 1.24 christos continue; 417 1.24 christos } 418 1.24 christos } 419 1.24 christos break; 420 1.24 christos case AF_INET6: 421 1.24 christos if (inet_pton(AF_INET6, hosts[nhostsbak], 422 1.24 christos host_addr) == 1) { 423 1.24 christos hints.ai_flags &= AI_NUMERICHOST; 424 1.24 christos } else { 425 1.24 christos /* 426 1.24 christos * Skip if we have an AF_INET address. 427 1.24 christos */ 428 1.24 christos if (inet_pton(AF_INET, hosts[nhostsbak], 429 1.24 christos host_addr) == 1) { 430 1.24 christos close(fd); 431 1.24 christos continue; 432 1.24 christos } 433 1.24 christos } 434 1.24 christos if (setsockopt(fd, IPPROTO_IPV6, 435 1.24 christos IPV6_V6ONLY, &on, sizeof on) < 0) { 436 1.24 christos syslog(LOG_ERR, 437 1.24 christos "can't set v6-only binding for " 438 1.24 christos "ipv6 socket: %m"); 439 1.24 christos continue; 440 1.24 christos } 441 1.24 christos break; 442 1.24 christos default: 443 1.24 christos break; 444 1.24 christos } 445 1.24 christos 446 1.24 christos /* 447 1.24 christos * If no hosts were specified, just bind to INADDR_ANY 448 1.24 christos */ 449 1.24 christos if (strcmp("*", hosts[nhostsbak]) == 0) 450 1.24 christos hosts[nhostsbak] = NULL; 451 1.24 christos if (strcmp(nconf->nc_netid, "local") != 0) { 452 1.24 christos if ((aicode = getaddrinfo(hosts[nhostsbak], 453 1.24 christos servname, &hints, &res)) != 0) { 454 1.24 christos syslog(LOG_ERR, 455 1.24 christos "cannot get local address for %s: %s", 456 1.24 christos nconf->nc_netid, 457 1.24 christos gai_strerror(aicode)); 458 1.24 christos continue; 459 1.24 christos } 460 1.24 christos addrlen = res->ai_addrlen; 461 1.24 christos sa = (struct sockaddr *)res->ai_addr; 462 1.24 christos } 463 1.24 christos #ifndef RPCBIND_RUMP 464 1.24 christos oldmask = umask(S_IXUSR|S_IXGRP|S_IXOTH); 465 1.24 christos #endif 466 1.24 christos if (bind(fd, sa, addrlen) != 0) { 467 1.24 christos syslog(LOG_ERR, "cannot bind %s on %s: %m", 468 1.24 christos (hosts[nhostsbak] == NULL) ? "*" : 469 1.24 christos hosts[nhostsbak], nconf->nc_netid); 470 1.24 christos if (res != NULL) 471 1.24 christos freeaddrinfo(res); 472 1.24 christos continue; 473 1.24 christos } else 474 1.24 christos bound = 1; 475 1.24 christos #ifndef RPCBIND_RUMP 476 1.24 christos (void)umask(oldmask); 477 1.24 christos #endif 478 1.24 christos 479 1.24 christos /* Copy the address */ 480 1.24 christos taddr.addr.len = taddr.addr.maxlen = addrlen; 481 1.24 christos taddr.addr.buf = malloc(addrlen); 482 1.24 christos if (taddr.addr.buf == NULL) { 483 1.27 christos syslog(LOG_ERR, "%s: Cannot allocate memory", 484 1.27 christos __func__); 485 1.24 christos if (res != NULL) 486 1.24 christos freeaddrinfo(res); 487 1.24 christos return 1; 488 1.24 christos } 489 1.24 christos memcpy(taddr.addr.buf, sa, addrlen); 490 1.24 christos #ifdef RPCBIND_DEBUG 491 1.24 christos if (debugging) { 492 1.24 christos /* 493 1.24 christos * for debugging print out our universal 494 1.24 christos * address 495 1.24 christos */ 496 1.24 christos char *uaddr; 497 1.24 christos struct netbuf nb; 498 1.24 christos 499 1.24 christos nb.buf = sa; 500 1.24 christos nb.len = nb.maxlen = sa->sa_len; 501 1.24 christos uaddr = taddr2uaddr(nconf, &nb); 502 1.24 christos (void)fprintf(stderr, 503 1.24 christos "rpcbind : my address is %s\n", uaddr); 504 1.24 christos (void)free(uaddr); 505 1.24 christos } 506 1.24 christos #endif 507 1.24 christos 508 1.24 christos if (nconf->nc_semantics != NC_TPI_CLTS) 509 1.24 christos listen(fd, SOMAXCONN); 510 1.24 christos 511 1.24 christos my_xprt = (SVCXPRT *)svc_tli_create(fd, nconf, &taddr, 512 1.24 christos RPC_MAXDATASIZE, RPC_MAXDATASIZE); 513 1.24 christos if (my_xprt == NULL) { 514 1.24 christos syslog(LOG_ERR, 515 1.24 christos "Could not create service for `%s'", 516 1.24 christos nconf->nc_netid); 517 1.24 christos goto error; 518 1.24 christos } 519 1.24 christos } 520 1.24 christos if (!bound) 521 1.24 christos return 1; 522 1.24 christos } else { 523 1.24 christos #ifndef RPCBIND_RUMP 524 1.24 christos oldmask = umask(S_IXUSR|S_IXGRP|S_IXOTH); 525 1.24 christos #endif 526 1.24 christos if (bind(fd, sa, addrlen) < 0) { 527 1.24 christos syslog(LOG_ERR, "cannot bind %s: %m", nconf->nc_netid); 528 1.24 christos if (res != NULL) 529 1.24 christos freeaddrinfo(res); 530 1.1 fvdl return 1; 531 1.1 fvdl } 532 1.23 christos #ifndef RPCBIND_RUMP 533 1.24 christos (void) umask(oldmask); 534 1.23 christos #endif 535 1.1 fvdl 536 1.24 christos /* Copy the address */ 537 1.24 christos taddr.addr.len = taddr.addr.maxlen = addrlen; 538 1.24 christos taddr.addr.buf = malloc(addrlen); 539 1.24 christos if (taddr.addr.buf == NULL) { 540 1.27 christos syslog(LOG_ERR, "%s: Cannot allocate memory", __func__); 541 1.24 christos if (res != NULL) 542 1.24 christos freeaddrinfo(res); 543 1.24 christos return 1; 544 1.24 christos } 545 1.24 christos memcpy(taddr.addr.buf, sa, addrlen); 546 1.15 dsl #ifdef RPCBIND_DEBUG 547 1.24 christos if (debugging) { 548 1.24 christos /* for debugging print out our universal address */ 549 1.24 christos char *uaddr; 550 1.24 christos struct netbuf nb; 551 1.24 christos 552 1.24 christos nb.buf = sa; 553 1.24 christos nb.len = nb.maxlen = sa->sa_len; 554 1.24 christos uaddr = taddr2uaddr(nconf, &nb); 555 1.24 christos (void) fprintf(stderr, "rpcbind : my address is %s\n", 556 1.24 christos uaddr); 557 1.24 christos (void) free(uaddr); 558 1.24 christos } 559 1.1 fvdl #endif 560 1.1 fvdl 561 1.24 christos if (nconf->nc_semantics != NC_TPI_CLTS) 562 1.24 christos listen(fd, SOMAXCONN); 563 1.24 christos 564 1.24 christos my_xprt = (SVCXPRT *)svc_tli_create(fd, nconf, &taddr, 565 1.24 christos RPC_MAXDATASIZE, RPC_MAXDATASIZE); 566 1.27 christos if (my_xprt == NULL) { 567 1.24 christos syslog(LOG_ERR, "%s: could not create service", 568 1.24 christos nconf->nc_netid); 569 1.24 christos goto error; 570 1.24 christos } 571 1.1 fvdl } 572 1.1 fvdl 573 1.1 fvdl #ifdef PORTMAP 574 1.1 fvdl /* 575 1.1 fvdl * Register both the versions for tcp/ip, udp/ip and local. 576 1.1 fvdl */ 577 1.1 fvdl if ((strcmp(nconf->nc_protofmly, NC_INET) == 0 && 578 1.1 fvdl (strcmp(nconf->nc_proto, NC_TCP) == 0 || 579 1.1 fvdl strcmp(nconf->nc_proto, NC_UDP) == 0)) || 580 1.1 fvdl strcmp(nconf->nc_netid, "local") == 0) { 581 1.1 fvdl struct pmaplist *pml; 582 1.1 fvdl 583 1.1 fvdl if (!svc_register(my_xprt, PMAPPROG, PMAPVERS, 584 1.5 fvdl pmap_service, 0)) { 585 1.24 christos syslog(LOG_ERR, "Could not register on `%s'", 586 1.24 christos nconf->nc_netid); 587 1.1 fvdl goto error; 588 1.1 fvdl } 589 1.26 christos pml = malloc(sizeof(*pml)); 590 1.8 christos if (pml == NULL) { 591 1.27 christos syslog(LOG_ERR, "%s: Cannot allocate memory", __func__); 592 1.11 christos goto error; 593 1.1 fvdl } 594 1.24 christos 595 1.1 fvdl pml->pml_map.pm_prog = PMAPPROG; 596 1.1 fvdl pml->pml_map.pm_vers = PMAPVERS; 597 1.1 fvdl pml->pml_map.pm_port = PMAPPORT; 598 1.1 fvdl if (strcmp(nconf->nc_proto, NC_TCP) == 0) { 599 1.1 fvdl if (tcptrans[0]) { 600 1.24 christos syslog(LOG_ERR, 601 1.11 christos "Cannot have more than one TCP transport"); 602 1.8 christos free(pml); 603 1.1 fvdl goto error; 604 1.1 fvdl } 605 1.1 fvdl tcptrans = strdup(nconf->nc_netid); 606 1.11 christos if (tcptrans == NULL) { 607 1.11 christos free(pml); 608 1.27 christos syslog(LOG_ERR, "%s: Cannot allocate memory", 609 1.27 christos __func__); 610 1.11 christos goto error; 611 1.11 christos } 612 1.1 fvdl pml->pml_map.pm_prot = IPPROTO_TCP; 613 1.1 fvdl 614 1.1 fvdl /* Let's snarf the universal address */ 615 1.1 fvdl /* "h1.h2.h3.h4.p1.p2" */ 616 1.1 fvdl tcp_uaddr = taddr2uaddr(nconf, &taddr.addr); 617 1.1 fvdl } else if (strcmp(nconf->nc_proto, NC_UDP) == 0) { 618 1.1 fvdl if (udptrans[0]) { 619 1.11 christos free(pml); 620 1.24 christos syslog(LOG_ERR, 621 1.11 christos "Cannot have more than one UDP transport"); 622 1.1 fvdl goto error; 623 1.1 fvdl } 624 1.1 fvdl udptrans = strdup(nconf->nc_netid); 625 1.11 christos if (udptrans == NULL) { 626 1.11 christos free(pml); 627 1.27 christos syslog(LOG_ERR, "%s: Cannot allocate memory", 628 1.27 christos __func__); 629 1.11 christos goto error; 630 1.11 christos } 631 1.1 fvdl pml->pml_map.pm_prot = IPPROTO_UDP; 632 1.1 fvdl 633 1.1 fvdl /* Let's snarf the universal address */ 634 1.1 fvdl /* "h1.h2.h3.h4.p1.p2" */ 635 1.1 fvdl udp_uaddr = taddr2uaddr(nconf, &taddr.addr); 636 1.28 christos } else if (strcmp(nconf->nc_netid, "local") == 0) { 637 1.24 christos #ifdef IPPROTO_ST 638 1.24 christos pml->pml_map.pm_prot = IPPROTO_ST; 639 1.28 christos #else 640 1.28 christos pml->pml_map.pm_prot = 0; 641 1.24 christos #endif 642 1.28 christos } 643 1.1 fvdl pml->pml_next = list_pml; 644 1.1 fvdl list_pml = pml; 645 1.1 fvdl 646 1.1 fvdl /* Add version 3 information */ 647 1.26 christos pml = malloc(sizeof(*pml)); 648 1.8 christos if (pml == NULL) { 649 1.27 christos syslog(LOG_ERR, "%s: Cannot allocate memory", __func__); 650 1.11 christos goto error; 651 1.1 fvdl } 652 1.1 fvdl pml->pml_map = list_pml->pml_map; 653 1.1 fvdl pml->pml_map.pm_vers = RPCBVERS; 654 1.1 fvdl pml->pml_next = list_pml; 655 1.1 fvdl list_pml = pml; 656 1.1 fvdl 657 1.1 fvdl /* Add version 4 information */ 658 1.26 christos pml = malloc(sizeof(*pml)); 659 1.8 christos if (pml == NULL) { 660 1.27 christos syslog(LOG_ERR, "%s: Cannot allocate memory", __func__); 661 1.11 christos goto error; 662 1.1 fvdl } 663 1.1 fvdl pml->pml_map = list_pml->pml_map; 664 1.1 fvdl pml->pml_map.pm_vers = RPCBVERS4; 665 1.1 fvdl pml->pml_next = list_pml; 666 1.1 fvdl list_pml = pml; 667 1.1 fvdl 668 1.1 fvdl /* Also add version 2 stuff to rpcbind list */ 669 1.1 fvdl rbllist_add(PMAPPROG, PMAPVERS, nconf, &taddr.addr); 670 1.1 fvdl } 671 1.1 fvdl #endif 672 1.1 fvdl 673 1.1 fvdl /* version 3 registration */ 674 1.1 fvdl if (!svc_reg(my_xprt, RPCBPROG, RPCBVERS, rpcb_service_3, NULL)) { 675 1.24 christos syslog(LOG_ERR, "Could not register %s version 3", 676 1.24 christos nconf->nc_netid); 677 1.1 fvdl goto error; 678 1.1 fvdl } 679 1.1 fvdl rbllist_add(RPCBPROG, RPCBVERS, nconf, &taddr.addr); 680 1.1 fvdl 681 1.1 fvdl /* version 4 registration */ 682 1.1 fvdl if (!svc_reg(my_xprt, RPCBPROG, RPCBVERS4, rpcb_service_4, NULL)) { 683 1.24 christos syslog(LOG_ERR, "Could not register %s version 4", 684 1.24 christos nconf->nc_netid); 685 1.1 fvdl goto error; 686 1.1 fvdl } 687 1.1 fvdl rbllist_add(RPCBPROG, RPCBVERS4, nconf, &taddr.addr); 688 1.1 fvdl 689 1.1 fvdl /* decide if bound checking works for this transport */ 690 1.1 fvdl status = add_bndlist(nconf, &taddr.addr); 691 1.15 dsl #ifdef RPCBIND_DEBUG 692 1.1 fvdl if (debugging) { 693 1.1 fvdl if (status < 0) { 694 1.1 fvdl fprintf(stderr, "Error in finding bind status for %s\n", 695 1.1 fvdl nconf->nc_netid); 696 1.1 fvdl } else if (status == 0) { 697 1.1 fvdl fprintf(stderr, "check binding for %s\n", 698 1.1 fvdl nconf->nc_netid); 699 1.1 fvdl } else if (status > 0) { 700 1.1 fvdl fprintf(stderr, "No check binding for %s\n", 701 1.1 fvdl nconf->nc_netid); 702 1.1 fvdl } 703 1.1 fvdl } 704 1.19 christos #else 705 1.19 christos __USE(status); 706 1.1 fvdl #endif 707 1.1 fvdl /* 708 1.1 fvdl * rmtcall only supported on CLTS transports for now. 709 1.1 fvdl */ 710 1.1 fvdl if (nconf->nc_semantics == NC_TPI_CLTS) { 711 1.1 fvdl status = create_rmtcall_fd(nconf); 712 1.1 fvdl 713 1.15 dsl #ifdef RPCBIND_DEBUG 714 1.1 fvdl if (debugging) { 715 1.1 fvdl if (status < 0) { 716 1.1 fvdl fprintf(stderr, 717 1.1 fvdl "Could not create rmtcall fd for %s\n", 718 1.1 fvdl nconf->nc_netid); 719 1.1 fvdl } else { 720 1.1 fvdl fprintf(stderr, "rmtcall fd for %s is %d\n", 721 1.1 fvdl nconf->nc_netid, status); 722 1.1 fvdl } 723 1.1 fvdl } 724 1.1 fvdl #endif 725 1.1 fvdl } 726 1.1 fvdl return (0); 727 1.1 fvdl error: 728 1.23 christos #ifdef RPCBIND_RUMP 729 1.23 christos (void)rump_sys_close(fd); 730 1.23 christos #else 731 1.11 christos (void)close(fd); 732 1.23 christos #endif 733 1.1 fvdl return (1); 734 1.1 fvdl } 735 1.1 fvdl 736 1.24 christos /* 737 1.24 christos * Create the list of addresses that we're bound to. Normally, this 738 1.24 christos * list is empty because we're listening on the wildcard address 739 1.24 christos * (nhost == 0). If -h is specified on the command line, then 740 1.24 christos * bound_sa will have a list of the addresses that the program binds 741 1.24 christos * to specifically. This function takes that list and converts them to 742 1.24 christos * struct sockaddr * and stores them in bound_sa. 743 1.24 christos */ 744 1.24 christos static void 745 1.24 christos update_bound_sa(void) 746 1.24 christos { 747 1.24 christos struct addrinfo hints, *res = NULL; 748 1.24 christos int i; 749 1.24 christos 750 1.24 christos if (nhosts == 0) 751 1.24 christos return; 752 1.26 christos bound_sa = calloc(nhosts, sizeof(*bound_sa)); 753 1.26 christos if (bound_sa == NULL) 754 1.26 christos err(EXIT_FAILURE, "no space for bound address array"); 755 1.24 christos memset(&hints, 0, sizeof(hints)); 756 1.24 christos hints.ai_family = PF_UNSPEC; 757 1.24 christos for (i = 0; i < nhosts; i++) { 758 1.24 christos if (getaddrinfo(hosts[i], NULL, &hints, &res) != 0) 759 1.24 christos continue; 760 1.24 christos bound_sa[i] = malloc(res->ai_addrlen); 761 1.26 christos if (bound_sa[i] == NULL) 762 1.26 christos err(EXIT_FAILURE, "no space for bound address"); 763 1.24 christos memcpy(bound_sa[i], res->ai_addr, res->ai_addrlen); 764 1.24 christos } 765 1.24 christos } 766 1.24 christos 767 1.24 christos /* 768 1.24 christos * Match the sa against the list of addresses we've bound to. If 769 1.24 christos * we've not specifically bound to anything, we match everything. 770 1.24 christos * Otherwise, if the IPv4 or IPv6 address matches one of the addresses 771 1.24 christos * in bound_sa, we return true. If not, we return false. 772 1.24 christos */ 773 1.24 christos int 774 1.24 christos listen_addr(const struct sockaddr *sa) 775 1.24 christos { 776 1.24 christos int i; 777 1.24 christos 778 1.24 christos /* 779 1.24 christos * If nhosts == 0, then there were no -h options on the 780 1.24 christos * command line, so all addresses are addresses we're 781 1.24 christos * listening to. 782 1.24 christos */ 783 1.24 christos if (nhosts == 0) 784 1.24 christos return 1; 785 1.24 christos for (i = 0; i < nhosts; i++) { 786 1.24 christos if (bound_sa[i] == NULL || 787 1.24 christos sa->sa_family != bound_sa[i]->sa_family) 788 1.24 christos continue; 789 1.24 christos switch (sa->sa_family) { 790 1.24 christos case AF_INET: 791 1.24 christos if (memcmp(&SA2SINADDR(sa), &SA2SINADDR(bound_sa[i]), 792 1.24 christos sizeof(struct in_addr)) == 0) 793 1.24 christos return (1); 794 1.24 christos break; 795 1.24 christos #ifdef INET6 796 1.24 christos case AF_INET6: 797 1.24 christos if (memcmp(&SA2SIN6ADDR(sa), &SA2SIN6ADDR(bound_sa[i]), 798 1.24 christos sizeof(struct in6_addr)) == 0) 799 1.24 christos return (1); 800 1.24 christos break; 801 1.24 christos #endif 802 1.24 christos default: 803 1.24 christos break; 804 1.24 christos } 805 1.24 christos } 806 1.24 christos return (0); 807 1.24 christos } 808 1.24 christos 809 1.1 fvdl static void 810 1.1 fvdl rbllist_add(rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf, 811 1.1 fvdl struct netbuf *addr) 812 1.1 fvdl { 813 1.1 fvdl rpcblist_ptr rbl; 814 1.1 fvdl 815 1.27 christos rbl = calloc(1, sizeof(*rbl)); 816 1.8 christos if (rbl == NULL) { 817 1.27 christos syslog(LOG_ERR, "%s: Cannot allocate memory", __func__); 818 1.11 christos return; 819 1.1 fvdl } 820 1.1 fvdl 821 1.1 fvdl rbl->rpcb_map.r_prog = prog; 822 1.1 fvdl rbl->rpcb_map.r_vers = vers; 823 1.1 fvdl rbl->rpcb_map.r_netid = strdup(nconf->nc_netid); 824 1.1 fvdl rbl->rpcb_map.r_addr = taddr2uaddr(nconf, addr); 825 1.13 christos rbl->rpcb_map.r_owner = strdup(rpcbind_superuser); 826 1.27 christos if (rbl->rpcb_map.r_netid == NULL || 827 1.27 christos rbl->rpcb_map.r_addr == NULL || 828 1.27 christos rbl->rpcb_map.r_owner == NULL) 829 1.27 christos { 830 1.27 christos free(rbl->rpcb_map.r_netid); 831 1.27 christos free(rbl->rpcb_map.r_addr); 832 1.27 christos free(rbl->rpcb_map.r_owner); 833 1.27 christos free(rbl); 834 1.27 christos syslog(LOG_ERR, "%s: Cannot allocate memory", __func__); 835 1.27 christos return; 836 1.27 christos } 837 1.1 fvdl rbl->rpcb_next = list_rbl; /* Attach to global list */ 838 1.1 fvdl list_rbl = rbl; 839 1.1 fvdl } 840 1.1 fvdl 841 1.1 fvdl /* 842 1.1 fvdl * Catch the signal and die 843 1.1 fvdl */ 844 1.1 fvdl static void 845 1.24 christos terminate(int signum __unused) 846 1.1 fvdl { 847 1.25 christos #ifndef RPCBIND_RUMP 848 1.24 christos close(rpcbindlockfd); 849 1.25 christos #endif 850 1.1 fvdl #ifdef WARMSTART 851 1.1 fvdl syslog(LOG_ERR, 852 1.24 christos "rpcbind terminating on signal %d. Restart with \"rpcbind -w\"", 853 1.24 christos signum); 854 1.1 fvdl write_warmstart(); /* Dump yourself */ 855 1.1 fvdl #endif 856 1.23 christos #ifdef RPCBIND_RUMP 857 1.23 christos exit(2); 858 1.23 christos #else 859 1.22 christos exit(EXIT_FAILURE); 860 1.23 christos #endif 861 1.1 fvdl } 862 1.1 fvdl 863 1.1 fvdl void 864 1.24 christos rpcbind_abort(void) 865 1.1 fvdl { 866 1.1 fvdl #ifdef WARMSTART 867 1.1 fvdl write_warmstart(); /* Dump yourself */ 868 1.1 fvdl #endif 869 1.1 fvdl abort(); 870 1.1 fvdl } 871 1.1 fvdl 872 1.23 christos #ifndef RPCBIND_RUMP 873 1.1 fvdl /* get command line options */ 874 1.1 fvdl static void 875 1.1 fvdl parseargs(int argc, char *argv[]) 876 1.1 fvdl { 877 1.1 fvdl int c; 878 1.1 fvdl 879 1.24 christos #ifdef WARMSTART 880 1.24 christos #define WSOP "w" 881 1.24 christos #else 882 1.24 christos #define WSOP "" 883 1.24 christos #endif 884 1.24 christos #ifdef LIBWRAP 885 1.24 christos #define WRAPOP "W" 886 1.24 christos #else 887 1.24 christos #define WRAPOP "" 888 1.24 christos #endif 889 1.24 christos while ((c = getopt(argc, argv, "6adh:iLls" WRAPOP WSOP)) != -1) { 890 1.1 fvdl switch (c) { 891 1.24 christos case '6': 892 1.24 christos ipv6_only = 1; 893 1.24 christos break; 894 1.1 fvdl case 'a': 895 1.1 fvdl doabort = 1; /* when debugging, do an abort on */ 896 1.1 fvdl break; /* errors; for rpcbind developers */ 897 1.1 fvdl /* only! */ 898 1.1 fvdl case 'd': 899 1.30 christos debugging++; 900 1.1 fvdl break; 901 1.24 christos case 'h': 902 1.24 christos ++nhosts; 903 1.31 nia if (reallocarr(&hosts, nhosts, sizeof(*hosts)) != 0) 904 1.26 christos err(EXIT_FAILURE, "Can't allocate host array"); 905 1.24 christos hosts[nhosts - 1] = strdup(optarg); 906 1.24 christos if (hosts[nhosts - 1] == NULL) 907 1.26 christos err(EXIT_FAILURE, "Can't allocate host"); 908 1.24 christos break; 909 1.1 fvdl case 'i': 910 1.1 fvdl insecure = 1; 911 1.1 fvdl break; 912 1.1 fvdl case 'L': 913 1.1 fvdl oldstyle_local = 1; 914 1.1 fvdl break; 915 1.1 fvdl case 'l': 916 1.1 fvdl verboselog = 1; 917 1.1 fvdl break; 918 1.1 fvdl case 's': 919 1.1 fvdl runasdaemon = 1; 920 1.1 fvdl break; 921 1.24 christos #ifdef LIBWRAP 922 1.24 christos case 'W': 923 1.24 christos libwrap = 1; 924 1.24 christos break; 925 1.24 christos #endif 926 1.1 fvdl #ifdef WARMSTART 927 1.1 fvdl case 'w': 928 1.1 fvdl warmstart = 1; 929 1.1 fvdl break; 930 1.1 fvdl #endif 931 1.1 fvdl default: /* error */ 932 1.1 fvdl fprintf(stderr, "usage: rpcbind [-Idwils]\n"); 933 1.24 christos fprintf(stderr, 934 1.24 christos "Usage: %s [-6adiLls%s%s] [-h bindip]\n", 935 1.24 christos getprogname(), WRAPOP, WSOP); 936 1.22 christos exit(EXIT_FAILURE); 937 1.1 fvdl } 938 1.1 fvdl } 939 1.1 fvdl if (doabort && !debugging) { 940 1.1 fvdl fprintf(stderr, 941 1.1 fvdl "-a (abort) specified without -d (debugging) -- ignored.\n"); 942 1.1 fvdl doabort = 0; 943 1.1 fvdl } 944 1.24 christos #undef WRAPOP 945 1.24 christos #undef WSOP 946 1.1 fvdl } 947 1.23 christos #endif 948 1.1 fvdl 949 1.1 fvdl void 950 1.24 christos reap(int dummy __unused) 951 1.1 fvdl { 952 1.1 fvdl int save_errno = errno; 953 1.1 fvdl 954 1.1 fvdl while (wait3(NULL, WNOHANG, NULL) > 0) 955 1.1 fvdl ; 956 1.1 fvdl errno = save_errno; 957 1.1 fvdl } 958 1.1 fvdl 959 1.1 fvdl void 960 1.24 christos toggle_verboselog(int dummy __unused) 961 1.1 fvdl { 962 1.1 fvdl verboselog = !verboselog; 963 1.1 fvdl } 964