From 648e1d641a8b0ae7c5bc4899f84b884d4335c97f Mon Sep 17 00:00:00 2001
From: Ophestra <cat@gensokyo.uk>
Date: Tue, 18 Feb 2025 23:05:37 +0900
Subject: [PATCH] app: separate interface from implementation

Signed-off-by: Ophestra <cat@gensokyo.uk>
---
 fst/sandbox.go                |  3 +--
 internal/app/app.go           | 30 +++++-------------------------
 internal/app/app_stub_test.go |  6 +++---
 internal/app/export_test.go   |  4 ++--
 internal/app/seal.go          |  3 +--
 internal/app/start.go         |  3 ++-
 internal/sys/interface.go     | 15 +++------------
 internal/sys/std.go           |  5 +++--
 main.go                       |  2 +-
 9 files changed, 21 insertions(+), 50 deletions(-)

diff --git a/fst/sandbox.go b/fst/sandbox.go
index 5295f95..27df5f9 100644
--- a/fst/sandbox.go
+++ b/fst/sandbox.go
@@ -8,7 +8,6 @@ import (
 
 	"git.gensokyo.uk/security/fortify/dbus"
 	"git.gensokyo.uk/security/fortify/helper/bwrap"
-	"git.gensokyo.uk/security/fortify/internal/sys"
 )
 
 // SandboxConfig describes resources made available to the sandbox.
