aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/sys/interface.go
blob: f2465e6243e2af817c1216247409d2ac1a31c0f3 (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
// Package sys wraps OS interaction library functions.
package sys

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

	"hakurei.app/container"
	"hakurei.app/hst"
	"hakurei.app/internal/hlog"
)

// State provides safe interaction with operating system state.
type State interface {
	// Getuid provides [os.Getuid].
	Getuid() int
	// Getgid provides [os.Getgid].
	Getgid() int
	// LookupEnv provides [os.LookupEnv].
	LookupEnv(key string) (string, bool)
	// TempDir provides [os.TempDir].
	TempDir() string
	// LookPath provides exec.LookPath.
	LookPath(file string) (string, error)
	// MustExecutable provides [container.MustExecutable].
	MustExecutable() string
	// LookupGroup provides [user.LookupGroup].
	LookupGroup(name string) (*user.Group, error)
	// ReadDir provides [os.ReadDir].
	ReadDir(name string) ([]fs.DirEntry, error)
	// Stat provides [os.Stat].
	Stat(name string) (fs.FileInfo, error)
	// Open provides [os.Open].
	Open(name string) (fs.File, error)
	// EvalSymlinks provides filepath.EvalSymlinks.
	EvalSymlinks(path string) (string, error)
	// Exit provides [os.Exit].
	Exit(code int)

	Println(v ...any)
	Printf(format string, v ...any)

	// Paths returns a populated [hst.Paths] struct.
	Paths() hst.Paths
	// Uid invokes hsu and returns target uid.
	// Any errors returned by Uid is already wrapped [hlog.BaseError].
	Uid(identity int) (int, error)
}

// MustGetUserID obtains user id from hsu by querying uid of identity 0.
func MustGetUserID(os State) int { return (MustUid(os, 0) / 10000) - 100 }

// CopyPaths is a generic implementation of [hst.Paths].
func CopyPaths(os State, v *hst.Paths, userid int) {
	if tempDir, err := container.NewAbs(os.TempDir()); err != nil {
		log.Fatalf("invalid TMPDIR: %v", err)
	} else {
		v.TempDir = tempDir
	}

	v.SharePath = v.TempDir.Append("hakurei." + strconv.Itoa(userid))
	hlog.Verbosef("process share directory at %q", v.SharePath)

	r, _ := os.LookupEnv(xdgRuntimeDir)
	if a, err := container.NewAbs(r); err != nil {
		// fall back to path in share since hakurei has no hard XDG dependency
		v.RunDirPath = v.SharePath.Append("run")
		v.RuntimePath = v.RunDirPath.Append("compat")
	} else {
		v.RuntimePath = a
		v.RunDirPath = v.RuntimePath.Append("hakurei")
	}
	hlog.Verbosef("runtime directory at %q", v.RunDirPath)
}