fortify/internal/state/print.go
Ophestra Umiker 62cb8a91b6
app: clean up interactions and handle all application state and setup/teardown
There was an earlier attempt of cleaning up the app package however it ended up creating even more of a mess and the code structure largely still looked like Ego with state setup scattered everywhere and a bunch of ugly hacks had to be implemented to keep track of all of them. In this commit the entire app package is rewritten to track everything that has to do with an app in one thread safe value.

In anticipation of the client/server split also made changes:
- Console messages are cleaned up to be consistent
- State tracking is fully rewritten to be cleaner and usable for multiple process and client/server
- Encapsulate errors to easier identify type of action causing the error as well as additional info
- System-level setup operations is grouped in a way that can be collectively committed/reverted
  and gracefully handles errors returned by each operation
- Resource sharing is made more fine-grained with PID-scoped resources whenever possible,
  a few remnants (X11, Wayland, PulseAudio) will be addressed when a generic proxy is available
- Application setup takes a JSON-friendly config struct and deterministically generates system setup operations

Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
2024-09-22 01:15:39 +09:00

125 lines
3.1 KiB
Go

package state
import (
"fmt"
"os"
"path"
"strconv"
"strings"
"text/tabwriter"
"time"
"git.ophivana.moe/cat/fortify/internal"
"git.ophivana.moe/cat/fortify/internal/verbose"
)
// MustPrintLauncherStateSimpleGlobal prints active launcher states of all simple stores
// in an implementation-specific way.
func MustPrintLauncherStateSimpleGlobal(w **tabwriter.Writer) {
sc := internal.GetSC()
now := time.Now().UTC()
// read runtime directory to get all UIDs
if dirs, err := os.ReadDir(sc.RunDirPath); err != nil {
fmt.Println("cannot read runtime directory:", err)
os.Exit(1)
} else {
for _, e := range dirs {
// skip non-directories
if !e.IsDir() {
verbose.Println("skipped non-directory entry", e.Name())
continue
}
// skip non-numerical names
if _, err = strconv.Atoi(e.Name()); err != nil {
verbose.Println("skipped non-uid entry", e.Name())
continue
}
// obtain temporary store
s := NewSimple(sc.RunDirPath, e.Name()).(*simpleStore)
// print states belonging to this store
s.mustPrintLauncherState(w, now)
// mustPrintLauncherState causes store activity so store needs to be closed
if err = s.Close(); err != nil {
fmt.Printf("warn: error closing store for user %s: %s\n", e.Name(), err)
}
}
}
}
func (s *simpleStore) mustPrintLauncherState(w **tabwriter.Writer, now time.Time) {
var innerErr error
if ok, err := s.Do(func(b Backend) {
innerErr = func() error {
// read launcher states
states, err := b.Load()
if err != nil {
return err
}
// initialise tabwriter if nil
if *w == nil {
*w = tabwriter.NewWriter(os.Stdout, 0, 1, 4, ' ', 0)
// write header when initialising
if !verbose.Get() {
_, _ = fmt.Fprintln(*w, "\tUID\tPID\tUptime\tEnablements\tLauncher\tCommand")
} else {
// argv is emitted in body when verbose
_, _ = fmt.Fprintln(*w, "\tUID\tPID\tArgv")
}
}
// print each state
for _, state := range states {
// skip nil states
if state == nil {
_, _ = fmt.Fprintln(*w, "\tnil state entry")
continue
}
// build enablements string
ets := strings.Builder{}
// append enablement strings in order
for i := Enablement(0); i < EnableLength; i++ {
if state.Capability.Has(i) {
ets.WriteString(", " + i.String())
}
}
// prevent an empty string when
if ets.Len() == 0 {
ets.WriteString("(No enablements)")
}
if !verbose.Get() {
_, _ = fmt.Fprintf(*w, "\t%s\t%d\t%s\t%s\t%s\t%s\n",
s.path[len(s.path)-1], state.PID, now.Sub(state.Time).String(), strings.TrimPrefix(ets.String(), ", "), state.Launcher,
state.Command)
} else {
// emit argv instead when verbose
_, _ = fmt.Fprintf(*w, "\t%s\t%d\t%s\n",
s.path[len(s.path)-1], state.PID, state.Argv)
}
}
return nil
}()
}); err != nil {
fmt.Printf("cannot perform action on store '%s': %s\n", path.Join(s.path...), err)
if !ok {
fmt.Println("warn: store faulted before printing")
os.Exit(1)
}
}
if innerErr != nil {
fmt.Printf("cannot print launcher state for store '%s': %s\n", path.Join(s.path...), innerErr)
os.Exit(1)
}
}