103 lines
2.2 KiB
Go
103 lines
2.2 KiB
Go
|
package daemon
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io/fs"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"slices"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
|
||
|
"github.com/adrg/xdg"
|
||
|
)
|
||
|
|
||
|
// EnvVars are variables which are derived based on the environment which the
|
||
|
// process is running in.
|
||
|
type EnvVars struct {
|
||
|
RuntimeDirPath string // TODO should be private to this package
|
||
|
StateDirPath string // TODO should be private to this package
|
||
|
HTTPSocketPath string
|
||
|
}
|
||
|
|
||
|
func getRPCSocketDirPath() string {
|
||
|
path, err := firstExistingDir(
|
||
|
"/run",
|
||
|
"/var/run",
|
||
|
"/tmp",
|
||
|
"/dev/shm",
|
||
|
)
|
||
|
if err != nil {
|
||
|
panic(fmt.Sprintf("Failed to find directory for RPC socket: %v", err))
|
||
|
}
|
||
|
|
||
|
return path
|
||
|
}
|
||
|
|
||
|
// GetEnvVars will return the EnvVars of the current processes, as determined by
|
||
|
// the process's environment.
|
||
|
var GetEnvVars = sync.OnceValue(func() (v EnvVars) {
|
||
|
// RUNTIME_DIRECTORY/STATE_DIRECTORY are used by the systemd service in
|
||
|
// conjunction with the RuntimeDirectory/StateDirectory directives.
|
||
|
|
||
|
v.RuntimeDirPath = envOr(
|
||
|
"RUNTIME_DIRECTORY",
|
||
|
func() string { return filepath.Join(xdg.RuntimeDir, "isle") },
|
||
|
)
|
||
|
|
||
|
v.StateDirPath = envOr(
|
||
|
"STATE_DIRECTORY",
|
||
|
func() string { return filepath.Join(xdg.StateHome, "isle") },
|
||
|
)
|
||
|
|
||
|
v.HTTPSocketPath = envOr(
|
||
|
"ISLE_SOCKET_PATH",
|
||
|
func() string {
|
||
|
return filepath.Join(getRPCSocketDirPath(), "isle-daemon.sock")
|
||
|
},
|
||
|
)
|
||
|
|
||
|
return
|
||
|
})
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// Jigs
|
||
|
|
||
|
func envOr(name string, fallback func() string) string {
|
||
|
if v := os.Getenv(name); v != "" {
|
||
|
return v
|
||
|
}
|
||
|
return fallback()
|
||
|
}
|
||
|
|
||
|
func firstExistingDir(paths ...string) (string, error) {
|
||
|
var errs []error
|
||
|
for _, path := range paths {
|
||
|
stat, err := os.Stat(path)
|
||
|
switch {
|
||
|
case errors.Is(err, fs.ErrExist):
|
||
|
continue
|
||
|
case err != nil:
|
||
|
errs = append(
|
||
|
errs, fmt.Errorf("checking if path %q exists: %w", path, err),
|
||
|
)
|
||
|
case !stat.IsDir():
|
||
|
errs = append(
|
||
|
errs, fmt.Errorf("path %q exists but is not a directory", path),
|
||
|
)
|
||
|
default:
|
||
|
return path, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
err := fmt.Errorf(
|
||
|
"no directory found at any of the following paths: %s",
|
||
|
strings.Join(paths, ", "),
|
||
|
)
|
||
|
if len(errs) > 0 {
|
||
|
err = errors.Join(slices.Insert(errs, 0, err)...)
|
||
|
}
|
||
|
return "", err
|
||
|
}
|