227 lines
4.4 KiB
Go
227 lines
4.4 KiB
Go
package app
|
|
|
|
import (
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"runtime/debug"
|
|
"strings"
|
|
|
|
"github.com/AlexxIT/go2rtc/pkg/shell"
|
|
"github.com/AlexxIT/go2rtc/pkg/yaml"
|
|
"github.com/rs/zerolog"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
var Version = "1.9.2"
|
|
var UserAgent = "go2rtc/" + Version
|
|
|
|
var ConfigPath string
|
|
var Info = map[string]any{
|
|
"version": Version,
|
|
}
|
|
|
|
const usage = `Usage of go2rtc:
|
|
|
|
-c, --config Path to config file or config string as YAML or JSON, support multiple
|
|
-d, --daemon Run in background
|
|
-v, --version Print version and exit
|
|
`
|
|
|
|
func Init() {
|
|
var confs Config
|
|
var daemon bool
|
|
var version bool
|
|
|
|
flag.Var(&confs, "config", "")
|
|
flag.Var(&confs, "c", "")
|
|
flag.BoolVar(&daemon, "daemon", false, "")
|
|
flag.BoolVar(&daemon, "d", false, "")
|
|
flag.BoolVar(&version, "version", false, "")
|
|
flag.BoolVar(&version, "v", false, "")
|
|
|
|
flag.Usage = func() { fmt.Print(usage) }
|
|
flag.Parse()
|
|
|
|
revision, vcsTime := readRevisionTime()
|
|
|
|
if version {
|
|
fmt.Printf("go2rtc version %s (%s) %s/%s\n", Version, revision, runtime.GOOS, runtime.GOARCH)
|
|
os.Exit(0)
|
|
}
|
|
|
|
if daemon {
|
|
if runtime.GOOS == "windows" {
|
|
fmt.Println("Daemon not supported on Windows")
|
|
os.Exit(1)
|
|
}
|
|
|
|
args := os.Args[1:]
|
|
for i, arg := range args {
|
|
if arg == "-daemon" {
|
|
args[i] = ""
|
|
}
|
|
}
|
|
// Re-run the program in background and exit
|
|
cmd := exec.Command(os.Args[0], args...)
|
|
if err := cmd.Start(); err != nil {
|
|
log.Fatal().Err(err).Send()
|
|
}
|
|
fmt.Println("Running in daemon mode with PID:", cmd.Process.Pid)
|
|
os.Exit(0)
|
|
}
|
|
|
|
if confs == nil {
|
|
confs = []string{"go2rtc.yaml"}
|
|
}
|
|
|
|
for _, conf := range confs {
|
|
if len(conf) == 0 {
|
|
continue
|
|
}
|
|
if conf[0] == '{' {
|
|
// config as raw YAML or JSON
|
|
configs = append(configs, []byte(conf))
|
|
} else if data := parseConfString(conf); data != nil {
|
|
configs = append(configs, data)
|
|
} else {
|
|
// config as file
|
|
if ConfigPath == "" {
|
|
ConfigPath = conf
|
|
}
|
|
|
|
if data, _ = os.ReadFile(conf); data == nil {
|
|
continue
|
|
}
|
|
|
|
data = []byte(shell.ReplaceEnvVars(string(data)))
|
|
configs = append(configs, data)
|
|
}
|
|
}
|
|
|
|
if ConfigPath != "" {
|
|
if !filepath.IsAbs(ConfigPath) {
|
|
if cwd, err := os.Getwd(); err == nil {
|
|
ConfigPath = filepath.Join(cwd, ConfigPath)
|
|
}
|
|
}
|
|
Info["config_path"] = ConfigPath
|
|
}
|
|
|
|
Info["revision"] = revision
|
|
|
|
var cfg struct {
|
|
Mod map[string]string `yaml:"log"`
|
|
}
|
|
|
|
cfg.Mod = map[string]string{
|
|
"format": "", // useless, but anyway
|
|
"level": "info",
|
|
"output": "stdout", // TODO: change to stderr someday
|
|
"time": zerolog.TimeFormatUnixMs,
|
|
}
|
|
|
|
LoadConfig(&cfg)
|
|
|
|
log.Logger = NewLogger(cfg.Mod)
|
|
|
|
modules = cfg.Mod
|
|
|
|
platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
|
|
log.Info().Str("version", Version).Str("platform", platform).Str("revision", revision).Msg("go2rtc")
|
|
log.Debug().Str("version", runtime.Version()).Str("vcs.time", vcsTime).Msg("build")
|
|
|
|
if ConfigPath != "" {
|
|
log.Info().Str("path", ConfigPath).Msg("config")
|
|
}
|
|
|
|
migrateStore()
|
|
}
|
|
|
|
func LoadConfig(v any) {
|
|
for _, data := range configs {
|
|
if err := yaml.Unmarshal(data, v); err != nil {
|
|
log.Warn().Err(err).Msg("[app] read config")
|
|
}
|
|
}
|
|
}
|
|
|
|
func PatchConfig(key string, value any, path ...string) error {
|
|
if ConfigPath == "" {
|
|
return errors.New("config file disabled")
|
|
}
|
|
|
|
// empty config is OK
|
|
b, _ := os.ReadFile(ConfigPath)
|
|
|
|
b, err := yaml.Patch(b, key, value, path...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return os.WriteFile(ConfigPath, b, 0644)
|
|
}
|
|
|
|
// internal
|
|
|
|
type Config []string
|
|
|
|
func (c *Config) String() string {
|
|
return strings.Join(*c, " ")
|
|
}
|
|
|
|
func (c *Config) Set(value string) error {
|
|
*c = append(*c, value)
|
|
return nil
|
|
}
|
|
|
|
var configs [][]byte
|
|
|
|
func readRevisionTime() (revision, vcsTime string) {
|
|
if info, ok := debug.ReadBuildInfo(); ok {
|
|
for _, setting := range info.Settings {
|
|
switch setting.Key {
|
|
case "vcs.revision":
|
|
if len(setting.Value) > 7 {
|
|
revision = setting.Value[:7]
|
|
} else {
|
|
revision = setting.Value
|
|
}
|
|
case "vcs.time":
|
|
vcsTime = setting.Value
|
|
case "vcs.modified":
|
|
if setting.Value == "true" {
|
|
revision = "mod." + revision
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func parseConfString(s string) []byte {
|
|
i := strings.IndexByte(s, '=')
|
|
if i < 0 {
|
|
return nil
|
|
}
|
|
|
|
items := strings.Split(s[:i], ".")
|
|
if len(items) < 2 {
|
|
return nil
|
|
}
|
|
|
|
// `log.level=trace` => `{log: {level: trace}}`
|
|
var pre string
|
|
var suf = s[i+1:]
|
|
for _, item := range items {
|
|
pre += "{" + item + ": "
|
|
suf += "}"
|
|
}
|
|
|
|
return []byte(pre + suf)
|
|
}
|