diff --git a/core/utils.go b/core/utils.go new file mode 100644 index 0000000..9f223e2 --- /dev/null +++ b/core/utils.go @@ -0,0 +1,37 @@ +package core + +import ( + "context" + "log/slog" + "time" +) + +func StartTimeWatchDog(ctx context.Context, logger *slog.Logger) <-chan struct{} { + logger.Info("starting watchdog") + ch := make(chan struct{}, 1) + go func() { + lastUnix := time.Now().Unix() + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + nowUnix := time.Now().Unix() + diff := nowUnix - lastUnix + if diff > 300 { + logger.Warn("system time jump detected(wake up from sleep?)", + slog.Int64("jump_seconds", diff), + ) + ch <- struct{}{} + logger.Debug("signal sent, watchdog exiting...") + return + } else { + lastUnix = nowUnix + } + } + } + }() + return ch +} diff --git a/main.go b/main.go index 612ea60..93124b7 100644 --- a/main.go +++ b/main.go @@ -11,16 +11,8 @@ import ( "tslink/core" ) -func main() { - useJsonFormatLogger := flag.Bool("json-format", false, "use json format logger") - logLevel := flag.String("level", "info", "log level (DEBUG|INFO|WARN|ERROR)") - configPath := flag.String("c", "config.toml", "path to config file") - flag.Parse() - - logger := core.NewLogger(*logLevel, *useJsonFormatLogger) - - logger.Info("Starting tslink server", "level", *logLevel, "configPath", *configPath) - cfg, err := core.LoadConfig(*configPath) +func serviceLogic(configPath string, logger *slog.Logger) bool { + cfg, err := core.LoadConfig(configPath) if err != nil { logger.With( slog.String("error", err.Error()), @@ -28,7 +20,7 @@ func main() { os.Exit(1) } - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancelAll := context.WithCancel(context.Background()) logger.Info("initializing tsnet server") srv, err := core.InitTsNet(ctx, &cfg.Core, logger) if err != nil { @@ -41,17 +33,50 @@ func main() { core.StartForwarders(ctx, srv, cfg.Forward) core.StartConnectors(ctx, srv, cfg.Connect) - core.RunLanDiscoverService(ctx, cfg.Connect, logger) + core.RunLanDiscoverService(ctx, cfg.Connect, logger.With("from", "lan_service")) sigHandler := make(chan os.Signal, 1) signal.Notify(sigHandler, os.Interrupt, syscall.SIGTERM) + watchDog := core.StartTimeWatchDog(ctx, logger.With("from", "watchdog")) + + go func() { + <-ctx.Done() + logger.Debug("stopping tsnet service") + srv.Close() + }() + for { select { case <-sigHandler: - logger.Warn("Shutting down") - cancel() + logger.Warn("Ctrl+C received, shutting down") + cancelAll() time.Sleep(time.Second) + return true + case <-watchDog: + logger.Warn("WatchDog trigged, restarting service") + cancelAll() + time.Sleep(time.Second) + return false + } + } +} + +func main() { + useJsonFormatLogger := flag.Bool("json-format", false, "use json format logger") + logLevel := flag.String("level", "info", "log level (DEBUG|INFO|WARN|ERROR)") + configPath := flag.String("c", "config.toml", "path to config file") + flag.Parse() + + logger := core.NewLogger(*logLevel, *useJsonFormatLogger) + + logger.Info("Starting tslink server", "level", logLevel, "configPath", configPath) + + for { + isStopped := serviceLogic(*configPath, logger) + if !isStopped { + logger.Warn("tslink server restart") + } else { return } }