Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief ATMI level cache
0003  *   No using named databases. Each environment is associated with single db
0004  *   To resolve conflicts in cluster mode we shall compare time with a microseconds
0005  *   for this we will extend tppost so that we can send rval as userfield1 and
0006  *   rcode as userfield2. When cache server receives request we add the records
0007  *   even if it is duplicate (for this timesync flag must be present). Then when
0008  *   we read all records, if two or more records found, then we will use the youngest
0009  *   one by UTC. If times stamps equals, then we use record as priority from the cluster
0010  *   node with a highest node id number.Z
0011  *   TODO: If want in feature stream latent commands (LCS tech),
0012  *   then we need locking! if config is changed.
0013  *
0014  * @file atmi_cache_init.c
0015  */
0016 /* -----------------------------------------------------------------------------
0017  * Enduro/X Middleware Platform for Distributed Transaction Processing
0018  * Copyright (C) 2009-2016, ATR Baltic, Ltd. All Rights Reserved.
0019  * Copyright (C) 2017-2023, Mavimax, Ltd. All Rights Reserved.
0020  * This software is released under one of the following licenses:
0021  * AGPL (with Java and Go exceptions) or Mavimax's license for commercial use.
0022  * See LICENSE file for full text.
0023  * -----------------------------------------------------------------------------
0024  * AGPL license:
0025  *
0026  * This program is free software; you can redistribute it and/or modify it under
0027  * the terms of the GNU Affero General Public License, version 3 as published
0028  * by the Free Software Foundation;
0029  *
0030  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0031  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0032  * PARTICULAR PURPOSE. See the GNU Affero General Public License, version 3
0033  * for more details.
0034  *
0035  * You should have received a copy of the GNU Affero General Public License along 
0036  * with this program; if not, write to the Free Software Foundation, Inc.,
0037  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0038  *
0039  * -----------------------------------------------------------------------------
0040  * A commercial use license is available from Mavimax, Ltd
0041  * contact@mavimax.com
0042  * -----------------------------------------------------------------------------
0043  */
0044 
0045 /*---------------------------Includes-----------------------------------*/
0046 #include <stdlib.h>
0047 #include <stdio.h>
0048 #include <errno.h>
0049 #include <string.h>
0050 #include <ndrstandard.h>
0051 #include <atmi.h>
0052 #include <atmi_tls.h>
0053 #include <typed_buf.h>
0054 #include <edbutil.h>
0055 
0056 #include "thlock.h"
0057 #include "userlog.h"
0058 #include "utlist.h"
0059 #include "exregex.h"
0060 #include <exparson.h>
0061 #include <atmi_cache.h>
0062 #include <Exfields.h>
0063 /*---------------------------Externs------------------------------------*/
0064 /*---------------------------Macros-------------------------------------*/
0065 /*---------------------------Enums--------------------------------------*/
0066 /*---------------------------Typedefs-----------------------------------*/
0067 /*---------------------------Globals------------------------------------*/
0068 /*---------------------------Statics------------------------------------*/
0069 expublic ndrx_tpcache_phydb_t *ndrx_G_tpcache_phydb = NULL; /* ptr to phys database */
0070 expublic ndrx_tpcache_db_t *ndrx_G_tpcache_db = NULL; /* ptr to cache database */
0071 expublic ndrx_tpcache_svc_t *ndrx_G_tpcache_svc = NULL; /* service cache       */
0072 /*---------------------------Prototypes---------------------------------*/
0073 
0074 /* NOTE! Table shall contain all defined buffer types  */
0075 expublic ndrx_tpcache_typesupp_t ndrx_G_tpcache_types[] =
0076 {
0077     /* typeid (idx), rule_compile func,         rule eval func,             refresh rule eval  */
0078     {BUF_TYPE_UBF, ndrx_cache_rulcomp_ubf,      ndrx_cache_ruleval_ubf,     ndrx_cache_refeval_ubf, 
0079                     ndrx_cache_keyget_ubf,      ndrx_cache_get_ubf,         ndrx_cache_put_ubf, 
0080                     ndrx_cache_del_ubf,         ndrx_cache_proc_flags_ubf,  ndrx_cache_delete_ubf, ndrx_cache_maxreject_ubf},
0081     /* dummy */
0082     {1,             NULL,                       NULL,                       NULL,
0083                     NULL,                       NULL,                       NULL,
0084                     NULL,                       NULL,                       NULL, NULL},
0085                     
0086     {BUF_TYPE_INIT, NULL,                       NULL,                       NULL,
0087                     NULL,                       NULL,                       NULL,
0088                     NULL,                       NULL,                       NULL, NULL},
0089                     
0090     {BUF_TYPE_NULL, NULL,                       NULL,                       NULL,
0091                     NULL,                       NULL,                       NULL,
0092                     NULL,                       NULL,                       NULL, NULL},
0093                     
0094     {BUF_TYPE_STRING,NULL,                      NULL,                       NULL,
0095                     NULL,                       NULL,                       NULL,
0096                     NULL,                       NULL,                       NULL, NULL},
0097                     
0098     {BUF_TYPE_CARRAY, NULL,                     NULL,                       NULL,
0099                     NULL,                       NULL,                       NULL,
0100                     NULL,                       NULL,                       NULL, NULL},
0101                     
0102     {BUF_TYPE_JSON, NULL,                       NULL,                       NULL,
0103                     NULL,                       NULL,                       NULL,
0104                     NULL,                       NULL,                       NULL, NULL},
0105                     
0106     {BUF_TYPE_VIEW, NULL,                       NULL,                       NULL,
0107                     NULL,                       NULL,                       NULL,
0108                     NULL,                       NULL,                       NULL, NULL},
0109     {EXFAIL}
0110 };
0111 
0112 /**
0113  * Function returns TRUE if cache is used
0114  * @return 
0115  */
0116 expublic int ndrx_cache_used(void)
0117 {
0118     if (NULL!=ndrx_G_tpcache_db && NULL!=ndrx_G_tpcache_svc)
0119     {
0120         return EXTRUE;
0121     }
0122     
0123     return EXFALSE;
0124 }
0125 
0126 /**
0127  * Map unix error
0128  * @param unixerr unix error
0129  * @return TP Error
0130  */
0131 expublic int ndrx_cache_maperr(int unixerr)
0132 {
0133     int ret = TPEOS;
0134     
0135     switch (unixerr)
0136     {
0137         case EDB_NOTFOUND:
0138             
0139             ret = TPENOENT;
0140             
0141             break;
0142     }
0143     
0144     return ret;
0145 }
0146 
0147 
0148 /**
0149  * Check the cachedb existence in cache, if exists, return
0150  * @param cachedb cache name
0151  * @return ptr to cache db or NULL if not found
0152  */
0153 expublic ndrx_tpcache_db_t* ndrx_cache_dbget(char *cachedb)
0154 {
0155     ndrx_tpcache_db_t *ret;
0156     
0157     EXHASH_FIND_STR( ndrx_G_tpcache_db, cachedb, ret);
0158     
0159     return ret;
0160 }
0161 
0162 /**
0163  * Get the physical database name
0164  * @param cachedb cache name
0165  * @return ptr to cache db or NULL if not found
0166  */
0167 expublic ndrx_tpcache_phydb_t* ndrx_cache_phydbget(char *cachedb)
0168 {
0169     ndrx_tpcache_phydb_t *ret;
0170     
0171     EXHASH_FIND_STR( ndrx_G_tpcache_phydb, cachedb, ret);
0172     
0173     return ret;
0174 }
0175 
0176 /**
0177  * Close database, physical
0178  * @param db descr struct
0179  */
0180 exprivate void ndrx_cache_phydb_free(ndrx_tpcache_phydb_t *phydb)
0181 {
0182 
0183     phydb->num_usages--;
0184     
0185     if (phydb->num_usages<=0)
0186     {
0187         if (NULL!=phydb->env)
0188         {
0189             edb_env_close(phydb->env);
0190         }
0191         EXHASH_DEL(ndrx_G_tpcache_phydb, phydb);
0192         NDRX_FREE(phydb);
0193     }
0194 }
0195 
0196 
0197 /**
0198  * Close database, close also reference to physical db
0199  * @param db descr struct
0200  */
0201 exprivate void ndrx_cache_db_free(ndrx_tpcache_db_t *db)
0202 {
0203     /* func checks the dbi validity */
0204     if (NULL!=db->phy)
0205     {
0206         edb_dbi_close(db->phy->env, db->dbi);
0207         ndrx_cache_phydb_free(db->phy);
0208         
0209     }
0210     
0211     NDRX_FREE(db);
0212 }
0213 
0214 /**
0215  * Delete single tpcall cache
0216  * @param tpc Tpcall cache
0217  * @return 
0218  */
0219 expublic void ndrx_cache_tpcallcache_free(ndrx_tpcallcache_t *tpc)
0220 {
0221     
0222     if (tpc->buf_type && 
0223             NULL!=ndrx_G_tpcache_types[tpc->buf_type->type_id].pf_cache_delete)
0224     {
0225         ndrx_G_tpcache_types[tpc->buf_type->type_id].pf_cache_delete(tpc);
0226     }
0227     
0228     if (NULL!=tpc->rsprule_tree)
0229     {
0230         Btreefree(tpc->rsprule_tree);
0231     }
0232     
0233     if (tpc->saveproj.regex_compiled)
0234     {
0235         ndrx_regfree(&tpc->saveproj.regex);
0236     }
0237     
0238     if (tpc->delproj.regex_compiled)
0239     {
0240         ndrx_regfree(&tpc->delproj.regex);
0241     }
0242     
0243     if (NULL!=tpc->keygroupmrej)
0244     {
0245         NDRX_FREE(tpc->keygroupmrej);
0246     }
0247     
0248     if (NULL!=tpc->keygroupmrej_abuf)
0249     {
0250         tpfree(tpc->keygroupmrej_abuf);
0251     }
0252     
0253     NDRX_FREE(tpc);
0254 }
0255 
0256 /**
0257  * Delete single service
0258  * @param svc service cache
0259  */
0260 expublic void ndrx_cache_svc_free(ndrx_tpcache_svc_t *svc)
0261 {
0262     ndrx_tpcallcache_t *elt, *el;
0263     
0264     /* killall tpcalls */
0265     DL_FOREACH_SAFE(svc->caches, el, elt)
0266     {
0267         DL_DELETE(svc->caches, el);
0268         ndrx_cache_tpcallcache_free(el);
0269     }
0270     
0271     NDRX_FREE(svc);
0272 }
0273 
0274 /**
0275  * Delete all service caches
0276  */
0277 expublic void ndrx_cache_svcs_free(void)
0278 {
0279     ndrx_tpcache_svc_t *el, *elt;
0280     EXHASH_ITER(hh, ndrx_G_tpcache_svc, el, elt)
0281     {
0282         EXHASH_DEL(ndrx_G_tpcache_svc, el);
0283         ndrx_cache_svc_free(el);
0284     }
0285 }
0286 
0287 /**
0288  * Delete all databases open
0289  */
0290 expublic void ndrx_cache_dbs_free(void)
0291 {
0292     ndrx_tpcache_db_t *el, *elt;
0293     EXHASH_ITER(hh, ndrx_G_tpcache_db, el, elt)
0294     {
0295         EXHASH_DEL(ndrx_G_tpcache_db, el);
0296         ndrx_cache_db_free(el);
0297     }
0298 }
0299 
0300 /**
0301  * Return hash handler of the global cache database
0302  * @return ptr to cache DB
0303  */
0304 expublic ndrx_tpcache_db_t *ndrx_cache_dbgethash(void)
0305 {
0306     return ndrx_G_tpcache_db;
0307 }
0308 
0309 
0310 /**
0311  * Sort data blocks by date
0312  * @param a
0313  * @param b
0314  * @return 
0315  */
0316 exprivate int sort_data_bydate(const EDB_val *a, const EDB_val *b)
0317 {
0318     ndrx_tpcache_data_t *ad = (ndrx_tpcache_data_t *)a->mv_data;
0319     ndrx_tpcache_data_t *bd = (ndrx_tpcache_data_t *)b->mv_data;
0320     
0321 #ifdef EX_ALIGNMENT_FORCE
0322     long a_t;
0323     long a_tusec;
0324     long b_t;
0325     long b_tusec;
0326     
0327     memcpy(&a_t, &ad->t, sizeof(a_t));
0328     memcpy(&a_tusec, &ad->tusec, sizeof(a_tusec));
0329     
0330     memcpy(&b_t, &bd->t, sizeof(b_t));
0331     memcpy(&b_tusec, &bd->tusec, sizeof(b_tusec));
0332     
0333     /* to get newer rec first, we change the compare order */
0334     return ndrx_utc_cmp(&b_t, &b_tusec, &a_t, &a_tusec);
0335     
0336 #else
0337     /* to get newer rec first, we change the compare order */
0338     return ndrx_utc_cmp(&bd->t, &bd->tusec, &ad->t, &ad->tusec);
0339 #endif
0340     
0341 }
0342 
0343 /**
0344  * Resolve physical db
0345  * @param db db object with filled db name and resource
0346  * @return EXSUCCEED/EXFAIL
0347  */
0348 exprivate int ndrx_cache_phydb_getref(ndrx_tpcache_db_t *db)
0349 {
0350     int ret = EXSUCCEED;
0351     unsigned int dbflags=0;
0352     ndrx_tpcache_phydb_t *phy=NULL;
0353     
0354     if (NULL!=(db->phy = ndrx_cache_phydbget(db->cachedbphy)))
0355     {
0356         db->phy->num_usages++;
0357         
0358         NDRX_LOG(log_debug, "Cache db [%s] already loaded, new usage: %d", 
0359                 db->cachedbphy, db->phy->num_usages);
0360         goto out;       
0361     }
0362     
0363     /* allocate new phy object */
0364     NDRX_CALLOC_OUT(phy, 1, sizeof(ndrx_tpcache_phydb_t), ndrx_tpcache_phydb_t);
0365     
0366     /* if not found, then open the env, configure the db... and increment counter */
0367     /* allocate physical db, if exist one.. */
0368     /* Open the database */
0369     if (EXSUCCEED!=(ret=edb_env_create(&phy->env)))
0370     {
0371         NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0372                 "CACHE: Failed to create env for [%s]: %s", 
0373                 db->cachedb, edb_strerror(errno));
0374         EXFAIL_OUT(ret);
0375     }
0376     
0377     if (EXSUCCEED!=(ret=edb_env_set_maxreaders(phy->env, db->max_readers)))
0378     {
0379         NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0380                 "CACHE: Failed to set max readers for [%s]: %s", 
0381                 db->cachedb, edb_strerror(ret));
0382         
0383         EXFAIL_OUT(ret);
0384     }
0385 
0386     if (EXSUCCEED!=(ret=edb_env_set_maxdbs(phy->env, db->max_dbs)))
0387     {
0388         NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0389                 "Failed to set max dbs for [%s]: %s", 
0390                 db->cachedb, edb_strerror(ret));
0391         
0392         EXFAIL_OUT(ret);
0393     }
0394     
0395     if (EXSUCCEED!=(ret=edb_env_set_mapsize(phy->env, db->map_size)))
0396     {
0397         NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0398                 "Failed to set map size for [%s]: %s", 
0399                 db->cachedb, edb_strerror(ret));
0400         
0401         EXFAIL_OUT(ret);
0402     }
0403     
0404     NDRX_STRCPY_SAFE(phy->resource, db->resource);
0405     NDRX_STRCPY_SAFE(phy->cachedb, db->cachedbphy);
0406     
0407     if (db->flags & NDRX_TPCACHE_FLAGS_NOSYNC)
0408     {
0409         dbflags|=EDB_NOSYNC;
0410     }
0411 
0412     if (db->flags & NDRX_TPCACHE_FLAGS_NOMETASYNC)
0413     {
0414         dbflags|=EDB_NOMETASYNC;
0415     }
0416 
0417     /* Open the DB 
0418      * In case of named database, we shall search for existing env.
0419      * if not found, only then we open.
0420      */
0421     if (EXSUCCEED!=(ret=edb_env_open(phy->env, db->resource, dbflags, db->perms)))
0422     {
0423         NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0424                 "Failed to open env [%s] [%s]: %s", 
0425                 db->cachedb, db->resource, edb_strerror(ret));
0426         
0427         EXFAIL_OUT(ret);
0428     }
0429     
0430     /* Add record to hash */
0431     EXHASH_ADD_STR(ndrx_G_tpcache_phydb, cachedb, phy);
0432     
0433     
0434     phy->num_usages++;
0435     db->phy = phy;
0436     
0437 out:
0438     /* avoid memory leak */
0439     if (NULL!=phy && EXSUCCEED!=ret)
0440     {
0441         NDRX_FREE(phy);
0442     }
0443 
0444     return ret;
0445 }
0446 
0447 /**
0448  * Resolve cache db
0449  * @param cachedb name of cache db
0450  * @param cachedbnam database name in physical db
0451  * @param cachedbphy physical database name if cachedbname same as cachedb, then
0452  * two level lookup will be employed instead of three.
0453  * @param mode either normal or create mode (started by ndrxd)
0454  * @return NULL in case of failure, non NULL, resolved ptr to cache db record
0455  */
0456 expublic ndrx_tpcache_db_t* ndrx_cache_dbresolve(char *cachedb, int mode)
0457 {
0458     int ret = EXSUCCEED;
0459     ndrx_tpcache_db_t* db;
0460     ndrx_inicfg_section_keyval_t * csection = NULL, *val = NULL, *val_tmp = NULL;
0461     /* len of @cachedb + / + subsect + EOS */
0462     char cachesection[sizeof(NDRX_CONF_SECTION_CACHEDB)+1+NDRX_CCTAG_MAX*2+1+1];
0463     char *p; 
0464     char *saveptr1 = NULL;
0465     EDB_txn *txn = NULL;
0466     unsigned int dbi_flags;
0467     int any_config = EXFALSE;
0468     char dbnametmp[NDRX_CCTAG_MAX+1];
0469     int tran_started = EXFALSE;
0470 
0471     if (NULL!=(db = ndrx_cache_dbget(cachedb)))
0472     {
0473 #ifdef NDRX_TPCACHE_DEBUG
0474         NDRX_LOG(log_debug, "Cache db [%s] already loaded", cachedb);
0475 #endif        
0476         goto out;
0477     }
0478 
0479     NDRX_CALLOC_OUT(db, 1, sizeof(ndrx_tpcache_db_t), ndrx_tpcache_db_t);
0480         
0481     /* Lookup the section from configuration */
0482     NDRX_STRCPY_SAFE(dbnametmp, cachedb);
0483     
0484     p=strchr(dbnametmp, NDRX_CACHE_NAMEDBSEP);
0485 
0486     if (NULL!=p)
0487     {
0488         NDRX_STRCPY_SAFE(db->cachedb, dbnametmp);
0489 
0490         *p=EXEOS;
0491         p++;
0492         /* the first is exact name */
0493         NDRX_STRCPY_SAFE(db->cachedbnam, dbnametmp);
0494         /* second is physical name */
0495         NDRX_STRCPY_SAFE(db->cachedbphy, p);
0496     }
0497     else
0498     {
0499         NDRX_STRCPY_SAFE(db->cachedbnam, dbnametmp);
0500         NDRX_STRCPY_SAFE(db->cachedb, dbnametmp);
0501         NDRX_STRCPY_SAFE(db->cachedbphy, dbnametmp);
0502     }
0503 
0504     NDRX_LOG(log_debug, "full name: [%s] logical name: [%s] physical name [%s]",
0505             db->cachedb, db->cachedbnam, db->cachedbphy);
0506     
0507     /* Add support for sub-sections for the key groups
0508      * the group is first sub-section and actual keyitem or keygroup
0509      * is second level 
0510      */
0511     if (0==strcmp(db->cachedb, db->cachedbnam))
0512     {
0513         snprintf(cachesection, sizeof(cachesection), "%s/%s",
0514                 NDRX_CONF_SECTION_CACHEDB, cachedb);
0515     }
0516     else
0517     {
0518         snprintf(cachesection, sizeof(cachesection), "%s/%s/%s",
0519                 NDRX_CONF_SECTION_CACHEDB, db->cachedbphy, db->cachedbnam);
0520     }
0521     
0522     NDRX_LOG(log_debug, "cache db [%s] mode %d looking up: [%s]", 
0523             cachedb, mode, cachesection);
0524     
0525     if (EXSUCCEED!=ndrx_inicfg_get_subsect(ndrx_get_G_cconfig(), 
0526                         NULL,  /* all config files */
0527                         cachesection,  /* global section */
0528                         &csection))
0529     {
0530         NDRX_CACHE_ERROR("%s: Failed to get section [%s]: %s", 
0531                 __func__, cachesection, Nstrerror(Nerror));
0532         EXFAIL_OUT(ret);
0533    }
0534     
0535    /* Parse arguments in the loop */
0536     
0537     /* Set max_readers, map_size defaults */
0538     
0539     db->max_readers = NDRX_CACHE_MAX_READERS_DFLT;
0540     db->map_size = NDRX_CACHE_MAP_SIZE_DFLT;
0541     db->perms = NDRX_CACHE_PERMS_DFLT;
0542     db->max_dbs = NDRX_CACHE_MAX_DBS_DFLT;
0543     
0544     EXHASH_ITER(hh, csection, val, val_tmp)
0545     {
0546         
0547         any_config = EXTRUE;
0548 #ifdef NDRX_TPCACHE_DEBUG
0549         NDRX_LOG(log_debug, "%s: config: key: [%s] value: [%s]",
0550                     __func__, val->key, val->val);
0551 #endif
0552         
0553         if (0==strcmp(val->key, NDRX_TPCACHE_KWD_CACHEDB))
0554         {
0555             if (0!=strcmp(val->val, db->cachedb))
0556             {
0557                 NDRX_CACHE_ERROR("%s: Invalid cache name [%s] for section [%s] "
0558                             "should be [%s]!", 
0559                 __func__, val->val, cachesection, db->cachedb);
0560                 EXFAIL_OUT(ret);
0561             }
0562         } 
0563         else if (0==strcmp(val->key, NDRX_TPCACHE_KWD_RESOURCE))
0564         {
0565             NDRX_STRCPY_SAFE(db->resource, val->val);
0566         }
0567         else if (0==strcmp(val->key, NDRX_TPCACHE_KWD_PERMS))
0568         {
0569             char *pend;
0570             db->perms = strtol(val->val, &pend, 0);
0571         }
0572         /* Also float: Parse 1000, 1K, 1M, 1G */
0573         else if (0==strcmp(val->key, NDRX_TPCACHE_KWD_LIMIT))
0574         {
0575             db->limit = (long)ndrx_num_dec_parsecfg(val->val);
0576         }
0577         else if (0==strcmp(val->key, NDRX_TPCACHE_KWD_EXPIRY))
0578         {
0579             db->expiry = (long)ndrx_num_time_parsecfg(val->val) / 1000L;
0580             
0581             /* we run seconds basis, thus divide by 1000 */
0582             
0583             if (0>=db->expiry)
0584             {
0585                 NDRX_CACHE_ERROR("Invalid expiry specified for db [%s], "
0586                         "the minimums is 1 second ", cachedb);
0587                 EXFAIL_OUT(ret);
0588             }
0589             
0590             db->flags|=NDRX_TPCACHE_FLAGS_EXPIRY;
0591         }
0592         else if (0==strcmp(val->key, NDRX_TPCACHE_KWD_FLAGS))
0593         {
0594             /* Decode flags... */
0595             p = strtok_r (val->val, ",", &saveptr1);
0596             while (p != NULL)
0597             {
0598                 /* strip off the string... */
0599                 ndrx_str_strip(p, "\t ");
0600                 
0601                 /* bootreset,lru,hits,fifo */
0602                 
0603                 if (0==strcmp(p, NDRX_TPCACHE_KWD_BOOTRST))
0604                 {
0605                     db->flags|=NDRX_TPCACHE_FLAGS_BOOTRST;
0606                 }
0607                 else if (0==strcmp(p, NDRX_TPCACHE_KWD_LRU))
0608                 {
0609                     db->flags|=NDRX_TPCACHE_FLAGS_LRU;
0610                 }
0611                 else if (0==strcmp(p, NDRX_TPCACHE_KWD_HITS))
0612                 {
0613                     db->flags|=NDRX_TPCACHE_FLAGS_HITS;
0614                 }
0615                 else if (0==strcmp(p, NDRX_TPCACHE_KWD_FIFO))
0616                 {
0617                     db->flags|=NDRX_TPCACHE_FLAGS_FIFO;
0618                 }
0619                 else if (0==strcmp(p, NDRX_TPCACHE_KWD_BCASTPUT))
0620                 {
0621                     db->flags|=NDRX_TPCACHE_FLAGS_BCASTPUT;
0622                 }
0623                 else if (0==strcmp(p, NDRX_TPCACHE_KWD_BCASTDEL))
0624                 {
0625                     db->flags|=NDRX_TPCACHE_FLAGS_BCASTDEL;
0626                 }
0627                 else if (0==strcmp(p, NDRX_TPCACHE_KWD_TIMESYNC))
0628                 {
0629                     db->flags|=NDRX_TPCACHE_FLAGS_TIMESYNC;
0630                 }
0631                 else if (0==strcmp(p, NDRX_TPCACHE_KWD_SCANDUP))
0632                 {
0633                     db->flags|=NDRX_TPCACHE_FLAGS_SCANDUP;
0634                 }
0635                 else if (0==strcmp(p, NDRX_TPCACHE_KWD_CLRNOSVC))
0636                 {
0637                     db->flags|=NDRX_TPCACHE_FLAGS_CLRNOSVC;
0638                 }
0639                 else if (0==strcmp(p, NDRX_TPCACHE_KWD_KEYITEMS))
0640                 {
0641                     db->flags|=NDRX_TPCACHE_FLAGS_KEYITEMS;
0642                 }
0643                 else if (0==strcmp(p, NDRX_TPCACHE_KWD_KEYGRP))
0644                 {
0645                     db->flags|=NDRX_TPCACHE_FLAGS_KEYGRP;
0646                 }
0647                 else if (0==strcmp(p, NDRX_TPCACHE_KWD_NOSYNC))
0648                 {
0649                     db->flags|=NDRX_TPCACHE_FLAGS_NOSYNC;
0650                 }
0651                 else if (0==strcmp(p, NDRX_TPCACHE_KWD_NOMETASYNC))
0652                 {
0653                     db->flags|=NDRX_TPCACHE_FLAGS_NOMETASYNC;
0654                 }
0655                 else
0656                 {
0657                     /* unknown flag */
0658                     NDRX_CACHE_ERROR("Ignoring unknown cachedb [%s] flag: [%s]",
0659                             cachedb, p);
0660                 }
0661                 p = strtok_r (NULL, ",", &saveptr1);
0662             }
0663             
0664         } /* Also float: Parse 1000, 1K, 1M, 1G */
0665         else if (0==strcmp(val->key, NDRX_TPCACHE_KWD_MAX_READERS))
0666         {
0667             db->max_readers = (long)ndrx_num_dec_parsecfg(val->val);
0668         }
0669         else if (0==strcmp(val->key, NDRX_TPCACHE_KWD_MAX_DBS))
0670         {
0671             db->max_dbs = (long)ndrx_num_dec_parsecfg(val->val);
0672         }
0673         /* Parse float: 1000.5, 1.2K, 1M, 1G */
0674         else if (0==strcmp(val->key, NDRX_TPCACHE_KWD_MAP_SIZE))
0675         {
0676             db->map_size = (long)ndrx_num_dec_parsecfg(val->val);
0677         }
0678         else if (0==strcmp(val->key, NDRX_TPCACHE_KWD_SUBSCR))
0679         {
0680             NDRX_STRCPY_SAFE(db->subscr, val->val);
0681         }
0682         else
0683         {
0684             NDRX_LOG(log_warn, "Ignoring unknown cache configuration param: [%s]", 
0685                     val->key);
0686             userlog("Ignoring unknown cache configuration param: [%s]", 
0687                     val->key);
0688         }
0689     }
0690     
0691     if (!any_config)
0692     {
0693         NDRX_CACHE_ERROR("Cache db [%s] not found in configuration", cachedb);
0694         EXFAIL_OUT(ret);
0695     }
0696     
0697     /* check mandatory attributes */
0698     
0699     if (EXEOS==db->cachedb[0])
0700     {
0701         NDRX_CACHE_ERROR("Missing `%s' parameter for cache [%s]", 
0702                 NDRX_TPCACHE_KWD_CACHEDB, cachedb);
0703         EXFAIL_OUT(ret);
0704     }
0705     
0706     if (EXEOS==db->resource[0])
0707     {
0708         NDRX_CACHE_ERROR("Missing `%s' parameter for cache [%s]", 
0709                 NDRX_TPCACHE_KWD_RESOURCE, cachedb);
0710         EXFAIL_OUT(ret);
0711     }
0712     
0713     /* Check invalid flags */
0714 
0715     if (((db->flags & NDRX_TPCACHE_FLAGS_LRU) &&
0716                        ((db->flags & NDRX_TPCACHE_FLAGS_HITS) ||
0717                        (db->flags & NDRX_TPCACHE_FLAGS_FIFO)))
0718         ||
0719             
0720         ((db->flags & NDRX_TPCACHE_FLAGS_HITS) &&
0721                        ((db->flags & NDRX_TPCACHE_FLAGS_LRU) ||
0722                        (db->flags & NDRX_TPCACHE_FLAGS_FIFO)))
0723         ||
0724         ((db->flags & NDRX_TPCACHE_FLAGS_FIFO) &&
0725                        ((db->flags & NDRX_TPCACHE_FLAGS_LRU) ||
0726                        (db->flags & NDRX_TPCACHE_FLAGS_HITS)))
0727             
0728             )
0729     {
0730         
0731         NDRX_LOG(log_error, "lru = %d", (db->flags & NDRX_TPCACHE_FLAGS_LRU));
0732         NDRX_LOG(log_error, "hits = %d", (db->flags & NDRX_TPCACHE_FLAGS_HITS));
0733         NDRX_LOG(log_error, "fifo = %d", (db->flags & NDRX_TPCACHE_FLAGS_FIFO));
0734         
0735         NDRX_CACHE_ERROR("For cache db [%s] flags lru,hits and fifo cannot be mixed!", 
0736                 cachedb);
0737         EXFAIL_OUT(ret);
0738     }
0739     
0740     /* Dump the DB config and open it and if we run in boot mode  
0741      * we have to reset the 
0742      */
0743 #ifdef NDRX_TPCACHE_DEBUG
0744     NDRX_TPCACHEDB_DUMPCFG(log_info, db);
0745 #endif
0746     
0747     NDRX_LOG(log_debug, "cachedb: [%s] boot mode: %d  flags: %ld reset: %d",
0748             db->cachedb, mode, db->flags, (int)(db->flags & NDRX_TPCACHE_FLAGS_BOOTRST));
0749     
0750     if (NDRX_TPCACH_INIT_BOOT==mode &&
0751             db->flags & NDRX_TPCACHE_FLAGS_BOOTRST)
0752     {
0753         char errdet[PATH_MAX+1];
0754         
0755         if (EXSUCCEED!=ndrx_mdb_unlink(db->resource, errdet, sizeof(errdet), 
0756                 LOG_FACILITY_NDRX))
0757         {
0758             NDRX_CACHE_TPERROR(TPESYSTEM, errdet);
0759             EXFAIL_OUT(ret);
0760         }
0761         
0762         #if 0
0763         if (EXSUCCEED!=(ret=edb_drop(txn, db->dbi, 0)))
0764         {
0765             NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0766                     "CACHE: Failed to clear db: [%s]: %s", 
0767                     db->cachedb, edb_strerror(ret));
0768 
0769             EXFAIL_OUT(ret);
0770         }
0771         #endif
0772     }
0773     
0774     
0775     if (EXSUCCEED!=ndrx_cache_phydb_getref(db))
0776     {
0777         NDRX_CACHE_ERROR("Failed to load physical db for [%s]/[%s]",
0778                 db->cachedbphy, db->cachedb);
0779         EXFAIL_OUT(ret);
0780     }
0781     
0782     /* Prepare the DB */
0783     if (EXSUCCEED!=(ret=edb_txn_begin(db->phy->env, NULL, 0, &txn)))
0784     {
0785         NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0786                 "Failed to begin transaction for [%s]: %s", 
0787                 db->cachedb, edb_strerror(ret));
0788         
0789         EXFAIL_OUT(ret);
0790     }
0791     tran_started = EXTRUE;
0792     
0793     /* open named db */
0794     if (db->flags & NDRX_TPCACHE_FLAGS_TIMESYNC)
0795     {
0796         /* We must perform sorting too so that first rec is 
0797          * is one we need to process and then we check for others to dump...
0798          */
0799         dbi_flags = EDB_DUPSORT;
0800     }
0801     else
0802     {
0803         dbi_flags = 0;
0804     }
0805     
0806     if (EXSUCCEED!=(ret=edb_dbi_open(txn, db->cachedbnam, dbi_flags|EDB_CREATE, &db->dbi)))
0807     {
0808         NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0809                 "Failed to open named db for [%s]: %s", 
0810                 db->cachedb, edb_strerror(ret));
0811         
0812         EXFAIL_OUT(ret);
0813     }
0814     
0815     if (db->flags & NDRX_TPCACHE_FLAGS_TIMESYNC)
0816     {
0817         if (EXSUCCEED!=(ret=edb_set_dupsort(txn, db->dbi, sort_data_bydate)))
0818         {
0819             NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0820                 "Failed to begin transaction for [%s]: %s", 
0821                 db->cachedb, edb_strerror(ret));
0822         
0823             goto out;
0824         }
0825     }
0826     
0827     NDRX_LOG(log_debug, "boot mode: %d  flags: %ld reset: %d",
0828             mode, db->flags, (int)(db->flags & NDRX_TPCACHE_FLAGS_BOOTRST));
0829     
0830     /* commit the tran */
0831     if (EXSUCCEED!=(ret=edb_txn_commit(txn)))
0832     {
0833         NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0834                 "Failed to open named db for [%s]: %s", 
0835                 db->cachedb, edb_strerror(ret));
0836         EXFAIL_OUT(ret);        
0837     }
0838     tran_started = EXFALSE;
0839     
0840     /* Add object to the hash */
0841     
0842     EXHASH_ADD_STR(ndrx_G_tpcache_db, cachedb, db);
0843     
0844     NDRX_LOG(log_debug, "Cache [%s] path: [%s] is open!", 
0845             db->resource, db->cachedb);
0846 out:
0847 
0848     if (NULL!=csection)
0849     {
0850         ndrx_keyval_hash_free(csection);
0851     }
0852 
0853     if (tran_started)
0854     {
0855         edb_txn_abort(txn);
0856     }
0857 
0858     if (EXSUCCEED!=ret)
0859     {        
0860         if (NULL!=db)
0861         {
0862             ndrx_cache_db_free(db);
0863         }
0864         
0865         return NULL;
0866     }
0867     else
0868     {
0869         return db;
0870     }
0871 }
0872 
0873 /**
0874  * Return tpcall cache by service and index!
0875  * 
0876  * WARNING!!! If doing changes in idexes, then data will become invalid and shall
0877  * be dropped!!!
0878  * 
0879  * @param svcnm service name to search for 
0880  * @param idx cache index
0881  * @return NULL (not found) or ptr to tpcall cache.
0882  */
0883 expublic ndrx_tpcallcache_t* ndrx_cache_findtpcall_byidx(char *svcnm, int idx)
0884 {
0885     ndrx_tpcache_svc_t *svcc;
0886     ndrx_tpcallcache_t* el;
0887     int ret = EXSUCCEED;
0888     int i=0;
0889     
0890     EXHASH_FIND_STR(ndrx_G_tpcache_svc, svcnm, svcc);
0891     
0892     if (NULL==svcc)
0893     {
0894         NDRX_LOG(log_debug, "No cache defined for service [%s]", svcnm);
0895         return NULL;
0896     }
0897     
0898     DL_FOREACH(svcc->caches, el)
0899     {
0900         if (i==idx)
0901         {
0902             return el;
0903         }
0904         
0905         i++;
0906     }
0907     
0908 out:
0909     return NULL;
0910 }
0911 
0912 /**
0913  * Find tpcall cache
0914  * @param idata XATMI buffer data
0915  * @param ilen data len
0916  * @param idx optional index (if not -1)
0917  * @return NULL - no cache found or not supported, ptr - to tpcall cache
0918  */
0919 expublic ndrx_tpcallcache_t* ndrx_cache_findtpcall(ndrx_tpcache_svc_t *svcc, 
0920         typed_buffer_descr_t *buf_type, char *idata, long ilen, int idx)
0921 {
0922     ndrx_tpcallcache_t* el;
0923     int ret = EXSUCCEED;
0924     char errdet[MAX_TP_ERROR_LEN+1];
0925     int i = -1;
0926     
0927     DL_FOREACH(svcc->caches, el)
0928     {
0929         i++;
0930         if (el->buf_type->type_id == buf_type->type_id)
0931         {
0932             if (i==idx)
0933             {
0934                 return el;
0935             }
0936             /* in case if index used, no rule testing! */
0937             else if ( idx > EXFAIL)
0938             {
0939                 continue;
0940             }
0941             
0942             if (ndrx_G_tpcache_types[el->buf_type->type_id].pf_rule_eval)
0943             {
0944                 ret = ndrx_G_tpcache_types[el->buf_type->type_id].pf_rule_eval(el, 
0945                         idata, ilen, errdet, sizeof(errdet));
0946                 if (EXFAIL==ret)
0947                 {
0948                     NDRX_CACHE_ERROR("%s: Failed to evaluate buffer [%s]: %s", 
0949                             __func__, el->rule, errdet);
0950                     return NULL;
0951                 }
0952                 else if (EXTRUE==ret)
0953                 {
0954 #ifdef NDRX_TPCACHE_DEBUG
0955                     NDRX_LOG(log_debug, "Buffer RULE TRUE [%s]", el->rule);
0956 #endif
0957                     return el;
0958                 }
0959                 else
0960                 {
0961 #ifdef NDRX_TPCACHE_DEBUG
0962                     NDRX_LOG(log_debug, "Buffer RULE FALSE [%s]", el->rule);
0963 #endif
0964                     /* search next... */
0965                 }
0966             }
0967             else
0968             {
0969                 /* We should not get here! */
0970                 NDRX_CACHE_ERROR("%s: Unsupported buffer type [%s] for cache", 
0971                                 __func__, el->buf_type->type);
0972                 return NULL;
0973             }
0974         }
0975         else if (i==idx)
0976         {
0977             NDRX_CACHE_ERROR("%s: Cache found at index [%d] but types "
0978                     "does not match [%s] vs [%s]", __func__,  el->buf_type->type, 
0979                     buf_type->type);
0980                 return NULL;
0981         }
0982     }
0983     
0984     return NULL;
0985 }
0986 
0987 /**
0988  * Close any open caches
0989  * @return 
0990  */
0991 expublic void ndrx_cache_uninit(void)
0992 {
0993     
0994     ndrx_cache_svcs_free();
0995     ndrx_cache_dbs_free();
0996     
0997 }
0998 
0999 /**
1000  * Normal init (used by server & clients)
1001  * @param mode See NDRX_TPCACH_INIT_*
1002  */
1003 expublic int ndrx_cache_init(int mode)
1004 {
1005     int ret = EXSUCCEED;
1006     char *svc;
1007     EXJSON_Value *root_value=NULL;
1008     EXJSON_Object *root_object, *array_object;
1009     EXJSON_Array *array;
1010     int type;
1011     int nrcaches;
1012     char *name;
1013     int cnt;
1014     const char *tmp;
1015     char flagstr[NDRX_CACHE_FLAGS_MAX+1];
1016     size_t i;
1017     ndrx_tpcallcache_t *cache = NULL;
1018     ndrx_tpcache_svc_t *cachesvc = NULL;
1019     char errdet[MAX_TP_ERROR_LEN+1];
1020     char *p_flags;
1021     ndrx_inicfg_section_keyval_t * csection = NULL, *val = NULL, *val_tmp = NULL;
1022     char *saveptr1 = NULL;
1023     
1024     /* So if we are here, the configuration file should be already parsed 
1025      * We need to load the config file to AST
1026      */
1027 
1028     /* Firstly we search for [@cache[/sub-sect]] 
1029      * Then for each ^svc\ .* we parse the json block and build
1030      * up the cache descriptor
1031      */
1032     if (NULL==ndrx_get_G_cconfig())
1033     {
1034         /* no config used... nothing to do.. */
1035 #ifdef NDRX_TPCACHE_DEBUG
1036         NDRX_LOG(log_debug, "No CConfig - skip cache init");
1037 #endif
1038         goto out;
1039     }
1040 
1041     if (EXSUCCEED!=ndrx_cconfig_get(NDRX_CONF_SECTION_CACHE, &csection))
1042     {
1043         /* config not found... nothing todo.. */
1044 #ifdef NDRX_TPCACHE_DEBUG
1045         NDRX_LOG(log_debug, "[%s] section not found - no cache init",
1046                 NDRX_CONF_SECTION_CACHE);
1047 #endif
1048         goto out;
1049     }
1050 
1051     EXHASH_ITER(hh, csection, val, val_tmp)
1052     {
1053         
1054 #ifdef NDRX_TPCACHE_DEBUG
1055         NDRX_LOG(log_info, "Found cache service: key: [%s] value: [%s]",
1056                 val->key, val->val);
1057 #endif
1058         /* Have to sort out the entries and build the AST */
1059   
1060         if (0!=strncmp(val->key, "svc ", 4) && 0!=strncmp(val->key, "svc\t", 4) )
1061         {
1062             continue;
1063         }
1064         
1065         /* Strip to remove the whitespace */
1066         svc = ndrx_str_lstrip_ptr(val->key+4, "\t ");
1067         
1068 #ifdef NDRX_TPCACHE_DEBUG
1069         NDRX_LOG(log_info, "Got service: [%s] ... parsing json config", svc);
1070 #endif
1071         
1072         /* parse json block */
1073         if (NULL!=root_value)
1074         {
1075             exjson_value_free(root_value);
1076         }
1077         
1078         root_value = exjson_parse_string_with_comments(val->val);
1079         
1080         NDRX_LOG(log_debug, "exjson_parse_string_with_comments %p", root_value);
1081                 
1082         type = exjson_value_get_type(root_value);
1083         NDRX_LOG(log_debug, "Type is %d", type);
1084 
1085         if (exjson_value_get_type(root_value) != EXJSONObject)
1086         {
1087             NDRX_CACHE_ERROR("cache: invalid service [%s] cache: Failed "
1088                     "to parse root element", svc);
1089             EXFAIL_OUT(ret);
1090         }
1091         
1092         root_object = exjson_value_get_object(root_value);
1093 
1094         nrcaches = exjson_object_get_count(root_object);
1095 
1096         if (1!=nrcaches)
1097         {
1098             NDRX_CACHE_ERROR("CACHE: invalid service [%s] cache: "
1099                     "Failed to parse root element",  svc);
1100             EXFAIL_OUT(ret);
1101         }
1102         
1103         name = (char *)exjson_object_get_name(root_object, 0);
1104         
1105         if (0!=strcmp(NDRX_CACHES_BLOCK, name))
1106         {
1107             NDRX_CACHE_ERROR("CACHE: Expected [%s] got [%s]",
1108                     NDRX_CACHES_BLOCK, name);
1109             EXFAIL_OUT(ret);
1110         }
1111 
1112         /* getting array from root value */
1113         array = exjson_object_get_array(root_object, NDRX_CACHES_BLOCK);
1114         cnt = exjson_array_get_count(array);
1115 
1116 #ifdef NDRX_TPCACHE_DEBUG
1117         NDRX_LOG(log_debug, "Got array values %d", cnt);
1118 #endif
1119         
1120         NDRX_CALLOC_OUT(cachesvc, 1, sizeof(ndrx_tpcache_svc_t), ndrx_tpcache_svc_t);
1121         
1122         NDRX_STRCPY_SAFE(cachesvc->svcnm, svc);
1123 
1124         for (i = 0; i < cnt; i++)
1125         {
1126             
1127             NDRX_CALLOC_OUT(cache, 1, sizeof(ndrx_tpcallcache_t), ndrx_tpcallcache_t);
1128             array_object = exjson_array_get_object(array, i);
1129                 
1130             cache->idx = i;
1131             NDRX_STRCPY_SAFE(cache->svcnm, cachesvc->svcnm);
1132             /* process flags.. by strtok.. but we need a temp buffer
1133              * Process flags first as some logic depends on them!
1134              */
1135             if (NULL!=(tmp = exjson_object_get_string(array_object, 
1136                     NDRX_TPCACHE_KWC_FLAGS)))
1137             {
1138                 NDRX_STRCPY_SAFE(flagstr, tmp);
1139                 
1140                 /* clean up the string */
1141                 ndrx_str_strip(flagstr, " \t");
1142                 NDRX_STRCPY_SAFE(cache->flagsstr, flagstr);
1143                 
1144 #ifdef NDRX_TPCACHE_DEBUG
1145                 NDRX_LOG(log_debug, "Processing flags: [%s]", flagstr);
1146 #endif                
1147                         
1148                 p_flags = strtok_r (flagstr, ",", &saveptr1);
1149                 while (p_flags != NULL)
1150                 {
1151                     /*
1152                      * New flags: delrex, delfull - used for delete buffer prepration
1153                      * if not set, defaults to delfull
1154                      */
1155                     if (0==strcmp(p_flags, NDRX_TPCACHE_KWC_DELREG))
1156                     {
1157                         cache->flags|=NDRX_TPCACHE_TPCF_DELREG;
1158                     }
1159                     else if (0==strcmp(p_flags, NDRX_TPCACHE_KWC_DELFULL))
1160                     {
1161                         cache->flags|=NDRX_TPCACHE_TPCF_DELFULL;
1162                     }
1163                     else if (0==strcmp(p_flags, NDRX_TPCACHE_KWC_SAVEREG))
1164                     {
1165                         cache->flags|=NDRX_TPCACHE_TPCF_SAVEREG;
1166                     }
1167                     else if (0==strcmp(p_flags, NDRX_TPCACHE_KWC_REPL)) /* default */
1168                     {
1169                         cache->flags|=NDRX_TPCACHE_TPCF_REPL;
1170                     }
1171                     else if (0==strcmp(p_flags, NDRX_TPCACHE_KWC_MERGE))
1172                     {
1173                         cache->flags|=NDRX_TPCACHE_TPCF_MERGE;
1174                     }
1175                     else if (0==strcmp(p_flags, NDRX_TPCACHE_KWC_NOSVCOK))
1176                     {
1177                         cache->flags|=NDRX_TPCACHE_TPCF_NOSVCOK;
1178                     }
1179                     else if (0==strcmp(p_flags, NDRX_TPCACHE_KWC_SAVEFULL))
1180                     {
1181                         cache->flags|=NDRX_TPCACHE_TPCF_SAVEFULL;
1182                     }
1183                     else if (0==strcmp(p_flags, NDRX_TPCACHE_KWC_INVAL))
1184                     {
1185                         cache->flags|=NDRX_TPCACHE_TPCF_INVAL;
1186                     }
1187                     else if (0==strcmp(p_flags, NDRX_TPCACHE_KWC_NEXT))
1188                     {
1189                         cache->flags|=NDRX_TPCACHE_TPCF_NEXT;
1190                     }
1191                     else if (0==strcmp(p_flags, NDRX_TPCACHE_KWC_INVLKEYGRP))
1192                     {
1193                         cache->flags|=NDRX_TPCACHE_TPCF_INVLKEYGRP;
1194                     }
1195                     else
1196                     {
1197                         NDRX_LOG(log_warn, "For service [%s] buffer index %d, "
1198                                 "invalid flag: [%s] - ignore", svc, i, p_flags);
1199                         userlog("For service [%s] buffer index %d, "
1200                                 "invalid flag: [%s] - ignore", svc, i, p_flags);
1201                     }
1202                     
1203                     p_flags = strtok_r (NULL, ",", &saveptr1);
1204                 }
1205             }
1206             
1207             if ((cache->flags & NDRX_TPCACHE_TPCF_REPL) && 
1208                     (cache->flags & NDRX_TPCACHE_TPCF_MERGE) 
1209                     )
1210             {
1211                 NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: invalid config - conflicting "
1212                         "flags `%s' and `%s' "
1213                         "for service [%s], buffer index: %d", 
1214                         NDRX_TPCACHE_KWC_REPL, NDRX_TPCACHE_KWC_MERGE, svc, i);
1215                 EXFAIL_OUT(ret);
1216             }
1217             
1218             /* default to replace */
1219             if ( !(cache->flags & NDRX_TPCACHE_TPCF_REPL) && 
1220                     !(cache->flags & NDRX_TPCACHE_TPCF_MERGE) 
1221                     )
1222             {
1223                 cache->flags |= NDRX_TPCACHE_TPCF_REPL;
1224             }
1225             
1226             /* set some defaults if not already set... */
1227             if ((cache->flags & NDRX_TPCACHE_TPCF_SAVEREG) && 
1228                     (cache->flags & NDRX_TPCACHE_TPCF_SAVEFULL))
1229             {
1230                 NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: invalid config - conflicting "
1231                         "flags `%s' and `%s' "
1232                         "for service [%s], buffer index: %d", 
1233                         NDRX_TPCACHE_KWC_SAVEREG, NDRX_TPCACHE_KWC_SAVEFULL,
1234                         svc, i);
1235                 EXFAIL_OUT(ret);
1236             }
1237             
1238             if ((cache->flags & NDRX_TPCACHE_TPCF_NEXT) && 
1239                     !(cache->flags & NDRX_TPCACHE_TPCF_INVAL))
1240             {
1241                 NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: invalid config - conflicting "
1242                         "flags `%s' can be used only with `%s' "
1243                         "for service [%s], buffer index: %d", 
1244                         NDRX_TPCACHE_KWC_NEXT, NDRX_TPCACHE_KWC_INVAL, svc, i);
1245                 EXFAIL_OUT(ret);
1246             }
1247             
1248             /* get buffer type */
1249             if (NULL==(tmp = exjson_object_get_string(array_object, 
1250                     NDRX_TPCACHE_KWC_TYPE)))
1251             {
1252                 NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: invalid config missing "
1253                         "[type] for service [%s], buffer index: %d", svc, i);
1254                 EXFAIL_OUT(ret);
1255             }
1256             
1257             NDRX_STRCPY_SAFE(cache->str_buf_type, tmp);
1258             
1259              if (NULL!=(tmp = exjson_object_get_string(array_object, 
1260                      NDRX_TPCACHE_KWC_SUBTYPE)))
1261             {
1262                 NDRX_STRCPY_SAFE(cache->str_buf_subtype, tmp);
1263             }
1264             
1265             /* Resolve buffer */
1266             if (NULL==(cache->buf_type = ndrx_get_buffer_descr(cache->str_buf_type, 
1267                     cache->str_buf_subtype)))
1268             {
1269                 NDRX_CACHE_TPERROR(TPEOTYPE, "CACHE: invalid buffer type "
1270                         "for service [%s], buffer index: %d - Unknown type "
1271                         "[%s]/subtype[%s]", svc, i, cache->str_buf_type, 
1272                         cache->str_buf_subtype);
1273                 EXFAIL_OUT(ret);
1274             }
1275             /* get db name */
1276 
1277             if (NULL==(tmp = exjson_object_get_string(array_object, 
1278                     NDRX_TPCACHE_KWC_CACHEDB)))
1279             {
1280                 NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: invalid config missing "
1281                         "[%s] for service [%s], buffer index: %d", 
1282                         NDRX_TPCACHE_KWC_CACHEDB, svc, i);
1283                 EXFAIL_OUT(ret);
1284             }
1285 
1286             NDRX_STRCPY_SAFE(cache->cachedbnm, tmp);
1287 
1288             /* split up */
1289 
1290             /* Resolve the DB */
1291             if (NULL==(cache->cachedb=ndrx_cache_dbresolve(cache->cachedbnm, mode)))
1292             {
1293                 NDRX_LOG(log_error, "%s failed", __func__);
1294                 EXFAIL_OUT(ret);
1295             }
1296                 
1297             if (cache->flags & NDRX_TPCACHE_TPCF_INVAL)
1298             {
1299                 ndrx_tpcache_svc_t *svcc;
1300                 /* 
1301                  * So we are NDRX_TPCACHE_TPCF_INVAL resolve the other cache..
1302                  * And lookup other keys too of inval cache
1303                  */
1304                 
1305                 /* Get data for invalidating their cache */
1306                 
1307                 if (NULL==(tmp = exjson_object_get_string(array_object, 
1308                         NDRX_TPCACHE_KWC_INVAL_SVC)))
1309                 {
1310                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: invalid config missing "
1311                             "[%s] for service [%s], buffer index: %d", 
1312                             NDRX_TPCACHE_KWC_INVAL_SVC, svc, i);
1313                     EXFAIL_OUT(ret);
1314                 }
1315 
1316                 NDRX_STRCPY_SAFE(cache->inval_svc, tmp);
1317                 
1318                 
1319                 if (NULL==(tmp = exjson_object_get_string(array_object, 
1320                         NDRX_TPCACHE_KWC_INVAL_IDX)))
1321                 {
1322                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: invalid config missing "
1323                             "[%s] for service [%s], buffer index: %d", 
1324                             NDRX_TPCACHE_KWC_INVAL_IDX, svc, i);
1325                     EXFAIL_OUT(ret);
1326                 }
1327                 
1328                 cache->inval_idx = atoi(tmp);
1329                 
1330                 NDRX_LOG(log_debug, "Searching for %s: [%s]/%d", 
1331                         NDRX_TPCACHE_KWC_INVAL_SVC,
1332                         cache->inval_svc, cache->inval_idx);
1333                 
1334                 /* Find service in cache */
1335                 EXHASH_FIND_STR(ndrx_G_tpcache_svc, cache->inval_svc, svcc);
1336 
1337                 if (NULL==svcc)
1338                 {
1339                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: %s [%s] not found, "
1340                             "or defined later (at this or another config file)",
1341                             NDRX_TPCACHE_KWC_INVAL_SVC, cache->inval_svc);
1342                     EXFAIL_OUT(ret);
1343                 }
1344                 
1345                 if (NULL==(cache->inval_cache =  ndrx_cache_findtpcall(svcc, 
1346                     cache->buf_type, NULL, EXFAIL, cache->inval_idx)))
1347                 {
1348                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: tpcall cache not found "
1349                             "for service [%s], index=%d, type=[%s]", 
1350                             cache->inval_svc, cache->inval_idx, 
1351                             cache->buf_type->type);
1352                     EXFAIL_OUT(ret);
1353                 }
1354                 
1355                 NDRX_LOG(log_debug, "Service found: %p", cache->inval_cache);
1356             }
1357             
1358             /* get db if key group is used */
1359             
1360             if (NULL!=(tmp = exjson_object_get_string(array_object, 
1361                     NDRX_TPCACHE_KWC_KEYGRPDB)))
1362             {
1363                 /* Resolve the DB */
1364                 if (NULL==(cache->keygrpdb=ndrx_cache_dbresolve((char *)tmp, mode)))
1365                 {
1366                     NDRX_LOG(log_error, "%s failed", __func__);
1367                     EXFAIL_OUT(ret);
1368                 }
1369             }
1370             
1371             /* validate the type */
1372             if (NULL==ndrx_G_tpcache_types[cache->buf_type->type_id].pf_get_key)
1373             {
1374                 NDRX_CACHE_TPERROR(TPEOTYPE, "CACHE: buffer type not supported "
1375                         "for service [%s], buffer index: %d - Unknown type "
1376                         "[%s]/subtype[%s]", svc, i, cache->str_buf_type, 
1377                         cache->str_buf_subtype);
1378                 EXFAIL_OUT(ret);
1379             }
1380             
1381             /* get key format */
1382             if (NULL==(tmp = exjson_object_get_string(array_object, 
1383                     NDRX_TPCACHE_KWC_KEYFMT)))
1384             {
1385                 NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: invalid config missing "
1386                         "[%s] for service [%s], buffer index: %d", 
1387                         NDRX_TPCACHE_KWC_KEYFMT, svc, i);
1388                 EXFAIL_OUT(ret);
1389             }
1390             
1391             NDRX_STRCPY_SAFE(cache->keyfmt, tmp);
1392             
1393             
1394             if (NULL!=(tmp = exjson_object_get_string(array_object, 
1395                     NDRX_TPCACHE_KWC_KEYGRPFMT)))
1396             {
1397                 NDRX_STRCPY_SAFE(cache->keygrpfmt, tmp);
1398             }
1399             
1400             if (NULL!=(tmp = exjson_object_get_string(array_object, 
1401                                         NDRX_TPCACHE_KWC_KEYGRPMAXTPERRNO)))
1402             {
1403                 cache->keygroupmtperrno = atoi(tmp);
1404                 
1405                 if (cache->keygroupmtperrno<TPMINVAL || 
1406                         cache->keygroupmtperrno > TPMAXVAL)
1407                 {
1408                     NDRX_LOG(log_error, "Invalid max keygroup reject tperrno code [%s] %d "
1409                             "(min: %d, max: %d)", NDRX_TPCACHE_KWC_KEYGRPMAXTPERRNO, 
1410                             cache->keygroupmtperrno, TPMINVAL, TPMAXVAL);
1411                     EXFAIL_OUT(ret);
1412                 }
1413             }
1414             
1415             if (NULL!=(tmp = exjson_object_get_string(array_object, 
1416                                         NDRX_TPCACHE_KWC_KEYGRPMAXTPURCODE)))
1417             {
1418                  cache->keygroupmtpurcode = atol(tmp);
1419             }
1420             
1421             /* Rule to be true to save to cache */
1422             if (NULL!=(tmp = exjson_object_get_string(array_object, 
1423                     NDRX_TPCACHE_KWC_RULE)))
1424             {
1425                 NDRX_STRCPY_SAFE(cache->rule, tmp);
1426             }
1427             else
1428             {
1429                 NDRX_LOG(log_info, "[%s] is missing - assume cache always",
1430                         NDRX_TPCACHE_KWC_RULE);
1431             }
1432             
1433             if (NULL!=(tmp = exjson_object_get_string(array_object, 
1434                     NDRX_TPCACHE_KWC_REFRESHRULE)))
1435             {
1436                 NDRX_STRCPY_SAFE(cache->refreshrule, tmp);
1437             }
1438             
1439             /* get fields to save */
1440             
1441             if (!(cache->flags & NDRX_TPCACHE_TPCF_SAVEFULL) &&
1442                     !(cache->flags & NDRX_TPCACHE_TPCF_INVAL)
1443                     )
1444             {
1445                 if (NULL!=(tmp = exjson_object_get_string(array_object, 
1446                         NDRX_TPCACHE_KWC_SAVE)))
1447                 {
1448 
1449                     NDRX_STRCPY_SAFE(cache->saveproj.expression, tmp);
1450 
1451                     /* if it is regex, then compile */
1452                     if (cache->flags & NDRX_TPCACHE_TPCF_SAVEREG)
1453                     {
1454                         if (EXSUCCEED!=ndrx_regcomp(&cache->saveproj.regex, 
1455                                 cache->saveproj.expression))
1456                         {
1457                             NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: failed to compile [%s] "
1458                                 "regex [%s] for svc [%s], "
1459                                 "buffer index: %d - see ndrx logs", 
1460                                     NDRX_TPCACHE_KWC_SAVE, 
1461                                     cache->saveproj.expression, svc, i);
1462                         }
1463                         cache->saveproj.regex_compiled=EXTRUE;
1464                     }
1465                 }
1466                 else
1467                 {
1468                     NDRX_LOG(log_info, "No save strategy specified - default "
1469                             "to full save");
1470                     cache->flags |= NDRX_TPCACHE_TPCF_SAVEFULL;
1471                 }
1472             }
1473             
1474             /* process delete fields 
1475              * RFU: Currently not used. Full buffer is used for broadcasting
1476              * invalidate data.
1477              */
1478             if (!(cache->flags & NDRX_TPCACHE_TPCF_DELFULL))
1479             {
1480                 if (NULL!=(tmp = exjson_object_get_string(array_object, 
1481                         NDRX_TPCACHE_KWC_DELETE)))
1482                 {                    
1483                     NDRX_STRCPY_SAFE(cache->delproj.expression, tmp);
1484 
1485                     /* if it is regex, then compile */
1486                     if (cache->flags & NDRX_TPCACHE_TPCF_DELREG)
1487                     {
1488                         if (EXSUCCEED!=ndrx_regcomp(&cache->delproj.regex, 
1489                                 cache->delproj.expression))
1490                         {
1491                             NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: failed to "
1492                                 "compile [%s] regex [%s] for svc [%s], "
1493                                 "buffer index: %d - see ndrx logs", 
1494                                         NDRX_TPCACHE_KWC_DELETE, 
1495                                     cache->delproj.expression, svc, i);
1496                         }
1497                         cache->delproj.regex_compiled=EXTRUE;
1498                     }
1499                 }
1500             }
1501             /* Process the reject expression by flags func */
1502             
1503             if (NULL!=(tmp = exjson_object_get_string(array_object, 
1504                     NDRX_TPCACHE_KWC_KEYGROUPMREJ)))
1505             {
1506                 cache->keygroupmrej = NDRX_STRDUP(tmp);
1507                 
1508                 if (NULL==cache->keygroupmrej)
1509                 {
1510                     int err = errno;
1511                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: failed to allocate [%s] buf"
1512                             "for svc [%s] buffer index: %d", 
1513                                 NDRX_TPCACHE_KWC_KEYGROUPMREJ,
1514                             svc, i, strerror(err));
1515                     EXFAIL_OUT(ret);
1516                 }
1517             }
1518             
1519             /* set keygroup limit - keygroupmax */
1520             if (NULL!=(tmp = exjson_object_get_string(array_object, 
1521                     NDRX_TPCACHE_KWC_KEYGROUPMAX)))
1522             {
1523                 cache->keygroupmax = atol(tmp);
1524             }
1525             
1526             if (NULL!=ndrx_G_tpcache_types[cache->buf_type->type_id].pf_process_flags)
1527             {
1528                 if (EXSUCCEED!=ndrx_G_tpcache_types[cache->buf_type->type_id].pf_process_flags(cache, 
1529                         errdet, sizeof(errdet)))
1530                     
1531                 {
1532                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: failed to check flags [%s] "
1533                             "for service [%s], buffer index: %d: %s", 
1534                             cache->flagsstr, svc, i, errdet);
1535                     EXFAIL_OUT(ret);
1536                 }
1537             }
1538             
1539             /* Rule to be true to save to cache */
1540             
1541             if (NULL!=ndrx_G_tpcache_types[cache->buf_type->type_id].pf_rule_compile)
1542             {
1543                 /* Compile the boolean expression! */
1544                 if (EXSUCCEED!=ndrx_G_tpcache_types[cache->buf_type->type_id].pf_rule_compile(
1545                         cache, errdet, sizeof(errdet)))
1546                 {
1547                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: failed to compile rule [%s] "
1548                             "for service [%s], buffer index: %d: %s", 
1549                             cache->rule, svc, i, errdet);
1550                     EXFAIL_OUT(ret);
1551                 }
1552             }
1553             
1554             /* Get & Compile response rule */
1555             if (NULL!=(tmp = exjson_object_get_string(array_object, 
1556                     NDRX_TPCACHE_KWC_RSPRULE)))
1557             {
1558                 NDRX_STRCPY_SAFE(cache->rsprule, tmp);
1559                 
1560                 /* Compile the boolean expression! */
1561                 if (NULL==(cache->rsprule_tree=Bboolco (cache->rsprule)))
1562                 {
1563                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: failed to "
1564                             "compile [%s] [%s] "
1565                             "for service [%s], buffer index: %d: %s", 
1566                             NDRX_TPCACHE_KWC_RSPRULE, 
1567                             cache->rsprule, svc, i, Bstrerror(Berror));
1568                     EXFAIL_OUT(ret);
1569                 }
1570             }
1571             
1572             /* Validate key group storage, if used */
1573             
1574             if (EXEOS!=cache->keygrpfmt[0] &&
1575                     !(cache->flags & NDRX_TPCACHE_TPCF_INVAL))
1576             {
1577                 /* this cache is part of key items... */
1578                 cache->flags|=NDRX_TPCACHE_TPCF_KEYITEMS;
1579                 
1580                 /* The database must be defined */
1581                 
1582                 if (NULL==cache->keygrpdb)
1583                 {
1584                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: [%s] must be "
1585                             "defined if [%s] is used!",
1586                             NDRX_TPCACHE_KWC_KEYGRPDB, NDRX_TPCACHE_KWC_KEYGRPFMT);
1587                     EXFAIL_OUT(ret);
1588                 }
1589                 
1590                 /* database must be with keygrp flag */
1591                 
1592                 if (!(cache->keygrpdb->flags & NDRX_TPCACHE_FLAGS_KEYGRP))
1593                 {
1594                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: [%s] flag must be "
1595                             "defined for [%s] database to use it for keyitems for"
1596                             " service [%s] buffer index %d",
1597                             NDRX_TPCACHE_KWC_KEYGRPDB, 
1598                             cache->keygrpdb->cachedb, svc, i);
1599                     EXFAIL_OUT(ret);
1600                 }
1601                 
1602                 /* and linked database must be with keyitems flag */
1603                 
1604                 if (!(cache->cachedb->flags & NDRX_TPCACHE_FLAGS_KEYITEMS))
1605                 {
1606                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: [%s] flag must be "
1607                             "defined for [%s] database to use it for keyitems for",
1608                             " service [%s] buffer index %d",
1609                             NDRX_TPCACHE_KWD_KEYITEMS, 
1610                             cache->cachedb->cachedb, svc, i);
1611                     EXFAIL_OUT(ret);
1612                 }
1613                 
1614                 if (NULL!=cache->keygroupmrej && cache->keygroupmax<=0)
1615                 {
1616                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: [%s] defined, but "
1617                             "[%s] is 0 - invalid config for"
1618                             " service [%s] buffer index %d",
1619                             NDRX_TPCACHE_KWC_KEYGROUPMREJ, 
1620                             NDRX_TPCACHE_KWC_KEYGROUPMAX, svc, i);
1621                     EXFAIL_OUT(ret);
1622                 }
1623                 
1624                 if (NULL==cache->keygroupmrej && cache->keygroupmax>0)
1625                 {
1626                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: [%s] defined, but "
1627                             "[%s] not defined - invalid config for"
1628                             " service [%s] buffer index %d",
1629                             NDRX_TPCACHE_KWC_KEYGROUPMAX, 
1630                             NDRX_TPCACHE_KWC_KEYGROUPMREJ, svc, i);
1631                     EXFAIL_OUT(ret);
1632                 }
1633                 
1634                 
1635                 if (0!=strcmp(cache->cachedb->cachedbphy, cache->keygrpdb->cachedbphy))
1636                 {
1637                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: [%s] for when using keygroup, "
1638                             "the [%s] and [%s] must come from same physical db name! "
1639                             "(<name>@<physicalname>) ([%s] != [%s])",
1640                             NDRX_TPCACHE_KWC_CACHEDB, NDRX_TPCACHE_KWC_KEYGRPDB,
1641                             cache->cachedb->cachedbphy, cache->keygrpdb->cachedbphy);
1642                     EXFAIL_OUT(ret);
1643                 }
1644                 
1645                 if (0!=strcmp(cache->cachedb->resource, cache->keygrpdb->resource))
1646                 {
1647                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: [%s] for when using keygroup, "
1648                             "the [%s] and [%s] must have from same physical db resource! "
1649                             "(<name>@<physicalname>) ([%s] != [%s])",
1650                             NDRX_TPCACHE_KWC_CACHEDB, NDRX_TPCACHE_KWC_KEYGRPDB,
1651                             cache->cachedb->resource, cache->keygrpdb->resource);
1652                     EXFAIL_OUT(ret);
1653                 }
1654             }
1655             else
1656             {
1657                 if (NULL!=cache->keygroupmrej)
1658                 {
1659                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: [%s] not used as "
1660                             "group format not defined - service [%s] buffer index %d",
1661                             NDRX_TPCACHE_KWC_KEYGROUPMREJ, svc, i);
1662                     EXFAIL_OUT(ret);
1663                 }
1664                 
1665                 if (cache->keygroupmax > 0)
1666                 {
1667                     NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: [%s] defined, but "
1668                             "group format not defined - service [%s] buffer index %d",
1669                             NDRX_TPCACHE_KWC_KEYGROUPMAX, svc, i);
1670                     EXFAIL_OUT(ret);
1671                 }
1672             }
1673             
1674             /* verify us of keygroup invalidate */
1675             if (cache->flags & NDRX_TPCACHE_TPCF_INVLKEYGRP &&
1676                     !(cache->flags & NDRX_TPCACHE_TPCF_INVAL))
1677             {
1678                 NDRX_CACHE_TPERROR(TPEINVAL, "CACHE: [%s] can be only used with "
1679                         "[%s] for svc [%s] idx %d",
1680                         NDRX_TPCACHE_KWC_INVLKEYGRP, NDRX_TPCACHE_KWC_INVAL, 
1681                             svc, i);
1682                     EXFAIL_OUT(ret);
1683             }
1684 
1685             /* Add to linked list */
1686             DL_APPEND(cachesvc->caches, cache);
1687             
1688 #ifdef NDRX_TPCACHE_DEBUG
1689             NDRX_TPCACHETPCALL_DUMPCFG(log_debug, cache);
1690 #endif
1691             
1692             cache = NULL;
1693         }
1694         
1695         EXHASH_ADD_STR(ndrx_G_tpcache_svc, svcnm, cachesvc);
1696         cachesvc = NULL;
1697     }
1698         
1699 out:
1700             
1701     /* cleanup code */
1702     if (NULL!=root_value)
1703     {
1704         exjson_value_free(root_value);
1705     }
1706 
1707     if (NULL!=csection)
1708     {
1709         ndrx_keyval_hash_free(csection);
1710     }
1711 
1712     if (EXSUCCEED!=ret)
1713     {
1714         if (NULL!=cache)
1715         {
1716             ndrx_cache_tpcallcache_free(cache);
1717         }
1718         
1719         if (NULL!=cachesvc)
1720         {
1721             ndrx_cache_svc_free(cachesvc);
1722         }
1723         
1724         /*  Remove all services already initialized... */
1725         ndrx_cache_svcs_free();
1726         
1727         /* Remove all databases */
1728         ndrx_cache_dbs_free();
1729     }
1730 
1731     return ret;
1732 }
1733 /* vim: set ts=4 sw=4 et smartindent: */