1/*- 2 * Copyright (c) 2009 Rick Macklem, University of Guelph 3 * 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 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28#include <sys/param.h> 29#include <sys/ioctl.h> 30#include <sys/linker.h> 31#include <sys/module.h> 32#include <sys/mount.h> 33#include <sys/socket.h> 34#include <sys/socketvar.h> 35#include <sys/stat.h> 36#include <sys/ucred.h> 37#include <sys/uio.h> 38#include <sys/vnode.h> 39#include <sys/wait.h> 40 41#include <nfs/nfssvc.h> 42 43#include <rpc/rpc.h> 44 45#include <fs/nfs/rpcv2.h> 46#include <fs/nfs/nfsproto.h> 47#include <fs/nfs/nfskpiport.h> 48#include <fs/nfs/nfs.h> 49 50#include <err.h> 51#include <errno.h> 52#include <fcntl.h> 53#include <grp.h> 54#include <netdb.h> 55#include <pwd.h> 56#include <signal.h> 57#include <stdio.h> 58#include <stdlib.h> 59#include <string.h> 60#include <syslog.h> 61#include <unistd.h> 62 63/* Global defs */ 64#ifdef DEBUG 65#define syslog(e, s) fprintf(stderr,(s)) 66static int debug = 1; 67#else 68static int debug = 0; 69#endif 70 71static pid_t children; 72 73static void nonfs(int); 74static void reapchild(int); 75static void usage(void); 76static void cleanup(int); 77static void child_cleanup(int); 78static void nfscbd_exit(int); 79static void killchildren(void); 80 81/* 82 * Nfs callback server daemon. 83 * 84 * 1 - do file descriptor and signal cleanup 85 * 2 - fork the nfscbd(s) 86 * 4 - create callback server socket(s) 87 * 5 - set up server socket for rpc 88 * 89 * For connectionless protocols, just pass the socket into the kernel via. 90 * nfssvc(). 91 * For connection based sockets, loop doing accepts. When you get a new 92 * socket from accept, pass the msgsock into the kernel via. nfssvc(). 93 */ 94int 95main(int argc, char *argv[]) 96{ 97 struct nfscbd_args nfscbdargs; 98 struct nfsd_nfscbd_args nfscbdargs2; 99 struct sockaddr_in inetaddr, inetpeer; 100 fd_set ready, sockbits; 101 int ch, connect_type_cnt, maxsock, msgsock, error; 102 int nfssvc_flag, on, sock, tcpsock, ret, mustfreeai = 0; 103 char *cp, princname[128]; 104 char myname[MAXHOSTNAMELEN], *myfqdnname = NULL; 105 struct addrinfo *aip, hints; 106 pid_t pid; 107 short myport = NFSV4_CBPORT; 108 socklen_t len; 109 110 if (modfind("nfscl") < 0) { 111 /* Not present in kernel, try loading it */ 112 if (kldload("nfscl") < 0 || 113 modfind("nfscl") < 0) 114 errx(1, "nfscl is not available"); 115 } 116 /* 117 * First, get our fully qualified host name, if possible. 118 */ 119 if (gethostname(myname, MAXHOSTNAMELEN) >= 0) { 120 cp = strchr(myname, '.'); 121 if (cp != NULL && *(cp + 1) != '\0') { 122 cp = myname; 123 } else { 124 /* 125 * No domain on myname, so try looking it up. 126 */ 127 cp = NULL; 128 memset((void *)&hints, 0, sizeof (hints)); 129 hints.ai_flags = AI_CANONNAME; 130 error = getaddrinfo(myname, NULL, &hints, &aip); 131 if (error == 0) { 132 if (aip->ai_canonname != NULL && 133 (cp = strchr(aip->ai_canonname, '.')) != NULL 134 && *(cp + 1) != '\0') { 135 cp = aip->ai_canonname; 136 mustfreeai = 1; 137 } else { 138 freeaddrinfo(aip); 139 } 140 } 141 } 142 if (cp == NULL) 143 warnx("Can't get fully qualified host name"); 144 myfqdnname = cp; 145 } 146 147 princname[0] = '\0'; 148#define GETOPT "p:P:" 149#define USAGE "[ -p port_num ] [ -P client_principal ]" 150 while ((ch = getopt(argc, argv, GETOPT)) != -1) 151 switch (ch) { 152 case 'p': 153 myport = atoi(optarg); 154 if (myport < 1) { 155 warnx("port# non-positive, reset to %d", 156 NFSV4_CBPORT); 157 myport = NFSV4_CBPORT; 158 } 159 break; 160 case 'P': 161 cp = optarg; 162 if (cp != NULL && strlen(cp) > 0 && 163 strlen(cp) < sizeof (princname)) { 164 if (strchr(cp, '@') == NULL && 165 myfqdnname != NULL) 166 snprintf(princname, sizeof (princname), 167 "%s@%s", cp, myfqdnname); 168 else 169 strlcpy(princname, cp, 170 sizeof (princname)); 171 } else { 172 warnx("client princ invalid. ignored\n"); 173 } 174 break; 175 default: 176 case '?': 177 usage(); 178 } 179 argv += optind; 180 argc -= optind; 181 182 if (argc > 0) 183 usage(); 184 185 if (mustfreeai) 186 freeaddrinfo(aip); 187 nfscbdargs2.principal = (const char *)princname; 188 if (debug == 0) { 189 daemon(0, 0); 190 (void)signal(SIGTERM, SIG_IGN); 191 (void)signal(SIGHUP, SIG_IGN); 192 (void)signal(SIGINT, SIG_IGN); 193 (void)signal(SIGQUIT, SIG_IGN); 194 } 195 (void)signal(SIGSYS, nonfs); 196 (void)signal(SIGCHLD, reapchild); 197 198 openlog("nfscbd:", LOG_PID, LOG_DAEMON); 199 200 pid = fork(); 201 if (pid < 0) { 202 syslog(LOG_ERR, "fork: %m"); 203 nfscbd_exit(1); 204 } else if (pid > 0) { 205 children = pid; 206 } else { 207 (void)signal(SIGUSR1, child_cleanup); 208 setproctitle("server"); 209 nfssvc_flag = NFSSVC_NFSCBD; 210 if (nfssvc(nfssvc_flag, &nfscbdargs2) < 0) { 211 syslog(LOG_ERR, "nfssvc: %m"); 212 nfscbd_exit(1); 213 } 214 exit(0); 215 } 216 (void)signal(SIGUSR1, cleanup); 217 218 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 219 syslog(LOG_ERR, "can't create udp socket"); 220 nfscbd_exit(1); 221 } 222 memset(&inetaddr, 0, sizeof inetaddr); 223 inetaddr.sin_family = AF_INET; 224 inetaddr.sin_addr.s_addr = INADDR_ANY; 225 inetaddr.sin_port = htons(myport); 226 inetaddr.sin_len = sizeof(inetaddr); 227 ret = bind(sock, (struct sockaddr *)&inetaddr, sizeof(inetaddr)); 228 /* If bind() fails, this is a restart, so just skip UDP. */ 229 if (ret == 0) { 230 len = sizeof(inetaddr); 231 if (getsockname(sock, (struct sockaddr *)&inetaddr, &len) < 0){ 232 syslog(LOG_ERR, "can't get bound addr"); 233 nfscbd_exit(1); 234 } 235 nfscbdargs.port = ntohs(inetaddr.sin_port); 236 if (nfscbdargs.port != myport) { 237 syslog(LOG_ERR, "BAD PORT#"); 238 nfscbd_exit(1); 239 } 240 nfscbdargs.sock = sock; 241 nfscbdargs.name = NULL; 242 nfscbdargs.namelen = 0; 243 if (nfssvc(NFSSVC_CBADDSOCK, &nfscbdargs) < 0) { 244 syslog(LOG_ERR, "can't Add UDP socket"); 245 nfscbd_exit(1); 246 } 247 } 248 (void)close(sock); 249 250 /* Now set up the master server socket waiting for tcp connections. */ 251 on = 1; 252 FD_ZERO(&sockbits); 253 connect_type_cnt = 0; 254 if ((tcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 255 syslog(LOG_ERR, "can't create tcp socket"); 256 nfscbd_exit(1); 257 } 258 if (setsockopt(tcpsock, 259 SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) 260 syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m"); 261 /* sin_port is already set */ 262 inetaddr.sin_family = AF_INET; 263 inetaddr.sin_addr.s_addr = INADDR_ANY; 264 inetaddr.sin_port = htons(myport); 265 inetaddr.sin_len = sizeof(inetaddr); 266 if (bind(tcpsock, 267 (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) { 268 syslog(LOG_ERR, "can't bind tcp addr"); 269 nfscbd_exit(1); 270 } 271 if (listen(tcpsock, 5) < 0) { 272 syslog(LOG_ERR, "listen failed"); 273 nfscbd_exit(1); 274 } 275 FD_SET(tcpsock, &sockbits); 276 maxsock = tcpsock; 277 connect_type_cnt++; 278 279 setproctitle("master"); 280 281 /* 282 * Loop forever accepting connections and passing the sockets 283 * into the kernel for the mounts. 284 */ 285 for (;;) { 286 ready = sockbits; 287 if (connect_type_cnt > 1) { 288 if (select(maxsock + 1, 289 &ready, NULL, NULL, NULL) < 1) { 290 syslog(LOG_ERR, "select failed: %m"); 291 nfscbd_exit(1); 292 } 293 } 294 if (FD_ISSET(tcpsock, &ready)) { 295 len = sizeof(inetpeer); 296 if ((msgsock = accept(tcpsock, 297 (struct sockaddr *)&inetpeer, &len)) < 0) { 298 syslog(LOG_ERR, "accept failed: %m"); 299 nfscbd_exit(1); 300 } 301 memset(inetpeer.sin_zero, 0, 302 sizeof (inetpeer.sin_zero)); 303 if (setsockopt(msgsock, SOL_SOCKET, 304 SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) 305 syslog(LOG_ERR, 306 "setsockopt SO_KEEPALIVE: %m"); 307 nfscbdargs.sock = msgsock; 308 nfscbdargs.name = (caddr_t)&inetpeer; 309 nfscbdargs.namelen = sizeof(inetpeer); 310 nfssvc(NFSSVC_CBADDSOCK, &nfscbdargs); 311 (void)close(msgsock); 312 } 313 } 314} 315 316static void 317usage(void) 318{ 319 320 errx(1, "usage: nfscbd %s", USAGE); 321} 322 323static void 324nonfs(int signo __unused) 325{ 326 syslog(LOG_ERR, "missing system call: NFS not available"); 327} 328 329static void 330reapchild(int signo __unused) 331{ 332 pid_t pid; 333 334 while ((pid = wait3(NULL, WNOHANG, NULL)) > 0) { 335 if (pid == children) 336 children = -1; 337 } 338} 339 340static void 341killchildren(void) 342{ 343 344 if (children > 0) 345 kill(children, SIGKILL); 346} 347 348/* 349 * Cleanup master after SIGUSR1. 350 */ 351static void 352cleanup(int signo __unused) 353{ 354 nfscbd_exit(0); 355} 356 357/* 358 * Cleanup child after SIGUSR1. 359 */ 360static void 361child_cleanup(int signo __unused) 362{ 363 exit(0); 364} 365 366static void 367nfscbd_exit(int status __unused) 368{ 369 killchildren(); 370 exit(status); 371} 372