nfscbd.c revision 298089
1153203Sobrien/*- 2144073Sobrien * Copyright (c) 2009 Rick Macklem, University of Guelph 3143442Sobrien * All rights reserved. 4143442Sobrien * 5143442Sobrien * Redistribution and use in source and binary forms, with or without 6143442Sobrien * modification, are permitted provided that the following conditions 7143442Sobrien * are met: 8143442Sobrien * 1. Redistributions of source code must retain the above copyright 9143442Sobrien * notice, this list of conditions and the following disclaimer. 10143442Sobrien * 2. Redistributions in binary form must reproduce the above copyright 11143442Sobrien * notice, this list of conditions and the following disclaimer in the 12143442Sobrien * documentation and/or other materials provided with the distribution. 13143442Sobrien * 14143442Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15143442Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16143442Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17143442Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18143442Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19143442Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20143442Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21143442Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22143442Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23143442Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24143442Sobrien * SUCH DAMAGE. 25143442Sobrien * 26143442Sobrien */ 27143442Sobrien 28143442Sobrien#include <sys/cdefs.h> 29143442Sobrien__FBSDID("$FreeBSD: head/usr.sbin/nfscbd/nfscbd.c 298089 2016-04-15 22:31:22Z pfg $"); 30143442Sobrien 31143442Sobrien#include <sys/param.h> 32143442Sobrien#include <sys/ioctl.h> 33143442Sobrien#include <sys/linker.h> 34143442Sobrien#include <sys/module.h> 35143442Sobrien#include <sys/mount.h> 36143442Sobrien#include <sys/socket.h> 37143442Sobrien#include <sys/socketvar.h> 38143442Sobrien#include <sys/stat.h> 39143442Sobrien#include <sys/ucred.h> 40143442Sobrien#include <sys/uio.h> 41143442Sobrien#include <sys/vnode.h> 42143442Sobrien#include <sys/wait.h> 43143442Sobrien 44143442Sobrien#include <nfs/nfssvc.h> 45143442Sobrien 46143442Sobrien#include <rpc/rpc.h> 47143442Sobrien 48143442Sobrien#include <fs/nfs/rpcv2.h> 49143442Sobrien#include <fs/nfs/nfsproto.h> 50143442Sobrien#include <fs/nfs/nfskpiport.h> 51143442Sobrien#include <fs/nfs/nfs.h> 52143442Sobrien 53143442Sobrien#include <err.h> 54143442Sobrien#include <errno.h> 55143442Sobrien#include <fcntl.h> 56143442Sobrien#include <grp.h> 57143442Sobrien#include <netdb.h> 58145998Sbrueffer#include <pwd.h> 59143442Sobrien#include <signal.h> 60143442Sobrien#include <stdio.h> 61143442Sobrien#include <stdlib.h> 62143442Sobrien#include <string.h> 63143442Sobrien#include <syslog.h> 64143442Sobrien#include <unistd.h> 65143442Sobrien 66143442Sobrien/* Global defs */ 67143442Sobrien#ifdef DEBUG 68143442Sobrien#define syslog(e, s) fprintf(stderr,(s)) 69143442Sobrienstatic int debug = 1; 70143442Sobrien#else 71143442Sobrienstatic int debug = 0; 72143442Sobrien#endif 73143442Sobrien 74143442Sobrienstatic pid_t children; 75143442Sobrien 76143442Sobrienstatic void nonfs(int); 77143442Sobrienstatic void reapchild(int); 78143442Sobrienstatic void usage(void); 79143442Sobrienstatic void cleanup(int); 80143442Sobrienstatic void child_cleanup(int); 81143442Sobrienstatic void nfscbd_exit(int); 82143442Sobrienstatic void killchildren(void); 83143442Sobrien 84143442Sobrien/* 85143442Sobrien * Nfs callback server daemon. 86143442Sobrien * 87143442Sobrien * 1 - do file descriptor and signal cleanup 88143442Sobrien * 2 - fork the nfscbd(s) 89143442Sobrien * 4 - create callback server socket(s) 90143442Sobrien * 5 - set up server socket for rpc 91143442Sobrien * 92143442Sobrien * For connectionless protocols, just pass the socket into the kernel via. 93147256Sbrooks * nfssvc(). 94143442Sobrien * For connection based sockets, loop doing accepts. When you get a new 95143442Sobrien * socket from accept, pass the msgsock into the kernel via. nfssvc(). 96143442Sobrien */ 97143442Sobrienint 98143442Sobrienmain(int argc, char *argv[]) 99143442Sobrien{ 100143442Sobrien struct nfscbd_args nfscbdargs; 101143442Sobrien struct nfsd_nfscbd_args nfscbdargs2; 102143442Sobrien struct sockaddr_in inetaddr, inetpeer; 103143442Sobrien fd_set ready, sockbits; 104143442Sobrien int ch, connect_type_cnt, len, maxsock, msgsock, error; 105143442Sobrien int nfssvc_flag, on, sock, tcpsock, ret, mustfreeai = 0; 106143442Sobrien char *cp, princname[128]; 107143442Sobrien char myname[MAXHOSTNAMELEN], *myfqdnname = NULL; 108143442Sobrien struct addrinfo *aip, hints; 109143442Sobrien pid_t pid; 110143442Sobrien short myport = NFSV4_CBPORT; 111143442Sobrien 112153203Sobrien if (modfind("nfscl") < 0) { 113143442Sobrien /* Not present in kernel, try loading it */ 114143442Sobrien if (kldload("nfscl") < 0 || 115143442Sobrien modfind("nfscl") < 0) 116143442Sobrien errx(1, "nfscl is not available"); 117143442Sobrien } 118143442Sobrien /* 119143442Sobrien * First, get our fully qualified host name, if possible. 120143442Sobrien */ 121143442Sobrien if (gethostname(myname, MAXHOSTNAMELEN) >= 0) { 122143442Sobrien cp = strchr(myname, '.'); 123143442Sobrien if (cp != NULL && *(cp + 1) != '\0') { 124143442Sobrien cp = myname; 125143442Sobrien } else { 126143442Sobrien /* 127143442Sobrien * No domain on myname, so try looking it up. 128143442Sobrien */ 129143442Sobrien cp = NULL; 130143442Sobrien memset((void *)&hints, 0, sizeof (hints)); 131152669Sjhb hints.ai_flags = AI_CANONNAME; 132143442Sobrien error = getaddrinfo(myname, NULL, &hints, &aip); 133173839Syongari if (error == 0) { 134143442Sobrien if (aip->ai_canonname != NULL && 135143442Sobrien (cp = strchr(aip->ai_canonname, '.')) != NULL 136143442Sobrien && *(cp + 1) != '\0') { 137143442Sobrien cp = aip->ai_canonname; 138152669Sjhb mustfreeai = 1; 139143442Sobrien } else { 140143442Sobrien freeaddrinfo(aip); 141143442Sobrien } 142143442Sobrien } 143199560Sjhb } 144143442Sobrien if (cp == NULL) 145143442Sobrien warnx("Can't get fully qualified host name"); 146143442Sobrien myfqdnname = cp; 147152669Sjhb } 148143442Sobrien 149143442Sobrien princname[0] = '\0'; 150188175Simp#define GETOPT "p:P:" 151143442Sobrien#define USAGE "[ -p port_num ] [ -P client_principal ]" 152143442Sobrien while ((ch = getopt(argc, argv, GETOPT)) != -1) 153143442Sobrien switch (ch) { 154143442Sobrien case 'p': 155143442Sobrien myport = atoi(optarg); 156143442Sobrien if (myport < 1) { 157143442Sobrien warnx("port# non-positive, reset to %d", 158143442Sobrien NFSV4_CBPORT); 159143442Sobrien myport = NFSV4_CBPORT; 160143442Sobrien } 161143442Sobrien break; 162143442Sobrien case 'P': 163143442Sobrien cp = optarg; 164143442Sobrien if (cp != NULL && strlen(cp) > 0 && 165143442Sobrien strlen(cp) < sizeof (princname)) { 166143442Sobrien if (strchr(cp, '@') == NULL && 167143442Sobrien myfqdnname != NULL) 168143442Sobrien snprintf(princname, sizeof (princname), 169143442Sobrien "%s@%s", cp, myfqdnname); 170143442Sobrien else 171143442Sobrien strlcpy(princname, cp, 172143442Sobrien sizeof (princname)); 173143442Sobrien } else { 174143442Sobrien warnx("client princ invalid. ignored\n"); 175143442Sobrien } 176143442Sobrien break; 177143442Sobrien default: 178143442Sobrien case '?': 179143442Sobrien usage(); 180143442Sobrien } 181143442Sobrien argv += optind; 182143442Sobrien argc -= optind; 183143442Sobrien 184143442Sobrien if (argc > 0) 185143442Sobrien usage(); 186143442Sobrien 187143442Sobrien if (mustfreeai) 188143442Sobrien freeaddrinfo(aip); 189143442Sobrien nfscbdargs2.principal = (const char *)princname; 190143442Sobrien if (debug == 0) { 191143442Sobrien daemon(0, 0); 192143442Sobrien (void)signal(SIGTERM, SIG_IGN); 193143442Sobrien (void)signal(SIGHUP, SIG_IGN); 194143442Sobrien (void)signal(SIGINT, SIG_IGN); 195143442Sobrien (void)signal(SIGQUIT, SIG_IGN); 196143442Sobrien } 197143442Sobrien (void)signal(SIGSYS, nonfs); 198143442Sobrien (void)signal(SIGCHLD, reapchild); 199145556Sobrien 200143442Sobrien openlog("nfscbd:", LOG_PID, LOG_DAEMON); 201143442Sobrien 202143442Sobrien pid = fork(); 203143442Sobrien if (pid < 0) { 204143442Sobrien syslog(LOG_ERR, "fork: %m"); 205143442Sobrien nfscbd_exit(1); 206143442Sobrien } else if (pid > 0) { 207143442Sobrien children = pid; 208143442Sobrien } else { 209143442Sobrien (void)signal(SIGUSR1, child_cleanup); 210143442Sobrien setproctitle("server"); 211143442Sobrien nfssvc_flag = NFSSVC_NFSCBD; 212143442Sobrien if (nfssvc(nfssvc_flag, &nfscbdargs2) < 0) { 213143442Sobrien syslog(LOG_ERR, "nfssvc: %m"); 214179458Sremko nfscbd_exit(1); 215179458Sremko } 216179458Sremko exit(0); 217179458Sremko } 218179458Sremko (void)signal(SIGUSR1, cleanup); 219179458Sremko 220179458Sremko if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 221179458Sremko syslog(LOG_ERR, "can't create udp socket"); 222179458Sremko nfscbd_exit(1); 223179458Sremko } 224179458Sremko memset(&inetaddr, 0, sizeof inetaddr); 225179458Sremko inetaddr.sin_family = AF_INET; 226179458Sremko inetaddr.sin_addr.s_addr = INADDR_ANY; 227179458Sremko inetaddr.sin_port = htons(myport); 228179458Sremko inetaddr.sin_len = sizeof(inetaddr); 229179458Sremko ret = bind(sock, (struct sockaddr *)&inetaddr, sizeof(inetaddr)); 230179458Sremko /* If bind() fails, this is a restart, so just skip UDP. */ 231179458Sremko if (ret == 0) { 232179458Sremko len = sizeof(inetaddr); 233179458Sremko if (getsockname(sock, (struct sockaddr *)&inetaddr, &len) < 0){ 234179458Sremko syslog(LOG_ERR, "can't get bound addr"); 235179458Sremko nfscbd_exit(1); 236179458Sremko } 237179458Sremko nfscbdargs.port = ntohs(inetaddr.sin_port); 238179458Sremko if (nfscbdargs.port != myport) { 239179458Sremko syslog(LOG_ERR, "BAD PORT#"); 240179458Sremko nfscbd_exit(1); 241179458Sremko } 242179458Sremko nfscbdargs.sock = sock; 243179458Sremko nfscbdargs.name = NULL; 244179458Sremko nfscbdargs.namelen = 0; 245179458Sremko if (nfssvc(NFSSVC_CBADDSOCK, &nfscbdargs) < 0) { 246179458Sremko syslog(LOG_ERR, "can't Add UDP socket"); 247179458Sremko nfscbd_exit(1); 248179458Sremko } 249179458Sremko } 250179458Sremko (void)close(sock); 251179458Sremko 252179458Sremko /* Now set up the master server socket waiting for tcp connections. */ 253179458Sremko on = 1; 254179458Sremko FD_ZERO(&sockbits); 255179458Sremko connect_type_cnt = 0; 256179458Sremko if ((tcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 257179458Sremko syslog(LOG_ERR, "can't create tcp socket"); 258179458Sremko nfscbd_exit(1); 259179458Sremko } 260143442Sobrien if (setsockopt(tcpsock, 261143442Sobrien SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) 262143442Sobrien syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m"); 263143442Sobrien /* sin_port is already set */ 264143442Sobrien inetaddr.sin_family = AF_INET; 265143442Sobrien inetaddr.sin_addr.s_addr = INADDR_ANY; 266143442Sobrien inetaddr.sin_port = htons(myport); 267143442Sobrien inetaddr.sin_len = sizeof(inetaddr); 268143442Sobrien if (bind(tcpsock, 269143442Sobrien (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) { 270143442Sobrien syslog(LOG_ERR, "can't bind tcp addr"); 271143442Sobrien nfscbd_exit(1); 272143442Sobrien } 273143442Sobrien if (listen(tcpsock, 5) < 0) { 274143442Sobrien syslog(LOG_ERR, "listen failed"); 275143442Sobrien nfscbd_exit(1); 276143442Sobrien } 277143442Sobrien FD_SET(tcpsock, &sockbits); 278143442Sobrien maxsock = tcpsock; 279143442Sobrien connect_type_cnt++; 280143442Sobrien 281143442Sobrien setproctitle("master"); 282143442Sobrien 283143442Sobrien /* 284143442Sobrien * Loop forever accepting connections and passing the sockets 285143442Sobrien * into the kernel for the mounts. 286143442Sobrien */ 287143442Sobrien for (;;) { 288143442Sobrien ready = sockbits; 289143442Sobrien if (connect_type_cnt > 1) { 290143442Sobrien if (select(maxsock + 1, 291143442Sobrien &ready, NULL, NULL, NULL) < 1) { 292143442Sobrien syslog(LOG_ERR, "select failed: %m"); 293143442Sobrien nfscbd_exit(1); 294143442Sobrien } 295143442Sobrien } 296143442Sobrien if (FD_ISSET(tcpsock, &ready)) { 297143442Sobrien len = sizeof(inetpeer); 298143442Sobrien if ((msgsock = accept(tcpsock, 299143442Sobrien (struct sockaddr *)&inetpeer, &len)) < 0) { 300143442Sobrien syslog(LOG_ERR, "accept failed: %m"); 301143442Sobrien nfscbd_exit(1); 302143442Sobrien } 303143442Sobrien memset(inetpeer.sin_zero, 0, 304143442Sobrien sizeof (inetpeer.sin_zero)); 305143442Sobrien if (setsockopt(msgsock, SOL_SOCKET, 306143442Sobrien SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) 307143442Sobrien syslog(LOG_ERR, 308143442Sobrien "setsockopt SO_KEEPALIVE: %m"); 309143442Sobrien nfscbdargs.sock = msgsock; 310143442Sobrien nfscbdargs.name = (caddr_t)&inetpeer; 311143442Sobrien nfscbdargs.namelen = sizeof(inetpeer); 312143442Sobrien nfssvc(NFSSVC_CBADDSOCK, &nfscbdargs); 313143442Sobrien (void)close(msgsock); 314143442Sobrien } 315170593Syongari } 316143442Sobrien} 317143442Sobrien 318143442Sobrienstatic void 319143442Sobrienusage(void) 320143442Sobrien{ 321143442Sobrien 322143442Sobrien errx(1, "usage: nfscbd %s", USAGE); 323143442Sobrien} 324143442Sobrien 325143442Sobrienstatic void 326143442Sobriennonfs(int signo __unused) 327143442Sobrien{ 328143442Sobrien syslog(LOG_ERR, "missing system call: NFS not available"); 329143442Sobrien} 330143442Sobrien 331143442Sobrienstatic void 332152669Sjhbreapchild(int signo __unused) 333143442Sobrien{ 334153203Sobrien pid_t pid; 335153203Sobrien 336153203Sobrien while ((pid = wait3(NULL, WNOHANG, NULL)) > 0) { 337143442Sobrien if (pid == children) 338143442Sobrien children = -1; 339143442Sobrien } 340143442Sobrien} 341143442Sobrien 342143442Sobrienstatic void 343153134Sjhbkillchildren(void) 344152669Sjhb{ 345143442Sobrien 346143442Sobrien if (children > 0) 347143442Sobrien kill(children, SIGKILL); 348143442Sobrien} 349143442Sobrien 350143442Sobrien/* 351143442Sobrien * Cleanup master after SIGUSR1. 352143442Sobrien */ 353143442Sobrienstatic void 354143442Sobriencleanup(int signo __unused) 355143442Sobrien{ 356143442Sobrien nfscbd_exit(0); 357143442Sobrien} 358143442Sobrien 359143442Sobrien/* 360143442Sobrien * Cleanup child after SIGUSR1. 361143442Sobrien */ 362143442Sobrienstatic void 363143442Sobrienchild_cleanup(int signo __unused) 364143442Sobrien{ 365143442Sobrien exit(0); 366143442Sobrien} 367143442Sobrien 368143442Sobrienstatic void 369143442Sobriennfscbd_exit(int status __unused) 370143442Sobrien{ 371143442Sobrien killchildren(); 372143442Sobrien exit(status); 373143442Sobrien} 374143442Sobrien