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 2007 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 /*
29  * Door server routines for nfsmapid daemon
30  * Translate NFSv4 users and groups between numeric and string values
31  */
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <alloca.h>
35 #include <signal.h>
36 #include <libintl.h>
37 #include <limits.h>
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <string.h>
41 #include <memory.h>
42 #include <pwd.h>
43 #include <grp.h>
44 #include <door.h>
45 #include <syslog.h>
46 #include <fcntl.h>
47 #include <unistd.h>
48 #include <assert.h>
49 #include <deflt.h>
50 #include <nfs/nfs4.h>
51 #include <nfs/nfssys.h>
52 #include <nfs/nfsid_map.h>
53 #include <nfs/mapid.h>
54 #include <sys/sdt.h>
55 #include <sys/idmap.h>
56 #include <idmap.h>
57 
58 #define		UID_MAX_STR_LEN		11	/* Digits in UID_MAX + 1 */
59 #define		DIAG_FILE		"/var/run/nfs4_domain"
60 
61 /*
62  * idmap_kcall() takes a door descriptor as it's argument when we
63  * need to (re)establish the in-kernel door handles. When we only
64  * want to flush the id kernel caches, we don't redo the door setup.
65  */
66 #define		FLUSH_KCACHES_ONLY	(int)-1
67 
68 FILE		*n4_fp;
69 int		 n4_fd;
70 
71 extern size_t	pwd_buflen;
72 extern size_t	grp_buflen;
73 extern thread_t	sig_thread;
74 
75 /*
76  * Prototypes
77  */
78 extern void	 check_domain(int);
79 extern void	 idmap_kcall(int);
80 extern int	 _nfssys(int, void *);
81 extern int	 valid_domain(const char *);
82 extern int	 validate_id_str(const char *);
83 extern int	 extract_domain(char *, char **, char **);
84 extern void	 update_diag_file(char *);
85 extern void	*cb_update_domain(void *);
86 extern int	 cur_domain_null(void);
87 
88 void
89 nfsmapid_str_uid(struct mapid_arg *argp, size_t arg_size)
90 {
91 	struct mapid_res result;
92 	struct passwd	 pwd;
93 	struct passwd	*pwd_ptr;
94 	int		 pwd_rc;
95 	char		*pwd_buf;
96 	char		*user;
97 	char		*domain;
98 	idmap_stat	 rc;
99 
100 	if (argp->u_arg.len <= 0 || arg_size < MAPID_ARG_LEN(argp->u_arg.len)) {
101 		result.status = NFSMAPID_INVALID;
102 		result.u_res.uid = UID_NOBODY;
103 		goto done;
104 	}
105 
106 	if (!extract_domain(argp->str, &user, &domain)) {
107 		unsigned long id;
108 
109 		/*
110 		 * Invalid "user@domain" string. Still, the user
111 		 * part might be an encoded uid, so do a final check.
112 		 * Remember, domain part of string was not set since
113 		 * not a valid string.
114 		 */
115 		if (!validate_id_str(user)) {
116 			result.status = NFSMAPID_UNMAPPABLE;
117 			result.u_res.uid = UID_NOBODY;
118 			goto done;
119 		}
120 
121 		errno = 0;
122 		id = strtoul(user, (char **)NULL, 10);
123 
124 		/*
125 		 * We don't accept ephemeral ids from the wire.
126 		 */
127 		if (errno || id > UID_MAX) {
128 			result.status = NFSMAPID_UNMAPPABLE;
129 			result.u_res.uid = UID_NOBODY;
130 			goto done;
131 		}
132 
133 		result.u_res.uid = (uid_t)id;
134 		result.status = NFSMAPID_NUMSTR;
135 		goto done;
136 	}
137 
138 	/*
139 	 * String properly constructed. Now we check for domain and
140 	 * group validity.
141 	 */
142 	if (!cur_domain_null() && !valid_domain(domain)) {
143 		/*
144 		 * If the domain part of the string does not
145 		 * match the NFS domain, try to map it using
146 		 * idmap service.
147 		 */
148 		rc = idmap_getuidbywinname(user, domain, &result.u_res.uid);
149 		if (rc != IDMAP_SUCCESS) {
150 			result.status = NFSMAPID_BADDOMAIN;
151 			result.u_res.uid = UID_NOBODY;
152 			goto done;
153 		}
154 		result.status = NFSMAPID_OK;
155 		goto done;
156 	}
157 
158 	if ((pwd_buf = malloc(pwd_buflen)) == NULL ||
159 	    (pwd_rc = getpwnam_r(user, &pwd, pwd_buf, pwd_buflen, &pwd_ptr))
160 	    != 0 || pwd_ptr == NULL) {
161 
162 		if (pwd_buf == NULL || pwd_rc != 0)
163 			result.status = NFSMAPID_INTERNAL;
164 		else {
165 			/*
166 			 * Not a valid user
167 			 */
168 			result.status = NFSMAPID_NOTFOUND;
169 			free(pwd_buf);
170 		}
171 		result.u_res.uid = UID_NOBODY;
172 		goto done;
173 	}
174 
175 	/*
176 	 * Valid user entry
177 	 */
178 	result.u_res.uid = pwd.pw_uid;
179 	result.status = NFSMAPID_OK;
180 	free(pwd_buf);
181 done:
182 	(void) door_return((char *)&result, sizeof (struct mapid_res), NULL, 0);
183 }
184 
185 /* ARGSUSED1 */
186 void
187 nfsmapid_uid_str(struct mapid_arg *argp, size_t arg_size)
188 {
189 	struct mapid_res	 result;
190 	struct mapid_res	*resp;
191 	struct passwd		 pwd;
192 	struct passwd		 *pwd_ptr;
193 	char			*pwd_buf = NULL;
194 	char			*idmap_buf = NULL;
195 	uid_t			 uid = argp->u_arg.uid;
196 	size_t			 uid_str_len;
197 	char			*pw_str;
198 	size_t			 pw_str_len;
199 	char			*at_str;
200 	size_t			 at_str_len;
201 	char			 dom_str[DNAMEMAX];
202 	size_t			 dom_str_len;
203 	idmap_stat		 rc;
204 
205 	if (uid == (uid_t)-1) {
206 		/*
207 		 * Sentinel uid is not a valid id
208 		 */
209 		resp = &result;
210 		resp->status = NFSMAPID_BADID;
211 		resp->u_res.len = 0;
212 		goto done;
213 	}
214 
215 	/*
216 	 * Make local copy of domain for further manipuation
217 	 * NOTE: mapid_get_domain() returns a ptr to TSD.
218 	 */
219 	if (cur_domain_null()) {
220 		dom_str_len = 0;
221 		dom_str[0] = '\0';
222 	} else {
223 		dom_str_len = strlcpy(dom_str, mapid_get_domain(), DNAMEMAX);
224 	}
225 
226 	/*
227 	 * If uid is ephemeral then resolve it using idmap service
228 	 */
229 	if (uid > UID_MAX) {
230 		rc = idmap_getwinnamebyuid(uid, &idmap_buf, NULL);
231 		if (rc != IDMAP_SUCCESS) {
232 			/*
233 			 * We don't put stringified ephemeral uids on
234 			 * the wire.
235 			 */
236 			resp = &result;
237 			resp->status = NFSMAPID_UNMAPPABLE;
238 			resp->u_res.len = 0;
239 			goto done;
240 		}
241 
242 		/*
243 		 * idmap_buf is already in the desired form i.e. name@domain
244 		 */
245 		pw_str = idmap_buf;
246 		pw_str_len = strlen(pw_str);
247 		at_str_len = dom_str_len = 0;
248 		at_str = "";
249 		dom_str[0] = '\0';
250 		goto gen_result;
251 	}
252 
253 	/*
254 	 * Handling non-ephemeral uids
255 	 *
256 	 * We want to encode the uid into a literal string... :
257 	 *
258 	 *	- upon failure to allocate space from the heap
259 	 *	- if there is no current domain configured
260 	 *	- if there is no such uid in the passwd DB's
261 	 */
262 	if ((pwd_buf = malloc(pwd_buflen)) == NULL || dom_str_len == 0 ||
263 	    getpwuid_r(uid, &pwd, pwd_buf, pwd_buflen, &pwd_ptr) != 0 ||
264 	    pwd_ptr == NULL) {
265 
266 		/*
267 		 * If we could not allocate from the heap, try
268 		 * allocating from the stack as a last resort.
269 		 */
270 		if (pwd_buf == NULL && (pwd_buf =
271 		    alloca(MAPID_RES_LEN(UID_MAX_STR_LEN))) == NULL) {
272 			resp = &result;
273 			resp->status = NFSMAPID_INTERNAL;
274 			resp->u_res.len = 0;
275 			goto done;
276 		}
277 
278 		/*
279 		 * Constructing literal string without '@' so that
280 		 * we'll know that it's not a user, but rather a
281 		 * uid encoded string.
282 		 */
283 		pw_str = pwd_buf;
284 		(void) sprintf(pw_str, "%u", uid);
285 		pw_str_len = strlen(pw_str);
286 		at_str_len = dom_str_len = 0;
287 		at_str = "";
288 		dom_str[0] = '\0';
289 	} else {
290 		/*
291 		 * Otherwise, we construct the "user@domain" string if
292 		 * it's not already in that form.
293 		 */
294 		pw_str = pwd.pw_name;
295 		pw_str_len = strlen(pw_str);
296 		if (strchr(pw_str, '@') == NULL) {
297 			at_str = "@";
298 			at_str_len = 1;
299 		} else {
300 			at_str_len = dom_str_len = 0;
301 			at_str = "";
302 			dom_str[0] = '\0';
303 		}
304 	}
305 
306 gen_result:
307 	uid_str_len = pw_str_len + at_str_len + dom_str_len;
308 	if ((resp = alloca(MAPID_RES_LEN(uid_str_len))) == NULL) {
309 		resp = &result;
310 		resp->status = NFSMAPID_INTERNAL;
311 		resp->u_res.len = 0;
312 		goto done;
313 	}
314 	/* LINTED format argument to sprintf */
315 	(void) sprintf(resp->str, "%s%s%s", pw_str, at_str, dom_str);
316 	resp->u_res.len = uid_str_len;
317 	if (pwd_buf)
318 		free(pwd_buf);
319 	if (idmap_buf)
320 		idmap_free(idmap_buf);
321 	resp->status = NFSMAPID_OK;
322 
323 done:
324 	/*
325 	 * There is a chance that the door_return will fail because the
326 	 * resulting string is too large, try to indicate that if possible
327 	 */
328 	if (door_return((char *)resp,
329 	    MAPID_RES_LEN(resp->u_res.len), NULL, 0) == -1) {
330 		resp->status = NFSMAPID_INTERNAL;
331 		resp->u_res.len = 0;
332 		(void) door_return((char *)&result, sizeof (struct mapid_res),
333 		    NULL, 0);
334 	}
335 }
336 
337 void
338 nfsmapid_str_gid(struct mapid_arg *argp, size_t arg_size)
339 {
340 	struct mapid_res	result;
341 	struct group		grp;
342 	struct group		*grp_ptr;
343 	int			grp_rc;
344 	char			*grp_buf;
345 	char			*group;
346 	char			*domain;
347 	idmap_stat		rc;
348 
349 	if (argp->u_arg.len <= 0 ||
350 	    arg_size < MAPID_ARG_LEN(argp->u_arg.len)) {
351 		result.status = NFSMAPID_INVALID;
352 		result.u_res.gid = GID_NOBODY;
353 		goto done;
354 	}
355 
356 	if (!extract_domain(argp->str, &group, &domain)) {
357 		unsigned long id;
358 
359 		/*
360 		 * Invalid "group@domain" string. Still, the
361 		 * group part might be an encoded gid, so do a
362 		 * final check. Remember, domain part of string
363 		 * was not set since not a valid string.
364 		 */
365 		if (!validate_id_str(group)) {
366 			result.status = NFSMAPID_UNMAPPABLE;
367 			result.u_res.gid = GID_NOBODY;
368 			goto done;
369 		}
370 
371 		errno = 0;
372 		id = strtoul(group, (char **)NULL, 10);
373 
374 		/*
375 		 * We don't accept ephemeral ids from the wire.
376 		 */
377 		if (errno || id > UID_MAX) {
378 			result.status = NFSMAPID_UNMAPPABLE;
379 			result.u_res.gid = GID_NOBODY;
380 			goto done;
381 		}
382 
383 		result.u_res.gid = (gid_t)id;
384 		result.status = NFSMAPID_NUMSTR;
385 		goto done;
386 	}
387 
388 	/*
389 	 * String properly constructed. Now we check for domain and
390 	 * group validity.
391 	 */
392 	if (!cur_domain_null() && !valid_domain(domain)) {
393 		/*
394 		 * If the domain part of the string does not
395 		 * match the NFS domain, try to map it using
396 		 * idmap service.
397 		 */
398 		rc = idmap_getgidbywinname(group, domain, &result.u_res.gid);
399 		if (rc != IDMAP_SUCCESS) {
400 			result.status = NFSMAPID_BADDOMAIN;
401 			result.u_res.gid = GID_NOBODY;
402 			goto done;
403 		}
404 		result.status = NFSMAPID_OK;
405 		goto done;
406 	}
407 
408 	if ((grp_buf = malloc(grp_buflen)) == NULL ||
409 	    (grp_rc = getgrnam_r(group, &grp, grp_buf, grp_buflen, &grp_ptr))
410 	    != 0 || grp_ptr == NULL) {
411 
412 		if (grp_buf == NULL || grp_rc != 0)
413 			result.status = NFSMAPID_INTERNAL;
414 		else {
415 			/*
416 			 * Not a valid group
417 			 */
418 			result.status = NFSMAPID_NOTFOUND;
419 			free(grp_buf);
420 		}
421 		result.u_res.gid = GID_NOBODY;
422 		goto done;
423 	}
424 
425 	/*
426 	 * Valid group entry
427 	 */
428 	result.status = NFSMAPID_OK;
429 	result.u_res.gid = grp.gr_gid;
430 	free(grp_buf);
431 done:
432 	(void) door_return((char *)&result, sizeof (struct mapid_res), NULL, 0);
433 }
434 
435 /* ARGSUSED1 */
436 void
437 nfsmapid_gid_str(struct mapid_arg *argp, size_t arg_size)
438 {
439 	struct mapid_res	 result;
440 	struct mapid_res	*resp;
441 	struct group		 grp;
442 	struct group		*grp_ptr;
443 	char			*grp_buf = NULL;
444 	char			*idmap_buf = NULL;
445 	idmap_stat		 rc;
446 	gid_t			 gid = argp->u_arg.gid;
447 	size_t			 gid_str_len;
448 	char			*gr_str;
449 	size_t			 gr_str_len;
450 	char			*at_str;
451 	size_t			 at_str_len;
452 	char			 dom_str[DNAMEMAX];
453 	size_t			 dom_str_len;
454 
455 	if (gid == (gid_t)-1) {
456 		/*
457 		 * Sentinel gid is not a valid id
458 		 */
459 		resp = &result;
460 		resp->status = NFSMAPID_BADID;
461 		resp->u_res.len = 0;
462 		goto done;
463 	}
464 
465 	/*
466 	 * Make local copy of domain for further manipuation
467 	 * NOTE: mapid_get_domain() returns a ptr to TSD.
468 	 */
469 	if (cur_domain_null()) {
470 		dom_str_len = 0;
471 		dom_str[0] = '\0';
472 	} else {
473 		dom_str_len = strlen(mapid_get_domain());
474 		bcopy(mapid_get_domain(), dom_str, dom_str_len);
475 		dom_str[dom_str_len] = '\0';
476 	}
477 
478 	/*
479 	 * If gid is ephemeral then resolve it using idmap service
480 	 */
481 	if (gid > UID_MAX) {
482 		rc = idmap_getwinnamebygid(gid, &idmap_buf, NULL);
483 		if (rc != IDMAP_SUCCESS) {
484 			/*
485 			 * We don't put stringified ephemeral gids on
486 			 * the wire.
487 			 */
488 			resp = &result;
489 			resp->status = NFSMAPID_UNMAPPABLE;
490 			resp->u_res.len = 0;
491 			goto done;
492 		}
493 
494 		/*
495 		 * idmap_buf is already in the desired form i.e. name@domain
496 		 */
497 		gr_str = idmap_buf;
498 		gr_str_len = strlen(gr_str);
499 		at_str_len = dom_str_len = 0;
500 		at_str = "";
501 		dom_str[0] = '\0';
502 		goto gen_result;
503 	}
504 
505 	/*
506 	 * Handling non-ephemeral gids
507 	 *
508 	 * We want to encode the gid into a literal string... :
509 	 *
510 	 *	- upon failure to allocate space from the heap
511 	 *	- if there is no current domain configured
512 	 *	- if there is no such gid in the group DB's
513 	 */
514 	if ((grp_buf = malloc(grp_buflen)) == NULL || dom_str_len == 0 ||
515 	    getgrgid_r(gid, &grp, grp_buf, grp_buflen, &grp_ptr) != 0 ||
516 	    grp_ptr == NULL) {
517 
518 		/*
519 		 * If we could not allocate from the heap, try
520 		 * allocating from the stack as a last resort.
521 		 */
522 		if (grp_buf == NULL && (grp_buf =
523 		    alloca(MAPID_RES_LEN(UID_MAX_STR_LEN))) == NULL) {
524 			resp = &result;
525 			resp->status = NFSMAPID_INTERNAL;
526 			resp->u_res.len = 0;
527 			goto done;
528 		}
529 
530 		/*
531 		 * Constructing literal string without '@' so that
532 		 * we'll know that it's not a group, but rather a
533 		 * gid encoded string.
534 		 */
535 		gr_str = grp_buf;
536 		(void) sprintf(gr_str, "%u", gid);
537 		gr_str_len = strlen(gr_str);
538 		at_str_len = dom_str_len = 0;
539 		at_str = "";
540 		dom_str[0] = '\0';
541 	} else {
542 		/*
543 		 * Otherwise, we construct the "group@domain" string if
544 		 * it's not already in that form.
545 		 */
546 		gr_str = grp.gr_name;
547 		gr_str_len = strlen(gr_str);
548 		if (strchr(gr_str, '@') == NULL) {
549 			at_str = "@";
550 			at_str_len = 1;
551 		} else {
552 			at_str_len = dom_str_len = 0;
553 			at_str = "";
554 			dom_str[0] = '\0';
555 		}
556 	}
557 
558 gen_result:
559 	gid_str_len = gr_str_len + at_str_len + dom_str_len;
560 	if ((resp = alloca(MAPID_RES_LEN(gid_str_len))) == NULL) {
561 		resp = &result;
562 		resp->status = NFSMAPID_INTERNAL;
563 		resp->u_res.len = 0;
564 		goto done;
565 	}
566 	/* LINTED format argument to sprintf */
567 	(void) sprintf(resp->str, "%s%s%s", gr_str, at_str, dom_str);
568 	resp->u_res.len = gid_str_len;
569 	if (grp_buf)
570 		free(grp_buf);
571 	if (idmap_buf)
572 		idmap_free(idmap_buf);
573 	resp->status = NFSMAPID_OK;
574 
575 done:
576 	/*
577 	 * There is a chance that the door_return will fail because the
578 	 * resulting string is too large, try to indicate that if possible
579 	 */
580 	if (door_return((char *)resp,
581 	    MAPID_RES_LEN(resp->u_res.len), NULL, 0) == -1) {
582 		resp->status = NFSMAPID_INTERNAL;
583 		resp->u_res.len = 0;
584 		(void) door_return((char *)&result, sizeof (struct mapid_res),
585 		    NULL, 0);
586 	}
587 }
588 
589 /* ARGSUSED */
590 void
591 nfsmapid_func(void *cookie, char *argp, size_t arg_size,
592 						door_desc_t *dp, uint_t n_desc)
593 {
594 	struct mapid_arg	*mapargp;
595 	struct mapid_res	mapres;
596 
597 	/*
598 	 * Make sure we have a valid argument
599 	 */
600 	if (arg_size < sizeof (struct mapid_arg)) {
601 		mapres.status = NFSMAPID_INVALID;
602 		mapres.u_res.len = 0;
603 		(void) door_return((char *)&mapres, sizeof (struct mapid_res),
604 		    NULL, 0);
605 		return;
606 	}
607 
608 	/* LINTED pointer cast */
609 	mapargp = (struct mapid_arg *)argp;
610 	switch (mapargp->cmd) {
611 	case NFSMAPID_STR_UID:
612 		nfsmapid_str_uid(mapargp, arg_size);
613 		return;
614 	case NFSMAPID_UID_STR:
615 		nfsmapid_uid_str(mapargp, arg_size);
616 		return;
617 	case NFSMAPID_STR_GID:
618 		nfsmapid_str_gid(mapargp, arg_size);
619 		return;
620 	case NFSMAPID_GID_STR:
621 		nfsmapid_gid_str(mapargp, arg_size);
622 		return;
623 	default:
624 		break;
625 	}
626 	mapres.status = NFSMAPID_INVALID;
627 	mapres.u_res.len = 0;
628 	(void) door_return((char *)&mapres, sizeof (struct mapid_res), NULL, 0);
629 }
630 
631 /*
632  * mapid_get_domain() always returns a ptr to TSD, so the
633  * check for a NULL domain is not a simple comparison with
634  * NULL but we need to check the contents of the TSD data.
635  */
636 int
637 cur_domain_null(void)
638 {
639 	char	*p;
640 
641 	if ((p = mapid_get_domain()) == NULL)
642 		return (1);
643 
644 	return (p[0] == '\0');
645 }
646 
647 int
648 extract_domain(char *cp, char **upp, char **dpp)
649 {
650 	/*
651 	 * Caller must insure that the string is valid
652 	 */
653 	*upp = cp;
654 
655 	if ((*dpp = strchr(cp, '@')) == NULL)
656 		return (0);
657 	*(*dpp)++ = '\0';
658 	return (1);
659 }
660 
661 int
662 valid_domain(const char *dom)
663 {
664 	const char	*whoami = "valid_domain";
665 
666 	if (!mapid_stdchk_domain(dom)) {
667 		syslog(LOG_ERR, gettext("%s: Invalid inbound domain name %s."),
668 		    whoami, dom);
669 		return (0);
670 	}
671 
672 	/*
673 	 * NOTE: mapid_get_domain() returns a ptr to TSD.
674 	 */
675 	return (strcasecmp(dom, mapid_get_domain()) == 0);
676 }
677 
678 int
679 validate_id_str(const char *id)
680 {
681 	while (*id) {
682 		if (!isdigit(*id++))
683 			return (0);
684 	}
685 	return (1);
686 }
687 
688 void
689 idmap_kcall(int door_id)
690 {
691 	struct nfsidmap_args args;
692 
693 	if (door_id >= 0) {
694 		args.state = 1;
695 		args.did = door_id;
696 	} else {
697 		args.state = 0;
698 		args.did = 0;
699 	}
700 	(void) _nfssys(NFS_IDMAP, &args);
701 }
702 
703 /*
704  * Get the current NFS domain.
705  *
706  * If NFSMAPID_DOMAIN is set in /etc/default/nfs, then it is the NFS domain;
707  * otherwise, the DNS domain is used.
708  */
709 void
710 check_domain(int sighup)
711 {
712 	const char	*whoami = "check_domain";
713 	static int	 setup_done = 0;
714 	static cb_t	 cb;
715 
716 	/*
717 	 * Construct the arguments to be passed to libmapid interface
718 	 * If called in response to a SIGHUP, reset any cached DNS TXT
719 	 * RR state.
720 	 */
721 	cb.fcn = cb_update_domain;
722 	cb.signal = sighup;
723 	mapid_reeval_domain(&cb);
724 
725 	/*
726 	 * Restart the signal handler thread if we're still setting up
727 	 */
728 	if (!setup_done) {
729 		setup_done = 1;
730 		if (thr_continue(sig_thread)) {
731 			syslog(LOG_ERR, gettext("%s: Fatal error: signal "
732 			    "handler thread could not be restarted."), whoami);
733 			exit(6);
734 		}
735 	}
736 }
737 
738 /*
739  * Need to be able to open the DIAG_FILE before nfsmapid(1m)
740  * releases it's root priviledges. The DIAG_FILE then remains
741  * open for the duration of this nfsmapid instance via n4_fd.
742  */
743 void
744 open_diag_file()
745 {
746 	static int	msg_done = 0;
747 
748 	if ((n4_fp = fopen(DIAG_FILE, "w+")) != NULL) {
749 		n4_fd = fileno(n4_fp);
750 		return;
751 	}
752 
753 	if (msg_done)
754 		return;
755 
756 	syslog(LOG_ERR, "Failed to create %s. Enable syslog "
757 	    "daemon.debug for more info", DIAG_FILE);
758 	msg_done = 1;
759 }
760 
761 /*
762  * When a new domain name is configured, save to DIAG_FILE
763  * and log to syslog, with LOG_DEBUG level (if configured).
764  */
765 void
766 update_diag_file(char *new)
767 {
768 	char	buf[DNAMEMAX];
769 	ssize_t	n;
770 	size_t	len;
771 
772 	(void) lseek(n4_fd, (off_t)0, SEEK_SET);
773 	(void) ftruncate(n4_fd, 0);
774 	(void) snprintf(buf, DNAMEMAX, "%s\n", new);
775 
776 	len = strlen(buf);
777 	n = write(n4_fd, buf, len);
778 	if (n < 0 || n < len)
779 		syslog(LOG_DEBUG, "Could not write %s to diag file", new);
780 	(void) fsync(n4_fd);
781 
782 	syslog(LOG_DEBUG, "nfsmapid domain = %s", new);
783 }
784 
785 /*
786  * Callback function for libmapid. This will be called
787  * by the lib, everytime the nfsmapid(1m) domain changes.
788  */
789 void *
790 cb_update_domain(void *arg)
791 {
792 	char	*new_dname = (char *)arg;
793 
794 	DTRACE_PROBE1(nfsmapid, daemon__domain, new_dname);
795 	update_diag_file(new_dname);
796 	idmap_kcall(FLUSH_KCACHES_ONLY);
797 
798 	return (NULL);
799 }
800