+ char *a = 0;
+ if (zh)
+ a= zh->errString;
+ yaz_log(log_level, "zebra_errAdd: %s",a);
+ return a;
+}
+
+ZEBRA_RES zebra_auth(ZebraHandle zh, const char *user, const char *pass)
+{
+ const char *p;
+ const char *astring;
+ char u[40];
+ ZebraService zs;
+
+ ZEBRA_CHECK_HANDLE(zh);
+
+ zs = zh->service;
+
+ sprintf(u, "perm.%.30s", user ? user : "anonymous");
+ p = res_get(zs->global_res, u);
+ xfree(zh->user_perm);
+ zh->user_perm = xstrdup(p ? p : "r");
+
+ /* Determine database access list */
+ astring = res_get(zs->dbaccess, user ? user : "anonymous");
+ if (astring)
+ zh->dbaccesslist = xstrdup(astring);
+ else
+ zh->dbaccesslist = 0;
+
+ /* users that don't require a password .. */
+ if (zh->user_perm && strchr(zh->user_perm, 'a'))
+ return ZEBRA_OK;
+
+ if (!zs->passwd_db || !passwd_db_auth(zs->passwd_db, user, pass))
+ return ZEBRA_OK;
+ return ZEBRA_FAIL;
+}
+
+ZEBRA_RES zebra_admin_import_begin(ZebraHandle zh, const char *database,
+ const char *record_type)
+{
+ yaz_log(log_level, "zebra_admin_import_begin db=%s rt=%s",
+ database, record_type);
+ if (zebra_select_database(zh, database) == ZEBRA_FAIL)
+ return ZEBRA_FAIL;
+ return zebra_begin_trans(zh, 1);
+}
+
+ZEBRA_RES zebra_admin_import_end(ZebraHandle zh)
+{
+ ZEBRA_CHECK_HANDLE(zh);
+ yaz_log(log_level, "zebra_admin_import_end");
+ return zebra_end_trans(zh);
+}
+
+ZEBRA_RES zebra_admin_import_segment(ZebraHandle zh, Z_Segment *segment)
+{
+ ZEBRA_RES res = ZEBRA_OK;
+ zint sysno;
+ int i;
+ ZEBRA_CHECK_HANDLE(zh);
+ yaz_log(log_level, "zebra_admin_import_segment");
+
+ for (i = 0; i<segment->num_segmentRecords; i++)
+ {
+ Z_NamePlusRecord *npr = segment->segmentRecords[i];
+
+ if (npr->which == Z_NamePlusRecord_intermediateFragment)
+ {
+ Z_FragmentSyntax *fragment = npr->u.intermediateFragment;
+ if (fragment->which == Z_FragmentSyntax_notExternallyTagged)
+ {
+ Odr_oct *oct = fragment->u.notExternallyTagged;
+ sysno = 0;
+
+ if(zebra_update_record(
+ zh,
+ action_update,
+ 0, /* record Type */
+ &sysno,
+ 0, /* match */
+ 0, /* fname */
+ (const char *) oct->buf, oct->len) == ZEBRA_FAIL)
+ res = ZEBRA_FAIL;
+ }
+ }
+ }
+ return res;
+}
+
+int delete_w_handle(const char *info, void *handle)
+{
+ ZebraHandle zh = (ZebraHandle) handle;
+ ISAM_P pos;
+ ASSERTZH;
+
+ if (*info == sizeof(pos))
+ {
+ memcpy(&pos, info+1, sizeof(pos));
+ isamb_unlink(zh->reg->isamb, pos);
+ }
+ return 0;
+}
+
+static int delete_SU_handle(void *handle, int ord)
+{
+ ZebraHandle zh = (ZebraHandle) handle;
+ char ord_buf[20];
+ int ord_len;
+
+ ord_len = key_SU_encode(ord, ord_buf);
+ ord_buf[ord_len] = '\0';
+
+ assert(zh->reg->isamb);
+ dict_delete_subtree(zh->reg->dict, ord_buf,
+ zh, delete_w_handle);
+ return 0;
+}
+
+ZEBRA_RES zebra_drop_database(ZebraHandle zh, const char *db)
+{
+ ZEBRA_RES ret = ZEBRA_OK;
+
+ yaz_log(log_level, "zebra_drop_database %s", db);
+ ZEBRA_CHECK_HANDLE(zh);
+
+ if (zebra_select_database(zh, db) == ZEBRA_FAIL)
+ return ZEBRA_FAIL;
+ if (zebra_begin_trans(zh, 1) == ZEBRA_FAIL)
+ return ZEBRA_FAIL;
+ if (zh->reg->isamb)
+ {
+ int db_ord;
+ if (zebraExplain_curDatabase(zh->reg->zei, db))
+ {
+ zebra_setError(zh, YAZ_BIB1_DATABASE_DOES_NOT_EXIST, db);
+ ret = ZEBRA_FAIL;
+ }
+ else
+ {
+ db_ord = zebraExplain_get_database_ord(zh->reg->zei);
+ dict_delete_subtree_ord(zh->reg->matchDict, db_ord,
+ 0 /* handle */, 0 /* func */);
+ zebraExplain_trav_ord(zh->reg->zei, zh, delete_SU_handle);
+ zebraExplain_removeDatabase(zh->reg->zei, zh);
+ zebra_remove_file_match(zh);
+ }
+ }
+ else
+ {
+ yaz_log(YLOG_WARN, "drop database only supported for isam:b");
+ zebra_setError(zh, YAZ_BIB1_ES_IMMEDIATE_EXECUTION_FAILED,
+ "drop database only supported for isam:b");
+ ret = ZEBRA_FAIL;
+ }
+ if (zebra_end_trans(zh) != ZEBRA_OK)
+ {
+ yaz_log(YLOG_WARN, "zebra_end_trans failed");
+ ret = ZEBRA_FAIL;
+ }
+ return ret;
+}
+
+ZEBRA_RES zebra_create_database(ZebraHandle zh, const char *db)
+{
+ yaz_log(log_level, "zebra_create_database %s", db);
+ ZEBRA_CHECK_HANDLE(zh);
+ assert(db);
+
+ if (zebra_select_database(zh, db) == ZEBRA_FAIL)
+ return ZEBRA_FAIL;
+ if (zebra_begin_trans(zh, 1))
+ return ZEBRA_FAIL;
+
+ /* announce database */
+ if (zebraExplain_newDatabase(zh->reg->zei, db, 0
+ /* explainDatabase */))
+ {
+ if (zebra_end_trans(zh) != ZEBRA_OK)
+ {
+ yaz_log(YLOG_WARN, "zebra_end_trans failed");
+ }
+ zebra_setError(zh, YAZ_BIB1_ES_IMMEDIATE_EXECUTION_FAILED, db);
+ return ZEBRA_FAIL;
+ }
+ return zebra_end_trans(zh);
+}
+
+int zebra_string_norm(ZebraHandle zh, const char *index_type,
+ const char *input_str, int input_len,
+ char *output_str, int output_len)
+{
+ WRBUF wrbuf;
+ zebra_map_t zm = zebra_map_get(zh->reg->zebra_maps, index_type);
+ ASSERTZH;
+ assert(input_str);
+ assert(output_str);
+ yaz_log(log_level, "zebra_string_norm ");
+
+ if (!zh->reg->zebra_maps)
+ return -1;
+ wrbuf = zebra_replace(zm, "", input_str, input_len);
+ if (!wrbuf)
+ return -2;
+ if (wrbuf_len(wrbuf) >= output_len)
+ return -3;
+ if (wrbuf_len(wrbuf))
+ memcpy(output_str, wrbuf_buf(wrbuf), wrbuf_len(wrbuf));
+ output_str[wrbuf_len(wrbuf)] = '\0';
+ return wrbuf_len(wrbuf);
+}
+
+/** \brief set register state (state*.LCK)
+ \param zh Zebra handle
+ \param val state
+ \param seqno sequence number
+
+ val is one of:
+ d=writing to shadow(shadow enabled); writing to register (shadow disabled)
+ o=reading only
+ c=commit (writing to register, reading from shadow, shadow mode only)
+*/
+static void zebra_set_state(ZebraHandle zh, int val, int seqno)
+{
+ char state_fname[256];
+ char *fname;
+ long p = getpid();
+ FILE *f;
+ ASSERTZH;
+ yaz_log(log_level, "zebra_set_state v=%c seq=%d", val, seqno);
+
+ sprintf(state_fname, "state.%s.LCK", zh->reg_name);
+ fname = zebra_mk_fname(res_get(zh->res, "lockDir"), state_fname);
+ f = fopen(fname, "w");
+
+ yaz_log(YLOG_DEBUG, "zebra_set_state: %c %d %ld", val, seqno, p);
+ fprintf(f, "%c %d %ld\n", val, seqno, p);
+ fclose(f);
+ xfree(fname);
+}
+
+static void zebra_get_state(ZebraHandle zh, char *val, int *seqno)
+{
+ char state_fname[256];
+ char *fname;
+ FILE *f;
+
+ ASSERTZH;
+ yaz_log(log_level, "zebra_get_state ");
+
+ sprintf(state_fname, "state.%s.LCK", zh->reg_name);
+ fname = zebra_mk_fname(res_get(zh->res, "lockDir"), state_fname);
+ f = fopen(fname, "r");
+ *val = 'o';
+ *seqno = 0;
+
+ if (f)
+ {
+ fscanf(f, "%c %d", val, seqno);
+ fclose(f);
+ }
+ xfree(fname);
+}
+
+ZEBRA_RES zebra_begin_read(ZebraHandle zh)
+{
+ return zebra_begin_trans(zh, 0);
+}
+
+ZEBRA_RES zebra_end_read(ZebraHandle zh)
+{
+ return zebra_end_trans(zh);
+}
+
+static void read_res_for_transaction(ZebraHandle zh)
+{
+ const char *group = res_get(zh->res, "group");
+ const char *v;
+ /* FIXME - do we still use groups ?? */
+
+ zh->m_group = group;
+ v = res_get_prefix(zh->res, "followLinks", group, "1");
+ zh->m_follow_links = atoi(v);
+
+ zh->m_record_id = res_get_prefix(zh->res, "recordId", group, 0);
+ zh->m_record_type = res_get_prefix(zh->res, "recordType", group, 0);
+
+ v = res_get_prefix(zh->res, "storeKeys", group, "1");
+ zh->m_store_keys = atoi(v);
+
+ v = res_get_prefix(zh->res, "storeData", group, "1");
+ zh->m_store_data = atoi(v);
+
+ v = res_get_prefix(zh->res, "explainDatabase", group, "0");
+ zh->m_explain_database = atoi(v);
+
+ v = res_get_prefix(zh->res, "openRW", group, "1");
+ zh->m_flag_rw = atoi(v);
+
+ v = res_get_prefix(zh->res, "fileVerboseLimit", group, "1000");
+ zh->m_file_verbose_limit = atoi(v);
+}
+
+ZEBRA_RES zebra_begin_trans(ZebraHandle zh, int rw)
+{
+ ZEBRA_CHECK_HANDLE(zh);
+ zebra_select_default_database(zh);
+ if (!zh->res)
+ {
+ zebra_setError(zh, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
+ "zebra_begin_trans: no database selected");
+ return ZEBRA_FAIL;
+ }
+ ASSERTZHRES;
+ yaz_log(log_level, "zebra_begin_trans rw=%d",rw);
+
+ if (zh->user_perm)
+ {
+ if (rw && !strchr(zh->user_perm, 'w'))
+ {
+ zebra_setError(
+ zh,
+ YAZ_BIB1_ES_PERMISSION_DENIED_ON_ES_CANNOT_MODIFY_OR_DELETE,
+ 0);
+ return ZEBRA_FAIL;
+ }
+ }
+
+ assert(zh->res);
+ if (rw)
+ {
+ int seqno = 0;
+ char val = '?';
+ const char *rval = 0;
+
+ (zh->trans_no++);
+ if (zh->trans_w_no)
+ {
+ read_res_for_transaction(zh);
+ return 0;
+ }
+ if (zh->trans_no != 1)
+ {
+ zebra_setError(zh, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
+ "zebra_begin_trans: no write trans within read");
+ return ZEBRA_FAIL;
+ }
+ if (zh->reg)
+ {
+ resultSetInvalidate(zh);
+ zebra_register_close(zh->service, zh->reg);
+ }
+ zh->trans_w_no = zh->trans_no;
+
+ zh->records_inserted = 0;
+ zh->records_updated = 0;
+ zh->records_deleted = 0;
+ zh->records_processed = 0;
+ zh->records_skipped = 0;
+
+#if HAVE_SYS_TIMES_H
+ times(&zh->tms1);
+#endif
+ /* lock */
+ if (zh->shadow_enable)
+ rval = res_get(zh->res, "shadow");
+
+ if (rval)
+ {
+ zebra_lock_r(zh->lock_normal);
+ zebra_lock_w(zh->lock_shadow);
+ }
+ else
+ {
+ zebra_lock_w(zh->lock_normal);
+ zebra_lock_w(zh->lock_shadow);
+ }
+ zebra_get_state(zh, &val, &seqno);
+ if (val != 'o')
+ {
+ /* either we didn't finish commit or shadow is dirty */
+ if (!rval)
+ {
+ yaz_log(YLOG_WARN, "previous transaction did not finish "
+ "(shadow disabled)");
+ }
+ zebra_unlock(zh->lock_shadow);
+ zebra_unlock(zh->lock_normal);
+ if (zebra_commit(zh))
+ {
+ zh->trans_no--;
+ zh->trans_w_no = 0;
+ return ZEBRA_FAIL;
+ }
+ if (rval)
+ {
+ zebra_lock_r(zh->lock_normal);
+ zebra_lock_w(zh->lock_shadow);
+ }
+ else
+ {
+ zebra_lock_w(zh->lock_normal);
+ zebra_lock_w(zh->lock_shadow);
+ }
+ }
+
+ zebra_set_state(zh, 'd', seqno);
+
+ zh->reg = zebra_register_open(zh->service, zh->reg_name,
+ 1, rval ? 1 : 0, zh->res,
+ zh->path_reg);
+ if (zh->reg)
+ zh->reg->seqno = seqno;
+ else
+ {
+ zebra_set_state(zh, 'o', seqno);
+
+ zebra_unlock(zh->lock_shadow);
+ zebra_unlock(zh->lock_normal);
+
+ zh->trans_no--;
+ zh->trans_w_no = 0;
+
+ zebra_setError(zh, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
+ "zebra_begin_trans: cannot open register");
+ yaz_log(YLOG_FATAL, "%s", zh->errString);
+ return ZEBRA_FAIL;
+ }
+ zebraExplain_curDatabase(zh->reg->zei, zh->basenames[0]);
+ }
+ else
+ {
+ int dirty = 0;
+ char val;
+ int seqno;
+
+ (zh->trans_no)++;
+
+ if (zh->trans_no != 1)
+ {
+ return zebra_flush_reg(zh);
+ }
+#if HAVE_SYS_TIMES_H
+ times(&zh->tms1);
+#endif
+ if (!zh->res)
+ {
+ (zh->trans_no)--;
+ zh->errCode = YAZ_BIB1_DATABASE_UNAVAILABLE;
+ return ZEBRA_FAIL;
+ }
+ if (!zh->lock_normal || !zh->lock_shadow)
+ {
+ (zh->trans_no)--;
+ zh->errCode = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
+ return ZEBRA_FAIL;
+ }
+ zebra_get_state(zh, &val, &seqno);
+ if (val == 'd')
+ val = 'o';
+
+ if (!zh->reg)
+ dirty = 1;
+ else if (seqno != zh->reg->seqno)
+ {
+ yaz_log(YLOG_DEBUG, "reopen seqno cur/old %d/%d",
+ seqno, zh->reg->seqno);
+ dirty = 1;
+ }
+ else if (zh->reg->last_val != val)
+ {
+ yaz_log(YLOG_DEBUG, "reopen last cur/old %d/%d",
+ val, zh->reg->last_val);
+ dirty = 1;
+ }
+ if (!dirty)
+ return ZEBRA_OK;
+
+ if (val == 'c')
+ zebra_lock_r(zh->lock_shadow);
+ else
+ zebra_lock_r(zh->lock_normal);
+
+ if (zh->reg)
+ {
+ resultSetInvalidate(zh);
+ zebra_register_close(zh->service, zh->reg);
+ }
+ zh->reg = zebra_register_open(zh->service, zh->reg_name,
+ 0, val == 'c' ? 1 : 0,
+ zh->res, zh->path_reg);
+ if (!zh->reg)
+ {
+ zebra_unlock(zh->lock_normal);
+ zebra_unlock(zh->lock_shadow);
+ zh->trans_no--;
+ zh->errCode = YAZ_BIB1_DATABASE_UNAVAILABLE;
+ return ZEBRA_FAIL;
+ }
+ zh->reg->last_val = val;
+ zh->reg->seqno = seqno;
+ }
+ read_res_for_transaction(zh);
+ return ZEBRA_OK;