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)) 66 static int debug = 1; 67 #else 68 static int debug = 0; 69 #endif 70 71 static pid_t children; 72 73 static void nonfs(int); 74 static void reapchild(int); 75 static void usage(void); 76 static void cleanup(int); 77 static void child_cleanup(int); 78 static void nfscbd_exit(int); 79 static 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 */ 94 int 95 main(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 316 static void 317 usage(void) 318 { 319 320 errx(1, "usage: nfscbd %s", USAGE); 321 } 322 323 static void 324 nonfs(int signo __unused) 325 { 326 syslog(LOG_ERR, "missing system call: NFS not available"); 327 } 328 329 static void 330 reapchild(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 340 static void 341 killchildren(void) 342 { 343 344 if (children > 0) 345 kill(children, SIGKILL); 346 } 347 348 /* 349 * Cleanup master after SIGUSR1. 350 */ 351 static void 352 cleanup(int signo __unused) 353 { 354 nfscbd_exit(0); 355 } 356 357 /* 358 * Cleanup child after SIGUSR1. 359 */ 360 static void 361 child_cleanup(int signo __unused) 362 { 363 exit(0); 364 } 365 366 static void 367 nfscbd_exit(int status __unused) 368 { 369 killchildren(); 370 exit(status); 371 } 372