1 /*
2  *     Copyright (c) 2000-2006 Alberto Reggiori <areggiori@webweaving.org>
3  *                        Dirk-Willem van Gulik <dirkx@webweaving.org>
4  *
5  * NOTICE
6  *
7  * This product is distributed under a BSD/ASF like license as described in the 'LICENSE'
8  * file you should have received together with this source code. If you did not get a
9  * a copy of such a license agreement you can pick up one at:
10  *
11  *     http://rdfstore.sourceforge.net/LICENSE
12  *
13  *
14  * $Id: handler.c,v 1.48 2006/06/19 10:10:22 areggiori Exp $
15  */
16 
17 #include "dbms.h"
18 #include "dbms_comms.h"
19 #include "dbms_compat.h"
20 #include "dbmsd.h"
21 
22 #include "deamon.h"
23 #include "mymalloc.h"
24 #include "handler.h"
25 #include "children.h"
26 #include "pathmake.h"
27 
28 #include "rdfstore_flat_store.h"
29 
30 dbase                 * first_dbp = NULL;
31 
32 #ifdef STATIC_BUFF
33 static dbase * free_dbase_list = NULL;
34 static int free_dbase_list_len = 0;
35 static int free_dbase_list_keep = 2;
36 static int free_dbase_list_max = 8;
37 #endif
38 static int dbase_counter = 0;
39 
40 #ifdef BERKELEY_DB_1_OR_2
41 static int rdfstore_backend_dbms_compare_int(
42         const DBT *a,
43         const DBT *b );
44 #else
45 static int rdfstore_backend_dbms_compare_int(
46         DB *file,
47         const DBT *a,
48         const DBT *b );
49 #endif
50 
51 #ifdef BERKELEY_DB_1_OR_2
52 static int rdfstore_backend_dbms_compare_double(
53         const DBT *a,
54         const DBT *b );
55 #else
56 static int rdfstore_backend_dbms_compare_double(
57         DB *file,
58         const DBT *a,
59         const DBT *b );
60 #endif
61 
62 
iprt(DBT * r)63 char * iprt( DBT * r ) {
64         static char tmp[ 128 ]; int i;
65 	if (r==NULL)
66 		return "<null>";
67 	if (r->data==NULL)
68 		return "<null ptr>";
69 	if (r->size < 0 || r->size > 1024*1024)
70 		return "<weird size>";
71 
72         for(i=0;i< ( r->size > 127 ? 127 : r->size);i++) {
73 		int c= ((char *)(r->data))[i];
74 		tmp[i] =  ((c<32) || (c>127)) ? '.' : c;
75 		};
76 
77         tmp[i]='\0';
78         return tmp;
79         }
80 
eptr(int i)81 char * eptr( int i ) {
82 	if (i==0)
83 		return "Ok   ";
84 	else
85 	if (i==1)
86 		return "NtFnd";
87 	else
88 	if (i==2)
89 		return "Incmp";
90 	else
91 	if (i>2)
92 		return "+?   ";
93 	else
94 		return "Fail ";
95 	}
96 
_dbclose(dbase * q)97 static int _dbclose(dbase *q)
98 {
99 	if ((q->handle->sync)(q->handle,0))
100 		return -1;
101 
102 #ifdef DB_VERSION_MAJOR
103 	if (	(q->cursor->c_close(q->cursor)) ||
104 	   	((q->handle->close)(q->handle, 0)) )
105 #else
106 	if ((q->handle->close)(q->handle))
107 #endif
108 		return -1;
109 	return 0;
110 }
111 
112 void
free_dbs(dbase * q)113 free_dbs(
114 	dbase * q
115 	)
116 {
117 	if ((q->handle) && (_dbclose(q)))
118 			dbms_log(L_ERROR,"Sync/Close(%s) returned an error during closing of db", q->name);
119 
120 #ifdef STATIC_BUFF
121 	if (free_dbase_list_len < free_dbase_list_keep) {
122 		q->nxt = free_dbase_list;
123 		free_dbase_list = q;
124 		free_dbase_list_len ++;
125 	} else
126 #endif
127         	myfree(q);
128 
129 #ifndef STATIC_BUFF
130 	if (q->pfile) myfree(q->pfile);
131        	if (q->name) myfree(q->name);
132 #endif
133 	dbase_counter --;
134 };
135 
136 void
zap_dbs(dbase * r)137 zap_dbs (
138 	dbase * r
139 	)
140 {
141 	dbase * * p;
142 	connection * s;
143 
144 	/* XXX we do not want this ?! before we
145 	 * know it we end up in n**2 land
146 	 */
147         for ( p = &first_dbp; *p && *p != r; )
148                 p = &((*p)->nxt);
149 
150         if ( *p == NULL) {
151                 dbms_log(L_ERROR,"DBase to zap not found");
152                 return;
153                 };
154 
155 	/* should we not first check all the connections
156 	 * to see if there are (about to) close..
157 	 */
158 	for(s=client_list; s;s=s->next)
159 		if (s->dbp == r) {
160 			s->close = 1; MX;
161 			};
162         *p = r->nxt;
163         free_dbs(r);
164         }
165 
166 
close_all_dbps()167 void close_all_dbps() {
168 	dbase * p;
169 
170         for(p=first_dbp; p;) {
171                 dbase * q;
172                 q = p; p=p->nxt;
173 		free_dbs( q ); /* XXXX why am I not just calling ZAP ? */
174 		};
175 	first_dbp=NULL;
176 	}
177 
178 /* opening of a local database..
179  */
open_dbp(dbase * p)180 int open_dbp( dbase * p ) {
181 
182 #if 0
183         HASHINFO priv = {
184 		16*1024, 	/* bsize; hash bucked size */
185 		8,		/* ffactor, # keys/bucket */
186 		3000,		/* nelements, guestimate */
187 		512*1024,	/* cache size */
188 		NULL,		/* hash function */
189 		0 		/* use current host order */
190 		};
191 #endif
192 
193 #ifdef BERKELEY_DB_1_OR_2 /* Berkeley DB Version 1  or 2 */
194 #ifdef DB_VERSION_MAJOR
195         DB_INFO       btreeinfo;
196         memset(&btreeinfo, 0, sizeof(btreeinfo));
197         btreeinfo.bt_compare = ( p->bt_compare_fcn_type == FLAT_STORE_BT_COMP_INT ) ? rdfstore_backend_dbms_compare_int : ( p->bt_compare_fcn_type == FLAT_STORE_BT_COMP_DOUBLE ) ? rdfstore_backend_dbms_compare_double : NULL ;
198 #else
199         BTREEINFO       btreeinfo;
200         memset(&btreeinfo, 0, sizeof(btreeinfo));
201         btreeinfo.compare = ( p->bt_compare_fcn_type == FLAT_STORE_BT_COMP_INT ) ? rdfstore_backend_dbms_compare_int : ( p->bt_compare_fcn_type == FLAT_STORE_BT_COMP_DOUBLE ) ? rdfstore_backend_dbms_compare_double : NULL ;
202 #endif
203 #endif
204 
205         umask(0);
206 
207 	/* XXX Do note that we _have_ a mode variable. We just ignore it.
208 	 * except for the create flag.
209 	 *
210  	 * XXX we could also pass a &priv=NULL pointer to let the DB's work this
211 	 * one out..
212 	 */
213 
214 #ifdef BERKELEY_DB_1_OR_2 /* Berkeley DB Version 1  or 2 */
215 
216 #ifdef DB_VERSION_MAJOR
217 	if (    (db_open(	p->pfile,
218         			DB_BTREE,
219 				DB_CREATE, /* only create it should be ((ro==0) ? ( DB_CREATE ) : ( DB_RDONLY ) ) */
220                                 0666, NULL, &btreeinfo, &p->handle )) ||
221 #if DB_VERSION_MAJOR == 2 && DB_VERSION_MINOR < 6
222                 ((p->handle->cursor)(p->handle, NULL, &p->cursor))
223 #else
224                 ((p->handle->cursor)(p->handle, NULL, &p->cursor, 0))
225 #endif
226                 ) {
227 #else
228 
229 #if defined(DB_LIBRARY_COMPATIBILITY_API) && DB_VERSION_MAJOR > 2
230 	if (!(p->handle = (DB *)__db185_open(	p->pfile,
231 						p->mode,
232                                                 0666, DB_BTREE, &btreeinfo ))) {
233 #else
234 	if (!(p->handle = (DB *)dbopen(	p->pfile,
235 					p->mode,
236                                         0666, DB_BTREE, &btreeinfo ))) {
237 #endif /* DB_LIBRARY_COMPATIBILITY_API */
238 
239 #endif
240 
241 #else /* Berkeley DB Version > 2 */
242 	if (db_create(&p->handle, NULL,0))
243 		return errno;
244 
245 	/* set the b-tree comparinson function to the one passed */
246 	if( p->bt_compare_fcn_type != NULL ) {
247 		p->handle->set_bt_compare(p->handle, ( p->bt_compare_fcn_type == FLAT_STORE_BT_COMP_INT ) ?
248                                                         rdfstore_backend_dbms_compare_int : ( p->bt_compare_fcn_type == FLAT_STORE_BT_COMP_DOUBLE ) ?
249                                                                                                 rdfstore_backend_dbms_compare_double : NULL );
250                         };
251 
252 	p->handle->set_errfile(p->handle,stderr);
253 	p->handle->set_errpfx(p->handle,"DBMS BerkelyDB");
254 
255 	if (    (p->handle->open(	p->handle,
256 #if DB_VERSION_MAJOR >= 4 && DB_VERSION_MINOR > 0 && DB_VERSION_PATCH >= 17
257 					NULL,
258 #endif
259                                 	p->pfile,
260                                 	NULL,
261                                 	DB_BTREE,
262 					DB_CREATE, /* only create it should be ((ro==0) ? ( DB_CREATE ) : ( DB_RDONLY ) ) */
263                                 	0666 )) ||
264                 ((p->handle->cursor)(p->handle, NULL, &p->cursor, 0)) ) {
265 #endif /* Berkeley DB Version > 2 */
266 
267 		return errno;
268 		};
269 
270 #ifndef BERKELEY_DB_1_OR_2 /* Berkeley DB Version > 2 */
271 /*
272         (void)p->handle->set_h_ffactor(p->handle, 1024);
273         (void)p->handle->set_h_nelem(p->handle, (u_int32_t)6000);
274 */
275 #endif
276 
277 	return 0;
278         }
279 
280 
281 dbase * get_dbp (connection *r, dbms_xsmode_t xsmode, int bt_compare_fcn_type, DBT * v2 ) {
282         dbase * p;
283 	char * pfile;
284 	char name[ 255 ], *n, *m;
285 	int i;
286 	int mode = 0;
287 	tops mops = T_NONE;
288 
289 	/* Clean up the name */
290 	bzero(name,sizeof(name));
291 	for(m = (unsigned char *)(v2->data),n=name,i=0;i<v2->size && i<sizeof(name)-1;i++)
292 		if (isalnum((int)(m[i]))) *n++ = m[i];
293 	*n='\0';
294 
295 	r->op = allowed_ops_on_dbase(r->address.sin_addr.s_addr, name);
296         dbms_log(L_DEBUG,"Permissions for %s/%s - %s",
297 		name, inet_ntoa(r->address.sin_addr),op2string(r->op));
298 
299 	switch(xsmode) {
300 	case DBMS_XSMODE_RDONLY:
301 		mops = T_RDONLY;
302 		mode = O_RDONLY;
303 		break;
304 		;;
305 	case DBMS_XSMODE_RDWR:
306 		mops = T_RDWR;
307 		mode = O_RDWR;
308 		break;
309 		;;
310 	case DBMS_XSMODE_CREAT:
311 		mops = T_CREAT;
312 		mode = O_RDWR | O_CREAT;
313 		break;
314 		;;
315 	case DBMS_XSMODE_DROP:
316 		mops = T_DROP;
317 		mode = O_RDWR | O_CREAT;
318 		break;
319 	default:
320 		dbms_log(L_ERROR,"Impossible XSmode(bug) %d requed on %s",
321 			xsmode,name);
322 		return NULL;
323 		break;
324 	}
325 
326 	if (mops > r->op) {
327 		char * ip = strdup(inet_ntoa(r->address.sin_addr));
328                 dbms_log(L_ERROR,"Access violation on %s: %s requested %s - but may up to %s",
329 			name, ip, op2string(mops),op2string(r->op));
330 		free(ip);
331 		return NULL;
332 	};
333 
334 	/* Max allowed operation */
335 	r->op = MIN(mops,r->op);
336         dbms_log(L_DEBUG,"Permissions for %s/%s - asked %s - granted %s",
337 		name, inet_ntoa(r->address.sin_addr),op2string(mops),
338 		op2string(r->op));
339 
340 #if 0
341 	/* We always add a RDWR to the open - as it may be the case
342 	 * that some later connection needs RW. XXX fixme.
343 	 */
344 	mode = ( mode & (~ O_RDONLY)) | O_RDWR;
345 #endif
346 
347 #ifndef RDFSTORE_PLATFORM_SOLARIS
348 #ifndef RDFSTORE_PLATFORM_LINUX
349 	/* Try to get an exclusive lock if possible */
350 	mode |= O_EXLOCK;
351 #endif
352 #endif
353 
354         for ( p = first_dbp; p; p=p->nxt)
355 		if (strcmp(p->name,name)==0) {
356 			int oldmode = p->mode;
357 
358 			/* If the database has the b-tree comparinson function we need - simply
359 			 * return it. If we are forking - and this is not the process
360 			 * really handling the database - then ignore all this. Otherwise we
361 			 * fail with an error
362 			 */
363 			if ((((p->bt_compare_fcn_type) & bt_compare_fcn_type) == bt_compare_fcn_type )
364 #ifdef FORKING
365 				|| (!mum_pid)
366 #endif
367 				) {
368 				return p;
369 			} else {
370                 		dbms_log(L_ERROR, "Wrong b-tree comparinson function %d on %s - it should be %d",
371 						bt_compare_fcn_type, p->name, p->bt_compare_fcn_type );
372 				return NULL;
373 				};
374 
375 			/* If the database already has the perm's we need - simply
376 			 * return it. If we are forking - and this is not the process
377 			 * really handling the database - then ignore all this
378 			 */
379 			if ((((p->mode) & mode) == mode )
380 #ifdef FORKING
381 				|| (!mum_pid)
382 #endif
383 			) return p;
384 
385 			/* we need to (re)open the database with the higher level perm's we
386 			 * we need this time..
387 			 */
388 			p->mode = mode;
389 			if (_dbclose(p) || open_dbp( p )) {
390                 		dbms_log(L_ERROR,
391 					"DBase %s could not be be reopened with the right permissions %d",
392 					p->name,p->mode);
393 				/* try to reopen the dbase with the old permissions
394 				 * (for the other connections still active)
395 				 */
396 				p->mode = oldmode;
397 
398 				/* bail out - but not clean up de *p; as other
399 				 * connections are still using it.
400 				 */
401 				if (open_dbp(p))
402                				 return NULL;
403 
404 				/* give up - and have the DB removed (even for
405 				 * 	the other connections ! */
406 				goto err_and_exit;
407 			}
408 			return p;
409 	}
410 
411 	if (dbase_counter > HARD_MAX_DBASE) {
412                 dbms_log(L_ERROR,"Hard max number of dabases hit. (bug?)");
413                 return NULL;
414                 };
415 
416 #ifdef STATIC_BUFF
417 	if (free_dbase_list)
418 	{
419 		p = free_dbase_list;
420 		free_dbase_list = free_dbase_list->nxt;
421 	} else {
422 		if (free_dbase_list_keep < free_dbase_list_max)
423 			free_dbase_list_keep += 2;
424 #else
425 {
426 #endif
427         	p = mymalloc(sizeof(dbase));
428 	}
429 
430 	if (p == NULL) {
431                	dbms_log(L_ERROR,"No Memory (for another dbase 1)");
432         	return NULL;
433 	};
434 	bzero(p,sizeof(dbase));
435         p->nxt = first_dbp;
436         first_dbp = p;
437 	dbase_counter ++;
438 
439 #ifndef STATIC_BUFF
440 	p->name = NULL;
441 	p->pfile = NULL;
442 #else
443   p->name[0] ='\0';
444 	p->pfile[0] = '\0';
445 #endif
446 	p->num_cls = 0;
447 	p->close = 0;
448 	p->mode = mode;
449 	p->bt_compare_fcn_type = bt_compare_fcn_type;
450         p->sname = v2->size;
451 	p->handle = NULL;
452 
453 #ifdef FORKING
454 	p->handled_by = NULL;
455 #endif
456 
457 #ifdef STATIC_BUFF
458 	if ( 1+ v2->size > MAX_STATIC_NAME )
459 #else
460 	if ((p->name = mymalloc( 1+v2->size ))==NULL)
461 #endif
462 	{
463                 dbms_log(L_ERROR,"No Memory (for another dbase 2)");
464 		goto clean_and_exit;
465                 };
466 
467         strcpy(p->name, name);
468 
469 	if (!(pfile= mkpath(my_dir,p->name)))
470 		goto clean_and_exit;
471 
472 #ifdef STATIC_BUFF
473 	if ( strlen(pfile)+1 > MAX_STATIC_PFILE )
474 #else
475    if ((p->pfile = mymalloc(strlen(pfile)+1)) == NULL )
476 #endif
477 	{
478                 dbms_log(L_ERROR,"No Memory (for another dbase 3)");
479 		goto clean_and_exit;
480                 };
481 	strcpy(p->pfile,pfile);
482 
483 	/* Check if the DB exists unless we are on an allowed
484 	 * create operations level.
485 	 */
486 	if (r->op < T_CREAT) {
487 		struct stat sb;
488 		int s=stat(p->pfile,&sb);
489 		if (s==-1) {
490 			dbms_log(L_ERROR,"DB %s not found\n",p->pfile);
491 			goto clean_and_exit;
492 		}
493 		/* DB exists - we are good. */
494 	};
495 
496 #ifdef FORKING
497 	/* if we are the main process, then pass
498 	 * on the request to a suitable child;
499 	 * if we are the 'child' then do the
500 	 * actual work..
501 	 */
502 	if (!mum_pid) {
503 		int mdbs=0,c=0;
504 		struct child_rec * q, *best;
505 
506 		/* count # of processes and get the least
507 		 * loaded one of the lot. Or create a
508 		 * fresh one. XXXX We could also go for
509 		 * a rotational approach, modulo the counter.
510 		 * that would remove the need to loop, but
511 		 * spoil the load distribution.
512 		 */
513 		if (child_counter < max_processes) {
514 		  	q=create_new_child();
515 			/* fork/child or error */
516 		  	if ((q == NULL) && (errno))
517 				goto clean_and_exit;
518 			if (q == NULL)
519 				return NULL; /* just bail out if we are the child */
520 		  	best=q;
521 			}
522 		else {
523 			for(c=0,q=children; q; q=q->nxt)
524 				if ( mdbs == 0 || q->num_dbs < mdbs ) {
525 					mdbs = q->num_dbs;
526 					best = q;
527 					};
528 			};
529 
530 		p->handled_by = best;
531 		p->handled_by->num_dbs ++;
532 
533 		return p;
534 	}; /* if mother */
535 	/* we are a child... just open normal.
536 	 */
537 #endif
538         if (open_dbp( p ) == 0)
539 		return p;
540 
541 err_and_exit:
542 	dbms_log(L_ERROR,"open_dbp(1) %s(mode %d) (bt_compare %d) failed: %s",p->pfile,p->mode,p->bt_compare_fcn_type, strerror(errno));
543 
544 clean_and_exit:
545 	p->close = 1; MX;
546 
547 	/* repair... and shuffle... */
548         first_dbp = p->nxt;
549 #ifndef STATIC_BUFF
550 	if (p->pfile) myfree(p->pfile);
551 	if (p->name) myfree(p->name);
552 	if (p) myfree(p);
553 #else
554 	p->nxt = free_dbase_list;
555 	free_dbase_list = p;
556 #endif
557 	dbase_counter --;
558 	return NULL;
559 }
560 
561 void do_init( connection * r) {
562 	DBT val;
563 	u_long proto;
564 	dbms_xsmode_t xsmode;
565 	int bt_compare_fcn_type;
566 
567         memset(&val, 0, sizeof(val));
568 
569 	val.data = &proto;
570 	val.size = sizeof( u_long );
571 
572 	xsmode = (dbms_xsmode_t)((u_long) ntohl( ((u_long *)(r->v1.data))[1] ));
573 
574 #ifdef FORKING
575 	assert(mum_pid==0);
576 #endif
577 	if (r->v1.size == 0) {
578 		reply_log(r,L_ERROR,"No protocol version");
579 		return;
580 		};
581 
582 	proto =((u_long *)(r->v1.data))[0];
583 	if ( ntohl(proto) != DBMS_PROTO ) {
584 		reply_log(r,L_ERROR,"Protocol not supported");
585 		return;
586 		};
587 
588 	bt_compare_fcn_type = ((int) ntohl( ((u_long *)(r->v1.data))[2] ));
589 	if (	( bt_compare_fcn_type != 0 ) &&
590 		( bt_compare_fcn_type < FLAT_STORE_BT_COMP_INT ) &&
591 		( bt_compare_fcn_type > FLAT_STORE_BT_COMP_DATE ) ) {
592 		reply_log(r,L_ERROR,"B-tree sorting function not supported");
593 		return;
594 		};
595 
596 	/* work out wether we have this dbase already open,
597 	 * and open it if ness.
598 	 */
599 	r->dbp = get_dbp( r, xsmode, bt_compare_fcn_type, &(r->v2)); /* returns NULL on error or if it is a child */
600 
601 	if (r->dbp == NULL) {
602 		if (errno == ENOENT) {
603 			dbms_log(L_DEBUG,"Closing instantly with a not found");
604 			dispatch(r, TOKEN_INIT | F_NOTFOUND,&val,NULL);
605 			return;
606 			};
607 #ifdef FORKING
608 		if (!mum_pid)
609 #endif
610 			reply_log(r,L_ERROR,"Open2 database '%s' failed: %s",
611 				iprt(&(r->v2)),strerror(errno));
612 		return;
613 		};
614 
615 	r->dbp->num_cls ++;
616 #ifdef FORKING
617 {
618 	/* We -also- need to record some xtra things which are lost acrss the connection. */
619 	u_long extra[4];
620 	extra[0] = ((u_long *)(r->v1.data))[0]; /* proto */
621 	extra[1] = ((u_long *)(r->v1.data))[1]; /* mode */
622 	extra[2] = ((u_long *)(r->v1.data))[2]; /* bt_compare_fcn_type */
623 	extra[3] = r->address.sin_addr.s_addr;
624 	r->v1.data = extra;
625 	r->v1.size = sizeof(extra);
626 	if (handoff_fd(r->dbp->handled_by, r))
627 		reply_log(r,L_ERROR,"handoff %s : %s",
628 				r->dbp->name,strerror(errno));
629 }
630 #else
631 	dispatch(r, TOKEN_INIT | F_FOUND,&val,NULL);
632 #endif
633 	return;
634 	}
635 
636 #ifdef FORKING
637 void do_pass( connection * mums_r) {
638 	/* this is not really a RQ coming in from a child.. bit instead
639  	 * a warning that we are about to pass a file descriptor
640 	 * in the next message. There is no need to actually confirm
641 	 * anything if we are successfull, we should just be on the
642 	 * standby to get the FD, and treat it as a new connection..
643 	 *
644 	 * note that the r->fd is _not_ a client fd, but the one to
645 	 * our mother.
646 	 */
647 	connection * r;
648 	int newfd;
649 	u_long proto;
650 	dbms_xsmode_t xsmode;
651 	DBT val;
652 	u_long bt_compare_fcn_type;
653 
654         memset(&val, 0, sizeof(val));
655 	assert(mums_r->v1.size = 4*sizeof(u_long));
656 	mums_r->address.sin_addr.s_addr = ((u_long *)(mums_r->v1.data))[3];
657 
658 	assert(mum_pid);
659 
660 	if ((newfd=takeon_fd(mum->clientfd))<0) {
661 		reply_log(mums_r,L_ERROR,"Take on failed: %s",
662 			strerror(errno));
663 		/* give up on the connection to mum ?*/
664 		mums_r->close = 1; MX;
665 		return;
666 		};
667 
668 	/* try to take this FD on board.. and let it do
669 	 * whatever error moaning itself.
670 	 */
671 	proto =((u_long *)(mums_r->v1.data))[0];
672 	xsmode = (dbms_xsmode_t)((u_long) htonl(((u_long *)(mums_r->v1.data))[1]));
673 
674 	dbms_log(L_INFORM,"PASS db='%s' mode %d",iprt(&(mums_r->v2)),xsmode);
675 
676 	if ((r = handle_new_connection( newfd, C_CLIENT, mums_r->address)) == NULL)
677 		return;
678 
679 	/* is this the sort of init data we can handle ?
680 	 */
681 	if ( ntohl(proto) != DBMS_PROTO ) {
682 		reply_log(r,L_ERROR,"Protocol not supported");
683 		return;
684 		};
685 
686 	bt_compare_fcn_type = ((int) ntohl( ((u_long *)(mums_r->v1.data))[2] ));
687 	if (	( bt_compare_fcn_type != 0 ) &&
688 		(	( bt_compare_fcn_type < FLAT_STORE_BT_COMP_INT ) ||
689 			( bt_compare_fcn_type > FLAT_STORE_BT_COMP_DATE ) ) ) {
690 		reply_log(r,L_ERROR,"B-tree sorting function not supported");
691 		return;
692 		};
693 
694 	r->dbp = get_dbp( r, xsmode, bt_compare_fcn_type, &(mums_r->v2));
695 
696 	if (r->dbp== NULL) {
697 		if (errno == ENOENT) {
698 			dispatch(r, TOKEN_INIT | F_NOTFOUND,&val,NULL);
699 			r->close = 1; MX;
700 			return;
701 			};
702 		reply_log(r,L_ERROR,"Open database %s failed: %s",
703 				iprt(&(mums_r->v2)),strerror(errno));
704 		return;
705 		};
706 
707 	r->dbp->num_cls ++;
708 	r->dbp->handled_by = NULL;
709 
710 	/* let the _real_ client know all is well. */
711 	proto=htonl(DBMS_PROTO);
712 	val.data= &proto;
713 	val.size = sizeof( u_long );
714 
715 	dispatch(r, TOKEN_INIT | F_FOUND,&val,NULL);
716 
717 	dbms_log(L_INFORM,"PASS send init repy on %d to client",r->clientfd);
718 	return;
719 	};
720 #endif
721 
722 void do_fetch( connection * r) {
723 	DBT key, val;
724 	int err;
725 
726 	memset(&key, 0, sizeof(key));
727         memset(&val, 0, sizeof(val));
728 
729 	if (r->type != C_CLIENT) {
730 		dbms_log(L_ERROR,"Command received from non-client command FETCH");
731 		return;
732 		};
733 
734 	key.data = r->v1.data;
735 	key.size = r->v1.size;
736 
737 #ifdef DB_VERSION_MAJOR
738 	err=(r->dbp->handle->get)(r->dbp->handle, NULL, &key, &val, 0);
739 #else
740 	err=(r->dbp->handle->get)(r->dbp->handle, &key, &val, 0);
741 #endif
742 
743 	if (err == 0)
744 		dispatch(r,TOKEN_FETCH | F_FOUND,&key,&val);
745 	else
746 #ifdef DB_VERSION_MAJOR
747 	if (err == DB_NOTFOUND)
748 #else
749 	if (err == 1)
750 #endif
751 		dispatch(r,TOKEN_FETCH | F_NOTFOUND,NULL,NULL);
752 	else {
753 		errno=err;
754 		reply_log(r,L_ERROR,"fetch on %s failed: %s (klen=%d, vlen=%d, err=%d(1))",r->dbp->name,strerror(errno), key.size,val.size,err);
755 		}
756 	}
757 
758 void do_inc ( connection * r) {
759 	DBT key, val;
760 	int err;
761 	unsigned long l;
762 	char * p;
763 	char outbuf[256]; /* surely shorter than UMAX_LONG */
764 
765 	memset(&key, 0, sizeof(key));
766         memset(&val, 0, sizeof(val));
767 
768 	if (r->type != C_CLIENT) {
769 		dbms_log(L_ERROR,"Command received from non-client command FETCH");
770 		return;
771 		};
772 
773 	/* all we get from the client is the key, and
774 	 * all we return is the (increased) value
775 	 */
776 	key.data = r->v1.data;
777 	key.size = r->v1.size;
778 
779 #ifdef DB_VERSION_MAJOR
780 	err=(r->dbp->handle->get)( r->dbp->handle, NULL, &key, &val, 0);
781 #else
782 	err=(r->dbp->handle->get)( r->dbp->handle, &key, &val,0);
783 #endif
784 
785 #ifdef DB_VERSION_MAJOR
786 	if ((err == DB_NOTFOUND) || (val.size == 0)) {
787 #else
788 	if ((err == 1) || (val.size == 0)) {
789 #endif
790                 dispatch(r,TOKEN_INC | F_NOTFOUND,NULL,NULL);
791 		return;
792 		}
793 	else
794 	if (err) {
795 #ifdef DB_VERSION_MAJOR
796 		errno=err;
797 #endif
798 		reply_log(r,L_ERROR,"inc on %s failed: %s",r->dbp->name,
799 			strerror(errno) );
800 		return;
801 		};
802 
803 	/* XXX bit of a hack; but perl seems to deal with
804          *     all storage as ascii strings in some un-
805          *     specified locale.
806          */
807 	bzero(outbuf,256);
808 	strncpy(outbuf,val.data,MIN( val.size, 255 ));
809 	l=strtoul( outbuf, &p, 10 );
810 
811 	if (*p || l == ULONG_MAX || errno == ERANGE) {
812 		reply_log(r,L_ERROR,"inc on %s failed: %s",r->dbp->name,
813 			"Not the (entire) string is an unsigned integer"
814 			);
815 		return;
816 		};
817 	/* this is where it all happens... */
818 	l++;
819 
820 	bzero(outbuf,256);
821 	snprintf(outbuf,255,"%lu",l);
822 	val.data = & outbuf;
823 	val.size = strlen(outbuf);
824 
825 	/* and put it back..
826 	 *
827  	 *  	Put routines return -1 on error (setting errno),  0
828          *	on success, and 1 if the R_NOOVERWRITE flag was set
829          *    	and the key already exists in the file.
830 	 */
831 #ifdef DB_VERSION_MAJOR
832         err=(r->dbp->handle->put)( r->dbp->handle, NULL, &key, &val, 0);
833 #else
834         err=(r->dbp->handle->put)( r->dbp->handle, &key, &val,0);
835 #endif
836 
837 	/* just send it back as an ascii string
838 	 */
839 #ifdef DB_VERSION_MAJOR
840 	if (( err == 0 ) || ( err < 0 ))
841 #else
842 	if (( err == 0 ) || ( err == 1 ))
843 #endif
844                 dispatch(r,TOKEN_INC | F_FOUND,NULL,&val);
845         else {
846 #ifdef DB_VERSION_MAJOR
847 		errno=err;
848 #endif
849 		reply_log(r,L_ERROR,"inc store on %s failed: %s",
850 			r->dbp->name,strerror(errno));
851 		};
852 	};
853 
854 void do_dec ( connection * r) {
855 	DBT key, val;
856 	int err;
857 	unsigned long l;
858 	char * p;
859 	char outbuf[256]; /* surely shorter than UMAX_LONG */
860 
861 	memset(&key, 0, sizeof(key));
862         memset(&val, 0, sizeof(val));
863 
864 	if (r->type != C_CLIENT) {
865 		dbms_log(L_ERROR,"Command received from non-client command FETCH");
866 		return;
867 		};
868 
869 	/* all we get from the client is the key, and
870 	 * all we return is the (decreased) value
871 	 */
872 	key.data = r->v1.data;
873 	key.size = r->v1.size;
874 
875 #ifdef DB_VERSION_MAJOR
876         err=(r->dbp->handle->get)( r->dbp->handle, NULL, &key, &val, 0);
877 #else
878         err=(r->dbp->handle->get)( r->dbp->handle, &key, &val,0);
879 #endif
880 
881 #ifdef DB_VERSION_MAJOR
882         if ((err == DB_NOTFOUND) || (val.size == 0)) {
883 #else
884         if ((err == 1) || (val.size == 0)) {
885 #endif
886                 dispatch(r,TOKEN_DEC | F_NOTFOUND,NULL,NULL);
887                 return;
888                 }
889         else
890         if (err) {
891 #ifdef DB_VERSION_MAJOR
892                 errno=err;
893 #endif
894                 reply_log(r,L_ERROR,"dec on %s failed: %s",r->dbp->name,
895                         strerror(errno) );
896                 return;
897                 };
898 
899 	/* XXX bit of a hack; but perl seems to deal with
900          *     all storage as ascii strings in some un-
901          *     specified locale.
902          */
903 	bzero(outbuf,256);
904 	strncpy(outbuf,val.data,MIN( val.size, 255 ));
905 	l=strtoul( outbuf, &p, 10 );
906 
907 	if (*p || l == ULONG_MAX || l == 0 || errno == ERANGE) {
908 		reply_log(r,L_ERROR,"dec on %s failed: %s",r->dbp->name,
909 			"Not the (entire) string is an unsigned integer"
910 			);
911 		return;
912 		};
913 	/* this is where it all happens... */
914 	l--;
915 
916 	bzero(outbuf,256);
917 	snprintf(outbuf,255,"%lu",l);
918 	val.data = & outbuf;
919 	val.size = strlen(outbuf);
920 
921 	/* and put it back..
922 	 *
923  	 *  	Put routines return -1 on error (setting errno),  0
924          *	on success, and 1 if the R_NOOVERWRITE flag was set
925          *    	and the key already exists in the file.
926 	 */
927 #ifdef DB_VERSION_MAJOR
928         err=(r->dbp->handle->put)( r->dbp->handle, NULL, &key, &val, 0);
929 #else
930         err=(r->dbp->handle->put)( r->dbp->handle, &key, &val,0);
931 #endif
932 
933         /* just send it back as an ascii string
934          */
935 #ifdef DB_VERSION_MAJOR
936         if (( err == 0 ) || ( err < 0 ))
937 #else
938         if (( err == 0 ) || ( err == 1 ))
939 #endif
940                 dispatch(r,TOKEN_DEC | F_FOUND,NULL,&val);
941         else
942 #ifdef DB_VERSION_MAJOR
943 		{
944                 errno=err;
945                 reply_log(r,L_ERROR,"dec store on %s failed: %s",
946                         r->dbp->name,strerror(errno));
947 		};
948 #else
949                 reply_log(r,L_ERROR,"dec store on %s failed: %s",
950                         r->dbp->name,strerror(errno));
951 #endif
952 	};
953 
954 /* atomic packed increment */
955 void do_packinc ( connection * r) {
956 	DBT key, val;
957 	int err;
958 	dbms_counter l=0;
959 	unsigned char outbuf[256];
960 
961 	memset(&key, 0, sizeof(key));
962         memset(&val, 0, sizeof(val));
963 
964 	if (r->type != C_CLIENT) {
965 		dbms_log(L_ERROR,"Command received from non-client command FETCH");
966 		return;
967 		};
968 
969 	/* all we get from the client is the key, and
970 	 * all we return is the (increased) value
971 	 */
972 	key.data = r->v1.data;
973 	key.size = r->v1.size;
974 
975 #ifdef DB_VERSION_MAJOR
976         err=(r->dbp->handle->get)( r->dbp->handle, NULL, &key, &val, 0);
977 #else
978         err=(r->dbp->handle->get)( r->dbp->handle, &key, &val,0);
979 #endif
980 
981 #ifdef DB_VERSION_MAJOR
982         if ((err == DB_NOTFOUND) || (val.size == 0)) {
983 #else
984         if ((err == 1) || (val.size == 0)) {
985 #endif
986                 dispatch(r,TOKEN_PACKINC | F_NOTFOUND,NULL,NULL);
987                 return;
988                 }
989         else
990         if (err) {
991 #ifdef DB_VERSION_MAJOR
992                 errno=err;
993 #endif
994                 reply_log(r,L_ERROR,"packinc on %s failed: %s",r->dbp->name,
995                         strerror(errno) );
996                 return;
997                 };
998 
999 	l = ntohl(*(dbms_counter *)val.data);
1000 
1001 	/* this is where it all happens... */
1002 	l++;
1003 
1004         val.data = outbuf;
1005         val.size = sizeof(dbms_counter);
1006 
1007 	*(dbms_counter *)val.data = htonl(l);
1008 
1009 	/* and put it back..
1010 	 *
1011  	 *  	Put routines return -1 on error (setting errno),  0
1012          *	on success, and 1 if the R_NOOVERWRITE flag was set
1013          *    	and the key already exists in the file.
1014 	 */
1015 #ifdef DB_VERSION_MAJOR
1016         err=(r->dbp->handle->put)( r->dbp->handle, NULL, &key, &val, 0);
1017 #else
1018         err=(r->dbp->handle->put)( r->dbp->handle, &key, &val,0);
1019 #endif
1020 
1021         /* just send it back as an ascii string
1022          */
1023 #ifdef DB_VERSION_MAJOR
1024         if (( err == 0 ) || ( err < 0 ))
1025 #else
1026         if (( err == 0 ) || ( err == 1 ))
1027 #endif
1028                 dispatch(r,TOKEN_PACKINC | F_FOUND,NULL,&val);
1029         else
1030 #ifdef DB_VERSION_MAJOR
1031 		{
1032                 errno=err;
1033                 reply_log(r,L_ERROR,"packinc store on %s failed: %s",
1034                         r->dbp->name,strerror(errno));
1035 		};
1036 #else
1037                 reply_log(r,L_ERROR,"packinc store on %s failed: %s",
1038                         r->dbp->name,strerror(errno));
1039 #endif
1040 	};
1041 
1042 /* atomic packed decrement */
1043 void do_packdec ( connection * r) {
1044 	DBT key, val;
1045 	int err;
1046 	dbms_counter l=0;
1047 	unsigned char outbuf[256]; /* surely shorter than UMAX_LONG */
1048 
1049 	memset(&key, 0, sizeof(key));
1050         memset(&val, 0, sizeof(val));
1051 
1052 	if (r->type != C_CLIENT) {
1053 		dbms_log(L_ERROR,"Command received from non-client command FETCH");
1054 		return;
1055 		};
1056 
1057 	/* all we get from the client is the key, and
1058 	 * all we return is the (increased) value
1059 	 */
1060 	key.data = r->v1.data;
1061 	key.size = r->v1.size;
1062 
1063 #ifdef DB_VERSION_MAJOR
1064         err=(r->dbp->handle->get)( r->dbp->handle, NULL, &key, &val, 0);
1065 #else
1066         err=(r->dbp->handle->get)( r->dbp->handle, &key, &val,0);
1067 #endif
1068 
1069 #ifdef DB_VERSION_MAJOR
1070         if ((err == DB_NOTFOUND) || (val.size == 0)) {
1071 #else
1072         if ((err == 1) || (val.size == 0)) {
1073 #endif
1074                 dispatch(r,TOKEN_PACKDEC | F_NOTFOUND,NULL,NULL);
1075                 return;
1076                 }
1077         else
1078         if (err) {
1079 #ifdef DB_VERSION_MAJOR
1080                 errno=err;
1081 #endif
1082                 reply_log(r,L_ERROR,"packdec on %s failed: %s",r->dbp->name,
1083                         strerror(errno) );
1084                 return;
1085                 };
1086 
1087 	l = ntohl(*(dbms_counter *)val.data);
1088 	/* this is where it all happens... */
1089 	l--;
1090 
1091 
1092         val.data = outbuf;
1093         val.size = sizeof(uint32_t)+1;
1094 
1095 	*(dbms_counter *)val.data = htonl(l);
1096 
1097 	/* and put it back..
1098 	 *
1099  	 *  	Put routines return -1 on error (setting errno),  0
1100          *	on success, and 1 if the R_NOOVERWRITE flag was set
1101          *    	and the key already exists in the file.
1102 	 */
1103 #ifdef DB_VERSION_MAJOR
1104         err=(r->dbp->handle->put)( r->dbp->handle, NULL, &key, &val, 0);
1105 #else
1106         err=(r->dbp->handle->put)( r->dbp->handle, &key, &val,0);
1107 #endif
1108 
1109         /* just send it back as an ascii string
1110          */
1111 #ifdef DB_VERSION_MAJOR
1112         if (( err == 0 ) || ( err < 0 ))
1113 #else
1114         if (( err == 0 ) || ( err == 1 ))
1115 #endif
1116                 dispatch(r,TOKEN_PACKDEC | F_FOUND,NULL,&val);
1117         else
1118 #ifdef DB_VERSION_MAJOR
1119 		{
1120                 errno=err;
1121                 reply_log(r,L_ERROR,"packdec store on %s failed: %s",
1122                         r->dbp->name,strerror(errno));
1123 		};
1124 #else
1125                 reply_log(r,L_ERROR,"packdec store on %s failed: %s",
1126                         r->dbp->name,strerror(errno));
1127 #endif
1128 	};
1129 
1130 void do_exists( connection * r) {
1131         DBT key, val;
1132         int err;
1133 
1134 	memset(&key, 0, sizeof(key));
1135         memset(&val, 0, sizeof(val));
1136 
1137 	if (r->type != C_CLIENT) {
1138 		dbms_log(L_ERROR,"Command received from non-client command EXISTS");
1139 		return;
1140 		};
1141 
1142         key.data = r->v1.data;
1143         key.size = r->v1.size;
1144 
1145 #ifdef DB_VERSION_MAJOR
1146         err=(r->dbp->handle->get)( r->dbp->handle, NULL, &key, &val, 0);
1147 #else
1148         err=(r->dbp->handle->get)( r->dbp->handle, &key, &val,0);
1149 #endif
1150 
1151         if ( err == 0 )
1152 		dispatch(r,TOKEN_EXISTS | F_FOUND,NULL,NULL);
1153 	else
1154 #ifdef DB_VERSION_MAJOR
1155         if (err == DB_NOTFOUND)
1156                 dispatch(r,TOKEN_EXISTS | F_NOTFOUND,NULL,NULL);
1157         else {
1158                 errno=err;
1159                 reply_log(r,L_ERROR,"exists on %s failed: %s",r->dbp->name,strerror(errno));
1160                 }
1161 #else
1162         if (err == 1)
1163                 dispatch(r,TOKEN_EXISTS | F_NOTFOUND,NULL,NULL);
1164         else
1165                 reply_log(r,L_ERROR,"exists on %s failed: %s",r->dbp->name,strerror(errno));
1166 #endif
1167         };
1168 
1169 void do_delete( connection * r) {
1170         DBT key;
1171         int err;
1172 
1173 	memset(&key, 0, sizeof(key));
1174 
1175 	if (r->type != C_CLIENT) {
1176 		dbms_log(L_ERROR,"Command received from non-client command DELETE");
1177 		return;
1178 		};
1179 
1180         key.data = r->v1.data;
1181         key.size = r->v1.size;
1182 
1183 #ifdef DB_VERSION_MAJOR
1184         err=(r->dbp->handle->del)( r->dbp->handle, NULL, &key, 0);
1185 #else
1186         err=(r->dbp->handle->del)( r->dbp->handle, &key,0);
1187 #endif
1188 
1189         if ( err == 0 )
1190                 dispatch(r,TOKEN_DELETE | F_FOUND,NULL,NULL);
1191         else
1192 #ifdef DB_VERSION_MAJOR
1193         if (err == DB_NOTFOUND)
1194                 dispatch(r,TOKEN_DELETE | F_NOTFOUND,NULL,NULL);
1195         else {
1196                 errno=err;
1197 		reply_log(r,L_ERROR,"delete on %s failed: %s",r->dbp->name,strerror(errno));
1198                 }
1199 #else
1200         if ( err == 1 )
1201                 dispatch(r,TOKEN_DELETE | F_NOTFOUND,NULL,NULL);
1202         else
1203 		reply_log(r,L_ERROR,"delete on %s failed: %s",r->dbp->name,strerror(errno));
1204 #endif
1205         };
1206 
1207 void do_store( connection * r) {
1208         DBT key, val;
1209         int err;
1210 
1211 	memset(&key, 0, sizeof(key));
1212 	memset(&val, 0, sizeof(val));
1213 
1214 	if (r->type != C_CLIENT) {
1215 		dbms_log(L_ERROR,"Command received from non-client command STORE");
1216 		return;
1217 		};
1218 
1219         key.data = r->v1.data;
1220         key.size = r->v1.size;
1221 
1222         val.data = r->v2.data;
1223         val.size = r->v2.size;
1224 
1225 #ifdef DB_VERSION_MAJOR
1226         err=(r->dbp->handle->put)( r->dbp->handle, NULL, &key, &val, 0);
1227 #else
1228         err=(r->dbp->handle->put)( r->dbp->handle, &key, &val,0);
1229 #endif
1230 
1231 	if ( err == 0 )
1232                 dispatch(r,TOKEN_STORE | F_FOUND,NULL,NULL); /* it was F_NOTFOUND wich was returning always 1 even if not there (F_NOTFOUND) */
1233         else
1234 #ifdef DB_VERSION_MAJOR
1235         if ( err < 0 )
1236                 dispatch(r,TOKEN_STORE | F_FOUND,NULL,NULL);
1237         else {
1238 		errno=err;
1239 		reply_log(r,L_ERROR,"store on %s failed: %s",r->dbp->name,strerror(errno));
1240 		};
1241 #else
1242         if ( err == 1 )
1243                 dispatch(r,TOKEN_STORE | F_NOTFOUND,NULL,NULL); /* it was F_FOUND which was returning always 0 even if already there (F_FOUND) see above dispatch */
1244         else
1245 		reply_log(r,L_ERROR,"store on %s failed: %s",r->dbp->name,strerror(errno));
1246 #endif
1247 
1248         };
1249 
1250 void do_sync( connection * r) {
1251         int err=0;
1252 
1253 	if (r->type != C_CLIENT) {
1254 		dbms_log(L_ERROR,"Command received from non-client command SYNC");
1255 		return;
1256 		};
1257 
1258         err=(r->dbp->handle->sync)( r->dbp->handle,0);
1259 
1260         if (err != 0 ) {
1261 		reply_log(r,L_ERROR,"sync on %s failed: %s",r->dbp->name,strerror(errno));
1262                 }
1263 	else {
1264         	dispatch(r,TOKEN_SYNC,NULL,NULL);
1265 		};
1266         };
1267 
1268 void do_clear( connection * r) {
1269 	int err;
1270 
1271 	if (r->type != C_CLIENT) {
1272 		dbms_log(L_ERROR,"Command received from non-client command CLEAR");
1273 		return;
1274 		};
1275 
1276 	/* close the database, remove the file, and repoen... ? */
1277 	if ( (_dbclose(r->dbp)) ||
1278 	     ((err=unlink(r->dbp->pfile)) !=0) ||
1279 	     ((err=open_dbp( r->dbp )) != 0) )
1280 	{
1281 		reply_log(r,L_ERROR,"clear on %s failed: %s",r->dbp->name,strerror(errno));
1282                 return;
1283                 };
1284 
1285 	trace("%6s %12s %s","SYNC",r->dbp->name,eptr(err));
1286 	dispatch(r, TOKEN_CLEAR,NULL, NULL);
1287 	};
1288 
1289 void do_list( connection * r) {
1290 #if 0
1291         DBT key, val;
1292         int err;
1293 
1294 	memset(&key, 0, sizeof(key));
1295         memset(&val, 0, sizeof(val));
1296 
1297 	/* now the issue here is... do we want to do
1298 	 * the entire array; as one HUGE malloc ?
1299 	 */
1300 
1301 	if (r->type != C_CLIENT) {
1302 		dbms_log(L_ERROR,"Command received from non-client command LIST");
1303 		return;
1304 		};
1305 
1306 	/* keep track of whom used the cursor last...*/
1307 	r->dbp->lastfd = r->clientfd;
1308 
1309 	f = R_FIRST;
1310         for(;;) {
1311         	err=(r->dbp->handle->seq)( r->dbp->handle, &key, &val,f);
1312 		if ( err ) last;
1313 		f = F_NEXT;
1314 
1315 		};
1316 
1317         if ( err < 0 )
1318 		reply_log(r,L_ERROR,"first on %s failed: %s",
1319 			r->dbp->name,strerror(errno));
1320 	else
1321 	if ( err == 1 )
1322                 dispatch(r,TOKEN_FIRSTKEY | F_NOTFOUND,NULL,NULL);
1323         else
1324                 dispatch(r,TOKEN_LIST | F_FOUND,&key,&val);
1325 #endif
1326 	reply_log(r,L_ERROR,"Not implemented.. yet");
1327 	}
1328 
1329 void do_ping( connection * r) {
1330         dispatch(r,TOKEN_PING | F_FOUND,NULL,NULL);
1331 	}
1332 
1333 void do_drop( connection * r) {
1334 	char dbpath[ 1024 ];
1335 	dbms_log(L_INFORM,"Drop cmd");
1336 
1337 	/* Construct name  - add .db where/if needed ?? */
1338 	/* snprintf(dbpath,sizeof(dbpath),"%s.db",r->dbp->pfile); */
1339 	snprintf(dbpath,sizeof(dbpath),"%s",r->dbp->pfile);
1340 
1341 	/* or r->dbp->close = 2; */
1342 	zap_dbs(r->dbp);
1343 
1344 	if (unlink(dbpath))
1345 		reply_log(r,L_ERROR,
1346 			"DB file %s could not be deleted: %s",
1347                         dbpath,strerror(errno));
1348 	else
1349         	dispatch(r,TOKEN_DROP| F_FOUND,NULL,NULL);
1350 }
1351 
1352 void do_close( connection * r) {
1353 
1354 	if (r->type != C_CLIENT) {
1355 		dbms_log(L_ERROR,"Command received from non-client command CLOSE");
1356 		return;
1357 		};
1358 
1359         dispatch(r,TOKEN_CLOSE,NULL,NULL);
1360 	r->close = 1; MX;
1361 	}
1362 
1363 /* Combined from function; from first record when flag==R_FIRST or DB_FIRST
1364  * or from the current cursor if flag=R_CURSOR or DB_SET_RANGE. If
1365  * no cursos is yet set; the latter two default to an R_FIRST or DB_FIRST.
1366  */
1367 static
1368 void _from( connection * r, DBT *key, DBT *val, int flag) {
1369         int err;
1370 
1371 	if (r->type != C_CLIENT) {
1372 		dbms_log(L_ERROR,"Command received from non-client command FIRST/FROM");
1373 		return;
1374 		};
1375 
1376 	/* keep track of whom used the cursor last...*/
1377 	r->dbp->lastfd = r->clientfd;
1378 
1379 #ifdef DB_VERSION_MAJOR
1380         err=(r->dbp->cursor->c_get)( r->dbp->cursor, key, val, flag);
1381 #else
1382       	err=(r->dbp->handle->seq)( r->dbp->handle, key, val,flag);
1383 #endif
1384 
1385 #if DB_VERSION_MAJOR >= 2
1386         if (err == DB_NOTFOUND)
1387                 dispatch(r,( (flag==DB_FIRST) ? TOKEN_FIRSTKEY : TOKEN_FROM ) | F_NOTFOUND,NULL,NULL);
1388         else
1389         if ( err == 0 )
1390                 dispatch(r,( (flag==DB_FIRST) ? TOKEN_FIRSTKEY : TOKEN_FROM ) | F_FOUND,key,val);
1391         else {
1392                 errno=err;
1393 		reply_log(r,L_ERROR,"first on %s failed: %s",r->dbp->name,strerror(errno));
1394                 }
1395 #else
1396 	if ( err == 1 )
1397                 dispatch(r,( (flag==R_FIRST) ? TOKEN_FIRSTKEY : TOKEN_FROM ) | F_NOTFOUND,NULL,NULL);
1398         else
1399         if ( err == 0 )
1400                 dispatch(r,( (flag==R_FIRST) ? TOKEN_FIRSTKEY : TOKEN_FROM ) | F_FOUND,key,val);
1401         else
1402 		reply_log(r,L_ERROR,"first on %s failed: %s",r->dbp->name,strerror(errno));
1403 #endif
1404 };
1405 
1406 void do_first(connection * r) {
1407         DBT key, val;
1408 	memset(&key, 0, sizeof(key));
1409         memset(&val, 0, sizeof(val));
1410 
1411 #if DB_VERSION_MAJOR >= 2
1412 	_from(r,&key,&val,DB_FIRST);
1413 #else
1414 	_from(r,&key,&val,R_FIRST);
1415 #endif
1416 }
1417 
1418 void do_from(connection *r) {
1419         DBT key, val;
1420 	memset(&key, 0, sizeof(key));
1421         memset(&val, 0, sizeof(val));
1422 
1423 	key.data = r->v1.data; /* copy the requested closest key */
1424 	key.size = r->v1.size;
1425 
1426 #if DB_VERSION_MAJOR >= 2
1427 	_from(r,&key,&val,DB_SET_RANGE);
1428 #else
1429 	_from(r,&key,&val,R_CURSOR);
1430 #endif
1431 };
1432 
1433 
1434 void do_next( connection * r) {
1435         DBT key, val;
1436         int err;
1437 
1438 	memset(&key, 0, sizeof(key));
1439         memset(&val, 0, sizeof(val));
1440 
1441 	if (r->type != C_CLIENT) {
1442 		dbms_log(L_ERROR,"Command received from non-client command NEXT");
1443 		return;
1444 		};
1445 
1446 	/* We need to set the cursor first, if we where
1447 	 * not the last using it.
1448 	 */
1449 	if ( r->dbp->lastfd != r->clientfd ) {
1450 		r->dbp->lastfd = r->clientfd;
1451         	key.data = r->v1.data; /* copy the previous key if any */
1452         	key.size = r->v1.size;
1453 
1454 #if DB_VERSION_MAJOR >= 2
1455                 err=(r->dbp->cursor->c_get)(r->dbp->cursor, &key, &val, DB_NEXT);
1456 #else
1457         	err=(r->dbp->handle->seq)( r->dbp->handle, &key, &val, R_NEXT);
1458 #endif
1459 
1460 #ifdef DB_VERSION_MAJOR
1461 		if ( (err != 0) && (err != DB_NOTFOUND) ) {
1462                 	reply_log(r,L_ERROR,"Internal DB Error %s",r->dbp->name);
1463 			return;
1464 			};
1465 #else
1466 		if (err<0 && errno ==0)
1467 			dbms_log(L_WARN,"seq-cursor We have the impossible err=%d and %d",
1468 				err,errno);
1469 
1470 		if ((err != 0) && (err != 1) && (errno != 0) ) {
1471                 	reply_log(r,L_ERROR,"Internal DB Error %s",r->dbp->name);
1472 			return;
1473 			};
1474 #endif
1475 
1476 		/* BUG: we could detect the fact that the previous key
1477 	 	 *	the callee was aware of, has been zapped. For
1478 	 	 *	now we note that, if the key is not there, we
1479 	 	 *	have received the next greater key. Which we
1480 		 * 	thus return ?! This is an issue.
1481 		 */
1482 		}
1483 	else
1484 		err = 0;
1485 
1486         if (err == 0)
1487 #if DB_VERSION_MAJOR >= 2
1488                 err=(r->dbp->cursor->c_get)(r->dbp->cursor, &key, &val, DB_NEXT);
1489 #else
1490 		err=(r->dbp->handle->seq)( r->dbp->handle, &key, &val, R_NEXT);
1491 #endif
1492 
1493 	trace("%6s %12s %20s: %s %s","NEXT",
1494 		r->dbp->name, iprt(&key),
1495 		iprt( err==0 ? &val : NULL ),eptr(err));
1496 
1497 #ifdef DB_VERSION_MAJOR
1498         if ( ( err == DB_NOTFOUND ) || ( err > 0 ) )
1499 		dispatch(r,TOKEN_NEXTKEY | F_NOTFOUND,NULL,NULL);
1500 	else
1501 #else
1502         if (( err == 1 ) || (( err <0 ) && (errno == 0)) )
1503 		dispatch(r,TOKEN_NEXTKEY | F_NOTFOUND,NULL,NULL);
1504 	else
1505 #endif
1506 	if ( err == 0 )
1507         	dispatch(r,TOKEN_NEXTKEY | F_FOUND,&key,&val);
1508         else {
1509 #ifdef DB_VERSION_MAJOR
1510 		errno=err;
1511 #endif
1512 		reply_log(r,L_ERROR,"next on %s failed: %s",r->dbp->name,strerror(errno));
1513 		};
1514         };
1515 
1516 struct command_req cmd_table[ TOKEN_MAX ];
1517 #define IT(i,s,f,o) { cmd_table[i].cnt = 0; cmd_table[i].cmd = i; cmd_table[i].info = s; cmd_table[i].handler = f; cmd_table[i].op = o; }
1518 void init_cmd_table( void )
1519 {
1520 	int i;
1521 	for(i=0;i<TOKEN_MAX;i++)
1522 		IT( i, "VOID",NULL, T_NONE );
1523 
1524 	IT( TOKEN_INIT,		"INIT",&do_init,	T_ERR);	/* chicken/egg - we do not know the error yet */
1525 	IT( TOKEN_FETCH,	"FTCH",&do_fetch,	T_RDONLY);
1526 	IT( TOKEN_STORE,	"STRE",&do_store,	T_RDWR);
1527 	IT( TOKEN_DELETE,	"DELE",&do_delete,	T_RDWR);
1528 	IT( TOKEN_CLOSE,	"CLSE",&do_close,	T_NONE);
1529 	IT( TOKEN_NEXTKEY,	"NEXT",&do_next,	T_RDONLY);
1530 	IT( TOKEN_FIRSTKEY,	"FRST",&do_first,	T_RDONLY);
1531 	IT( TOKEN_EXISTS,	"EXST",&do_exists,	T_RDONLY);
1532 	IT( TOKEN_SYNC,		"SYNC",&do_sync,	T_RDWR);
1533 	IT( TOKEN_CLEAR,	"CLRS",&do_clear,	T_CREAT);
1534 	IT( TOKEN_PING,		"PING",&do_ping,	T_NONE);
1535 	IT( TOKEN_DROP,		"DROP",&do_drop,	T_DROP);
1536 	IT( TOKEN_INC,		"INCR",&do_inc,		T_RDWR);
1537 	IT( TOKEN_DEC,		"DECR",&do_dec,		T_RDWR);
1538 	IT( TOKEN_PACKINC,	"PINC",&do_packinc,	T_RDWR);
1539 	IT( TOKEN_PACKDEC,	"PDEC",&do_packdec,	T_RDWR);
1540 	IT( TOKEN_LIST,		"LIST",&do_list,	T_RDONLY);
1541 	IT( TOKEN_FROM,		"FROM",&do_from,	T_RDONLY);
1542 #ifdef FORKING
1543 	IT( TOKEN_FDPASS,"PASS",&do_pass,		T_ERR);
1544 #endif
1545 }
1546 
1547 void parse_request( connection * r) {
1548 	register int i = r->cmd.token;
1549 
1550 	if ( i>=0 && i<= TOKEN_MAX && cmd_table[i].handler) {
1551 		if (cmd_table[i].op <= r->op) {
1552 			cmd_table[i].cnt++;
1553 			(cmd_table[i].handler)(r);
1554 		} else {
1555 			char * ip = strdup(inet_ntoa(r->address.sin_addr));
1556 			reply_log(r,L_ERROR,"Access violation for %s on %s (required is %s but IP is limited to %s)",
1557 				ip,cmd_table[i].info,
1558 				op2string(cmd_table[i].op),op2string(r->op));
1559 			free(ip);
1560 			r->close = 1; MX;
1561 		}
1562 		return;
1563 	}
1564 
1565 	reply_log(r,L_ERROR,"Unkown command token %d",i);
1566 	r->close = 1; MX;
1567 	return;
1568 }
1569 
1570 /* misc subroutines (copied from ../../backend_bdb_store.c - should be merged) */
1571 
1572 /*
1573  * The following compare function are used for btree(s) for basic
1574  * XML-Schema data types xsd:integer, xsd:double (and will xsd:date)
1575  *
1576  * They return:
1577  *      < 0 if a < b
1578  *      = 0 if a = b
1579  *      > 0 if a > b
1580  */
1581 #ifdef BERKELEY_DB_1_OR_2
1582 static int rdfstore_backend_dbms_compare_int(
1583         const DBT *a,
1584         const DBT *b ) {
1585 #else
1586 static int rdfstore_backend_dbms_compare_int(
1587         DB *file,
1588         const DBT *a,
1589         const DBT *b ) {
1590 #endif
1591         long ai, bi;
1592 
1593         memcpy(&ai, a->data, sizeof(long));
1594         memcpy(&bi, b->data, sizeof(long));
1595 
1596         return (ai - bi);
1597         };
1598 
1599 #ifdef BERKELEY_DB_1_OR_2
1600 static int rdfstore_backend_dbms_compare_double(
1601         const DBT *a,
1602         const DBT *b ) {
1603 #else
1604 static int rdfstore_backend_dbms_compare_double(
1605         DB *file,
1606         const DBT *a,
1607         const DBT *b ) {
1608 #endif
1609         double ad,bd;
1610 
1611         memcpy(&ad, a->data, sizeof(double));
1612         memcpy(&bd, b->data, sizeof(double));
1613 
1614         if (  ad <  bd ) {
1615                 return -1;
1616         } else if (  ad  >  bd) {
1617                 return 1;
1618                 };
1619 
1620         return 0;
1621         };
1622