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 /*
27  * DESCRIPTION:	Contains functions relating to the creation and manipulation
28  *		of map_ctrl structures. These are used to hold information
29  *		specific to one NIS map.
30  *
31  *		Because each of these contains a significant amount of state
32  *		information about an individual map they are created (on the
33  *		heap) when a map is opened and destroyed when it is closed.
34  *		The overhead of doing this is less than maintaining a pool
35  *		of map_ctrls.
36  *
37  *		If two processes access the same map two map_ctrls will be
38  *		created with similar contents (but differing DBM pointers).
39  *		Both will have the same hash value so when one is locked
40  *		access to the other will also be prevented.
41  */
42 
43 #include <unistd.h>
44 #include <stdlib.h>
45 #include <syslog.h>
46 #include <ndbm.h>
47 #include <string.h>
48 #include <sys/param.h>
49 #include "ypsym.h"
50 #include "ypdefs.h"
51 #include "shim.h"
52 #include "yptol.h"
53 #include "../ldap_util.h"
54 
55 extern int hash(char *s);
56 extern bool_t add_map_domain_to_list(char *domain, char ***map_list);
57 
58 /*
59  * Static variables for locking mechanism in
60  * N2L mode.
61  *  map_id_list: hash table for map lists
62  *  max_map: max number of maps in map_id_list
63  *      it is also used as the map ID for
64  *      unknown maps, see get_map_id()
65  *      in usr/src/cmd/ypcmd/shared/lockmap.c
66  */
67 static map_id_elt_t *map_id_list[MAXHASH];
68 static int max_map = 0;
69 
70 /* Switch on parts of ypdefs.h */
71 USE_DBM
72 USE_YPDBPATH
73 
74 /*
75  * FUNCTION: 	create_map_ctrl();
76  *
77  * DESCRIPTION: Create and a new map_ctrl in a non opened state.
78  *
79  * INPUTS:	Fully qualified map name
80  *
81  * OUTPUTS:	Pointer to map_ctrl
82  *		NULL on failure.
83  *
84  */
85 map_ctrl *
create_map_ctrl(char * name)86 create_map_ctrl(char *name)
87 {
88 	char *myself = "create_map_ctrl";
89 	map_ctrl *map;
90 
91 	map = (map_ctrl *)am(myself, sizeof (map_ctrl));
92 	if (NULL == map) {
93 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "Could not alloc map_ctrl");
94 		return (NULL);
95 	}
96 
97 	/* Clear new map (in case we have to free it) */
98 	map->entries = NULL;
99 	map->hash_val = 0;
100 	map->map_name = NULL;
101 	map->domain = NULL;
102 	map->map_path = NULL;
103 	map->ttl = NULL;
104 	map->ttl_path = NULL;
105 	map->trad_map_path = NULL;
106 	map->key_data.dptr = NULL;
107 	map->open_mode = 0;
108 	map->open_flags = 0;
109 
110 	/*
111 	 * Initialize the fields of the map_ctrl. By doing this once here we
112 	 * can save a lot of work as map entries are accessed.
113 	 */
114 	if (SUCCESS != map_ctrl_init(map, name)) {
115 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
116 				"Could not initialize map_ctrl for %s", name);
117 		free_map_ctrl(map);
118 		return (NULL);
119 	}
120 
121 	return (map);
122 }
123 
124 /*
125  * FUNCTION :	map_ctrl_init()
126  *
127  * DESCRIPTION:	Initializes the fields of a map_ctrl structure.
128  *
129  *		By doing this once (when the map_ctrl is created) we avoid
130  *		numerous other function having to repeat this string
131  *		manipulation.
132  *
133  * GIVEN :	Pointer to the structure
134  *		Fully qualified name of the map
135  *
136  * RETURNS :	SUCCESS = map_ctrl fully set up.
137  *		FAILURE = map_ctrl not set up CALLER MUST FREE.
138  */
139 suc_code
map_ctrl_init(map_ctrl * map,char * name)140 map_ctrl_init(map_ctrl *map, char *name)
141 {
142 	char *myself = "map_ctrl_init";
143 	char *p, *q;
144 
145 	/* Save map path for future reference */
146 	map->map_path = (char *)strdup(name);
147 	if (NULL ==  map->map_path) {
148 		logmsg(MSG_NOMEM, LOG_ERR,
149 				"Could not duplicate map path %s", map);
150 		return (FAILURE);
151 	}
152 
153 	/* Work out map's unqualified name from path */
154 	p = strrchr(name, SEP_CHAR);
155 	if (NULL == p) {
156 		/* Must be at least a domain and name */
157 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
158 			"Could not find separator in map path %s", map);
159 		return (FAILURE);
160 	}
161 	q = p + 1;
162 
163 	/* Check for and remove N2L prefix */
164 	if (yptol_mode) {
165 		/*
166 		 * Check for and remove N2L prefix. If not found not a problem
167 		 * we open some old style maps during DIT initialization.
168 		 */
169 		if (0 == strncmp(q, NTOL_PREFIX, strlen(NTOL_PREFIX)))
170 			q += strlen(NTOL_PREFIX);
171 	} else {
172 		if (0 == strncmp(q, NTOL_PREFIX, strlen(NTOL_PREFIX)))
173 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
174 				"Working in non N2L mode and path %s "
175 				"contains N2L prefix", name);
176 	}
177 
178 	/* Save unqualified map name */
179 	map->map_name = strdup(q);
180 	if (NULL == map->map_name) {
181 		logmsg(MSG_NOMEM, LOG_ERR,
182 				"Could not duplicate map name %s", q);
183 		return (FAILURE);
184 	}
185 
186 	/* Work out map's domain name from path */
187 	for (q = p-1; (SEP_CHAR != *q) && (q > name); q--);
188 
189 	if (q <= name) {
190 		/* Didn't find separator */
191 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
192 				"Could not find domain in map path %s", name);
193 		return (FAILURE);
194 	}
195 
196 	map->domain = (char *)am(myself, p - q);
197 	if (NULL == map->domain) {
198 		logmsg(MSG_NOMEM, LOG_ERR,
199 			"Could not alloc memory for domain in path %s", name);
200 		return (FAILURE);
201 	}
202 	(void) strncpy(map->domain, q + 1, p-q-1);
203 	map->domain[p-q-1] = '\0';
204 
205 	/* Work out extra names required by N2L */
206 	if (yptol_mode) {
207 		/*
208 		 * Work out what old style NIS path would have been. This is
209 		 * used to check for date of DBM file so add the DBM
210 		 * extension.
211 		 */
212 		map->trad_map_path = (char *)am(myself, strlen(map->map_name) +
213 					+ strlen(dbm_pag) + (p - name) + 2);
214 		if (NULL == map->trad_map_path) {
215 			logmsg(MSG_NOMEM, LOG_ERR,
216 				"Could not alocate memory for "
217 				"traditional map path derived from %s", name);
218 			return (FAILURE);
219 		}
220 
221 		strncpy(map->trad_map_path, name, p - name + 1);
222 		map->trad_map_path[p - name + 1] = '\0';
223 		strcat(map->trad_map_path, map->map_name);
224 		strcat(map->trad_map_path, dbm_pag);
225 
226 		/* Generate qualified TTL file name */
227 		map->ttl_path = (char *)am(myself, strlen(map->map_path) +
228 						strlen(TTL_POSTFIX) + 1);
229 		if (NULL == map->ttl_path) {
230 			logmsg(MSG_NOMEM, LOG_ERR,
231 				"Could not alocate memory for "
232 				"ttl path derived from %s", name);
233 			return (FAILURE);
234 		}
235 
236 		strcpy(map->ttl_path, map->map_path);
237 		strcat(map->ttl_path, TTL_POSTFIX);
238 	}
239 
240 	/* Work out hash value */
241 	map->hash_val = hash(name);
242 
243 	/* Set up magic number */
244 	map->magic = MAP_MAGIC;
245 
246 	/* Null out pointers */
247 	map->entries = NULL;
248 	map->ttl = NULL;
249 
250 	/* No key data yet */
251 	map->key_data.dptr = NULL;
252 	map->key_data.dsize = 0;
253 
254 	return (SUCCESS);
255 }
256 
257 /*
258  * FUNCTION: 	get_map_crtl();
259  *
260  * DESCRIPTION: Find an existing map_ctrl for a map of a given DBM * (i.e.
261  *		handle) . If none exists return an error.
262  *
263  * INPUTS:	Map handle
264  *
265  * OUTPUTS:	Pointer to map_ctrl
266  *		NULL on failure.
267  *
268  */
269 map_ctrl *
get_map_ctrl(DBM * db)270 get_map_ctrl(DBM *db)
271 {
272 	/* Check that this really is a map_ctrl not a DBM */
273 	if (((map_ctrl *)db)->magic != MAP_MAGIC) {
274 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
275 				"SHIM called with DBM ptr not map_crtl ptr");
276 		return (NULL);
277 	}
278 
279 	/* Since this is an opaque pointer just cast it */
280 	return ((map_ctrl *)db);
281 }
282 
283 /*
284  * FUNCTION:	dup_map_ctrl()
285  *
286  * DESCRIPTION:	Duplicates a map_ctrl structure
287  *
288  * GIVEN :	Map_ctrl to duplicate
289  *
290  * RETURNS :	Pointer to a new malloced map_ctrl. CALLER MUST FREE
291  *		NULL on failure.
292  */
293 map_ctrl *
dup_map_ctrl(map_ctrl * old_map)294 dup_map_ctrl(map_ctrl *old_map)
295 {
296 	map_ctrl *new_map;
297 
298 	/*
299 	 * Could save a little bit of time by duplicating the static parts
300 	 * of the old map but on balance it is cleaner to just make a new one
301 	 * from scratch
302 	 */
303 	new_map = create_map_ctrl(old_map->map_path);
304 
305 	if (NULL == new_map)
306 		return (NULL);
307 
308 	/* If old map had open handles duplicate them */
309 	if (NULL != old_map->entries) {
310 		new_map->open_flags = old_map->open_flags;
311 		new_map->open_mode = old_map->open_mode;
312 		if (FAILURE == open_yptol_files(new_map)) {
313 			free_map_ctrl(new_map);
314 			return (NULL);
315 		}
316 	}
317 
318 	return (new_map);
319 }
320 
321 /*
322  * FUNCTION: 	free_map_crtl();
323  *
324  * DESCRIPTION: Free contents of a map_ctr structure and closed any open
325  *		DBM files.
326  *
327  * INPUTS:	Pointer to pointer to a map_ctrl.
328  *
329  * OUTPUTS:	Nothing
330  *
331  */
332 void
free_map_ctrl(map_ctrl * map)333 free_map_ctrl(map_ctrl *map)
334 {
335 
336 	if (NULL != map->entries) {
337 		dbm_close(map->entries);
338 		map->entries = NULL;
339 	}
340 
341 	if (NULL != map->map_name) {
342 		sfree(map->map_name);
343 		map->map_name = NULL;
344 	}
345 
346 	if (NULL != map->map_path) {
347 		sfree(map->map_path);
348 		map->map_path = NULL;
349 	}
350 
351 	if (NULL != map->domain) {
352 		sfree(map->domain);
353 		map->domain = NULL;
354 	}
355 
356 	if (yptol_mode) {
357 		if (NULL != map->ttl) {
358 			dbm_close(map->ttl);
359 			map->ttl = NULL;
360 		}
361 
362 		if (NULL != map->trad_map_path) {
363 			sfree(map->trad_map_path);
364 			map->trad_map_path = NULL;
365 		}
366 
367 		if (NULL != map->ttl_path) {
368 			sfree(map->ttl_path);
369 			map->ttl_path = NULL;
370 		}
371 
372 		if (NULL != map->key_data.dptr) {
373 			sfree(map->key_data.dptr);
374 			map->key_data.dptr = NULL;
375 			map->key_data.dsize = 0;
376 		}
377 	}
378 
379 	map->magic = 0;
380 
381 	/* Since map_ctrls are now always in malloced memory */
382 	sfree(map);
383 
384 }
385 
386 /*
387  * FUNCTION :	get_map_name()
388  *
389  * DESCRIPTION:	Get the name of a map from its map_ctrl. This could be done
390  *		as a simple dereference but this function hides the internal
391  *		implementation of map_ctrl from higher layers.
392  *
393  * GIVEN :	A map_ctrl pointer
394  *
395  * RETURNS :	A pointer to the map_ctrl. Higher levels treat this as an
396  *		opaque DBM pointer.
397  *		NULL on failure.
398  */
399 char *
get_map_name(DBM * db)400 get_map_name(DBM *db)
401 {
402 	map_ctrl *map = (map_ctrl *)db;
403 
404 	if (NULL == map)
405 		return (NULL);
406 
407 	return (map->map_name);
408 }
409 
410 /*
411  * FUNCTION :	set_key_data()
412  *
413  * DESCRIPTION:	Sets up the key data freeing any that already exists.
414  *
415  * GIVEN :	Pointer to the map_ctrl to set up.
416  *		Datum containing the key. The dptr of this will be set to
417  *		point to the key data.
418  *
419  * RETURNS :	Nothing
420  */
421 void
set_key_data(map_ctrl * map,datum * data)422 set_key_data(map_ctrl *map, datum *data)
423 {
424 	char *myself = "set_key_data";
425 
426 	/*
427 	 * Free up any existing key data. Because each dbm file can only have
428 	 * one enumeration going at a time this is safe.
429 	 */
430 	if (NULL != map->key_data.dptr) {
431 		sfree(map->key_data.dptr);
432 		map->key_data.dptr = NULL;
433 		map->key_data.dsize = 0;
434 	}
435 
436 	/* If nothing in key just return */
437 	if (NULL == data->dptr)
438 		return;
439 
440 	/* Something is in the key so must duplicate out of static memory */
441 	map->key_data.dptr = (char *)am(myself, data->dsize);
442 	if (NULL == map->key_data.dptr) {
443 		logmsg(MSG_NOMEM, LOG_ERR, "Cannot alloc memory for key data");
444 	} else {
445 		memcpy(map->key_data.dptr, data->dptr, data->dsize);
446 		map->key_data.dsize = data->dsize;
447 	}
448 
449 	/* Set datum to point to malloced version of the data */
450 	data->dptr = map->key_data.dptr;
451 
452 	return;
453 
454 }
455 
456 /*
457  * FUNCTION :	open_yptol_files()
458  *
459  * DESCRIPTION:	Opens both yptol files for a map. This is called both when a
460  *		map is opened and when it is reopened as a result of an update
461  *		operation. Must be called with map locked.
462  *
463  * GIVEN :	Initialized map_ctrl
464  *
465  * RETURNS :	SUCCESS = Maps opened
466  *		FAILURE = Maps not opened (and mess tidied up)
467  */
468 suc_code
open_yptol_files(map_ctrl * map)469 open_yptol_files(map_ctrl *map)
470 {
471 
472 	/* Open entries map */
473 	map->entries = dbm_open(map->map_path, map->open_flags, map->open_mode);
474 
475 	if (NULL == map->entries) {
476 		/* Maybe we were asked to open a non-existent map. No problem */
477 		return (FAILURE);
478 	}
479 
480 	if (yptol_mode) {
481 		/* Open TTLs map. Must always be writable */
482 		map->ttl = dbm_open(map->ttl_path, O_RDWR | O_CREAT, 0644);
483 		if (NULL == map->ttl) {
484 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
485 				"Cannot open TTL file %s", map->ttl_path);
486 			dbm_close(map->entries);
487 			map->entries = NULL;
488 			return (FAILURE);
489 		}
490 	}
491 
492 	return (SUCCESS);
493 }
494 
495 /*
496  * FUNCTION :   insert_map_in_list()
497  *
498  * DESCRIPTION:	add a map in map_id_list[]
499  *
500  * GIVEN :      map name
501  *              map unique ID
502  *
503  * RETURNS :	SUCCESS = map added
504  *		FAILURE = map not added
505  */
506 suc_code
insert_map_in_list(char * map_name,int unique_value)507 insert_map_in_list(char *map_name, int unique_value)
508 {
509 	int index;
510 	bool_t yptol_nl_sav = yptol_newlock;
511 	map_id_elt_t *new_elt;
512 
513 	/*
514 	 * Index in the hash table is computed from the original
515 	 * hash function: make sure yptol_newlock is set to false.
516 	 */
517 	yptol_newlock = FALSE;
518 	index = hash(map_name);
519 	yptol_newlock = yptol_nl_sav;
520 
521 	new_elt = (map_id_elt_t *)calloc(1, sizeof (map_id_elt_t));
522 	if (new_elt == NULL) {
523 		return (FAILURE);
524 	}
525 	new_elt->map_name = strdup(map_name);
526 	if (new_elt->map_name == NULL) { /* strdup() failed */
527 		sfree(new_elt);
528 		return (FAILURE);
529 	}
530 	new_elt->map_id = unique_value;
531 
532 	if (map_id_list[index] == NULL) {
533 		new_elt->next = NULL;
534 	} else {
535 		new_elt->next = map_id_list[index];
536 	}
537 	/* insert at begining */
538 	map_id_list[index] = new_elt;
539 
540 	return (SUCCESS);
541 }
542 
543 #ifdef NISDB_LDAP_DEBUG
544 /*
545  * FUNCTION :   dump_map_id_list()
546  *
547  * DESCRIPTION:	display max_map and dump map_id_list[]
548  *		not called, here for debug convenience only
549  *
550  * GIVEN :      nothing
551  *
552  * RETURNS :	nothing
553  */
554 void
dump_map_id_list()555 dump_map_id_list()
556 {
557 	int i;
558 	map_id_elt_t *cur_elt;
559 
560 	logmsg(MSG_NOTIMECHECK, LOG_DEBUG,
561 		"dump_map_id_list: max_map is: %d, dumping map_idlist ...",
562 		max_map);
563 
564 	for (i = 0; i < MAXHASH; i++) {
565 		if (map_id_list[i] == NULL) {
566 			logmsg(MSG_NOTIMECHECK, LOG_DEBUG,
567 				"no map for index %d", i);
568 		} else {
569 			logmsg(MSG_NOTIMECHECK, LOG_DEBUG,
570 				"index %d has the following maps", i);
571 			cur_elt = map_id_list[i];
572 			do {
573 				logmsg(MSG_NOTIMECHECK, LOG_DEBUG,
574 					"%s, unique id: %d",
575 					cur_elt->map_name,
576 					cur_elt->map_id);
577 				cur_elt = cur_elt->next;
578 			} while (cur_elt != NULL);
579 		}
580 	}
581 }
582 #endif
583 
584 /*
585  * FUNCTION :   free_map_id_list()
586  *
587  * DESCRIPTION:	free all previously allocated elements of map_id_list[]
588  *		reset max_map to 0
589  *
590  * GIVEN :      nothing
591  *
592  * RETURNS :	nothing
593  */
594 void
free_map_id_list()595 free_map_id_list()
596 {
597 	int i;
598 	map_id_elt_t *cur_elt, *next_elt;
599 
600 	for (i = 0; i < MAXHASH; i++) {
601 		if (map_id_list[i] != NULL) {
602 			cur_elt = map_id_list[i];
603 			do {
604 				next_elt = cur_elt->next;
605 				if (cur_elt->map_name)
606 					sfree(cur_elt->map_name);
607 				sfree(cur_elt);
608 				cur_elt = next_elt;
609 			} while (cur_elt != NULL);
610 			map_id_list[i] = NULL;
611 		}
612 	}
613 	max_map = 0;
614 }
615 
616 /*
617  * FUNCTION :   map_id_list_init()
618  *
619  * DESCRIPTION:	initializes map_id_list[] and max_map
620  *
621  * GIVEN :      nothing
622  *
623  * RETURNS :	 0 if OK
624  *		-1 if failure
625  */
626 int
map_id_list_init()627 map_id_list_init()
628 {
629 	char **domain_list, **map_list = NULL;
630 	int domain_num;
631 	int i, j;
632 	char *myself = "map_id_list_init";
633 	char mapbuf[MAXPATHLEN];
634 	int mapbuf_len = sizeof (mapbuf);
635 	int map_name_len;
636 	int seqnum = 0;
637 	int rc = 0;
638 
639 	for (i = 0; i < MAXHASH; i++) {
640 		map_id_list[i] = NULL;
641 	}
642 
643 	domain_num = get_mapping_domain_list(&domain_list);
644 	for (i = 0; i < domain_num; i++) {
645 		if (map_list) {
646 			free_map_list(map_list);
647 			map_list = NULL;
648 		}
649 
650 		/* get map list from mapping file */
651 		map_list = get_mapping_map_list(domain_list[i]);
652 		if (map_list == NULL) {
653 			/* no map for this domain in mapping file */
654 			logmsg(MSG_NOTIMECHECK, LOG_DEBUG,
655 			    "%s: get_mapping_map_list()"
656 			    " found no map for domain %s",
657 			    myself, domain_list[i]);
658 		}
659 
660 		/* add maps from /var/yp/<domain> */
661 		if (add_map_domain_to_list(domain_list[i], &map_list) ==
662 		    FALSE) {
663 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
664 			    "%s: add_map_domain_to_list() failed", myself);
665 			free_map_id_list();
666 			if (map_list) free_map_list(map_list);
667 			return (-1);
668 		}
669 
670 		if (map_list == NULL || map_list[0] == NULL) {
671 			logmsg(MSG_NOTIMECHECK, LOG_DEBUG,
672 			    "%s: no map in domain %s",
673 			    myself, domain_list[i]);
674 			continue;
675 		}
676 
677 		for (j = 0; map_list[j] != NULL; j++) {
678 			/* build long name */
679 			map_name_len = ypdbpath_sz + 1 +
680 					strlen(domain_list[i]) + 1 +
681 					strlen(NTOL_PREFIX) +
682 					strlen(map_list[j]) + 1;
683 			if (map_name_len > mapbuf_len) {
684 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
685 				    "%s: map name too long for %s",
686 				    " in domain %s", myself, map_list[j],
687 				    domain_list[i]);
688 				free_map_id_list();
689 				if (map_list) free_map_list(map_list);
690 				return (-1);
691 			}
692 			(void) memset(mapbuf, 0, mapbuf_len);
693 			(void) snprintf(mapbuf, map_name_len, "%s%c%s%c%s%s",
694 				ypdbpath, SEP_CHAR, domain_list[i], SEP_CHAR,
695 				NTOL_PREFIX, map_list[j]);
696 
697 			if (insert_map_in_list(mapbuf, seqnum)
698 				    == FAILURE) {
699 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
700 				    "%s: failed to insert map %s",
701 				    " in domain %s", myself, map_list[j]);
702 				free_map_id_list();
703 				if (map_list) free_map_list(map_list);
704 				return (-1);
705 			}
706 			seqnum++;
707 		}
708 	}
709 
710 	max_map = seqnum;
711 
712 #ifdef NISDB_LDAP_DEBUG
713 	dump_map_id_list();
714 #endif
715 
716 	/*
717 	 * If more maps than allocated spaces in shared memory, that's a failure
718 	 * probably need to free previously allocated memory if failure,
719 	 * before returning.
720 	 */
721 	if (max_map > MAXHASH) {
722 		rc = -1;
723 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
724 		    "%s: too many maps (%d)",
725 		    myself, max_map);
726 		free_map_id_list();
727 	}
728 	if (map_list) free_map_list(map_list);
729 	return (rc);
730 }
731 
732 /*
733  * FUNCTION :   get_list_max()
734  *
735  * DESCRIPTION: return references to static variables map_id_list
736  *              and max_map;
737  *
738  * GIVEN :      address for referencing map_id_list
739  *              address for referencing max_map
740  *
741  * RETURNS :    nothing
742  */
743 void
get_list_max(map_id_elt_t *** list,int * max)744 get_list_max(map_id_elt_t ***list, int *max)
745 {
746 	*list = map_id_list;
747 	*max = max_map;
748 }
749