xref: /illumos-gate/usr/src/cmd/fs.d/nfs/nfs4cbd/nfs4cbd.c (revision 1979231e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * Portions of this source code were derived from Berkeley 4.3 BSD
32  * under license from the Regents of the University of California.
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 /*
38  * This module provides the user level support for the NFSv4
39  * callback program.  It is modeled after nfsd.  When a nfsv4
40  * mount occurs, the mount command forks and the child runs
41  * start_nfs4_callback.  If this is the first mount, then the
42  * process will hang around listening for incoming connection
43  * requests from the nfsv4 server.
44  *
45  * For connection-less protocols, the krpc is started immediately.
46  * For connection oriented protocols, the kernel module is informed
47  * of netid and universal address that it can give this
48  * information to the server during setclientid.
49  */
50 
51 #include <sys/param.h>
52 #include <sys/types.h>
53 #include <syslog.h>
54 #include <tiuser.h>
55 #include <rpc/rpc.h>
56 #include <errno.h>
57 #include <thread.h>
58 #include <sys/resource.h>
59 #include <sys/file.h>
60 #include <nfs/nfs.h>
61 #include <nfs/nfssys.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <netconfig.h>
65 #include <netdir.h>
66 #include <string.h>
67 #include <unistd.h>
68 #include <stropts.h>
69 #include <sys/tihdr.h>
70 #include <netinet/tcp.h>
71 #include "nfs_tbind.h"
72 #include "thrpool.h"
73 #include <rpcsvc/nfs4_prot.h>
74 #include <netdb.h>
75 #include <signal.h>
76 #include <strings.h>
77 #include <priv_utils.h>
78 #include <rpcsvc/daemon_utils.h>
79 
80 static	int	nfs4svc(int, struct netbuf *, struct netconfig *, int,
81 		struct netbuf *);
82 extern	int	_nfssys(int, void *);
83 
84 static	char	*MyName;
85 
86 /*
87  * The following are all globals used by routines in nfs_tbind.c.
88  */
89 size_t	end_listen_fds;		/* used by conn_close_oldest() */
90 size_t	num_fds = 0;		/* used by multiple routines */
91 int	listen_backlog = 32;	/* used by bind_to_{provider,proto}() */
92 int	num_servers;		/* used by cots_listen_event() */
93 int	(*Mysvc)(int, struct netbuf, struct netconfig *) = NULL;
94 				/* used by cots_listen_event() */
95 int	max_conns_allowed = -1;	/* used by cots_listen_event() */
96 
97 int
98 main(int argc, char *argv[])
99 {
100 	int pid;
101 	int i;
102 	struct protob *protobp;
103 	struct flock f;
104 	pid_t pi;
105 	struct svcpool_args cb_svcpool;
106 
107 	MyName = "nfs4cbd";
108 	Mysvc4 = nfs4svc;
109 
110 #ifndef	DEBUG
111 	/*
112 	 * Close existing file descriptors, open "/dev/null" as
113 	 * standard input, output, and error, and detach from
114 	 * controlling terminal.
115 	 */
116 	closefrom(0);
117 	(void) open("/dev/null", O_RDONLY);
118 	(void) open("/dev/null", O_WRONLY);
119 	(void) dup(1);
120 	(void) setsid();
121 #endif
122 
123 	/*
124 	 * create a child to continue our work
125 	 * Parent's exit will tell mount command we're ready to go
126 	 */
127 	if ((pi = fork()) > 0) {
128 		exit(0);
129 	}
130 
131 	if (pi == -1) {
132 		(void) syslog(LOG_ERR,
133 			"Could not start NFS4_CALLBACK service");
134 		exit(1);
135 	}
136 
137 	(void) _create_daemon_lock(NFS4CBD, DAEMON_UID, DAEMON_GID);
138 
139 	svcsetprio();
140 
141 	if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET,
142 	    DAEMON_UID, DAEMON_GID, PRIV_SYS_NFS, (char *)NULL) == -1) {
143 		(void) fprintf(stderr, "%s must be run with sufficient"
144 			" privileges\n", argv[0]);
145 		exit(1);
146 	}
147 	/* Basic privileges we don't need, remove from E/P. */
148 	__fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_FORK, PRIV_FILE_LINK_ANY,
149 	    PRIV_PROC_SESSION, PRIV_PROC_INFO, (char *)NULL);
150 
151 	/*
152 	 * establish our lock on the lock file and write our pid to it.
153 	 * exit if some other process holds the lock, or if there's any
154 	 * error in writing/locking the file.
155 	 */
156 	pid = _enter_daemon_lock(NFS4CBD);
157 	switch (pid) {
158 	case 0:
159 		break;
160 	case -1:
161 		syslog(LOG_ERR, "error locking for %s: %s", NFS4CBD,
162 		    strerror(errno));
163 		exit(2);
164 	default:
165 		/* daemon was already running */
166 		exit(0);
167 	}
168 
169 	openlog(MyName, LOG_PID | LOG_NDELAY, LOG_DAEMON);
170 
171 	cb_svcpool.id = NFS_CB_SVCPOOL_ID;
172 	cb_svcpool.maxthreads = 0;
173 	cb_svcpool.redline = 0;
174 	cb_svcpool.qsize = 0;
175 	cb_svcpool.timeout = 0;
176 	cb_svcpool.stksize = 0;
177 	cb_svcpool.max_same_xprt = 0;
178 
179 	/* create a SVC_POOL for the nfsv4 callback deamon */
180 	if (_nfssys(SVCPOOL_CREATE, &cb_svcpool)) {
181 		(void) syslog(LOG_ERR, "can't setup NFS_CB SVCPOOL: Exiting");
182 		exit(1);
183 	}
184 
185 	/*
186 	 * Set up blocked thread to do LWP creation on behalf of the kernel.
187 	 */
188 	if (svcwait(NFS_CB_SVCPOOL_ID)) {
189 		(void) syslog(LOG_ERR,
190 		    "Can't set up NFS_CB LWP creator: Exiting");
191 		exit(1);
192 	}
193 
194 
195 	/*
196 	 * Build a protocol block list for registration.
197 	 */
198 	protobp = (struct protob *)malloc(sizeof (struct protob));
199 	protobp->serv = "NFS4_CALLBACK";
200 	protobp->versmin = NFS_CB;
201 	protobp->versmax = NFS_CB;
202 	protobp->program = NFS4_CALLBACK;
203 	protobp->next = NULL;
204 
205 	if (do_all(protobp, NULL) == -1) {
206 		exit(1);
207 	}
208 
209 	free(protobp);
210 
211 	if (num_fds == 0) {
212 		(void) syslog(LOG_ERR,
213 		"Could not start NFS4_CALLBACK service for any protocol");
214 		exit(1);
215 	}
216 
217 	end_listen_fds = num_fds;
218 	/*
219 	 * Poll for non-data control events on the transport descriptors.
220 	 */
221 	poll_for_action();
222 
223 	/*
224 	 * If we get here, something failed in poll_for_action().
225 	 */
226 	return (1);
227 }
228 
229 char *
230 get_uaddr(int fd, struct netconfig *nconf, struct netbuf *nb)
231 {
232 	struct nfs_svc_args nsa;
233 	char *ua, *ua2, *mua = NULL;
234 	char me[MAXHOSTNAMELEN];
235 	struct nd_addrlist *nas;
236 	struct nd_hostserv hs;
237 	struct nd_mergearg ma;
238 
239 	ua = taddr2uaddr(nconf, nb);
240 
241 	if (ua == NULL) {
242 #ifdef	DEBUG
243 		fprintf(stderr, "taddr2uaddr failed for netid %s\n",
244 			nconf->nc_netid);
245 #endif
246 		return (NULL);
247 	}
248 
249 	gethostname(me, MAXHOSTNAMELEN);
250 
251 	hs.h_host = me;
252 	hs.h_serv = "nfs";
253 	if (netdir_getbyname(nconf, &hs, &nas)) {
254 #ifdef DEBUG
255 		netdir_perror("netdir_getbyname");
256 #endif
257 		return (NULL);
258 	}
259 
260 	ua2 = taddr2uaddr(nconf, nas->n_addrs);
261 
262 	if (ua2 == NULL) {
263 #ifdef	DEBUG
264 		fprintf(stderr, "taddr2uaddr failed for netid %s.\n",
265 			nconf->nc_netid);
266 #endif
267 		return (NULL);
268 	}
269 
270 	ma.s_uaddr = ua;
271 	ma.c_uaddr = ua2;
272 	ma.m_uaddr = NULL;
273 
274 	if (netdir_options(nconf, ND_MERGEADDR, 0, (char *)&ma)) {
275 #ifdef DEBUG
276 		netdir_perror("netdir_options");
277 #endif
278 		return (NULL);
279 	}
280 
281 	mua = ma.m_uaddr;
282 	return (mua);
283 }
284 
285 /*
286  * Establish NFS4 callback service thread.
287  */
288 static int
289 nfs4svc(int fd, struct netbuf *addrmask, struct netconfig *nconf,
290 	int cmd, struct netbuf *addr)
291 {
292 	struct nfs4_svc_args nsa;
293 	char *ua;
294 	int error;
295 
296 	ua = get_uaddr(fd, nconf, addr);
297 
298 	if (ua == NULL) {
299 		syslog(LOG_NOTICE, "nfsv4 cannot determine local hostname "
300 			"binding for transport %s - delegations will not be "
301 			"available on this transport\n", nconf->nc_netid);
302 		return (0);
303 	}
304 
305 #ifdef	DEBUG
306 	if (cmd & NFS4_KRPC_START)
307 		fprintf(stderr, "nfs4cbd: starting callback rpc on %s %s\n",
308 			nconf->nc_netid, ua);
309 	else
310 		fprintf(stderr, "nfs4cbd: listening on %s %s\n",
311 			nconf->nc_netid, ua);
312 #endif
313 
314 	nsa.fd = fd;
315 	nsa.cmd = cmd;
316 	nsa.netid = nconf->nc_netid;
317 	if (addrmask)
318 		nsa.addrmask = *addrmask;
319 	else
320 		bzero(&nsa.addrmask, sizeof (struct netbuf));
321 	nsa.addr = ua;
322 	nsa.protofmly = nconf->nc_protofmly;
323 	nsa.proto = nconf->nc_proto;
324 	if ((error = _nfssys(NFS4_SVC, &nsa)) != 0)
325 		syslog(LOG_ERR, "nfssys NFS4_SVC failed\n");
326 
327 	return (error);
328 }
329