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