dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / nscd / nscd_dbimpl.c
blobe981d44557618ab8f734a9fc585c325e1e63e49d
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include "nscd_db.h"
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:
39 * _nscd_add_db_entry
40 * _nscd_delete_db_entry
41 * _nscd_get_db_entry
42 * _nscd_alloc_db
43 * _nscd_free_db
44 * _nscd_walk_db
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
52 * the structure.
54 typedef struct nscd_hash {
55 nscd_db_entry_t db_entry;
56 struct nscd_hash *next_p;
57 struct nscd_hash *prev_p;
58 } nscd_hash_t;
61 * This structure defines a nscd database which
62 * is implemented as an array of nscd_hash_t.
64 struct nscd_db_s {
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.
73 struct cookie {
74 int idx; /* the current bucket */
75 nscd_hash_t *hash; /* the current hash entry */
76 nscd_db_t *db; /* the database */
80 * FUNCTION: calc_hash
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.
86 static unsigned long
87 calc_hash(
88 const char *str)
90 unsigned int hval = 0;
91 char ch;
93 while (*str != '\0') {
94 unsigned int g;
96 ch = (char)*str++;
97 if (isupper(ch))
98 ch = _tolower(ch);
99 hval = (hval << 4) + ch;
100 if ((g = (hval & 0xf0000000)) != 0)
101 hval ^= g >> 24;
102 hval &= ~g;
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 *
116 scan_hash(
117 int type,
118 const char *str,
119 const nscd_hash_t *idx_p,
120 nscd_db_option_t option,
121 int id_num)
123 int id_matched = 0;
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) {
130 switch (option) {
131 case NSCD_GET_FIRST_DB_ENTRY:
132 return (idx_p);
133 case NSCD_GET_EXACT_DB_ENTRY:
134 if (id_num == db_entry->id_num)
135 return (idx_p);
136 break;
137 case NSCD_GET_NEXT_DB_ENTRY:
138 if (id_num < 0)
139 return (idx_p);
140 if (id_matched == 1)
141 return (idx_p);
142 if (id_num == db_entry->id_num)
143 id_matched = 1;
144 break;
148 idx_p = idx_p->next_p;
150 return (NULL);
154 * FUNCTION: _nscd_get_db_entry
156 * Find a nscd database entry from a nscd database.
158 const nscd_db_entry_t *
159 _nscd_get_db_entry(
160 const nscd_db_t *db,
161 int type,
162 const char *str,
163 nscd_db_option_t option,
164 int id_num)
166 unsigned long hash;
167 const nscd_hash_t *idx_p, *hash_p;
169 if (db == NULL || str == NULL)
170 return (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.
187 nscd_rc_t
188 _nscd_add_db_entry(
189 nscd_db_t *db,
190 const char *str,
191 nscd_db_entry_t *entry,
192 nscd_db_option_t option)
194 int i;
195 unsigned long hash;
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 */
206 if (idx_p == NULL)
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;
212 switch (option) {
214 case NSCD_ADD_DB_ENTRY_FIRST:
215 next_p = idx_p;
216 goto add_entry;
218 case NSCD_ADD_DB_ENTRY_REPLACE:
219 if (db_entry->type != entry->type)
220 goto cont;
221 if (strcasecmp(db_entry->name, str) != 0)
222 goto cont;
224 if (db_entry->id_num == entry->id_num) {
225 prev_p = idx_p->prev_p;
226 next_p = idx_p->next_p;
227 free(idx_p);
228 goto add_entry;
230 goto cont;
232 case NSCD_ADD_DB_ENTRY_IF_NONE:
233 if (db_entry->type != entry->type)
234 break;
235 if (strcasecmp(db_entry->name, str) != 0)
236 break;
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) {
243 prev_p = idx_p;
244 goto add_entry;
248 cont:
249 idx_p = idx_p->next_p;
252 add_entry:
255 * the nscd_entry_t field should be the first field
256 * in a nscd_hash_t
258 hash_entry = (nscd_hash_t *)entry;
260 /* update the prev link list */
261 hash_entry->prev_p = prev_p;
262 if (prev_p == NULL)
263 db->hash_tbl_p[i] = hash_entry;
264 else
265 prev_p->next_p = hash_entry;
267 /* update the next link list */
268 hash_entry->next_p = next_p;
269 if (next_p != NULL)
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
281 * threads.
283 nscd_rc_t
284 _nscd_delete_db_entry(
285 nscd_db_t *db,
286 int type,
287 const char *str,
288 nscd_db_option_t option,
289 int id_num)
291 int i;
292 int del_more = 0;
293 unsigned long hash;
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 */
303 if (idx_p == NULL)
304 return (NSCD_SUCCESS);
306 while (idx_p != NULL) {
307 db_entry = &idx_p->db_entry;
308 if (db_entry->type != type)
309 goto cont;
310 if (strcasecmp(db_entry->name, str) != 0)
311 goto cont;
313 switch (option) {
315 case NSCD_DEL_FIRST_DB_ENTRY:
316 prev_p = idx_p->prev_p;
317 next_p = idx_p->next_p;
318 del_more = 0;
319 break;
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;
325 del_more = 0;
326 } else
327 goto cont;
328 break;
330 case NSCD_DEL_ALL_DB_ENTRY:
331 prev_p = idx_p->prev_p;
332 next_p = idx_p->next_p;
333 break;
336 if (prev_p == NULL)
337 db->hash_tbl_p[i] = next_p;
338 else
339 prev_p->next_p = next_p;
341 if (next_p != NULL)
342 next_p->prev_p = prev_p;
344 free(idx_p);
346 if (del_more == 0)
347 break;
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
353 idx_p = next_p;
354 continue;
356 cont:
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
369 * database.
371 nscd_db_entry_t *
372 _nscd_alloc_db_entry(
373 int type,
374 const char *name,
375 int dataSize,
376 int num_data,
377 int num_array)
379 int size;
380 int array_o, data_o;
381 nscd_hash_t *hash;
382 void *p;
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 */
393 data_o = size;
394 size += dataSize;
396 /* allocate the memory */
397 hash = (nscd_hash_t *)calloc(1, size);
399 if (hash == NULL)
400 return (NULL);
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'.
421 void
422 _nscd_delete_db_entry_cookie(
423 nscd_db_t *db,
424 void **cookie)
426 nscd_hash_t *hp;
427 struct cookie *c;
429 /* snaity check */
430 if (cookie == NULL || *cookie == NULL || db == NULL)
431 return;
432 c = *cookie;
434 /* more snaity check */
435 if (db != c->db || c->hash == NULL ||
436 c->idx < 0 || c->idx >= db->array_size)
437 return;
439 /* retrieve the hash entry from the cookie */
440 hp = c->hash;
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
454 * called next
456 c->hash = NULL;
457 db->hash_tbl_p[c->idx] = hp->next_p;
458 c->idx--;
460 } else {
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 */
468 free(hp);
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.
482 nscd_db_t *
483 _nscd_alloc_db(
484 int size)
486 int sz;
487 nscd_db_t *db;
489 /* allocate the database */
490 db = (nscd_db_t *)calloc(1, sizeof (nscd_db_t));
491 if (db == NULL)
492 return (NULL);
494 /* allocate the bucket array */
495 if (size == NSCD_DB_SIZE_LARGE)
496 sz = 67;
497 else if (size == NSCD_DB_SIZE_MEDIUM)
498 sz = 37;
499 else if (size == NSCD_DB_SIZE_SMALL)
500 sz = 13;
501 else if (size == NSCD_DB_SIZE_TINY)
502 sz = 3;
503 db->hash_tbl_p = (nscd_hash_t **)calloc(sz + 1,
504 sizeof (nscd_hash_t *));
505 if (db->hash_tbl_p == NULL) {
506 free(db);
507 return (NULL);
510 db->array_size = sz;
512 return (db);
516 * FUNCTION: _nscd_free_db
518 * Delete a nscd database.
520 void
521 _nscd_free_db(
522 nscd_db_t *db)
524 int i;
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];
535 while (hp != NULL) {
536 next_p = hp->next_p;
537 free(hp);
538 hp = next_p;
542 /* free the bucket array */
543 free(db->hash_tbl_p);
545 free(db);
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.
559 nscd_db_entry_t *
560 _nscd_walk_db(
561 nscd_db_t *db,
562 void **cookie)
564 struct cookie *c;
566 /* sanity check */
567 if (cookie == NULL || db == NULL)
568 return (NULL);
570 if (*cookie != NULL) {
572 c = *cookie;
575 * More sanity check. _nscd_delete_db_entry_cookie()
576 * could change c->idx to -1.
578 if (db != c->db ||
579 c->idx < -1 || c->idx >= db->array_size)
580 return (NULL);
582 /* is there a next entry ? */
583 if (c->hash != NULL)
584 c->hash = c->hash->next_p;
586 /* yes, return it */
587 if (c->hash != NULL) {
588 return (&c->hash->db_entry);
590 } else {
591 c = (struct cookie *)calloc(1, sizeof (*c));
592 if (c == NULL)
593 return (NULL);
594 c->idx = -1;
595 c->hash = NULL;
596 c->db = db;
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];
602 if (c->hash != NULL)
603 break;
606 /* no (more) non-empty bucket, we are done */
607 if (c->hash == NULL) {
608 free(c);
609 *cookie = NULL;
610 return (NULL);
613 *cookie = c;
614 return (&c->hash->db_entry);