/* Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ package config import ( "fmt" "io" "net" "net/http" "os" "path/filepath" "runtime" "strings" "sync" "time" "github.com/cgrates/birpc/context" "github.com/cgrates/cgrates/utils" ) var ( cgrCfg *CGRConfig // will be shared getDftFsConnCfg = func() *FsConnCfg { return new(FsConnCfg) } // returns default FreeSWITCH Connection configuration, built out of json default configuration getDftKamConnCfg = func() *KamConnCfg { return new(KamConnCfg) } // returns default Kamailio Connection configuration getDftAstConnCfg = func() *AsteriskConnCfg { return new(AsteriskConnCfg) } // returns default Asterisk Connection configuration getDftLoaderCfg = func() *LoaderSCfg { return &LoaderSCfg{Opts: new(LoaderSOptsCfg), Cache: make(map[string]*CacheParamCfg)} } getDftRemHstCfg = func() *RemoteHost { return new(RemoteHost) } getDftEvExpCfg = func() *EventExporterCfg { return &EventExporterCfg{Opts: &EventExporterOpts{}} } getDftEvRdrCfg = func() *EventReaderCfg { return &EventReaderCfg{Opts: &EventReaderOpts{}} } ) func init() { cgrCfg = NewDefaultCGRConfig() // populate default ERs reader for _, rdr := range cgrCfg.ersCfg.Readers { if rdr.ID == utils.MetaDefault { getDftEvRdrCfg = rdr.Clone break } } // populate default EEs exporter for _, exp := range cgrCfg.eesCfg.Exporters { if exp.ID == utils.MetaDefault { getDftEvExpCfg = exp.Clone break } } getDftFsConnCfg = cgrCfg.fsAgentCfg.EventSocketConns[0].Clone // We leave it crashing here on purpose if no Connection defaults defined getDftKamConnCfg = cgrCfg.kamAgentCfg.EvapiConns[0].Clone getDftAstConnCfg = cgrCfg.asteriskAgentCfg.AsteriskConns[0].Clone getDftLoaderCfg = cgrCfg.loaderCfg[0].Clone getDftRemHstCfg = cgrCfg.rpcConns[utils.MetaLocalHost].Conns[0].Clone } // CgrConfig is used to retrieve system configuration from other packages func CgrConfig() *CGRConfig { return cgrCfg } // SetCgrConfig is used to set system configuration from other places func SetCgrConfig(cfg *CGRConfig) { cgrCfg = cfg } // NewDefaultCGRConfig returns the default configuration func NewDefaultCGRConfig() (cfg *CGRConfig) { cfg, _ = newCGRConfig([]byte(CGRATES_CFG_JSON)) return } func newCGRConfig(config []byte) (cfg *CGRConfig, err error) { cfg = &CGRConfig{ DataFolderPath: "/usr/share/cgrates/", rpcConns: make(RPCConns), templates: make(FCTemplates), generalCfg: &GeneralCfg{ NodeID: utils.UUIDSha1Prefix(), Opts: &GeneralOpts{ ExporterIDs: []*utils.DynamicStringSliceOpt{}, }, }, loggerCfg: &LoggerCfg{ Opts: new(LoggerOptsCfg), }, dataDbCfg: &DataDbCfg{ Items: make(map[string]*ItemOpts), Opts: &DataDBOpts{}, }, tlsCfg: new(TLSCfg), cacheCfg: &CacheCfg{Partitions: make(map[string]*CacheParamCfg)}, listenCfg: new(ListenCfg), httpCfg: &HTTPCfg{ ClientOpts: &http.Transport{}, dialer: &net.Dialer{}, }, filterSCfg: new(FilterSCfg), cdrsCfg: &CdrsCfg{Opts: &CdrsOpts{ Accounts: []*utils.DynamicBoolOpt{}, Attributes: []*utils.DynamicBoolOpt{}, Chargers: []*utils.DynamicBoolOpt{}, Export: []*utils.DynamicBoolOpt{}, Rates: []*utils.DynamicBoolOpt{}, Stats: []*utils.DynamicBoolOpt{}, Thresholds: []*utils.DynamicBoolOpt{}, }}, analyzerSCfg: &AnalyzerSCfg{ Opts: &AnalyzerSOpts{ ExporterIDs: []*utils.DynamicStringSliceOpt{}, }, }, sessionSCfg: &SessionSCfg{ STIRCfg: new(STIRcfg), DefaultUsage: make(map[string]time.Duration), Opts: &SessionsOpts{ Accounts: []*utils.DynamicBoolOpt{}, Attributes: []*utils.DynamicBoolOpt{}, CDRs: []*utils.DynamicBoolOpt{}, Chargers: []*utils.DynamicBoolOpt{}, Resources: []*utils.DynamicBoolOpt{}, Routes: []*utils.DynamicBoolOpt{}, Stats: []*utils.DynamicBoolOpt{}, Thresholds: []*utils.DynamicBoolOpt{}, Initiate: []*utils.DynamicBoolOpt{}, Update: []*utils.DynamicBoolOpt{}, Terminate: []*utils.DynamicBoolOpt{}, Message: []*utils.DynamicBoolOpt{}, AttributesDerivedReply: []*utils.DynamicBoolOpt{}, BlockerError: []*utils.DynamicBoolOpt{}, CDRsDerivedReply: []*utils.DynamicBoolOpt{}, ResourcesAuthorize: []*utils.DynamicBoolOpt{}, ResourcesAllocate: []*utils.DynamicBoolOpt{}, ResourcesRelease: []*utils.DynamicBoolOpt{}, ResourcesDerivedReply: []*utils.DynamicBoolOpt{}, RoutesDerivedReply: []*utils.DynamicBoolOpt{}, StatsDerivedReply: []*utils.DynamicBoolOpt{}, ThresholdsDerivedReply: []*utils.DynamicBoolOpt{}, MaxUsage: []*utils.DynamicBoolOpt{}, ForceDuration: []*utils.DynamicBoolOpt{}, TTL: []*utils.DynamicDurationOpt{}, Chargeable: []*utils.DynamicBoolOpt{}, TTLLastUsage: []*utils.DynamicDurationPointerOpt{}, TTLLastUsed: []*utils.DynamicDurationPointerOpt{}, DebitInterval: []*utils.DynamicDurationOpt{}, TTLMaxDelay: []*utils.DynamicDurationOpt{}, TTLUsage: []*utils.DynamicDurationPointerOpt{}, }, }, fsAgentCfg: new(FsAgentCfg), kamAgentCfg: new(KamAgentCfg), asteriskAgentCfg: new(AsteriskAgentCfg), diameterAgentCfg: new(DiameterAgentCfg), radiusAgentCfg: &RadiusAgentCfg{ ClientDictionaries: make(map[string]string), ClientSecrets: make(map[string]string), }, dnsAgentCfg: new(DNSAgentCfg), attributeSCfg: &AttributeSCfg{Opts: &AttributesOpts{ ProfileIDs: []*utils.DynamicStringSliceOpt{}, ProcessRuns: []*utils.DynamicIntOpt{}, ProfileRuns: []*utils.DynamicIntOpt{}, ProfileIgnoreFilters: []*utils.DynamicBoolOpt{}, }}, chargerSCfg: new(ChargerSCfg), resourceSCfg: &ResourceSConfig{Opts: &ResourcesOpts{ UsageID: []*utils.DynamicStringOpt{}, UsageTTL: []*utils.DynamicDurationOpt{}, Units: []*utils.DynamicFloat64Opt{}, }}, statsCfg: &StatSCfg{Opts: &StatsOpts{ ProfileIDs: []*utils.DynamicStringSliceOpt{}, ProfileIgnoreFilters: []*utils.DynamicBoolOpt{}, RoundingDecimals: []*utils.DynamicIntOpt{}, PrometheusStatIDs: []*utils.DynamicStringSliceOpt{}, }}, thresholdSCfg: &ThresholdSCfg{Opts: &ThresholdsOpts{ ProfileIDs: []*utils.DynamicStringSliceOpt{}, ProfileIgnoreFilters: []*utils.DynamicBoolOpt{}, }}, routeSCfg: &RouteSCfg{Opts: &RoutesOpts{ Context: []*utils.DynamicStringOpt{}, IgnoreErrors: []*utils.DynamicBoolOpt{}, MaxCost: []*utils.DynamicInterfaceOpt{}, ProfileCount: []*utils.DynamicIntPointerOpt{}, Limit: []*utils.DynamicIntPointerOpt{}, Offset: []*utils.DynamicIntPointerOpt{}, MaxItems: []*utils.DynamicIntPointerOpt{}, Usage: []*utils.DynamicDecimalBigOpt{}, }}, tpeSCfg: new(TpeSCfg), sureTaxCfg: new(SureTaxCfg), dispatcherSCfg: &DispatcherSCfg{Opts: &DispatchersOpts{ Dispatchers: []*utils.DynamicBoolOpt{}, }}, registrarCCfg: &RegistrarCCfgs{ RPC: &RegistrarCCfg{Hosts: make(map[string][]*RemoteHost)}, Dispatchers: &RegistrarCCfg{Hosts: make(map[string][]*RemoteHost)}, }, loaderCgrCfg: new(LoaderCgrCfg), migratorCgrCfg: &MigratorCgrCfg{ OutDataDBOpts: &DataDBOpts{}, }, loaderCfg: make(LoaderSCfgs, 0), httpAgentCfg: make(HTTPAgentCfgs, 0), admS: new(AdminSCfg), ersCfg: new(ERsCfg), eesCfg: &EEsCfg{Cache: make(map[string]*CacheParamCfg)}, rateSCfg: &RateSCfg{Opts: &RatesOpts{ ProfileIDs: []*utils.DynamicStringSliceOpt{}, StartTime: []*utils.DynamicStringOpt{}, Usage: []*utils.DynamicDecimalBigOpt{}, IntervalStart: []*utils.DynamicDecimalBigOpt{}, ProfileIgnoreFilters: []*utils.DynamicBoolOpt{}, }}, efsCfg: new(EFsCfg), actionSCfg: &ActionSCfg{Opts: &ActionsOpts{ ProfileIDs: []*utils.DynamicStringSliceOpt{}, ProfileIgnoreFilters: []*utils.DynamicBoolOpt{}, PosterAttempts: []*utils.DynamicIntOpt{}, }}, sipAgentCfg: new(SIPAgentCfg), configSCfg: new(ConfigSCfg), apiBanCfg: new(APIBanCfg), coreSCfg: new(CoreSCfg), accountSCfg: &AccountSCfg{Opts: &AccountsOpts{ ProfileIDs: []*utils.DynamicStringSliceOpt{}, Usage: []*utils.DynamicDecimalBigOpt{}, ProfileIgnoreFilters: []*utils.DynamicBoolOpt{}, }}, configDBCfg: &ConfigDBCfg{ Opts: &DataDBOpts{}, }, rldCh: make(chan string, 1), cacheDP: make(utils.MapStorage), } cfg.sections = newSections(cfg) cfg.initChanels() var cgrJSONCfg *CgrJsonCfg if cgrJSONCfg, err = NewCgrJsonCfgFromBytes(config); err != nil { return } if err = cfg.sections.Load(context.Background(), cgrJSONCfg, cfg); err != nil { return } err = cfg.checkConfigSanity() return } // NewCGRConfigFromJSONStringWithDefaults returns the given config with the default option loaded func NewCGRConfigFromJSONStringWithDefaults(cfgJSONStr string) (cfg *CGRConfig, err error) { cfg = NewDefaultCGRConfig() jsnCfg := new(CgrJsonCfg) if err = NewRjReaderFromBytes([]byte(cfgJSONStr)).Decode(jsnCfg); err != nil { return } err = cfg.sections.Load(context.Background(), jsnCfg, cfg) return } // NewCGRConfigFromPath reads all json files out of a folder/subfolders and loads them up in lexical order func NewCGRConfigFromPath(ctx *context.Context, path string) (cfg *CGRConfig, err error) { cfg = NewDefaultCGRConfig() cfg.ConfigPath = path if err = loadConfigFromPath(ctx, path, cfg.sections, false, cfg); err != nil { return } err = cfg.checkConfigSanity() return } // newCGRConfigFromPathWithoutEnv reads all json files out of a folder/subfolders and loads them up in lexical order // it will not read *env variables and will not checkConfigSanity as it is not needed for configs func newCGRConfigFromPathWithoutEnv(ctx *context.Context, path string) (cfg *CGRConfig, err error) { cfg = NewDefaultCGRConfig() cfg.ConfigPath = path err = loadConfigFromPath(ctx, path, cfg.sections, true, cfg) return } func isHidden(fileName string) bool { if fileName == "." || fileName == ".." { return false } return strings.HasPrefix(fileName, ".") } // CGRConfig holds system configuration, defaults are overwritten with values from config file if found type CGRConfig struct { sections Sections rldCh chan string // index here the channels used for reloads lks map[string]*sync.RWMutex db ConfigDB // to store the last dbConn that executed an config update DataFolderPath string // Path towards data folder, for tests internal usage, not loading out of .json options ConfigPath string // Path towards config loaderCfg LoaderSCfgs // LoaderS configs httpAgentCfg HTTPAgentCfgs // HttpAgent configs rpcConns RPCConns templates FCTemplates generalCfg *GeneralCfg // General config loggerCfg *LoggerCfg // Logger config dataDbCfg *DataDbCfg // Database config tlsCfg *TLSCfg // TLS config cacheCfg *CacheCfg // Cache config listenCfg *ListenCfg // Listen config httpCfg *HTTPCfg // HTTP config filterSCfg *FilterSCfg // FilterS config cdrsCfg *CdrsCfg // Cdrs config sessionSCfg *SessionSCfg // SessionS config fsAgentCfg *FsAgentCfg // FreeSWITCHAgent config kamAgentCfg *KamAgentCfg // KamailioAgent config asteriskAgentCfg *AsteriskAgentCfg // AsteriskAgent config diameterAgentCfg *DiameterAgentCfg // DiameterAgent config radiusAgentCfg *RadiusAgentCfg // RadiusAgent config dnsAgentCfg *DNSAgentCfg // DNSAgent config attributeSCfg *AttributeSCfg // AttributeS config chargerSCfg *ChargerSCfg // ChargerS config resourceSCfg *ResourceSConfig // ResourceS config statsCfg *StatSCfg // StatS config thresholdSCfg *ThresholdSCfg // ThresholdS config routeSCfg *RouteSCfg // RouteS config sureTaxCfg *SureTaxCfg // SureTax config dispatcherSCfg *DispatcherSCfg // DispatcherS config registrarCCfg *RegistrarCCfgs // RegistrarC config loaderCgrCfg *LoaderCgrCfg // LoaderCgr config migratorCgrCfg *MigratorCgrCfg // MigratorCgr config analyzerSCfg *AnalyzerSCfg // AnalyzerS config admS *AdminSCfg // APIer config ersCfg *ERsCfg // EventReader config eesCfg *EEsCfg // EventExporter config efsCfg *EFsCfg // EventFailover config rateSCfg *RateSCfg // RateS config actionSCfg *ActionSCfg // ActionS config sipAgentCfg *SIPAgentCfg // SIPAgent config configSCfg *ConfigSCfg // ConfigS config apiBanCfg *APIBanCfg // APIBan config coreSCfg *CoreSCfg // CoreS config accountSCfg *AccountSCfg // AccountS config tpeSCfg *TpeSCfg // TpeS config configDBCfg *ConfigDBCfg // ConfigDB conifg cacheDP utils.MapStorage cacheDPMux sync.RWMutex } var posibleLoaderTypes = utils.NewStringSet([]string{utils.MetaAttributes, utils.MetaResources, utils.MetaFilters, utils.MetaStats, utils.MetaRoutes, utils.MetaThresholds, utils.MetaChargers, utils.MetaDispatchers, utils.MetaDispatcherHosts, utils.MetaRateProfiles, utils.MetaAccounts, utils.MetaActionProfiles}) var possibleReaderTypes = utils.NewStringSet([]string{utils.MetaFileCSV, utils.MetaKafkajsonMap, utils.MetaFileXML, utils.MetaSQL, utils.MetaFileFWV, utils.MetaFileJSON, utils.MetaNone, utils.MetaAMQPjsonMap, utils.MetaS3jsonMap, utils.MetaSQSjsonMap, utils.MetaAMQPV1jsonMap, utils.MetaNatsjsonMap}) var possibleExporterTypes = utils.NewStringSet([]string{utils.MetaFileCSV, utils.MetaNone, utils.MetaFileFWV, utils.MetaHTTPPost, utils.MetaHTTPjsonMap, utils.MetaAMQPjsonMap, utils.MetaAMQPV1jsonMap, utils.MetaSQSjsonMap, utils.MetaKafkajsonMap, utils.MetaS3jsonMap, utils.MetaElastic, utils.MetaVirt, utils.MetaSQL, utils.MetaNatsjsonMap, utils.MetaLog, utils.MetaRpc}) func (cfg *CGRConfig) AddSection(sec Section) { cfg.sections = append(cfg.sections, sec) cfg.lks[sec.SName()] = new(sync.RWMutex) } func (cfg *CGRConfig) GetAllSectionIDs() (s []string) { s = make([]string, 0, len(cfg.sections)) for _, f := range cfg.sections { s = append(s, f.SName()) } return } // LazySanityCheck used after check config sanity to display warnings related to the config func (cfg *CGRConfig) LazySanityCheck() { for _, expID := range cfg.cdrsCfg.OnlineCDRExports { for _, ee := range cfg.eesCfg.Exporters { if ee.ID == expID && ee.Type == utils.MetaS3jsonMap || ee.Type == utils.MetaSQSjsonMap { poster := utils.SQSPoster if ee.Type == utils.MetaS3jsonMap { poster = utils.S3Poster } argsMap := utils.GetUrlRawArguments(ee.ExportPath) for _, arg := range []string{utils.AWSRegion, utils.AWSKey, utils.AWSSecret} { if _, has := argsMap[arg]; !has { utils.Logger.Warning(fmt.Sprintf("<%s> No %s present for AWS for exporter with ID : <%s>.", poster, arg, ee.ID)) } } } } } for _, exporter := range cfg.eesCfg.Exporters { if exporter.Type == utils.MetaS3jsonMap || exporter.Type == utils.MetaSQSjsonMap { poster := utils.SQSPoster if exporter.Type == utils.MetaS3jsonMap { poster = utils.S3Poster } argsMap := utils.GetUrlRawArguments(exporter.ExportPath) for _, arg := range []string{utils.AWSRegion, utils.AWSKey, utils.AWSSecret} { if _, has := argsMap[arg]; !has { utils.Logger.Warning(fmt.Sprintf("<%s> No %s present for AWS for exporter with ID: <%s>.", poster, arg, exporter.ID)) } } } } } // loadConfigDBCfg loads the ConfigDB section of the configuration func (cfg *CGRConfig) loadConfigDBCfg(ctx *context.Context, jsnCfg ConfigDB) (err error) { jsnDBCfg := new(DbJsonCfg) if err = jsnCfg.GetSection(ctx, ConfigDBJSON, jsnDBCfg); err != nil { return } return cfg.configDBCfg.loadFromJSONCfg(jsnDBCfg) } // SureTaxCfg use locking to retrieve the configuration, possibility later for runtime reload func (cfg *CGRConfig) SureTaxCfg() *SureTaxCfg { cfg.lks[SureTaxJSON].Lock() defer cfg.lks[SureTaxJSON].Unlock() return cfg.sureTaxCfg } // DiameterAgentCfg returns the config for Diameter Agent func (cfg *CGRConfig) DiameterAgentCfg() *DiameterAgentCfg { cfg.lks[DiameterAgentJSON].Lock() defer cfg.lks[DiameterAgentJSON].Unlock() return cfg.diameterAgentCfg } // RadiusAgentCfg returns the config for Radius Agent func (cfg *CGRConfig) RadiusAgentCfg() *RadiusAgentCfg { cfg.lks[RadiusAgentJSON].Lock() defer cfg.lks[RadiusAgentJSON].Unlock() return cfg.radiusAgentCfg } // DNSAgentCfg returns the config for DNS Agent func (cfg *CGRConfig) DNSAgentCfg() *DNSAgentCfg { cfg.lks[DNSAgentJSON].Lock() defer cfg.lks[DNSAgentJSON].Unlock() return cfg.dnsAgentCfg } // AttributeSCfg returns the config for AttributeS func (cfg *CGRConfig) AttributeSCfg() *AttributeSCfg { cfg.lks[AttributeSJSON].Lock() defer cfg.lks[AttributeSJSON].Unlock() return cfg.attributeSCfg } // ChargerSCfg returns the config for ChargerS func (cfg *CGRConfig) ChargerSCfg() *ChargerSCfg { cfg.lks[ChargerSJSON].Lock() defer cfg.lks[ChargerSJSON].Unlock() return cfg.chargerSCfg } // ResourceSCfg returns the config for ResourceS func (cfg *CGRConfig) ResourceSCfg() *ResourceSConfig { // not done cfg.lks[ResourceSJSON].Lock() defer cfg.lks[ResourceSJSON].Unlock() return cfg.resourceSCfg } // StatSCfg returns the config for StatS func (cfg *CGRConfig) StatSCfg() *StatSCfg { // not done cfg.lks[StatSJSON].Lock() defer cfg.lks[StatSJSON].Unlock() return cfg.statsCfg } // ThresholdSCfg returns the config for ThresholdS func (cfg *CGRConfig) ThresholdSCfg() *ThresholdSCfg { cfg.lks[ThresholdSJSON].Lock() defer cfg.lks[ThresholdSJSON].Unlock() return cfg.thresholdSCfg } // RouteSCfg returns the config for RouteS func (cfg *CGRConfig) RouteSCfg() *RouteSCfg { cfg.lks[RouteSJSON].Lock() defer cfg.lks[RouteSJSON].Unlock() return cfg.routeSCfg } // SessionSCfg returns the config for SessionS func (cfg *CGRConfig) SessionSCfg() *SessionSCfg { cfg.lks[SessionSJSON].Lock() defer cfg.lks[SessionSJSON].Unlock() return cfg.sessionSCfg } // FsAgentCfg returns the config for FsAgent func (cfg *CGRConfig) FsAgentCfg() *FsAgentCfg { cfg.lks[FreeSWITCHAgentJSON].Lock() defer cfg.lks[FreeSWITCHAgentJSON].Unlock() return cfg.fsAgentCfg } // KamAgentCfg returns the config for KamAgent func (cfg *CGRConfig) KamAgentCfg() *KamAgentCfg { cfg.lks[KamailioAgentJSON].Lock() defer cfg.lks[KamailioAgentJSON].Unlock() return cfg.kamAgentCfg } // AsteriskAgentCfg returns the config for AsteriskAgent func (cfg *CGRConfig) AsteriskAgentCfg() *AsteriskAgentCfg { cfg.lks[AsteriskAgentJSON].Lock() defer cfg.lks[AsteriskAgentJSON].Unlock() return cfg.asteriskAgentCfg } // HTTPAgentCfg returns the config for HttpAgent func (cfg *CGRConfig) HTTPAgentCfg() HTTPAgentCfgs { cfg.lks[HTTPAgentJSON].Lock() defer cfg.lks[HTTPAgentJSON].Unlock() return cfg.httpAgentCfg } // FilterSCfg returns the config for FilterS func (cfg *CGRConfig) FilterSCfg() *FilterSCfg { cfg.lks[FilterSJSON].Lock() defer cfg.lks[FilterSJSON].Unlock() return cfg.filterSCfg } // CacheCfg returns the config for Cache func (cfg *CGRConfig) CacheCfg() *CacheCfg { cfg.lks[CacheJSON].Lock() defer cfg.lks[CacheJSON].Unlock() return cfg.cacheCfg } // LoaderCfg returns the Loader Service func (cfg *CGRConfig) LoaderCfg() LoaderSCfgs { cfg.lks[LoaderSJSON].Lock() defer cfg.lks[LoaderSJSON].Unlock() return cfg.loaderCfg } // LoaderCgrCfg returns the config for cgr-loader func (cfg *CGRConfig) LoaderCgrCfg() *LoaderCgrCfg { cfg.lks[LoaderJSON].Lock() defer cfg.lks[LoaderJSON].Unlock() return cfg.loaderCgrCfg } // DispatcherSCfg returns the config for DispatcherS func (cfg *CGRConfig) DispatcherSCfg() *DispatcherSCfg { cfg.lks[DispatcherSJSON].Lock() defer cfg.lks[DispatcherSJSON].Unlock() return cfg.dispatcherSCfg } // RegistrarCCfg returns the config for RegistrarC func (cfg *CGRConfig) RegistrarCCfg() *RegistrarCCfgs { cfg.lks[DispatcherSJSON].Lock() defer cfg.lks[DispatcherSJSON].Unlock() return cfg.registrarCCfg } // MigratorCgrCfg returns the config for Migrator func (cfg *CGRConfig) MigratorCgrCfg() *MigratorCgrCfg { cfg.lks[MigratorJSON].Lock() defer cfg.lks[MigratorJSON].Unlock() return cfg.migratorCgrCfg } // DataDbCfg returns the config for DataDb func (cfg *CGRConfig) DataDbCfg() *DataDbCfg { cfg.lks[DataDBJSON].Lock() defer cfg.lks[DataDBJSON].Unlock() return cfg.dataDbCfg } // GeneralCfg returns the General config section func (cfg *CGRConfig) GeneralCfg() *GeneralCfg { cfg.lks[GeneralJSON].Lock() defer cfg.lks[GeneralJSON].Unlock() return cfg.generalCfg } // LoggerCfg returns the General config section func (cfg *CGRConfig) LoggerCfg() *LoggerCfg { cfg.lks[LoggerJSON].Lock() defer cfg.lks[LoggerJSON].Unlock() return cfg.loggerCfg } // TLSCfg returns the config for Tls func (cfg *CGRConfig) TLSCfg() *TLSCfg { cfg.lks[TlsJSON].Lock() defer cfg.lks[TlsJSON].Unlock() return cfg.tlsCfg } // ListenCfg returns the server Listen config func (cfg *CGRConfig) ListenCfg() *ListenCfg { cfg.lks[ListenJSON].Lock() defer cfg.lks[ListenJSON].Unlock() return cfg.listenCfg } // EFsCfg returns the export failover config func (cfg *CGRConfig) EFsCfg() *EFsCfg { cfg.lks[EFsJSON].Lock() defer cfg.lks[EFsJSON].Unlock() return cfg.efsCfg } // HTTPCfg returns the config for HTTP func (cfg *CGRConfig) HTTPCfg() *HTTPCfg { cfg.lks[HTTPJSON].Lock() defer cfg.lks[HTTPJSON].Unlock() return cfg.httpCfg } // CdrsCfg returns the config for CDR Server func (cfg *CGRConfig) CdrsCfg() *CdrsCfg { cfg.lks[CDRsJSON].Lock() defer cfg.lks[CDRsJSON].Unlock() return cfg.cdrsCfg } // AnalyzerSCfg returns the config for AnalyzerS func (cfg *CGRConfig) AnalyzerSCfg() *AnalyzerSCfg { cfg.lks[AnalyzerSJSON].Lock() defer cfg.lks[AnalyzerSJSON].Unlock() return cfg.analyzerSCfg } // AdminSCfg reads the Apier configuration func (cfg *CGRConfig) AdminSCfg() *AdminSCfg { cfg.lks[AdminSJSON].Lock() defer cfg.lks[AdminSJSON].Unlock() return cfg.admS } // ERsCfg reads the EventReader configuration func (cfg *CGRConfig) ERsCfg() *ERsCfg { cfg.lks[ERsJSON].RLock() defer cfg.lks[ERsJSON].RUnlock() return cfg.ersCfg } // EEsCfg reads the EventExporter configuration func (cfg *CGRConfig) EEsCfg() *EEsCfg { cfg.lks[EEsJSON].RLock() defer cfg.lks[EEsJSON].RUnlock() return cfg.eesCfg } // EEsNoLksCfg reads the EventExporter configuration without locks func (cfg *CGRConfig) EEsNoLksCfg() *EEsCfg { return cfg.eesCfg } // RateSCfg reads the RateS configuration func (cfg *CGRConfig) RateSCfg() *RateSCfg { cfg.lks[RateSJSON].RLock() defer cfg.lks[RateSJSON].RUnlock() return cfg.rateSCfg } // ActionSCfg reads the ActionS configuration func (cfg *CGRConfig) ActionSCfg() *ActionSCfg { cfg.lks[ActionSJSON].RLock() defer cfg.lks[ActionSJSON].RUnlock() return cfg.actionSCfg } // AccountSCfg reads the AccountS configuration func (cfg *CGRConfig) AccountSCfg() *AccountSCfg { cfg.lks[AccountSJSON].RLock() defer cfg.lks[AccountSJSON].RUnlock() return cfg.accountSCfg } // TpeSCfg reads the TpeS configuration func (cfg *CGRConfig) TpeSCfg() *TpeSCfg { cfg.Lock(TPeSJSON) defer cfg.Unlock(TPeSJSON) return cfg.tpeSCfg } // SIPAgentCfg reads the Apier configuration func (cfg *CGRConfig) SIPAgentCfg() *SIPAgentCfg { cfg.lks[SIPAgentJSON].Lock() defer cfg.lks[SIPAgentJSON].Unlock() return cfg.sipAgentCfg } // RPCConns reads the RPCConns configuration func (cfg *CGRConfig) RPCConns() RPCConns { cfg.lks[RPCConnsJSON].RLock() defer cfg.lks[RPCConnsJSON].RUnlock() return cfg.rpcConns } // TemplatesCfg returns the config for templates func (cfg *CGRConfig) TemplatesCfg() FCTemplates { cfg.lks[TemplatesJSON].Lock() defer cfg.lks[TemplatesJSON].Unlock() return cfg.templates } // ConfigSCfg returns the configs configuration func (cfg *CGRConfig) ConfigSCfg() *ConfigSCfg { cfg.lks[ConfigSJSON].RLock() defer cfg.lks[ConfigSJSON].RUnlock() return cfg.configSCfg } // APIBanCfg reads the ApiBan configuration func (cfg *CGRConfig) APIBanCfg() *APIBanCfg { cfg.lks[APIBanJSON].Lock() defer cfg.lks[APIBanJSON].Unlock() return cfg.apiBanCfg } // CoreSCfg reads the CoreS configuration func (cfg *CGRConfig) CoreSCfg() *CoreSCfg { cfg.lks[CoreSJSON].Lock() defer cfg.lks[CoreSJSON].Unlock() return cfg.coreSCfg } // ConfigDBCfg reads the CoreS configuration func (cfg *CGRConfig) ConfigDBCfg() *ConfigDBCfg { cfg.lks[ConfigDBJSON].Lock() defer cfg.lks[ConfigDBJSON].Unlock() return cfg.configDBCfg } // GetReloadChan returns the reload chanel for the given section func (cfg *CGRConfig) GetReloadChan() chan string { return cfg.rldCh } func (cfg *CGRConfig) rLockSections() { for _, lk := range cfg.lks { lk.RLock() } } func (cfg *CGRConfig) rUnlockSections() { for _, lk := range cfg.lks { lk.RUnlock() } } func (cfg *CGRConfig) lockSections() { for _, lk := range cfg.lks { lk.Lock() } } func (cfg *CGRConfig) unlockSections() { for _, lk := range cfg.lks { lk.Unlock() } } // RLock will read-lock locks with ID. // User needs to know what he is doing since this can panic func (cfg *CGRConfig) RLock(sID string) { cfg.lks[sID].RLock() } // RUnlock will read-unlock locks with ID. // User needs to know what he is doing since this can panic func (cfg *CGRConfig) RUnlock(sID string) { cfg.lks[sID].RUnlock() } // Lock will lock the given section // User needs to know what he is doing since this can panic func (cfg *CGRConfig) Lock(sID string) { cfg.lks[sID].Lock() } // Unlock will unlock the given section // User needs to know what he is doing since this can panic func (cfg *CGRConfig) Unlock(sID string) { cfg.lks[sID].Unlock() } // RLocks will read-lock locks with IDs. // User needs to know what he is doing since this can panic func (cfg *CGRConfig) RLocks(lkIDs ...string) { for _, lkID := range lkIDs { cfg.RLock(lkID) } } // RUnlocks will read-unlock locks with IDs. // User needs to know what he is doing since this can panic func (cfg *CGRConfig) RUnlocks(lkIDs ...string) { for _, lkID := range lkIDs { cfg.RUnlock(lkID) } } // LockSections will lock the given sections // User needs to know what he is doing since this can panic func (cfg *CGRConfig) LockSections(lkIDs ...string) { for _, lkID := range lkIDs { cfg.Lock(lkID) } } // UnlockSections will unlock the given sections // User needs to know what he is doing since this can panic func (cfg *CGRConfig) UnlockSections(lkIDs ...string) { for _, lkID := range lkIDs { cfg.Unlock(lkID) } } func (cfg *CGRConfig) loadCfgWithLocks(ctx *context.Context, path, section string) (err error) { sections := cfg.sections if section == utils.EmptyString || section == utils.MetaAll { cfg.lockSections() defer cfg.unlockSections() } else if sec, has := sections.Get(section); !has { return fmt.Errorf("Invalid section: <%s> ", section) } else { cfg.lks[section].Lock() defer cfg.lks[section].Unlock() sections = Sections{sec} } return loadConfigFromPath(ctx, path, sections, false, cfg) } func loadConfigFromReader(ctx *context.Context, rdr io.Reader, loadFuncs Sections, envOff bool, cfg *CGRConfig) (err error) { jsnCfg := new(CgrJsonCfg) var rjr *RjReader if rjr, err = NewRjReader(rdr); err != nil { return } rjr.envOff = envOff defer rjr.Close() // make sure we make the buffer nil if err = rjr.Decode(jsnCfg); err != nil { return } return loadFuncs.Load(ctx, jsnCfg, cfg) } // Reads all .json files out of a folder/subfolders and loads them up in lexical order func loadConfigFromPath(ctx *context.Context, path string, loadFuncs Sections, envOff bool, cfg *CGRConfig) (err error) { if utils.IsURL(path) { return loadConfigFromHTTP(ctx, path, loadFuncs, cfg) // prefix protocol } var fi os.FileInfo if fi, err = os.Stat(path); err != nil { if os.IsNotExist(err) { return utils.ErrPathNotReachable(path) } return } else if !fi.IsDir() && path != utils.ConfigPath { // If config dir defined, needs to exist, not checking for default return fmt.Errorf("path: %s not a directory", path) } // safe to assume that path is a directory return loadConfigFromFolder(ctx, path, loadFuncs, envOff, cfg) } func loadConfigFromFolder(ctx *context.Context, cfgDir string, loadFuncs Sections, envOff bool, cfg *CGRConfig) (err error) { jsonFilesFound := false if err = filepath.Walk(cfgDir, func(path string, info os.FileInfo, err error) (werr error) { if !info.IsDir() || isHidden(info.Name()) { // also ignore hidden files and folders return } var cfgFiles []string if cfgFiles, werr = filepath.Glob(filepath.Join(path, "*.json")); werr != nil { return } if cfgFiles == nil { // No need of processing further since there are no config files in the folder return } if !jsonFilesFound { jsonFilesFound = true } for _, jsonFilePath := range cfgFiles { if werr = loadConfigFromFile(ctx, jsonFilePath, loadFuncs, envOff, cfg); werr != nil { return } } return }); err != nil { return } if !jsonFilesFound { return fmt.Errorf("No config file found on path %s ", cfgDir) } return } // loadConfigFromFile loads the config from a file // extracted from a loadConfigFromFolder in order to test all cases func loadConfigFromFile(ctx *context.Context, jsonFilePath string, loadFuncs Sections, envOff bool, cfg *CGRConfig) (err error) { var cfgFile *os.File cfgFile, err = os.Open(jsonFilePath) if err != nil { return } err = loadConfigFromReader(ctx, cfgFile, loadFuncs, envOff, cfg) cfgFile.Close() if err != nil { err = fmt.Errorf("file <%s>:%s", jsonFilePath, err.Error()) } return } func loadConfigFromHTTP(ctx *context.Context, urlPaths string, loadFuncs Sections, cfg *CGRConfig) (err error) { for _, urlPath := range strings.Split(urlPaths, utils.InfieldSep) { var myClient = &http.Client{ Timeout: CgrConfig().GeneralCfg().ReplyTimeout, } var req *http.Request if req, err = http.NewRequestWithContext(ctx, utils.EmptyString, urlPath, nil); err != nil { return } var cfgReq *http.Response if cfgReq, err = myClient.Do(req); err != nil { return utils.ErrPathNotReachable(urlPath) } err = loadConfigFromReader(ctx, cfgReq.Body, loadFuncs, false, cfg) cfgReq.Body.Close() if err != nil { err = fmt.Errorf("url <%s>:%s", urlPath, err.Error()) return } } return } // populates the config locks and the reload channels func (cfg *CGRConfig) initChanels() { cfg.lks = make(map[string]*sync.RWMutex) for _, section := range cfg.sections { cfg.lks[section.SName()] = new(sync.RWMutex) } } // reloadSections sends a signal to the reload channel for the needed sections // the list of sections should be always valid because we load the config first with this list func (cfg *CGRConfig) reloadSections(sections ...string) { subsystemsThatNeedDataDB := utils.NewStringSet([]string{DataDBJSON, CDRsJSON, SessionSJSON, AttributeSJSON, ChargerSJSON, ResourceSJSON, StatSJSON, ThresholdSJSON, RouteSJSON, LoaderSJSON, DispatcherSJSON, RateSJSON, AdminSJSON, AccountSJSON, ActionSJSON}) needsDataDB := false for _, section := range sections { if !needsDataDB && subsystemsThatNeedDataDB.Has(section) { needsDataDB = true cfg.rldCh <- SectionToService[DataDBJSON] // reload datadb before } if needsDataDB { break } } runtime.Gosched() for _, section := range sections { if srv := SectionToService[section]; srv != utils.EmptyString && section != DataDBJSON { cfg.rldCh <- srv } } } // AsMapInterface returns the config as a map[string]interface{} func (cfg *CGRConfig) AsMapInterface(separator string) (mp map[string]interface{}) { return cfg.sections.AsMapInterface(separator) } // Clone returns a deep copy of CGRConfig func (cfg *CGRConfig) Clone() (cln *CGRConfig) { cln = &CGRConfig{ DataFolderPath: cfg.DataFolderPath, ConfigPath: cfg.ConfigPath, loaderCfg: *cfg.loaderCfg.Clone(), httpAgentCfg: *cfg.httpAgentCfg.Clone(), rpcConns: cfg.rpcConns.Clone(), templates: cfg.templates.Clone(), generalCfg: cfg.generalCfg.Clone(), loggerCfg: cfg.loggerCfg.Clone(), dataDbCfg: cfg.dataDbCfg.Clone(), tlsCfg: cfg.tlsCfg.Clone(), cacheCfg: cfg.cacheCfg.Clone(), listenCfg: cfg.listenCfg.Clone(), httpCfg: cfg.httpCfg.Clone(), filterSCfg: cfg.filterSCfg.Clone(), cdrsCfg: cfg.cdrsCfg.Clone(), sessionSCfg: cfg.sessionSCfg.Clone(), fsAgentCfg: cfg.fsAgentCfg.Clone(), kamAgentCfg: cfg.kamAgentCfg.Clone(), asteriskAgentCfg: cfg.asteriskAgentCfg.Clone(), diameterAgentCfg: cfg.diameterAgentCfg.Clone(), radiusAgentCfg: cfg.radiusAgentCfg.Clone(), dnsAgentCfg: cfg.dnsAgentCfg.Clone(), attributeSCfg: cfg.attributeSCfg.Clone(), chargerSCfg: cfg.chargerSCfg.Clone(), resourceSCfg: cfg.resourceSCfg.Clone(), statsCfg: cfg.statsCfg.Clone(), thresholdSCfg: cfg.thresholdSCfg.Clone(), routeSCfg: cfg.routeSCfg.Clone(), sureTaxCfg: cfg.sureTaxCfg.Clone(), dispatcherSCfg: cfg.dispatcherSCfg.Clone(), registrarCCfg: cfg.registrarCCfg.Clone(), loaderCgrCfg: cfg.loaderCgrCfg.Clone(), migratorCgrCfg: cfg.migratorCgrCfg.Clone(), analyzerSCfg: cfg.analyzerSCfg.Clone(), admS: cfg.admS.Clone(), ersCfg: cfg.ersCfg.Clone(), eesCfg: cfg.eesCfg.Clone(), efsCfg: cfg.efsCfg.Clone(), rateSCfg: cfg.rateSCfg.Clone(), sipAgentCfg: cfg.sipAgentCfg.Clone(), configSCfg: cfg.configSCfg.Clone(), apiBanCfg: cfg.apiBanCfg.Clone(), coreSCfg: cfg.coreSCfg.Clone(), actionSCfg: cfg.actionSCfg.Clone(), accountSCfg: cfg.accountSCfg.Clone(), tpeSCfg: cfg.tpeSCfg.Clone(), configDBCfg: cfg.configDBCfg.Clone(), rldCh: make(chan string), cacheDP: make(utils.MapStorage), } cln.sections = newSections(cln) for _, sec := range cfg.sections[len(cln.sections):] { cln.sections = append(cln.sections, sec.CloneSection()) } cln.initChanels() return } // GetDataProvider returns the config as a data provider interface func (cfg *CGRConfig) GetDataProvider() utils.MapStorage { cfg.cacheDPMux.RLock() if len(cfg.cacheDP) < len(cfg.sections) { cfg.cacheDP = cfg.AsMapInterface(cfg.GeneralCfg().RSRSep) } mp := cfg.cacheDP.Clone() cfg.cacheDPMux.RUnlock() return mp }