xref: /titanic_41/usr/src/cmd/nscd/getnode.c (revision fd9cb95cbb2f626355a60efb9d02c5f0a33c10e6)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Routines to handle getipnode* calls in nscd. Note that the
31  * getnodeby* APIs were renamed getipnodeby*. The interfaces
32  * related to them in the nscd will remain as getnode*.
33  */
34 
35 #include <assert.h>
36 #include <errno.h>
37 #include <memory.h>
38 #include <signal.h>
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <strings.h>
43 #include <sys/door.h>
44 #include <sys/stat.h>
45 #include <sys/time.h>
46 #include <sys/types.h>
47 #include <sys/wait.h>
48 #include <thread.h>
49 #include <unistd.h>
50 #include <ucred.h>
51 #include <nss_common.h>
52 #include <inet/ip6.h>
53 
54 #include "getxby_door.h"
55 #include "server_door.h"
56 #include "nscd.h"
57 
58 static hash_t *addr_hash;		/* node address hash */
59 static hash_t *nnam_hash;		/* node name hash */
60 static mutex_t  node_lock = DEFAULTMUTEX;
61 static waiter_t node_wait;
62 
63 static void getnode_addrkeepalive(int keep, int interval);
64 static void getnode_invalidate_unlocked(void);
65 static void getnode_namekeepalive(int keep, int interval);
66 static void update_node_bucket(nsc_bucket_t ** old, nsc_bucket_t *new,
67 		int callnumber);
68 static nsc_bucket_t *fixbuffer(nsc_return_t *in, int maxlen);
69 static void do_findnaddrs(nsc_bucket_t *ptr, int *table, char *addr);
70 static void do_findnnams(nsc_bucket_t *ptr, int *table, char *name);
71 static void do_invalidate(nsc_bucket_t ** ptr, int callnumber);
72 
73 /*
74  * Create a combined key
75  * combined key =
76  * name + ":" + af_family(in %d format) + ":" + flags(in %d format)
77  * name = foo, af_family = 26, flags = 3, addrconfig = 1
78  * key = "foo:26:3"
79  * The string is allocated and caller has to free it.
80  *
81  * Insert ":" in between to ensure the uniqueness of the key.
82  *
83  * af_family: [2 | 26] ([AF_INET  | AF_INET6])
84  * flags: 0 - strict
85  *	  1 - AI_V4MAPPED
86  *	  3 - (AI_V4MAPPED | AI_ALL)
87  *	  4 - AI_ADDRCONFIG
88  *	  5 - AI_DEFAULT (AI_V4MAPPED | AI_ADDRCONFIG)
89  *	  7 - (AI_V4MAPPED | AI_ALL | AI_ADDRCONFIG)
90  */
91 static char *
92 get_ipnode_combined_key(nsc_call_t *in) {
93 	int	len;
94 	char	*key;
95 	char	af_str[8];		/* in %d  format */
96 	char	flags_str[8];		/* in %d  format */
97 
98 	snprintf(af_str, 8, "%d", in->nsc_u.ipnode.af_family);
99 	snprintf(flags_str, 8, "%d", in->nsc_u.ipnode.flags);
100 	/* name + ":" + af_str + ":" + flags_str + 1 */
101 	len = strlen(in->nsc_u.ipnode.name) + strlen(af_str) +
102 		strlen(flags_str) +  3;
103 
104 	if ((key = (char *)malloc(len)) == NULL)
105 		return (NULL);
106 
107 	snprintf(key, len, "%s:%s:%s", in->nsc_u.ipnode.name, af_str,
108 		flags_str);
109 	return (key);
110 }
111 /*
112  * Parse key and copy the values to out
113  */
114 static void
115 copy_ipnode_combined_key(char *key, nsc_call_t *out) {
116 	char	*token, *lasts;
117 
118 	if ((token = strtok_r(key, ":", &lasts)) != NULL) {
119 		/* copy name */
120 		(void) strncpy(out->nsc_u.ipnode.name, token,
121 						NSCDMAXNAMELEN);
122 		if ((token = strtok_r(NULL, ":", &lasts)) != NULL) {
123 			/* Copy af_family */
124 			out->nsc_u.ipnode.af_family = atoi(token);
125 
126 			if ((token = strtok_r(NULL, ":", &lasts)) != NULL) {
127 				/* Copy flags */
128 				out->nsc_u.ipnode.flags = atoi(token);
129 			}
130 		}
131 	}
132 }
133 
134 static void
135 safe_inet_ntop(int af, const void  *addr,  char  *cp, size_t size)
136 {
137 	if (inet_ntop(af, addr, cp, size) != cp) {
138 		/*
139 		 * Called only for logging purposes...
140 		 * this should never happen in the nscd... but
141 		 * just in case we'll make sure we have a
142 		 * plausible error message.
143 		 */
144 		(void) snprintf(cp, size, "<inet_ntop returned %s>",
145 		    strerror(errno));
146 	}
147 }
148 
149 void
150 getnode_init(void)
151 {
152 	addr_hash = make_hash(current_admin.node.nsc_suggestedsize);
153 	nnam_hash = make_hash(current_admin.node.nsc_suggestedsize);
154 }
155 
156 static void
157 do_invalidate(nsc_bucket_t ** ptr, int callnumber)
158 {
159 	if (*ptr != NULL && *ptr != (nsc_bucket_t *)-1) {
160 		/* leave pending calls alone */
161 		update_node_bucket(ptr, NULL, callnumber);
162 	}
163 }
164 
165 static void
166 do_findnnams(nsc_bucket_t *ptr, int *table, char *name)
167 {
168 	/*
169 	 * be careful with ptr - it may be -1 or NULL.
170 	 */
171 
172 	if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
173 		/* leave pending calls alone */
174 		char *tmp = (char *)insertn(table, ptr->nsc_hits,
175 			(int)strdup(name));
176 		if (tmp != (char *)-1)
177 			free(tmp);
178 	}
179 }
180 
181 static void
182 do_findnaddrs(nsc_bucket_t *ptr, int *table, char *addr)
183 {
184 
185 	if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
186 
187 		/* leave pending calls alone */
188 		char *tmp = (char *)insertn(table, ptr->nsc_hits,
189 			(int)strdup(addr));
190 		if (tmp != (char *)-1)
191 			free(tmp);
192 	}
193 }
194 
195 void
196 getnode_revalidate(void)
197 {
198 	for (;;) {
199 		int slp;
200 		int interval;
201 		int count;
202 
203 		slp = current_admin.node.nsc_pos_ttl;
204 
205 		if (slp < 60)
206 			slp = 60;
207 
208 		if ((count = current_admin.node.nsc_keephot) != 0) {
209 			interval = (slp/2)/count;
210 			if (interval == 0) interval = 1;
211 			(void) sleep(slp*2/3);
212 			getnode_namekeepalive(count, interval);
213 			getnode_addrkeepalive(count, interval);
214 		} else {
215 			(void) sleep(slp);
216 		}
217 	}
218 }
219 
220 static void
221 getnode_namekeepalive(int keep, int interval)
222 {
223 	int *table;
224 	union {
225 		nsc_data_t  ping;
226 		char space[sizeof (nsc_data_t) + NSCDMAXNAMELEN];
227 	} u;
228 
229 	int i;
230 
231 	if (!keep)
232 		return;
233 
234 	table = maken(keep);
235 	(void) mutex_lock(&node_lock);
236 	operate_hash(nnam_hash, do_findnnams, (char *)table);
237 	(void) mutex_unlock(&node_lock);
238 
239 	for (i = 1; i <= keep; i++) {
240 		char *tmp;
241 		u.ping.nsc_call.nsc_callnumber = GETIPNODEBYNAME;
242 
243 		if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1)
244 			continue; /* unused slot in table */
245 		if (current_admin.debug_level >= DBG_ALL)
246 			logit("keepalive: reviving node %s\n", tmp);
247 		copy_ipnode_combined_key(tmp, &u.ping.nsc_call);
248 
249 		launch_update(&u.ping.nsc_call);
250 		(void) sleep(interval);
251 	}
252 
253 	for (i = 1; i <= keep; i++) {
254 		char *tmp;
255 		if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1)
256 			free(tmp);
257 	}
258 
259 	free(table);
260 }
261 
262 static void
263 getnode_addrkeepalive(int keep, int interval)
264 {
265 	int *table;
266 	union {
267 		nsc_data_t  ping;
268 		char space[sizeof (nsc_data_t) + 80];
269 	} u;
270 
271 	int i;
272 
273 	if (!keep)
274 		return;
275 
276 	table = maken(keep);
277 	(void) mutex_lock(&node_lock);
278 	operate_hash(addr_hash, do_findnaddrs, (char *)table);
279 	(void) mutex_unlock(&node_lock);
280 
281 	for (i = 1; i <= keep; i++) {
282 		char *tmp;
283 		char addr[IPV6_ADDR_LEN];
284 
285 		u.ping.nsc_call.nsc_callnumber = GETIPNODEBYADDR;
286 
287 		if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1)
288 			continue; /* enused slot in table */
289 		u.ping.nsc_call.nsc_u.addr.a_type = AF_INET6;
290 		u.ping.nsc_call.nsc_u.addr.a_length = IPV6_ADDR_LEN;
291 		if (inet_pton(AF_INET6, (const char *) tmp, (void *) addr) !=
292 		    1)
293 			continue; /* illegal address - skip it */
294 		else {
295 			(void) memcpy(u.ping.nsc_call.nsc_u.addr.a_data,
296 			    addr, IPV6_ADDR_LEN);
297 			if (current_admin.debug_level >= DBG_ALL)
298 				logit("keepalive: reviving address %s\n", tmp);
299 			launch_update(&u.ping.nsc_call);
300 		}
301 		(void) sleep(interval);
302 	}
303 
304 	for (i = 1; i <= keep; i++) {
305 		char *tmp;
306 		if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1)
307 		free(tmp);
308 	}
309 
310 	free(table);
311 }
312 
313 /*
314  *   This routine marks all entries as invalid
315  *
316  */
317 
318 void
319 getnode_invalidate(void)
320 {
321 	(void) mutex_lock(&node_lock);
322 	getnode_invalidate_unlocked();
323 	(void) mutex_unlock(&node_lock);
324 }
325 
326 static void
327 getnode_invalidate_unlocked(void)
328 {
329 	operate_hash_addr(nnam_hash, do_invalidate, (char *)GETIPNODEBYNAME);
330 	operate_hash_addr(addr_hash, do_invalidate, (char *)GETIPNODEBYADDR);
331 	current_admin.node.nsc_invalidate_count++;
332 }
333 
334 /*
335  * Mark only the ipnodebyname cache entries as invalid
336  */
337 
338 void
339 getnode_name_invalidate(void)
340 {
341 	(void) mutex_lock(&node_lock);
342 	operate_hash_addr(nnam_hash, do_invalidate, (char *)GETIPNODEBYNAME);
343 	current_admin.node.nsc_invalidate_count++;
344 	(void) mutex_unlock(&node_lock);
345 }
346 
347 void
348 getnode_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in, time_t now)
349 {
350 	int		out_of_date;
351 	nsc_bucket_t	*retb;
352 	char 		**bucket, *key;
353 
354 	static time_t	lastmod;
355 
356 	int bufferspace = maxsize - sizeof (nsc_return_t);
357 
358 	if (current_admin.node.nsc_enabled == 0) {
359 		out->nsc_return_code = NOSERVER;
360 		out->nsc_bufferbytesused = sizeof (*out);
361 		return;
362 	}
363 
364 	(void) mutex_lock(&node_lock);
365 
366 	if (current_admin.node.nsc_check_files) {
367 		struct stat buf;
368 
369 		if (stat("/etc/inet/ipnodes", &buf) < 0) {
370 			/*EMPTY*/;
371 		} else if (lastmod == 0) {
372 			lastmod = buf.st_mtime;
373 		} else if (lastmod < buf.st_mtime) {
374 			getnode_invalidate_unlocked();
375 			lastmod = buf.st_mtime;
376 		}
377 	}
378 
379 
380 	if (current_admin.debug_level >= DBG_ALL) {
381 		if (MASKUPDATEBIT(in->nsc_callnumber) == GETIPNODEBYADDR) {
382 			char addr[INET6_ADDRSTRLEN];
383 			safe_inet_ntop(AF_INET6,
384 			    (const void *)in->nsc_u.addr.a_data, addr,
385 			    sizeof (addr));
386 			logit("getnode_lookup: looking for address %s\n", addr);
387 		} else {
388 			logit("getnode_lookup: looking for nodename %s:%d:%d\n",
389 				in->nsc_u.ipnode.name,
390 				in->nsc_u.ipnode.af_family,
391 				in->nsc_u.ipnode.flags);
392 		}
393 	}
394 
395 	for (;;) {
396 		if (MASKUPDATEBIT(in->nsc_callnumber) == GETIPNODEBYADDR) {
397 			char addr[INET6_ADDRSTRLEN];
398 			if (inet_ntop(AF_INET6,
399 			    (const void *)in->nsc_u.addr.a_data,
400 			    addr, sizeof (addr)) == NULL) {
401 				out->nsc_errno = NSS_NOTFOUND;
402 				out->nsc_return_code = NOTFOUND;
403 				out->nsc_bufferbytesused = sizeof (*out);
404 				goto getout;
405 			}
406 			bucket = get_hash(addr_hash, addr);
407 		} else { /* bounce excessively long requests */
408 			if (strlen(in->nsc_u.ipnode.name) > NSCDMAXNAMELEN) {
409 				ucred_t *uc = NULL;
410 
411 				if (door_ucred(&uc) != 0) {
412 					logit("getnode_lookup: Name too long, "
413 					    "but no user credential: %s\n",
414 					    strerror(errno));
415 				} else {
416 					logit("getnode_lookup: Name too long "
417 					    "from pid %d uid %d\n",
418 					    ucred_getpid(uc),
419 					    ucred_getruid(uc));
420 					ucred_free(uc);
421 				}
422 
423 				out->nsc_errno = NSS_NOTFOUND;
424 				out->nsc_return_code = NOTFOUND;
425 				out->nsc_bufferbytesused = sizeof (*out);
426 				goto getout;
427 			}
428 			if ((key = get_ipnode_combined_key(in)) == NULL) {
429 
430 				out->nsc_errno = NSS_NOTFOUND;
431 				out->nsc_return_code = NOTFOUND;
432 				out->nsc_bufferbytesused = sizeof (*out);
433 				goto getout;
434 			}
435 
436 			bucket = get_hash(nnam_hash, key);
437 			free(key);
438 		}
439 
440 		if (*bucket == (char *)-1) {	/* pending lookup */
441 			if (get_clearance(in->nsc_callnumber) != 0) {
442 				/* no threads available */
443 				out->nsc_return_code = NOSERVER;
444 				/* cannot process now */
445 				out->nsc_bufferbytesused = sizeof (*out);
446 				current_admin.node.nsc_throttle_count++;
447 				goto getout;
448 			}
449 			nscd_wait(&node_wait, &node_lock, bucket);
450 			release_clearance(in->nsc_callnumber);
451 			continue; /* go back and relookup hash bucket */
452 		}
453 		break;
454 	}
455 
456 	/*
457 	 * check for no name_service mode
458 	 */
459 
460 	if (*bucket == NULL && current_admin.avoid_nameservice) {
461 		out->nsc_return_code = NOTFOUND;
462 		out->nsc_bufferbytesused = sizeof (*out);
463 	} else if ((*bucket == NULL) ||	/* New entry in name service */
464 	    (in->nsc_callnumber & UPDATEBIT) || /* needs updating */
465 	    (out_of_date = (!current_admin.avoid_nameservice &&
466 		(current_admin.node.nsc_old_data_ok == 0) &&
467 		(((nsc_bucket_t *)*bucket)->nsc_timestamp < now)))) {
468 		/* time has expired */
469 		int saved_errno;
470 		int saved_hits = 0;
471 		struct hostent *p;
472 
473 		if (get_clearance(in->nsc_callnumber) != 0) {
474 			/* no threads available */
475 			out->nsc_return_code = NOSERVER;
476 			/* cannot process now */
477 			out->nsc_bufferbytesused = sizeof (* out);
478 			current_admin.node.nsc_throttle_count++;
479 			goto getout;
480 		}
481 
482 		if (*bucket != NULL) {
483 			saved_hits = ((nsc_bucket_t *)*bucket)->nsc_hits;
484 		}
485 
486 		/*
487 		 * block any threads accessing this bucket if data is
488 		 * non-existent or out of date
489 		 */
490 
491 		if (*bucket == NULL || out_of_date) {
492 			update_node_bucket((nsc_bucket_t **)bucket,
493 					(nsc_bucket_t *)-1,
494 					in->nsc_callnumber);
495 		} else {
496 		/*
497 		 * if still not -1 bucket we are doing update... mark to
498 		 * prevent pileups of threads if the name service is hanging...
499 		 */
500 			((nsc_bucket_t *)(*bucket))->nsc_status |=
501 				ST_UPDATE_PENDING;
502 			/* cleared by deletion of old data */
503 		}
504 		(void) mutex_unlock(&node_lock);
505 
506 		if (MASKUPDATEBIT(in->nsc_callnumber) == GETIPNODEBYADDR) {
507 			p = _uncached_getipnodebyaddr(in->nsc_u.addr.a_data,
508 					in->nsc_u.addr.a_length,
509 					in->nsc_u.addr.a_type,
510 					&out->nsc_u.hst,
511 					out->nsc_u.buff+sizeof (struct hostent),
512 					bufferspace,
513 					&saved_errno);
514 		} else {
515 			p = _uncached_getipnodebyname(in->nsc_u.ipnode.name,
516 					&out->nsc_u.hst,
517 					out->nsc_u.buff+sizeof (struct hostent),
518 					bufferspace,
519 					in->nsc_u.ipnode.af_family,
520 					in->nsc_u.ipnode.flags,
521 					&saved_errno);
522 		}
523 
524 		(void) mutex_lock(&node_lock);
525 
526 		release_clearance(in->nsc_callnumber);
527 
528 		if (p == NULL) { /* data not found */
529 			if (current_admin.debug_level >= DBG_CANT_FIND) {
530 				if (MASKUPDATEBIT(in->nsc_callnumber) ==
531 				    GETIPNODEBYADDR) {
532 					char addr[INET6_ADDRSTRLEN];
533 					safe_inet_ntop(AF_INET6,
534 					(const void *)in->nsc_u.addr.a_data,
535 					addr, sizeof (addr));
536 					logit("getnode_lookup: nscd COULDN'T "\
537 						"FIND address %s\n", addr);
538 				} else {
539 					logit("getnode_lookup: nscd COULDN'T "\
540 						"FIND node name %s:%d:%d\n",
541 						in->nsc_u.ipnode.name,
542 						in->nsc_u.ipnode.af_family,
543 						in->nsc_u.ipnode.flags);
544 				}
545 			}
546 
547 			if (!(UPDATEBIT & in->nsc_callnumber))
548 				current_admin.node.nsc_neg_cache_misses++;
549 
550 			retb = (nsc_bucket_t *)malloc(sizeof (nsc_bucket_t));
551 			retb->nsc_refcount = 1;
552 			retb->nsc_data.nsc_return_code = NOTFOUND;
553 			retb->nsc_data.nsc_bufferbytesused =
554 				sizeof (nsc_return_t);
555 			retb->nsc_data.nsc_errno = saved_errno;
556 			(void) memcpy(out, &(retb->nsc_data),
557 				retb->nsc_data.nsc_bufferbytesused);
558 			update_node_bucket((nsc_bucket_t **)bucket, retb,
559 				in->nsc_callnumber);
560 			goto getout;
561 		}
562 
563 		else {
564 			if (current_admin.debug_level >= DBG_ALL) {
565 				if (MASKUPDATEBIT(in->nsc_callnumber) ==
566 				    GETIPNODEBYADDR) {
567 					char addr[INET6_ADDRSTRLEN];
568 					safe_inet_ntop(AF_INET6,
569 					(const void *)in->nsc_u.addr.a_data,
570 					addr, sizeof (addr));
571 					logit("getnode_lookup: nscd FOUND "\
572 					    "addr %s\n", addr);
573 				} else {
574 					logit("getnode_lookup: nscd FOUND "\
575 					    "node name %s:%d:%d\n",
576 					    in->nsc_u.ipnode.name,
577 					    in->nsc_u.ipnode.af_family,
578 					    in->nsc_u.ipnode.flags);
579 				}
580 			}
581 			if (!(UPDATEBIT & in->nsc_callnumber))
582 			    current_admin.node.nsc_pos_cache_misses++;
583 
584 			retb = fixbuffer(out, bufferspace);
585 
586 			update_node_bucket((nsc_bucket_t **)bucket, retb,
587 				in->nsc_callnumber);
588 			if (saved_hits)
589 				retb->nsc_hits = saved_hits;
590 		}
591 	} else { 	/* found entry in cache */
592 		retb = (nsc_bucket_t *)*bucket;
593 
594 		retb->nsc_hits++;
595 
596 		(void) memcpy(out, &(retb->nsc_data),
597 			retb->nsc_data.nsc_bufferbytesused);
598 
599 		if (out->nsc_return_code == SUCCESS) {
600 			if (!(UPDATEBIT & in->nsc_callnumber))
601 			    current_admin.node.nsc_pos_cache_hits++;
602 			if (current_admin.debug_level >= DBG_ALL) {
603 				if (MASKUPDATEBIT(in->nsc_callnumber) ==
604 				    GETIPNODEBYADDR) {
605 					char addr[INET6_ADDRSTRLEN];
606 					safe_inet_ntop(AF_INET6,
607 					    (const void *)in->nsc_u.addr.a_data,
608 					    addr, sizeof (addr));
609 					logit("getnode_lookup: found address "\
610 					    "%s in cache\n", addr);
611 				} else {
612 					logit("getnode_lookup: found node "\
613 					    "name %s:%d:%d in cache\n",
614 					    in->nsc_u.ipnode.name,
615 					    in->nsc_u.ipnode.af_family,
616 					    in->nsc_u.ipnode.flags);
617 				}
618 			}
619 		} else {
620 			if (!(UPDATEBIT & in->nsc_callnumber))
621 			    current_admin.node.nsc_neg_cache_hits++;
622 			if (current_admin.debug_level >= DBG_ALL) {
623 				if (MASKUPDATEBIT(in->nsc_callnumber) ==
624 				    GETIPNODEBYADDR) {
625 					char addr[INET6_ADDRSTRLEN];
626 					safe_inet_ntop(AF_INET6,
627 					    (const void *)in->nsc_u.addr.a_data,
628 					    addr, sizeof (addr));
629 					logit("getnode_lookup: %s marked as "\
630 					    "NOT FOUND in cache.\n", addr);
631 				} else {
632 					logit("getnode_lookup: %s:%d:%d marked"\
633 					    " as NOT FOUND in cache.\n",
634 					    in->nsc_u.ipnode.name,
635 					    in->nsc_u.ipnode.af_family,
636 					    in->nsc_u.ipnode.flags);
637 				}
638 			}
639 		}
640 
641 		if ((retb->nsc_timestamp < now) &&
642 		    !(in->nsc_callnumber & UPDATEBIT) &&
643 		    !(retb->nsc_status & ST_UPDATE_PENDING)) {
644 			logit("launch update since time = %d\n",
645 			    retb->nsc_timestamp);
646 			retb->nsc_status |= ST_UPDATE_PENDING;
647 			/* cleared by deletion of old data */
648 			launch_update(in);
649 		}
650 	}
651 
652 getout:
653 
654 	(void) mutex_unlock(&node_lock);
655 }
656 
657 
658 /*ARGSUSED*/
659 static void
660 update_node_bucket(nsc_bucket_t ** old, nsc_bucket_t *new, int callnumber)
661 {
662 	if (*old != NULL && *old != (nsc_bucket_t *)-1) { /* old data exists */
663 		free(*old);
664 		current_admin.node.nsc_entries--;
665 	}
666 
667 	/*
668 	 *  we can do this before reseting *old since we're holding the lock
669 	 */
670 
671 	else if (*old == (nsc_bucket_t *)-1) {
672 		nscd_signal(&node_wait, (char **)old);
673 	}
674 
675 	*old = new;
676 
677 	if ((new != NULL) &&
678 	    (new != (nsc_bucket_t *)-1)) {
679 		/* real data, not just update pending or invalidate */
680 
681 		new->nsc_hits = 1;
682 		new->nsc_status = 0;
683 		new->nsc_refcount = 1;
684 		current_admin.node.nsc_entries++;
685 
686 		if (new->nsc_data.nsc_return_code == SUCCESS) {
687 			new->nsc_timestamp = time(NULL) +
688 				current_admin.node.nsc_pos_ttl;
689 		} else {
690 			new->nsc_timestamp = time(NULL) +
691 				current_admin.node.nsc_neg_ttl;
692 		}
693 	}
694 }
695 
696 /* Allocate a bucket to fit the data(nsc_return_t *in size) */
697 /* copy the data into the bucket and return the bucket */
698 
699 /*ARGSUSED*/
700 static nsc_bucket_t *
701 fixbuffer(nsc_return_t *in, int maxlen)
702 {
703 	nsc_return_t *out;
704 	nsc_bucket_t *retb;
705 	char *dest;
706 	char ** aliaseslist;
707 	char ** addrlist;
708 	int offset;
709 	int strs;
710 	int i;
711 	int numaliases;
712 	int numaddrs;
713 
714 	/* find out the size of the data block we're going to need */
715 
716 	strs = 1 + strlen(in->nsc_u.hst.h_name);
717 	for (numaliases = 0; in->nsc_u.hst.h_aliases[numaliases]; numaliases++)
718 		strs += 1 + strlen(in->nsc_u.hst.h_aliases[numaliases]);
719 	strs += sizeof (char *) * (numaliases+1);
720 	for (numaddrs = 0; in->nsc_u.hst.h_addr_list[numaddrs]; numaddrs++)
721 		strs += in->nsc_u.hst.h_length;
722 	strs += sizeof (char *) * (numaddrs+1+3);
723 
724 	/* allocate it and copy it in code doesn't assume packing */
725 	/* order in original buffer */
726 
727 	if ((retb = (nsc_bucket_t *)malloc(sizeof (*retb) + strs)) == NULL) {
728 		return (NULL);
729 	}
730 
731 	out = &(retb->nsc_data);
732 	out->nsc_bufferbytesused = sizeof (*in) + strs;
733 	out->nsc_return_code 	= SUCCESS;
734 	out->nsc_errno 		= 0;
735 
736 	dest = retb->nsc_data.nsc_u.buff + sizeof (struct hostent);
737 	offset = (int)dest;
738 
739 	/* allocat the h_aliases list and the h_addr_list first to align 'em. */
740 	aliaseslist = (char **)dest;
741 
742 	dest += sizeof (char *) * (numaliases+1);
743 
744 	addrlist = (char **)dest;
745 
746 	dest += sizeof (char *) * (numaddrs+1);
747 
748 	(void) strcpy(dest, in->nsc_u.hst.h_name);
749 	strs = 1 + strlen(in->nsc_u.hst.h_name);
750 	out->nsc_u.hst.h_name = dest - offset;
751 	dest += strs;
752 
753 
754 	/* fill out the h_aliases list */
755 
756 	for (i = 0; i < numaliases; i++) {
757 		(void) strcpy(dest, in->nsc_u.hst.h_aliases[i]);
758 		strs = 1 + strlen(in->nsc_u.hst.h_aliases[i]);
759 		aliaseslist[i] = dest - offset;
760 		dest += strs;
761 	}
762 	aliaseslist[i] = 0;	/* null term ptr chain */
763 
764 	out->nsc_u.hst.h_aliases = (char **)((int)aliaseslist-offset);
765 
766 	/* fill out the h_addr list */
767 
768 	dest = (char *)(((int)dest + 3) & ~3);
769 
770 	for (i = 0; i < numaddrs; i++) {
771 		(void) memcpy(dest, in->nsc_u.hst.h_addr_list[i],
772 			in->nsc_u.hst.h_length);
773 		strs = in->nsc_u.hst.h_length;
774 		addrlist[i] = dest - offset;
775 		dest += strs;
776 		dest = (char *)(((int)dest + 3) & ~3);
777 	}
778 
779 	addrlist[i] = 0;	/* null term ptr chain */
780 
781 	out->nsc_u.hst.h_addr_list = (char **)((int)addrlist-offset);
782 
783 	out->nsc_u.hst.h_length = in->nsc_u.hst.h_length;
784 	out->nsc_u.hst.h_addrtype = in->nsc_u.hst.h_addrtype;
785 
786 	(void) memcpy(in, &(retb->nsc_data),
787 	    retb->nsc_data.nsc_bufferbytesused);
788 
789 	return (retb);
790 
791 }
792 
793 void
794 getnode_nam_reaper()
795 {
796 	nsc_reaper("getnode_nam", nnam_hash, &current_admin.node, &node_lock);
797 }
798 
799 void
800 getnode_addr_reaper()
801 {
802 	nsc_reaper("getnode_addr", addr_hash, &current_admin.node, &node_lock);
803 }
804