/* * Copyright (c) 2002-2006 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. */ #include "sm/generic.h" SM_RCSID("@(#)$Id: ibdb.c,v 1.127 2007/06/18 04:42:31 ca Exp $") #include "sm/error.h" #include "sm/fcntl.h" #include "sm/memops.h" #include "sm/stat.h" #include "sm/time.h" #include "sm/heap.h" #include "sm/assert.h" #include "sm/str.h" #include "sm/cstr.h" #include "sm/io.h" #include "sm/fdset.h" #include "sm/reccom.h" #include "sm/ibdb.h" #include "sm/rcb.h" #include "sm/pthread.h" #include "sm/fs.h" #include "ibdb.h" #include "sm/bhtable.h" #include "ibdbc.h" /* CONF: Make this configurable */ #define IBC_HT_SIZE 5013 #define IBC_HT_LIMIT 65536 #define SEQ_MIN_PURGE 10 /* minimum difference between seq and first */ #define SEQ_MIN_CLEAN 2 /* min diff. between cleanup runs */ #ifndef SM_IBDB_STATS # define SM_IBDB_STATS 1 #endif /* ** ToDo: ** Deal with entries that are too long for a single record. ** ** What happens if the various directories are symbolic links? ** Does renaming work? mkdir/rmdir will break things? ** A startup script could create another IBDB directory ** (ibdbn) which will be renamed to the default directory ** on startup if necessary (i.e., W -> R, N -> W, use W). ** ** Check disk space (at least) when creating a new file. */ /* specify an INCEDB backup on disk */ struct ibdb_ctx_S { /* sm_magic? */ uint ibdb_version; /* version of this ibdb */ sm_file_T *ibdb_fp; /* current file */ sm_str_P ibdb_path; /* pathname of file */ sm_str_P ibdb_name_rm; /* pathname of file to remove */ char *ibdb_dir; /* directory of file */ char *ibdb_name; /* basename of file */ char *ibdb_base_dir; /* name of base directory */ int ibdb_mode; /* file mode */ uint32_t ibdb_first; /* first sequence number */ uint32_t ibdb_sequence; /* current sequence number */ size_t ibdb_maxsize; /* maximum size of one file */ size_t ibdb_cursize; /* current size of file */ time_t ibdb_created; /* creation time */ #if 0 time_t ibdb_touched; /* last commit; not (yet) used */ #endif uint ibdb_changes; /* number of changes since last commit */ #if SM_IBDB_STATS /* ** This could be used to remove previous files if both counters ** are zero when a new file is created in ibdb_next(). ** It could also be used to decide when to "rollover", i.e., when ** these counters are zero and cursize is "close" to maxsize. */ uint ibdb_opentas; /* outstanding transactions */ uint ibdb_openrcpts; /* outstanding recipients */ ulong ibdb_commits; #endif /* SM_IBDB_STATS */ /* locks entire ibdb_ctx... */ pthread_mutex_t ibdb_mutex; /* Pool for reuse. See include/sm/edb.h */ ibdb_req_hd_T ibdb_reql_pool; uint32_t ibdb_cleanrun; /* seq. # of last cleanup run */ bht_P ibdb_ct; fs_ctx_P ibdb_fs_ctx; int ibdb_fs_idx; uchar *ibdb_fp_buf; /* buffer for fp if necessary */ }; /* ** IBDB_REQ_NEW -- return new ibdb request (allocated or from pool) ** ** Parameters: ** ibdb_ctx -- IBDB context ** pedb_req -- ibdb req (output) ** ** Returns: ** usual sm_error code; ENOMEM ** ** Locking: ** ibdb_ctx (reql_pool) must be locked by caller ** ** Last code review: 2005-03-31 18:29:33 ** Last code change: */ static sm_ret_T ibdb_req_new(ibdb_ctx_P ibdb_ctx, ibdb_req_P *pedb_req) { ibdb_req_P ibdb_req; /* Add a parameter for locking?? It can be done locally... */ SM_REQUIRE(pedb_req != NULL); SM_REQUIRE(ibdb_ctx != NULL); if (!IBDBREQL_EMPTY(&ibdb_ctx->ibdb_reql_pool)) { ibdb_req = IBDBREQL_FIRST(&ibdb_ctx->ibdb_reql_pool); IBDBREQL_REMOVE(&ibdb_ctx->ibdb_reql_pool); } else { ibdb_req = (ibdb_req_P) sm_zalloc(sizeof(*ibdb_req)); if (NULL == ibdb_req) return sm_error_temp(SM_EM_IBDB, ENOMEM); } *pedb_req = ibdb_req; return SM_SUCCESS; } /* ** IBDB_REQ_REL -- release ibdb request: put it into pool, free allocated ** substructures/fields ** ** Parameters: ** ibdb_ctx -- IBDB context ** ibdb_req -- ibdb req ** ** Returns: ** SM_SUCCESS ** ** Locking: ** ibdb_ctx (reql_pool) must be locked by caller ** ** Last code review: 2005-03-31 18:36:28 ** Last code change: */ static sm_ret_T ibdb_req_rel(ibdb_ctx_P ibdb_ctx, ibdb_req_P ibdb_req) { if (NULL == ibdb_req) return SM_SUCCESS; SM_REQUIRE(ibdb_ctx != NULL); /* Add a parameter for locking?? It can be done locally... */ IBDBREQL_PRE(&ibdb_ctx->ibdb_reql_pool, ibdb_req); SM_STR_FREE(ibdb_req->ibdb_req_addr_pa); SM_CSTR_FREE(ibdb_req->ibdb_req_cdb_id); /* ** Clean out? ** sm_memzero(ibdb_req, sizeof(*ibdb_req)); ** can't be used since it would destroy the linked list. */ return SM_SUCCESS; } /* ** IBDB_REQ_FREE -- free ibdb request ** ** Parameters: ** ibdb_req -- ibdb req ** ** Returns: ** SM_SUCCESS ** ** Last code review: 2005-03-31 18:30:29 ** Last code change: */ static sm_ret_T ibdb_req_free(ibdb_req_P ibdb_req) { if (NULL == ibdb_req) return SM_SUCCESS; SM_STR_FREE(ibdb_req->ibdb_req_addr_pa); SM_CSTR_FREE(ibdb_req->ibdb_req_cdb_id); sm_free_size(ibdb_req, sizeof(*ibdb_req)); return SM_SUCCESS; } /* ** IBDB_REQL_FREE -- free ibdb request list ** ** Parameters: ** ibdb_ctx -- IBDB context ** ** Returns: ** SM_SUCCESS ** ** Locking: ** ibdb_ctx (reql_pool) must be locked by caller ** ** Last code review: 2005-03-31 22:35:30 ** Last code change: */ static sm_ret_T ibdb_reql_free(ibdb_ctx_P ibdb_ctx) { ibdb_req_P ibdb_req; SM_REQUIRE(ibdb_ctx != NULL); while (!IBDBREQL_EMPTY(&ibdb_ctx->ibdb_reql_pool)) { ibdb_req = IBDBREQL_FIRST(&ibdb_ctx->ibdb_reql_pool); IBDBREQL_REMOVE(&ibdb_ctx->ibdb_reql_pool); (void) ibdb_req_free(ibdb_req); } return SM_SUCCESS; } /* ** IBDB_REQ_CANCEL -- cancel ibdb requests ** ** Parameters: ** ibdb_ctx -- IBDB context ** ibdb_req_hd -- head of request list for IBDB to cancel ** ** Returns: ** SM_SUCCESS except for (un)lock errors ** ** Locking: ** ibdb_ctx (reql_pool) is locked during operation. ** ** Last code review: 2005-03-31 22:37:35 ** Last code change: */ sm_ret_T ibdb_req_cancel(ibdb_ctx_P ibdb_ctx, ibdb_req_hd_P ibdb_req_hd) { sm_ret_T ret; int r; ibdb_req_P ibdb_req; if (IBDBREQL_EMPTY(ibdb_req_hd)) return SM_SUCCESS; SM_REQUIRE(ibdb_ctx != NULL); r = pthread_mutex_lock(&ibdb_ctx->ibdb_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG? */ return sm_error_perm(SM_EM_IBDB, r); } ret = SM_SUCCESS; /* Remove request list */ while (!IBDBREQL_EMPTY(ibdb_req_hd)) { ibdb_req = IBDBREQL_FIRST(ibdb_req_hd); IBDBREQL_REMOVE(ibdb_req_hd); (void) ibdb_req_rel(ibdb_ctx, ibdb_req); /* always ok... */ } r = pthread_mutex_unlock(&ibdb_ctx->ibdb_mutex); SM_ASSERT(0 == r); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_IBDB, r); return ret; } /* ** IBDB_CTX_FREE -- free IBDB context ** ** Parameters: ** ibdb_ctx -- ibdb context ** ** Returns: ** none. ** ** Last code review: 2005-03-31 22:51:10 ** Last code change: */ static void ibdb_ctx_free(ibdb_ctx_P ibdb_ctx) { if (NULL == ibdb_ctx) return; SM_FREE(ibdb_ctx->ibdb_name); SM_FREE(ibdb_ctx->ibdb_fp_buf); SM_FREE(ibdb_ctx->ibdb_base_dir); SM_STR_FREE(ibdb_ctx->ibdb_path); SM_STR_FREE(ibdb_ctx->ibdb_name_rm); bht_destroy(ibdb_ctx->ibdb_ct, NULL, NULL); sm_free_size(ibdb_ctx, sizeof(*ibdb_ctx)); return; } /* ** IBDB_DIR_CRT -- create path for IBDB directory ** ** Parameters: ** base_dir -- name of base directory (can be NULL) ** flags -- flags ** pdir -- (pointer to) directory ** ** Returns: ** ** Last code review: ** Last code change: */ sm_ret_T ibdb_dir_crt(const char *base_dir, uint flags, char **pdir) { size_t len; char *dir; SM_REQUIRE(pdir != NULL); len = IBDB_DIR_LEN; if (base_dir != NULL && *base_dir != '\0') len += strlen(base_dir) + 1; dir = (char *) sm_zalloc(len); if (dir != NULL && base_dir != NULL && *base_dir != '\0') strlcpy(dir, base_dir, len); if (dir != NULL) { if (IBDB_IS_OPEN_FLAG(flags, IBDB_OFL_WRITE)) strlcat(dir, IBDB_WR_DIR, len); else if (IBDB_IS_OPEN_FLAG(flags, IBDB_OFL_RCVR)) strlcat(dir, IBDB_REC_DIR, len); else if (IBDB_IS_OPEN_FLAG(flags, IBDB_OFL_CLEAN)) strlcat(dir, IBDB_CL_DIR, len); else dir = "."; /* Really?? abort instead? */ } *pdir = dir; return SM_SUCCESS; } /* ** IBDB_CTX_NEW -- allocate new IBDB context ** ** Parameters: ** base_dir -- name of base directory (can be NULL) ** name -- base name, will be extended by seq ** seq -- sequence number ** flags -- flags ** ** Returns: ** NULL on error (out of memory) ** otherwise pointer to new IBDB context ** ** Last code review: 2005-03-31 22:50:37 ** Last code change: 2005-03-31 22:40:47 */ static ibdb_ctx_P ibdb_ctx_new(const char *base_dir, const char *name, uint32_t seq, uint flags) { size_t l; ibdb_ctx_P ibdb_ctx; SM_ASSERT(name != NULL); ibdb_ctx = (ibdb_ctx_P) sm_zalloc(sizeof(*ibdb_ctx)); if (NULL == ibdb_ctx) return NULL; ibdb_ctx->ibdb_ct = bht_new(IBC_HT_SIZE, IBC_HT_LIMIT); if (NULL == ibdb_ctx->ibdb_ct) goto error; l = 0; if (base_dir != NULL && *base_dir != '\0') { size_t lb; lb = strlen(base_dir) + 1; ibdb_ctx->ibdb_base_dir = (char *) sm_malloc(lb); if (NULL == ibdb_ctx->ibdb_base_dir) goto error; strlcpy(ibdb_ctx->ibdb_base_dir, base_dir, lb); } IBDB_DIR(ibdb_ctx->ibdb_dir, ibdb_ctx->ibdb_base_dir, flags); if (NULL == ibdb_ctx->ibdb_dir) goto error; l += strlen(name) + 1; ibdb_ctx->ibdb_name = (char *) sm_malloc(l); if (NULL == ibdb_ctx->ibdb_name) goto error; strlcpy(ibdb_ctx->ibdb_name, name, l); l += strlen(ibdb_ctx->ibdb_dir) + 1 + UINT32_LEN; ibdb_ctx->ibdb_path = sm_str_new(NULL, l, l); if (NULL == ibdb_ctx->ibdb_path) goto error; crt_ibdb_path(ibdb_ctx->ibdb_path, ibdb_ctx->ibdb_dir, name, seq); ++l; ibdb_ctx->ibdb_name_rm = sm_str_new(NULL, l, l); if (NULL == ibdb_ctx->ibdb_name_rm) goto error; ibdb_ctx->ibdb_sequence = seq; ibdb_ctx->ibdb_first = seq; return ibdb_ctx; error: ibdb_ctx_free(ibdb_ctx); return NULL; } /* ** IBDB_NEXT -- switch to next IBDB file ** ** Parameters: ** ibdb_ctx -- ibdb context ** ** Returns: ** usual sm_error code; I/O errors (flush, fsync, ...) et.al. ** ** Side Effects: close fp, open next file, change free space, write ** version number to file ** does not undo any of these on error (that could occur ** (almost) anywhere in the sequence) ** ** Last code review: 2005-03-31 23:13:10 ** Last code change: 2005-11-20 17:27:27 */ static sm_ret_T ibdb_next(ibdb_ctx_P ibdb_ctx) { sm_ret_T ret; sm_file_T *fp; SM_ASSERT(ibdb_ctx != NULL); ret = sm_io_flush(ibdb_ctx->ibdb_fp); if (sm_is_err(ret)) return ret; ret = fsync(sm_io_fileno(ibdb_ctx->ibdb_fp)); if (sm_is_err(ret)) return ret; ret = sm_io_close(ibdb_ctx->ibdb_fp, SM_IO_CF_NONE); ibdb_ctx->ibdb_fp = NULL; if (sm_is_err(ret)) return ret; if (ibdb_ctx->ibdb_fs_ctx != NULL) { ulong kbfree; ret = fs_chgfree(ibdb_ctx->ibdb_fs_ctx, ibdb_ctx->ibdb_fs_idx, (long) ((0L - (long) ibdb_ctx->ibdb_cursize) / ONEKB), &kbfree); } ibdb_ctx->ibdb_sequence++; sm_str_clr(ibdb_ctx->ibdb_path); crt_ibdb_path(ibdb_ctx->ibdb_path, ibdb_ctx->ibdb_dir, ibdb_ctx->ibdb_name, ibdb_ctx->ibdb_sequence); ret = sm_io_open(SmStStdio, sm_str_getdata(ibdb_ctx->ibdb_path), ibdb_ctx->ibdb_mode, &fp, SM_IO_WHAT_END); if (sm_is_err(ret)) return ret; if (NULL != ibdb_ctx->ibdb_fp_buf) { ret = sm_io_setvbuf(fp, ibdb_ctx->ibdb_fp_buf, SM_IO_FBF, IBDB_BUF_SIZE); if (sm_is_err(ret)) goto error; } /* only if writing? */ ibdb_ctx->ibdb_version = IBDB_VERSION; ibdb_ctx->ibdb_fp = fp; ibdb_ctx->ibdb_cursize = 0; #if 0 ibdb_ctx->ibdb_maxsize = ?; ibdb_ctx->ibdb_created = now; ibdb_ctx->ibdb_touched = now; #endif /* 0 */ /* first: record type */ ret = sm_io_fputuint32(fp, RT_IBDB_VERS_R); if (sm_is_err(ret)) goto error; ret = sm_io_fputv(fp, SM_RCBV_INT, RT_IBDB_VERS_N, ibdb_ctx->ibdb_version, SM_RCBV_END); if (sm_is_err(ret)) goto error; ret = sm_io_falign(fp, IBDB_REC_SIZE); if (sm_is_err(ret)) goto error; error: /* no cleanup... many side effects possible! */ return ret; } /* ** IBDB_COMMIT -- commit ibdb backup to disk ** ** Parameters: ** ibdb_ctx -- ibdb context ** ** Returns: ** usual sm_error code; I/O errors (flush, fsync), (un)lock ** ** Side Effects: flush/fsync file ** ** Last code review: 2005-04-12 15:56:25 ** Last code change: 2005-04-12 15:56:14 */ sm_ret_T ibdb_commit(ibdb_ctx_P ibdb_ctx) { sm_ret_T ret; int r; SM_ASSERT(ibdb_ctx != NULL); r = pthread_mutex_lock(&ibdb_ctx->ibdb_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG? */ return sm_error_perm(SM_EM_IBDB, r); } if (ibdb_ctx->ibdb_changes > 0) { ret = sm_io_flush(ibdb_ctx->ibdb_fp); if (sm_is_err(ret)) goto error; #if HAVE_FDATASYNC r = fdatasync(sm_io_fileno(ibdb_ctx->ibdb_fp)); #else r = fsync(sm_io_fileno(ibdb_ctx->ibdb_fp)); #endif if (r != 0) { ret = sm_error_perm(SM_EM_IBDB, r); goto error; } #if SM_IBDB_STATS ++ibdb_ctx->ibdb_commits; #endif #if 0 ibdb_ctx->ibdb_touched = now; #endif ibdb_ctx->ibdb_changes = 0; } else ret = SM_SUCCESS; error: r = pthread_mutex_unlock(&ibdb_ctx->ibdb_mutex); SM_ASSERT(0 == r); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_IBDB, r); return ret; } /* ** IBDB_OPEN -- open ibdb backup on disk ** ** Parameters: ** base_dir -- name of base directory (can be NULL) ** name -- base name, will be extended by seq ** mode -- open mode ** seq -- initial sequence number (> 0) ** size -- maximum size (> 0) ** flags -- flags ** fs_ctx -- file system context (can be NULL) ** pibdb_ctx -- (pointer to) ibdb context (output) ** ** Returns: ** usual sm_error code; ENOMEM, I/O errors ** ** Last code review: 2005-03-31 23:58:30 ** Last code change: 2005-11-20 17:27:37 */ sm_ret_T ibdb_open(const char *base_dir, const char *name, int mode, uint32_t seq, size_t size, uint flags, fs_ctx_P fs_ctx, ibdb_ctx_P *pibdb_ctx) { int r; sm_ret_T ret; sm_file_T *fp; size_t bufsize; uint fct_state; ibdb_ctx_P ibdb_ctx; struct stat sb; #define FST_MUTEX_INIT 0x01 /* mutex is initialized */ SM_REQUIRE(pibdb_ctx != NULL); SM_REQUIRE(seq > 0); SM_REQUIRE(size > 0); /* larger minimum? */ fp = NULL; fct_state = 0; ibdb_ctx = ibdb_ctx_new(base_dir, name, seq, flags); if (NULL == ibdb_ctx) return sm_error_temp(SM_EM_IBDB, ENOMEM); /* Check whether ibdb_ctx->ibdb_dir exists: if no: try to create it */ r = stat(ibdb_ctx->ibdb_dir, &sb); if (r < 0) { r = sm_mkdir(ibdb_ctx->ibdb_dir, 0700); if (r < 0) { ret = sm_error_temp(SM_EM_IBDB, errno); goto error; } } ret = sm_io_open(SmStStdio, sm_str_getdata(ibdb_ctx->ibdb_path), mode, &fp, SM_IO_WHAT_END); if (sm_is_err(ret)) goto error; (void) sm_whatbuf(fp, &bufsize); if (0 == bufsize || (bufsize % IBDB_REC_SIZE) != 0) { bufsize = (0 == bufsize) ? IBDB_BUF_SIZE : (((bufsize / IBDB_REC_SIZE) + 1) * IBDB_REC_SIZE); ibdb_ctx->ibdb_fp_buf = sm_malloc(bufsize); if (NULL == ibdb_ctx->ibdb_fp_buf) goto error; ret = sm_io_setvbuf(fp, ibdb_ctx->ibdb_fp_buf, SM_IO_FBF, bufsize); if (sm_is_err(ret)) goto error; } r = pthread_mutex_init(&ibdb_ctx->ibdb_mutex, SM_PTHREAD_MUTEXATTR); if (r != 0) { ret = sm_error_perm(SM_EM_IBDB, r); goto error; } SM_SET_FLAG(fct_state, FST_MUTEX_INIT); IBDBREQL_INIT(&ibdb_ctx->ibdb_reql_pool); /* preallocate some entries?? */ if (fs_ctx != NULL) { ibdb_ctx->ibdb_fs_ctx = fs_ctx; ret = fs_new(fs_ctx, ibdb_ctx->ibdb_dir, &ibdb_ctx->ibdb_fs_idx); if (sm_is_err(ret)) goto error; } /* only if writing? */ ibdb_ctx->ibdb_version = IBDB_VERSION; ibdb_ctx->ibdb_fp = fp; ibdb_ctx->ibdb_maxsize = size; ibdb_ctx->ibdb_mode = mode; /* // ibdb_ctx->ibdb_created; // ibdb_ctx->ibdb_touched; */ /* first: record type */ ret = sm_io_fputuint32(fp, RT_IBDB_VERS_R); if (sm_is_err(ret)) goto error; ret = sm_io_fputv(fp, SM_RCBV_INT, RT_IBDB_VERS_N, ibdb_ctx->ibdb_version, SM_RCBV_END); if (sm_is_err(ret)) goto error; ret = sm_io_falign(fp, IBDB_REC_SIZE); if (sm_is_err(ret)) goto error; *pibdb_ctx = ibdb_ctx; return ret; error: /* note: fs_new() not undone on error */ if (fp != NULL) { (void) sm_io_close(fp, SM_IO_CF_NONE); fp = NULL; } if (SM_IS_FLAG(fct_state, FST_MUTEX_INIT)) { (void) pthread_mutex_destroy(&ibdb_ctx->ibdb_mutex); SM_CLR_FLAG(fct_state, FST_MUTEX_INIT); } ibdb_ctx_free(ibdb_ctx); return ret; #undef FST_MUTEX_INIT } /* ** We could put the stuff ourselves in uio and call fvwrite() just once. ** That would require that we store the constants in an int array. ** We could also optimize and write putrecordTYPE() functions that put ** and int, str, char * into the buffer. */ /* ** IBDB_HDRMOD_WR -- write header modification record ** ** Parameters: ** ibdb_ctx -- INCEDB context ** ibdb_hdrmod -- header modification data ** flags -- flags ** locktype -- kind of locking ** ** Returns: ** usual sm_error code; ENOMEM (for new ta), I/O errors ** ** Last code review: ** Last code change: */ sm_ret_T ibdb_hdrmod_wr(ibdb_ctx_P ibdb_ctx, ibdb_hdrmod_P ibdb_hdrmod, uint flags, thr_lock_T locktype) { sm_file_T *fp; sm_ret_T ret; int r; SM_ASSERT(ibdb_ctx != NULL); SM_ASSERT(ibdb_hdrmod != NULL); if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&ibdb_ctx->ibdb_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG? */ return sm_error_perm(SM_EM_IBDB, r); } } if (!ibdb_is_noroll(flags) && ibdb_ctx->ibdb_cursize >= ibdb_ctx->ibdb_maxsize) { /* roll-over */ ret = ibdb_next(ibdb_ctx); if (sm_is_err(ret)) goto error; } fp = ibdb_ctx->ibdb_fp; /* first: record type */ ret = sm_io_fputuint32(fp, RT_IBDB_HDRMOD); if (sm_is_err(ret)) goto error; /* header modification */ ret = sm_io_fputv(fp, SM_RCBV_BUF, RT_IBDB_HM_TAID, (uchar *) ibdb_hdrmod->ibh_ta_id, strlen(ibdb_hdrmod->ibh_ta_id), /* use constant?? */ SM_RCBV_INT, RT_IBDB_HM_TYPE, (uint32_t) ibdb_hdrmod->ibh_type, SM_RCBV_INT, RT_IBDB_HM_POS, ibdb_hdrmod->ibh_pos, SM_RCBV_CSTR, RT_IBDB_HM_HDR, ibdb_hdrmod->ibh_hdr, SM_RCBV_END); if (sm_is_err(ret)) goto error; ret = sm_io_falign(fp, IBDB_REC_SIZE); if (sm_is_err(ret)) goto error; #if 0 ibdb_ctx->ibdb_touched = now; #endif ibdb_ctx->ibdb_cursize += IBDB_REC_SIZE; ibdb_ctx->ibdb_changes++; ret = SM_SUCCESS; /* fall through for unlocking */ error: if ((!sm_is_err(ret) && thr_unl_no_err(locktype)) || (sm_is_err(ret) && thr_unl_if_err(locktype))) { r = pthread_mutex_unlock(&ibdb_ctx->ibdb_mutex); SM_ASSERT(0 == r); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_IBDB, r); } /* cleanup? */ return ret; } /* ** IBDB_HDRMODL_WR -- write header modification list ** ** Parameters: ** ibdb_ctx -- INCEDB context ** hdrmodhd -- header of header modification list ** ta_id -- ta_id ** flags -- flags ** locktype -- kind of locking ** ** Returns: ** usual sm_error code; ENOMEM (for new ta), I/O errors ** ** Last code review: ** Last code change: */ sm_ret_T ibdb_hdrmodl_wr(ibdb_ctx_P ibdb_ctx, sm_hdrmodhd_P sm_hdrmodhd, sessta_id_T ta_id, uint flags, thr_lock_T locktype) { sm_file_T *fp; sm_ret_T ret; int r; sm_hdrmod_P sm_hdrmod; if (NULL == sm_hdrmodhd) return SM_SUCCESS; SM_ASSERT(ibdb_ctx != NULL); if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&ibdb_ctx->ibdb_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG? */ return sm_error_perm(SM_EM_IBDB, r); } } if (!ibdb_is_noroll(flags) && ibdb_ctx->ibdb_cursize >= ibdb_ctx->ibdb_maxsize) { /* roll-over */ ret = ibdb_next(ibdb_ctx); if (sm_is_err(ret)) goto error; } fp = ibdb_ctx->ibdb_fp; for (sm_hdrmod = HDRMODL_FIRST(sm_hdrmodhd); sm_hdrmod != HDRMODL_END(sm_hdrmodhd); sm_hdrmod = HDRMODL_NEXT(sm_hdrmod)) { /* first: record type */ ret = sm_io_fputuint32(fp, RT_IBDB_HDRMOD); if (sm_is_err(ret)) goto error; /* header modification */ ret = sm_io_fputv(fp, SM_RCBV_BUF, RT_IBDB_HM_TAID, (uchar *) ta_id, strlen(ta_id), /* use constant?? */ SM_RCBV_INT, RT_IBDB_HM_TYPE, (uint32_t) sm_hdrmod->sm_hm_type, SM_RCBV_INT, RT_IBDB_HM_POS, sm_hdrmod->sm_hm_pos, /* this may exceed the record size??? */ SM_RCBV_CSTR, RT_IBDB_HM_HDR, sm_hdrmod->sm_hm_hdr, SM_RCBV_END); if (sm_is_err(ret)) goto error; ret = sm_io_falign(fp, IBDB_REC_SIZE); if (sm_is_err(ret)) goto error; ibdb_ctx->ibdb_cursize += IBDB_REC_SIZE; ibdb_ctx->ibdb_changes++; } ret = SM_SUCCESS; /* fall through for unlocking */ error: if ((!sm_is_err(ret) && thr_unl_no_err(locktype)) || (sm_is_err(ret) && thr_unl_if_err(locktype))) { r = pthread_mutex_unlock(&ibdb_ctx->ibdb_mutex); SM_ASSERT(0 == r); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_IBDB, r); } /* cleanup? */ return ret; } /* ** IBDB_TA_STATUS -- write transaction status ** ** Parameters: ** ibdb_ctx -- INCEDB context ** ibdb_ta -- transaction (sender) data ** status -- transaction status ** flags -- flags ** rcpt_idx_high -- highest rcpt idx (only for cancel) ** locktype -- kind of locking ** ** Returns: ** usual sm_error code; ENOMEM (for new ta), I/O errors ** ** Last code review: 2005-04-01 00:01:25 ** Last code change: 2006-08-05 04:30:39 */ sm_ret_T ibdb_ta_status(ibdb_ctx_P ibdb_ctx, ibdb_ta_P ibdb_ta, int status, uint flags, rcpt_idx_T rcpt_idx_high, thr_lock_T locktype) { sm_file_T *fp; sm_ret_T ret; int r; SM_ASSERT(ibdb_ctx != NULL); SM_ASSERT(ibdb_ta != NULL); if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&ibdb_ctx->ibdb_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG? */ return sm_error_perm(SM_EM_IBDB, r); } } if (!ibdb_is_noroll(flags) && ibdb_ctx->ibdb_cursize >= ibdb_ctx->ibdb_maxsize) { /* roll-over */ ret = ibdb_next(ibdb_ctx); if (sm_is_err(ret)) goto error; } fp = ibdb_ctx->ibdb_fp; /* first: record type */ ret = sm_io_fputuint32(fp, RT_IBDB_TA); if (sm_is_err(ret)) goto error; /* transaction status */ ret = sm_io_fputv(fp, SM_RCBV_INT, RT_IBDB_TA_ST, status, SM_RCBV_BUF, RT_IBDB_TAID, (uchar *) ibdb_ta->ibt_ta_id, strlen(ibdb_ta->ibt_ta_id), /* use constant?? */ SM_RCBV_CSTR, RT_IBDB_CDBID, ibdb_ta->ibt_cdb_id, SM_RCBV_INT, RT_IBDB_NRCPTS, ibdb_ta->ibt_nrcpts, /* this may exceed the record size??? */ SM_RCBV_STR, RT_IBDB_MAIL, ibdb_ta->ibt_mail_pa, SM_RCBV_END); if (sm_is_err(ret)) goto error; ret = sm_io_falign(fp, IBDB_REC_SIZE); if (sm_is_err(ret)) goto error; #if 0 ibdb_ctx->ibdb_touched = now; #endif ibdb_ctx->ibdb_cursize += IBDB_REC_SIZE; ibdb_ctx->ibdb_changes++; if (IBDB_TA_NEW == status) ret = sm_ibc_ta_add(ibdb_ctx->ibdb_ct, ibdb_ta->ibt_ta_id, ibdb_ctx->ibdb_sequence); else if (IBDB_TA_CANCEL == status) { rcpt_idx_T u, lim; /* remove all rcpts from cache */ lim = SM_MAX(rcpt_idx_high + 1, ibdb_ta->ibt_nrcpts); for (u = 0; u < lim; u++) (void) sm_ibc_rcpt_rm(ibdb_ctx->ibdb_ct, ibdb_ta->ibt_ta_id, u); } else if (status != IBDB_TA_CANCEL) ret = sm_ibc_ta_rm(ibdb_ctx->ibdb_ct, ibdb_ta->ibt_ta_id); if (sm_is_err(ret)) goto error; #if SM_IBDB_STATS if (IBDB_TA_NEW == status) ibdb_ctx->ibdb_opentas++; else if (status != IBDB_TA_CANCEL) { SM_ASSERT(ibdb_ctx->ibdb_opentas > 0); ibdb_ctx->ibdb_opentas--; } #endif /* SM_IBDB_STATS */ ret = SM_SUCCESS; if (IBDB_IS_FLAG(flags, IBDB_FL_CLEAN)) ret = ibdb_clean(ibdb_ctx, THR_NO_LOCK); /* fall through for unlocking */ error: if ((!sm_is_err(ret) && thr_unl_no_err(locktype)) || (sm_is_err(ret) && thr_unl_if_err(locktype))) { r = pthread_mutex_unlock(&ibdb_ctx->ibdb_mutex); SM_ASSERT(0 == r); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_IBDB, r); } /* cleanup? */ return ret; } /* ** IBDB_RCPT_STATUS -- write recipient status ** ** Parameters: ** ibdb_ctx -- INCEDB context ** ibdb_rcpt -- recipient data ** status -- recipient status ** flags -- flags ** locktype -- kind of locking ** ** Side Effects: ** writes directly to IBDB (not undone on error; theoretically ** this could be tried with fgetpos(), fsetpos()) ** ** Returns: ** usual sm_error code; ENOMEM (for new rcpt), I/O errors ** ** Last code review: 2005-03-31 05:38:59 ** Last code change: 2005-03-30 23:08:35 */ sm_ret_T ibdb_rcpt_status(ibdb_ctx_P ibdb_ctx, ibdb_rcpt_P ibdb_rcpt, int status, uint flags, thr_lock_T locktype) { sm_file_T *fp; sm_ret_T ret; int r; SM_ASSERT(ibdb_ctx != NULL); SM_ASSERT(ibdb_rcpt != NULL); if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&ibdb_ctx->ibdb_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG? */ return sm_error_perm(SM_EM_IBDB, r); } } if (!ibdb_is_noroll(flags) && ibdb_ctx->ibdb_cursize >= ibdb_ctx->ibdb_maxsize) { /* roll-over */ ret = ibdb_next(ibdb_ctx); if (sm_is_err(ret)) goto error; } fp = ibdb_ctx->ibdb_fp; /* first: record type */ ret = sm_io_fputuint32(fp, RT_IBDB_RCPT); if (sm_is_err(ret)) goto error; /* recipient status */ ret = sm_io_fputv(fp, SM_RCBV_INT, RT_IBDB_RCPT_ST, status, SM_RCBV_INT, RT_IBDB_RCPT_IDX, ibdb_rcpt->ibr_idx, SM_RCBV_BUF, RT_IBDB_TAID, (uchar *) ibdb_rcpt->ibr_ta_id, strlen(ibdb_rcpt->ibr_ta_id), /* use constant?? */ /* this may exceed the record size??? */ SM_RCBV_STR, RT_IBDB_RCPT_PA, ibdb_rcpt->ibr_pa, SM_RCBV_END); if (sm_is_err(ret)) goto error; ret = sm_io_falign(fp, IBDB_REC_SIZE); if (sm_is_err(ret)) goto error; #if 0 ibdb_ctx->ibdb_touched = now; #endif ibdb_ctx->ibdb_cursize += IBDB_REC_SIZE; ibdb_ctx->ibdb_changes++; if (IBDB_RCPT_NEW == status) ret = sm_ibc_rcpt_add(ibdb_ctx->ibdb_ct, ibdb_rcpt->ibr_ta_id, ibdb_rcpt->ibr_idx, ibdb_ctx->ibdb_sequence); else ret = sm_ibc_rcpt_rm(ibdb_ctx->ibdb_ct, ibdb_rcpt->ibr_ta_id, ibdb_rcpt->ibr_idx); if (sm_is_err(ret)) goto error; #if SM_IBDB_STATS if (IBDB_RCPT_NEW == status) ibdb_ctx->ibdb_openrcpts++; else { SM_ASSERT(ibdb_ctx->ibdb_openrcpts > 0); ibdb_ctx->ibdb_openrcpts--; } #endif /* SM_IBDB_STATS */ ret = SM_SUCCESS; if (IBDB_IS_FLAG(flags, IBDB_FL_CLEAN)) ret = ibdb_clean(ibdb_ctx, THR_NO_LOCK); /* fall through for unlocking */ error: if ((!sm_is_err(ret) && thr_unl_no_err(locktype)) || (sm_is_err(ret) && thr_unl_if_err(locktype))) { r = pthread_mutex_unlock(&ibdb_ctx->ibdb_mutex); SM_ASSERT(0 == r); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_IBDB, r); } return ret; } /* ** IBDB_UNLINK -- unlink ibdb files ** ** Parameters: ** ibdb_ctx -- ibdb context ** first -- first sequence number to unlink ** last -- last sequence number to unlink ** ** Returns: ** SM_SUCCESS (unlink() errors are ignored) ** ** Locking: ibdb_ctx must be locked by caller. ** ** Last code review: 2005-03-31 05:00:22 ** Last code change: */ static sm_ret_T ibdb_unlink(ibdb_ctx_P ibdb_ctx, size_t first, size_t last) { size_t i; SM_ASSERT(ibdb_ctx != NULL); for (i = first; i <= last; i++) { sm_str_clr(ibdb_ctx->ibdb_name_rm); crt_ibdb_path(ibdb_ctx->ibdb_name_rm, ibdb_ctx->ibdb_dir, ibdb_ctx->ibdb_name, i); (void) unlink((const char *) sm_str_getdata(ibdb_ctx->ibdb_name_rm)); sm_str_clr(ibdb_ctx->ibdb_name_rm); } if (ibdb_ctx->ibdb_fs_ctx != NULL && last >= first) { ulong kbfree; /* This is just an estimate! The actual size may vary. */ (void) fs_chgfree(ibdb_ctx->ibdb_fs_ctx, ibdb_ctx->ibdb_fs_idx, (ibdb_ctx->ibdb_maxsize * (last - first + 1)) / ONEKB, &kbfree); } return SM_SUCCESS; } /* ** IBDB_CLOSE -- close ibdb ** ** Parameters: ** ibdb_ctx -- ibdb context ** ** Returns: ** usual sm_error code, only from sm_io_close() ** ** Locking: ** Does NOT lock ibdb_ctx because it shuts down IBDB. ** Should it try to get the lock at least? ** ** Last code review: 2005-04-01 00:03:17 ** Last code change: */ sm_ret_T ibdb_close(ibdb_ctx_P ibdb_ctx) { sm_ret_T ret; sm_file_T *fp; SM_ASSERT(ibdb_ctx != NULL); (void) ibdb_reql_free(ibdb_ctx); fp = ibdb_ctx->ibdb_fp; if (fp != NULL) { ret = sm_io_close(fp, SM_IO_CF_NONE); ibdb_ctx->ibdb_fp = NULL; if (sm_is_err(ret)) return ret; } #if SM_IBDB_STATS /* ** If there are no open transactions and no open recipients ** then unlink all ibdb files except for the last one ** because one file is needed to keep track of the last used id ** (see qm_rdibdb()). */ if (ibdb_ctx->ibdb_opentas == 0 && ibdb_ctx->ibdb_openrcpts == 0 && ibdb_ctx->ibdb_first + 1 <= ibdb_ctx->ibdb_sequence) { ibdb_unlink(ibdb_ctx, ibdb_ctx->ibdb_first, ibdb_ctx->ibdb_sequence - 1); } #endif /* SM_IBDB_STATS */ (void) pthread_mutex_destroy(&ibdb_ctx->ibdb_mutex); ibdb_ctx_free(ibdb_ctx); return SM_SUCCESS; } /* ** IBDB_HDRMOD_APP -- append header modification data to request list ** ** Parameters: ** ibdb_ctx -- IBDB context ** ibdb_hdrmod -- header modification data ** ibdb_req_hd -- request list ** ** Side Effects: none on error (except if unlock fails) ** ** Returns: ** usual sm_error code; ENOMEM, (un)lock ** ** Locking: ** locks ibdb_ctx during operation ** ** Last code review: ** Last code change: */ sm_ret_T ibdb_hdrmod_app(ibdb_ctx_P ibdb_ctx, ibdb_hdrmod_P ibdb_hdrmod, ibdb_req_hd_P ibdb_req_hd) { sm_ret_T ret; int r; ibdb_req_P ibdb_req; SM_ASSERT(ibdb_ctx != NULL); SM_ASSERT(ibdb_hdrmod != NULL); r = pthread_mutex_lock(&ibdb_ctx->ibdb_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG? */ return sm_error_perm(SM_EM_IBDB, r); } ret = ibdb_req_new(ibdb_ctx, &ibdb_req); if (sm_is_err(ret)) goto error; /* note: We could unlock here, except for releasing req in error case */ SESSTA_COPY(ibdb_req->ibdb_req_ss_ta_id, ibdb_hdrmod->ibh_ta_id); ibdb_req->ibh_req_hdr = SM_CSTR_DUP(ibdb_hdrmod->ibh_hdr); if (NULL == ibdb_req->ibh_req_hdr) { (void) ibdb_req_free(ibdb_req); /* always ok */ ret = sm_error_temp(SM_EM_IBDB, ENOMEM); goto error; } ibdb_req->ibh_req_pos = ibdb_hdrmod->ibh_pos; ibdb_req->ibh_req_type = ibdb_hdrmod->ibh_type; ibdb_req->ibdb_req_type = IBDB_REQ_HDRMOD; IBDBREQL_APP(ibdb_req_hd, ibdb_req); /* fall through for unlocking */ error: r = pthread_mutex_unlock(&ibdb_ctx->ibdb_mutex); SM_ASSERT(0 == r); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_IBDB, r); return ret; } /* ** IBDB_TA_APP -- append transaction (status) to request list ** ** Parameters: ** ibdb_ctx -- IBDB context ** ibdb_ta -- transaction (sender) data ** ibdb_req_hd -- request list ** status -- transaction status ** ** Side Effects: none on error (except if unlock fails) ** ** Returns: ** usual sm_error code; ENOMEM, (un)lock ** ** Locking: ** locks ibdb_ctx during operation ** ** Last code review: 2005-03-31 18:33:26 ** Last code change: */ sm_ret_T ibdb_ta_app(ibdb_ctx_P ibdb_ctx, ibdb_ta_P ibdb_ta, ibdb_req_hd_P ibdb_req_hd, int status) { sm_ret_T ret; int r; ibdb_req_P ibdb_req; SM_ASSERT(ibdb_ctx != NULL); SM_ASSERT(ibdb_ta != NULL); r = pthread_mutex_lock(&ibdb_ctx->ibdb_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG? */ return sm_error_perm(SM_EM_IBDB, r); } ret = ibdb_req_new(ibdb_ctx, &ibdb_req); if (sm_is_err(ret)) goto error; /* note: We could unlock here, except for releasing req in error case */ SESSTA_COPY(ibdb_req->ibdb_req_ss_ta_id, ibdb_ta->ibt_ta_id); ibdb_req->ibdb_req_addr_pa = sm_str_dup(NULL, ibdb_ta->ibt_mail_pa); if (NULL == ibdb_req->ibdb_req_addr_pa) { (void) ibdb_req_free(ibdb_req); /* always ok */ ret = sm_error_temp(SM_EM_IBDB, ENOMEM); goto error; } ibdb_req->ibdb_req_cdb_id = SM_CSTR_DUP(ibdb_ta->ibt_cdb_id); ibdb_req->ibdb_req_nrcpts = ibdb_ta->ibt_nrcpts; ibdb_req->ibdb_req_status = status; ibdb_req->ibdb_req_type = IBDB_REQ_TA; IBDBREQL_APP(ibdb_req_hd, ibdb_req); /* fall through for unlocking */ error: r = pthread_mutex_unlock(&ibdb_ctx->ibdb_mutex); SM_ASSERT(0 == r); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_IBDB, r); return ret; } /* ** IBDB_RCPT_APP -- append recipient (status) to request list ** ** Parameters: ** ibdb_ctx -- IBDB context ** ibdb_rcpt -- recipient data ** ibdb_req_hd -- request list ** status -- recipient status ** ** Side Effects: none on error (except if unlock fails) ** ** Returns: ** usual sm_error code; ENOMEM ** ** Locking: ** locks ibdb_ctx during operation ** ** Last code review: 2005-04-01 00:05:27 ** Last code change: */ sm_ret_T ibdb_rcpt_app(ibdb_ctx_P ibdb_ctx, ibdb_rcpt_P ibdb_rcpt, ibdb_req_hd_P ibdb_req_hd, int status) { ibdb_req_P ibdb_req; sm_ret_T ret; int r; SM_ASSERT(ibdb_ctx != NULL); SM_ASSERT(ibdb_rcpt != NULL); r = pthread_mutex_lock(&ibdb_ctx->ibdb_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG? */ return sm_error_perm(SM_EM_IBDB, r); } ret = ibdb_req_new(ibdb_ctx, &ibdb_req); if (sm_is_err(ret)) goto error; /* note: We could unlock here, except for releasing req in error case */ SESSTA_COPY(ibdb_req->ibdb_req_ss_ta_id, ibdb_rcpt->ibr_ta_id); ibdb_req->ibdb_req_addr_pa = sm_str_dup(NULL, ibdb_rcpt->ibr_pa); if (NULL == ibdb_req->ibdb_req_addr_pa) { ibdb_req_free(ibdb_req); ret = sm_error_temp(SM_EM_IBDB, ENOMEM); goto error; } ibdb_req->ibdb_req_rcpt_idx = ibdb_rcpt->ibr_idx; ibdb_req->ibdb_req_status = status; ibdb_req->ibdb_req_type = IBDB_REQ_RCPT; IBDBREQL_APP(ibdb_req_hd, ibdb_req); error: r = pthread_mutex_unlock(&ibdb_ctx->ibdb_mutex); SM_ASSERT(0 == r); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_IBDB, r); /* cleanup? */ return ret; } /* ** IBDB_WR_STATUS -- write status (request list) ** ** Parameters: ** ibdb_ctx -- IBDB context ** ibdb_req_hd -- request list ** ** Returns: ** usual sm_error code; ENOMEM (for new rcpt/ta), I/O errors ** ** Side Effects: ** writes to IBDB, does not undo changes when an error occurs, ** see ibdb_{ta,rcpt}_status() ** ** Locking: ** locks ibdb_ctx during operation ** ** Last code review: 2005-04-01 00:54:12 ** Last code change: */ sm_ret_T ibdb_wr_status(ibdb_ctx_P ibdb_ctx, ibdb_req_hd_P ibdb_req_hd) { sm_ret_T ret; int r; ibdb_req_P ibdb_req; ibdb_rcpt_T ibdb_rcpt; ibdb_ta_T ibdb_ta; ibdb_hdrmod_T ibdb_hdrmod; SM_ASSERT(ibdb_ctx != NULL); SM_ASSERT(ibdb_req_hd != NULL); if (IBDBREQL_EMPTY(ibdb_req_hd)) return SM_SUCCESS; r = pthread_mutex_lock(&ibdb_ctx->ibdb_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG? */ return sm_error_perm(SM_EM_IBDB, r); } ret = SM_SUCCESS; if (IBDBREQL_EMPTY(ibdb_req_hd)) goto done; /* walk through request list and write them to IBDB */ for (ibdb_req = IBDBREQL_FIRST(ibdb_req_hd); ibdb_req != IBDBREQL_END(ibdb_req_hd); ibdb_req = IBDBREQL_NEXT(ibdb_req)) { /* Check type */ switch (ibdb_req->ibdb_req_type) { case IBDB_REQ_RCPT: ibdb_rcpt.ibr_ta_id = ibdb_req->ibdb_req_ss_ta_id; ibdb_rcpt.ibr_pa = ibdb_req->ibdb_req_addr_pa; ibdb_rcpt.ibr_idx = ibdb_req->ibdb_req_rcpt_idx; ret = ibdb_rcpt_status(ibdb_ctx, &ibdb_rcpt, ibdb_req->ibdb_req_status, IBDB_FL_NOROLL, THR_NO_LOCK); break; case IBDB_REQ_TA: ibdb_ta.ibt_ta_id = ibdb_req->ibdb_req_ss_ta_id; ibdb_ta.ibt_mail_pa = ibdb_req->ibdb_req_addr_pa; ibdb_ta.ibt_cdb_id = ibdb_req->ibdb_req_cdb_id; ibdb_ta.ibt_nrcpts = ibdb_req->ibdb_req_nrcpts; ret = ibdb_ta_status(ibdb_ctx, &ibdb_ta, ibdb_req->ibdb_req_status, IBDB_FL_NOROLL, 0, THR_NO_LOCK); break; case IBDB_REQ_HDRMOD: ibdb_hdrmod.ibh_ta_id = ibdb_req->ibdb_req_ss_ta_id; ibdb_hdrmod.ibh_hdr = ibdb_req->ibh_req_hdr; ibdb_hdrmod.ibh_pos = ibdb_req->ibh_req_pos; ibdb_hdrmod.ibh_type = ibdb_req->ibh_req_type; ret = ibdb_hdrmod_wr(ibdb_ctx, &ibdb_hdrmod, IBDB_FL_NOROLL, THR_NO_LOCK); break; default: /* ** XXX Really abort? May screw up DB... ** How about logging a fatal error and returning ** that to the caller? */ sm_abort("wrong ibdb_req_type %d", ibdb_req->ibdb_req_type); ret = sm_error_perm(SM_EM_IBDB, SM_E_UNEXPECTED); break; } if (sm_is_err(ret)) goto error; } /* Commit it? Not really necessary... */ /* Everything is ok: remove requests from list */ while (!IBDBREQL_EMPTY(ibdb_req_hd)) { ibdb_req = IBDBREQL_FIRST(ibdb_req_hd); IBDBREQL_REMOVE(ibdb_req_hd); (void) ibdb_req_rel(ibdb_ctx, ibdb_req); /* always ok... */ } /* Always try to clean? */ ret = ibdb_clean(ibdb_ctx, THR_NO_LOCK); /* Fall through for unlocking */ done: error: r = pthread_mutex_unlock(&ibdb_ctx->ibdb_mutex); SM_ASSERT(0 == r); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_IBDB, r); /* cleanup? */ return ret; } /* ** IBDB_CLEAN -- cleanup IBDB ** Determine lowest sequence number that is still in use and remove ** all "older" files. ** ** Parameters: ** ibdb_ctx -- ibdb context ** locktype -- kind of locking ** ** Returns: ** usual sm_error code; only (un)lock ** ** Locking: locks ibdb_ctx if requested. ** ** Side Effects: ** unlinks ibdb files that are no longer needed. ** ** Last code review: 2005-03-31 05:05:02 ** Last code change: */ sm_ret_T ibdb_clean(ibdb_ctx_P ibdb_ctx, thr_lock_T locktype) { sm_ret_T ret; int r; uint32_t seq; SM_ASSERT(ibdb_ctx != NULL); if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&ibdb_ctx->ibdb_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG? */ return sm_error_perm(SM_EM_IBDB, r); } } ret = SM_SUCCESS; /* Not worth to do anything? At least SEQ_MIN_PURGE files? */ if (ibdb_ctx->ibdb_sequence - ibdb_ctx->ibdb_first <= SEQ_MIN_PURGE || ibdb_ctx->ibdb_sequence - ibdb_ctx->ibdb_cleanrun <= SEQ_MIN_CLEAN) goto unlock; ibdb_ctx->ibdb_cleanrun = ibdb_ctx->ibdb_sequence; ret = sm_ibc_low_seq(ibdb_ctx->ibdb_ct, &seq); /* always OK */ if (sm_is_err(ret)) goto unlock; #if 0 sm_io_fprintf(smioerr, "ibdb_clean: first=%u, seq=%u, cur=%u\n", ibdb_ctx->ibdb_first, seq, ibdb_ctx->ibdb_sequence); #endif /* no reference at all? remove everything but current file */ if (0 == seq) { /* make sure seq is > 0 */ if (ibdb_ctx->ibdb_sequence <= 1) goto unlock; seq = ibdb_ctx->ibdb_sequence - 1; } if (seq + 1 <= ibdb_ctx->ibdb_first || seq >= ibdb_ctx->ibdb_sequence || seq == 0) goto unlock; ret = ibdb_unlink(ibdb_ctx, ibdb_ctx->ibdb_first, seq - 1); if (sm_is_err(ret)) /* always OK */ goto unlock; #if 0 sm_io_fprintf(smioerr, "ibdb_clean ok: first=%u, seq=%u, cur=%u\n", ibdb_ctx->ibdb_first, seq, ibdb_ctx->ibdb_sequence); #endif ibdb_ctx->ibdb_first = seq; unlock: if ((!sm_is_err(ret) && thr_unl_no_err(locktype)) || (sm_is_err(ret) && thr_unl_if_err(locktype))) { r = pthread_mutex_unlock(&ibdb_ctx->ibdb_mutex); SM_ASSERT(0 == r); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_IBDB, r); } return ret; } /* ** IBDB_FS_GETFREE -- get free disk space in IBDB ** ** Parameters: ** ibdb_ctx -- ibdb context ** pkbfree -- (pointer to) free space (KB) (output) ** ** Returns: ** usual sm_error code; fs_getfree(): errno from stat(); ENXIO ** ** Last code review: 2005-04-21 23:50:08 ** Last code change: */ sm_ret_T ibdb_fs_getfree(ibdb_ctx_P ibdb_ctx, ulong *pkbfree) { sm_ret_T ret; SM_REQUIRE(pkbfree != NULLPTR); if (ibdb_ctx->ibdb_fs_ctx != NULL) { ret = fs_getfree(ibdb_ctx->ibdb_fs_ctx, ibdb_ctx->ibdb_fs_idx, pkbfree); } else { ret = sm_error_perm(SM_EM_IBDB, ENXIO); *pkbfree = 0; } return ret; } /* ** IBDB_STATS -- print IBDB stats ** ** Parameters: ** ibdb_ctx -- ibdb context ** fp -- output file ** ** Returns: ** SM_SUCCESS ** ** Last code review: 2005-04-01 00:55:05 ** Last code change: */ sm_ret_T ibdb_stats(ibdb_ctx_P ibdb_ctx, sm_file_T *fp) { #if SM_IBDB_STATS SM_REQUIRE(ibdb_ctx != NULL); sm_io_fprintf(fp, "IBDB open TAs =%u\n" "IBDB open RCPTs =%u\n" "IBDB commits =%lu\n" "IBDB seq nr =%u\n" "IBDB changes =%u\n" "IBDB first =%u\n" "IBDB cleanrun =%u\n" , ibdb_ctx->ibdb_opentas , ibdb_ctx->ibdb_openrcpts , ibdb_ctx->ibdb_commits , ibdb_ctx->ibdb_sequence , ibdb_ctx->ibdb_changes , ibdb_ctx->ibdb_first , ibdb_ctx->ibdb_cleanrun ); #endif /* SM_IBDB_STATS */ return SM_SUCCESS; } typedef struct ibdbs_ctx_S ibdbs_ctx_T, *ibdbs_ctx_P; struct ibdbs_ctx_S { sm_file_T *ibdbs_fp; /* file for output */ uint32_t ibdbs_entries; /* number of entries */ uint32_t ibdbs_sequence; /* current sequence number */ }; /* ** SM_IBC_SHOW -- Callback function: print bht entries ** ** Parameters: ** ibdb_ctx -- ibdb context ** ctx -- ibdbs_context ** ** Returns: ** SM_SUCCESS */ static sm_ret_T sm_ibc_show(bht_entry_P bhte, void *ctx) { ibc_e_P ibc_e; ibdbs_ctx_P ibdbs_ctx; SM_REQUIRE(bhte != NULL); SM_REQUIRE(ctx != NULL); ibc_e = (ibc_e_P) bhte->bhe_value; ibdbs_ctx = (ibdbs_ctx_P) ctx; ++ibdbs_ctx->ibdbs_entries; if (ibdbs_ctx->ibdbs_sequence > ibc_e->ibc_sequence) { sm_io_fprintf(ibdbs_ctx->ibdbs_fp, "ibc: id=%" SM_XSTR(SMTP_RCPTID_LEN) "s, type=%d, seq=%u\n" , ibc_e->ibc_id , ibc_e->ibc_type , ibc_e->ibc_sequence ); ibdbs_ctx->ibdbs_sequence = ibc_e->ibc_sequence; } return SM_SUCCESS; } /* ** IBDBC_SHOW_SEQ -- print IBDB Cache content ** ** Parameters: ** bht -- Hash table ** fp -- file pointer ** ** Returns: ** usual sm_error code; only (un)lock ** ** Locking: ** must be done by caller (ibdb_ctx->ibdb_mutex) */ sm_ret_T ibdbc_show_seq(ibdb_ctx_P ibdb_ctx, thr_lock_T locktype, sm_file_T *fp) { sm_ret_T ret; int r; ibdbs_ctx_T ibdbs_ctx; SM_ASSERT(ibdb_ctx != NULL); if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&ibdb_ctx->ibdb_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG? */ return sm_error_perm(SM_EM_IBDB, r); } } ret = SM_SUCCESS; ibdbs_ctx.ibdbs_fp = fp; ibdbs_ctx.ibdbs_sequence = UINT32_MAX; ibdbs_ctx.ibdbs_entries = 0; ret = bht_walk(ibdb_ctx->ibdb_ct, sm_ibc_show, &ibdbs_ctx); sm_io_fprintf(fp, "ibdbc: lowest_seq=%u\n", ibdbs_ctx.ibdbs_sequence); sm_io_fprintf(fp, "ibdbc: #entries =%u\n", ibdbs_ctx.ibdbs_entries); if ((!sm_is_err(ret) && thr_unl_no_err(locktype)) || (sm_is_err(ret) && thr_unl_if_err(locktype))) { r = pthread_mutex_unlock(&ibdb_ctx->ibdb_mutex); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_IBDB, r); } return ret; } #if 0 ///* //** SM_IDBSETINFO -- set/modify information for a file //** //** Parameters: //** fp -- file to set info for //** what -- type of info to set //** valp -- location of data used for setting //** //** Returns: //** usual sm_error code. //*/ // //sm_ret_T //sm_idbsetinfo(sm_file_T *fp, int what, void *valp) //{ // int r; // // switch (what) // { // case SM_IO_WHAT_COMMIT: // SM_ASSERT(is_valid_fd(f_fd(*fp))); // r = fsync(f_fd(*fp)); // if (r == -1) // return sm_error_perm(SM_EM_IBDB, errno); // return SM_SUCCESS; // // default: // return sm_error_perm(SM_EM_IBDB, EINVAL); // } //} // ///* //** SM_IDBGETINFO -- get information about the open file //** //** Parameters: //** fp -- file to get info for //** what -- type of info to get //** valp -- location to place found info //** //** Returns: //** Success: may or may not place info in 'valp' depending //** on 'what' value, and returns values >=0. Return //** value may be the obtained info //** Failure: usual sm_error code. //*/ // //sm_ret_T //sm_idbgetinfo(sm_file_T *fp, int what, void *valp) //{ // switch (what) // { // case SM_IO_WHAT_FD: // return f_fd(*fp); // // case SM_IO_WHAT_SIZE: // { // struct stat st; // // if (fstat(f_fd(*fp), &st) < 0) // return sm_error_perm(SM_EM_IBDB, errno); // return st.st_size; // } // // case SM_IO_IS_READABLE: // { // fd_set readfds; // struct timeval timeout; // // FD_ZERO(&readfds); // SM_FD_SET(f_fd(*fp), &readfds); // timeout.tv_sec = 0; // timeout.tv_usec = 0; // if (select(f_fd(*fp) + 1, FDSET_CAST &readfds, NULL, NULL, // &timeout) > 0 && // SM_FD_ISSET(f_fd(*fp), &readfds)) // return 1; // return 0; // } // // default: // return sm_error_perm(SM_EM_IBDB, EINVAL); // } //} // ///* //** SM_IDBREAD -- read from the file //** //** Parameters: //** //** Returns: //** usual sm_error code //*/ // //sm_ret_T //sm_idbread(sm_file_T *fp, uchar *buf, size_t n, ssize_t *bytesread) //{ // ssize_t ret; // // *bytesread = 0; // do // { // errno = 0; // ret = read(f_fd(*fp), buf, n); // } while (ret == -1 && EINTR == errno); // if (ret == -1) // return sm_error_perm(SM_EM_IBDB, errno); // *bytesread = ret; // // /* if the read succeeded, update the current offset */ // if (ret > 0) // fp->f_lseekoff += ret; // return SM_SUCCESS; //} #endif /* 0 */