1 /* $NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $ */ 2 /* $FreeBSD$ */ 3 4 /* 5 * Copyright (c) 1995 6 * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed for the FreeBSD project 19 * 4. Neither the name of the author nor the names of any co-contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 */ 36 37 #include <sys/cdefs.h> 38 #ifndef lint 39 __RCSID("$NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $"); 40 #endif 41 42 /* 43 * main() function for NFS lock daemon. Most of the code in this 44 * file was generated by running rpcgen /usr/include/rpcsvc/nlm_prot.x. 45 * 46 * The actual program logic is in the file lock_proc.c 47 */ 48 49 #include <sys/types.h> 50 #include <sys/socket.h> 51 52 #include <netinet/in.h> 53 #include <arpa/inet.h> 54 55 #include <err.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <errno.h> 59 #include <syslog.h> 60 #include <signal.h> 61 #include <string.h> 62 #include <unistd.h> 63 #include <libutil.h> 64 #include <netconfig.h> 65 #include <netdb.h> 66 67 #include <rpc/rpc.h> 68 #include <rpc/rpc_com.h> 69 #include <rpcsvc/sm_inter.h> 70 71 #include "lockd.h" 72 #include <rpcsvc/nlm_prot.h> 73 74 int debug_level = 0; /* 0 = no debugging syslog() calls */ 75 int _rpcsvcdirty = 0; 76 77 int grace_expired; 78 int nsm_state; 79 pid_t client_pid; 80 struct mon mon_host; 81 char **hosts, *svcport_str = NULL; 82 int nhosts = 0; 83 int xcreated = 0; 84 85 void create_service(struct netconfig *nconf); 86 void init_nsm(void); 87 void nlm_prog_0(struct svc_req *, SVCXPRT *); 88 void nlm_prog_1(struct svc_req *, SVCXPRT *); 89 void nlm_prog_3(struct svc_req *, SVCXPRT *); 90 void nlm_prog_4(struct svc_req *, SVCXPRT *); 91 void out_of_mem(void); 92 void usage(void); 93 94 void sigalarm_handler(void); 95 96 int 97 main(int argc, char **argv) 98 { 99 int ch, i, s; 100 void *nc_handle; 101 char *endptr, **hosts_bak; 102 struct sigaction sigalarm; 103 int grace_period = 30; 104 struct netconfig *nconf; 105 int have_v6 = 1; 106 int maxrec = RPC_MAXDATASIZE; 107 in_port_t svcport = 0; 108 109 while ((ch = getopt(argc, argv, "d:g:h:p:")) != (-1)) { 110 switch (ch) { 111 case 'd': 112 debug_level = atoi(optarg); 113 if (!debug_level) { 114 usage(); 115 /* NOTREACHED */ 116 } 117 break; 118 case 'g': 119 grace_period = atoi(optarg); 120 if (!grace_period) { 121 usage(); 122 /* NOTREACHED */ 123 } 124 break; 125 case 'h': 126 ++nhosts; 127 hosts_bak = hosts; 128 hosts_bak = realloc(hosts, nhosts * sizeof(char *)); 129 if (hosts_bak == NULL) { 130 if (hosts != NULL) { 131 for (i = 0; i < nhosts; i++) 132 free(hosts[i]); 133 free(hosts); 134 out_of_mem(); 135 } 136 } 137 hosts = hosts_bak; 138 hosts[nhosts - 1] = strdup(optarg); 139 if (hosts[nhosts - 1] == NULL) { 140 for (i = 0; i < (nhosts - 1); i++) 141 free(hosts[i]); 142 free(hosts); 143 out_of_mem(); 144 } 145 break; 146 case 'p': 147 endptr = NULL; 148 svcport = (in_port_t)strtoul(optarg, &endptr, 10); 149 if (endptr == NULL || *endptr != '\0' || 150 svcport == 0 || svcport >= IPPORT_MAX) 151 usage(); 152 svcport_str = strdup(optarg); 153 break; 154 default: 155 case '?': 156 usage(); 157 /* NOTREACHED */ 158 } 159 } 160 if (geteuid()) { /* This command allowed only to root */ 161 fprintf(stderr, "Sorry. You are not superuser\n"); 162 exit(1); 163 } 164 165 (void)rpcb_unset(NLM_PROG, NLM_SM, NULL); 166 (void)rpcb_unset(NLM_PROG, NLM_VERS, NULL); 167 (void)rpcb_unset(NLM_PROG, NLM_VERSX, NULL); 168 (void)rpcb_unset(NLM_PROG, NLM_VERS4, NULL); 169 170 /* 171 * Check if IPv6 support is present. 172 */ 173 s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 174 if (s < 0) 175 have_v6 = 0; 176 else 177 close(s); 178 179 rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec); 180 181 /* 182 * If no hosts were specified, add a wildcard entry to bind to 183 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the 184 * list. 185 */ 186 if (nhosts == 0) { 187 hosts = malloc(sizeof(char**)); 188 if (hosts == NULL) 189 out_of_mem(); 190 191 hosts[0] = "*"; 192 nhosts = 1; 193 } else { 194 hosts_bak = hosts; 195 if (have_v6) { 196 hosts_bak = realloc(hosts, (nhosts + 2) * 197 sizeof(char *)); 198 if (hosts_bak == NULL) { 199 for (i = 0; i < nhosts; i++) 200 free(hosts[i]); 201 free(hosts); 202 out_of_mem(); 203 } else 204 hosts = hosts_bak; 205 206 nhosts += 2; 207 hosts[nhosts - 2] = "::1"; 208 } else { 209 hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *)); 210 if (hosts_bak == NULL) { 211 for (i = 0; i < nhosts; i++) 212 free(hosts[i]); 213 214 free(hosts); 215 out_of_mem(); 216 } else { 217 nhosts += 1; 218 hosts = hosts_bak; 219 } 220 } 221 hosts[nhosts - 1] = "127.0.0.1"; 222 } 223 224 nc_handle = setnetconfig(); 225 while ((nconf = getnetconfig(nc_handle))) { 226 /* We want to listen only on udp6, tcp6, udp, tcp transports */ 227 if (nconf->nc_flag & NC_VISIBLE) { 228 /* Skip if there's no IPv6 support */ 229 if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) { 230 /* DO NOTHING */ 231 } else { 232 create_service(nconf); 233 } 234 } 235 } 236 endnetconfig(nc_handle); 237 238 /* 239 * Note that it is NOT sensible to run this program from inetd - the 240 * protocol assumes that it will run immediately at boot time. 241 */ 242 if (daemon(0, debug_level > 0)) { 243 err(1, "cannot fork"); 244 /* NOTREACHED */ 245 } 246 247 openlog("rpc.lockd", 0, LOG_DAEMON); 248 if (debug_level) 249 syslog(LOG_INFO, "Starting, debug level %d", debug_level); 250 else 251 syslog(LOG_INFO, "Starting"); 252 253 sigalarm.sa_handler = (sig_t) sigalarm_handler; 254 sigemptyset(&sigalarm.sa_mask); 255 sigalarm.sa_flags = SA_RESETHAND; /* should only happen once */ 256 sigalarm.sa_flags |= SA_RESTART; 257 if (sigaction(SIGALRM, &sigalarm, NULL) != 0) { 258 syslog(LOG_WARNING, "sigaction(SIGALRM) failed: %s", 259 strerror(errno)); 260 exit(1); 261 } 262 grace_expired = 0; 263 alarm(grace_period); 264 265 init_nsm(); 266 267 client_pid = client_request(); 268 269 svc_run(); /* Should never return */ 270 exit(1); 271 } 272 273 /* 274 * This routine creates and binds sockets on the appropriate 275 * addresses. It gets called one time for each transport and 276 * registrates the service with rpcbind on that trasport. 277 */ 278 void 279 create_service(struct netconfig *nconf) 280 { 281 struct addrinfo hints, *res = NULL; 282 struct sockaddr_in *sin; 283 struct sockaddr_in6 *sin6; 284 struct __rpc_sockinfo si; 285 struct netbuf servaddr; 286 SVCXPRT *transp = NULL; 287 int aicode; 288 int fd; 289 int nhostsbak; 290 int r; 291 int registered = 0; 292 u_int32_t host_addr[4]; /* IPv4 or IPv6 */ 293 294 if ((nconf->nc_semantics != NC_TPI_CLTS) && 295 (nconf->nc_semantics != NC_TPI_COTS) && 296 (nconf->nc_semantics != NC_TPI_COTS_ORD)) 297 return; /* not my type */ 298 299 /* 300 * XXX - using RPC library internal functions. 301 */ 302 if (!__rpc_nconf2sockinfo(nconf, &si)) { 303 syslog(LOG_ERR, "cannot get information for %s", 304 nconf->nc_netid); 305 return; 306 } 307 308 /* Get rpc.statd's address on this transport */ 309 memset(&hints, 0, sizeof hints); 310 hints.ai_flags = AI_PASSIVE; 311 hints.ai_family = si.si_af; 312 hints.ai_socktype = si.si_socktype; 313 hints.ai_protocol = si.si_proto; 314 315 /* 316 * Bind to specific IPs if asked to 317 */ 318 nhostsbak = nhosts; 319 while (nhostsbak > 0) { 320 --nhostsbak; 321 322 /* 323 * XXX - using RPC library internal functions. 324 */ 325 if ((fd = __rpc_nconf2fd(nconf)) < 0) { 326 syslog(LOG_ERR, "cannot create socket for %s", 327 nconf->nc_netid); 328 continue; 329 } 330 331 switch (hints.ai_family) { 332 case AF_INET: 333 if (inet_pton(AF_INET, hosts[nhostsbak], 334 host_addr) == 1) { 335 hints.ai_flags &= AI_NUMERICHOST; 336 } else { 337 /* 338 * Skip if we have an AF_INET6 address. 339 */ 340 if (inet_pton(AF_INET6, hosts[nhostsbak], 341 host_addr) == 1) { 342 close(fd); 343 continue; 344 } 345 } 346 break; 347 case AF_INET6: 348 if (inet_pton(AF_INET6, hosts[nhostsbak], 349 host_addr) == 1) { 350 hints.ai_flags &= AI_NUMERICHOST; 351 } else { 352 /* 353 * Skip if we have an AF_INET address. 354 */ 355 if (inet_pton(AF_INET, hosts[nhostsbak], 356 host_addr) == 1) { 357 close(fd); 358 continue; 359 } 360 } 361 break; 362 default: 363 break; 364 } 365 366 /* 367 * If no hosts were specified, just bind to INADDR_ANY 368 */ 369 if (strcmp("*", hosts[nhostsbak]) == 0) { 370 if (svcport_str == NULL) { 371 res = malloc(sizeof(struct addrinfo)); 372 if (res == NULL) 373 out_of_mem(); 374 res->ai_flags = hints.ai_flags; 375 res->ai_family = hints.ai_family; 376 res->ai_protocol = hints.ai_protocol; 377 switch (res->ai_family) { 378 case AF_INET: 379 sin = malloc(sizeof(struct sockaddr_in)); 380 if (sin == NULL) 381 out_of_mem(); 382 sin->sin_family = AF_INET; 383 sin->sin_port = htons(0); 384 sin->sin_addr.s_addr = htonl(INADDR_ANY); 385 res->ai_addr = (struct sockaddr*) sin; 386 res->ai_addrlen = (socklen_t) 387 sizeof(res->ai_addr); 388 break; 389 case AF_INET6: 390 sin6 = malloc(sizeof(struct sockaddr_in6)); 391 if (sin6 == NULL) 392 out_of_mem(); 393 sin6->sin6_family = AF_INET6; 394 sin6->sin6_port = htons(0); 395 sin6->sin6_addr = in6addr_any; 396 res->ai_addr = (struct sockaddr*) sin6; 397 res->ai_addrlen = (socklen_t) sizeof(res->ai_addr); 398 break; 399 default: 400 break; 401 } 402 } else { 403 if ((aicode = getaddrinfo(NULL, svcport_str, 404 &hints, &res)) != 0) { 405 syslog(LOG_ERR, 406 "cannot get local address for %s: %s", 407 nconf->nc_netid, 408 gai_strerror(aicode)); 409 continue; 410 } 411 } 412 } else { 413 if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str, 414 &hints, &res)) != 0) { 415 syslog(LOG_ERR, 416 "cannot get local address for %s: %s", 417 nconf->nc_netid, gai_strerror(aicode)); 418 continue; 419 } 420 } 421 422 r = bindresvport_sa(fd, res->ai_addr); 423 if (r != 0) { 424 syslog(LOG_ERR, "bindresvport_sa: %m"); 425 exit(1); 426 } 427 428 transp = svc_tli_create(fd, nconf, NULL, 429 RPC_MAXDATASIZE, RPC_MAXDATASIZE); 430 431 if (transp != (SVCXPRT *) NULL) { 432 if (!svc_reg(transp, NLM_PROG, NLM_SM, nlm_prog_0, 433 NULL)) 434 syslog(LOG_ERR, 435 "can't register %s NLM_PROG, NLM_SM service", 436 nconf->nc_netid); 437 438 if (!svc_reg(transp, NLM_PROG, NLM_VERS, nlm_prog_1, 439 NULL)) 440 syslog(LOG_ERR, 441 "can't register %s NLM_PROG, NLM_VERS service", 442 nconf->nc_netid); 443 444 if (!svc_reg(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, 445 NULL)) 446 syslog(LOG_ERR, 447 "can't register %s NLM_PROG, NLM_VERSX service", 448 nconf->nc_netid); 449 450 if (!svc_reg(transp, NLM_PROG, NLM_VERS4, nlm_prog_4, 451 NULL)) 452 syslog(LOG_ERR, 453 "can't register %s NLM_PROG, NLM_VERS4 service", 454 nconf->nc_netid); 455 456 } else 457 syslog(LOG_WARNING, "can't create %s services", 458 nconf->nc_netid); 459 460 if (registered == 0) { 461 registered = 1; 462 memset(&hints, 0, sizeof hints); 463 hints.ai_flags = AI_PASSIVE; 464 hints.ai_family = si.si_af; 465 hints.ai_socktype = si.si_socktype; 466 hints.ai_protocol = si.si_proto; 467 468 if (svcport_str == NULL) { 469 svcport_str = malloc(NI_MAXSERV * sizeof(char)); 470 if (svcport_str == NULL) 471 out_of_mem(); 472 473 if (getnameinfo(res->ai_addr, 474 res->ai_addr->sa_len, NULL, NI_MAXHOST, 475 svcport_str, NI_MAXSERV * sizeof(char), 476 NI_NUMERICHOST | NI_NUMERICSERV)) 477 errx(1, "Cannot get port number"); 478 } 479 480 if((aicode = getaddrinfo(NULL, svcport_str, &hints, 481 &res)) != 0) { 482 syslog(LOG_ERR, "cannot get local address: %s", 483 gai_strerror(aicode)); 484 exit(1); 485 } 486 487 servaddr.buf = malloc(res->ai_addrlen); 488 memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen); 489 servaddr.len = res->ai_addrlen; 490 491 rpcb_set(NLM_PROG, NLM_SM, nconf, &servaddr); 492 rpcb_set(NLM_PROG, NLM_VERS, nconf, &servaddr); 493 rpcb_set(NLM_PROG, NLM_VERSX, nconf, &servaddr); 494 rpcb_set(NLM_PROG, NLM_VERS4, nconf, &servaddr); 495 496 xcreated++; 497 freeaddrinfo(res); 498 } 499 } /* end while */ 500 } 501 502 void 503 sigalarm_handler(void) 504 { 505 506 grace_expired = 1; 507 } 508 509 void 510 usage() 511 { 512 errx(1, "usage: rpc.lockd [-d <debuglevel>]" 513 " [-g <grace period>] [-h <bindip>] [-p <port>]"); 514 } 515 516 /* 517 * init_nsm -- 518 * Reset the NSM state-of-the-world and acquire its state. 519 */ 520 void 521 init_nsm(void) 522 { 523 enum clnt_stat ret; 524 my_id id; 525 sm_stat stat; 526 char name[] = "NFS NLM"; 527 char localhost[] = "localhost"; 528 529 /* 530 * !!! 531 * The my_id structure isn't used by the SM_UNMON_ALL call, as far 532 * as I know. Leave it empty for now. 533 */ 534 memset(&id, 0, sizeof(id)); 535 id.my_name = name; 536 537 /* 538 * !!! 539 * The statd program must already be registered when lockd runs. 540 */ 541 do { 542 ret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON_ALL, 543 (xdrproc_t)xdr_my_id, &id, (xdrproc_t)xdr_sm_stat, &stat); 544 if (ret == RPC_PROGUNAVAIL) { 545 syslog(LOG_WARNING, "%lu %s", SM_PROG, 546 clnt_sperrno(ret)); 547 sleep(2); 548 continue; 549 } 550 break; 551 } while (0); 552 553 if (ret != 0) { 554 syslog(LOG_ERR, "%lu %s", SM_PROG, clnt_sperrno(ret)); 555 exit(1); 556 } 557 558 nsm_state = stat.state; 559 560 /* setup constant data for SM_MON calls */ 561 mon_host.mon_id.my_id.my_name = localhost; 562 mon_host.mon_id.my_id.my_prog = NLM_PROG; 563 mon_host.mon_id.my_id.my_vers = NLM_SM; 564 mon_host.mon_id.my_id.my_proc = NLM_SM_NOTIFY; /* bsdi addition */ 565 } 566 567 /* 568 * Out of memory, fatal 569 */ 570 void out_of_mem() 571 { 572 syslog(LOG_ERR, "out of memory"); 573 exit(2); 574 } 575