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
|
Layout: `C:\Program Files\gpu-turnstile\` holds the exe and
|
||||||
`gpu-turnstile.env`, logs go to `C:\ProgramData\gpu-turnstile\` (set
|
`gpu-turnstile.env`, logs go to `C:\ProgramData\gpu-turnstile\` — the
|
||||||
`LOG_FILE` in the env file — there is no console). The service always runs
|
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,
|
as the virtual account `NT SERVICE\gpu-turnstile` (low-privilege,
|
||||||
per-service, no password); the installer automatically grants it write
|
per-service, no password); the installer automatically grants it write
|
||||||
access to the install and data directories — nothing else to do.
|
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
|
it (Windows: `%ProgramFiles%\gpu-turnstile\`, plus
|
||||||
`%ProgramData%\gpu-turnstile\` for logs; Linux: `/var/lib/gpu-turnstile/`
|
`%ProgramData%\gpu-turnstile\` for logs; Linux: `/var/lib/gpu-turnstile/`
|
||||||
with the config at `/etc/gpu-turnstile.env`). An existing config in the
|
with the config at `/etc/gpu-turnstile.env`). An existing config in the
|
||||||
target location is never overwritten. `--no-copy` registers the current
|
target location is never overwritten. On Windows the install also makes
|
||||||
executable location as-is instead.
|
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
|
### Windows
|
||||||
|
|
||||||
@@ -195,8 +200,9 @@ executable location as-is instead.
|
|||||||
`%ProgramData%\gpu-turnstile\`, copies the exe and (if none exists there
|
`%ProgramData%\gpu-turnstile\`, copies the exe and (if none exists there
|
||||||
yet) the `gpu-turnstile.env` into the Program Files directory, and
|
yet) the `gpu-turnstile.env` into the Program Files directory, and
|
||||||
registers that copy as a Windows service; recovery actions restart it
|
registers that copy as a Windows service; recovery actions restart it
|
||||||
5 s after any failure. Logs go to the ProgramData directory via
|
5 s after any failure. The install ensures the env file sets `LOG_FILE`
|
||||||
`LOG_FILE` since there is no console.
|
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
|
- **Account**: the service always runs as the virtual account
|
||||||
`NT SERVICE\gpu-turnstile` — a per-service low-privilege identity the
|
`NT SERVICE\gpu-turnstile` — a per-service low-privilege identity the
|
||||||
SCM manages (no password, automatic logon-as-a-service right, no admin
|
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()
|
installDir, dataDir := installDirs()
|
||||||
if copyBin && !strings.EqualFold(filepath.Dir(exe), installDir) {
|
if copyBin {
|
||||||
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
|
|
||||||
targetCfg := filepath.Join(installDir, "gpu-turnstile.env")
|
targetCfg := filepath.Join(installDir, "gpu-turnstile.env")
|
||||||
if configPath != "" && !strings.EqualFold(configPath, targetCfg) {
|
if !strings.EqualFold(filepath.Dir(exe), installDir) {
|
||||||
if _, statErr := os.Stat(targetCfg); os.IsNotExist(statErr) {
|
if err := os.MkdirAll(installDir, 0o755); err != nil {
|
||||||
copyFile(configPath, targetCfg) //nolint:errcheck // best effort
|
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)
|
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
|
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).
|
// copyFile copies src to dst (0755 on the new file).
|
||||||
func copyFile(src, dst string) error {
|
func copyFile(src, dst string) error {
|
||||||
in, err := os.Open(src)
|
in, err := os.Open(src)
|
||||||
|
|||||||
Reference in New Issue
Block a user