aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/env/env.go
blob: 0c4137333efbfdc35eb0efcdbcb8700e9cf71487 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// Package env provides the [Paths] struct for efficiently building paths from
// the environment.
package env

import (
	"errors"
	"io/fs"
	"log"
	"os"
	"strconv"

	"hakurei.app/check"
	"hakurei.app/fhs"
	"hakurei.app/hst"
)

const VarRunNscd = fhs.Var + "run/nscd"

// Paths holds paths copied from the environment and is used to create [hst.Paths].
type Paths struct {
	// TempDir is returned by [os.TempDir].
	TempDir *check.Absolute
	// RuntimePath is copied from $XDG_RUNTIME_DIR.
	RuntimePath *check.Absolute
	// Whether [VarRunNscd] is a directory.
	HasNscd bool
}

// Copy expands [Paths] into [hst.Paths].
func (env *Paths) Copy(v *hst.Paths, userid int) {
	if env == nil || env.TempDir == nil || v == nil {
		panic("attempting to use an invalid Paths")
	}

	v.TempDir = env.TempDir
	v.SharePath = env.TempDir.Append("hakurei." + strconv.Itoa(userid))

	if env.RuntimePath == nil {
		// fall back to path in share since hakurei has no hard XDG dependency
		v.RuntimePath = v.SharePath.Append("compat")
	} else {
		v.RuntimePath = env.RuntimePath
	}
	v.RunDirPath = v.RuntimePath.Append("hakurei")
}

// CopyPaths returns a populated [Paths].
func CopyPaths() *Paths {
	return CopyPathsFunc(log.Fatalf, os.TempDir, os.Getenv, os.Stat)
}

// CopyPathsFunc returns a populated [Paths], using the provided [log.Fatalf],
// [os.TempDir], [os.Getenv] functions.
func CopyPathsFunc(
	fatalf func(format string, v ...any),
	tempdir func() string,
	getenv func(key string) string,
	stat func(name string) (fs.FileInfo, error),
) *Paths {
	const xdgRuntimeDir = "XDG_RUNTIME_DIR"

	var env Paths

	if tempDir, err := check.NewAbs(tempdir()); err != nil {
		fatalf("invalid TMPDIR: %v", err)
		panic("unreachable")
	} else {
		env.TempDir = tempDir
	}

	if a, err := check.NewAbs(getenv(xdgRuntimeDir)); err == nil {
		env.RuntimePath = a
	}

	if fi, err := stat(VarRunNscd); err != nil {
		if !errors.Is(err, fs.ErrNotExist) {
			fatalf("%v", err)
			panic("unreachable")
		}
	} else {
		env.HasNscd = fi.IsDir()
	}

	return &env
}