@@ -47,7 +46,7 @@ type SandboxConfig struct {
 // SandboxSys encapsulates system functions used during the creation of [bwrap.Config].
 type SandboxSys interface {
 	Geteuid() int
-	Paths() sys.Paths
+	Paths() Paths
 	ReadDir(name string) ([]fs.DirEntry, error)
 	EvalSymlinks(path string) (string, error)
 
diff --git a/internal/app/app.go b/internal/app/app.go
index 2a96c54..cf21ac7 100644
--- a/internal/app/app.go
+++ b/internal/app/app.go
@@ -1,7 +1,6 @@
 package app
 
 import (
-	"context"
 	"sync"
 
 	"git.gensokyo.uk/security/fortify/fst"
@@ -9,23 +8,11 @@ import (
 	"git.gensokyo.uk/security/fortify/internal/sys"
 )
 
-type App interface {
-	// ID returns a copy of App's unique ID.
-	ID() fst.ID
-	// Run sets up the system and runs the App.
-	Run(ctx context.Context, rs *RunState) error
-
-	Seal(config *fst.Config) error
-	String() string
-}
-
-type RunState struct {
-	// Start is true if fsu is successfully started.
-	Start bool
-	// ExitCode is the value returned by shim.
-	ExitCode int
-	// WaitErr is error returned by the underlying wait syscall.
-	WaitErr error
+func New(os sys.State) (fst.App, error) {
+	a := new(app)
+	a.id = new(fst.ID)
+	a.os = os
+	return a, fst.NewAppID(a.id)
 }
 
 type app struct {
@@ -63,10 +50,3 @@ func (a *app) String() string {
 
 	return "(unsealed fortified app)"
 }
-
-func New(os sys.State) (App, error) {
-	a := new(app)
-	a.id = new(fst.ID)
-	a.os = os
-	return a, fst.NewAppID(a.id)
-}
diff --git a/internal/app/app_stub_test.go b/internal/app/app_stub_test.go
index 89804d2..262055f 100644
--- a/internal/app/app_stub_test.go
+++ b/internal/app/app_stub_test.go
@@ -7,7 +7,7 @@ import (
 	"os/user"
 	"strconv"
 
-	"git.gensokyo.uk/security/fortify/internal/sys"
+	"git.gensokyo.uk/security/fortify/fst"
 )
 
 // fs methods are not implemented using a real FS
@@ -126,8 +126,8 @@ func (s *stubNixOS) Open(name string) (fs.File, error) {
 	}
 }
 
-func (s *stubNixOS) Paths() sys.Paths {
-	return sys.Paths{
+func (s *stubNixOS) Paths() fst.Paths {
+	return fst.Paths{
 		SharePath:   "/tmp/fortify.1971",
 		RuntimePath: "/run/user/1971",
 		RunDirPath:  "/run/user/1971/fortify",
diff --git a/internal/app/export_test.go b/internal/app/export_test.go
index 2e027b8..836e04c 100644
--- a/internal/app/export_test.go
+++ b/internal/app/export_test.go
@@ -7,14 +7,14 @@ import (
 	"git.gensokyo.uk/security/fortify/system"
 )
 
-func NewWithID(id fst.ID, os sys.State) App {
+func NewWithID(id fst.ID, os sys.State) fst.App {
 	a := new(app)
 	a.id = &id
 	a.os = os
 	return a
 }
 
-func AppSystemBwrap(a App) (*system.I, *bwrap.Config) {
+func AppSystemBwrap(a fst.App) (*system.I, *bwrap.Config) {
 	v := a.(*app)
 	return v.seal.sys.I, v.seal.sys.bwrap
 }
diff --git a/internal/app/seal.go b/internal/app/seal.go
index e8c33a3..12891b2 100644
--- a/internal/app/seal.go
+++ b/internal/app/seal.go
@@ -18,7 +18,6 @@ import (
 	"git.gensokyo.uk/security/fortify/internal"
 	"git.gensokyo.uk/security/fortify/internal/fmsg"
 	"git.gensokyo.uk/security/fortify/internal/state"
-	"git.gensokyo.uk/security/fortify/internal/sys"
 	"git.gensokyo.uk/security/fortify/system"
 )
 
@@ -64,7 +63,7 @@ type appSeal struct {
 	// seal system-level component
 	sys *appSealSys
 
-	sys.Paths
+	fst.Paths
 
 	// protected by upstream mutex
 }
diff --git a/internal/app/start.go b/internal/app/start.go
index 33eaf86..cfb8364 100644
--- a/internal/app/start.go
+++ b/internal/app/start.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 	"time"
 
+	"git.gensokyo.uk/security/fortify/fst"
 	"git.gensokyo.uk/security/fortify/helper"
 	"git.gensokyo.uk/security/fortify/internal/app/shim"
 	"git.gensokyo.uk/security/fortify/internal/fmsg"
@@ -19,7 +20,7 @@ import (
 
 const shimSetupTimeout = 5 * time.Second
 
-func (a *app) Run(ctx context.Context, rs *RunState) error {
+func (a *app) Run(ctx context.Context, rs *fst.RunState) error {
 	a.lock.Lock()
 	defer a.lock.Unlock()
 
diff --git a/internal/sys/interface.go b/internal/sys/interface.go
index da1cf07..0d2ebaf 100644
--- a/internal/sys/interface.go
+++ b/internal/sys/interface.go
@@ -6,6 +6,7 @@ import (
 	"path"
 	"strconv"
 
+	"git.gensokyo.uk/security/fortify/fst"
 	"git.gensokyo.uk/security/fortify/internal/fmsg"
 )
 
@@ -38,24 +39,14 @@ type State interface {
 	Printf(format string, v ...any)
 
 	// Paths returns a populated [Paths] struct.
-	Paths() Paths
+	Paths() fst.Paths
 	// Uid invokes fsu and returns target uid.
 	// Any errors returned by Uid is already wrapped [fmsg.BaseError].
 	Uid(aid int) (int, error)
 }
 
-// Paths contains environment dependent paths used by fortify.
-type Paths struct {
-	// path to shared directory e.g. /tmp/fortify.%d
-	SharePath string `json:"share_path"`
-	// XDG_RUNTIME_DIR value e.g. /run/user/%d
-	RuntimePath string `json:"runtime_path"`
-	// application runtime directory e.g. /run/user/%d/fortify
-	RunDirPath string `json:"run_dir_path"`
-}
-
 // CopyPaths is a generic implementation of [System.Paths].
-func CopyPaths(os State, v *Paths) {
+func CopyPaths(os State, v *fst.Paths) {
 	v.SharePath = path.Join(os.TempDir(), "fortify."+strconv.Itoa(os.Geteuid()))
 
 	fmsg.Verbosef("process share directory at %q", v.SharePath)
diff --git a/internal/sys/std.go b/internal/sys/std.go
index 88579f6..bab2b92 100644
--- a/internal/sys/std.go
+++ b/internal/sys/std.go
@@ -13,13 +13,14 @@ import (
 	"sync"
 	"syscall"
 
+	"git.gensokyo.uk/security/fortify/fst"
 	"git.gensokyo.uk/security/fortify/internal"
 	"git.gensokyo.uk/security/fortify/internal/fmsg"
 )
 
 // Std implements System using the standard library.
 type Std struct {
-	paths     Paths
+	paths     fst.Paths
 	pathsOnce sync.Once
 
 	uidOnce sync.Once
@@ -46,7 +47,7 @@ func (s *Std) Printf(format string, v ...any)               { fmsg.Verbosef(form
 
 const xdgRuntimeDir = "XDG_RUNTIME_DIR"
 
-func (s *Std) Paths() Paths {
+func (s *Std) Paths() fst.Paths {
 	s.pathsOnce.Do(func() { CopyPaths(s, &s.paths) })
 	return s.paths
 }
diff --git a/main.go b/main.go
index b4bfff9..c1aee94 100644
--- a/main.go
+++ b/main.go
@@ -319,7 +319,7 @@ func main() {
 }
 
 func runApp(config *fst.Config) {
-	rs := new(app.RunState)
+	rs := new(fst.RunState)
 	ctx, stop := signal.NotifyContext(context.Background(),
 		syscall.SIGINT, syscall.SIGTERM)
 	defer stop() // unreachable