Self-install into canonical layout on both platforms, --no-copy to opt out
Windows: --install-service creates %ProgramFiles%\gpu-turnstile and %ProgramData%\gpu-turnstile, copies the exe and (if absent) the env file in, and registers the copy. Linux: binary goes to /var/lib/gpu-turnstile (not /usr/local/sbin: replacing a running binary needs directory write, which must not be granted on a shared system dir to a sandboxed service). --no-copy registers the current location as-is on both platforms.
This commit is contained in:
@@ -113,9 +113,16 @@ func copyFile(src, dst string, mode os.FileMode) error {
|
||||
|
||||
// Install copies the current executable into /var/lib/gpu-turnstile, makes
|
||||
// sure /etc/gpu-turnstile.env exists (copied from the given config file if
|
||||
// provided), writes the hardened unit, then enables and starts it. Needs
|
||||
// root.
|
||||
func Install(configPath string) error {
|
||||
// provided), writes the hardened unit, then enables and starts it. With
|
||||
// copyBin=false the current executable location and config path are
|
||||
// registered as-is instead. Needs root.
|
||||
//
|
||||
// The binary lives in the StateDirectory rather than /usr/local/sbin on
|
||||
// purpose: replacing a running binary needs write access to its
|
||||
// *directory*, and granting the sandboxed service user write access to a
|
||||
// shared system directory would let a compromised service overwrite other
|
||||
// binaries. /var/lib/gpu-turnstile is exclusively ours.
|
||||
func Install(configPath string, copyBin bool) error {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -123,21 +130,29 @@ func Install(configPath string) error {
|
||||
if abs, absErr := filepath.Abs(exe); absErr == nil {
|
||||
exe = abs
|
||||
}
|
||||
if err := os.MkdirAll(stateDir, 0o755); err != nil {
|
||||
return fmt.Errorf("create %s (run as root): %w", stateDir, err)
|
||||
}
|
||||
installedExe := filepath.Join(stateDir, Name)
|
||||
if exe != installedExe {
|
||||
if err := copyFile(exe, installedExe, 0o755); err != nil {
|
||||
return fmt.Errorf("install binary to %s: %w", installedExe, err)
|
||||
cfg := etcConfig
|
||||
if copyBin {
|
||||
if err := os.MkdirAll(stateDir, 0o755); err != nil {
|
||||
return fmt.Errorf("create %s (run as root): %w", stateDir, err)
|
||||
}
|
||||
installedExe := filepath.Join(stateDir, Name)
|
||||
if exe != installedExe {
|
||||
if err := copyFile(exe, installedExe, 0o755); err != nil {
|
||||
return fmt.Errorf("install binary to %s: %w", installedExe, err)
|
||||
}
|
||||
}
|
||||
exe = installedExe
|
||||
if _, err := os.Stat(etcConfig); os.IsNotExist(err) && configPath != "" {
|
||||
// Missing config is not fatal: the service fails fast with a
|
||||
// clear "no consumer URL" error until the user writes one.
|
||||
copyFile(configPath, etcConfig, 0o644) //nolint:errcheck // best effort
|
||||
}
|
||||
} else if configPath != "" {
|
||||
if abs, absErr := filepath.Abs(configPath); absErr == nil {
|
||||
cfg = abs
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(etcConfig); os.IsNotExist(err) && configPath != "" {
|
||||
// Missing config is not fatal: the service fails fast with a clear
|
||||
// "no consumer URL" error until the user writes one.
|
||||
copyFile(configPath, etcConfig, 0o644) //nolint:errcheck // best effort
|
||||
}
|
||||
if err := os.WriteFile(unitPath, []byte(renderUnit(installedExe, etcConfig)), 0o644); err != nil {
|
||||
if err := os.WriteFile(unitPath, []byte(renderUnit(exe, cfg)), 0o644); err != nil {
|
||||
return fmt.Errorf("write %s (run as root): %w", unitPath, err)
|
||||
}
|
||||
if out, err := exec.Command("systemctl", "daemon-reload").CombinedOutput(); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user