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.
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.
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]
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
35 * This file implements the database functionality used by the
36 * switch and configuration components. The implementation is
37 * based on hash and has table. If the need arises in the future,
38 * the code in this file can be changed to use other algorithms.
39 * The following lists the functions implemented:
42 * _nscd_delete_db_entry
47 * _nscd_delete_db_entry_cookie
51 * This structure defines an instance of the hash entry
52 * which implements the nscd database entry. The
53 * db_entry field should always be the first one in
56 typedef struct nscd_hash
{
57 nscd_db_entry_t db_entry
;
58 struct nscd_hash
*next_p
;
59 struct nscd_hash
*prev_p
;
63 * This structure defines a nscd database which
64 * is implemented as an array of nscd_hash_t.
67 int array_size
; /* number of elements in hash_tbl_p */
68 nscd_hash_t
**hash_tbl_p
;
72 * This cookie structure is used to iterate through the
73 * database entries contained in a nscd database.
76 int idx
; /* the current bucket */
77 nscd_hash_t
*hash
; /* the current hash entry */
78 nscd_db_t
*db
; /* the database */
84 * Calculate a hash for a string based on the elf_hash
85 * algorithm, hash is case insensitive. Uses tolower
86 * instead of _tolower because of I18N.
92 unsigned int hval
= 0;
95 while (*str
!= NULL
) {
101 hval
= (hval
<< 4) + ch
;
102 if ((g
= (hval
& 0xf0000000)) != 0)
106 return ((unsigned long)hval
);
110 * FUNCTION: scan_hash
112 * Scan a hash table for a matching hash entry. Assume 'str' is
113 * not NULL. If option is NSCD_GET_NEXT_DB_ENTRY and id_num
114 * is less than zero, then treats the option as NSCD_GET_FIRST_DB_ENTRY.
117 static const nscd_hash_t
*
121 const nscd_hash_t
*idx_p
,
122 nscd_db_option_t option
,
126 nscd_db_entry_t
*db_entry
;
128 while (idx_p
!= NULL
) {
129 db_entry
= &((nscd_hash_t
*)idx_p
)->db_entry
;
130 if (db_entry
->type
== type
) {
131 if (strcasecmp(str
, db_entry
->name
) == 0) {
133 case NSCD_GET_FIRST_DB_ENTRY
:
135 case NSCD_GET_EXACT_DB_ENTRY
:
136 if (id_num
== db_entry
->id_num
)
139 case NSCD_GET_NEXT_DB_ENTRY
:
144 if (id_num
== db_entry
->id_num
)
150 idx_p
= idx_p
->next_p
;
156 * FUNCTION: _nscd_get_db_entry
158 * Find a nscd database entry from a nscd database.
160 const nscd_db_entry_t
*
165 nscd_db_option_t option
,
169 const nscd_hash_t
*idx_p
, *hash_p
;
171 if (db
== NULL
|| str
== NULL
)
174 hash
= calc_hash(str
);
175 idx_p
= db
->hash_tbl_p
[hash
% db
->array_size
];
177 hash_p
= scan_hash(type
, str
, idx_p
, option
, id_num
);
179 return (&hash_p
->db_entry
);
183 * FUNCTION: _nscd_add_db_entry
185 * Add a nscd database entry to a nscd database. This function
186 * is not MT safe. The caller should lock the database to
187 * prevent concurrent updates done by other threads.
193 nscd_db_entry_t
*entry
,
194 nscd_db_option_t option
)
198 nscd_hash_t
*next_p
= NULL
, *prev_p
= NULL
;
199 nscd_hash_t
*idx_p
, *hash_entry
;
200 nscd_db_entry_t
*db_entry
;
202 /* find the bucket */
203 hash
= calc_hash(str
);
204 i
= hash
% db
->array_size
;
205 idx_p
= db
->hash_tbl_p
[i
];
207 /* can not replace nothing */
209 if (option
== NSCD_ADD_DB_ENTRY_REPLACE
)
210 return (NSCD_DB_ENTRY_NOT_FOUND
);
212 while (idx_p
!= NULL
) {
213 db_entry
= &idx_p
->db_entry
;
216 case NSCD_ADD_DB_ENTRY_FIRST
:
220 case NSCD_ADD_DB_ENTRY_REPLACE
:
221 if (db_entry
->type
!= entry
->type
)
223 if (strcasecmp(db_entry
->name
, str
) != 0)
226 if (db_entry
->id_num
== entry
->id_num
) {
227 prev_p
= idx_p
->prev_p
;
228 next_p
= idx_p
->next_p
;
234 case NSCD_ADD_DB_ENTRY_IF_NONE
:
235 if (db_entry
->type
!= entry
->type
)
237 if (strcasecmp(db_entry
->name
, str
) != 0)
239 return (NSCD_DB_ENTRY_FOUND
);
242 if (idx_p
->next_p
== NULL
) {
243 if (option
== NSCD_ADD_DB_ENTRY_LAST
||
244 option
== NSCD_ADD_DB_ENTRY_IF_NONE
) {
251 idx_p
= idx_p
->next_p
;
257 * the nscd_entry_t field should be the first field
260 hash_entry
= (nscd_hash_t
*)entry
;
262 /* update the prev link list */
263 hash_entry
->prev_p
= prev_p
;
265 db
->hash_tbl_p
[i
] = hash_entry
;
267 prev_p
->next_p
= hash_entry
;
269 /* update the next link list */
270 hash_entry
->next_p
= next_p
;
272 next_p
->prev_p
= hash_entry
;
274 return (NSCD_SUCCESS
);
278 * FUNCTION: _nscd_delete_db_entry
280 * Delete a nscd database entry from a nscd database. This
281 * function is not MT safe. The caller should lock the
282 * database to prevent concurrent updates done by other
286 _nscd_delete_db_entry(
290 nscd_db_option_t option
,
296 nscd_hash_t
*idx_p
, *next_p
= NULL
, *prev_p
= NULL
;
297 nscd_db_entry_t
*db_entry
;
299 /* find the bucket */
300 hash
= calc_hash(str
);
301 i
= hash
% db
->array_size
;
302 idx_p
= db
->hash_tbl_p
[i
];
304 /* delete nothing always works */
306 return (NSCD_SUCCESS
);
308 while (idx_p
!= NULL
) {
309 db_entry
= &idx_p
->db_entry
;
310 if (db_entry
->type
!= type
)
312 if (strcasecmp(db_entry
->name
, str
) != 0)
317 case NSCD_DEL_FIRST_DB_ENTRY
:
318 prev_p
= idx_p
->prev_p
;
319 next_p
= idx_p
->next_p
;
323 case NSCD_DEL_EXACT_DB_ENTRY
:
324 if (db_entry
->id_num
== id_num
) {
325 prev_p
= idx_p
->prev_p
;
326 next_p
= idx_p
->next_p
;
332 case NSCD_DEL_ALL_DB_ENTRY
:
333 prev_p
= idx_p
->prev_p
;
334 next_p
= idx_p
->next_p
;
339 db
->hash_tbl_p
[i
] = next_p
;
341 prev_p
->next_p
= next_p
;
344 next_p
->prev_p
= prev_p
;
351 * only when option == NSCD_DEL_ALL_DB_ENTRY
352 * will we get here. next_p should be set to
353 * idx_p->next_p beforehand
360 idx_p
= idx_p
->next_p
;
363 return (NSCD_SUCCESS
);
367 * FUNCTION: _nscd_alloc_db_entry
369 * Allocate and return the memory for a database entry
370 * so the caller can insert data and then add it to the
374 _nscd_alloc_db_entry(
386 /* first part: hash data structure and name string */
387 size
= sizeof (*hash
) + strlen(name
) + 1;
388 array_o
= size
= roundup(size
);
390 /* second part: pointer array to data */
391 size
+= (num_data
+ num_array
) * sizeof (void **);
392 size
= roundup(size
);
394 /* third part: actual data */
398 /* allocate the memory */
399 hash
= (nscd_hash_t
*)calloc(1, size
);
404 /* init the entry based on caller's input */
405 hash
->db_entry
.num_data
= num_data
;
406 hash
->db_entry
.num_array
= num_array
;
407 hash
->db_entry
.type
= type
;
408 hash
->db_entry
.name
= (char *)hash
+ sizeof (*hash
);
409 p
= (char *)hash
+ array_o
;
410 hash
->db_entry
.data_array
= (void **)p
;
411 *(hash
->db_entry
.data_array
) = (char *)hash
+ data_o
;
412 (void) strcpy(hash
->db_entry
.name
, name
);
414 return (&hash
->db_entry
);
418 * FUNCTION: _nscd_delete_db_entry_cookie
420 * Delete a database entry using the information from
421 * the input 'cookie'.
424 _nscd_delete_db_entry_cookie(
432 if (cookie
== NULL
|| *cookie
== NULL
|| db
== NULL
)
436 /* more snaity check */
437 if (db
!= c
->db
|| c
->hash
== NULL
||
438 c
->idx
< 0 || c
->idx
>= db
->array_size
)
441 /* retrieve the hash entry from the cookie */
445 * Update the next/previous link list.
446 * Need to update c->hash as well, in case
447 * the cookie is also used in a walk-db
448 * loop. This is to make sure that the
449 * next _nscd_walk_db() call will
450 * find the (updated) next hash entry in line.
452 if (hp
->prev_p
== NULL
) {
454 * make sure the current bucket will be
455 * walked again if _nscd_walk_db is
459 db
->hash_tbl_p
[c
->idx
] = hp
->next_p
;
463 c
->hash
= hp
->prev_p
;
464 hp
->prev_p
->next_p
= hp
->next_p
;
466 if (hp
->next_p
!= NULL
)
467 hp
->next_p
->prev_p
= hp
->prev_p
;
469 /* delete the entry */
474 * FUNCTION: _nscd_alloc_db
476 * Allocate the space for a nscd database.
478 * The input argument, size, indicates the size of the database.
479 * NSCD_DB_SIZE_LARGE specifies an bucket array of size 67,
480 * NSCD_DB_SIZE_MEDIUM specifies an bucket array of size 37,
481 * NSCD_DB_SIZE_SMALL specifies an bucket array of size 13,
482 * NSCD_DB_SIZE_TINY specifies an bucket array of size 3.
491 /* allocate the database */
492 db
= (nscd_db_t
*)calloc(1, sizeof (nscd_db_t
));
496 /* allocate the bucket array */
497 if (size
== NSCD_DB_SIZE_LARGE
)
499 else if (size
== NSCD_DB_SIZE_MEDIUM
)
501 else if (size
== NSCD_DB_SIZE_SMALL
)
503 else if (size
== NSCD_DB_SIZE_TINY
)
505 db
->hash_tbl_p
= (nscd_hash_t
**)calloc(sz
+ 1,
506 sizeof (nscd_hash_t
*));
507 if (db
->hash_tbl_p
== NULL
) {
518 * FUNCTION: _nscd_free_db
520 * Delete a nscd database.
527 nscd_hash_t
*hp
, *next_p
;
530 * find non-empty buckets and for each one of them,
531 * delete all the database entries in it's link list
533 for (i
= 0; i
< db
->array_size
; i
++) {
535 hp
= db
->hash_tbl_p
[i
];
544 /* free the bucket array */
545 free(db
->hash_tbl_p
);
551 * FUNCTION: _nscd_walk_db
553 * Iterate through the database entries contained in
554 * a nscd database and return one entry at a time.
555 * The cookie structure is used to indicate the
556 * location of the returned entry for the next call
557 * to this function. For the very first call, *cookie
558 * should be set to NULL. For subsequent calls, the one
559 * returned by the previous call sould be used.
569 if (cookie
== NULL
|| db
== NULL
)
572 if (*cookie
!= NULL
) {
577 * More sanity check. _nscd_delete_db_entry_cookie()
578 * could change c->idx to -1.
581 c
->idx
< -1 || c
->idx
>= db
->array_size
)
584 /* is there a next entry ? */
586 c
->hash
= c
->hash
->next_p
;
589 if (c
->hash
!= NULL
) {
590 return (&c
->hash
->db_entry
);
593 c
= (struct cookie
*)calloc(1, sizeof (*c
));
601 /* find the first non-empty bucket */
602 for (c
->idx
++; c
->idx
< db
->array_size
; c
->idx
++) {
603 c
->hash
= db
->hash_tbl_p
[c
->idx
];
608 /* no (more) non-empty bucket, we are done */
609 if (c
->hash
== NULL
) {
616 return (&c
->hash
->db_entry
);