Install sets a default LOG_FILE in the env file on Windows (no console as a service)
This commit is contained in:
@@ -112,8 +112,9 @@ gpu-turnstile.exe --remove-service
|
||||
```
|
||||
|
||||
Layout: `C:\Program Files\gpu-turnstile\` holds the exe and
|
||||
`gpu-turnstile.env`, logs go to `C:\ProgramData\gpu-turnstile\` (set
|
||||
`LOG_FILE` in the env file — there is no console). The service always runs
|
||||
`gpu-turnstile.env`, logs go to `C:\ProgramData\gpu-turnstile\` — the
|
||||
installer sets `LOG_FILE` in the env file by default (there is no console;
|
||||
your own `LOG_FILE` setting is kept). The service always runs
|
||||
as the virtual account `NT SERVICE\gpu-turnstile` (low-privilege,
|
||||
per-service, no password); the installer automatically grants it write
|
||||
access to the install and data directories — nothing else to do.
|
||||
|
||||
@@ -186,8 +186,13 @@ By default install creates the canonical layout and copies the binary into
|
||||
it (Windows: `%ProgramFiles%\gpu-turnstile\`, plus
|
||||
`%ProgramData%\gpu-turnstile\` for logs; Linux: `/var/lib/gpu-turnstile/`
|
||||
with the config at `/etc/gpu-turnstile.env`). An existing config in the
|
||||
target location is never overwritten. `--no-copy` registers the current
|
||||
executable location as-is instead.
|
||||
target location is never overwritten. On Windows the install also makes
|
||||
sure the env file sets `LOG_FILE` to
|
||||
`%ProgramData%\gpu-turnstile\gpu-turnstile.log` (appended only when no
|
||||
`LOG_FILE=` line exists) since a service has no console; on Linux logs go
|
||||
to the journal via stderr, so no default is set there. `--no-copy`
|
||||
registers the current executable location as-is and leaves the config
|
||||
untouched.
|
||||
|
||||
### Windows
|
||||
|
||||
@@ -195,8 +200,9 @@ executable location as-is instead.
|
||||
`%ProgramData%\gpu-turnstile\`, copies the exe and (if none exists there
|
||||
yet) the `gpu-turnstile.env` into the Program Files directory, and
|
||||
registers that copy as a Windows service; recovery actions restart it
|
||||
5 s after any failure. Logs go to the ProgramData directory via
|
||||
`LOG_FILE` since there is no console.
|
||||
5 s after any failure. The install ensures the env file sets `LOG_FILE`
|
||||
to `%ProgramData%\gpu-turnstile\gpu-turnstile.log` since there is no
|
||||
console — an existing `LOG_FILE` setting is kept.
|
||||
- **Account**: the service always runs as the virtual account
|
||||
`NT SERVICE\gpu-turnstile` — a per-service low-privilege identity the
|
||||
SCM manages (no password, automatic logon-as-a-service right, no admin
|
||||
|
||||
@@ -152,25 +152,33 @@ func Install(configPath string, copyBin bool) error {
|
||||
}
|
||||
}
|
||||
|
||||
installDir, _ := installDirs()
|
||||
if copyBin && !strings.EqualFold(filepath.Dir(exe), installDir) {
|
||||
if err := os.MkdirAll(installDir, 0o755); err != nil {
|
||||
return fmt.Errorf("create %s: %w", installDir, err)
|
||||
}
|
||||
installedExe := filepath.Join(installDir, "gpu-turnstile.exe")
|
||||
if same, _ := sameFileContent(exe, installedExe); !same {
|
||||
if err := copyFile(exe, installedExe); err != nil {
|
||||
return fmt.Errorf("copy binary to %s: %w", installedExe, err)
|
||||
}
|
||||
}
|
||||
exe = installedExe
|
||||
installDir, dataDir := installDirs()
|
||||
if copyBin {
|
||||
targetCfg := filepath.Join(installDir, "gpu-turnstile.env")
|
||||
if configPath != "" && !strings.EqualFold(configPath, targetCfg) {
|
||||
if _, statErr := os.Stat(targetCfg); os.IsNotExist(statErr) {
|
||||
copyFile(configPath, targetCfg) //nolint:errcheck // best effort
|
||||
if !strings.EqualFold(filepath.Dir(exe), installDir) {
|
||||
if err := os.MkdirAll(installDir, 0o755); err != nil {
|
||||
return fmt.Errorf("create %s: %w", installDir, err)
|
||||
}
|
||||
installedExe := filepath.Join(installDir, "gpu-turnstile.exe")
|
||||
if same, _ := sameFileContent(exe, installedExe); !same {
|
||||
if err := copyFile(exe, installedExe); err != nil {
|
||||
return fmt.Errorf("copy binary to %s: %w", installedExe, err)
|
||||
}
|
||||
}
|
||||
exe = installedExe
|
||||
if configPath != "" && !strings.EqualFold(configPath, targetCfg) {
|
||||
if _, statErr := os.Stat(targetCfg); os.IsNotExist(statErr) {
|
||||
copyFile(configPath, targetCfg) //nolint:errcheck // best effort
|
||||
}
|
||||
}
|
||||
configPath = targetCfg
|
||||
}
|
||||
// A service has no console: without LOG_FILE the output vanishes, so
|
||||
// the installed env file defaults to logging into ProgramData. An
|
||||
// existing LOG_FILE setting is left alone.
|
||||
if err := ensureLogFile(targetCfg, filepath.Join(dataDir, "gpu-turnstile.log")); err != nil {
|
||||
return err
|
||||
}
|
||||
configPath = targetCfg
|
||||
}
|
||||
binPath := fmt.Sprintf(`"%s" -config "%s"`, exe, configPath)
|
||||
|
||||
@@ -304,6 +312,30 @@ func sameFileContent(a, b string) (bool, error) {
|
||||
return bytes.Equal(ba, bb), nil
|
||||
}
|
||||
|
||||
// ensureLogFile makes sure the env file at path sets LOG_FILE, creating
|
||||
// the file or appending the line as needed. An existing LOG_FILE= line
|
||||
// (even an empty one) is respected and left untouched.
|
||||
func ensureLogFile(path, logPath string) error {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("read %s: %w", path, err)
|
||||
}
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
if strings.HasPrefix(strings.TrimSpace(line), "LOG_FILE=") {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
s := string(data)
|
||||
if s != "" && !strings.HasSuffix(s, "\n") {
|
||||
s += "\n"
|
||||
}
|
||||
s += "LOG_FILE=" + logPath + "\n"
|
||||
if err := os.WriteFile(path, []byte(s), 0o644); err != nil {
|
||||
return fmt.Errorf("write %s: %w", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyFile copies src to dst (0755 on the new file).
|
||||
func copyFile(src, dst string) error {
|
||||
in, err := os.Open(src)
|
||||
|
||||
Reference in New Issue
Block a user