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.
33 * This file implements the database functionality used by the
34 * switch and configuration components. The implementation is
35 * based on hash and has table. If the need arises in the future,
36 * the code in this file can be changed to use other algorithms.
37 * The following lists the functions implemented:
40 * _nscd_delete_db_entry
45 * _nscd_delete_db_entry_cookie
49 * This structure defines an instance of the hash entry
50 * which implements the nscd database entry. The
51 * db_entry field should always be the first one in
54 typedef struct nscd_hash
{
55 nscd_db_entry_t db_entry
;
56 struct nscd_hash
*next_p
;
57 struct nscd_hash
*prev_p
;
61 * This structure defines a nscd database which
62 * is implemented as an array of nscd_hash_t.
65 int array_size
; /* number of elements in hash_tbl_p */
66 nscd_hash_t
**hash_tbl_p
;
70 * This cookie structure is used to iterate through the
71 * database entries contained in a nscd database.
74 int idx
; /* the current bucket */
75 nscd_hash_t
*hash
; /* the current hash entry */
76 nscd_db_t
*db
; /* the database */
82 * Calculate a hash for a string based on the elf_hash
83 * algorithm, hash is case insensitive. Uses tolower
84 * instead of _tolower because of I18N.
90 unsigned int hval
= 0;
93 while (*str
!= '\0') {
99 hval
= (hval
<< 4) + ch
;
100 if ((g
= (hval
& 0xf0000000)) != 0)
104 return ((unsigned long)hval
);
108 * FUNCTION: scan_hash
110 * Scan a hash table for a matching hash entry. Assume 'str' is
111 * not NULL. If option is NSCD_GET_NEXT_DB_ENTRY and id_num
112 * is less than zero, then treats the option as NSCD_GET_FIRST_DB_ENTRY.
115 static const nscd_hash_t
*
119 const nscd_hash_t
*idx_p
,
120 nscd_db_option_t option
,
124 nscd_db_entry_t
*db_entry
;
126 while (idx_p
!= NULL
) {
127 db_entry
= &((nscd_hash_t
*)idx_p
)->db_entry
;
128 if (db_entry
->type
== type
) {
129 if (strcasecmp(str
, db_entry
->name
) == 0) {
131 case NSCD_GET_FIRST_DB_ENTRY
:
133 case NSCD_GET_EXACT_DB_ENTRY
:
134 if (id_num
== db_entry
->id_num
)
137 case NSCD_GET_NEXT_DB_ENTRY
:
142 if (id_num
== db_entry
->id_num
)
148 idx_p
= idx_p
->next_p
;
154 * FUNCTION: _nscd_get_db_entry
156 * Find a nscd database entry from a nscd database.
158 const nscd_db_entry_t
*
163 nscd_db_option_t option
,
167 const nscd_hash_t
*idx_p
, *hash_p
;
169 if (db
== NULL
|| str
== NULL
)
172 hash
= calc_hash(str
);
173 idx_p
= db
->hash_tbl_p
[hash
% db
->array_size
];
175 hash_p
= scan_hash(type
, str
, idx_p
, option
, id_num
);
177 return (&hash_p
->db_entry
);
181 * FUNCTION: _nscd_add_db_entry
183 * Add a nscd database entry to a nscd database. This function
184 * is not MT safe. The caller should lock the database to
185 * prevent concurrent updates done by other threads.
191 nscd_db_entry_t
*entry
,
192 nscd_db_option_t option
)
196 nscd_hash_t
*next_p
= NULL
, *prev_p
= NULL
;
197 nscd_hash_t
*idx_p
, *hash_entry
;
198 nscd_db_entry_t
*db_entry
;
200 /* find the bucket */
201 hash
= calc_hash(str
);
202 i
= hash
% db
->array_size
;
203 idx_p
= db
->hash_tbl_p
[i
];
205 /* can not replace nothing */
207 if (option
== NSCD_ADD_DB_ENTRY_REPLACE
)
208 return (NSCD_DB_ENTRY_NOT_FOUND
);
210 while (idx_p
!= NULL
) {
211 db_entry
= &idx_p
->db_entry
;
214 case NSCD_ADD_DB_ENTRY_FIRST
:
218 case NSCD_ADD_DB_ENTRY_REPLACE
:
219 if (db_entry
->type
!= entry
->type
)
221 if (strcasecmp(db_entry
->name
, str
) != 0)
224 if (db_entry
->id_num
== entry
->id_num
) {
225 prev_p
= idx_p
->prev_p
;
226 next_p
= idx_p
->next_p
;
232 case NSCD_ADD_DB_ENTRY_IF_NONE
:
233 if (db_entry
->type
!= entry
->type
)
235 if (strcasecmp(db_entry
->name
, str
) != 0)
237 return (NSCD_DB_ENTRY_FOUND
);
240 if (idx_p
->next_p
== NULL
) {
241 if (option
== NSCD_ADD_DB_ENTRY_LAST
||
242 option
== NSCD_ADD_DB_ENTRY_IF_NONE
) {
249 idx_p
= idx_p
->next_p
;
255 * the nscd_entry_t field should be the first field
258 hash_entry
= (nscd_hash_t
*)entry
;
260 /* update the prev link list */
261 hash_entry
->prev_p
= prev_p
;
263 db
->hash_tbl_p
[i
] = hash_entry
;
265 prev_p
->next_p
= hash_entry
;
267 /* update the next link list */
268 hash_entry
->next_p
= next_p
;
270 next_p
->prev_p
= hash_entry
;
272 return (NSCD_SUCCESS
);
276 * FUNCTION: _nscd_delete_db_entry
278 * Delete a nscd database entry from a nscd database. This
279 * function is not MT safe. The caller should lock the
280 * database to prevent concurrent updates done by other
284 _nscd_delete_db_entry(
288 nscd_db_option_t option
,
294 nscd_hash_t
*idx_p
, *next_p
= NULL
, *prev_p
= NULL
;
295 nscd_db_entry_t
*db_entry
;
297 /* find the bucket */
298 hash
= calc_hash(str
);
299 i
= hash
% db
->array_size
;
300 idx_p
= db
->hash_tbl_p
[i
];
302 /* delete nothing always works */
304 return (NSCD_SUCCESS
);
306 while (idx_p
!= NULL
) {
307 db_entry
= &idx_p
->db_entry
;
308 if (db_entry
->type
!= type
)
310 if (strcasecmp(db_entry
->name
, str
) != 0)
315 case NSCD_DEL_FIRST_DB_ENTRY
:
316 prev_p
= idx_p
->prev_p
;
317 next_p
= idx_p
->next_p
;
321 case NSCD_DEL_EXACT_DB_ENTRY
:
322 if (db_entry
->id_num
== id_num
) {
323 prev_p
= idx_p
->prev_p
;
324 next_p
= idx_p
->next_p
;
330 case NSCD_DEL_ALL_DB_ENTRY
:
331 prev_p
= idx_p
->prev_p
;
332 next_p
= idx_p
->next_p
;
337 db
->hash_tbl_p
[i
] = next_p
;
339 prev_p
->next_p
= next_p
;
342 next_p
->prev_p
= prev_p
;
349 * only when option == NSCD_DEL_ALL_DB_ENTRY
350 * will we get here. next_p should be set to
351 * idx_p->next_p beforehand
358 idx_p
= idx_p
->next_p
;
361 return (NSCD_SUCCESS
);
365 * FUNCTION: _nscd_alloc_db_entry
367 * Allocate and return the memory for a database entry
368 * so the caller can insert data and then add it to the
372 _nscd_alloc_db_entry(
384 /* first part: hash data structure and name string */
385 size
= sizeof (*hash
) + strlen(name
) + 1;
386 array_o
= size
= roundup(size
);
388 /* second part: pointer array to data */
389 size
+= (num_data
+ num_array
) * sizeof (void **);
390 size
= roundup(size
);
392 /* third part: actual data */
396 /* allocate the memory */
397 hash
= (nscd_hash_t
*)calloc(1, size
);
402 /* init the entry based on caller's input */
403 hash
->db_entry
.num_data
= num_data
;
404 hash
->db_entry
.num_array
= num_array
;
405 hash
->db_entry
.type
= type
;
406 hash
->db_entry
.name
= (char *)hash
+ sizeof (*hash
);
407 p
= (char *)hash
+ array_o
;
408 hash
->db_entry
.data_array
= (void **)p
;
409 *(hash
->db_entry
.data_array
) = (char *)hash
+ data_o
;
410 (void) strcpy(hash
->db_entry
.name
, name
);
412 return (&hash
->db_entry
);
416 * FUNCTION: _nscd_delete_db_entry_cookie
418 * Delete a database entry using the information from
419 * the input 'cookie'.
422 _nscd_delete_db_entry_cookie(
430 if (cookie
== NULL
|| *cookie
== NULL
|| db
== NULL
)
434 /* more snaity check */
435 if (db
!= c
->db
|| c
->hash
== NULL
||
436 c
->idx
< 0 || c
->idx
>= db
->array_size
)
439 /* retrieve the hash entry from the cookie */
443 * Update the next/previous link list.
444 * Need to update c->hash as well, in case
445 * the cookie is also used in a walk-db
446 * loop. This is to make sure that the
447 * next _nscd_walk_db() call will
448 * find the (updated) next hash entry in line.
450 if (hp
->prev_p
== NULL
) {
452 * make sure the current bucket will be
453 * walked again if _nscd_walk_db is
457 db
->hash_tbl_p
[c
->idx
] = hp
->next_p
;
461 c
->hash
= hp
->prev_p
;
462 hp
->prev_p
->next_p
= hp
->next_p
;
464 if (hp
->next_p
!= NULL
)
465 hp
->next_p
->prev_p
= hp
->prev_p
;
467 /* delete the entry */
472 * FUNCTION: _nscd_alloc_db
474 * Allocate the space for a nscd database.
476 * The input argument, size, indicates the size of the database.
477 * NSCD_DB_SIZE_LARGE specifies an bucket array of size 67,
478 * NSCD_DB_SIZE_MEDIUM specifies an bucket array of size 37,
479 * NSCD_DB_SIZE_SMALL specifies an bucket array of size 13,
480 * NSCD_DB_SIZE_TINY specifies an bucket array of size 3.
489 /* allocate the database */
490 db
= (nscd_db_t
*)calloc(1, sizeof (nscd_db_t
));
494 /* allocate the bucket array */
495 if (size
== NSCD_DB_SIZE_LARGE
)
497 else if (size
== NSCD_DB_SIZE_MEDIUM
)
499 else if (size
== NSCD_DB_SIZE_SMALL
)
501 else if (size
== NSCD_DB_SIZE_TINY
)
503 db
->hash_tbl_p
= (nscd_hash_t
**)calloc(sz
+ 1,
504 sizeof (nscd_hash_t
*));
505 if (db
->hash_tbl_p
== NULL
) {
516 * FUNCTION: _nscd_free_db
518 * Delete a nscd database.
525 nscd_hash_t
*hp
, *next_p
;
528 * find non-empty buckets and for each one of them,
529 * delete all the database entries in it's link list
531 for (i
= 0; i
< db
->array_size
; i
++) {
533 hp
= db
->hash_tbl_p
[i
];
542 /* free the bucket array */
543 free(db
->hash_tbl_p
);
549 * FUNCTION: _nscd_walk_db
551 * Iterate through the database entries contained in
552 * a nscd database and return one entry at a time.
553 * The cookie structure is used to indicate the
554 * location of the returned entry for the next call
555 * to this function. For the very first call, *cookie
556 * should be set to NULL. For subsequent calls, the one
557 * returned by the previous call sould be used.
567 if (cookie
== NULL
|| db
== NULL
)
570 if (*cookie
!= NULL
) {
575 * More sanity check. _nscd_delete_db_entry_cookie()
576 * could change c->idx to -1.
579 c
->idx
< -1 || c
->idx
>= db
->array_size
)
582 /* is there a next entry ? */
584 c
->hash
= c
->hash
->next_p
;
587 if (c
->hash
!= NULL
) {
588 return (&c
->hash
->db_entry
);
591 c
= (struct cookie
*)calloc(1, sizeof (*c
));
599 /* find the first non-empty bucket */
600 for (c
->idx
++; c
->idx
< db
->array_size
; c
->idx
++) {
601 c
->hash
= db
->hash_tbl_p
[c
->idx
];
606 /* no (more) non-empty bucket, we are done */
607 if (c
->hash
== NULL
) {
614 return (&c
->hash
->db_entry
);