256 lines
7.2 KiB
Go
256 lines
7.2 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
spm "weforge.xyz/Spitfire/SPM"
|
|
)
|
|
|
|
// -------------------------------------------------------------------
|
|
// GLOBALS
|
|
// -------------------------------------------------------------------
|
|
var (
|
|
copyingInProgress int32
|
|
updateStatusMsg string
|
|
browserCmd *exec.Cmd
|
|
browserMutex sync.Mutex // Protects the lock file map
|
|
isBackgroundMode bool
|
|
)
|
|
|
|
// StateFile holds the update state from our INI
|
|
type StateFile struct {
|
|
IsUpdating bool
|
|
Progress int32
|
|
Stage string // "Idle","Downloading","Decompressing","ReadyToInstall","Installing","Complete","Failed"
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
// LOG INIT
|
|
// -------------------------------------------------------------------
|
|
func init() {
|
|
log.SetFlags(0)
|
|
log.SetPrefix("[UPDATER] ")
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
// INI Helpers
|
|
// -------------------------------------------------------------------
|
|
func getSpmDir() string {
|
|
installDir, err := spm.GetDefaultInstallDir()
|
|
if err != nil {
|
|
log.Println("Warning: Using '.' for spmDir =>", err)
|
|
return "."
|
|
}
|
|
spmDir := filepath.Join(installDir, "spm")
|
|
_ = os.MkdirAll(spmDir, 0755)
|
|
return spmDir
|
|
}
|
|
|
|
func getStateFilePath() string {
|
|
return filepath.Join(getSpmDir(), "update_state.ini")
|
|
}
|
|
|
|
func WriteState(isUpdating bool, progress int32, stage string) {
|
|
content := "[UpdateState]\n" +
|
|
fmt.Sprintf("IsUpdating=%v\n", isUpdating) +
|
|
fmt.Sprintf("Progress=%d\n", progress) +
|
|
fmt.Sprintf("Stage=%s\n", stage)
|
|
_ = os.WriteFile(getStateFilePath(), []byte(content), 0644)
|
|
}
|
|
|
|
func ReadState() StateFile {
|
|
sf := StateFile{}
|
|
data, err := os.ReadFile(getStateFilePath())
|
|
if err != nil {
|
|
return sf
|
|
}
|
|
|
|
lines := strings.Split(string(data), "\n")
|
|
for _, line := range lines {
|
|
line = strings.TrimSpace(line)
|
|
if line == "" || strings.HasPrefix(line, "[") {
|
|
continue
|
|
}
|
|
parts := strings.SplitN(line, "=", 2)
|
|
if len(parts) != 2 {
|
|
continue
|
|
}
|
|
key := strings.TrimSpace(parts[0])
|
|
val := strings.TrimSpace(parts[1])
|
|
switch key {
|
|
case "IsUpdating":
|
|
sf.IsUpdating = (val == "true")
|
|
case "Progress":
|
|
if i, errAtoi := strconv.Atoi(val); errAtoi == nil {
|
|
sf.Progress = int32(i)
|
|
}
|
|
case "Stage":
|
|
sf.Stage = val
|
|
}
|
|
}
|
|
return sf
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
// BROWSER
|
|
// -------------------------------------------------------------------
|
|
// LaunchBrowser starts the browser and creates a unique lock file in the spmdir.
|
|
func LaunchBrowser() {
|
|
spmDir := getSpmDir()
|
|
|
|
// Generate a unique lock file name (based on timestamp)
|
|
lockFileName := fmt.Sprintf("browser_lock_%d.lock", time.Now().UnixNano())
|
|
lockFilePath := filepath.Join(spmDir, lockFileName)
|
|
|
|
// Create the lock file
|
|
file, err := os.Create(lockFilePath)
|
|
if err != nil {
|
|
fmt.Println("Failed to create lock file:", err)
|
|
return
|
|
}
|
|
file.Close()
|
|
|
|
// Run the browser process and wait for it to finish
|
|
fmt.Println("Starting browser...")
|
|
err = spm.RunAndWait()
|
|
if err != nil {
|
|
fmt.Println("Browser process encountered an error:", err)
|
|
}
|
|
|
|
// Remove the lock file after the browser finishes
|
|
fmt.Printf("Removing lock file: %s\n", lockFilePath)
|
|
err = os.Remove(lockFilePath)
|
|
if err != nil {
|
|
fmt.Println("Failed to remove lock file:", err)
|
|
} else {
|
|
fmt.Println("Lock file removed successfully.")
|
|
}
|
|
}
|
|
|
|
// IsBrowserRunning checks if there are any browser lock files in the spmdir.
|
|
func IsBrowserRunning() bool {
|
|
spmDir := getSpmDir()
|
|
|
|
// Look for lock files in the spmdir
|
|
lockFiles, err := filepath.Glob(filepath.Join(spmDir, "browser_lock_*.lock"))
|
|
if err != nil {
|
|
fmt.Println("Error checking for lock files:", err)
|
|
return false
|
|
}
|
|
|
|
// Return true if at least one lock file is found
|
|
return len(lockFiles) > 0
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
// BACKGROUND SERVICE
|
|
// -------------------------------------------------------------------
|
|
func runBackgroundUpdater() {
|
|
// Delete the update_state.ini file on startup
|
|
stateFilePath := getStateFilePath()
|
|
if _, err := os.Stat(stateFilePath); err == nil {
|
|
err = os.Remove(stateFilePath)
|
|
if err != nil {
|
|
log.Printf("Failed to delete state file %s: %v\n", stateFilePath, err)
|
|
} else {
|
|
log.Printf("Deleted state file: %s\n", stateFilePath)
|
|
}
|
|
} else {
|
|
log.Printf("State file does not exist, no need to delete: %s\n", stateFilePath)
|
|
}
|
|
|
|
// Remove all lock files in the spmdir
|
|
spmDir := getSpmDir()
|
|
lockFilePattern := filepath.Join(spmDir, "browser_lock_*.lock")
|
|
lockFiles, err := filepath.Glob(lockFilePattern)
|
|
if err != nil {
|
|
log.Printf("Failed to scan for lock files in %s: %v\n", spmDir, err)
|
|
} else {
|
|
for _, lockFile := range lockFiles {
|
|
err := os.Remove(lockFile)
|
|
if err != nil {
|
|
log.Printf("Failed to delete lock file %s: %v\n", lockFile, err)
|
|
} else {
|
|
log.Printf("Deleted lock file: %s\n", lockFile)
|
|
}
|
|
}
|
|
}
|
|
|
|
log.Println("Background updater started (no GUI).")
|
|
|
|
// Start a separate goroutine to periodically update the progress in update_state.ini
|
|
go func() {
|
|
for {
|
|
// Get current progress and task from spm
|
|
progress, task := spm.GetProgress()
|
|
|
|
// Determine if the updater is in "Copying files to install directory" stage
|
|
isUpdating := task == "Copying files to install directory"
|
|
|
|
// Write the state to the update_state.ini file
|
|
WriteState(isUpdating, int32(progress), task)
|
|
|
|
// Sleep for 1 hour before updating the progress again
|
|
time.Sleep(1 * time.Hour)
|
|
}
|
|
}()
|
|
|
|
// Main loop for periodic AutoDownloadUpdates and AutoInstallUpdates
|
|
for {
|
|
// Run AutoDownloadUpdates every 10 seconds
|
|
err := spm.AutoDownloadUpdates()
|
|
if err != nil {
|
|
log.Printf("AutoDownloadUpdates failed: %v\n", err)
|
|
WriteState(false, 0, fmt.Sprintf("Update failed: %v", err))
|
|
time.Sleep(10 * time.Second)
|
|
continue
|
|
}
|
|
|
|
// Check if the browser is running
|
|
if IsBrowserRunning() {
|
|
log.Println("Browser is running. Waiting before installing updates.")
|
|
time.Sleep(10 * time.Second)
|
|
continue
|
|
}
|
|
|
|
// If the browser is not running, run AutoInstallUpdates
|
|
log.Println("\nBrowser is not running. Starting installation of updates.")
|
|
err = spm.AutoInstallUpdates()
|
|
if err != nil {
|
|
log.Printf("AutoInstallUpdates failed: %v\n", err)
|
|
WriteState(false, 0, fmt.Sprintf("Installation failed: %v", err))
|
|
time.Sleep(10 * time.Second)
|
|
continue
|
|
}
|
|
|
|
// Write to update_state.ini indicating updates are complete
|
|
WriteState(false, 100, "Complete")
|
|
log.Println("Updates installed successfully. Waiting before next check.")
|
|
|
|
// Wait 10 seconds before checking again
|
|
time.Sleep(10 * time.Second)
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
// MAIN
|
|
// -------------------------------------------------------------------
|
|
func main() {
|
|
if len(os.Args) > 1 && os.Args[1] == "--update-service" {
|
|
isBackgroundMode = true
|
|
runBackgroundUpdater()
|
|
return
|
|
} else {
|
|
log.Println("Launcher started (foreground mode).")
|
|
ShowUpdateWindow()
|
|
}
|
|
fmt.Println("Exiting launcher.")
|
|
}
|