1 /* 2 * Copyright (c) 1995 3 * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed for the FreeBSD project 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 */ 33 34 /* main() function for status monitor daemon. Some of the code in this */ 35 /* file was generated by running rpcgen /usr/include/rpcsvc/sm_inter.x */ 36 /* The actual program logic is in the file procs.c */ 37 38 #include <sys/cdefs.h> 39 __FBSDID("$FreeBSD$"); 40 41 #include <err.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <rpc/rpc.h> 45 #include <rpc/rpc_com.h> 46 #include <string.h> 47 #include <syslog.h> 48 #include <sys/types.h> 49 #include <sys/socket.h> 50 #include <sys/wait.h> 51 #include <netinet/in.h> 52 #include <arpa/inet.h> 53 #include <netdb.h> 54 #include <signal.h> 55 #include <unistd.h> 56 #include "statd.h" 57 58 int debug = 0; /* Controls syslog() calls for debug messages */ 59 60 char **hosts, *svcport_str = NULL; 61 int nhosts = 0; 62 int xcreated = 0; 63 64 void create_service(struct netconfig *nconf); 65 static void handle_sigchld(int sig); 66 void out_of_mem(void); 67 68 static void usage(void); 69 70 int 71 main(int argc, char **argv) 72 { 73 struct sigaction sa; 74 struct netconfig *nconf; 75 void *nc_handle; 76 in_port_t svcport; 77 int ch, i, s; 78 char *endptr, **hosts_bak; 79 int have_v6 = 1; 80 int maxrec = RPC_MAXDATASIZE; 81 82 while ((ch = getopt(argc, argv, "dh:p:")) != -1) 83 switch (ch) { 84 case 'd': 85 debug = 1; 86 break; 87 case 'h': 88 ++nhosts; 89 hosts_bak = hosts; 90 hosts_bak = realloc(hosts, nhosts * sizeof(char *)); 91 if (hosts_bak == NULL) { 92 if (hosts != NULL) { 93 for (i = 0; i < nhosts; i++) 94 free(hosts[i]); 95 free(hosts); 96 out_of_mem(); 97 } 98 } 99 hosts = hosts_bak; 100 hosts[nhosts - 1] = strdup(optarg); 101 if (hosts[nhosts - 1] == NULL) { 102 for (i = 0; i < (nhosts - 1); i++) 103 free(hosts[i]); 104 free(hosts); 105 out_of_mem(); 106 } 107 break; 108 case 'p': 109 endptr = NULL; 110 svcport = (in_port_t)strtoul(optarg, &endptr, 10); 111 if (endptr == NULL || *endptr != '\0' || svcport == 0 || 112 svcport >= IPPORT_MAX) 113 usage(); 114 115 svcport_str = strdup(optarg); 116 break; 117 default: 118 usage(); 119 } 120 argc -= optind; 121 argv += optind; 122 123 (void)rpcb_unset(SM_PROG, SM_VERS, NULL); 124 125 /* 126 * Check if IPv6 support is present. 127 */ 128 s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 129 if (s < 0) 130 have_v6 = 0; 131 else 132 close(s); 133 134 rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec); 135 136 /* 137 * If no hosts were specified, add a wildcard entry to bind to 138 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the 139 * list. 140 */ 141 if (nhosts == 0) { 142 hosts = malloc(sizeof(char**)); 143 if (hosts == NULL) 144 out_of_mem(); 145 146 hosts[0] = "*"; 147 nhosts = 1; 148 } else { 149 hosts_bak = hosts; 150 if (have_v6) { 151 hosts_bak = realloc(hosts, (nhosts + 2) * 152 sizeof(char *)); 153 if (hosts_bak == NULL) { 154 for (i = 0; i < nhosts; i++) 155 free(hosts[i]); 156 free(hosts); 157 out_of_mem(); 158 } else 159 hosts = hosts_bak; 160 161 nhosts += 2; 162 hosts[nhosts - 2] = "::1"; 163 } else { 164 hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *)); 165 if (hosts_bak == NULL) { 166 for (i = 0; i < nhosts; i++) 167 free(hosts[i]); 168 169 free(hosts); 170 out_of_mem(); 171 } else { 172 nhosts += 1; 173 hosts = hosts_bak; 174 } 175 } 176 hosts[nhosts - 1] = "127.0.0.1"; 177 } 178 179 nc_handle = setnetconfig(); 180 while ((nconf = getnetconfig(nc_handle))) { 181 /* We want to listen only on udp6, tcp6, udp, tcp transports */ 182 if (nconf->nc_flag & NC_VISIBLE) { 183 /* Skip if there's no IPv6 support */ 184 if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) { 185 /* DO NOTHING */ 186 } else { 187 create_service(nconf); 188 } 189 } 190 } 191 endnetconfig(nc_handle); 192 init_file("/var/db/statd.status"); 193 194 /* Note that it is NOT sensible to run this program from inetd - the */ 195 /* protocol assumes that it will run immediately at boot time. */ 196 daemon(0, 0); 197 openlog("rpc.statd", 0, LOG_DAEMON); 198 if (debug) syslog(LOG_INFO, "Starting - debug enabled"); 199 else syslog(LOG_INFO, "Starting"); 200 201 /* Install signal handler to collect exit status of child processes */ 202 sa.sa_handler = handle_sigchld; 203 sigemptyset(&sa.sa_mask); 204 sigaddset(&sa.sa_mask, SIGCHLD); 205 sa.sa_flags = SA_RESTART; 206 sigaction(SIGCHLD, &sa, NULL); 207 208 /* Initialisation now complete - start operating */ 209 notify_hosts(); /* Forks a process (if necessary) to do the */ 210 /* SM_NOTIFY calls, which may be slow. */ 211 212 svc_run(); /* Should never return */ 213 exit(1); 214 } 215 216 /* 217 * This routine creates and binds sockets on the appropriate 218 * addresses. It gets called one time for each transport and 219 * registrates the service with rpcbind on that trasport. 220 */ 221 void 222 create_service(struct netconfig *nconf) 223 { 224 struct addrinfo hints, *res = NULL; 225 struct sockaddr_in *sin; 226 struct sockaddr_in6 *sin6; 227 struct __rpc_sockinfo si; 228 struct netbuf servaddr; 229 SVCXPRT *transp = NULL; 230 int aicode; 231 int fd; 232 int nhostsbak; 233 int r; 234 int registered = 0; 235 u_int32_t host_addr[4]; /* IPv4 or IPv6 */ 236 237 if ((nconf->nc_semantics != NC_TPI_CLTS) && 238 (nconf->nc_semantics != NC_TPI_COTS) && 239 (nconf->nc_semantics != NC_TPI_COTS_ORD)) 240 return; /* not my type */ 241 242 /* 243 * XXX - using RPC library internal functions. 244 */ 245 if (!__rpc_nconf2sockinfo(nconf, &si)) { 246 syslog(LOG_ERR, "cannot get information for %s", 247 nconf->nc_netid); 248 return; 249 } 250 251 /* Get rpc.statd's address on this transport */ 252 memset(&hints, 0, sizeof hints); 253 hints.ai_flags = AI_PASSIVE; 254 hints.ai_family = si.si_af; 255 hints.ai_socktype = si.si_socktype; 256 hints.ai_protocol = si.si_proto; 257 258 /* 259 * Bind to specific IPs if asked to 260 */ 261 nhostsbak = nhosts; 262 while (nhostsbak > 0) { 263 --nhostsbak; 264 265 /* 266 * XXX - using RPC library internal functions. 267 */ 268 if ((fd = __rpc_nconf2fd(nconf)) < 0) { 269 syslog(LOG_ERR, "cannot create socket for %s", 270 nconf->nc_netid); 271 continue; 272 } 273 switch (hints.ai_family) { 274 case AF_INET: 275 if (inet_pton(AF_INET, hosts[nhostsbak], 276 host_addr) == 1) { 277 hints.ai_flags &= AI_NUMERICHOST; 278 } else { 279 /* 280 * Skip if we have an AF_INET6 address. 281 */ 282 if (inet_pton(AF_INET6, hosts[nhostsbak], 283 host_addr) == 1) { 284 close(fd); 285 continue; 286 } 287 } 288 break; 289 case AF_INET6: 290 if (inet_pton(AF_INET6, hosts[nhostsbak], 291 host_addr) == 1) { 292 hints.ai_flags &= AI_NUMERICHOST; 293 } else { 294 /* 295 * Skip if we have an AF_INET address. 296 */ 297 if (inet_pton(AF_INET, hosts[nhostsbak], 298 host_addr) == 1) { 299 close(fd); 300 continue; 301 } 302 } 303 break; 304 default: 305 break; 306 } 307 308 /* 309 * If no hosts were specified, just bind to INADDR_ANY 310 */ 311 if (strcmp("*", hosts[nhostsbak]) == 0) { 312 if (svcport_str == NULL) { 313 res = malloc(sizeof(struct addrinfo)); 314 if (res == NULL) 315 out_of_mem(); 316 res->ai_flags = hints.ai_flags; 317 res->ai_family = hints.ai_family; 318 res->ai_protocol = hints.ai_protocol; 319 switch (res->ai_family) { 320 case AF_INET: 321 sin = malloc(sizeof(struct sockaddr_in)); 322 if (sin == NULL) 323 out_of_mem(); 324 sin->sin_family = AF_INET; 325 sin->sin_port = htons(0); 326 sin->sin_addr.s_addr = htonl(INADDR_ANY); 327 res->ai_addr = (struct sockaddr*) sin; 328 res->ai_addrlen = (socklen_t) 329 sizeof(res->ai_addr); 330 break; 331 case AF_INET6: 332 sin6 = malloc(sizeof(struct sockaddr_in6)); 333 if (sin6 == NULL) 334 out_of_mem(); 335 sin6->sin6_family = AF_INET6; 336 sin6->sin6_port = htons(0); 337 sin6->sin6_addr = in6addr_any; 338 res->ai_addr = (struct sockaddr*) sin6; 339 res->ai_addrlen = (socklen_t) sizeof(res->ai_addr); 340 break; 341 default: 342 break; 343 } 344 } else { 345 if ((aicode = getaddrinfo(NULL, svcport_str, 346 &hints, &res)) != 0) { 347 syslog(LOG_ERR, 348 "cannot get local address for %s: %s", 349 nconf->nc_netid, 350 gai_strerror(aicode)); 351 continue; 352 } 353 } 354 } else { 355 if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str, 356 &hints, &res)) != 0) { 357 syslog(LOG_ERR, 358 "cannot get local address for %s: %s", 359 nconf->nc_netid, gai_strerror(aicode)); 360 continue; 361 } 362 } 363 364 r = bindresvport_sa(fd, res->ai_addr); 365 if (r != 0) { 366 syslog(LOG_ERR, "bindresvport_sa: %m"); 367 exit(1); 368 } 369 370 if (nconf->nc_semantics != NC_TPI_CLTS) 371 listen(fd, SOMAXCONN); 372 373 transp = svc_tli_create(fd, nconf, NULL, 374 RPC_MAXDATASIZE, RPC_MAXDATASIZE); 375 376 if (transp != (SVCXPRT *) NULL) { 377 if (!svc_register(transp, SM_PROG, SM_VERS, 378 sm_prog_1, 0)) { 379 syslog(LOG_ERR, "can't register on %s", 380 nconf->nc_netid); 381 } else { 382 if (!svc_reg(transp, SM_PROG, SM_VERS, 383 sm_prog_1, NULL)) 384 syslog(LOG_ERR, 385 "can't register %s SM_PROG service", 386 nconf->nc_netid); 387 } 388 } else 389 syslog(LOG_WARNING, "can't create %s services", 390 nconf->nc_netid); 391 392 if (registered == 0) { 393 registered = 1; 394 memset(&hints, 0, sizeof hints); 395 hints.ai_flags = AI_PASSIVE; 396 hints.ai_family = si.si_af; 397 hints.ai_socktype = si.si_socktype; 398 hints.ai_protocol = si.si_proto; 399 400 if (svcport_str == NULL) { 401 svcport_str = malloc(NI_MAXSERV * sizeof(char)); 402 if (svcport_str == NULL) 403 out_of_mem(); 404 405 if (getnameinfo(res->ai_addr, 406 res->ai_addr->sa_len, NULL, NI_MAXHOST, 407 svcport_str, NI_MAXSERV * sizeof(char), 408 NI_NUMERICHOST | NI_NUMERICSERV)) 409 errx(1, "Cannot get port number"); 410 } 411 412 if((aicode = getaddrinfo(NULL, svcport_str, &hints, 413 &res)) != 0) { 414 syslog(LOG_ERR, "cannot get local address: %s", 415 gai_strerror(aicode)); 416 exit(1); 417 } 418 419 servaddr.buf = malloc(res->ai_addrlen); 420 memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen); 421 servaddr.len = res->ai_addrlen; 422 423 rpcb_set(SM_PROG, SM_VERS, nconf, &servaddr); 424 425 xcreated++; 426 freeaddrinfo(res); 427 } 428 } /* end while */ 429 } 430 431 static void 432 usage() 433 { 434 fprintf(stderr, "usage: rpc.statd [-d] [-h <bindip>] [-p <port>]\n"); 435 exit(1); 436 } 437 438 /* handle_sigchld ---------------------------------------------------------- */ 439 /* 440 Purpose: Catch SIGCHLD and collect process status 441 Retruns: Nothing. 442 Notes: No special action required, other than to collect the 443 process status and hence allow the child to die: 444 we only use child processes for asynchronous transmission 445 of SM_NOTIFY to other systems, so it is normal for the 446 children to exit when they have done their work. 447 */ 448 449 static void handle_sigchld(int sig __unused) 450 { 451 int pid, status; 452 pid = wait4(-1, &status, WNOHANG, (struct rusage*)0); 453 if (!pid) syslog(LOG_ERR, "Phantom SIGCHLD??"); 454 else if (status == 0) 455 { 456 if (debug) syslog(LOG_DEBUG, "Child %d exited OK", pid); 457 } 458 else syslog(LOG_ERR, "Child %d failed with status %d", pid, 459 WEXITSTATUS(status)); 460 } 461 462 /* 463 * Out of memory, fatal 464 */ 465 void 466 out_of_mem() 467 { 468 469 syslog(LOG_ERR, "out of memory"); 470 exit(2); 471 } 472