xref: /illumos-gate/usr/src/cmd/fs.d/nfs/nfsmapid/nfsmapid.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stropts.h>
29 #include <signal.h>
30 #include <fcntl.h>
31 #include <door.h>
32 #include <thread.h>
33 #include <priv_utils.h>
34 #include <locale.h>
35 #include <strings.h>
36 #include <syslog.h>
37 #include <unistd.h>
38 #include <nfs/nfs4.h>
39 #include <nfs/nfsid_map.h>
40 #include <rpcsvc/daemon_utils.h>
41 #include <arpa/nameser.h>
42 #include <nfs/nfssys.h>
43 #include <errno.h>
44 #include <pwd.h>
45 #include <grp.h>
46 
47 extern struct group *_uncached_getgrgid_r(gid_t, struct group *, char *, int);
48 extern struct group *_uncached_getgrnam_r(const char *, struct group *,
49     char *, int);
50 extern struct passwd *_uncached_getpwuid_r(uid_t, struct passwd *, char *, int);
51 extern struct passwd *_uncached_getpwnam_r(const char *, struct passwd *,
52     char *, int);
53 
54 /*
55  * seconds to cache nfsmapid domain info
56  */
57 #define	NFSCFG_DEFAULT_DOMAIN_TMOUT	(5 * 60)
58 #define	NFSMAPID_DOOR   "/var/run/nfsmapid_door"
59 
60 extern void	nfsmapid_func(void *, char *, size_t, door_desc_t *, uint_t);
61 
62 extern void	check_domain(int);
63 extern void	idmap_kcall(int);
64 extern void	open_diag_file(void);
65 
66 size_t		pwd_buflen = 0;
67 size_t		grp_buflen = 0;
68 thread_t	sig_thread;
69 static char	*MyName;
70 
71 /*
72  * nfscfg_domain_tmout is used by nfsv4-test scripts to query
73  * the nfsmapid daemon for the proper timeout. Don't delete !
74  */
75 time_t		 nfscfg_domain_tmout = NFSCFG_DEFAULT_DOMAIN_TMOUT;
76 
77 /*
78  * Processing for daemonization
79  */
80 static void
81 daemonize(void)
82 {
83 	switch (fork()) {
84 		case -1:
85 			perror("nfsmapid: can't fork");
86 			exit(2);
87 			/* NOTREACHED */
88 		case 0:		/* child */
89 			break;
90 
91 		default:	/* parent */
92 			_exit(0);
93 	}
94 
95 	if (chdir("/") < 0)
96 		syslog(LOG_ERR, gettext("chdir /: %m"));
97 
98 	/*
99 	 * Close stdin, stdout, and stderr.
100 	 * Open again to redirect input+output
101 	 */
102 	(void) close(0);
103 	(void) close(1);
104 	(void) close(2);
105 	(void) open("/dev/null", O_RDONLY);
106 	(void) open("/dev/null", O_WRONLY);
107 	(void) dup(1);
108 	(void) setsid();
109 }
110 
111 /* ARGSUSED */
112 static void *
113 sig_handler(void *arg)
114 {
115 	siginfo_t	si;
116 	sigset_t	sigset;
117 	struct timespec	tmout;
118 	int		ret;
119 
120 	tmout.tv_nsec = 0;
121 	(void) sigemptyset(&sigset);
122 	(void) sigaddset(&sigset, SIGHUP);
123 	(void) sigaddset(&sigset, SIGTERM);
124 #ifdef	DEBUG
125 	(void) sigaddset(&sigset, SIGINT);
126 #endif
127 
128 	/*CONSTCOND*/
129 	while (1) {
130 		tmout.tv_sec = nfscfg_domain_tmout;
131 		if ((ret = sigtimedwait(&sigset, &si, &tmout)) != 0) {
132 			/*
133 			 * EAGAIN: no signals arrived during timeout.
134 			 * check/update config files and continue.
135 			 */
136 			if (ret == -1 && errno == EAGAIN) {
137 				check_domain(0);
138 				continue;
139 			}
140 
141 			switch (si.si_signo) {
142 				case SIGHUP:
143 					check_domain(1);
144 					break;
145 #ifdef DEBUG
146 				case SIGINT:
147 					exit(0);
148 #endif
149 				case SIGTERM:
150 				default:
151 					exit(si.si_signo);
152 			}
153 		}
154 	}
155 	/*NOTREACHED*/
156 	return (NULL);
157 }
158 
159 /*
160  * Thread initialization. Mask out all signals we want our
161  * signal handler to handle for us from any other threads.
162  */
163 static void
164 thr_init(void)
165 {
166 	sigset_t sigset;
167 	long	 thr_flags = (THR_NEW_LWP|THR_DAEMON|THR_SUSPENDED);
168 
169 	/*
170 	 * Before we kick off any other threads, mask out desired
171 	 * signals from main thread so that any subsequent threads
172 	 * don't receive said signals.
173 	 */
174 	(void) thr_sigsetmask(0, NULL, &sigset);
175 	(void) sigaddset(&sigset, SIGHUP);
176 	(void) sigaddset(&sigset, SIGTERM);
177 #ifdef	DEBUG
178 	(void) sigaddset(&sigset, SIGINT);
179 #endif
180 	(void) thr_sigsetmask(SIG_SETMASK, &sigset, NULL);
181 
182 	/*
183 	 * Create the signal handler thread suspended ! We do things
184 	 * this way at setup time to minimize the probability of
185 	 * introducing any race conditions _if_ the process were to
186 	 * get a SIGHUP signal while creating a new DNS query thread
187 	 * in get_dns_txt_domain().
188 	 */
189 	if (thr_create(NULL, 0, sig_handler, 0, thr_flags, &sig_thread)) {
190 		syslog(LOG_ERR,
191 			gettext("Failed to create signal handling thread"));
192 		exit(4);
193 	}
194 }
195 
196 static void
197 daemon_init(void)
198 {
199 	struct passwd pwd;
200 	struct group grp;
201 	char *pwd_buf;
202 	char *grp_buf;
203 
204 	/*
205 	 * passwd/group reentrant interfaces limits
206 	 */
207 	pwd_buflen = (size_t)sysconf(_SC_GETPW_R_SIZE_MAX);
208 	grp_buflen = (size_t)sysconf(_SC_GETGR_R_SIZE_MAX);
209 
210 	/*
211 	 * MT initialization is done first so that if there is the
212 	 * need to fire an additional thread to continue to query
213 	 * DNS, that thread is started off with the main thread's
214 	 * sigmask.
215 	 */
216 	thr_init();
217 
218 	/*
219 	 * Determine nfsmapid domain.
220 	 */
221 	check_domain(0);
222 
223 	/*
224 	 * In the case of nfsmapid running diskless, it is important
225 	 * to get the initial connections to the nameservices
226 	 * established to prevent problems like opening a devfs
227 	 * node to contact a nameservice being blocked by the
228 	 * resolution of an active devfs lookup.
229 	 * First issue a set*ent to "open" the databases and then
230 	 * get an entry and finally lookup a bogus entry to trigger
231 	 * any lazy opens.
232 	 */
233 	setpwent();
234 	setgrent();
235 	(void) getpwent();
236 	(void) getgrent();
237 	if ((pwd_buf = malloc(pwd_buflen)) == NULL)
238 		return;
239 
240 	(void) _uncached_getpwnam_r("NF21dmvP", &pwd, pwd_buf, pwd_buflen);
241 	(void) _uncached_getpwuid_r(1181794, &pwd, pwd_buf, pwd_buflen);
242 
243 	if ((grp_buf = realloc(pwd_buf, grp_buflen)) == NULL) {
244 		free(pwd_buf);
245 		return;
246 	}
247 
248 	(void) _uncached_getgrnam_r("NF21dmvP", &grp, grp_buf, grp_buflen);
249 	(void) _uncached_getgrgid_r(1181794, &grp, grp_buf, grp_buflen);
250 	free(grp_buf);
251 }
252 
253 static int
254 start_svcs(void)
255 {
256 	int doorfd = -1;
257 #ifdef DEBUG
258 	int dfd;
259 #endif
260 
261 	if ((doorfd = door_create(nfsmapid_func, NULL,
262 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
263 		syslog(LOG_ERR, "Unable to create door: %m\n");
264 		return (1);
265 	}
266 
267 #ifdef DEBUG
268 	/*
269 	 * Create a file system path for the door
270 	 */
271 	if ((dfd = open(NFSMAPID_DOOR, O_RDWR|O_CREAT|O_TRUNC,
272 				S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
273 		syslog(LOG_ERR, "Unable to open %s: %m\n", NFSMAPID_DOOR);
274 		(void) close(doorfd);
275 		return (1);
276 	}
277 
278 	/*
279 	 * Clean up any stale associations
280 	 */
281 	(void) fdetach(NFSMAPID_DOOR);
282 
283 	/*
284 	 * Register in namespace to pass to the kernel to door_ki_open
285 	 */
286 	if (fattach(doorfd, NFSMAPID_DOOR) == -1) {
287 		syslog(LOG_ERR, "Unable to fattach door: %m\n");
288 		(void) close(dfd);
289 		(void) close(doorfd);
290 		return (1);
291 	}
292 	(void) close(dfd);
293 #endif
294 
295 	/*
296 	 * Now that we're actually running, go
297 	 * ahead and flush the kernel flushes
298 	 * Pass door name to kernel for door_ki_open
299 	 */
300 	idmap_kcall(doorfd);
301 
302 	/*
303 	 * Wait for incoming calls
304 	 */
305 	/*CONSTCOND*/
306 	while (1)
307 		(void) pause();
308 
309 	syslog(LOG_ERR, gettext("Door server exited"));
310 	return (10);
311 }
312 
313 /* ARGSUSED */
314 int
315 main(int argc, char **argv)
316 {
317 	MyName = argv[0];
318 
319 	(void) setlocale(LC_ALL, "");
320 	(void) textdomain(TEXT_DOMAIN);
321 
322 	/* _check_services() framework setup */
323 	(void) _create_daemon_lock(NFSMAPID, DAEMON_UID, DAEMON_GID);
324 
325 	/*
326 	 * Open diag file in /var/run while we've got the perms
327 	 */
328 	open_diag_file();
329 
330 	/*
331 	 * Initialize the daemon to basic + sys_nfs
332 	 */
333 #ifndef	DEBUG
334 	if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET,
335 	    DAEMON_UID, DAEMON_GID, PRIV_SYS_NFS, (char *)NULL) == -1) {
336 		(void) fprintf(stderr, gettext("%s PRIV_SYS_NFS privilege "
337 			"missing\n"), MyName);
338 		exit(1);
339 	}
340 #endif
341 
342 	/*
343 	 * Take away a subset of basic, while this is not the absolute
344 	 * minimum, it is important that it is unique among other
345 	 * daemons to insure that we get a unique cred that will
346 	 * result in a unique open_owner.  If not, we run the risk
347 	 * of a diskless client deadlocking with a thread holding
348 	 * the open_owner seqid lock while upcalling the daemon.
349 	 * XXX This restriction will go away once we stop holding
350 	 * XXX open_owner lock across rfscalls!
351 	 */
352 	(void) priv_set(PRIV_OFF, PRIV_PERMITTED,
353 		PRIV_FILE_LINK_ANY, PRIV_PROC_SESSION,
354 		(char *)NULL);
355 
356 #ifndef DEBUG
357 	daemonize();
358 	switch (_enter_daemon_lock(NFSMAPID)) {
359 		case 0:
360 			break;
361 
362 		case -1:
363 			syslog(LOG_ERR, "error locking for %s: %s", NFSMAPID,
364 			    strerror(errno));
365 			exit(3);
366 
367 		default:
368 			/* daemon was already running */
369 			exit(0);
370 	}
371 #endif
372 	openlog(MyName, LOG_PID | LOG_NDELAY, LOG_DAEMON);
373 
374 	/* Initialize daemon subsystems */
375 	daemon_init();
376 
377 	/* start services */
378 	return (start_svcs());
379 }
380