xref: /freebsd/usr.sbin/nfscbd/nfscbd.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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