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 */
92typedef 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 */
114typedef 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
127void 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
133void 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
147void 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
159int 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
205err:
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
214osm_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
281Exit:
282	OSM_LOG_EXIT(p_log);
283	return p_domain;
284}
285
286int 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
446EndParsing:
447	fclose(p_file);
448
449Exit:
450	cl_spinlock_release(&p_domain_imp->lock);
451	OSM_LOG_EXIT(p_log);
452	return status;
453}
454
455static 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
465int 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;
534Exit:
535	cl_spinlock_release(&p_domain_imp->lock);
536	free(p_tmp_file_name);
537Exit2:
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 */
544static 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
551int 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
563static 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
570int 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
585char *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
601int 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
639Exit:
640	cl_spinlock_release(&p_domain_imp->lock);
641
642	return 0;
643}
644
645int 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
685int 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