aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--comp/_fortify5
-rw-r--r--internal/state/print.go118
-rw-r--r--main.go16
-rw-r--r--print.go73
4 files changed, 85 insertions, 127 deletions
diff --git a/comp/_fortify b/comp/_fortify
index bad412ef..b7e97c28 100644
--- a/comp/_fortify
+++ b/comp/_fortify
@@ -21,6 +21,11 @@ _fortify_run() {
'--dbus-log[Force logging in the D-Bus proxy]'
}
+_fortify_ps() {
+ _arguments \
+ '--short[Print instance id]'
+}
+
(( $+functions[_fortify_commands] )) || _fortify_commands()
{
local -a _fortify_cmds
diff --git a/internal/state/print.go b/internal/state/print.go
deleted file mode 100644
index b280878e..00000000
--- a/internal/state/print.go
+++ /dev/null
@@ -1,118 +0,0 @@
-package state
-
-import (
- "fmt"
- "os"
- "strings"
- "text/tabwriter"
- "time"
-
- "git.gensokyo.uk/security/fortify/internal/fmsg"
- "git.gensokyo.uk/security/fortify/internal/system"
-)
-
-// MustPrintLauncherStateSimpleGlobal prints active launcher states of all simple stores
-// in an implementation-specific way.
-func MustPrintLauncherStateSimpleGlobal(w **tabwriter.Writer, runDir string) {
- now := time.Now().UTC()
- s := NewMulti(runDir)
-
- // read runtime directory to get all UIDs
- if aids, err := s.List(); err != nil {
- fmsg.Fatal("cannot list store:", err)
- } else {
- for _, aid := range aids {
- // print states belonging to this store
- s.(*multiStore).mustPrintLauncherState(aid, w, now)
- }
- }
-
- // mustPrintLauncherState causes store activity so store needs to be closed
- if err := s.Close(); err != nil {
- fmsg.Printf("cannot close store: %v", err)
- }
-}
-
-func (s *multiStore) mustPrintLauncherState(aid int, w **tabwriter.Writer, now time.Time) {
- var innerErr error
-
- if ok, err := s.Do(aid, func(c Cursor) {
- innerErr = func() error {
- // read launcher states
- states, err := c.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 !fmsg.Verbose() {
- _, _ = fmt.Fprintln(*w, "\tPID\tApp\tUptime\tEnablements\tCommand")
- } else {
- // argv is emitted in body when verbose
- _, _ = fmt.Fprintln(*w, "\tPID\tApp\tArgv")
- }
- }
-
- // print each state
- for _, state := range states {
- // skip nil states
- if state == nil {
- _, _ = fmt.Fprintln(*w, "\tnil state entry")
- continue
- }
-
- // build enablements and command string
- var (
- ets *strings.Builder
- cs = "(No command information)"
- )
-
- // check if enablements are provided
- if state.Config != nil {
- ets = new(strings.Builder)
- // append enablement strings in order
- for i := system.Enablement(0); i < system.Enablement(system.ELen); i++ {
- if state.Config.Confinement.Enablements.Has(i) {
- ets.WriteString(", " + i.String())
- }
- }
-
- cs = fmt.Sprintf("%q", state.Config.Command)
- }
- if ets != nil {
- // prevent an empty string
- if ets.Len() == 0 {
- ets.WriteString("(No enablements)")
- }
- } else {
- ets = new(strings.Builder)
- ets.WriteString("(No confinement information)")
- }
-
- if !fmsg.Verbose() {
- _, _ = fmt.Fprintf(*w, "\t%d\t%d\t%s\t%s\t%s\n",
- state.PID, aid, now.Sub(state.Time).Round(time.Second).String(), strings.TrimPrefix(ets.String(), ", "), cs)
- } else {
- // emit argv instead when verbose
- _, _ = fmt.Fprintf(*w, "\t%d\t%d\t%s\n",
- state.PID, aid, state.ID)
- }
- }
-
- return nil
- }()
- }); err != nil {
- fmsg.Printf("cannot perform action on app %d: %v", aid, err)
- if !ok {
- fmsg.Fatal("store faulted before printing")
- }
- }
-
- if innerErr != nil {
- fmsg.Fatalf("cannot print launcher state of app %d: %s", aid, innerErr)
- }
-}
diff --git a/main.go b/main.go
index 5821b3ca..e83fea00 100644
--- a/main.go
+++ b/main.go
@@ -112,16 +112,14 @@ func main() {
flag.CommandLine.Usage()
fmsg.Exit(0)
case "ps": // print all state info
- var w *tabwriter.Writer
- state.MustPrintLauncherStateSimpleGlobal(&w, os.Paths().RunDirPath)
- if w != nil {
- if err := w.Flush(); err != nil {
- fmsg.Println("cannot format output:", err)
- }
- } else {
- fmt.Println("No information available")
- }
+ set := flag.NewFlagSet("ps", flag.ExitOnError)
+ var short bool
+ set.BoolVar(&short, "short", false, "Print instance id")
+
+ // Ignore errors; set is set for ExitOnError.
+ _ = set.Parse(args[1:])
+ printPs(short)
fmsg.Exit(0)
case "show": // pretty-print app info
if len(args) != 2 {
diff --git a/print.go b/print.go
index 4c574b7d..f2b60aa6 100644
--- a/print.go
+++ b/print.go
@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
direct "os"
+ "strconv"
"strings"
"text/tabwriter"
"time"
@@ -138,6 +139,78 @@ func printShow(instance *state.State, config *fst.Config) {
}
}
+func printPs(short bool) {
+ now := time.Now().UTC()
+
+ var entries state.Entries
+ s := state.NewMulti(os.Paths().RunDirPath)
+ if e, err := state.Join(s); err != nil {
+ fmsg.Fatalf("cannot join store: %v", err)
+ } else {
+ entries = e
+ }
+ if err := s.Close(); err != nil {
+ fmsg.Printf("cannot close store: %v", err)
+ }
+
+ if short {
+ var v []string
+ if flagJSON {
+ v = make([]string, 0, len(entries))
+ }
+
+ for _, instance := range entries {
+ if !flagJSON {
+ fmt.Println(instance.ID.String())
+ } else {
+ v = append(v, instance.ID.String())
+ }
+ }
+
+ if flagJSON {
+ printJSON(v)
+ }
+
+ return
+ }
+
+ if flagJSON {
+ printJSON(entries)
+ return
+ }
+
+ // buffer output to reduce terminal activity
+ w := tabwriter.NewWriter(direct.Stdout, 0, 1, 4, ' ', 0)
+ fmt.Fprintln(w, "\tInstance\tPID\tApp\tUptime\tEnablements\tCommand")
+ for _, instance := range entries {
+ printInstance(w, instance, now)
+ }
+ if err := w.Flush(); err != nil {
+ fmsg.Fatalf("cannot flush tabwriter: %v", err)
+ }
+}
+
+func printInstance(w *tabwriter.Writer, instance *state.State, now time.Time) {
+ // gracefully skip nil states
+ if instance == nil {
+ fmsg.Println("got invalid state entry")
+ return
+ }
+
+ var (
+ es = "(No confinement information)"
+ cs = "(No command information)"
+ as = "(No configuration information)"
+ )
+ if instance.Config != nil {
+ es = instance.Config.Confinement.Enablements.String()
+ cs = fmt.Sprintf("%q", instance.Config.Command)
+ as = strconv.Itoa(instance.Config.Confinement.AppID)
+ }
+ fmt.Fprintf(w, "\t%s\t%d\t%s\t%s\t%s\t%s\n",
+ instance.ID.String()[:8], instance.PID, as, now.Sub(instance.Time).Round(time.Second).String(), strings.TrimPrefix(es, ", "), cs)
+}
+
func printJSON(v any) {
encoder := json.NewEncoder(direct.Stdout)
encoder.SetIndent("", " ")