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