mirror of
https://github.com/mumble-voip/grumble.git
synced 2025-12-19 21:59:59 -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
|
|
@ -30,6 +30,9 @@ var usageTmpl = `usage: grumble [options]
|
|||
--log <log-path> (default: $DATADIR/grumble.log)
|
||||
Log file path.
|
||||
|
||||
--ini <config-path> (default: $DATADIR/grumble.ini)
|
||||
Config file path.
|
||||
|
||||
--regen-keys
|
||||
Force grumble to regenerate its global RSA
|
||||
keypair (and certificate).
|
||||
|
|
@ -49,6 +52,7 @@ type args struct {
|
|||
ShowHelp bool
|
||||
DataDir string
|
||||
LogPath string
|
||||
ConfigPath string
|
||||
RegenKeys bool
|
||||
SQLiteDB string
|
||||
CleanUp bool
|
||||
|
|
@ -63,10 +67,6 @@ func defaultDataDir() string {
|
|||
return filepath.Join(homedir, dirname)
|
||||
}
|
||||
|
||||
func defaultLogPath() string {
|
||||
return filepath.Join(defaultDataDir(), "grumble.log")
|
||||
}
|
||||
|
||||
func Usage() {
|
||||
t, err := template.New("usage").Parse(usageTmpl)
|
||||
if err != nil {
|
||||
|
|
@ -92,7 +92,9 @@ func init() {
|
|||
|
||||
flag.BoolVar(&Args.ShowHelp, "help", false, "")
|
||||
flag.StringVar(&Args.DataDir, "datadir", defaultDataDir(), "")
|
||||
flag.StringVar(&Args.LogPath, "log", defaultLogPath(), "")
|
||||
flag.StringVar(&Args.LogPath, "log", "", "")
|
||||
flag.StringVar(&Args.ConfigPath, "ini", "", "")
|
||||
|
||||
flag.BoolVar(&Args.RegenKeys, "regen-keys", false, "")
|
||||
|
||||
flag.StringVar(&Args.SQLiteDB, "import-murmurdb", "", "")
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import (
|
|||
"mumble.info/grumble/pkg/ban"
|
||||
"mumble.info/grumble/pkg/freezer"
|
||||
"mumble.info/grumble/pkg/mumbleproto"
|
||||
"mumble.info/grumble/pkg/serverconf"
|
||||
)
|
||||
|
||||
// Freeze a server to disk and closes the log file.
|
||||
|
|
@ -74,7 +73,7 @@ func (server *Server) Freeze() (fs *freezer.Server, err error) {
|
|||
fs = new(freezer.Server)
|
||||
|
||||
// Freeze all config kv-pairs
|
||||
allCfg := server.cfg.GetAll()
|
||||
allCfg := server.cfg.GetAllPersistent()
|
||||
for k, v := range allCfg {
|
||||
fs.Config = append(fs.Config, &freezer.ConfigKeyValuePair{
|
||||
Key: proto.String(k),
|
||||
|
|
@ -420,11 +419,10 @@ func NewServerFromFrozen(name string) (s *Server, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
s, err = NewServer(id)
|
||||
s, err = NewServer(id, configFile.ServerConfig(id, cfgMap))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.cfg = serverconf.New(cfgMap)
|
||||
|
||||
// Unfreeze the server's frozen bans.
|
||||
s.UnfreezeBanList(fs.BanList)
|
||||
|
|
@ -835,29 +833,3 @@ func (server *Server) UpdateFrozenBans(bans []ban.Ban) {
|
|||
}
|
||||
server.numLogOps += 1
|
||||
}
|
||||
|
||||
// UpdateConfig writes an updated config value to the datastore.
|
||||
func (server *Server) UpdateConfig(key, value string) {
|
||||
fcfg := &freezer.ConfigKeyValuePair{
|
||||
Key: proto.String(key),
|
||||
Value: proto.String(value),
|
||||
}
|
||||
err := server.freezelog.Put(fcfg)
|
||||
if err != nil {
|
||||
server.Fatal(err)
|
||||
}
|
||||
server.numLogOps += 1
|
||||
}
|
||||
|
||||
// ResetConfig writes to the freezelog that the config with key
|
||||
// has been reset to its default value.
|
||||
func (server *Server) ResetConfig(key string) {
|
||||
fcfg := &freezer.ConfigKeyValuePair{
|
||||
Key: proto.String(key),
|
||||
}
|
||||
err := server.freezelog.Put(fcfg)
|
||||
if err != nil {
|
||||
server.Fatal(err)
|
||||
}
|
||||
server.numLogOps += 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,12 @@ import (
|
|||
|
||||
"mumble.info/grumble/pkg/blobstore"
|
||||
"mumble.info/grumble/pkg/logtarget"
|
||||
"mumble.info/grumble/pkg/serverconf"
|
||||
)
|
||||
|
||||
var servers map[int64]*Server
|
||||
var blobStore blobstore.BlobStore
|
||||
var configFile *serverconf.ConfigFile
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
|
|
@ -36,10 +38,42 @@ func main() {
|
|||
}
|
||||
dataDir.Close()
|
||||
|
||||
// Open the config file
|
||||
var configFn string
|
||||
if Args.ConfigPath != "" {
|
||||
configFn = Args.ConfigPath
|
||||
} else {
|
||||
configFn = filepath.Join(Args.DataDir, "grumble.ini")
|
||||
}
|
||||
if filepath.Ext(configFn) == ".ini" {
|
||||
// Create it if it doesn't exist
|
||||
configFd, err := os.OpenFile(configFn, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0700)
|
||||
if err == nil {
|
||||
configFd.WriteString(serverconf.DefaultConfigFile)
|
||||
log.Fatalf("Default config written to %v\n", configFn)
|
||||
configFd.Close()
|
||||
} else if err != nil && !os.IsExist(err) {
|
||||
log.Fatalf("Unable to open config file (%v): %v", configFn, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
configFile, err = serverconf.NewConfigFile(configFn)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to open config file (%v): %v", configFn, err)
|
||||
return
|
||||
}
|
||||
config := configFile.GlobalConfig()
|
||||
|
||||
// Set up logging
|
||||
var logFn string
|
||||
if Args.LogPath != "" {
|
||||
logFn = Args.LogPath
|
||||
} else {
|
||||
logFn = config.PathValue("LogPath", Args.DataDir)
|
||||
}
|
||||
logtarget.Default, err = logtarget.OpenFile(Args.LogPath, os.Stderr)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Unable to open log file (%v): %v", Args.LogPath, err)
|
||||
fmt.Fprintf(os.Stderr, "Unable to open log file (%v): %v", logFn, err)
|
||||
return
|
||||
}
|
||||
log.SetPrefix("[G] ")
|
||||
|
|
@ -48,6 +82,23 @@ func main() {
|
|||
log.Printf("Grumble")
|
||||
log.Printf("Using data directory: %s", Args.DataDir)
|
||||
|
||||
// Warn on some unsupported configuration options for users migrating from Murmur
|
||||
if config.StringValue("database") != "" {
|
||||
log.Println("* Grumble does not yet support Murmur databases directly (see issue #21 on github).")
|
||||
if driver := config.StringValue("dbDriver"); driver == "QSQLITE" {
|
||||
log.Println(" To convert a previous SQLite database, use the --import-murmurdb flag.")
|
||||
}
|
||||
}
|
||||
if config.StringValue("sslDHParams") != "" {
|
||||
log.Println("* Go does not implement DHE modes in TLS, so the configured dhparams are ignored.")
|
||||
}
|
||||
if config.StringValue("ice") != "" {
|
||||
log.Println("* Grumble does not support ZeroC ICE.")
|
||||
}
|
||||
if config.StringValue("grpc") != "" {
|
||||
log.Println("* Grumble does not yet support gRPC (see issue #23 on github).")
|
||||
}
|
||||
|
||||
// Open the blobstore. If the directory doesn't
|
||||
// already exist, create the directory and open
|
||||
// the blobstore.
|
||||
|
|
@ -65,9 +116,9 @@ func main() {
|
|||
// and corresponding certificate.
|
||||
// These are used as the default certificate of all virtual servers
|
||||
// and the SSH admin console, but can be overridden using the "key"
|
||||
// and "cert" arguments to Grumble.
|
||||
certFn := filepath.Join(Args.DataDir, "cert.pem")
|
||||
keyFn := filepath.Join(Args.DataDir, "key.pem")
|
||||
// and "cert" arguments to Grumble. todo(rubenseyer) implement override by cli
|
||||
certFn := config.PathValue("CertPath", Args.DataDir)
|
||||
keyFn := config.PathValue("KeyPath", Args.DataDir)
|
||||
shouldRegen := false
|
||||
if Args.RegenKeys {
|
||||
shouldRegen = true
|
||||
|
|
@ -164,10 +215,10 @@ func main() {
|
|||
if err != nil {
|
||||
log.Fatalf("Unable to read file from data directory: %v", err.Error())
|
||||
}
|
||||
// The data dir file descriptor.
|
||||
// The servers dir file descriptor.
|
||||
err = serversDir.Close()
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to close data directory: %v", err.Error())
|
||||
log.Fatalf("Unable to close servers directory: %v", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -191,7 +242,7 @@ func main() {
|
|||
|
||||
// If no servers were found, create the default virtual server.
|
||||
if len(servers) == 0 {
|
||||
s, err := NewServer(1)
|
||||
s, err := NewServer(1, configFile.ServerConfig(1, nil))
|
||||
if err != nil {
|
||||
log.Fatalf("Couldn't start server: %s", err.Error())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ func MurmurImport(filename string) (err error) {
|
|||
|
||||
// Create a new Server from a Murmur SQLite database
|
||||
func NewServerFromSQLite(id int64, db *sql.DB) (s *Server, err error) {
|
||||
s, err = NewServer(id)
|
||||
s, err = NewServer(id, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import (
|
|||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -138,12 +137,16 @@ func (lf clientLogForwarder) Write(incoming []byte) (int, error) {
|
|||
}
|
||||
|
||||
// Allocate a new Murmur instance
|
||||
func NewServer(id int64) (s *Server, err error) {
|
||||
func NewServer(id int64, config *serverconf.Config) (s *Server, err error) {
|
||||
s = new(Server)
|
||||
|
||||
s.Id = id
|
||||
|
||||
s.cfg = serverconf.New(nil)
|
||||
if config == nil {
|
||||
s.cfg = serverconf.New(nil, nil)
|
||||
} else {
|
||||
s.cfg = config
|
||||
}
|
||||
|
||||
s.Users = make(map[uint32]*User)
|
||||
s.UserCertMap = make(map[string]*User)
|
||||
|
|
@ -191,10 +194,6 @@ func (server *Server) setConfigPassword(key, password string) {
|
|||
// Could be racy, but shouldn't really matter...
|
||||
val := "sha1$" + salt + "$" + digest
|
||||
server.cfg.Set(key, val)
|
||||
|
||||
if server.cfgUpdate != nil {
|
||||
server.cfgUpdate <- &KeyValuePair{Key: key, Value: val}
|
||||
}
|
||||
}
|
||||
|
||||
// SetSuperUserPassword sets password as the new SuperUser password
|
||||
|
|
@ -436,14 +435,6 @@ func (server *Server) handlerLoop() {
|
|||
case client := <-server.clientAuthenticated:
|
||||
server.finishAuthenticate(client)
|
||||
|
||||
// Disk freeze config update
|
||||
case kvp := <-server.cfgUpdate:
|
||||
if !kvp.Reset {
|
||||
server.UpdateConfig(kvp.Key, kvp.Value)
|
||||
} else {
|
||||
server.ResetConfig(kvp.Key)
|
||||
}
|
||||
|
||||
// Server registration update
|
||||
// Tick every hour + a minute offset based on the server id.
|
||||
case <-regtick:
|
||||
|
|
@ -1356,7 +1347,6 @@ func (server *Server) initPerLaunchData() {
|
|||
server.bye = make(chan bool)
|
||||
server.incoming = make(chan *Message)
|
||||
server.voicebroadcast = make(chan *VoiceBroadcast)
|
||||
server.cfgUpdate = make(chan *KeyValuePair)
|
||||
server.tempRemove = make(chan *Channel, 1)
|
||||
server.clientAuthenticated = make(chan *Client)
|
||||
}
|
||||
|
|
@ -1371,7 +1361,6 @@ func (server *Server) cleanPerLaunchData() {
|
|||
server.bye = nil
|
||||
server.incoming = nil
|
||||
server.voicebroadcast = nil
|
||||
server.cfgUpdate = nil
|
||||
server.tempRemove = nil
|
||||
server.clientAuthenticated = nil
|
||||
}
|
||||
|
|
@ -1460,8 +1449,8 @@ func (server *Server) Start() (err error) {
|
|||
*/
|
||||
|
||||
// Wrap a TLS listener around the TCP connection
|
||||
certFn := filepath.Join(Args.DataDir, "cert.pem")
|
||||
keyFn := filepath.Join(Args.DataDir, "key.pem")
|
||||
certFn := server.cfg.PathValue("CertPath", Args.DataDir)
|
||||
keyFn := server.cfg.PathValue("KeyPath", Args.DataDir)
|
||||
cert, err := tls.LoadX509KeyPair(certFn, keyFn)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -6,4 +6,5 @@ require (
|
|||
github.com/golang/protobuf v1.3.5
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71
|
||||
gopkg.in/ini.v1 v1.55.0
|
||||
)
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -10,3 +10,5 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/ini.v1 v1.55.0 h1:E8yzL5unfpW3M6fz/eB7Cb5MQAYSZ7GKo4Qth+N2sgQ=
|
||||
gopkg.in/ini.v1 v1.55.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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