All checks were successful
Test / Create distribution (push) Successful in 35s
Test / Hakurei (push) Successful in 45s
Test / Hpkg (push) Successful in 3m17s
Test / Sandbox (push) Successful in 43s
Test / Hakurei (race detector) (push) Successful in 45s
Test / Sandbox (race detector) (push) Successful in 43s
Test / Flake checks (push) Successful in 1m27s
Having the bit field value here (in decimal, no less) is unfriendly to text editors. Use a bunch of booleans here to improve ease of use. Signed-off-by: Ophestra <cat@gensokyo.uk>
308 lines
7.1 KiB
Go
308 lines
7.1 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
"text/tabwriter"
|
|
"time"
|
|
|
|
"hakurei.app/container"
|
|
"hakurei.app/hst"
|
|
"hakurei.app/internal/app/state"
|
|
"hakurei.app/internal/hlog"
|
|
"hakurei.app/system/dbus"
|
|
)
|
|
|
|
func printShowSystem(output io.Writer, short, flagJSON bool) {
|
|
t := newPrinter(output)
|
|
defer t.MustFlush()
|
|
|
|
info := &hst.Info{Paths: std.Paths()}
|
|
|
|
// get hid by querying uid of identity 0
|
|
if uid, err := std.Uid(0); err != nil {
|
|
hlog.PrintBaseError(err, "cannot obtain uid from setuid wrapper:")
|
|
os.Exit(1)
|
|
} else {
|
|
info.User = (uid / 10000) - 100
|
|
}
|
|
|
|
if flagJSON {
|
|
printJSON(output, short, info)
|
|
return
|
|
}
|
|
|
|
t.Printf("User:\t%d\n", info.User)
|
|
t.Printf("TempDir:\t%s\n", info.TempDir)
|
|
t.Printf("SharePath:\t%s\n", info.SharePath)
|
|
t.Printf("RuntimePath:\t%s\n", info.RuntimePath)
|
|
t.Printf("RunDirPath:\t%s\n", info.RunDirPath)
|
|
}
|
|
|
|
func printShowInstance(
|
|
output io.Writer, now time.Time,
|
|
instance *state.State, config *hst.Config,
|
|
short, flagJSON bool) {
|
|
if flagJSON {
|
|
if instance != nil {
|
|
printJSON(output, short, instance)
|
|
} else {
|
|
printJSON(output, short, config)
|
|
}
|
|
return
|
|
}
|
|
|
|
t := newPrinter(output)
|
|
defer t.MustFlush()
|
|
|
|
if config.Container == nil {
|
|
mustPrint(output, "Warning: this configuration uses permissive defaults!\n\n")
|
|
}
|
|
|
|
if instance != nil {
|
|
t.Printf("State\n")
|
|
t.Printf(" Instance:\t%s (%d)\n", instance.ID.String(), instance.PID)
|
|
t.Printf(" Uptime:\t%s\n", now.Sub(instance.Time).Round(time.Second).String())
|
|
t.Printf("\n")
|
|
}
|
|
|
|
t.Printf("App\n")
|
|
if config.ID != "" {
|
|
t.Printf(" Identity:\t%d (%s)\n", config.Identity, config.ID)
|
|
} else {
|
|
t.Printf(" Identity:\t%d\n", config.Identity)
|
|
}
|
|
t.Printf(" Enablements:\t%s\n", config.Enablements.Unwrap().String())
|
|
if len(config.Groups) > 0 {
|
|
t.Printf(" Groups:\t%s\n", strings.Join(config.Groups, ", "))
|
|
}
|
|
if config.Data != nil {
|
|
t.Printf(" Data:\t%s\n", config.Data)
|
|
}
|
|
if config.Container != nil {
|
|
params := config.Container
|
|
if params.Hostname != "" {
|
|
t.Printf(" Hostname:\t%s\n", params.Hostname)
|
|
}
|
|
flags := make([]string, 0, 7)
|
|
writeFlag := func(name string, value bool) {
|
|
if value {
|
|
flags = append(flags, name)
|
|
}
|
|
}
|
|
writeFlag("userns", params.Userns)
|
|
writeFlag("devel", params.Devel)
|
|
writeFlag("net", params.Net)
|
|
writeFlag("device", params.Device)
|
|
writeFlag("tty", params.Tty)
|
|
writeFlag("mapuid", params.MapRealUID)
|
|
writeFlag("directwl", config.DirectWayland)
|
|
writeFlag("autoetc", params.AutoEtc)
|
|
if len(flags) == 0 {
|
|
flags = append(flags, "none")
|
|
}
|
|
t.Printf(" Flags:\t%s\n", strings.Join(flags, " "))
|
|
|
|
if params.AutoRoot != nil {
|
|
t.Printf(" Root:\t%s (%d)\n", params.AutoRoot, params.RootFlags)
|
|
}
|
|
|
|
etc := params.Etc
|
|
if etc == nil {
|
|
etc = container.AbsFHSEtc
|
|
}
|
|
t.Printf(" Etc:\t%s\n", etc)
|
|
|
|
if config.Path != nil {
|
|
t.Printf(" Path:\t%s\n", config.Path)
|
|
}
|
|
}
|
|
if len(config.Args) > 0 {
|
|
t.Printf(" Arguments:\t%s\n", strings.Join(config.Args, " "))
|
|
}
|
|
t.Printf("\n")
|
|
|
|
if !short {
|
|
if config.Container != nil && len(config.Container.Filesystem) > 0 {
|
|
t.Printf("Filesystem\n")
|
|
for _, f := range config.Container.Filesystem {
|
|
if !f.Valid() {
|
|
t.Println(" <invalid>")
|
|
continue
|
|
}
|
|
t.Printf(" %s\n", f)
|
|
}
|
|
t.Printf("\n")
|
|
}
|
|
if len(config.ExtraPerms) > 0 {
|
|
t.Printf("Extra ACL\n")
|
|
for _, p := range config.ExtraPerms {
|
|
if p == nil {
|
|
continue
|
|
}
|
|
t.Printf(" %s\n", p.String())
|
|
}
|
|
t.Printf("\n")
|
|
}
|
|
}
|
|
|
|
printDBus := func(c *dbus.Config) {
|
|
t.Printf(" Filter:\t%v\n", c.Filter)
|
|
if len(c.See) > 0 {
|
|
t.Printf(" See:\t%q\n", c.See)
|
|
}
|
|
if len(c.Talk) > 0 {
|
|
t.Printf(" Talk:\t%q\n", c.Talk)
|
|
}
|
|
if len(c.Own) > 0 {
|
|
t.Printf(" Own:\t%q\n", c.Own)
|
|
}
|
|
if len(c.Call) > 0 {
|
|
t.Printf(" Call:\t%q\n", c.Call)
|
|
}
|
|
if len(c.Broadcast) > 0 {
|
|
t.Printf(" Broadcast:\t%q\n", c.Broadcast)
|
|
}
|
|
}
|
|
if config.SessionBus != nil {
|
|
t.Printf("Session bus\n")
|
|
printDBus(config.SessionBus)
|
|
t.Printf("\n")
|
|
}
|
|
if config.SystemBus != nil {
|
|
t.Printf("System bus\n")
|
|
printDBus(config.SystemBus)
|
|
t.Printf("\n")
|
|
}
|
|
}
|
|
|
|
func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON bool) {
|
|
var entries state.Entries
|
|
if e, err := state.Join(s); err != nil {
|
|
log.Fatalf("cannot join store: %v", err)
|
|
} else {
|
|
entries = e
|
|
}
|
|
if err := s.Close(); err != nil {
|
|
log.Printf("cannot close store: %v", err)
|
|
}
|
|
|
|
if !short && flagJSON {
|
|
es := make(map[string]*state.State, len(entries))
|
|
for id, instance := range entries {
|
|
es[id.String()] = instance
|
|
}
|
|
printJSON(output, short, es)
|
|
return
|
|
}
|
|
|
|
// sort state entries by id string to ensure consistency between runs
|
|
exp := make([]*expandedStateEntry, 0, len(entries))
|
|
for id, instance := range entries {
|
|
// gracefully skip nil states
|
|
if instance == nil {
|
|
log.Printf("got invalid state entry %s", id.String())
|
|
continue
|
|
}
|
|
|
|
// gracefully skip inconsistent states
|
|
if id != instance.ID {
|
|
log.Printf("possible store corruption: entry %s has id %s",
|
|
id.String(), instance.ID.String())
|
|
continue
|
|
}
|
|
exp = append(exp, &expandedStateEntry{s: id.String(), State: instance})
|
|
}
|
|
slices.SortFunc(exp, func(a, b *expandedStateEntry) int { return a.Time.Compare(b.Time) })
|
|
|
|
if short {
|
|
if flagJSON {
|
|
v := make([]string, len(exp))
|
|
for i, e := range exp {
|
|
v[i] = e.s
|
|
}
|
|
printJSON(output, short, v)
|
|
} else {
|
|
for _, e := range exp {
|
|
mustPrintln(output, e.s[:8])
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
t := newPrinter(output)
|
|
defer t.MustFlush()
|
|
|
|
t.Println("\tInstance\tPID\tApplication\tUptime")
|
|
for _, e := range exp {
|
|
if len(e.s) != 1<<5 {
|
|
// unreachable
|
|
log.Printf("possible store corruption: invalid instance string %s", e.s)
|
|
continue
|
|
}
|
|
|
|
as := "(No configuration information)"
|
|
if e.Config != nil {
|
|
as = strconv.Itoa(e.Config.Identity)
|
|
id := e.Config.ID
|
|
if id == "" {
|
|
id = "app.hakurei." + e.s[:8]
|
|
}
|
|
as += " (" + id + ")"
|
|
}
|
|
t.Printf("\t%s\t%d\t%s\t%s\n",
|
|
e.s[:8], e.PID, as, now.Sub(e.Time).Round(time.Second).String())
|
|
}
|
|
}
|
|
|
|
type expandedStateEntry struct {
|
|
s string
|
|
*state.State
|
|
}
|
|
|
|
func printJSON(output io.Writer, short bool, v any) {
|
|
encoder := json.NewEncoder(output)
|
|
if !short {
|
|
encoder.SetIndent("", " ")
|
|
}
|
|
if err := encoder.Encode(v); err != nil {
|
|
log.Fatalf("cannot serialise: %v", err)
|
|
}
|
|
}
|
|
|
|
func newPrinter(output io.Writer) *tp { return &tp{tabwriter.NewWriter(output, 0, 1, 4, ' ', 0)} }
|
|
|
|
type tp struct{ *tabwriter.Writer }
|
|
|
|
func (p *tp) Printf(format string, a ...any) {
|
|
if _, err := fmt.Fprintf(p, format, a...); err != nil {
|
|
log.Fatalf("cannot write to tabwriter: %v", err)
|
|
}
|
|
}
|
|
func (p *tp) Println(a ...any) {
|
|
if _, err := fmt.Fprintln(p, a...); err != nil {
|
|
log.Fatalf("cannot write to tabwriter: %v", err)
|
|
}
|
|
}
|
|
func (p *tp) MustFlush() {
|
|
if err := p.Writer.Flush(); err != nil {
|
|
log.Fatalf("cannot flush tabwriter: %v", err)
|
|
}
|
|
}
|
|
func mustPrint(output io.Writer, a ...any) {
|
|
if _, err := fmt.Fprint(output, a...); err != nil {
|
|
log.Fatalf("cannot print: %v", err)
|
|
}
|
|
}
|
|
func mustPrintln(output io.Writer, a ...any) {
|
|
if _, err := fmt.Fprintln(output, a...); err != nil {
|
|
log.Fatalf("cannot print: %v", err)
|
|
}
|
|
}
|