Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config system #26

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 35 additions & 12 deletions cmd/grumble/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var usageTmpl = `usage: grumble [options]
grumble {{.Version}} ({{.BuildDate}})
target: {{.OS}}, {{.Arch}}

--help
--help, --version
Shows this help listing.

--datadir <data-dir> (default: {{.DefaultDataDir}})
Expand All @@ -30,6 +30,20 @@ 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.

--supw <password> [server-id]
Set password for SuperUser account. Optionally takes
the virtual server to modify as the first positional argument.

--readsupw [server-id]
Like --supw, but reads from stdin instead.

--disablesu [server-id]
Disables the SuperUser account. Optionally takes
the virtual server to modify as the first positional argument.

--regen-keys
Force grumble to regenerate its global RSA
keypair (and certificate).
Expand All @@ -46,12 +60,17 @@ var usageTmpl = `usage: grumble [options]
`

type args struct {
ShowHelp bool
DataDir string
LogPath string
RegenKeys bool
SQLiteDB string
CleanUp bool
ShowHelp bool
DataDir string
LogPath string
ConfigPath string
SuperUserPW string
ReadPass bool
DisablePass bool
RegenKeys bool
ServerId int64
SQLiteDB string
CleanUp bool
}

func defaultDataDir() string {
Expand All @@ -63,10 +82,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 {
Expand All @@ -90,9 +105,17 @@ var Args args
func init() {
flag.Usage = Usage

flag.BoolVar(&Args.ShowHelp, "version", false, "")
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.StringVar(&Args.SuperUserPW, "supw", "", "")
flag.BoolVar(&Args.ReadPass, "readsupw", false, "")
flag.BoolVar(&Args.DisablePass, "disablesu", false, "")

flag.BoolVar(&Args.RegenKeys, "regen-keys", false, "")

flag.StringVar(&Args.SQLiteDB, "import-murmurdb", "", "")
Expand Down
2 changes: 1 addition & 1 deletion cmd/grumble/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ func (client *Client) tlsRecvLoop() {
Release: proto.String("Grumble"),
CryptoModes: cryptstate.SupportedModes(),
}
if client.server.cfg.BoolValue("SendOSInfo") {
if client.server.cfg.BoolValue("sendversion") {
version.Os = proto.String(runtime.GOOS)
version.OsVersion = proto.String("(Unknown version)")
}
Expand Down
32 changes: 2 additions & 30 deletions cmd/grumble/freeze.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
105 changes: 95 additions & 10 deletions cmd/grumble/grumble.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@ package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"regexp"
"strconv"

"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
Expand All @@ -27,6 +31,20 @@ func main() {
Usage()
return
}
if Args.ReadPass {
data, err := ioutil.ReadAll(os.Stdin)
if err != nil {
log.Fatalf("Failed to read password from stdin: %v", err)
}
Args.SuperUserPW = string(data)
}
if flag.NArg() > 0 && (Args.SuperUserPW != "" || Args.DisablePass) {
Args.ServerId, err = strconv.ParseInt(flag.Arg(0), 10, 64)
if err != nil {
log.Fatalf("Failed to parse server id %v: %v", flag.Arg(0), err)
return
}
}

// Open the data dir to check whether it exists.
dataDir, err := os.Open(Args.DataDir)
Expand All @@ -36,10 +54,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
logtarget.Default, err = logtarget.OpenFile(Args.LogPath, os.Stderr)
var logFn string
if Args.LogPath != "" {
logFn = Args.LogPath
} else {
logFn = config.PathValue("logfile", Args.DataDir)
}
logtarget.Default, err = logtarget.OpenFile(logFn, os.Stderr)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to open log file (%v): %v", Args.LogPath, err)
log.Fatalf("Unable to open log file (%v): %v", logFn, err)
return
}
log.SetPrefix("[G] ")
Expand All @@ -48,6 +98,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.
Expand All @@ -63,11 +130,9 @@ func main() {

// Check whether we should regenerate the default global keypair
// 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")
// These are used as the default certificate of all virtual servers.
certFn := config.PathValue("sslCert", Args.DataDir)
keyFn := config.PathValue("sslKey", Args.DataDir)
shouldRegen := false
if Args.RegenKeys {
shouldRegen = true
Expand Down Expand Up @@ -164,10 +229,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
}

Expand All @@ -181,6 +246,18 @@ func main() {
if err != nil {
log.Fatalf("Unable to load server: %v", err.Error())
}

// Check if SuperUser password should be updated.
if Args.ServerId == 0 || Args.ServerId == s.Id {
if Args.DisablePass {
s.cfg.Reset("SuperUserPassword")
log.Printf("Disabled SuperUser for server %v", name)
} else if Args.SuperUserPW != "" {
s.SetSuperUserPassword(Args.SuperUserPW)
log.Printf("Set SuperUser password for server %v", name)
}
}

err = s.FreezeToFile()
if err != nil {
log.Fatalf("Unable to freeze server to disk: %v", err.Error())
Expand All @@ -189,9 +266,17 @@ func main() {
}
}

// If SuperUser password flags were passed, the servers should not start.
if Args.SuperUserPW != "" || Args.DisablePass {
if len(servers) == 0 {
log.Fatalf("No servers found to set password for")
}
return
}

// 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())
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/grumble/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
return
}

maxChannelUsers := server.cfg.IntValue("MaxChannelUsers")
maxChannelUsers := server.cfg.IntValue("usersperchannel")
if maxChannelUsers != 0 && len(dstChan.clients) >= maxChannelUsers {
client.sendPermissionDeniedFallback(mumbleproto.PermissionDenied_ChannelFull,
0x010201, "Channel is full")
Expand Down Expand Up @@ -653,7 +653,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {

// Texture change
if userstate.Texture != nil {
maximg := server.cfg.IntValue("MaxImageMessageLength")
maximg := server.cfg.IntValue("imagemessagelength")
if maximg > 0 && len(userstate.Texture) > maximg {
client.sendPermissionDeniedType(mumbleproto.PermissionDenied_TextTooLong)
return
Expand Down
2 changes: 1 addition & 1 deletion cmd/grumble/murmurdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
21 changes: 12 additions & 9 deletions cmd/grumble/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,19 @@ const registerUrl = "https://mumble.info/register.cgi"
// This function is used to determine whether or not to periodically
// contact the master server list and update this server's metadata.
func (server *Server) IsPublic() bool {
if len(server.cfg.StringValue("RegisterName")) == 0 {
if len(server.cfg.StringValue("registerName")) == 0 {
return false
}
if len(server.cfg.StringValue("RegisterHost")) == 0 {
if len(server.cfg.StringValue("registerHostname")) == 0 {
return false
}
if len(server.cfg.StringValue("RegisterPassword")) == 0 {
if len(server.cfg.StringValue("registerPassword")) == 0 {
return false
}
if len(server.cfg.StringValue("RegisterWebUrl")) == 0 {
if len(server.cfg.StringValue("registerUrl")) == 0 {
return false
}
if !server.cfg.BoolValue("allowping") {
return false
}
return true
Expand Down Expand Up @@ -80,11 +83,11 @@ func (server *Server) RegisterPublicServer() {

// Render registration XML template
reg := Register{
Name: server.cfg.StringValue("RegisterName"),
Host: server.cfg.StringValue("RegisterHost"),
Password: server.cfg.StringValue("RegisterPassword"),
Url: server.cfg.StringValue("RegisterWebUrl"),
Location: server.cfg.StringValue("RegisterLocation"),
Name: server.cfg.StringValue("registerName"),
Host: server.cfg.StringValue("registerHostname"),
Password: server.cfg.StringValue("registerPassword"),
Url: server.cfg.StringValue("registerUrl"),
Location: server.cfg.StringValue("registerLocation"),
Port: server.CurrentPort(),
Digest: digest,
Users: len(server.clients),
Expand Down
Loading