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
daemonize(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 *
sig_handler(void * arg)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
thr_init(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
daemon_init(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
start_svcs(void)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
main(int argc,char ** argv)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