1 /*
2  * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2007 Mellanox Technologies LTD. All rights reserved.
4  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5  * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
6  *
7  * This software is available to you under a choice of one of two
8  * licenses.  You may choose to be licensed under the terms of the GNU
9  * General Public License (GPL) Version 2, available from the file
10  * COPYING in the main directory of this source tree, or the
11  * OpenIB.org BSD license below:
12  *
13  *     Redistribution and use in source and binary forms, with or
14  *     without modification, are permitted provided that the following
15  *     conditions are met:
16  *
17  *      - Redistributions of source code must retain the above
18  *        copyright notice, this list of conditions and the following
19  *        disclaimer.
20  *
21  *      - Redistributions in binary form must reproduce the above
22  *        copyright notice, this list of conditions and the following
23  *        disclaimer in the documentation and/or other materials
24  *        provided with the distribution.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
30  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
31  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33  * SOFTWARE.
34  *
35  */
36 
37 /*
38  * Abstract:
39  * Implementation of the osm_db interface using simple text files
40  */
41 
42 #if HAVE_CONFIG_H
43 #  include <config.h>
44 #endif				/* HAVE_CONFIG_H */
45 
46 #include <sys/stat.h>
47 #include <sys/types.h>
48 #include <errno.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <opensm/osm_file_ids.h>
53 #define FILE_ID OSM_FILE_DB_FILES_C
54 #include <opensm/st.h>
55 #include <opensm/osm_db.h>
56 #include <opensm/osm_log.h>
57 
58 /****d* Database/OSM_DB_MAX_LINE_LEN
59  * NAME
60  * OSM_DB_MAX_LINE_LEN
61  *
62  * DESCRIPTION
63  * The Maximal line length allowed for the file
64  *
65  * SYNOPSIS
66  */
67 #define OSM_DB_MAX_LINE_LEN 1024
68 /**********/
69 
70 /****d* Database/OSM_DB_MAX_GUID_LEN
71  * NAME
72  * OSM_DB_MAX_GUID_LEN
73  *
74  * DESCRIPTION
75  * The Maximal word length allowed for the file (guid or lid)
76  *
77  * SYNOPSIS
78  */
79 #define OSM_DB_MAX_GUID_LEN 32
80 /**********/
81 
82 /****s* OpenSM: Database/osm_db_domain_imp
83  * NAME
84  * osm_db_domain_imp
85  *
86  * DESCRIPTION
87  * An implementation for domain of the database based on text files and
88  *  hash tables.
89  *
90  * SYNOPSIS
91  */
92 typedef struct osm_db_domain_imp {
93 	char *file_name;
94 	st_table *p_hash;
95 	cl_spinlock_t lock;
96 	boolean_t dirty;
97 } osm_db_domain_imp_t;
98 /*
99  * FIELDS
100  *
101  * SEE ALSO
102  * osm_db_domain_t
103  *********/
104 
105 /****s* OpenSM: Database/osm_db_imp_t
106  * NAME
107  * osm_db_imp_t
108  *
109  * DESCRIPTION
110  * An implementation for file based database
111  *
112  * SYNOPSIS
113  */
114 typedef struct osm_db_imp {
115 	const char *db_dir_name;
116 } osm_db_imp_t;
117 /*
118  * FIELDS
119  *
120  * db_dir_name
121  *   The directory holding the database
122  *
123  * SEE ALSO
124  * osm_db_t
125  *********/
126 
127 void osm_db_construct(IN osm_db_t * p_db)
128 {
129 	memset(p_db, 0, sizeof(osm_db_t));
130 	cl_list_construct(&p_db->domains);
131 }
132 
133 void osm_db_domain_destroy(IN osm_db_domain_t * p_db_domain)
134 {
135 	osm_db_domain_imp_t *p_domain_imp;
136 	p_domain_imp = (osm_db_domain_imp_t *) p_db_domain->p_domain_imp;
137 
138 	osm_db_clear(p_db_domain);
139 
140 	cl_spinlock_destroy(&p_domain_imp->lock);
141 
142 	st_free_table(p_domain_imp->p_hash);
143 	free(p_domain_imp->file_name);
144 	free(p_domain_imp);
145 }
146 
147 void osm_db_destroy(IN osm_db_t * p_db)
148 {
149 	osm_db_domain_t *p_domain;
150 
151 	while ((p_domain = cl_list_remove_head(&p_db->domains)) != NULL) {
152 		osm_db_domain_destroy(p_domain);
153 		free(p_domain);
154 	}
155 	cl_list_destroy(&p_db->domains);
156 	free(p_db->p_db_imp);
157 }
158 
159 int osm_db_init(IN osm_db_t * p_db, IN osm_log_t * p_log)
160 {
161 	osm_db_imp_t *p_db_imp;
162 	struct stat dstat;
163 
164 	OSM_LOG_ENTER(p_log);
165 
166 	p_db_imp = malloc(sizeof(osm_db_imp_t));
167 	if (!p_db_imp) {
168 		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6100: "
169 			"Failed to allocate db memory\n");
170 		return -1;
171 	}
172 
173 	p_db_imp->db_dir_name = getenv("OSM_CACHE_DIR");
174 	if (!p_db_imp->db_dir_name || !(*p_db_imp->db_dir_name))
175 		p_db_imp->db_dir_name = OSM_DEFAULT_CACHE_DIR;
176 
177 	/* Create the directory if it doesn't exist */
178 	/* There is a difference in creating directory between windows and linux */
179 #ifdef __WIN__
180 	{
181 		int ret;
182 
183 		ret = SHCreateDirectoryEx(NULL, p_db_imp->db_dir_name, NULL);
184 		if (ret != ERROR_SUCCESS && ret != ERROR_ALREADY_EXISTS &&
185 			ret != ERROR_FILE_EXISTS)
186 			goto err;
187 	}
188 #else				/* __WIN__ */
189 	/* make sure the directory exists */
190 	if (lstat(p_db_imp->db_dir_name, &dstat)) {
191 		if (mkdir(p_db_imp->db_dir_name, 0755))
192 			goto err;
193 	}
194 #endif
195 
196 	p_db->p_log = p_log;
197 	p_db->p_db_imp = (void *)p_db_imp;
198 
199 	cl_list_init(&p_db->domains, 5);
200 
201 	OSM_LOG_EXIT(p_log);
202 
203 	return 0;
204 
205 err:
206 	OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6101: "
207 		"Failed to create the db directory:%s\n",
208 		p_db_imp->db_dir_name);
209 	free(p_db_imp);
210 	OSM_LOG_EXIT(p_log);
211 	return 1;
212 }
213 
214 osm_db_domain_t *osm_db_domain_init(IN osm_db_t * p_db, IN const char *domain_name)
215 {
216 	osm_db_domain_t *p_domain;
217 	osm_db_domain_imp_t *p_domain_imp;
218 	size_t path_len;
219 	osm_log_t *p_log = p_db->p_log;
220 	FILE *p_file;
221 
222 	OSM_LOG_ENTER(p_log);
223 
224 	/* allocate a new domain object */
225 	p_domain = malloc(sizeof(osm_db_domain_t));
226 	if (p_domain == NULL) {
227 		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 610C: "
228 			"Failed to allocate domain memory\n");
229 		goto Exit;
230 	}
231 
232 	p_domain_imp = malloc(sizeof(osm_db_domain_imp_t));
233 	if (p_domain_imp == NULL) {
234 		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 610D: "
235 			"Failed to allocate domain_imp memory\n");
236 		free(p_domain);
237 		p_domain = NULL;
238 		goto Exit;
239 	}
240 
241 	path_len = strlen(((osm_db_imp_t *) p_db->p_db_imp)->db_dir_name)
242 	    + strlen(domain_name) + 2;
243 
244 	/* set the domain file name */
245 	p_domain_imp->file_name = malloc(path_len);
246 	if (p_domain_imp->file_name == NULL) {
247 		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 610E: "
248 			"Failed to allocate file_name memory\n");
249 		free(p_domain_imp);
250 		free(p_domain);
251 		p_domain = NULL;
252 		goto Exit;
253 	}
254 	snprintf(p_domain_imp->file_name, path_len, "%s/%s",
255 		 ((osm_db_imp_t *) p_db->p_db_imp)->db_dir_name, domain_name);
256 
257 	/* make sure the file exists - or exit if not writable */
258 	p_file = fopen(p_domain_imp->file_name, "a+");
259 	if (!p_file) {
260 		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6102: "
261 			"Failed to open the db file:%s\n",
262 			p_domain_imp->file_name);
263 		free(p_domain_imp);
264 		free(p_domain);
265 		p_domain = NULL;
266 		goto Exit;
267 	}
268 	fclose(p_file);
269 
270 	/* initialize the hash table object */
271 	p_domain_imp->p_hash = st_init_strtable();
272 	CL_ASSERT(p_domain_imp->p_hash != NULL);
273 	p_domain_imp->dirty = FALSE;
274 
275 	p_domain->p_db = p_db;
276 	cl_list_insert_tail(&p_db->domains, p_domain);
277 	p_domain->p_domain_imp = p_domain_imp;
278 	cl_spinlock_construct(&p_domain_imp->lock);
279 	cl_spinlock_init(&p_domain_imp->lock);
280 
281 Exit:
282 	OSM_LOG_EXIT(p_log);
283 	return p_domain;
284 }
285 
286 int osm_db_restore(IN osm_db_domain_t * p_domain)
287 {
288 
289 	osm_log_t *p_log = p_domain->p_db->p_log;
290 	osm_db_domain_imp_t *p_domain_imp =
291 	    (osm_db_domain_imp_t *) p_domain->p_domain_imp;
292 	FILE *p_file;
293 	int status;
294 	char sLine[OSM_DB_MAX_LINE_LEN];
295 	boolean_t before_key;
296 	char *p_first_word, *p_rest_of_line, *p_last;
297 	char *p_key = NULL;
298 	char *p_prev_val = NULL, *p_accum_val = NULL;
299 	char *endptr = NULL;
300 	unsigned int line_num;
301 
302 	OSM_LOG_ENTER(p_log);
303 
304 	/* take the lock on the domain */
305 	cl_spinlock_acquire(&p_domain_imp->lock);
306 
307 	/* open the file - read mode */
308 	p_file = fopen(p_domain_imp->file_name, "r");
309 
310 	if (!p_file) {
311 		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6103: "
312 			"Failed to open the db file:%s\n",
313 			p_domain_imp->file_name);
314 		status = 1;
315 		goto Exit;
316 	}
317 
318 	/* parse the file allocating new hash tables as required */
319 	/*
320 	   states:
321 	   before_key (0) -> in_key (1)
322 
323 	   before_key: if a word on the first byte - it is the key. state=in_key
324 	   the rest of the line is start of the value.
325 	   in_key: unless the line is empty - add it (with newlines) to the value.
326 	   if empty: state=before_key
327 	 */
328 	status = 0;
329 	before_key = TRUE;
330 	line_num = 0;
331 	/* if we got to EOF in the middle of a key we add a last newline */
332 	while ((fgets(sLine, OSM_DB_MAX_LINE_LEN, p_file) != NULL) ||
333 	       ((before_key == FALSE) && strcpy(sLine, "\n"))
334 	    ) {
335 		line_num++;
336 		if (before_key) {
337 			if ((sLine[0] != ' ') && (sLine[0] != '\t')
338 			    && (sLine[0] != '\n')) {
339 				/* we got a new key */
340 				before_key = FALSE;
341 
342 				/* handle the key */
343 				p_first_word =
344 				    strtok_r(sLine, " \t\n", &p_last);
345 				if (!p_first_word) {
346 					OSM_LOG(p_log, OSM_LOG_ERROR,
347 						"ERR 6104: "
348 						"Failed to get key from line:%u : %s (file:%s)\n",
349 						line_num, sLine,
350 						p_domain_imp->file_name);
351 					status = 1;
352 					goto EndParsing;
353 				}
354 				if (strlen(p_first_word) > OSM_DB_MAX_GUID_LEN) {
355 					OSM_LOG(p_log, OSM_LOG_ERROR,
356 						"ERR 610A: "
357 						"Illegal key from line:%u : %s (file:%s)\n",
358 						line_num, sLine,
359 						p_domain_imp->file_name);
360 					status = 1;
361 					goto EndParsing;
362 				}
363 
364 				p_key = malloc(sizeof(char) *
365 					       (strlen(p_first_word) + 1));
366 				strcpy(p_key, p_first_word);
367 
368 				p_rest_of_line = strtok_r(NULL, "\n", &p_last);
369 				if (p_rest_of_line != NULL) {
370 					p_accum_val = malloc(sizeof(char) *
371 					    (strlen(p_rest_of_line) + 1));
372 					strcpy(p_accum_val, p_rest_of_line);
373 				} else {
374 					p_accum_val = malloc(2);
375 					strcpy(p_accum_val, "\0");
376 				}
377 			} else if (sLine[0] != '\n') {
378 				OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6105: "
379 					"How did we get here? line:%u : %s (file:%s)\n",
380 					line_num, sLine,
381 					p_domain_imp->file_name);
382 				status = 1;
383 				goto EndParsing;
384 			}
385 		} /* before key */
386 		else {
387 			/* we already have a key */
388 
389 			if (sLine[0] == '\n') {
390 				/* got an end of key */
391 				before_key = TRUE;
392 
393 				/* make sure the key was not previously used */
394 				if (st_lookup(p_domain_imp->p_hash,
395 					      (st_data_t) p_key,
396 					      (void *)&p_prev_val)) {
397 					/* if previously used we ignore this guid */
398 					OSM_LOG(p_log, OSM_LOG_ERROR,
399 						"ERR 6106: "
400 						"Key:%s already exists in:%s with value:%s."
401 						" Removing it\n", p_key,
402 						p_domain_imp->file_name,
403 						p_prev_val);
404 						free(p_key);
405 						p_key = NULL;
406 						free(p_accum_val);
407 						p_accum_val = NULL;
408 						continue;
409 				} else {
410 					p_prev_val = NULL;
411 				}
412 
413 				OSM_LOG(p_log, OSM_LOG_DEBUG,
414 					"Got key:%s value:%s\n", p_key,
415 					p_accum_val);
416 
417 				/* check that the key is a number */
418 				if (!strtouq(p_key, &endptr, 0)
419 				    && *endptr != '\0') {
420 					OSM_LOG(p_log, OSM_LOG_ERROR,
421 						"ERR 610B: "
422 						"Key:%s is invalid\n", p_key);
423 						free(p_key);
424 						p_key = NULL;
425 						free(p_accum_val);
426 						p_accum_val = NULL;
427 				} else {
428 					/* store our key and value */
429 					st_insert(p_domain_imp->p_hash,
430 						  (st_data_t) p_key,
431 						  (st_data_t) p_accum_val);
432 				}
433 			} else {
434 				/* accumulate into the value */
435 				p_prev_val = p_accum_val;
436 				p_accum_val = malloc(strlen(p_prev_val) +
437 						     strlen(sLine) + 1);
438 				strcpy(p_accum_val, p_prev_val);
439 				free(p_prev_val);
440 				p_prev_val = NULL;
441 				strcat(p_accum_val, sLine);
442 			}
443 		}		/* in key */
444 	}			/* while lines or last line */
445 
446 EndParsing:
447 	fclose(p_file);
448 
449 Exit:
450 	cl_spinlock_release(&p_domain_imp->lock);
451 	OSM_LOG_EXIT(p_log);
452 	return status;
453 }
454 
455 static int dump_tbl_entry(st_data_t key, st_data_t val, st_data_t arg)
456 {
457 	FILE *p_file = (FILE *) arg;
458 	char *p_key = (char *)key;
459 	char *p_val = (char *)val;
460 
461 	fprintf(p_file, "%s %s\n\n", p_key, p_val);
462 	return ST_CONTINUE;
463 }
464 
465 int osm_db_store(IN osm_db_domain_t * p_domain,
466 		 IN boolean_t fsync_high_avail_files)
467 {
468 	osm_log_t *p_log = p_domain->p_db->p_log;
469 	osm_db_domain_imp_t *p_domain_imp;
470 	FILE *p_file = NULL;
471 	int fd, status = 0;
472 	char *p_tmp_file_name = NULL;
473 
474 	OSM_LOG_ENTER(p_log);
475 
476 	p_domain_imp = (osm_db_domain_imp_t *) p_domain->p_domain_imp;
477 
478 	p_tmp_file_name = malloc(sizeof(char) *
479 				 (strlen(p_domain_imp->file_name) + 8));
480 	if (!p_tmp_file_name) {
481 		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6113: "
482 			"Failed to allocate memory for temporary file name\n");
483 		goto Exit2;
484 	}
485 	strcpy(p_tmp_file_name, p_domain_imp->file_name);
486 	strcat(p_tmp_file_name, ".tmp");
487 
488 	cl_spinlock_acquire(&p_domain_imp->lock);
489 
490 	if (p_domain_imp->dirty == FALSE)
491 		goto Exit;
492 
493 	/* open up the output file */
494 	p_file = fopen(p_tmp_file_name, "w");
495 	if (!p_file) {
496 		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6107: "
497 			"Failed to open the db file:%s for writing: err:%s\n",
498 			p_domain_imp->file_name, strerror(errno));
499 		status = 1;
500 		goto Exit;
501 	}
502 
503 	st_foreach(p_domain_imp->p_hash, dump_tbl_entry, (st_data_t) p_file);
504 
505 	if (fsync_high_avail_files) {
506 		if (fflush(p_file) == 0) {
507 			fd = fileno(p_file);
508 			if (fd != -1) {
509 				if (fsync(fd) == -1)
510 					OSM_LOG(p_log, OSM_LOG_ERROR,
511 						"ERR 6110: fsync() failed (%s) for %s\n",
512 						strerror(errno),
513 						p_domain_imp->file_name);
514 			} else
515 				OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6111: "
516 					"fileno() failed for %s\n",
517 					p_domain_imp->file_name);
518 		} else
519 			OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6112: "
520 				"fflush() failed (%s) for %s\n",
521 				strerror(errno), p_domain_imp->file_name);
522 	}
523 
524 	fclose(p_file);
525 
526 	status = rename(p_tmp_file_name, p_domain_imp->file_name);
527 	if (status) {
528 		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6108: "
529 			"Failed to rename the db file to:%s (err:%s)\n",
530 			p_domain_imp->file_name, strerror(errno));
531 		goto Exit;
532 	}
533 	p_domain_imp->dirty = FALSE;
534 Exit:
535 	cl_spinlock_release(&p_domain_imp->lock);
536 	free(p_tmp_file_name);
537 Exit2:
538 	OSM_LOG_EXIT(p_log);
539 	return status;
540 }
541 
542 /* simply de-allocate the key and the value and return the code
543    that makes the st_foreach delete the entry */
544 static int clear_tbl_entry(st_data_t key, st_data_t val, st_data_t arg)
545 {
546 	free((char *)key);
547 	free((char *)val);
548 	return ST_DELETE;
549 }
550 
551 int osm_db_clear(IN osm_db_domain_t * p_domain)
552 {
553 	osm_db_domain_imp_t *p_domain_imp =
554 	    (osm_db_domain_imp_t *) p_domain->p_domain_imp;
555 
556 	cl_spinlock_acquire(&p_domain_imp->lock);
557 	st_foreach(p_domain_imp->p_hash, clear_tbl_entry, (st_data_t) NULL);
558 	cl_spinlock_release(&p_domain_imp->lock);
559 
560 	return 0;
561 }
562 
563 static int get_key_of_tbl_entry(st_data_t key, st_data_t val, st_data_t arg)
564 {
565 	cl_list_t *p_list = (cl_list_t *) arg;
566 	cl_list_insert_tail(p_list, (void *)key);
567 	return ST_CONTINUE;
568 }
569 
570 int osm_db_keys(IN osm_db_domain_t * p_domain, OUT cl_list_t * p_key_list)
571 {
572 	osm_db_domain_imp_t *p_domain_imp =
573 	    (osm_db_domain_imp_t *) p_domain->p_domain_imp;
574 
575 	cl_spinlock_acquire(&p_domain_imp->lock);
576 
577 	st_foreach(p_domain_imp->p_hash, get_key_of_tbl_entry,
578 		   (st_data_t) p_key_list);
579 
580 	cl_spinlock_release(&p_domain_imp->lock);
581 
582 	return 0;
583 }
584 
585 char *osm_db_lookup(IN osm_db_domain_t * p_domain, IN char *p_key)
586 {
587 	osm_db_domain_imp_t *p_domain_imp =
588 	    (osm_db_domain_imp_t *) p_domain->p_domain_imp;
589 	char *p_val = NULL;
590 
591 	cl_spinlock_acquire(&p_domain_imp->lock);
592 
593 	if (!st_lookup(p_domain_imp->p_hash, (st_data_t) p_key, (void *)&p_val))
594 		p_val = NULL;
595 
596 	cl_spinlock_release(&p_domain_imp->lock);
597 
598 	return p_val;
599 }
600 
601 int osm_db_update(IN osm_db_domain_t * p_domain, IN char *p_key, IN char *p_val)
602 {
603 	osm_log_t *p_log = p_domain->p_db->p_log;
604 	osm_db_domain_imp_t *p_domain_imp =
605 	    (osm_db_domain_imp_t *) p_domain->p_domain_imp;
606 	char *p_prev_val = NULL;
607 	char *p_new_key;
608 	char *p_new_val;
609 
610 	cl_spinlock_acquire(&p_domain_imp->lock);
611 
612 	if (st_lookup(p_domain_imp->p_hash,
613 		      (st_data_t) p_key, (void *)&p_prev_val)) {
614 		OSM_LOG(p_log, OSM_LOG_DEBUG,
615 			"Key:%s previously exists in:%s with value:%s\n",
616 			p_key, p_domain_imp->file_name, p_prev_val);
617 		p_new_key = p_key;
618 		/* same key, same value - nothing to update */
619 		if (p_prev_val && !strcmp(p_val, p_prev_val))
620 			goto Exit;
621 	} else {
622 		/* need to allocate the key */
623 		p_new_key = malloc(sizeof(char) * (strlen(p_key) + 1));
624 		strcpy(p_new_key, p_key);
625 	}
626 
627 	/* need to arange a new copy of the  value */
628 	p_new_val = malloc(sizeof(char) * (strlen(p_val) + 1));
629 	strcpy(p_new_val, p_val);
630 
631 	st_insert(p_domain_imp->p_hash, (st_data_t) p_new_key,
632 		  (st_data_t) p_new_val);
633 
634 	if (p_prev_val)
635 		free(p_prev_val);
636 
637 	p_domain_imp->dirty = TRUE;
638 
639 Exit:
640 	cl_spinlock_release(&p_domain_imp->lock);
641 
642 	return 0;
643 }
644 
645 int osm_db_delete(IN osm_db_domain_t * p_domain, IN char *p_key)
646 {
647 	osm_log_t *p_log = p_domain->p_db->p_log;
648 	osm_db_domain_imp_t *p_domain_imp =
649 	    (osm_db_domain_imp_t *) p_domain->p_domain_imp;
650 	char *p_prev_val = NULL;
651 	int res;
652 
653 	OSM_LOG_ENTER(p_log);
654 
655 	cl_spinlock_acquire(&p_domain_imp->lock);
656 	if (st_delete(p_domain_imp->p_hash,
657 		      (void *)&p_key, (void *)&p_prev_val)) {
658 		if (st_lookup(p_domain_imp->p_hash,
659 			      (st_data_t) p_key, (void *)&p_prev_val)) {
660 			OSM_LOG(p_log, OSM_LOG_ERROR,
661 				"key:%s still exists in:%s with value:%s\n",
662 				p_key, p_domain_imp->file_name, p_prev_val);
663 			res = 1;
664 		} else {
665 			free(p_key);
666 			free(p_prev_val);
667 			p_domain_imp->dirty = TRUE;
668 			res = 0;
669 		}
670 	} else {
671 		OSM_LOG(p_log, OSM_LOG_DEBUG,
672 			"fail to find key:%s. delete failed\n", p_key);
673 		res = 1;
674 	}
675 	cl_spinlock_release(&p_domain_imp->lock);
676 
677 	OSM_LOG_EXIT(p_log);
678 	return res;
679 }
680 
681 #ifdef TEST_OSMDB
682 #include <stdlib.h>
683 #include <math.h>
684 
685 int main(int argc, char **argv)
686 {
687 	osm_db_t db;
688 	osm_log_t log;
689 	osm_db_domain_t *p_dbd;
690 	cl_list_t keys;
691 	cl_list_iterator_t kI;
692 	char *p_key;
693 	char *p_val;
694 	int i;
695 
696 	cl_list_construct(&keys);
697 	cl_list_init(&keys, 10);
698 
699 	osm_log_init_v2(&log, TRUE, 0xff, "/var/log/osm_db_test.log", 0, FALSE);
700 
701 	osm_db_construct(&db);
702 	if (osm_db_init(&db, &log)) {
703 		printf("db init failed\n");
704 		exit(1);
705 	}
706 
707 	p_dbd = osm_db_domain_init(&db, "lid_by_guid");
708 	if (!p_dbd) {
709 		printf("db domain init failed\n");
710 		exit(1);
711 	}
712 
713 	if (osm_db_restore(p_dbd)) {
714 		printf("failed to restore\n");
715 	}
716 
717 	if (osm_db_keys(p_dbd, &keys)) {
718 		printf("failed to get keys\n");
719 	} else {
720 		kI = cl_list_head(&keys);
721 		while (kI != cl_list_end(&keys)) {
722 			p_key = cl_list_obj(kI);
723 			kI = cl_list_next(kI);
724 
725 			p_val = osm_db_lookup(p_dbd, p_key);
726 			printf("key = %s val = %s\n", p_key, p_val);
727 		}
728 	}
729 
730 	cl_list_remove_all(&keys);
731 
732 	/* randomly add and remove numbers */
733 	for (i = 0; i < 10; i++) {
734 		int k;
735 		float v;
736 		int is_add;
737 		char val_buf[16];
738 		char key_buf[16];
739 
740 		k = floor(1.0 * rand() / RAND_MAX * 100);
741 		v = rand();
742 		sprintf(key_buf, "%u", k);
743 		sprintf(val_buf, "%u", v);
744 
745 		is_add = (rand() < RAND_MAX / 2);
746 
747 		if (is_add) {
748 			osm_db_update(p_dbd, key_buf, val_buf);
749 		} else {
750 			osm_db_delete(p_dbd, key_buf);
751 		}
752 	}
753 	if (osm_db_keys(p_dbd, &keys)) {
754 		printf("failed to get keys\n");
755 	} else {
756 		kI = cl_list_head(&keys);
757 		while (kI != cl_list_end(&keys)) {
758 			p_key = cl_list_obj(kI);
759 			kI = cl_list_next(kI);
760 
761 			p_val = osm_db_lookup(p_dbd, p_key);
762 			printf("key = %s val = %s\n", p_key, p_val);
763 		}
764 	}
765 	if (osm_db_store(p_dbd, FALSE))
766 		printf("failed to store\n");
767 
768 	osm_db_destroy(&db);
769 	cl_list_destroy(&keys);
770 }
771 #endif
772