diff --git a/irc/channel.go b/irc/channel.go index 8c45e5fa..337102fd 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -515,8 +515,9 @@ func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffe return } - if len(topic) > client.server.limits.TopicLen { - topic = topic[:client.server.limits.TopicLen] + topicLimit := client.server.Limits().TopicLen + if len(topic) > topicLimit { + topic = topic[:topicLimit] } channel.stateMutex.Lock() diff --git a/irc/client.go b/irc/client.go index 4198e743..ad3717f2 100644 --- a/irc/client.go +++ b/irc/client.go @@ -85,9 +85,9 @@ type Client struct { // NewClient returns a client with all the appropriate info setup. func NewClient(server *Server, conn net.Conn, isTLS bool) *Client { now := time.Now() - limits := server.Limits() - fullLineLenLimit := limits.LineLen.Tags + limits.LineLen.Rest - socket := NewSocket(conn, fullLineLenLimit*2, server.MaxSendQBytes()) + config := server.Config() + fullLineLenLimit := config.Limits.LineLen.Tags + config.Limits.LineLen.Rest + socket := NewSocket(conn, fullLineLenLimit*2, config.Server.MaxSendQBytes) client := &Client{ atime: now, authorized: server.Password() == nil, @@ -112,7 +112,7 @@ func NewClient(server *Server, conn net.Conn, isTLS bool) *Client { // error is not useful to us here anyways so we can ignore it client.certfp, _ = client.socket.CertFP() } - if server.checkIdent && !utils.AddrIsUnix(conn.RemoteAddr()) { + if config.Server.CheckIdent && !utils.AddrIsUnix(conn.RemoteAddr()) { _, serverPortString, err := net.SplitHostPort(conn.LocalAddr().String()) serverPort, _ := strconv.Atoi(serverPortString) if err != nil { diff --git a/irc/config.go b/irc/config.go index a6bf7b21..e515114c 100644 --- a/irc/config.go +++ b/irc/config.go @@ -28,10 +28,10 @@ import ( "gopkg.in/yaml.v2" ) -// PassConfig holds the connection password. -type PassConfig struct { - Password string -} +// here's how this works: exported (capitalized) members of the config structs +// are defined in the YAML file and deserialized directly from there. They may +// be postprocessed and overwritten by LoadConfig. Unexported (lowercase) members +// are derived from the exported members in LoadConfig. // TLSListenConfig defines configuration options for listening on TLS. type TLSListenConfig struct { @@ -51,15 +51,6 @@ func (conf *TLSListenConfig) Config() (*tls.Config, error) { }, err } -// PasswordBytes returns the bytes represented by the password hash. -func (conf *PassConfig) PasswordBytes() []byte { - bytes, err := passwd.DecodePasswordHash(conf.Password) - if err != nil { - log.Fatal("decode password error: ", err) - } - return bytes -} - type AccountConfig struct { Registration AccountRegistrationConfig AuthenticationEnabled bool `yaml:"authentication-enabled"` @@ -171,11 +162,24 @@ func (conf *OperConfig) PasswordBytes() []byte { } // LineLenConfig controls line lengths. -type LineLenConfig struct { +type LineLenLimits struct { Tags int Rest int } +// Various server-enforced limits on data size. +type Limits struct { + AwayLen int `yaml:"awaylen"` + ChanListModes int `yaml:"chan-list-modes"` + ChannelLen int `yaml:"channellen"` + KickLen int `yaml:"kicklen"` + MonitorEntries int `yaml:"monitor-entries"` + NickLen int `yaml:"nicklen"` + TopicLen int `yaml:"topiclen"` + WhowasEntries int `yaml:"whowas-entries"` + LineLen LineLenLimits `yaml:"linelen"` +} + // STSConfig controls the STS configuration/ type STSConfig struct { Enabled bool @@ -219,9 +223,10 @@ type Config struct { } Server struct { - PassConfig Password string + passwordBytes []byte Name string + nameCasefolded string Listen []string TLSListeners map[string]*TLSListenConfig `yaml:"tls-listeners"` STS STSConfig @@ -251,14 +256,19 @@ type Config struct { Accounts AccountConfig Channels struct { - DefaultModes *string `yaml:"default-modes"` - Registration ChannelRegistrationConfig + RawDefaultModes *string `yaml:"default-modes"` + defaultModes modes.Modes + Registration ChannelRegistrationConfig } OperClasses map[string]*OperClassConfig `yaml:"oper-classes"` Opers map[string]*OperConfig + // parsed operator definitions, unexported so they can't be defined + // directly in YAML: + operators map[string]*Oper + Logging []logger.LoggingConfig Debug struct { @@ -267,17 +277,7 @@ type Config struct { StackImpact StackImpactConfig } - Limits struct { - AwayLen uint `yaml:"awaylen"` - ChanListModes uint `yaml:"chan-list-modes"` - ChannelLen uint `yaml:"channellen"` - KickLen uint `yaml:"kicklen"` - MonitorEntries uint `yaml:"monitor-entries"` - NickLen uint `yaml:"nicklen"` - TopicLen uint `yaml:"topiclen"` - WhowasEntries uint `yaml:"whowas-entries"` - LineLen LineLenConfig `yaml:"linelen"` - } + Limits Limits Fakelag FakelagConfig @@ -437,11 +437,6 @@ func LoadConfig(filename string) (config *Config, err error) { config.Filename = filename - // we need this so PasswordBytes returns the correct info - if config.Server.Password != "" { - config.Server.PassConfig.Password = config.Server.Password - } - if config.Network.Name == "" { return nil, ErrNetworkNameMissing } @@ -691,5 +686,33 @@ func LoadConfig(filename string) (config *Config, err error) { } } + // casefold/validate server name + config.Server.nameCasefolded, err = Casefold(config.Server.Name) + if err != nil { + return nil, fmt.Errorf("Server name isn't valid [%s]: %s", config.Server.Name, err.Error()) + } + + // process operator definitions, store them to config.operators + operclasses, err := config.OperatorClasses() + if err != nil { + return nil, err + } + opers, err := config.Operators(operclasses) + if err != nil { + return nil, err + } + config.operators = opers + + // parse default channel modes + config.Channels.defaultModes = ParseDefaultChannelModes(config.Channels.RawDefaultModes) + + if config.Server.Password != "" { + bytes, err := passwd.DecodePasswordHash(config.Server.Password) + if err != nil { + return nil, err + } + config.Server.passwordBytes = bytes + } + return config, nil } diff --git a/irc/database.go b/irc/database.go index 2e54a8d1..99baa5ab 100644 --- a/irc/database.go +++ b/irc/database.go @@ -267,7 +267,7 @@ func schemaChangeV2ToV3(config *Config, tx *buntdb.Tx) error { } // explicitly store the channel modes - defaultModes := ParseDefaultChannelModes(config) + defaultModes := ParseDefaultChannelModes(config.Channels.RawDefaultModes) modeStrings := make([]string, len(defaultModes)) for i, mode := range defaultModes { modeStrings[i] = string(mode) diff --git a/irc/getters.go b/irc/getters.go index 55a72944..7ea5f19a 100644 --- a/irc/getters.go +++ b/irc/getters.go @@ -6,15 +6,12 @@ package irc import ( "github.com/oragono/oragono/irc/isupport" "github.com/oragono/oragono/irc/modes" - "sync/atomic" ) -func (server *Server) MaxSendQBytes() int { - return int(atomic.LoadUint32(&server.maxSendQBytes)) -} - -func (server *Server) SetMaxSendQBytes(m int) { - atomic.StoreUint32(&server.maxSendQBytes, uint32(m)) +func (server *Server) Config() *Config { + server.configurableStateMutex.RLock() + defer server.configurableStateMutex.RUnlock() + return server.config } func (server *Server) ISupport() *isupport.List { @@ -24,63 +21,41 @@ func (server *Server) ISupport() *isupport.List { } func (server *Server) Limits() Limits { - server.configurableStateMutex.RLock() - defer server.configurableStateMutex.RUnlock() - return server.limits + return server.Config().Limits } func (server *Server) Password() []byte { - server.configurableStateMutex.RLock() - defer server.configurableStateMutex.RUnlock() - return server.password + return server.Config().Server.passwordBytes } func (server *Server) RecoverFromErrors() bool { - server.configurableStateMutex.RLock() - defer server.configurableStateMutex.RUnlock() - return server.recoverFromErrors + // default to true if unset + rfe := server.Config().Debug.RecoverFromErrors + return rfe == nil || *rfe } func (server *Server) ProxyAllowedFrom() []string { - server.configurableStateMutex.RLock() - defer server.configurableStateMutex.RUnlock() - return server.proxyAllowedFrom + return server.Config().Server.ProxyAllowedFrom } func (server *Server) WebIRCConfig() []webircConfig { - server.configurableStateMutex.RLock() - defer server.configurableStateMutex.RUnlock() - return server.webirc + return server.Config().Server.WebIRC } func (server *Server) DefaultChannelModes() modes.Modes { - server.configurableStateMutex.RLock() - defer server.configurableStateMutex.RUnlock() - return server.defaultChannelModes + return server.Config().Channels.defaultModes } func (server *Server) ChannelRegistrationEnabled() bool { - server.configurableStateMutex.RLock() - defer server.configurableStateMutex.RUnlock() - return server.config.Channels.Registration.Enabled + return server.Config().Channels.Registration.Enabled } func (server *Server) AccountConfig() *AccountConfig { - server.configurableStateMutex.RLock() - defer server.configurableStateMutex.RUnlock() - if server.config == nil { - return nil - } - return &server.config.Accounts + return &server.Config().Accounts } func (server *Server) FakelagConfig() *FakelagConfig { - server.configurableStateMutex.RLock() - defer server.configurableStateMutex.RUnlock() - if server.config == nil { - return nil - } - return &server.config.Fakelag + return &server.Config().Fakelag } func (server *Server) GetOperator(name string) (oper *Oper) { @@ -90,7 +65,7 @@ func (server *Server) GetOperator(name string) (oper *Oper) { } server.configurableStateMutex.RLock() defer server.configurableStateMutex.RUnlock() - return server.operators[name] + return server.config.operators[name] } func (client *Client) Nick() string { diff --git a/irc/handlers.go b/irc/handlers.go index 4d596f56..789cbdf8 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -1501,12 +1501,12 @@ func monitorAddHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb var online []string var offline []string - limit := server.Limits().MonitorEntries + limits := server.Limits() targets := strings.Split(msg.Params[1], ",") for _, target := range targets { // check name length - if len(target) < 1 || len(targets) > server.limits.NickLen { + if len(target) < 1 || len(targets) > limits.NickLen { continue } @@ -1516,9 +1516,9 @@ func monitorAddHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb continue } - err = server.monitorManager.Add(client, casefoldedTarget, limit) + err = server.monitorManager.Add(client, casefoldedTarget, limits.MonitorEntries) if err == errMonitorLimitExceeded { - rb.Add(nil, server.name, ERR_MONLISTFULL, client.Nick(), strconv.Itoa(server.limits.MonitorEntries), strings.Join(targets, ",")) + rb.Add(nil, server.name, ERR_MONLISTFULL, client.Nick(), strconv.Itoa(limits.MonitorEntries), strings.Join(targets, ",")) break } else if err != nil { continue @@ -1814,14 +1814,15 @@ func passHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp } // if no password exists, skip checking - if len(server.password) == 0 { + serverPassword := server.Password() + if serverPassword == nil { client.SetAuthorized(true) return false } // check the provided password password := []byte(msg.Params[0]) - if passwd.ComparePassword(server.password, password) != nil { + if passwd.ComparePassword(serverPassword, password) != nil { rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.nick, client.t("Password incorrect")) rb.Add(nil, server.name, "ERROR", client.t("Password incorrect")) return true diff --git a/irc/modes.go b/irc/modes.go index a6f67cd6..0fee6d78 100644 --- a/irc/modes.go +++ b/irc/modes.go @@ -87,12 +87,12 @@ func ApplyUserModeChanges(client *Client, changes modes.ModeChanges, force bool) } // ParseDefaultChannelModes parses the `default-modes` line of the config -func ParseDefaultChannelModes(config *Config) modes.Modes { - if config.Channels.DefaultModes == nil { +func ParseDefaultChannelModes(rawModes *string) modes.Modes { + if rawModes == nil { // not present in config, fall back to compile-time default return DefaultChannelModes } - modeChangeStrings := strings.Split(strings.TrimSpace(*config.Channels.DefaultModes), " ") + modeChangeStrings := strings.Fields(*rawModes) modeChanges, _ := modes.ParseChannelModeChanges(modeChangeStrings...) defaultChannelModes := make(modes.Modes, 0) for _, modeChange := range modeChanges { diff --git a/irc/modes_test.go b/irc/modes_test.go index a929aa10..0f99c3f5 100644 --- a/irc/modes_test.go +++ b/irc/modes_test.go @@ -27,10 +27,8 @@ func TestParseDefaultChannelModes(t *testing.T) { {nil, modes.Modes{modes.NoOutside, modes.OpOnlyTopic}}, } - var config Config for _, testcase := range parseTests { - config.Channels.DefaultModes = testcase.raw - result := ParseDefaultChannelModes(&config) + result := ParseDefaultChannelModes(testcase.raw) if !reflect.DeepEqual(result, testcase.expected) { t.Errorf("expected modes %s, got %s", testcase.expected, result) } diff --git a/irc/server.go b/irc/server.go index 193e11f8..c05767ab 100644 --- a/irc/server.go +++ b/irc/server.go @@ -58,24 +58,6 @@ var ( CapValues = caps.NewValues() ) -// Limits holds the maximum limits for various things such as topic lengths. -type Limits struct { - AwayLen int - ChannelLen int - KickLen int - MonitorEntries int - NickLen int - TopicLen int - ChanListModes int - LineLen LineLenLimits -} - -// LineLenLimits holds the maximum limits for IRC lines. -type LineLenLimits struct { - Tags int - Rest int -} - // ListenerWrapper wraps a listener so it can be safely reconfigured or stopped type ListenerWrapper struct { listener net.Listener @@ -91,7 +73,6 @@ type Server struct { batches *BatchManager channels *ChannelManager channelRegistry *ChannelRegistry - checkIdent bool clients *ClientManager config *Config configFilename string @@ -99,35 +80,23 @@ type Server struct { connectionLimiter *connection_limits.Limiter connectionThrottler *connection_limits.Throttler ctime time.Time - defaultChannelModes modes.Modes dlines *DLineManager - loggingRawIO bool isupport *isupport.List klines *KLineManager languages *languages.Manager - limits Limits listeners map[string]*ListenerWrapper logger *logger.Manager - maxSendQBytes uint32 monitorManager *MonitorManager motdLines []string name string nameCasefolded string - networkName string - operators map[string]*Oper - operclasses map[string]*OperClass - password []byte passwords *passwd.SaltedManager - recoverFromErrors bool rehashMutex sync.Mutex // tier 4 rehashSignal chan os.Signal pprofServer *http.Server - proxyAllowedFrom []string signals chan os.Signal snomasks *SnoManager store *buntdb.DB - stsEnabled bool - webirc []webircConfig whoWas *WhoWasList stats *Stats semaphores *ServerSemaphores @@ -204,35 +173,35 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) { func (server *Server) setISupport() { maxTargetsString := strconv.Itoa(maxTargets) - server.configurableStateMutex.RLock() + config := server.Config() // add RPL_ISUPPORT tokens isupport := isupport.NewList() - isupport.Add("AWAYLEN", strconv.Itoa(server.limits.AwayLen)) + isupport.Add("AWAYLEN", strconv.Itoa(config.Limits.AwayLen)) isupport.Add("CASEMAPPING", "ascii") isupport.Add("CHANMODES", strings.Join([]string{modes.Modes{modes.BanMask, modes.ExceptMask, modes.InviteMask}.String(), "", modes.Modes{modes.UserLimit, modes.Key}.String(), modes.Modes{modes.InviteOnly, modes.Moderated, modes.NoOutside, modes.OpOnlyTopic, modes.ChanRoleplaying, modes.Secret}.String()}, ",")) - isupport.Add("CHANNELLEN", strconv.Itoa(server.limits.ChannelLen)) + isupport.Add("CHANNELLEN", strconv.Itoa(config.Limits.ChannelLen)) isupport.Add("CHANTYPES", "#") isupport.Add("ELIST", "U") isupport.Add("EXCEPTS", "") isupport.Add("INVEX", "") - isupport.Add("KICKLEN", strconv.Itoa(server.limits.KickLen)) - isupport.Add("MAXLIST", fmt.Sprintf("beI:%s", strconv.Itoa(server.limits.ChanListModes))) + isupport.Add("KICKLEN", strconv.Itoa(config.Limits.KickLen)) + isupport.Add("MAXLIST", fmt.Sprintf("beI:%s", strconv.Itoa(config.Limits.ChanListModes))) isupport.Add("MAXTARGETS", maxTargetsString) isupport.Add("MODES", "") - isupport.Add("MONITOR", strconv.Itoa(server.limits.MonitorEntries)) - isupport.Add("NETWORK", server.networkName) - isupport.Add("NICKLEN", strconv.Itoa(server.limits.NickLen)) + isupport.Add("MONITOR", strconv.Itoa(config.Limits.MonitorEntries)) + isupport.Add("NETWORK", config.Network.Name) + isupport.Add("NICKLEN", strconv.Itoa(config.Limits.NickLen)) isupport.Add("PREFIX", "(qaohv)~&@%+") isupport.Add("RPCHAN", "E") isupport.Add("RPUSER", "E") isupport.Add("STATUSMSG", "~&@%+") isupport.Add("TARGMAX", fmt.Sprintf("NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:%s,TAGMSG:%s,NOTICE:%s,MONITOR:", maxTargetsString, maxTargetsString, maxTargetsString)) - isupport.Add("TOPICLEN", strconv.Itoa(server.limits.TopicLen)) + isupport.Add("TOPICLEN", strconv.Itoa(config.Limits.TopicLen)) isupport.Add("UTF8MAPPING", casemappingName) // account registration - if server.config.Accounts.Registration.Enabled { + if config.Accounts.Registration.Enabled { // 'none' isn't shown in the REGCALLBACKS vars var enabledCallbacks []string for _, name := range server.config.Accounts.Registration.EnabledCallbacks { @@ -246,8 +215,6 @@ func (server *Server) setISupport() { isupport.Add("REGCREDTYPES", "passphrase,certfp") } - server.configurableStateMutex.RUnlock() - isupport.RegenerateCachedReply() server.configurableStateMutex.Lock() @@ -672,7 +639,7 @@ func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) { rb.Add(nil, client.server.name, RPL_WHOISACCOUNT, client.nick, target.AccountName(), client.t("is logged in as")) } if target.HasMode(modes.Bot) { - rb.Add(nil, client.server.name, RPL_WHOISBOT, client.nick, target.nick, ircfmt.Unescape(fmt.Sprintf(client.t("is a $bBot$b on %s"), client.server.networkName))) + rb.Add(nil, client.server.name, RPL_WHOISBOT, client.nick, target.nick, ircfmt.Unescape(fmt.Sprintf(client.t("is a $bBot$b on %s"), client.server.Config().Network.Name))) } if 0 < len(target.languages) { @@ -744,13 +711,16 @@ func (server *Server) rehash() error { return nil } -func (server *Server) applyConfig(config *Config, initial bool) error { +func (server *Server) applyConfig(config *Config, initial bool) (err error) { if initial { server.ctime = time.Now() server.configFilename = config.Filename + server.name = config.Server.Name + server.nameCasefolded = config.Server.nameCasefolded } else { // enforce configs that can't be changed after launch: - if server.limits.LineLen.Tags != config.Limits.LineLen.Tags || server.limits.LineLen.Rest != config.Limits.LineLen.Rest { + currentLimits := server.Limits() + if currentLimits.LineLen.Tags != config.Limits.LineLen.Tags || currentLimits.LineLen.Rest != config.Limits.LineLen.Rest { return fmt.Errorf("Maximum line length (linelen) cannot be changed after launching the server, rehash aborted") } else if server.name != config.Server.Name { return fmt.Errorf("Server name cannot be changed after launching the server, rehash aborted") @@ -759,48 +729,11 @@ func (server *Server) applyConfig(config *Config, initial bool) error { } } - server.logger.Info("rehash", "Using config file", server.configFilename) - - casefoldedName, err := Casefold(config.Server.Name) - if err != nil { - return fmt.Errorf("Server name isn't valid [%s]: %s", config.Server.Name, err.Error()) - } - - // confirm operator stuff all exists and is fine - operclasses, err := config.OperatorClasses() - if err != nil { - return fmt.Errorf("Error rehashing config file operclasses: %s", err.Error()) - } - opers, err := config.Operators(operclasses) - if err != nil { - return fmt.Errorf("Error rehashing config file opers: %s", err.Error()) - } - - // TODO: support rehash of existing operator perms? - // sanity checks complete, start modifying server state + server.logger.Info("rehash", "Using config file", server.configFilename) + oldConfig := server.Config() - if initial { - server.name = config.Server.Name - server.nameCasefolded = casefoldedName - } - - server.configurableStateMutex.Lock() - server.networkName = config.Network.Name - if config.Server.Password != "" { - server.password = config.Server.PasswordBytes() - } else { - server.password = nil - } - // apply new WebIRC command restrictions - server.webirc = config.Server.WebIRC - // apply new PROXY command restrictions - server.proxyAllowedFrom = config.Server.ProxyAllowedFrom - server.recoverFromErrors = true - if config.Debug.RecoverFromErrors != nil { - server.recoverFromErrors = *config.Debug.RecoverFromErrors - } - server.configurableStateMutex.Unlock() + // first, reload config sections for functionality implemented in subpackages: err = server.connectionLimiter.ApplyConfig(config.Server.ConnectionLimiter) if err != nil { @@ -812,6 +745,16 @@ func (server *Server) applyConfig(config *Config, initial bool) error { return err } + // reload logging config + wasLoggingRawIO := !initial && server.logger.IsLoggingRawIO() + err = server.logger.ApplyConfig(config.Logging) + if err != nil { + return err + } + nowLoggingRawIO := server.logger.IsLoggingRawIO() + // notify existing clients if raw i/o logging was enabled by a rehash + sendRawOutputNotice := !wasLoggingRawIO && nowLoggingRawIO + // setup new and removed caps addedCaps := caps.NewSet() removedCaps := caps.NewSet() @@ -844,8 +787,7 @@ func (server *Server) applyConfig(config *Config, initial bool) error { server.languages = lm // SASL - oldAccountConfig := server.AccountConfig() - authPreviouslyEnabled := oldAccountConfig != nil && oldAccountConfig.AuthenticationEnabled + authPreviouslyEnabled := oldConfig != nil && oldConfig.Accounts.AuthenticationEnabled if config.Accounts.AuthenticationEnabled && !authPreviouslyEnabled { // enabling SASL SupportedCapabilities.Enable(caps.SASL) @@ -857,39 +799,39 @@ func (server *Server) applyConfig(config *Config, initial bool) error { removedCaps.Add(caps.SASL) } - nickReservationPreviouslyDisabled := oldAccountConfig != nil && !oldAccountConfig.NickReservation.Enabled + nickReservationPreviouslyDisabled := oldConfig != nil && !oldConfig.Accounts.NickReservation.Enabled nickReservationNowEnabled := config.Accounts.NickReservation.Enabled if nickReservationPreviouslyDisabled && nickReservationNowEnabled { server.accounts.buildNickToAccountIndex() } - hsPreviouslyDisabled := oldAccountConfig != nil && !oldAccountConfig.VHosts.Enabled + hsPreviouslyDisabled := oldConfig != nil && !oldConfig.Accounts.VHosts.Enabled hsNowEnabled := config.Accounts.VHosts.Enabled if hsPreviouslyDisabled && hsNowEnabled { server.accounts.initVHostRequestQueue() } // STS + stsPreviouslyEnabled := oldConfig != nil && oldConfig.Server.STS.Enabled stsValue := config.Server.STS.Value() - var stsDisabled bool + stsDisabledByRehash := false stsCurrentCapValue, _ := CapValues.Get(caps.STS) - server.logger.Debug("rehash", "STS Vals", stsCurrentCapValue, stsValue, fmt.Sprintf("server[%v] config[%v]", server.stsEnabled, config.Server.STS.Enabled)) - if config.Server.STS.Enabled && !server.stsEnabled { + server.logger.Debug("rehash", "STS Vals", stsCurrentCapValue, stsValue, fmt.Sprintf("server[%v] config[%v]", stsPreviouslyEnabled, config.Server.STS.Enabled)) + if config.Server.STS.Enabled && !stsPreviouslyEnabled { // enabling STS SupportedCapabilities.Enable(caps.STS) addedCaps.Add(caps.STS) CapValues.Set(caps.STS, stsValue) - } else if !config.Server.STS.Enabled && server.stsEnabled { + } else if !config.Server.STS.Enabled && stsPreviouslyEnabled { // disabling STS SupportedCapabilities.Disable(caps.STS) removedCaps.Add(caps.STS) - stsDisabled = true - } else if config.Server.STS.Enabled && server.stsEnabled && stsValue != stsCurrentCapValue { + stsDisabledByRehash = true + } else if config.Server.STS.Enabled && stsPreviouslyEnabled && stsValue != stsCurrentCapValue { // STS policy updated CapValues.Set(caps.STS, stsValue) updatedCaps.Add(caps.STS) } - server.stsEnabled = config.Server.STS.Enabled // burst new and removed caps var capBurstClients ClientSet @@ -912,7 +854,7 @@ func (server *Server) applyConfig(config *Config, initial bool) error { } for sClient := range capBurstClients { - if stsDisabled { + if stsDisabledByRehash { // remove STS policy //TODO(dan): this is an ugly hack. we can write this better. stsPolicy := "sts=duration=0" @@ -932,44 +874,8 @@ func (server *Server) applyConfig(config *Config, initial bool) error { } } - // set server options - server.configurableStateMutex.Lock() - lineLenConfig := LineLenLimits{ - Tags: config.Limits.LineLen.Tags, - Rest: config.Limits.LineLen.Rest, - } - server.limits = Limits{ - AwayLen: int(config.Limits.AwayLen), - ChannelLen: int(config.Limits.ChannelLen), - KickLen: int(config.Limits.KickLen), - MonitorEntries: int(config.Limits.MonitorEntries), - NickLen: int(config.Limits.NickLen), - TopicLen: int(config.Limits.TopicLen), - ChanListModes: int(config.Limits.ChanListModes), - LineLen: lineLenConfig, - } - server.operclasses = operclasses - server.operators = opers - server.checkIdent = config.Server.CheckIdent - - server.defaultChannelModes = ParseDefaultChannelModes(config) - server.configurableStateMutex.Unlock() - - // set new sendqueue size - server.SetMaxSendQBytes(config.Server.MaxSendQBytes) - server.loadMOTD(config.Server.MOTD, config.Server.MOTDFormatting) - // reload logging config - err = server.logger.ApplyConfig(config.Logging) - if err != nil { - return err - } - nowLoggingRawIO := server.logger.IsLoggingRawIO() - // notify clients if raw i/o logging was enabled by a rehash - sendRawOutputNotice := !initial && !server.loggingRawIO && nowLoggingRawIO - server.loggingRawIO = nowLoggingRawIO - // save a pointer to the new config server.configurableStateMutex.Lock() server.config = config diff --git a/irc/utils/bitset.go b/irc/utils/bitset.go index e5358f68..e2d8ccf1 100644 --- a/irc/utils/bitset.go +++ b/irc/utils/bitset.go @@ -52,11 +52,9 @@ func BitsetSet(set []uint64, position uint, on bool) (changed bool) { } // BitsetEmpty returns whether the bitset is empty. -// Right now, this is technically free of race conditions because we don't -// have a method that can simultaneously modify two bits separated by a word boundary -// such that one of those modifications is an unset. If we did, there would be a race -// that could produce false positives. It's probably better to assume that they are -// already possible under concurrent modification (which is not how we're using this). +// This has false positives under concurrent modification (i.e., it can return true +// even though w.r.t. the sequence of atomic modifications, there was no point at +// which the bitset was completely empty), but that's not how we're using this method. func BitsetEmpty(set []uint64) (empty bool) { for i := 0; i < len(set); i++ { if atomic.LoadUint64(&set[i]) != 0 { diff --git a/irc/whowas.go b/irc/whowas.go index 7ad3756f..3ecf870d 100644 --- a/irc/whowas.go +++ b/irc/whowas.go @@ -32,7 +32,7 @@ type WhoWas struct { } // NewWhoWasList returns a new WhoWasList -func NewWhoWasList(size uint) *WhoWasList { +func NewWhoWasList(size int) *WhoWasList { return &WhoWasList{ buffer: make([]WhoWas, size), start: -1,