aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/state
diff options
context:
space:
mode:
authorOphestra Umiker <cat@ophivana.moe>2024-09-04 01:20:12 +0900
committerOphestra Umiker <cat@ophivana.moe>2024-09-04 01:20:12 +0900
commitd8f76f3b2594db4687c0203c4f2be8d3e4ef7740 (patch)
treeb1d8bfb5250d71094e397f8afa87710de1f1ca2f /internal/state
parent7e6eb82195c650bc34829e5cca2d2296e78c0707 (diff)
rename to fortify and restructure
More sandbox features will be added and this will no longer track ego's features and behaviour. Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
Diffstat (limited to 'internal/state')
-rw-r--r--internal/state/exit.go68
-rw-r--r--internal/state/register.go12
-rw-r--r--internal/state/track.go115
-rw-r--r--internal/state/value.go21
4 files changed, 216 insertions, 0 deletions
diff --git a/internal/state/exit.go b/internal/state/exit.go
new file mode 100644
index 00000000..e0e2c04a
--- /dev/null
+++ b/internal/state/exit.go
@@ -0,0 +1,68 @@
+package state
+
+import (
+ "errors"
+ "fmt"
+ "io/fs"
+ "os"
+
+ "git.ophivana.moe/cat/fortify/internal/acl"
+ "git.ophivana.moe/cat/fortify/internal/system"
+ "git.ophivana.moe/cat/fortify/internal/xcb"
+)
+
+func Fatal(msg ...any) {
+ fmt.Println(msg...)
+ BeforeExit()
+ os.Exit(1)
+}
+
+func BeforeExit() {
+ if u == nil {
+ fmt.Println("warn: beforeExit called before app init")
+ return
+ }
+
+ if statePath == "" {
+ if system.V.Verbose {
+ fmt.Println("State path is unset")
+ }
+ } else {
+ if err := os.Remove(statePath); err != nil && !errors.Is(err, fs.ErrNotExist) {
+ fmt.Println("Error removing state file:", err)
+ }
+ }
+
+ if d, err := readLaunchers(); err != nil {
+ fmt.Println("Error reading active launchers:", err)
+ os.Exit(1)
+ } else if len(d) > 0 {
+ // other launchers are still active
+ if system.V.Verbose {
+ fmt.Printf("Found %d active launchers, exiting without cleaning up\n", len(d))
+ }
+ return
+ }
+
+ if system.V.Verbose {
+ fmt.Println("No other launchers active, will clean up")
+ }
+
+ if xcbActionComplete {
+ if system.V.Verbose {
+ fmt.Printf("X11: Removing XHost entry SI:localuser:%s\n", u.Username)
+ }
+ if err := xcb.ChangeHosts(xcb.HostModeDelete, xcb.FamilyServerInterpreted, "localuser\x00"+u.Username); err != nil {
+ fmt.Println("Error removing XHost entry:", err)
+ }
+ }
+
+ for _, candidate := range cleanupCandidate {
+ if err := acl.UpdatePerm(candidate, uid); err != nil {
+ fmt.Printf("Error stripping ACL entry from '%s': %s\n", candidate, err)
+ }
+ if system.V.Verbose {
+ fmt.Printf("Stripped ACL entry for user '%s' from '%s'\n", u.Username, candidate)
+ }
+ }
+}
diff --git a/internal/state/register.go b/internal/state/register.go
new file mode 100644
index 00000000..2fff565e
--- /dev/null
+++ b/internal/state/register.go
@@ -0,0 +1,12 @@
+package state
+
+func RegisterRevertPath(p string) {
+ cleanupCandidate = append(cleanupCandidate, p)
+}
+
+func XcbActionComplete() {
+ if xcbActionComplete {
+ Fatal("xcb inserted twice")
+ }
+ xcbActionComplete = true
+}
diff --git a/internal/state/track.go b/internal/state/track.go
new file mode 100644
index 00000000..913d684d
--- /dev/null
+++ b/internal/state/track.go
@@ -0,0 +1,115 @@
+package state
+
+import (
+ "encoding/gob"
+ "errors"
+ "flag"
+ "fmt"
+ "io/fs"
+ "os"
+ "os/exec"
+ "path"
+ "strconv"
+
+ "git.ophivana.moe/cat/fortify/internal/system"
+)
+
+// we unfortunately have to assume there are never races between processes
+// this and launcher should eventually be replaced by a server process
+
+var (
+ stateActionEarly bool
+ statePath string
+ cleanupCandidate []string
+ xcbActionComplete bool
+)
+
+type launcherState struct {
+ PID int
+ Launcher string
+ Argv []string
+ Command []string
+}
+
+func init() {
+ flag.BoolVar(&stateActionEarly, "state", false, "query state value of current active launchers")
+}
+
+func Early() {
+ if !stateActionEarly {
+ return
+ }
+
+ launchers, err := readLaunchers()
+ if err != nil {
+ fmt.Println("Error reading launchers:", err)
+ os.Exit(1)
+ }
+
+ fmt.Println("\tPID\tLauncher")
+ for _, state := range launchers {
+ fmt.Printf("\t%d\t%s\nCommand: %s\nArgv: %s\n", state.PID, state.Launcher, state.Command, state.Argv)
+ }
+
+ os.Exit(0)
+}
+
+// SaveProcess called after process start, before wait
+func SaveProcess(uid string, cmd *exec.Cmd) error {
+ statePath = path.Join(system.V.RunDir, uid, strconv.Itoa(cmd.Process.Pid))
+ state := launcherState{
+ PID: cmd.Process.Pid,
+ Launcher: cmd.Path,
+ Argv: cmd.Args,
+ Command: command,
+ }
+
+ if err := os.Mkdir(path.Join(system.V.RunDir, uid), 0700); err != nil && !errors.Is(err, fs.ErrExist) {
+ return err
+ }
+
+ if f, err := os.OpenFile(statePath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600); err != nil {
+ return err
+ } else {
+ defer func() {
+ if f.Close() != nil {
+ // unreachable
+ panic("state file closed prematurely")
+ }
+ }()
+ return gob.NewEncoder(f).Encode(state)
+ }
+}
+
+func readLaunchers() ([]*launcherState, error) {
+ var f *os.File
+ var r []*launcherState
+ launcherPrefix := path.Join(system.V.RunDir, u.Uid)
+
+ if pl, err := os.ReadDir(launcherPrefix); err != nil {
+ return nil, err
+ } else {
+ for _, e := range pl {
+ if err = func() error {
+ if f, err = os.Open(path.Join(launcherPrefix, e.Name())); err != nil {
+ return err
+ } else {
+ defer func() {
+ if f.Close() != nil {
+ // unreachable
+ panic("foreign state file closed prematurely")
+ }
+ }()
+
+ var s launcherState
+ r = append(r, &s)
+ return gob.NewDecoder(f).Decode(&s)
+ }
+ }(); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ return r, nil
+}
diff --git a/internal/state/value.go b/internal/state/value.go
new file mode 100644
index 00000000..cb20818c
--- /dev/null
+++ b/internal/state/value.go
@@ -0,0 +1,21 @@
+package state
+
+import (
+ "os/user"
+)
+
+var (
+ u *user.User
+ uid int
+ command []string
+)
+
+func Set(val user.User, c []string, d int) {
+ if u != nil {
+ panic("state set twice")
+ }
+
+ u = &val
+ command = c
+ uid = d
+}