mirror of
https://github.com/mumble-voip/grumble.git
synced 2026-01-26 21:39:10 -08:00
Config system
(see also issue #21) Removes synchronizing set config keys to freezelog, since the system is the preferred way to persist configuration.
This commit is contained in:
parent
6f8c2bf2f5
commit
40ef449f6e
11 changed files with 263 additions and 88 deletions
|
|
@ -5,6 +5,7 @@
|
|||
package serverconf
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
|
@ -20,29 +21,34 @@ var defaultCfg = map[string]string{
|
|||
"RememberChannel": "true",
|
||||
"WelcomeText": "Welcome to this server running <b>Grumble</b>.",
|
||||
"SendVersion": "true",
|
||||
"LogPath": "grumble.log",
|
||||
"CertPath": "cert.pem",
|
||||
"KeyPath": "key.pem",
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
cfgMap map[string]string
|
||||
mutex sync.RWMutex
|
||||
fallbackMap map[string]string
|
||||
persistentMap map[string]string
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
// Create a new Config using cfgMap as the intial internal config map.
|
||||
// If cfgMap is nil, ConfigWithMap will create a new config map.
|
||||
func New(cfgMap map[string]string) *Config {
|
||||
if cfgMap == nil {
|
||||
cfgMap = make(map[string]string)
|
||||
// New returns a new Config using persistentMap as the initial internal config map.
|
||||
// The map persistentMap may not be reused. If set to nil, a new map is created.
|
||||
// Optionally, defaults may be passed in fallbackMap. This map is only read, not written.
|
||||
func New(persistentMap, fallbackMap map[string]string) *Config {
|
||||
if persistentMap == nil {
|
||||
persistentMap = make(map[string]string)
|
||||
}
|
||||
return &Config{cfgMap: cfgMap}
|
||||
return &Config{persistentMap: persistentMap, fallbackMap: fallbackMap}
|
||||
}
|
||||
|
||||
// GetAll gets a copy of the Config's internal config map
|
||||
func (cfg *Config) GetAll() (all map[string]string) {
|
||||
// GetAllPersistent returns a copy of the internal persistent key-value map.
|
||||
func (cfg *Config) GetAllPersistent() (all map[string]string) {
|
||||
cfg.mutex.RLock()
|
||||
defer cfg.mutex.RUnlock()
|
||||
|
||||
all = make(map[string]string)
|
||||
for k, v := range cfg.cfgMap {
|
||||
for k, v := range cfg.persistentMap {
|
||||
all[k] = v
|
||||
}
|
||||
return
|
||||
|
|
@ -52,14 +58,14 @@ func (cfg *Config) GetAll() (all map[string]string) {
|
|||
func (cfg *Config) Set(key string, value string) {
|
||||
cfg.mutex.Lock()
|
||||
defer cfg.mutex.Unlock()
|
||||
cfg.cfgMap[key] = value
|
||||
cfg.persistentMap[key] = value
|
||||
}
|
||||
|
||||
// Reset the value of a config key
|
||||
func (cfg *Config) Reset(key string) {
|
||||
cfg.mutex.Lock()
|
||||
defer cfg.mutex.Unlock()
|
||||
delete(cfg.cfgMap, key)
|
||||
delete(cfg.persistentMap, key)
|
||||
}
|
||||
|
||||
// StringValue gets the value of a specific config key encoded as a string
|
||||
|
|
@ -67,7 +73,12 @@ func (cfg *Config) StringValue(key string) (value string) {
|
|||
cfg.mutex.RLock()
|
||||
defer cfg.mutex.RUnlock()
|
||||
|
||||
value, exists := cfg.cfgMap[key]
|
||||
value, exists := cfg.persistentMap[key]
|
||||
if exists {
|
||||
return value
|
||||
}
|
||||
|
||||
value, exists = cfg.fallbackMap[key]
|
||||
if exists {
|
||||
return value
|
||||
}
|
||||
|
|
@ -80,7 +91,7 @@ func (cfg *Config) StringValue(key string) (value string) {
|
|||
return ""
|
||||
}
|
||||
|
||||
// IntValue gets the value of a speific config key as an int
|
||||
// Get the value of a specific config key as an int
|
||||
func (cfg *Config) IntValue(key string) (intval int) {
|
||||
str := cfg.StringValue(key)
|
||||
intval, _ = strconv.Atoi(str)
|
||||
|
|
@ -94,9 +105,19 @@ func (cfg *Config) Uint32Value(key string) (uint32val uint32) {
|
|||
return uint32(uintval)
|
||||
}
|
||||
|
||||
// BoolValue gets the value fo a sepcific config key as a bool
|
||||
// Get the value of a specific config key as a bool
|
||||
func (cfg *Config) BoolValue(key string) (boolval bool) {
|
||||
str := cfg.StringValue(key)
|
||||
boolval, _ = strconv.ParseBool(str)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the value of a specific config key as a path,
|
||||
// joined with the path in rel if not absolute.
|
||||
func (cfg *Config) PathValue(key string, rel string) (path string) {
|
||||
str := cfg.StringValue(key)
|
||||
if filepath.IsAbs(str) {
|
||||
return filepath.Clean(str)
|
||||
}
|
||||
return filepath.Join(rel, str)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func TestIntValue(t *testing.T) {
|
||||
cfg := New(nil)
|
||||
cfg := New(nil, nil)
|
||||
cfg.Set("Test", "13")
|
||||
if cfg.IntValue("Test") != 13 {
|
||||
t.Errorf("Expected 13")
|
||||
|
|
@ -17,7 +17,7 @@ func TestIntValue(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFloatAsInt(t *testing.T) {
|
||||
cfg := New(nil)
|
||||
cfg := New(nil, nil)
|
||||
cfg.Set("Test", "13.4")
|
||||
if cfg.IntValue("Test") != 0 {
|
||||
t.Errorf("Expected 0")
|
||||
|
|
@ -25,14 +25,14 @@ func TestFloatAsInt(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDefaultValue(t *testing.T) {
|
||||
cfg := New(nil)
|
||||
cfg := New(nil, nil)
|
||||
if cfg.IntValue("MaxBandwidth") != 72000 {
|
||||
t.Errorf("Expected 72000")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoolValue(t *testing.T) {
|
||||
cfg := New(nil)
|
||||
cfg := New(nil, nil)
|
||||
cfg.Set("DoStuffOnStartup", "true")
|
||||
if cfg.BoolValue("DoStuffOnStartup") != true {
|
||||
t.Errorf("Expected true")
|
||||
|
|
|
|||
54
pkg/serverconf/file.go
Normal file
54
pkg/serverconf/file.go
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package serverconf
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type cfg interface {
|
||||
// GlobalMap returns a copy of the top-level (global) configuration map.
|
||||
GlobalMap() map[string]string
|
||||
}
|
||||
|
||||
type ConfigFile struct {
|
||||
cfg
|
||||
}
|
||||
|
||||
func NewConfigFile(path string) (*ConfigFile, error) {
|
||||
var f cfg
|
||||
f, err := newinicfg(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ConfigFile{f}, nil
|
||||
}
|
||||
|
||||
// GlobalConfig returns a new *serverconf.Config representing the top-level
|
||||
// (global) configuration.
|
||||
func (c *ConfigFile) GlobalConfig() *Config {
|
||||
return New(nil, c.GlobalMap())
|
||||
}
|
||||
|
||||
// ServerConfig returns a new *serverconf.Config with the fallback representing
|
||||
// the global configuration with server-specific values incremented by id.
|
||||
// Optionally a persistent map which has priority may be passed. This map
|
||||
// is consumed and cannot be reused.
|
||||
func (c *ConfigFile) ServerConfig(id int64, persistentMap map[string]string) *Config {
|
||||
m := c.GlobalMap()
|
||||
|
||||
// Some server specific values from the global config must be offset.
|
||||
// These are read differently by the server as well.
|
||||
if v, ok := m["Port"]; ok {
|
||||
i, err := strconv.ParseInt(v, 10, 64)
|
||||
if err == nil {
|
||||
m["Port"] = strconv.FormatInt(i+id-1, 10)
|
||||
}
|
||||
}
|
||||
if v, ok := m["WebPort"]; ok {
|
||||
i, err := strconv.ParseInt(v, 10, 64)
|
||||
if err == nil {
|
||||
m["WebPort"] = strconv.FormatInt(i+id-1, 10)
|
||||
}
|
||||
}
|
||||
|
||||
return New(persistentMap, m)
|
||||
}
|
||||
83
pkg/serverconf/file_ini.go
Normal file
83
pkg/serverconf/file_ini.go
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
package serverconf
|
||||
|
||||
import (
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
type inicfg struct {
|
||||
file *ini.File
|
||||
}
|
||||
|
||||
func newinicfg(path string) (*inicfg, error) {
|
||||
file, err := ini.LoadSources(ini.LoadOptions{AllowBooleanKeys: true, UnescapeValueDoubleQuotes: true}, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file.BlockMode = false // read only, avoid locking
|
||||
return &inicfg{file}, nil
|
||||
}
|
||||
|
||||
func (f *inicfg) GlobalMap() map[string]string {
|
||||
return f.file.Section("").KeysHash()
|
||||
}
|
||||
|
||||
// todo(rubenseyer): not all of these even work.. make sure to implement them
|
||||
var DefaultConfigFile = `# Grumble configuration file.
|
||||
#
|
||||
# The commented out settings represent the defaults.
|
||||
# Options here may be overridden by virtual server specific configuration.
|
||||
# Make sure to enclose values containing # or ; in double quotes or backticks.
|
||||
|
||||
# Address to bind the listeners to.
|
||||
#Address = 0.0.0.0
|
||||
|
||||
# Port is the port to bind the native Mumble protocol to.
|
||||
# WebPort is the port to bind the WebSocket Mumble protocol to.
|
||||
# They are incremented for each virtual server.
|
||||
#Port = 64738
|
||||
#WebPort = 443
|
||||
|
||||
# "Message of the day" HTML string sent to connecting clients.
|
||||
#WelcomeText = "Welcome to this server running <b>Grumble</b>."
|
||||
|
||||
# Maximum bandwidth (in bits per second) per client for voice.
|
||||
# Grumble does not yet enforce this limit, but some clients nicely follow it.
|
||||
#MaxBandwidth = 72000
|
||||
|
||||
# Maximum number of concurrent clients.
|
||||
#MaxUsers = 1000
|
||||
#MaxUsersPerChannel = 0
|
||||
|
||||
#MaxTextMessageLength = 5000
|
||||
#MaxImageMessageLength = 131072
|
||||
#AllowHTML
|
||||
|
||||
# DefaultChannel is the channel (by ID) new users join.
|
||||
# The root channel is the default.
|
||||
#DefaultChannel = 0
|
||||
|
||||
# Whether users will rejoin the last channel they were in.
|
||||
#RememberChannel
|
||||
|
||||
# Whether to include server version and server os in ping response.
|
||||
#SendVersion
|
||||
#SendOSInfo
|
||||
|
||||
# Path to the log file (relative to the data directory).
|
||||
#LogPath = grumble.log
|
||||
|
||||
# Path to TLS certificate and key (relative to the data directory).
|
||||
# The certificate needs to have the entire chain concatenated to be validate.
|
||||
# If these paths do not exist, Grumble will autogenerate a certificate.
|
||||
#CertPath = cert.pem
|
||||
#KeyPath = key.pem
|
||||
|
||||
# Options for public server registration.
|
||||
# All of these have to be set to make the server public.
|
||||
# RegisterName additionally sets the name of the root channel.
|
||||
# RegisterPassword is a simple, arbitrary secret to guard your registration. Don't lose it.
|
||||
#RegisterName =
|
||||
#RegisterHost =
|
||||
#RegisterPassword =
|
||||
#RegisterWebUrl =
|
||||
`
|
||||
Loading…
Add table
Add a link
Reference in a new issue