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