package main import ( "bufio" "context" "crypto/tls" "os" "sync" "github.com/jackc/pgx/v5/pgxpool" "go.lindenii.runxiyu.org/lindenii-common/scfg" ) var config struct { Server_name string `scfg:"server_name"` TLS struct { Cert string `scfg:"cert"` Key string `scfg:"key"` } `scfg:"tls"` DB struct { Type string `scfg:"type"` Conn string `scfg:"conn"` } `scfg:"db"` MX struct { Net string `scfg:"net"` Addr string `scfg:"addr"` } `scfg:"mx"` IMAP struct { Net string `scfg:"net"` Addr string `scfg:"addr"` } `scfg:"imap"` _tls_config *tls.Config } var ( config_mutex sync.RWMutex // covers things like the database too config_context context.Context config_context_cancel context.CancelFunc global_db *pgxpool.Pool // only call Close() after replacing this global variable ) // load_config loads the configuration file and sets up global things according // to the configuration directives. func load_config(path string) error { config_file, err := os.Open(path) if err != nil { return err } decoder := scfg.NewDecoder(bufio.NewReader(config_file)) if func() error { config_mutex.Lock() defer config_mutex.Unlock() err = decoder.Decode(&config) if err != nil { return err } config_context, config_context_cancel = context.WithCancel(context.Background()) // TLS key loading and TLS config creation cer, err := tls.LoadX509KeyPair(config.TLS.Cert, config.TLS.Key) if err != nil { return err } config._tls_config = &tls.Config{ Certificates: []tls.Certificate{cer}, MinVersion: tls.VersionTLS13, } // Database setup if config.DB.Type != "postgres" { return err_unsupported_database_type } global_db, err = pgxpool.New(config_context, config.DB.Conn) // BUG: Context-related leak: cancel context when the config is invalidated if err != nil { return err } return nil }() != nil { return err } return nil } // config_fetch_one fetches one value from the configuration. // // Consecutive calls do not guarantee a consistent snapshot of the // configuration. Use config_consistent_run in these cases. func config_fetch_one[T any](x *T) T { config_mutex.RLock() defer config_mutex.RUnlock() return *x } // config_consistent_run runs the supplied function with a consistent snapshot // of values covered by config_mutex. func config_consistent_run(f func()) { config_mutex.RLock() defer config_mutex.RUnlock() f() }