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 transp = svc_tli_create(fd, nconf, NULL, 371 RPC_MAXDATASIZE, RPC_MAXDATASIZE); 372 373 if (transp != (SVCXPRT *) NULL) { 374 if (!svc_register(transp, SM_PROG, SM_VERS, 375 sm_prog_1, 0)) { 376 syslog(LOG_ERR, "can't register on %s", 377 nconf->nc_netid); 378 } else { 379 if (!svc_reg(transp, SM_PROG, SM_VERS, 380 sm_prog_1, NULL)) 381 syslog(LOG_ERR, 382 "can't register %s SM_PROG service", 383 nconf->nc_netid); 384 } 385 } else 386 syslog(LOG_WARNING, "can't create %s services", 387 nconf->nc_netid); 388 389 if (registered == 0) { 390 registered = 1; 391 memset(&hints, 0, sizeof hints); 392 hints.ai_flags = AI_PASSIVE; 393 hints.ai_family = si.si_af; 394 hints.ai_socktype = si.si_socktype; 395 hints.ai_protocol = si.si_proto; 396 397 if (svcport_str == NULL) { 398 svcport_str = malloc(NI_MAXSERV * sizeof(char)); 399 if (svcport_str == NULL) 400 out_of_mem(); 401 402 if (getnameinfo(res->ai_addr, 403 res->ai_addr->sa_len, NULL, NI_MAXHOST, 404 svcport_str, NI_MAXSERV * sizeof(char), 405 NI_NUMERICHOST | NI_NUMERICSERV)) 406 errx(1, "Cannot get port number"); 407 } 408 409 if((aicode = getaddrinfo(NULL, svcport_str, &hints, 410 &res)) != 0) { 411 syslog(LOG_ERR, "cannot get local address: %s", 412 gai_strerror(aicode)); 413 exit(1); 414 } 415 416 servaddr.buf = malloc(res->ai_addrlen); 417 memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen); 418 servaddr.len = res->ai_addrlen; 419 420 rpcb_set(SM_PROG, SM_VERS, nconf, &servaddr); 421 422 xcreated++; 423 freeaddrinfo(res); 424 } 425 } /* end while */ 426 } 427 428 static void 429 usage() 430 { 431 fprintf(stderr, "usage: rpc.statd [-d] [-h <bindip>] [-p <port>]\n"); 432 exit(1); 433 } 434 435 /* handle_sigchld ---------------------------------------------------------- */ 436 /* 437 Purpose: Catch SIGCHLD and collect process status 438 Retruns: Nothing. 439 Notes: No special action required, other than to collect the 440 process status and hence allow the child to die: 441 we only use child processes for asynchronous transmission 442 of SM_NOTIFY to other systems, so it is normal for the 443 children to exit when they have done their work. 444 */ 445 446 static void handle_sigchld(int sig __unused) 447 { 448 int pid, status; 449 pid = wait4(-1, &status, WNOHANG, (struct rusage*)0); 450 if (!pid) syslog(LOG_ERR, "Phantom SIGCHLD??"); 451 else if (status == 0) 452 { 453 if (debug) syslog(LOG_DEBUG, "Child %d exited OK", pid); 454 } 455 else syslog(LOG_ERR, "Child %d failed with status %d", pid, 456 WEXITSTATUS(status)); 457 } 458 459 /* 460 * Out of memory, fatal 461 */ 462 void 463 out_of_mem() 464 { 465 466 syslog(LOG_ERR, "out of memory"); 467 exit(2); 468 } 469