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