164 lines
3.5 KiB
Go
Raw Normal View History

2025-02-13 21:08:11 +03:00
package main
import (
"context"
"errors"
"fmt"
"os"
"os/signal"
"strconv"
"sync"
"syscall"
2025-02-14 17:16:20 +03:00
"magitrickle"
"magitrickle/constant"
"magitrickle/models"
2025-02-13 21:08:11 +03:00
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"gopkg.in/yaml.v3"
)
2025-02-14 17:16:20 +03:00
const cfgFolderLocation = "/opt/var/lib/magitrickle"
2025-02-13 21:08:11 +03:00
const cfgFileLocation = cfgFolderLocation + "/config.yaml"
2025-02-14 17:16:20 +03:00
const pidFileLocation = "/opt/var/run/magitrickle.pid"
2025-02-13 21:08:11 +03:00
func checkPIDFile() error {
data, err := os.ReadFile(pidFileLocation)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
pid, err := strconv.Atoi(string(data))
if err != nil {
return errors.New("invalid PID file content")
}
if err := syscall.Kill(pid, 0); err == nil {
return fmt.Errorf("process %d is already running", pid)
}
_ = os.Remove(pidFileLocation)
return nil
}
func createPIDFile() error {
pid := os.Getpid()
return os.WriteFile(pidFileLocation, []byte(strconv.Itoa(pid)), 0644)
}
func removePIDFile() {
_ = os.Remove(pidFileLocation)
}
func main() {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
log.Info().
Str("version", constant.Version).
Str("commit", constant.Commit).
2025-02-14 17:16:20 +03:00
Msg("starting MagiTrickle daemon")
2025-02-13 21:08:11 +03:00
if err := checkPIDFile(); err != nil {
2025-02-14 17:16:20 +03:00
log.Fatal().Err(err).Msg("failed to start MagiTrickle daemon")
2025-02-13 21:08:11 +03:00
}
if err := createPIDFile(); err != nil {
log.Fatal().Err(err).Msg("failed to create PID file")
}
defer removePIDFile()
2025-02-14 03:17:43 +03:00
cfg := models.Config{}
2025-02-13 21:08:11 +03:00
cfgFile, err := os.ReadFile(cfgFileLocation)
2025-02-14 03:17:43 +03:00
if err != nil {
2025-02-13 21:08:11 +03:00
if !errors.Is(err, os.ErrNotExist) {
log.Fatal().Err(err).Msg("failed to read config.yaml")
}
2025-02-14 03:17:43 +03:00
cfg = models.Config{
ConfigVersion: "0.1.0",
2025-02-14 17:16:20 +03:00
App: magitrickle.DefaultAppConfig,
2025-02-13 21:08:11 +03:00
}
out, err := yaml.Marshal(cfg)
if err != nil {
log.Fatal().Err(err).Msg("failed to serialize config.yaml")
}
err = os.MkdirAll(cfgFolderLocation, os.ModePerm)
if err != nil {
log.Fatal().Err(err).Msg("failed to create config directory")
}
err = os.WriteFile(cfgFileLocation, out, 0600)
if err != nil {
log.Fatal().Err(err).Msg("failed to save config.yaml")
}
2025-02-14 03:17:43 +03:00
} else {
err = yaml.Unmarshal(cfgFile, &cfg)
if err != nil {
log.Fatal().Err(err).Msg("failed to parse config.yaml")
}
2025-02-13 21:08:11 +03:00
}
2025-02-14 03:17:43 +03:00
switch cfg.App.LogLevel {
2025-02-13 21:08:11 +03:00
case "trace":
zerolog.SetGlobalLevel(zerolog.TraceLevel)
case "debug":
zerolog.SetGlobalLevel(zerolog.DebugLevel)
case "info":
zerolog.SetGlobalLevel(zerolog.InfoLevel)
case "warn":
zerolog.SetGlobalLevel(zerolog.WarnLevel)
case "error":
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
case "fatal":
zerolog.SetGlobalLevel(zerolog.FatalLevel)
case "panic":
zerolog.SetGlobalLevel(zerolog.PanicLevel)
case "nolevel":
zerolog.SetGlobalLevel(zerolog.NoLevel)
case "disabled":
zerolog.SetGlobalLevel(zerolog.Disabled)
default:
zerolog.SetGlobalLevel(zerolog.InfoLevel)
}
2025-02-14 17:16:20 +03:00
app := magitrickle.New()
2025-02-14 03:17:43 +03:00
err = app.ImportConfig(cfg)
2025-02-13 21:08:11 +03:00
if err != nil {
2025-02-14 03:17:43 +03:00
log.Fatal().Err(err).Msg("failed to import config")
2025-02-13 21:08:11 +03:00
}
log.Info().Msg("starting service")
/*
Starting app with graceful shutdown
*/
2025-02-14 03:17:43 +03:00
ctx, cancel := context.WithCancel(context.Background())
2025-02-13 21:08:11 +03:00
appResult := make(chan error)
go func() {
appResult <- app.Start(ctx)
}()
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
var once sync.Once
closeEvent := func() {
log.Info().Msg("shutting down service")
cancel()
}
for {
select {
case err, _ := <-appResult:
if err != nil {
log.Error().Err(err).Msg("failed to start application")
}
log.Info().Msg("exiting application")
return
case <-c:
once.Do(closeEvent)
}
}
}