xref: /titanic_41/usr/src/lib/libnisdb/yptol/dit_access.c (revision dfb96a4f56fb431b915bc67e5d9d5c8d4f4f6679)
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 /*
29  * DESCRIPTION: Contains top level functions to read/write to the DIT. These
30  *		are the API between the shim and the mapping system.
31  *		Things calling these should have no knowledge of LDAP. Things
32  *		called by them should have no knowledge of NIS.
33  *
34  *		Error handling here may appear to be limited but, because the
35  *		NIS protocol cannot carry meaningful information about why a
36  *		N2L operation failed, functions that don't work log
37  *		an error and then just return FAILURE.
38  *
39  */
40 
41 /*
42  * Includes. WE WANT TO USE REAL DBM FUNCTIONS SO DO NOT INCLUDE SHIM_HOOKS.H.
43  */
44 #include <unistd.h>
45 #include <syslog.h>
46 #include <ndbm.h>
47 #include <sys/systeminfo.h>
48 #include <string.h>
49 #include <lber.h>
50 #include <ldap.h>
51 #include <errno.h>
52 #include "ypsym.h"
53 #include "ypdefs.h"
54 #include "shim.h"
55 #include "../ldap_structs.h"
56 #include "../ldap_parse.h"
57 #include "../nisdb_ldap.h"
58 #include "../ldap_util.h"
59 #include "../ldap_op.h"
60 #include "../ldap_attr.h"
61 #include "../nis_parse_ldap_conf.h"
62 #include "../nisdb_mt.h"
63 #include "yptol.h"
64 #include "dit_access_utils.h"
65 #include "stdio.h"
66 
67 extern bool delete_map(char *name);
68 extern bool rename_map(char *from, char *to, bool_t secure_map);
69 
70 /* Enable standard YP code features defined in ypdefs.h */
71 USE_YP_MASTER_NAME
72 USE_YP_DOMAIN_NAME
73 USE_YP_SECURE
74 USE_YP_INTERDOMAIN
75 
76 /*
77  * Decs
78  */
79 suc_code add_special_entries(DBM *, map_ctrl *, bool_t *);
80 void free_null_terminated_list(char **list);
81 
82 
83 /*
84  * FUNCTION:    is_yptol_mode();
85  *
86  * DESCRIPTION:	Determines if we should run in N2L or traditional mode based
87  *		on the presence of the N2L mapping file. If there are problems
88  *		with the file, e.g. unreadable, this will be picked up latter.
89  *
90  * INPUTS:     	Nothing
91  *
92  * OUTPUTS:   	TRUE = Run in N2L mode
93  *		FALSE = Run in traditional mode.
94  */
95 bool_t
96 is_yptol_mode()
97 {
98 	struct stat filestat;
99 
100 	if (stat(YP_DEFAULTCONFFILE, &filestat) != -1)
101 		return (TRUE);
102 
103 	return (FALSE);
104 }
105 
106 /*
107  * FUNCTION:    read_from_dit();
108  *
109  * DESCRIPTION:	Read (i.e. get and map) a single NIS entry from the LDAP DIT.
110  *		Also handles retry attempts, on failure, and interpretation of
111  *		internal error codes.
112  *
113  * INPUTS:     	Map name (unqualified)
114  *		Domain name
115  *		Entry key
116  *		Pointer to return location
117  *
118  * OUTPUTS:   	If successful DBM datum containing result.
119  *		On error DBM datum pointing to NULL and, if the cached value
120  *		is not to be used, an error code.
121  */
122 int
123 read_from_dit(char *map, char *domain, datum *key, datum *value)
124 {
125 	int count;
126 	int res;
127 	__nisdb_retry_t	*retrieveRetry;
128 
129 	/* Initialize tsd */
130 	__nisdb_get_tsd()->domainContext = 0;
131 	__nisdb_get_tsd()->escapeFlag = '\0';
132 
133 	for (count = 0; count < ypDomains.numDomains; count++) {
134 		if (0 == ypDomains.domainLabels[count])
135 			continue;
136 		if (0 == strcasecmp(domain, ypDomains.domainLabels[count])) {
137 			__nisdb_get_tsd()->domainContext =
138 					ypDomains.domains[count];
139 			break;
140 		}
141 	}
142 
143 	retrieveRetry = &ldapDBTableMapping.retrieveErrorRetry;
144 
145 	/* Loop 'attempts' times of forever if -1 */
146 	for (count = retrieveRetry->attempts; (0 <= count) ||
147 			(-1 == retrieveRetry->attempts); count --) {
148 		if (TRUE == singleReadFromDIT(map, domain, key, value, &res))
149 			/* It worked, return value irrelevant */
150 			return (0);
151 
152 		if (LDAP_TIMEOUT == res) { /* Exceeded search timeout */
153 			value->dptr = NULL;
154 			return (0);
155 		}
156 
157 		if (is_fatal_error(res))
158 			break;
159 
160 		/*
161 		 * Didn't work. If not the special case where no repeats are
162 		 * done sleep.
163 		 */
164 		if (0 != retrieveRetry->attempts)
165 			(void) poll(NULL, 0, retrieveRetry->timeout*1000);
166 	}
167 
168 	/* Make sure returned pointer is NULL */
169 	value->dptr = NULL;
170 
171 	/* If we get here access failed work out what to return */
172 	if (ldapDBTableMapping.retrieveError == use_cached)
173 		return (0);
174 
175 	return (res);
176 }
177 
178 /*
179  * FUNCTION:    write_to_dit();
180  *
181  * DESCRIPTION:	Maps and writes a NIS entry to the LDAP DIT.
182  *		Also handles retry attempts, on failure, and interpretation of
183  *		internal error codes.
184  *
185  * INPUTS:     	Pointer to (unqualified) map name
186  *		Pointer to domain name
187  *		The entries key
188  *		What to write
189  *		Replace flag indicating
190  *			TRUE = Replace (overwrite) any existing entries
191  *			FALSE = Return error if there are existing entries
192  *		Flag indicating if we should tolerate mapping errors.
193  *
194  * OUTPUTS:   	SUCCESS = Write was successful
195  *		FAILURE = Write failed
196  *
197  */
198 suc_code
199 write_to_dit(char *map, char *domain, datum key, datum value,
200 					bool_t replace, bool_t ignore_map_errs)
201 {
202 	int count;
203 	int res;
204 	__nisdb_retry_t	*storeRetry = &ldapDBTableMapping.storeErrorRetry;
205 
206 	/* Initialize tsd */
207 	__nisdb_get_tsd()->domainContext = 0;
208 	__nisdb_get_tsd()->escapeFlag = '\0';
209 
210 	for (count = 0; count < ypDomains.numDomains; count++) {
211 		if (0 == ypDomains.domainLabels[count])
212 			continue;
213 		if (0 == strcasecmp(domain, ypDomains.domainLabels[count])) {
214 			__nisdb_get_tsd()->domainContext =
215 						ypDomains.domains[count];
216 			break;
217 		}
218 	}
219 
220 	storeRetry = &ldapDBTableMapping.storeErrorRetry;
221 
222 	/* Loop 'attempts' times of forever if -1 */
223 	for (count = storeRetry->attempts; (0 <= count) ||
224 				(-1 == storeRetry->attempts); count --) {
225 		res = singleWriteToDIT(map, domain, &key, &value, replace);
226 		if (LDAP_SUCCESS == res)
227 			return (SUCCESS);
228 
229 		if (is_fatal_error(res)) {
230 			/*
231 			 * The mapping failed and will fail again if it is
232 			 * retried. However there are some cases where an
233 			 * actual mapping fault (rather than a LDAP problem)
234 			 * may be ignored.
235 			 */
236 			if (ignore_map_errs) {
237 				switch (res) {
238 					case LDAP_INVALID_DN_SYNTAX:
239 					case LDAP_OBJECT_CLASS_VIOLATION:
240 					case LDAP_NOT_ALLOWED_ON_RDN:
241 					case MAP_NAMEFIELD_MATCH_ERROR:
242 					case MAP_NO_DN:
243 						return (SUCCESS);
244 					default:
245 						break;
246 				}
247 			}
248 			return (FAILURE);
249 		}
250 
251 		if (ldapDBTableMapping.storeError != sto_retry)
252 			return (FAILURE);
253 
254 		/*
255 		 * Didn't work. If not the special case where no repeats are
256 		 * done sleep.
257 		 */
258 		if (0 != storeRetry->attempts)
259 			(void) poll(NULL, 0, storeRetry->timeout*1000);
260 
261 	}
262 	return (FAILURE);
263 }
264 
265 /*
266  * FUNCTION :	get_ttl_value()
267  *
268  * DESCRIPTION:	Get the TTL value, derived from mapping file or DIT, for a
269  *		entry.
270  *
271  * GIVEN :	Pointer to map
272  *		A flag indication if TTL should be max, min or random
273  *
274  * RETURNS :	TTL value in seconds.
275  *		-1 on failure
276  */
277 int
278 get_ttl_value(map_ctrl *map, TTL_TYPE type)
279 {
280 	__nis_table_mapping_t *table_map;
281 	int interval, res;
282 	char *myself = "get_ttl_value";
283 
284 	/*  Get the mapping structure corresponding to `map.domain' */
285 	table_map = mappingFromMap(map->map_name, map->domain, &res);
286 
287 	if (0 == table_map) {
288 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
289 			"Get TTL request could not access map %s in domain %s "
290 			"(error %d)", map->map_name, map->domain, res);
291 		return (-1);
292 	}
293 
294 	switch (type) {
295 		case TTL_MAX:
296 			return (table_map->initTtlHi);
297 
298 		case TTL_MIN:
299 			return (table_map->initTtlLo);
300 
301 		default:
302 			logmsg(MSG_NOTIMECHECK, LOG_INFO,
303 			"%s passed illegal TTL type (%d)", myself, type);
304 			/* If unknown TTL type drop through to TTL_RAND */
305 
306 		case TTL_RAND:
307 			interval = table_map->initTtlHi - table_map->initTtlLo;
308 			if (0 >= interval)
309 				return (table_map->initTtlLo);
310 
311 			/*
312 			 * Must get a random value. We assume srand48() got
313 			 * called at initialization.
314 			 */
315 			return (lrand48() % interval);
316 
317 		case TTL_RUNNING:
318 			return (table_map->ttl);
319 
320 
321 	}
322 }
323 
324 /*
325  * FUNCTION :	get_mapping_domain_list()
326  *
327  * DESCRIPTION:	Gets a list of domain names specified, by nisLDAPdomainContext
328  *		attributes, in the mapping file. This is used only for initial
329  *		DIT setup. Once the DIT has been set up get_domain_list() is
330  *		used instead.
331  *
332  * GIVEN :	Pointer returned array.
333  *
334  * RETURNS :	Number of element in returned array.
335  *		Array of elements this is in static memory
336  *		and must not be freed by the caller.
337  */
338 int
339 get_mapping_domain_list(char ***ptr)
340 {
341 	*ptr = ypDomains.domainLabels;
342 	return (ypDomains.numDomains);
343 }
344 
345 /*
346  * FUNCTION :	get_mapping_yppasswdd_domain_list()
347  *
348  * DESCRIPTION:	Gets a list of domain names specified, by the
349  *		nisLDAPyppasswddDomains attribute, in the mapping file. This
350  *		is the list of domains for which passwords should be changed.
351  *
352  * GIVEN :	Pointer returned array
353  *
354  * RETURNS :	Number of element in returned array.
355  *		0 if no nisLDAPyppasswddDomains attribute is present.
356  *		Array of elements this is in static memory
357  *		and must not be freed by the caller.
358  */
359 int
360 get_mapping_yppasswdd_domain_list(char ***ptr)
361 {
362 	*ptr = ypDomains.yppasswddDomainLabels;
363 	return (ypDomains.numYppasswdd);
364 }
365 
366 /*
367  * FUNCTION :	free_map_list()
368  *
369  * DESCRIPTION:	Frees a map list.
370  *
371  * GIVEN :	Pointer to the map list.
372  *
373  * RETURNS :	Nothing
374  */
375 void
376 free_map_list(char **map_list)
377 {
378 	free_null_terminated_list(map_list);
379 }
380 
381 /*
382  * FUNCTION :	get_passwd_list()
383  *
384  * DESCRIPTION:	Gets a list of either passwd or passwd.adjunct map files
385  *		defined in the mapping file. These are the files which have
386  *		'magic' nisLDAPdatabaseIdMapping entries aliasing them to
387  *		passwd or passwd.adjunct. This function is required so that
388  *		yppasswdd can work out which maps to synchronize with any
389  *		password changes.
390  *
391  *		This information is not currently stored by the parser but
392  *		we can recover it from the hash table. This makes hard work but
393  *		passwords should not be changed very frequently
394  *
395  * GIVEN :	Flag indicating if a list is required for passwd or
396  *		passwd.adjunct
397  *		Domain to return the list for.
398  *
399  * RETURNS :	Null terminated list of map names in malloced memory. To be
400  *		freed by caller. (Possibly empty if no passwd maps found)
401  *		NULL on error
402  */
403 char **
404 get_passwd_list(bool_t adjunct, char *domain)
405 {
406 	char *myself = "get_passwd_list";
407 	__nis_hash_item_mt *it;
408 	int	i, size;
409 	char 	*end_ptr;
410 	char	*target;	/* What we are looking for */
411 	int	target_len;
412 	int	domain_len;
413 	char	**res;		/* Result array */
414 	char	**res_old;	/* Old value of res during realloc */
415 	int	array_size;	/* Current malloced size */
416 	int	res_count = 0;	/* Current result count */
417 
418 	/*
419 	 * Always need an array even if just for terminator. Normally one
420 	 * chunk will be enough.
421 	 */
422 	res = am(myself, ARRAY_CHUNK * sizeof (char *));
423 	if (NULL == res)
424 		return (NULL);
425 	array_size = ARRAY_CHUNK;
426 
427 	/* Set up target */
428 	if (adjunct)
429 		target = PASSWD_ADJUNCT_PREFIX;
430 	else
431 		target = PASSWD_PREFIX;
432 	target_len = strlen(target);
433 	domain_len = strlen(domain);
434 
435 	/* Work out hash table length */
436 	size = sizeof (ldapMappingList.keys) / sizeof (ldapMappingList.keys[0]);
437 	/* For all hash table entries */
438 	for (i = 0; i < size; i++) {
439 		/* Walk linked list for this hash table entry */
440 		for (it = ldapMappingList.keys[i]; NULL != it; it = it->next) {
441 			/* Check right map */
442 			if ((target_len + domain_len + 1) > strlen(it->name))
443 				continue;
444 			if (0 != strncmp(it->name, target, target_len))
445 				continue;
446 
447 			/* Check right domain (minus trailing dot) */
448 			if (strlen(domain) >= strlen(it->name))
449 				continue;
450 			end_ptr = it->name + strlen(it->name) -
451 							strlen(domain) - 1;
452 			if (',' != *(end_ptr - 1))
453 				continue;
454 			if (0 != strncmp(end_ptr, domain, strlen(domain)))
455 				continue;
456 
457 			/* Check if we need to enlarge array */
458 			if ((res_count + 1) >= array_size) {
459 				array_size += ARRAY_CHUNK;
460 				res_old = res;
461 				res = realloc(res, array_size *
462 							sizeof (char *));
463 				if (NULL == res) {
464 					res_old[res_count] = NULL;
465 					free_passwd_list(res_old);
466 					return (NULL);
467 				}
468 			}
469 
470 			/* What we really need is strndup() */
471 			res[res_count] = am(myself, end_ptr - it->name + 1);
472 			if (NULL == res[res_count]) {
473 				free_passwd_list(res);
474 				return (NULL);
475 			}
476 
477 			/* Copy from start to end_ptr */
478 			(void) memcpy(res[res_count], it->name,
479 				    end_ptr-it->name - 1);
480 			res_count ++;
481 		}
482 	}
483 
484 	/* Terminate array */
485 	res[res_count] = NULL;
486 	return (res);
487 }
488 
489 /*
490  * FUNCTION :	free_passwd_list()
491  *
492  * DESCRIPTION:	Frees a password list obtained with get_passwd_list()
493  *
494  * INPUTS :	Address of list to free.
495  *
496  * OUTPUTS :	Nothing
497  */
498 void
499 free_passwd_list(char **list)
500 {
501 	free_null_terminated_list(list);
502 }
503 
504 /*
505  * FUNCTION :	free_null_terminated_list()
506  *
507  * DESCRIPTION:	Frees a generic null terminated list.
508  *
509  * INPUTS :	Address of list to free.
510  *
511  * OUTPUTS :	Nothing
512  */
513 void
514 free_null_terminated_list(char **list)
515 {
516 	int index;
517 
518 	/* Free all the strings */
519 	for (index = 0; NULL != list[index]; index ++)
520 		sfree(list[index]);
521 
522 	/* Free the array */
523 	sfree(list);
524 }
525 
526 
527 /*
528  * FUNCTION :	add_special_entries()
529  *
530  * DESCRIPTION:	Adds the special (YP_*) entries to a map.
531  *
532  *		Part of dit_access because requires access to the mapping
533  *		file in order to work out if secure and interdomain entries
534  *		should be created.
535  *
536  * GIVEN :	Pointer to an open, temporary, DBM file
537  *		Pointer to map information (do not use DBM fields).
538  *		Pointer to a location in which to return security flag
539  *
540  * RETURNS :	SUCCESS = All entries created
541  *		FAILURE = Some entries not created
542  */
543 suc_code
544 add_special_entries(DBM *db, map_ctrl *map, bool_t *secure_flag)
545 {
546 	char local_host[MAX_MASTER_NAME];
547 	__nis_table_mapping_t *table_map;
548 	int res;
549 
550 	/* Last modified time is now */
551 	update_timestamp(db);
552 
553 	/* Add domain name */
554 	addpair(db, yp_domain_name, map->domain);
555 
556 	/* For N2L mode local machine is always the master */
557 	sysinfo(SI_HOSTNAME, local_host, sizeof (local_host));
558 	addpair(db, yp_master_name, local_host);
559 
560 	/*  Get the mapping structure corresponding to `map.domain' */
561 	table_map = mappingFromMap(map->map_name, map->domain, &res);
562 	if (0 == table_map)
563 		return (FAILURE);
564 
565 	/* Add secure and interdomain flags if required */
566 	if (table_map->securemap_flag) {
567 		addpair(db, yp_secure, "");
568 		*secure_flag = TRUE;
569 	} else {
570 		*secure_flag = FALSE;
571 	}
572 	if (table_map->usedns_flag)
573 		addpair(db, yp_interdomain, "");
574 
575 	return (SUCCESS);
576 }
577 
578 /*
579  * FUNCTION:	update_map_from_dit()
580  *
581  * DESCRIPTION:	Core code called to update an entire map.
582  *		Information is recovered from LDAP and used to build a duplicate
583  *		copy of the live maps. When this is complete the maps are
584  *		locked and then overwritten by the new copy.
585  *
586  * INPUTS:	map_ctrl containing lots of information about the map and a
587  *		pointer to it's lock which will be required.
588  *		Flag indicating if progress logging is required.
589  *
590  * OUTPUTS:	SUCCESS = Map updated
591  *		FAILURE = Map not updated
592  */
593 suc_code
594 update_map_from_dit(map_ctrl *map, bool_t log_flag) {
595 	__nis_table_mapping_t	*t;
596 	__nis_rule_value_t	*rv;
597 	__nis_ldap_search_t	*ls;
598 	__nis_object_dn_t	*objectDN = NULL;
599 	datum			*datval, *datkey;
600 	int			nr = 0, i, j, nv, numDNs;
601 	int			statP = SUCCESS, flag;
602 	char			*objname, **dn;
603 	/* Name of temporary entries DBM file */
604 	char			*temp_entries;
605 	/* Name of temporary TTL DBM file */
606 	char			*temp_ttl;
607 	/* Temporary DBM handles */
608 	DBM			*temp_entries_db;
609 	DBM			*temp_ttl_db;
610 	map_ctrl		temp_map;
611 	datum			key;
612 	char			*myself = "update_map_from_dit";
613 	bool_t			secure_flag;
614 	int			entry_count = 1;
615 	int			next_print = PRINT_FREQ;
616 
617 	if (!map || !map->map_name || !map->domain) {
618 		return (FAILURE);
619 	}
620 
621 	__nisdb_get_tsd()->escapeFlag = '\0';
622 
623 	/*
624 	 * netgroup.byxxx maps are a special case. They are regenerated from
625 	 * the netgroup map, not the DIT, so handle special case.
626 	 */
627 	if ((0 == strcmp(map->map_name, NETGROUP_BYHOST)) ||
628 		0 == (strcmp(map->map_name,  NETGROUP_BYUSER))) {
629 		return (update_netgroup_byxxx(map));
630 	}
631 
632 	/* Get the mapping information for the map */
633 	if ((t = mappingFromMap(map->map_name, map->domain, &statP)) == 0) {
634 		if (statP == MAP_NO_MAPPING_EXISTS)
635 			logmsg(MSG_NOTIMECHECK, LOG_WARNING,
636 			"%s: No mapping information available for %s,%s",
637 				myself, map->map_name, map->domain);
638 		return (FAILURE);
639 	}
640 
641 	/* Allocate and set up names */
642 	if (SUCCESS != alloc_temp_names(map->map_path,
643 				&temp_entries, &temp_ttl)) {
644 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
645 			"%s: Unable to create map names for %s",
646 			myself, map->map_path);
647 		return (FAILURE);
648 	}
649 
650 	/* Create temp entry and TTL file */
651 	if ((temp_entries_db = dbm_open(temp_entries, O_RDWR | O_CREAT, 0644))
652 						== NULL) {
653 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
654 						myself, temp_entries);
655 		sfree(temp_entries);
656 		sfree(temp_ttl);
657 		return (FAILURE);
658 	}
659 
660 	if ((temp_ttl_db = dbm_open(temp_ttl, O_RDWR | O_CREAT, 0644))
661 						== NULL) {
662 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
663 						myself, temp_ttl);
664 		dbm_close(temp_entries_db);
665 		delete_map(temp_entries);
666 		sfree(temp_entries);
667 		sfree(temp_ttl);
668 		return (FAILURE);
669 	}
670 
671 	/* Initialize domainContext tsd */
672 	__nisdb_get_tsd()->domainContext = 0;
673 	for (i = 0; i < ypDomains.numDomains; i++) {
674 		if (0 == ypDomains.domainLabels[i])
675 			continue;
676 		if (0 == strcasecmp(map->domain, ypDomains.domainLabels[i])) {
677 			__nisdb_get_tsd()->domainContext = ypDomains.domains[i];
678 			break;
679 		}
680 	}
681 
682 	if (!(objname = getFullMapName(map->map_name, map->domain))) {
683 		if (temp_entries_db)
684 			dbm_close(temp_entries_db);
685 		if (temp_ttl_db)
686 			dbm_close(temp_ttl_db);
687 		delete_map(temp_entries);
688 		sfree(temp_entries);
689 		delete_map(temp_ttl);
690 		sfree(temp_ttl);
691 		return (FAILURE);
692 	}
693 
694 	/* Try each mapping for the map */
695 	for (flag = 0; t != 0; t = t->next) {
696 
697 		/* Check if the mapping is the correct one */
698 		if (strcmp(objname, t->objName) != 0) {
699 			continue;
700 		}
701 
702 		/* Check if rulesFromLDAP are provided */
703 		if (t->numRulesFromLDAP == 0) {
704 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
705 				"%s: No rulesFromLDAP available for %s (%s)",
706 				myself, t->dbId, map->map_name);
707 			continue;
708 		}
709 
710 		/* Set flag to indicate update is enabled */
711 		flag = 1;
712 		/* Create ldap request for enumeration */
713 		for (objectDN = t->objectDN;
714 				objectDN && objectDN->read.base;
715 				objectDN = objectDN->next) {
716 			if ((ls = createLdapRequest(t, 0, 0, 1, NULL,
717 						objectDN)) == 0) {
718 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
719 					"%s: Failed to create "
720 					"ldapSearch request for "
721 					"%s (%s) for base %s",
722 					myself, t->dbId,
723 					map->map_name,
724 					objectDN->read.base);
725 				statP = FAILURE;
726 				break;
727 			}
728 
729 			if (log_flag) {
730 				printf("Waiting for LDAP search results.\n");
731 			}
732 
733 			/* Query LDAP */
734 			nr = (ls->isDN)?0:-1;
735 			rv = ldapSearch(ls, &nr, 0, &statP);
736 			freeLdapSearch(ls);
737 			if (rv == 0) {
738 				if (statP == LDAP_NO_SUCH_OBJECT) {
739 				/*
740 				 * No Entry exists in the ldap server. Not
741 				 * a problem. Maybe there are just no entries
742 				 * in this map.
743 				 */
744 					continue;
745 				}
746 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
747 					"%s: ldapSearch error %d "
748 					"(%s) for %s (%s) for base %s",
749 					myself, statP, ldap_err2string(statP),
750 					t->dbId, map->map_name,
751 					objectDN->read.base);
752 				statP = FAILURE;
753 				break;
754 			}
755 
756 			if (log_flag) {
757 				printf("Processing search results.\n");
758 			}
759 
760 			/* Obtain list of DNs for logging */
761 			if ((dn = findDNs(myself, rv, nr, 0, &numDNs)) == 0) {
762 				statP = FAILURE;
763 				break;
764 			}
765 
766 			/* For each entry in the result  do the following */
767 			for (i = 0; i < nr; i++) {
768 			/* Convert LDAP data to NIS equivalents */
769 				statP = buildNISRuleValue(t, &rv[i],
770 						map->domain);
771 				if (statP == MAP_INDEXLIST_ERROR)
772 					continue;
773 				if (statP != SUCCESS) {
774 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
775 					    "%s: Conversion error %d (LDAP to "
776 					    "name=value pairs) "
777 					    "for (dn: %s) for "
778 					    "%s (%s) for base %s",
779 					    myself, statP, NIL(dn[i]),
780 					    t->dbId, map->map_name,
781 					    objectDN->read.base);
782 					continue;
783 				}
784 
785 				/* Obtain the datum for value */
786 				datval = ruleValueToDatum(t, &rv[i], &statP);
787 				if (datval == 0) {
788 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
789 						"%s: Conversion error %d "
790 						"(name=value pairs to NIS)"
791 						" for (dn: %s) for "
792 						"%s (%s) for base %s",
793 						myself, statP, NIL(dn[i]),
794 						t->dbId, map->map_name,
795 						objectDN->read.base);
796 					continue;
797 				}
798 
799 				/* Obtain the datum for key */
800 				datkey = getKeyFromRuleValue(t, &rv[i],
801 						&nv, &statP);
802 				if (datkey == 0) {
803 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
804 						"%s: Unable to obtain NIS "
805 						"key from LDAP data (dn:%s) "
806 						"for %s (%s) for base %s",
807 						myself, NIL(dn[i]), t->dbId,
808 						map->map_name,
809 						objectDN->read.base);
810 					sfree(datval->dptr);
811 					sfree(datval);
812 					continue;
813 				}
814 
815 				/* Write to the temporary map */
816 				for (j = 0; j < nv; j++, entry_count ++) {
817 					if (datkey[j].dsize == 0)
818 						continue;
819 					errno = 0;
820 					/* DBM_INSERT to match */
821 					/* singleReadFromDIT */
822 					if (dbm_store(temp_entries_db,
823 						datkey[j],
824 						*datval,
825 						DBM_INSERT) < 0) {
826 						/*
827 						 * For some cases errno may
828 						 * still be 0 but dbm_error
829 						 * isn't informative at all.
830 						 */
831 						logmsg(MSG_NOTIMECHECK,
832 						    LOG_WARNING,
833 						    "%s: dbm store error "
834 						    "(errno=%d) "
835 						    "for (key=%s, value=%s) "
836 						    "for %s (%s) for base %s",
837 						    myself,
838 						    errno,
839 						    datkey[j].dptr,
840 						    datval->dptr, t->dbId,
841 						    map->map_name,
842 						    objectDN->read.base);
843 						/* clear the error */
844 						dbm_clearerr(temp_entries_db);
845 					}
846 					sfree(datkey[j].dptr);
847 
848 					if (log_flag && (entry_count >=
849 							next_print)) {
850 						printf("%d entries processed\n",
851 							entry_count);
852 						next_print *= 2;
853 					}
854 
855 				}
856 				sfree(datkey);
857 				sfree(datval->dptr);
858 				sfree(datval);
859 			}
860 
861 			freeRuleValue(rv, nr);
862 			freeDNs(dn, numDNs);
863 		} /* End of for over objectDN */
864 	}
865 	sfree(objname);
866 
867 	if (t != 0 || flag == 0) {
868 		if (temp_entries_db)
869 			dbm_close(temp_entries_db);
870 		if (temp_ttl_db)
871 			dbm_close(temp_ttl_db);
872 		delete_map(temp_entries);
873 		sfree(temp_entries);
874 		delete_map(temp_ttl);
875 		sfree(temp_ttl);
876 		return (statP);
877 	}
878 	/* Set up enough of map_ctrl to call update_entry_ttl */
879 	temp_map.map_name = map->map_name;
880 	temp_map.domain = map->domain;
881 	temp_map.ttl = temp_ttl_db;
882 
883 	/* Generate new TTL file */
884 	key = dbm_firstkey(temp_entries_db);
885 	while (key.dptr != 0) {
886 		if (!is_special_key(&key))
887 			/*
888 			 * We don't want all the entries to time out at the
889 			 * same time so create random TTLs.
890 			 */
891 			if (FAILURE == update_entry_ttl(&temp_map, &key,
892 								TTL_RAND))
893 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
894 					"%s: Could not update TTL for "
895 					"(key=%s) for map %s,%s",
896 					myself, NIL(key.dptr), map->map_name,
897 					map->domain);
898 		key = dbm_nextkey(temp_entries_db);
899 	}
900 
901 	/* Update map TTL */
902 	if (SUCCESS != update_map_ttl(&temp_map)) {
903 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not update map TTL "
904 			"for %s,%s", myself, map->map_name, map->domain);
905 	}
906 
907 	/* Set up 'special' nis entries */
908 	add_special_entries(temp_entries_db, map, &secure_flag);
909 
910 	/* Close temp DBM files */
911 	dbm_close(temp_entries_db);
912 	dbm_close(temp_ttl_db);
913 
914 	/* Lock access to the map for copy */
915 	lock_map_ctrl(map);
916 
917 	/* Move temp maps to real ones */
918 	rename_map(temp_entries, map->map_path, secure_flag);
919 	rename_map(temp_ttl, map->ttl_path, secure_flag);
920 
921 	/* Free file names */
922 	sfree(temp_entries);
923 	sfree(temp_ttl);
924 
925 	/* Unlock map */
926 	unlock_map_ctrl(map);
927 
928 	return (SUCCESS);
929 }
930 
931 /*
932  * FUNCTION :	get_mapping_map_list()
933  *
934  * DESCRIPTION:	Gets a list of nis maps for a given domain specified in the
935  *		mapping file. This information is not saved so have to go
936  *		through the entire hash table. At least this is only done at
937  *		initialization time.
938  *
939  * GIVEN :	Domain name
940  *
941  * RETURNS :	List of map names in malloced memory. MUST BE FREED BY CALLER.
942  */
943 char **
944 get_mapping_map_list(char *domain)
945 {
946 	char *myself = "get_mapping_map_list";
947 	__nis_hash_item_mt *it;
948 	int	i, j, size;
949 	char 	*end_ptr;
950 	char	**res;		/* Result array */
951 	char	**res_old;	/* Old value of res during realloc */
952 	int	array_size;	/* Current malloced size */
953 	int	res_count = 0;	/* Current result count */
954 
955 	/*
956 	 * Always need an array even if just for terminator. Normally one
957 	 * chunk will be enough.
958 	 */
959 	res = am(myself, ARRAY_CHUNK * sizeof (char *));
960 	if (NULL == res)
961 		return (NULL);
962 	array_size = ARRAY_CHUNK;
963 
964 	/* Work out hash table length */
965 	size = sizeof (ldapMappingList.keys) / sizeof (ldapMappingList.keys[0]);
966 	/* For all hash table entries */
967 	for (i = 0; i < size; i++) {
968 		/* Walk linked list for this hash table entry */
969 		for (it = ldapMappingList.keys[i]; NULL != it; it = it->next) {
970 
971 			/* Check it's not a split field entry */
972 			if (0 != ((__nis_table_mapping_t *)it)->numSplits)
973 				continue;
974 
975 			/* Check right domain (minus trailing dot) */
976 			if (strlen(domain) >= strlen(it->name))
977 				continue;
978 			end_ptr = it->name + strlen(it->name) -
979 							strlen(domain) - 1;
980 			if (',' != *(end_ptr - 1))
981 				continue;
982 			if (0 != strncmp(end_ptr, domain, strlen(domain)))
983 				continue;
984 
985 			/* Check if we need to enlarge array */
986 			if ((res_count + 1) >= array_size) {
987 				array_size += ARRAY_CHUNK;
988 				res_old = res;
989 				res = realloc(res, array_size *
990 							sizeof (char *));
991 				if (NULL == res) {
992 					res_old[res_count] = NULL;
993 					free_passwd_list(res_old);
994 					return (NULL);
995 				}
996 			}
997 
998 			/*
999 			 * We will need the sequence number when we come to
1000 			 * sort the entries so for now store a pointer to
1001 			 * the __nis_hash_item_mt.
1002 			 */
1003 			res[res_count] = (char *)it;
1004 			res_count ++;
1005 		}
1006 	}
1007 
1008 	/* Terminate array */
1009 	res[res_count] = NULL;
1010 
1011 	/* Bubble sort entries into the same order as mapping file */
1012 	for (i = res_count - 2; 0 <= i; i--) {
1013 		for (j = 0; j <= i; j++) {
1014 			if (((__nis_table_mapping_t *)res[j + 1])->seq_num <
1015 				((__nis_table_mapping_t *)res[j])->seq_num) {
1016 				end_ptr = res[j];
1017 				res[j] = res[j+1];
1018 				res[j + 1] = end_ptr;
1019 			}
1020 		}
1021 	}
1022 
1023 	/* Finally copy the real strings in to each entry */
1024 	for (i = 0; NULL != res[i]; i ++) {
1025 
1026 		/* Get hash table entry back */
1027 		it = (__nis_hash_item_mt *)res[i];
1028 
1029 		end_ptr = it->name + strlen(it->name) - strlen(domain) - 1;
1030 
1031 		/* What we really need is strndup() */
1032 		res[i] = am(myself, end_ptr - it->name + 1);
1033 		if (NULL == res[i]) {
1034 			free_map_list(res);
1035 			return (NULL);
1036 		}
1037 
1038 		/* Copy from start to end_ptr */
1039 		(void) memcpy(res[i], it->name, end_ptr-it->name - 1);
1040 	}
1041 
1042 	return (res);
1043 }
1044 
1045 /*
1046  * FUNCTION :	make_nis_container()
1047  *
1048  * DESCRIPTION: Sets up container for map_name in the DIT.
1049  *
1050  * GIVEN :	Map name
1051  *		The domain name.
1052  *		Flag indicating if container should be created.
1053  *
1054  * RETURNS :	SUCCESS	= It worked
1055  *		FAILURE	= There was a problem.
1056  */
1057 suc_code
1058 make_nis_container(char *map_name, char *domain, bool_t init_containers) {
1059 	int			i, rc, statP = SUCCESS;
1060 	__nis_table_mapping_t	*t;
1061 	char			*dn;
1062 	char			*myself = "make_nis_container";
1063 
1064 	if (!map_name || !domain)
1065 		return (FAILURE);
1066 
1067 	if (FALSE == init_containers) {
1068 		/*
1069 		 * If we are not creating containers it is debatable what we
1070 		 * should do . Maybe we should check for a pre-
1071 		 * existing container and return failure if it does not exist.
1072 		 *
1073 		 * For now we assume the user will not have called us in this
1074 		 * mode unless they know what they are doing. So return
1075 		 * success. If they have got it wrong then latter writes will
1076 		 * fail.
1077 		 */
1078 		return (SUCCESS);
1079 	}
1080 
1081 	/* Get the mapping information for the map */
1082 	if ((t = mappingFromMap(map_name, domain, &statP)) == 0) {
1083 		if (statP == MAP_NO_MAPPING_EXISTS)
1084 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
1085 			"%s: No mapping information available for %s,%s",
1086 				myself, NIL(map_name), NIL(domain));
1087 		return (FAILURE);
1088 	}
1089 
1090 	/* Two times. One for readDN and other for writeDN */
1091 	for (i = 0; i < 2; i++) {
1092 		if (i == 0)
1093 			dn = t->objectDN->read.base;
1094 		else {
1095 			if (t->objectDN->write.base == 0) {
1096 				logmsg(MSG_NOTIMECHECK, LOG_INFO,
1097 					"%s: No baseDN in writespec. Write "
1098 					"disabled for %s,%s",
1099 					myself, map_name, domain);
1100 				break;
1101 			}
1102 			if (!strcasecmp(dn, t->objectDN->write.base))
1103 				break;
1104 			dn = t->objectDN->write.base;
1105 		}
1106 
1107 		if ((rc = makeNISObject(0, dn)) == FAILURE) {
1108 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
1109 				"%s: Unable to create ldap container (dn: %s) "
1110 				"for %s,%s", myself, dn, map_name, domain);
1111 			return (FAILURE);
1112 		}
1113 	}
1114 	return (SUCCESS);
1115 }
1116 
1117 /*
1118  * FUNCTION :	make_nis_domain()
1119  *
1120  * DESCRIPTION:	Sets up a nisDomainObject in the DIT
1121  *
1122  * GIVEN:	Name of the domain
1123  *		Flag indicating if domain should be create or possibly just
1124  *		checked for.
1125  */
1126 suc_code
1127 make_nis_domain(char *domain, bool_t init_containers) {
1128 
1129 	if (FALSE == init_containers) {
1130 		/*
1131 		 * If we are not creating containers it is debatable what we
1132 		 * should do with domains. Maybe we should check for a pre-
1133 		 * existing domain and return failure if it does not exist.
1134 		 *
1135 		 * For now we assume the user will not have called us in this
1136 		 * mode unless they know what they are doing. So return
1137 		 * success. If they have got it wrong then latter writes will
1138 		 * fail.
1139 		 */
1140 		return (SUCCESS);
1141 	}
1142 
1143 	/* Create the domain */
1144 	return (makeNISObject(domain, 0));
1145 }
1146 
1147 /*
1148  * FUNCTION:	update_netgroup_byxxx()
1149  *
1150  * DESCRIPTION:	Updates the netgroup.byxxx series of maps based on the current
1151  *		netgroup file. We invoke revnetgroup so that if any changes
1152  *		are made to this utility the same changes are made here.
1153  *
1154  * INPUTS:	map_ctrl containing lots of information about the map and a
1155  *		pointer to it's lock which will be required.
1156  *
1157  * OUTPUTS:	SUCCESS = Map updated
1158  *		FAILURE = Map not updated
1159  */
1160 suc_code
1161 update_netgroup_byxxx(map_ctrl *map) {
1162 	/* Name of temporary entries DBM file */
1163 	char			*temp_entries;
1164 	/* Name of temporary TTL DBM file */
1165 	char			*temp_ttl;
1166 	/* Temporary DBM handles */
1167 	DBM			*temp_entries_db;
1168 	DBM			*temp_ttl_db;
1169 	map_ctrl		temp_map;
1170 	char			*myself = "update_netgroup_byxxx";
1171 	char			*cmdbuf;
1172 	int			cmd_length;
1173 	datum			key;
1174 	map_ctrl		*netgroupmap;
1175 	int			res;
1176 	/* Temporary revnetgroup files */
1177 	const char 		*byhost = NETGROUP_BYHOST "_REV" TEMP_POSTFIX;
1178 	const char 		*byuser = NETGROUP_BYUSER "_REV" TEMP_POSTFIX;
1179 	const char		*temp_file_name;
1180 
1181 
1182 	/*
1183 	 * We need to use two different temporary files: one for netgroup.byhost
1184 	 * and other for netgroup.byuser, since these two maps can be updated
1185 	 * simultaneously. These temporary files will hold the output of
1186 	 * revnetgroup [-h|-u] command. They are then used to generate the
1187 	 * corresponding dbm files and thereafter deleted.
1188 	 */
1189 	if (0 == strcmp(map->map_name, NETGROUP_BYHOST))
1190 		temp_file_name = byhost;
1191 	else
1192 		temp_file_name = byuser;
1193 
1194 	/* Alloc enough cmd buf for revnet cmd */
1195 	cmd_length = strlen("/usr/sbin/makedbm -u ") +
1196 			(strlen(map->map_path) - strlen(map->map_name)) +
1197 			strlen(NETGROUP_MAP) +
1198 			strlen(" | /usr/sbin/revnetgroup -h > ") +
1199 			(strlen(map->map_path) - strlen(map->map_name)) +
1200 			strlen(temp_file_name) + 1;
1201 	cmdbuf = am(myself, cmd_length);
1202 
1203 	if (NULL == cmdbuf) {
1204 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
1205 				"%s: Could not alloc cmdbuf.", myself);
1206 		return (FAILURE);
1207 	}
1208 
1209 	/*
1210 	 * If necessary update (and wait for) netgroups map. This is a lot of
1211 	 * work but if the netgroup map itself is not being accessed it may
1212 	 * contain information that is not up to date with the DIT.
1213 	 *
1214 	 * We use the cmdbuf to store the qualified netgroup map name there will
1215 	 * be enough space for this but we are not yet creating the cmd.
1216 	 */
1217 	strlcpy(cmdbuf, map->map_path, strlen(map->map_path) -
1218 						strlen(map->map_name) + 1);
1219 	strcat(cmdbuf, NETGROUP_MAP);
1220 	netgroupmap = (map_ctrl *)shim_dbm_open(cmdbuf,
1221 						O_RDWR | O_CREAT, 0644);
1222 	if (NULL == netgroupmap) {
1223 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
1224 				"%s: Could not update %s.", myself, cmdbuf);
1225 		sfree(cmdbuf);
1226 		return (FAILURE);
1227 	}
1228 
1229 	if (has_map_expired(netgroupmap)) {
1230 		lock_map_ctrl(netgroupmap);
1231 		update_map_if_required(netgroupmap, TRUE);
1232 		unlock_map_ctrl(netgroupmap);
1233 	}
1234 	shim_dbm_close((DBM *)netgroupmap);
1235 
1236 	/* Dump netgroup file through revnetgroup to a temp file */
1237 	strcpy(cmdbuf, "/usr/sbin/makedbm -u ");
1238 
1239 	/* Unmake the netgroup file in same domain as map */
1240 	strncat(cmdbuf, map->map_path, strlen(map->map_path) -
1241 						strlen(map->map_name));
1242 	strcat(cmdbuf, NETGROUP_MAP);
1243 
1244 	if (0 == strcmp(map->map_name, NETGROUP_BYHOST)) {
1245 		strcat(cmdbuf, " | /usr/sbin/revnetgroup -h > ");
1246 	} else {
1247 		strcat(cmdbuf, " | /usr/sbin/revnetgroup -u > ");
1248 	}
1249 
1250 	/* Create temp file file in same domain as map */
1251 	strncat(cmdbuf, map->map_path, strlen(map->map_path) -
1252 						strlen(map->map_name));
1253 	strcat(cmdbuf, temp_file_name);
1254 
1255 	if (0 > system(cmdbuf)) {
1256 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not run \"%s\" "
1257 			"(errno=%d)", myself, cmdbuf, errno);
1258 		sfree(cmdbuf);
1259 		return (FAILURE);
1260 	}
1261 	sfree(cmdbuf);
1262 
1263 	/* Allocate and set up names */
1264 	if (SUCCESS != alloc_temp_names(map->map_path,
1265 				&temp_entries, &temp_ttl)) {
1266 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
1267 			"%s: Unable to create map names for %s",
1268 			myself, map->map_path);
1269 		return (FAILURE);
1270 	}
1271 
1272 	/* Make the temporary DBM file */
1273 	cmdbuf = am(myself, strlen("/usr/sbin/makedbm") +
1274 			(strlen(map->map_path) - strlen(map->map_name)) +
1275 			strlen(temp_file_name) +
1276 			strlen(temp_entries) + 3);
1277 	if (NULL == cmdbuf) {
1278 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
1279 				"%s: Could not allocate cmdbuf.", myself);
1280 		sfree(temp_entries);
1281 		sfree(temp_ttl);
1282 		return (FAILURE);
1283 	}
1284 
1285 	strcpy(cmdbuf, "/usr/sbin/makedbm ");
1286 	strncat(cmdbuf, map->map_path, strlen(map->map_path) -
1287 						strlen(map->map_name));
1288 	strcat(cmdbuf, temp_file_name);
1289 	strcat(cmdbuf, " ");
1290 	strcat(cmdbuf, temp_entries);
1291 
1292 	if (0 > system(cmdbuf)) {
1293 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not run \"%s\" "
1294 			"(errno=%d)", myself, cmdbuf, errno);
1295 		sfree(cmdbuf);
1296 		sfree(temp_entries);
1297 		sfree(temp_ttl);
1298 		return (FAILURE);
1299 	}
1300 
1301 	/* Already have enough command buffer to rm temporary file */
1302 	strlcpy(cmdbuf, map->map_path, strlen(map->map_path) -
1303 						strlen(map->map_name) + 1);
1304 	strcat(cmdbuf, temp_file_name);
1305 	res = unlink(cmdbuf);
1306 	/* If the temp file did not exist no problem. Probably had no entries */
1307 	if ((0 != res) && (ENOENT != errno)) {
1308 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not delete \"%s\" "
1309 			"(errno=%d)", myself, cmdbuf, errno);
1310 		sfree(temp_entries);
1311 		sfree(temp_ttl);
1312 		sfree(cmdbuf);
1313 		return (FAILURE);
1314 	}
1315 	sfree(cmdbuf);
1316 
1317 	if ((temp_entries_db = dbm_open(temp_entries, O_RDWR | O_CREAT, 0644))
1318 						== NULL) {
1319 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
1320 						myself, temp_entries);
1321 		sfree(temp_entries);
1322 		sfree(temp_ttl);
1323 		return (FAILURE);
1324 	}
1325 
1326 	if ((temp_ttl_db = dbm_open(temp_ttl, O_RDWR | O_CREAT, 0644))
1327 						== NULL) {
1328 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
1329 						myself, temp_ttl);
1330 		dbm_close(temp_entries_db);
1331 		sfree(temp_entries);
1332 		sfree(temp_ttl);
1333 		return (FAILURE);
1334 	}
1335 
1336 	/*
1337 	 * Set up enough of map_ctrl to call update_entry_ttl. Since there is
1338 	 * no mapping, and thus not TTL, defined for these maps use the TTL
1339 	 * values for netgroup map
1340 	 */
1341 	temp_map.map_name = NETGROUP_MAP;
1342 	temp_map.domain = map->domain;
1343 	temp_map.ttl = temp_ttl_db;
1344 
1345 	/*
1346 	 * Generate new TTL file.  Since these maps work only on the whole map
1347 	 * expiry these will not actually be used but there presence makes it
1348 	 * easier to handle these maps in the same way as other maps.
1349 	 */
1350 	key = dbm_firstkey(temp_entries_db);
1351 	while (key.dptr != 0) {
1352 		if (!is_special_key(&key))
1353 			/*
1354 			 * For these maps want all timouts to be maximum
1355 			 */
1356 			if (FAILURE == update_entry_ttl(&temp_map, &key,
1357 								TTL_MAX))
1358 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
1359 					"%s: Could not update TTL for "
1360 					"(key=%s) for map %s,%s",
1361 					myself, NIL(key.dptr), map->map_name,
1362 					map->domain);
1363 		key = dbm_nextkey(temp_entries_db);
1364 	}
1365 
1366 	/* Update map TTL */
1367 	update_map_ttl(&temp_map);
1368 
1369 	/* Close temp DBM files */
1370 	dbm_close(temp_entries_db);
1371 	dbm_close(temp_ttl_db);
1372 
1373 	/* Lock access to the map for copy */
1374 	lock_map_ctrl(map);
1375 
1376 	/* Move temp maps to real ones */
1377 	rename_map(temp_entries, map->map_path, FALSE);
1378 	rename_map(temp_ttl, map->ttl_path, FALSE);
1379 
1380 	/* Free file names */
1381 	sfree(temp_entries);
1382 	sfree(temp_ttl);
1383 
1384 	/* Unlock map */
1385 	unlock_map_ctrl(map);
1386 
1387 	return (SUCCESS);
1388 }
1389