*** empty log message ***
[idzebra-moved-to-github.git] / index / zebraapi.c
1 /*
2  * Copyright (C) 1995-2002, Index Data
3  * All rights reserved.
4  *
5  * $Id: zebraapi.c,v 1.47 2002-03-15 20:11:36 adam Exp $
6  */
7
8 #include <assert.h>
9 #include <stdio.h>
10 #ifdef WIN32
11 #include <io.h>
12 #include <process.h>
13 #include <direct.h>
14 #else
15 #include <unistd.h>
16 #endif
17
18 #include <yaz/diagbib1.h>
19 #include "zserver.h"
20 #include <charmap.h>
21
22 static void zebra_chdir (ZebraService zh)
23 {
24     const char *dir = res_get (zh->res, "chdir");
25     if (!dir)
26         return;
27     logf (LOG_DEBUG, "chdir %s", dir);
28 #ifdef WIN32
29     _chdir(dir);
30 #else
31     chdir (dir);
32 #endif
33 }
34
35 static void zebra_flush_reg (ZebraHandle zh)
36 {
37     zebraExplain_flush (zh->service->zei, 1, zh);
38     
39     extract_flushWriteKeys (zh);
40     zebra_index_merge (zh);
41 }
42
43
44 static int zebra_register_activate (ZebraHandle zh, int rw, int useshadow);
45 static int zebra_register_deactivate (ZebraHandle zh);
46
47 static int zebra_begin_read (ZebraHandle zh);
48 static void zebra_end_read (ZebraHandle zh);
49
50 ZebraHandle zebra_open (ZebraService zs)
51 {
52     ZebraHandle zh;
53
54     assert (zs);
55     if (zs->stop_flag)
56         return 0;
57
58     zh = (ZebraHandle) xmalloc (sizeof(*zh));
59     yaz_log (LOG_LOG, "zebra_open zs=%p returns %p", zs, zh);
60
61     zh->service = zs;
62     zh->sets = 0;
63     zh->destroyed = 0;
64     zh->errCode = 0;
65     zh->errString = 0;
66
67     zh->trans_no = 0;
68     zh->seqno = 0;
69     zh->last_val = 0;
70
71     zh->lock_normal = zebra_lock_create ("norm.LCK", 0);
72     zh->lock_shadow = zebra_lock_create ("shadow.LCK", 0);
73
74     zh->key_buf = 0;
75     zh->admin_databaseName = 0;
76
77     zh->keys.buf_max = 0;
78     zh->keys.buf = 0;
79
80     zebra_mutex_cond_lock (&zs->session_lock);
81
82     zh->next = zs->sessions;
83     zs->sessions = zh;
84
85     zebra_mutex_cond_unlock (&zs->session_lock);
86
87     return zh;
88 }
89
90
91 ZebraService zebra_start (const char *configName)
92 {
93     ZebraService zh = xmalloc (sizeof(*zh));
94
95     yaz_log (LOG_LOG, "zebra_start %s", configName);
96
97     zh->configName = xstrdup(configName);
98     zh->sessions = 0;
99     zh->stop_flag = 0;
100     zh->active = 1;
101
102     zh->registerState = -1;
103     zh->registerChange = 0;
104
105     if (!(zh->res = res_open (zh->configName)))
106     {
107         logf (LOG_WARN, "Failed to read resources `%s'", zh->configName);
108 //      return zh;
109     }
110     zebra_chdir (zh);
111
112     zebra_mutex_cond_init (&zh->session_lock);
113     if (!res_get (zh->res, "passwd"))
114         zh->passwd_db = NULL;
115     else
116     {
117         zh->passwd_db = passwd_db_open ();
118         if (!zh->passwd_db)
119             logf (LOG_WARN|LOG_ERRNO, "passwd_db_open failed");
120         else
121             passwd_db_file (zh->passwd_db, res_get (zh->res, "passwd"));
122     }
123
124     return zh;
125 }
126
127 static int zebra_register_activate (ZebraHandle zh, int rw, int useshadow)
128 {
129     ZebraService zs = zh->service;
130     int record_compression = REC_COMPRESS_NONE;
131     char *recordCompression = 0;
132
133     yaz_log (LOG_LOG, "zebra_open_register_activate rw = %d useshadow=%d",
134              rw, useshadow);
135
136     zs->dh = data1_create ();
137     if (!zs->dh)
138         return -1;
139     zs->bfs = bfs_create (res_get (zs->res, "register"));
140     if (!zs->bfs)
141     {
142         data1_destroy(zs->dh);
143         return -1;
144     }
145     bf_lockDir (zs->bfs, res_get (zs->res, "lockDir"));
146     if (useshadow)
147         bf_cache (zs->bfs, res_get (zs->res, "shadow"));
148     data1_set_tabpath (zs->dh, res_get(zs->res, "profilePath"));
149     zs->recTypes = recTypes_init (zs->dh);
150     recTypes_default_handlers (zs->recTypes);
151
152     zs->zebra_maps = zebra_maps_open (zs->res);
153     zs->rank_classes = NULL;
154
155     zs->records = 0;
156     zs->dict = 0;
157     zs->sortIdx = 0;
158     zs->isams = 0;
159     zs->matchDict = 0;
160 #if ZMBOL
161     zs->isam = 0;
162     zs->isamc = 0;
163     zs->isamd = 0;
164 #endif
165     zs->zei = 0;
166     zs->matchDict = 0;
167     
168     zebraRankInstall (zs, rank1_class);
169
170     recordCompression = res_get_def (zh->service->res,
171                                      "recordCompression", "none");
172     if (!strcmp (recordCompression, "none"))
173         record_compression = REC_COMPRESS_NONE;
174     if (!strcmp (recordCompression, "bzip2"))
175         record_compression = REC_COMPRESS_BZIP2;
176
177     if (!(zs->records = rec_open (zs->bfs, rw, record_compression)))
178     {
179         logf (LOG_WARN, "rec_open");
180         return -1;
181     }
182     if (rw)
183     {
184         zs->matchDict = dict_open (zs->bfs, GMATCH_DICT, 20, 1, 0);
185     }
186     if (!(zs->dict = dict_open (zs->bfs, FNAME_DICT, 40, rw, 0)))
187     {
188         logf (LOG_WARN, "dict_open");
189         return -1;
190     }
191     if (!(zs->sortIdx = sortIdx_open (zs->bfs, rw)))
192     {
193         logf (LOG_WARN, "sortIdx_open");
194         return -1;
195     }
196     if (res_get_match (zs->res, "isam", "s", ISAM_DEFAULT))
197     {
198         struct ISAMS_M_s isams_m;
199         if (!(zs->isams = isams_open (zs->bfs, FNAME_ISAMS, rw,
200                                       key_isams_m(zs->res, &isams_m))))
201         {
202             logf (LOG_WARN, "isams_open");
203             return -1;
204         }
205     }
206 #if ZMBOL
207     else if (res_get_match (zs->res, "isam", "i", ISAM_DEFAULT))
208     {
209         if (!(zs->isam = is_open (zs->bfs, FNAME_ISAM, key_compare, rw,
210                                   sizeof (struct it_key), zs->res)))
211         {
212             logf (LOG_WARN, "is_open");
213             return -1;
214         }
215     }
216     else if (res_get_match (zs->res, "isam", "c", ISAM_DEFAULT))
217     {
218         struct ISAMC_M_s isamc_m;
219         if (!(zs->isamc = isc_open (zs->bfs, FNAME_ISAMC,
220                                     rw, key_isamc_m(zs->res, &isamc_m))))
221         {
222             logf (LOG_WARN, "isc_open");
223             return -1;
224         }
225     }
226     else if (res_get_match (zs->res, "isam", "d", ISAM_DEFAULT))
227     {
228         struct ISAMD_M_s isamd_m;
229         
230         if (!(zs->isamd = isamd_open (zs->bfs, FNAME_ISAMD,
231                                       rw, key_isamd_m(zs->res, &isamd_m))))
232         {
233             logf (LOG_WARN, "isamd_open");
234             return -1;
235         }
236     }
237 #endif
238     zs->zei = zebraExplain_open (zs->records, zs->dh,
239                                  zs->res, rw, zh,
240                                  explain_extract);
241     if (!zs->zei)
242     {
243         logf (LOG_WARN, "Cannot obtain EXPLAIN information");
244         return -1;
245     }
246     zs->active = 2;
247     yaz_log (LOG_LOG, "zebra_register_activate ok");
248     return 0;
249 }
250
251 void zebra_admin_shutdown (ZebraHandle zh)
252 {
253     zebra_mutex_cond_lock (&zh->service->session_lock);
254     zh->service->stop_flag = 1;
255     if (!zh->service->sessions)
256         zebra_register_deactivate(zh);
257     zh->service->active = 0;
258     zebra_mutex_cond_unlock (&zh->service->session_lock);
259 }
260
261 void zebra_admin_start (ZebraHandle zh)
262 {
263     ZebraService zs = zh->service;
264     zh->errCode = 0;
265     zebra_mutex_cond_lock (&zs->session_lock);
266     if (!zs->stop_flag)
267         zh->service->active = 1;
268     zebra_mutex_cond_unlock (&zs->session_lock);
269 }
270
271 static int zebra_register_deactivate (ZebraHandle zh)
272 {
273     ZebraService zs = zh->service;
274     zs->stop_flag = 0;
275     if (zs->active <= 1)
276     {
277         yaz_log(LOG_LOG, "zebra_register_deactivate (ignored since active=%d)",
278                 zs->active);
279         return 0;
280     }
281     yaz_log(LOG_LOG, "zebra_register_deactivate");
282     zebra_chdir (zs);
283     if (zs->records)
284     {
285         zebraExplain_close (zs->zei, 0);
286         dict_close (zs->dict);
287         if (zs->matchDict)
288             dict_close (zs->matchDict);
289         sortIdx_close (zs->sortIdx);
290         if (zs->isams)
291             isams_close (zs->isams);
292 #if ZMBOL
293         if (zs->isam)
294             is_close (zs->isam);
295         if (zs->isamc)
296             isc_close (zs->isamc);
297         if (zs->isamd)
298             isamd_close (zs->isamd);
299 #endif
300         rec_close (&zs->records);
301     }
302     resultSetInvalidate (zh);
303
304     recTypes_destroy (zs->recTypes);
305     zebra_maps_close (zs->zebra_maps);
306     zebraRankDestroy (zs);
307     bfs_destroy (zs->bfs);
308     data1_destroy (zs->dh);
309
310     if (zs->passwd_db)
311         passwd_db_close (zs->passwd_db);
312     zs->active = 1;
313     return 0;
314 }
315
316 void zebra_stop(ZebraService zs)
317 {
318     if (!zs)
319         return ;
320     yaz_log (LOG_LOG, "zebra_stop");
321
322     zebra_mutex_cond_lock (&zs->session_lock);
323     while (zs->sessions)
324     {
325         zebra_register_deactivate(zs->sessions);
326         zebra_close (zs->sessions);
327     }
328         
329     zebra_mutex_cond_unlock (&zs->session_lock);
330
331     zebra_mutex_cond_destroy (&zs->session_lock);
332
333     res_close (zs->res);
334     xfree (zs->configName);
335     xfree (zs);
336 }
337
338 void zebra_close (ZebraHandle zh)
339 {
340     ZebraService zs;
341     struct zebra_session **sp;
342
343     if (!zh)
344         return;
345
346     zs = zh->service;
347     yaz_log (LOG_LOG, "zebra_close zh=%p", zh);
348     if (!zh)
349         return ;
350     resultSetDestroy (zh, -1, 0, 0);
351
352     if (zh->key_buf)
353     {
354         xfree (zh->key_buf);
355         zh->key_buf = 0;
356     }
357     
358     xfree (zh->admin_databaseName);
359     zebra_mutex_cond_lock (&zs->session_lock);
360     zebra_lock_destroy (zh->lock_normal);
361     zebra_lock_destroy (zh->lock_shadow);
362     sp = &zs->sessions;
363     while (1)
364     {
365         assert (*sp);
366         if (*sp == zh)
367         {
368             *sp = (*sp)->next;
369             break;
370         }
371         sp = &(*sp)->next;
372     }
373 //    if (!zs->sessions && zs->stop_flag)
374 //      zebra_register_deactivate(zs);
375     zebra_mutex_cond_unlock (&zs->session_lock);
376     xfree (zh);
377 }
378
379 struct map_baseinfo {
380     ZebraHandle zh;
381     NMEM mem;
382     int num_bases;
383     char **basenames;
384     int new_num_bases;
385     char **new_basenames;
386     int new_num_max;
387 };
388         
389 void map_basenames_func (void *vp, const char *name, const char *value)
390 {
391     struct map_baseinfo *p = (struct map_baseinfo *) vp;
392     int i, no;
393     char fromdb[128], todb[8][128];
394     
395     no =
396         sscanf (value, "%127s %127s %127s %127s %127s %127s %127s %127s %127s",
397                 fromdb, todb[0], todb[1], todb[2], todb[3], todb[4],
398                 todb[5], todb[6], todb[7]);
399     if (no < 2)
400         return ;
401     no--;
402     for (i = 0; i<p->num_bases; i++)
403         if (p->basenames[i] && !strcmp (p->basenames[i], fromdb))
404         {
405             p->basenames[i] = 0;
406             for (i = 0; i < no; i++)
407             {
408                 if (p->new_num_bases == p->new_num_max)
409                     return;
410                 p->new_basenames[(p->new_num_bases)++] = 
411                     nmem_strdup (p->mem, todb[i]);
412             }
413             return;
414         }
415 }
416
417 void map_basenames (ZebraHandle zh, ODR stream,
418                     int *num_bases, char ***basenames)
419 {
420     struct map_baseinfo info;
421     struct map_baseinfo *p = &info;
422     int i;
423
424     info.zh = zh;
425     info.num_bases = *num_bases;
426     info.basenames = *basenames;
427     info.new_num_max = 128;
428     info.new_num_bases = 0;
429     info.new_basenames = (char **)
430         odr_malloc (stream, sizeof(*info.new_basenames) * info.new_num_max);
431     info.mem = stream->mem;
432
433     res_trav (zh->service->res, "mapdb", &info, map_basenames_func);
434     
435     for (i = 0; i<p->num_bases; i++)
436         if (p->basenames[i] && p->new_num_bases < p->new_num_max)
437         {
438             p->new_basenames[(p->new_num_bases)++] = 
439                 nmem_strdup (p->mem, p->basenames[i]);
440         }
441     *num_bases = info.new_num_bases;
442     *basenames = info.new_basenames;
443     for (i = 0; i<*num_bases; i++)
444         logf (LOG_LOG, "base %s", (*basenames)[i]);
445 }
446
447 void zebra_search_rpn (ZebraHandle zh, ODR stream, ODR decode,
448                        Z_RPNQuery *query, int num_bases, char **basenames, 
449                        const char *setname)
450 {
451     zh->hits = 0;
452     if (zebra_begin_read (zh))
453         return;
454     map_basenames (zh, stream, &num_bases, &basenames);
455     resultSetAddRPN (zh, stream, decode, query, num_bases, basenames, setname);
456
457     zebra_end_read (zh);
458
459     logf(LOG_APP,"SEARCH:%d:",zh->hits);
460 }
461
462 void zebra_records_retrieve (ZebraHandle zh, ODR stream,
463                              const char *setname, Z_RecordComposition *comp,
464                              oid_value input_format, int num_recs,
465                              ZebraRetrievalRecord *recs)
466 {
467     ZebraPosSet poset;
468     int i, *pos_array;
469
470     if (zebra_begin_read (zh))
471         return;
472
473     pos_array = (int *) xmalloc (num_recs * sizeof(*pos_array));
474     for (i = 0; i<num_recs; i++)
475         pos_array[i] = recs[i].position;
476     poset = zebraPosSetCreate (zh, setname, num_recs, pos_array);
477     if (!poset)
478     {
479         logf (LOG_DEBUG, "zebraPosSetCreate error");
480         zh->errCode = 30;
481         zh->errString = nmem_strdup (stream->mem, setname);
482     }
483     else
484     {
485         for (i = 0; i<num_recs; i++)
486         {
487             if (poset[i].term)
488             {
489                 recs[i].errCode = 0;
490                 recs[i].format = VAL_SUTRS;
491                 recs[i].len = strlen(poset[i].term);
492                 recs[i].buf = poset[i].term;
493                 recs[i].base = poset[i].db;
494             }
495             else if (poset[i].sysno)
496             {
497                 recs[i].errCode =
498                     zebra_record_fetch (zh, poset[i].sysno, poset[i].score,
499                                         stream, input_format, comp,
500                                         &recs[i].format, &recs[i].buf,
501                                         &recs[i].len,
502                                         &recs[i].base);
503                 recs[i].errString = NULL;
504             }
505             else
506             {
507                 char num_str[20];
508
509                 sprintf (num_str, "%d", pos_array[i]);  
510                 zh->errCode = 13;
511                 zh->errString = nmem_strdup (stream->mem, num_str);
512                 break;
513             }
514         }
515         zebraPosSetDestroy (zh, poset, num_recs);
516     }
517     zebra_end_read (zh);
518     xfree (pos_array);
519 }
520
521 void zebra_scan (ZebraHandle zh, ODR stream, Z_AttributesPlusTerm *zapt,
522                  oid_value attributeset,
523                  int num_bases, char **basenames,
524                  int *position, int *num_entries, ZebraScanEntry **entries,
525                  int *is_partial)
526 {
527     if (zebra_begin_read (zh))
528     {
529         *entries = 0;
530         *num_entries = 0;
531         return;
532     }
533     map_basenames (zh, stream, &num_bases, &basenames);
534     rpn_scan (zh, stream, zapt, attributeset,
535               num_bases, basenames, position,
536               num_entries, entries, is_partial);
537     zebra_end_read (zh);
538 }
539
540 void zebra_sort (ZebraHandle zh, ODR stream,
541                  int num_input_setnames, const char **input_setnames,
542                  const char *output_setname, Z_SortKeySpecList *sort_sequence,
543                  int *sort_status)
544 {
545     if (zebra_begin_read (zh))
546         return;
547     resultSetSort (zh, stream->mem, num_input_setnames, input_setnames,
548                    output_setname, sort_sequence, sort_status);
549     zebra_end_read(zh);
550 }
551
552 int zebra_deleleResultSet(ZebraHandle zh, int function,
553                           int num_setnames, char **setnames,
554                           int *statuses)
555 {
556     int i, status;
557     if (zebra_begin_read(zh))
558         return Z_DeleteStatus_systemProblemAtTarget;
559     switch (function)
560     {
561     case Z_DeleteRequest_list:
562         resultSetDestroy (zh, num_setnames, setnames, statuses);
563         break;
564     case Z_DeleteRequest_all:
565         resultSetDestroy (zh, -1, 0, statuses);
566         break;
567     }
568     zebra_end_read (zh);
569     status = Z_DeleteStatus_success;
570     for (i = 0; i<num_setnames; i++)
571         if (statuses[i] == Z_DeleteStatus_resultSetDidNotExist)
572             status = statuses[i];
573     return status;
574 }
575
576 int zebra_errCode (ZebraHandle zh)
577 {
578     return zh->errCode;
579 }
580
581 const char *zebra_errString (ZebraHandle zh)
582 {
583     return diagbib1_str (zh->errCode);
584 }
585
586 char *zebra_errAdd (ZebraHandle zh)
587 {
588     return zh->errString;
589 }
590
591 int zebra_hits (ZebraHandle zh)
592 {
593     return zh->hits;
594 }
595
596 int zebra_auth (ZebraService zh, const char *user, const char *pass)
597 {
598     if (!zh->passwd_db || !passwd_db_auth (zh->passwd_db, user, pass))
599     {
600         logf(LOG_APP,"AUTHOK:%s", user?user:"ANONYMOUS");
601         return 0;
602     }
603
604     logf(LOG_APP,"AUTHFAIL:%s", user?user:"ANONYMOUS");
605     return 1;
606 }
607
608 void zebra_admin_import_begin (ZebraHandle zh, const char *database)
609 {
610     zebra_begin_trans (zh);
611     xfree (zh->admin_databaseName);
612     zh->admin_databaseName = xstrdup(database);
613 }
614
615 void zebra_admin_import_end (ZebraHandle zh)
616 {
617     zebra_end_trans (zh);
618 }
619
620 void zebra_admin_import_segment (ZebraHandle zh, Z_Segment *segment)
621 {
622     int sysno;
623     int i;
624     if (zh->service->active < 2)
625         return;
626     for (i = 0; i<segment->num_segmentRecords; i++)
627     {
628         Z_NamePlusRecord *npr = segment->segmentRecords[i];
629         const char *databaseName = npr->databaseName;
630
631         if (!databaseName)
632             databaseName = zh->admin_databaseName;
633         printf ("--------------%d--------------------\n", i);
634         if (npr->which == Z_NamePlusRecord_intermediateFragment)
635         {
636             Z_FragmentSyntax *fragment = npr->u.intermediateFragment;
637             if (fragment->which == Z_FragmentSyntax_notExternallyTagged)
638             {
639                 Odr_oct *oct = fragment->u.notExternallyTagged;
640                 printf ("%.*s", (oct->len > 100 ? 100 : oct->len) ,
641                         oct->buf);
642                 
643                 sysno = 0;
644                 extract_rec_in_mem (zh, "grs.sgml",
645                                     oct->buf, oct->len,
646                                     databaseName,
647                                     0 /* delete_flag */,
648                                     0 /* test_mode */,
649                                     &sysno /* sysno */,
650                                     1 /* store_keys */,
651                                     1 /* store_data */,
652                                     0 /* match criteria */);
653             }
654         }
655     }
656 }
657
658 void zebra_admin_create (ZebraHandle zh, const char *database)
659 {
660     ZebraService zs;
661
662     zebra_begin_trans (zh);
663
664     zs = zh->service;
665     /* announce database */
666     if (zebraExplain_newDatabase (zh->service->zei, database, 0 
667                                   /* explainDatabase */))
668     {
669         zh->errCode = 224;
670         zh->errString = "Database already exist";
671     }
672     zebra_end_trans (zh);
673 }
674
675 int zebra_string_norm (ZebraHandle zh, unsigned reg_id,
676                        const char *input_str, int input_len,
677                        char *output_str, int output_len)
678 {
679     WRBUF wrbuf;
680     if (!zh->service->zebra_maps)
681         return -1;
682     wrbuf = zebra_replace(zh->service->zebra_maps, reg_id, "",
683                           input_str, input_len);
684     if (!wrbuf)
685         return -2;
686     if (wrbuf_len(wrbuf) >= output_len)
687         return -3;
688     if (wrbuf_len(wrbuf))
689         memcpy (output_str, wrbuf_buf(wrbuf), wrbuf_len(wrbuf));
690     output_str[wrbuf_len(wrbuf)] = '\0';
691     return wrbuf_len(wrbuf);
692 }
693
694
695 void zebra_set_state (int val, int seqno)
696 {
697     long p = getpid();
698     FILE *f = fopen ("state.LCK", "w");
699     fprintf (f, "%c %d %ld\n", val, seqno, p);
700     fclose (f);
701 }
702
703 void zebra_get_state (char *val, int *seqno)
704 {
705     FILE *f = fopen ("state.LCK", "r");
706
707     *val = 'o';
708     *seqno = 0;
709
710     if (f)
711     {
712         fscanf (f, "%c %d", val, seqno);
713         fclose (f);
714     }
715 }
716
717 static int zebra_begin_read (ZebraHandle zh)
718 {
719     int dirty = 0;
720     char val;
721     int seqno;
722
723
724     (zh->trans_no)++;
725
726     if (zh->trans_no != 1)
727     {
728         zebra_flush_reg (zh);
729         return 0;
730     }
731
732     zebra_get_state (&val, &seqno);
733     if (val == 'd')
734         val = 'o';
735     if (seqno != zh->seqno)
736     {
737         yaz_log (LOG_LOG, "reopen seqno cur/old %d/%d", seqno, zh->seqno);
738         dirty = 1;
739     }
740     else if (zh->last_val != val)
741     {
742         yaz_log (LOG_LOG, "reopen last cur/old %d/%d", val, zh->last_val);
743         dirty = 1;
744     }
745     if (!dirty)
746         return 0;
747
748     if (val == 'c')
749         zebra_lock_r (zh->lock_shadow);
750     else
751         zebra_lock_r (zh->lock_normal);
752     
753     zh->last_val = val;
754     zh->seqno = seqno;
755
756     zebra_register_deactivate (zh);
757
758     zebra_register_activate (zh, 0, val == 'c' ? 1 : 0);
759     return 0;
760 }
761
762 static void zebra_end_read (ZebraHandle zh)
763 {
764     (zh->trans_no)--;
765
766     if (zh->trans_no != 0)
767         return;
768
769     zebra_unlock (zh->lock_normal);
770     zebra_unlock (zh->lock_shadow);
771 }
772
773 void zebra_begin_trans (ZebraHandle zh)
774 {
775     int pass;
776     int seqno = 0;
777     char val = '?';
778     const char *rval;
779
780     (zh->trans_no++);
781     if (zh->trans_no != 1)
782     {
783         return;
784     }
785
786     yaz_log (LOG_LOG, "zebra_begin_trans");
787 #if HAVE_SYS_TIMES_H
788     times (&zh->tms1);
789 #endif
790
791     /* lock */
792     rval = res_get (zh->service->res, "shadow");
793
794     for (pass = 0; pass < 2; pass++)
795     {
796         if (rval)
797         {
798             zebra_lock_r (zh->lock_normal);
799             zebra_lock_w (zh->lock_shadow);
800         }
801         else
802         {
803             zebra_lock_w (zh->lock_normal);
804             zebra_lock_w (zh->lock_shadow);
805         }
806         
807         zebra_get_state (&val, &seqno);
808         if (val == 'c')
809         {
810             yaz_log (LOG_LOG, "previous transaction didn't finish commit");
811             zebra_unlock (zh->lock_shadow);
812             zebra_unlock (zh->lock_normal);
813             zebra_commit (zh);
814             continue;
815         }
816         else if (val == 'd')
817         {
818             if (rval)
819             {
820                 BFiles bfs = bfs_create (res_get (zh->service->res, "shadow"));
821                 yaz_log (LOG_LOG, "previous transaction didn't reach commit");
822                 bf_commitClean (bfs, rval);
823                 bfs_destroy (bfs);
824             }
825             else
826             {
827                 yaz_log (LOG_WARN, "your previous transaction didn't finish");
828             }
829         }
830         break;
831     }
832     if (pass == 2)
833     {
834         yaz_log (LOG_FATAL, "zebra_begin_trans couldn't finish commit");
835         abort();
836         return;
837     }
838     zebra_set_state ('d', seqno);
839
840     zebra_register_activate (zh, 1, rval ? 1 : 0);
841     zh->seqno = seqno;
842 }
843
844 void zebra_end_trans (ZebraHandle zh)
845 {
846     char val;
847     int seqno;
848     const char *rval;
849
850     zh->trans_no--;
851     if (zh->trans_no != 0)
852         return;
853
854     yaz_log (LOG_LOG, "zebra_end_trans");
855     rval = res_get (zh->service->res, "shadow");
856
857     zebra_flush_reg (zh);
858
859     zebra_register_deactivate (zh);
860
861     zebra_get_state (&val, &seqno);
862     if (val != 'd')
863     {
864         BFiles bfs = bfs_create (res_get (zh->service->res, "shadow"));
865         bf_commitClean (bfs, rval);
866         bfs_destroy (bfs);
867     }
868     if (!rval)
869         seqno++;
870     zebra_set_state ('o', seqno);
871
872     zebra_unlock (zh->lock_shadow);
873     zebra_unlock (zh->lock_normal);
874
875 #if HAVE_SYS_TIMES_H
876     times (&zh->tms2);
877     logf (LOG_LOG, "user/system: %ld/%ld",
878                     (long) (zh->tms2.tms_utime - zh->tms1.tms_utime),
879                     (long) (zh->tms2.tms_stime - zh->tms1.tms_stime));
880
881 #endif
882 }
883
884 void zebra_repository_update (ZebraHandle zh)
885 {
886     zebra_begin_trans (zh);
887     logf (LOG_LOG, "updating %s", zh->rGroup.path);
888     repositoryUpdate (zh);    
889     zebra_end_trans (zh);
890 }
891
892 void zebra_repository_delete (ZebraHandle zh)
893 {
894     logf (LOG_LOG, "deleting %s", zh->rGroup.path);
895     repositoryDelete (zh);
896 }
897
898 void zebra_repository_show (ZebraHandle zh)
899 {
900     repositoryShow (zh);
901 }
902
903 void zebra_commit (ZebraHandle zh)
904 {
905     int seqno;
906     char val;
907     const char *rval = res_get (zh->service->res, "shadow");
908     BFiles bfs;
909
910     if (!rval)
911     {
912         logf (LOG_WARN, "Cannot perform commit");
913         logf (LOG_WARN, "No shadow area defined");
914         return;
915     }
916
917     zebra_lock_w (zh->lock_normal);
918     zebra_lock_r (zh->lock_shadow);
919
920     bfs = bfs_create (res_get (zh->service->res, "register"));
921
922     zebra_get_state (&val, &seqno);
923
924     if (rval && *rval)
925         bf_cache (bfs, rval);
926     if (bf_commitExists (bfs))
927     {
928         zebra_set_state ('c', seqno);
929
930         logf (LOG_LOG, "commit start");
931         bf_commitExec (bfs);
932 #ifndef WIN32
933         sync ();
934 #endif
935         logf (LOG_LOG, "commit clean");
936         bf_commitClean (bfs, rval);
937         seqno++;
938         zebra_set_state ('o', seqno);
939     }
940     else
941     {
942         logf (LOG_LOG, "nothing to commit");
943     }
944     bfs_destroy (bfs);
945
946     zebra_unlock (zh->lock_shadow);
947     zebra_unlock (zh->lock_normal);
948 }
949
950 void zebra_init (ZebraHandle zh)
951 {
952     const char *rval = res_get (zh->service->res, "shadow");
953     BFiles bfs = 0;
954
955     bfs = bfs_create (res_get (zh->service->res, "register"));
956     if (rval && *rval)
957         bf_cache (bfs, rval);
958     
959     bf_reset (bfs);
960     bfs_destroy (bfs);
961     zebra_set_state ('o', 0);
962 }
963
964 void zebra_compact (ZebraHandle zh)
965 {
966     BFiles bfs = bfs_create (res_get (zh->service->res, "register"));
967     inv_compact (bfs);
968     bfs_destroy (bfs);
969 }
970
971 int zebra_record_insert (ZebraHandle zh, const char *buf, int len)
972 {
973     int sysno = 0;
974     zebra_begin_trans (zh);
975     extract_rec_in_mem (zh, "grs.sgml",
976                         buf, len,
977                         "Default",  /* database */
978                         0 /* delete_flag */,
979                         0 /* test_mode */,
980                         &sysno /* sysno */,
981                         1 /* store_keys */,
982                         1 /* store_data */,
983                         0 /* match criteria */);
984     zebra_end_trans (zh);
985     return sysno;
986 }