From f2f417e451234bb01cc359a63d39ce25936d77bb Mon Sep 17 00:00:00 2001 From: Alessandro Ros Date: Sat, 16 Sep 2023 22:14:13 +0200 Subject: [PATCH] search for configuration file in various paths, print paths if configuration is not found (#1993) (#2276) (#2357) --- internal/conf/conf.go | 52 ++++++++++++++++++++------------------ internal/conf/conf_test.go | 24 +++++++++--------- internal/core/core.go | 34 +++++++++++++++++++------ 3 files changed, 65 insertions(+), 45 deletions(-) diff --git a/internal/conf/conf.go b/internal/conf/conf.go index 8a145eda..0bfaf774 100644 --- a/internal/conf/conf.go +++ b/internal/conf/conf.go @@ -4,7 +4,6 @@ package conf import ( "bytes" "encoding/json" - "errors" "fmt" "os" "sort" @@ -32,50 +31,53 @@ func getSortedKeys(paths map[string]*PathConf) []string { return ret } -func loadFromFile(fpath string, conf *Conf) (bool, error) { - if fpath == "mediamtx.yml" { - // give priority to the legacy configuration file, in order not to break - // existing setups - if _, err := os.Stat("rtsp-simple-server.yml"); err == nil { - fpath = "rtsp-simple-server.yml" +func firstThatExists(paths []string) string { + for _, pa := range paths { + _, err := os.Stat(pa) + if err == nil { + return pa } } + return "" +} - // mediamtx.yml is optional - // other configuration files are not - if fpath == "mediamtx.yml" || fpath == "rtsp-simple-server.yml" { - if _, err := os.Stat(fpath); errors.Is(err, os.ErrNotExist) { - // load defaults +func loadFromFile(fpath string, defaultConfPaths []string, conf *Conf) (string, error) { + if fpath == "" { + fpath = firstThatExists(defaultConfPaths) + + // when the configuration file is not explicitly set, + // it is optional. Load defaults. + if fpath == "" { conf.UnmarshalJSON(nil) //nolint:errcheck - return false, nil + return "", nil } } byts, err := os.ReadFile(fpath) if err != nil { - return true, err + return "", err } if key, ok := os.LookupEnv("RTSP_CONFKEY"); ok { // legacy format byts, err = decrypt.Decrypt(key, byts) if err != nil { - return true, err + return "", err } } if key, ok := os.LookupEnv("MTX_CONFKEY"); ok { byts, err = decrypt.Decrypt(key, byts) if err != nil { - return true, err + return "", err } } err = yaml.Load(byts, conf) if err != nil { - return true, err + return "", err } - return true, nil + return fpath, nil } func contains(list []headers.AuthMethod, item headers.AuthMethod) bool { @@ -183,30 +185,30 @@ type Conf struct { } // Load loads a Conf. -func Load(fpath string) (*Conf, bool, error) { +func Load(fpath string, defaultConfPaths []string) (*Conf, string, error) { conf := &Conf{} - found, err := loadFromFile(fpath, conf) + fpath, err := loadFromFile(fpath, defaultConfPaths, conf) if err != nil { - return nil, false, err + return nil, "", err } err = env.Load("RTSP", conf) // legacy prefix if err != nil { - return nil, false, err + return nil, "", err } err = env.Load("MTX", conf) if err != nil { - return nil, false, err + return nil, "", err } err = conf.Check() if err != nil { - return nil, false, err + return nil, "", err } - return conf, found, nil + return conf, fpath, nil } // Clone clones the configuration. diff --git a/internal/conf/conf_test.go b/internal/conf/conf_test.go index 1b241a5f..17b65432 100644 --- a/internal/conf/conf_test.go +++ b/internal/conf/conf_test.go @@ -39,9 +39,9 @@ func TestConfFromFile(t *testing.T) { require.NoError(t, err) defer os.Remove(tmpf) - conf, hasFile, err := Load(tmpf) + conf, confPath, err := Load(tmpf, nil) require.NoError(t, err) - require.Equal(t, true, hasFile) + require.Equal(t, tmpf, confPath) require.Equal(t, LogLevel(logger.Debug), conf.LogLevel) @@ -73,7 +73,7 @@ func TestConfFromFile(t *testing.T) { require.NoError(t, err) defer os.Remove(tmpf) - _, _, err = Load(tmpf) + _, _, err = Load(tmpf, nil) require.NoError(t, err) }() @@ -82,7 +82,7 @@ func TestConfFromFile(t *testing.T) { require.NoError(t, err) defer os.Remove(tmpf) - _, _, err = Load(tmpf) + _, _, err = Load(tmpf, nil) require.NoError(t, err) }() @@ -93,7 +93,7 @@ func TestConfFromFile(t *testing.T) { require.NoError(t, err) defer os.Remove(tmpf) - _, _, err = Load(tmpf) + _, _, err = Load(tmpf, nil) require.NoError(t, err) }() } @@ -109,9 +109,9 @@ func TestConfFromFileAndEnv(t *testing.T) { require.NoError(t, err) defer os.Remove(tmpf) - conf, hasFile, err := Load(tmpf) + conf, confPath, err := Load(tmpf, nil) require.NoError(t, err) - require.Equal(t, true, hasFile) + require.Equal(t, tmpf, confPath) require.Equal(t, Protocols{Protocol(gortsplib.TransportTCP): {}}, conf.Protocols) @@ -142,9 +142,9 @@ func TestConfFromEnvOnly(t *testing.T) { os.Setenv("MTX_PATHS_CAM1_SOURCE", "rtsp://testing") defer os.Unsetenv("MTX_PATHS_CAM1_SOURCE") - conf, hasFile, err := Load("mediamtx.yml") + conf, confPath, err := Load("", nil) require.NoError(t, err) - require.Equal(t, false, hasFile) + require.Equal(t, "", confPath) pa, ok := conf.Paths["cam1"] require.Equal(t, true, ok) @@ -194,9 +194,9 @@ func TestConfEncryption(t *testing.T) { require.NoError(t, err) defer os.Remove(tmpf) - conf, hasFile, err := Load(tmpf) + conf, confPath, err := Load(tmpf, nil) require.NoError(t, err) - require.Equal(t, true, hasFile) + require.Equal(t, tmpf, confPath) _, ok := conf.Paths["path1"] require.Equal(t, true, ok) @@ -283,7 +283,7 @@ func TestConfErrors(t *testing.T) { require.NoError(t, err) defer os.Remove(tmpf) - _, _, err = Load(tmpf) + _, _, err = Load(tmpf, nil) require.EqualError(t, err, ca.err) }) } diff --git a/internal/core/core.go b/internal/core/core.go index b01381f0..dde413a0 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -6,7 +6,9 @@ import ( "fmt" "os" "os/signal" + "path/filepath" "reflect" + "strings" "time" "github.com/alecthomas/kong" @@ -23,9 +25,17 @@ import ( var version = "v0.0.0" +var defaultConfPaths = []string{ + "rtsp-simple-server.yml", + "mediamtx.yml", + "/usr/local/etc/mediamtx.yml", + "/usr/etc/mediamtx.yml", + "/etc/mediamtx/mediamtx.yml", +} + var cli struct { Version bool `help:"print version"` - Confpath string `arg:"" default:"mediamtx.yml"` + Confpath string `arg:"" default:""` } // Core is an instance of mediamtx. @@ -34,7 +44,6 @@ type Core struct { ctxCancel func() confPath string conf *conf.Conf - confFound bool logger *logger.Logger externalCmdPool *externalcmd.Pool metrics *metrics @@ -89,12 +98,11 @@ func New(args []string) (*Core, bool) { p := &Core{ ctx: ctx, ctxCancel: ctxCancel, - confPath: cli.Confpath, chAPIConfigSet: make(chan *conf.Conf), done: make(chan struct{}), } - p.conf, p.confFound, err = conf.Load(p.confPath) + p.conf, p.confPath, err = conf.Load(cli.Confpath, defaultConfPaths) if err != nil { fmt.Printf("ERR: %s\n", err) return nil, false @@ -151,7 +159,7 @@ outer: case <-confChanged: p.Log(logger.Info, "reloading configuration (file changed)") - newConf, _, err := conf.Load(p.confPath) + newConf, _, err := conf.Load(p.confPath, nil) if err != nil { p.Log(logger.Error, "%s", err) break outer @@ -202,8 +210,17 @@ func (p *Core) createResources(initial bool) error { if initial { p.Log(logger.Info, "MediaMTX %s", version) - if !p.confFound { - p.Log(logger.Warn, "configuration file not found, using an empty configuration") + + if p.confPath == "" { + list := make([]string, len(defaultConfPaths)) + for i, pa := range defaultConfPaths { + a, _ := filepath.Abs(pa) + list[i] = a + } + + p.Log(logger.Warn, + "configuration file not found (looked in %s), using an empty configuration", + strings.Join(list, ", ")) } // on Linux, try to raise the number of file descriptors that can be opened @@ -490,7 +507,7 @@ func (p *Core) createResources(initial bool) error { } } - if initial && p.confFound { + if initial && p.confPath != "" { p.confWatcher, err = confwatcher.New(p.confPath) if err != nil { return err @@ -505,6 +522,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) { newConf.LogLevel != p.conf.LogLevel || !reflect.DeepEqual(newConf.LogDestinations, p.conf.LogDestinations) || newConf.LogFile != p.conf.LogFile + closeMetrics := newConf == nil || newConf.Metrics != p.conf.Metrics || newConf.MetricsAddress != p.conf.MetricsAddress ||