//go:build linux package main import ( "fmt" "os" "path/filepath" "hamlog/internal/applog" ) // clearDownloadMark has nothing to clear on Linux: there is no // mark-of-the-web, and no SmartScreen to refuse a programmatic launch. func clearDownloadMark(path string) {} // makeExecutable restores the executable bit. A binary downloaded over HTTP // arrives 0644 — on Windows the extension decides and this is a no-op, but here // a freshly installed OpsLog that nothing can exec is a dead station. func makeExecutable(path string) error { return os.Chmod(path, 0o755) } // scheduleDeferredSwap is the fallback for when the running binary could not be // renamed out of the way — the path Windows needs a detached PowerShell helper // for, because nothing there can move a file over a running image. // // On Linux it should never be reached. A rename only touches the directory // entry, and the running process holds the inode, so replacing the binary of a // live process is ordinary and the staging rename in DownloadAndApplyUpdate // succeeds. If it did fail, the cause was the filesystem (read-only mount, no // write permission on the directory, a full disk) and no helper would get past // it either — so do the honest thing: try the move once more now, and say // plainly what is wrong if it still refuses. func (a *App) scheduleDeferredSwap(exe, pending string) error { if err := os.Rename(pending, exe); err != nil { return fmt.Errorf("install the new build: %w (the folder %s must be writable)", err, filepath.Dir(exe)) } if err := makeExecutable(exe); err != nil { return fmt.Errorf("make the new build executable: %w", err) } applog.Printf("update: installed %s over %s after the staging rename failed", filepath.Base(pending), filepath.Base(exe)) return nil }