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