system: wrap console output functions
All checks were successful
Test / Create distribution (push) Successful in 24s
Test / Run NixOS test (push) Successful in 3m13s

This eliminates all fmsg imports from internal/system.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-02-17 17:54:28 +09:00
parent 3ae2ab652e
commit b1e1d5627e
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
9 changed files with 93 additions and 92 deletions

View File

@ -252,8 +252,12 @@ func (a *app) Seal(config *fst.Config) error {
// store activity begins after Start is called and must end before Wait // store activity begins after Start is called and must end before Wait
seal.store = state.NewMulti(seal.RunDirPath) seal.store = state.NewMulti(seal.RunDirPath)
// initialise system interface with full uid // initialise system interface with os uid
seal.sys.I = system.New(seal.sys.user.uid) seal.sys.I = system.New(seal.sys.user.uid)
seal.sys.I.IsVerbose = fmsg.Load
seal.sys.I.Verbose = fmsg.Verbose
seal.sys.I.Verbosef = fmsg.Verbosef
seal.sys.I.WrapErr = fmsg.WrapError
// pass through enablements // pass through enablements
seal.et = config.Confinement.Enablements seal.et = config.Confinement.Enablements

View File

@ -5,7 +5,6 @@ import (
"slices" "slices"
"git.gensokyo.uk/security/fortify/acl" "git.gensokyo.uk/security/fortify/acl"
"git.gensokyo.uk/security/fortify/internal/fmsg"
) )
// UpdatePerm appends an ephemeral acl update Op. // UpdatePerm appends an ephemeral acl update Op.
@ -31,23 +30,21 @@ type ACL struct {
perms acl.Perms perms acl.Perms
} }
func (a *ACL) Type() Enablement { func (a *ACL) Type() Enablement { return a.et }
return a.et
}
func (a *ACL) apply(sys *I) error { func (a *ACL) apply(sys *I) error {
fmsg.Verbose("applying ACL", a) sys.println("applying ACL", a)
return fmsg.WrapErrorSuffix(acl.UpdatePerm(a.path, sys.uid, a.perms...), return sys.wrapErrSuffix(acl.UpdatePerm(a.path, sys.uid, a.perms...),
fmt.Sprintf("cannot apply ACL entry to %q:", a.path)) fmt.Sprintf("cannot apply ACL entry to %q:", a.path))
} }
func (a *ACL) revert(sys *I, ec *Criteria) error { func (a *ACL) revert(sys *I, ec *Criteria) error {
if ec.hasType(a) { if ec.hasType(a) {
fmsg.Verbose("stripping ACL", a) sys.println("stripping ACL", a)
return fmsg.WrapErrorSuffix(acl.UpdatePerm(a.path, sys.uid), return sys.wrapErrSuffix(acl.UpdatePerm(a.path, sys.uid),
fmt.Sprintf("cannot strip ACL entry from %q:", a.path)) fmt.Sprintf("cannot strip ACL entry from %q:", a.path))
} else { } else {
fmsg.Verbose("skipping ACL", a) sys.println("skipping ACL", a)
return nil return nil
} }
} }
@ -60,9 +57,7 @@ func (a *ACL) Is(o Op) bool {
slices.Equal(a.perms, a0.perms) slices.Equal(a.perms, a0.perms)
} }
func (a *ACL) Path() string { func (a *ACL) Path() string { return a.path }
return a.path
}
func (a *ACL) String() string { func (a *ACL) String() string {
return fmt.Sprintf("%s type: %s path: %q", return fmt.Sprintf("%s type: %s path: %q",

View File

@ -8,7 +8,6 @@ import (
"sync" "sync"
"git.gensokyo.uk/security/fortify/dbus" "git.gensokyo.uk/security/fortify/dbus"
"git.gensokyo.uk/security/fortify/internal/fmsg"
) )
var ( var (
@ -28,7 +27,7 @@ func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath st
// session bus is mandatory // session bus is mandatory
if session == nil { if session == nil {
return nil, fmsg.WrapError(ErrDBusConfig, return nil, sys.wrapErr(ErrDBusConfig,
"attempted to seal message bus proxy without session bus config") "attempted to seal message bus proxy without session bus config")
} }
@ -48,12 +47,12 @@ func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath st
d.proxy = dbus.New(sessionBus, systemBus) d.proxy = dbus.New(sessionBus, systemBus)
defer func() { defer func() {
if fmsg.Load() && d.proxy.Sealed() { if sys.IsVerbose() && d.proxy.Sealed() {
fmsg.Verbose("sealed session proxy", session.Args(sessionBus)) sys.println("sealed session proxy", session.Args(sessionBus))
if system != nil { if system != nil {
fmsg.Verbose("sealed system proxy", system.Args(systemBus)) sys.println("sealed system proxy", system.Args(systemBus))
} }
fmsg.Verbose("message bus proxy final args:", d.proxy) sys.println("message bus proxy final args:", d.proxy)
} }
}() }()
@ -62,7 +61,7 @@ func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath st
// seal dbus proxy // seal dbus proxy
d.out = &scanToFmsg{msg: new(strings.Builder)} d.out = &scanToFmsg{msg: new(strings.Builder)}
return d.out.Dump, fmsg.WrapErrorSuffix(d.proxy.Seal(session, system), return d.out.Dump, sys.wrapErrSuffix(d.proxy.Seal(session, system),
"cannot seal message bus proxy:") "cannot seal message bus proxy:")
} }
@ -74,32 +73,30 @@ type DBus struct {
system bool system bool
} }
func (d *DBus) Type() Enablement { func (d *DBus) Type() Enablement { return Process }
return Process
}
func (d *DBus) apply(sys *I) error { func (d *DBus) apply(sys *I) error {
fmsg.Verbosef("session bus proxy on %q for upstream %q", d.proxy.Session()[1], d.proxy.Session()[0]) sys.printf("session bus proxy on %q for upstream %q", d.proxy.Session()[1], d.proxy.Session()[0])
if d.system { if d.system {
fmsg.Verbosef("system bus proxy on %q for upstream %q", d.proxy.System()[1], d.proxy.System()[0]) sys.printf("system bus proxy on %q for upstream %q", d.proxy.System()[1], d.proxy.System()[0])
} }
// this starts the process and blocks until ready // this starts the process and blocks until ready
if err := d.proxy.Start(sys.ctx, d.out, true); err != nil { if err := d.proxy.Start(sys.ctx, d.out, true); err != nil {
d.out.Dump() d.out.Dump()
return fmsg.WrapErrorSuffix(err, return sys.wrapErrSuffix(err,
"cannot start message bus proxy:") "cannot start message bus proxy:")
} }
fmsg.Verbose("starting message bus proxy:", d.proxy) sys.println("starting message bus proxy:", d.proxy)
return nil return nil
} }
func (d *DBus) revert(_ *I, _ *Criteria) error { func (d *DBus) revert(sys *I, _ *Criteria) error {
// criteria ignored here since dbus is always process-scoped // criteria ignored here since dbus is always process-scoped
fmsg.Verbose("terminating message bus proxy") sys.println("terminating message bus proxy")
d.proxy.Close() d.proxy.Close()
defer fmsg.Verbose("message bus proxy exit") defer sys.println("message bus proxy exit")
return fmsg.WrapErrorSuffix(d.proxy.Wait(), "message bus proxy error:") return sys.wrapErrSuffix(d.proxy.Wait(), "message bus proxy error:")
} }
func (d *DBus) Is(o Op) bool { func (d *DBus) Is(o Op) bool {

View File

@ -3,8 +3,6 @@ package system
import ( import (
"fmt" "fmt"
"os" "os"
"git.gensokyo.uk/security/fortify/internal/fmsg"
) )
// Link registers an Op that links dst to src. // Link registers an Op that links dst to src.
@ -27,27 +25,23 @@ type Hardlink struct {
func (l *Hardlink) Type() Enablement { return l.et } func (l *Hardlink) Type() Enablement { return l.et }
func (l *Hardlink) apply(_ *I) error { func (l *Hardlink) apply(sys *I) error {
fmsg.Verbose("linking", l) sys.println("linking", l)
return fmsg.WrapErrorSuffix(os.Link(l.src, l.dst), return sys.wrapErrSuffix(os.Link(l.src, l.dst),
fmt.Sprintf("cannot link %q:", l.dst)) fmt.Sprintf("cannot link %q:", l.dst))
} }
func (l *Hardlink) revert(_ *I, ec *Criteria) error { func (l *Hardlink) revert(sys *I, ec *Criteria) error {
if ec.hasType(l) { if ec.hasType(l) {
fmsg.Verbosef("removing hard link %q", l.dst) sys.printf("removing hard link %q", l.dst)
return fmsg.WrapErrorSuffix(os.Remove(l.dst), return sys.wrapErrSuffix(os.Remove(l.dst),
fmt.Sprintf("cannot remove hard link %q:", l.dst)) fmt.Sprintf("cannot remove hard link %q:", l.dst))
} else { } else {
fmsg.Verbosef("skipping hard link %q", l.dst) sys.printf("skipping hard link %q", l.dst)
return nil return nil
} }
} }
func (l *Hardlink) Is(o Op) bool { func (l *Hardlink) Is(o Op) bool { l0, ok := o.(*Hardlink); return ok && l0 != nil && *l == *l0 }
l0, ok := o.(*Hardlink)
return ok && l0 != nil && *l == *l0
}
func (l *Hardlink) Path() string { return l.src } func (l *Hardlink) Path() string { return l.src }
func (l *Hardlink) String() string { return fmt.Sprintf("%q from %q", l.dst, l.src) } func (l *Hardlink) String() string { return fmt.Sprintf("%q from %q", l.dst, l.src) }

View File

@ -4,8 +4,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"git.gensokyo.uk/security/fortify/internal/fmsg"
) )
// Ensure the existence and mode of a directory. // Ensure the existence and mode of a directory.
@ -39,33 +37,33 @@ func (m *Mkdir) Type() Enablement {
return m.et return m.et
} }
func (m *Mkdir) apply(_ *I) error { func (m *Mkdir) apply(sys *I) error {
fmsg.Verbose("ensuring directory", m) sys.println("ensuring directory", m)
// create directory // create directory
err := os.Mkdir(m.path, m.perm) err := os.Mkdir(m.path, m.perm)
if !errors.Is(err, os.ErrExist) { if !errors.Is(err, os.ErrExist) {
return fmsg.WrapErrorSuffix(err, return sys.wrapErrSuffix(err,
fmt.Sprintf("cannot create directory %q:", m.path)) fmt.Sprintf("cannot create directory %q:", m.path))
} }
// directory exists, ensure mode // directory exists, ensure mode
return fmsg.WrapErrorSuffix(os.Chmod(m.path, m.perm), return sys.wrapErrSuffix(os.Chmod(m.path, m.perm),
fmt.Sprintf("cannot change mode of %q to %s:", m.path, m.perm)) fmt.Sprintf("cannot change mode of %q to %s:", m.path, m.perm))
} }
func (m *Mkdir) revert(_ *I, ec *Criteria) error { func (m *Mkdir) revert(sys *I, ec *Criteria) error {
if !m.ephemeral { if !m.ephemeral {
// skip non-ephemeral dir and do not log anything // skip non-ephemeral dir and do not log anything
return nil return nil
} }
if ec.hasType(m) { if ec.hasType(m) {
fmsg.Verbose("destroying ephemeral directory", m) sys.println("destroying ephemeral directory", m)
return fmsg.WrapErrorSuffix(os.Remove(m.path), return sys.wrapErrSuffix(os.Remove(m.path),
fmt.Sprintf("cannot remove ephemeral directory %q:", m.path)) fmt.Sprintf("cannot remove ephemeral directory %q:", m.path))
} else { } else {
fmsg.Verbose("skipping ephemeral directory", m) sys.println("skipping ephemeral directory", m)
return nil return nil
} }
} }

View File

@ -5,8 +5,6 @@ import (
"errors" "errors"
"log" "log"
"sync" "sync"
"git.gensokyo.uk/security/fortify/internal/fmsg"
) )
const ( const (
@ -55,19 +53,42 @@ func TypeString(e Enablement) string {
} }
} }
// New initialises sys with no-op verbose functions.
func New(uid int) (sys *I) {
sys = new(I)
sys.uid = uid
sys.IsVerbose = func() bool { return false }
sys.Verbose = func(...any) {}
sys.Verbosef = func(string, ...any) {}
sys.WrapErr = func(err error, _ ...any) error { return err }
return
}
type I struct { type I struct {
uid int uid int
ops []Op ops []Op
ctx context.Context ctx context.Context
IsVerbose func() bool
Verbose func(v ...any)
Verbosef func(format string, v ...any)
WrapErr func(err error, a ...any) error
// whether sys has been reverted // whether sys has been reverted
state bool state bool
lock sync.Mutex lock sync.Mutex
} }
func (sys *I) UID() int { func (sys *I) UID() int { return sys.uid }
return sys.uid func (sys *I) println(v ...any) { sys.Verbose(v...) }
func (sys *I) printf(format string, v ...any) { sys.Verbosef(format, v...) }
func (sys *I) wrapErr(err error, a ...any) error { return sys.WrapErr(err, a...) }
func (sys *I) wrapErrSuffix(err error, a ...any) error {
if err == nil {
return nil
}
return sys.wrapErr(err, append(a, err)...)
} }
func (sys *I) Equal(v *I) bool { func (sys *I) Equal(v *I) bool {
@ -99,7 +120,7 @@ func (sys *I) Commit(ctx context.Context) error {
// sp is set to nil when all ops are applied // sp is set to nil when all ops are applied
if sp != nil { if sp != nil {
// rollback partial commit // rollback partial commit
fmsg.Verbosef("commit faulted after %d ops, rolling back partial commit", len(sp.ops)) sys.printf("commit faulted after %d ops, rolling back partial commit", len(sp.ops))
if err := sp.Revert(&Criteria{nil}); err != nil { if err := sp.Revert(&Criteria{nil}); err != nil {
log.Println("errors returned reverting partial commit:", err) log.Println("errors returned reverting partial commit:", err)
} }
@ -139,7 +160,3 @@ func (sys *I) Revert(ec *Criteria) error {
// errors.Join filters nils // errors.Join filters nils
return errors.Join(errs...) return errors.Join(errs...)
} }
func New(uid int) *I {
return &I{uid: uid}
}

View File

@ -7,8 +7,6 @@ import (
"io" "io"
"os" "os"
"syscall" "syscall"
"git.gensokyo.uk/security/fortify/internal/fmsg"
) )
// CopyFile registers an Op that copies from src. // CopyFile registers an Op that copies from src.
@ -33,8 +31,8 @@ type Tmpfile struct {
} }
func (t *Tmpfile) Type() Enablement { return Process } func (t *Tmpfile) Type() Enablement { return Process }
func (t *Tmpfile) apply(_ *I) error { func (t *Tmpfile) apply(sys *I) error {
fmsg.Verbose("copying", t) sys.println("copying", t)
if t.payload == nil { if t.payload == nil {
// this is a misuse of the API; do not return an error message // this is a misuse of the API; do not return an error message
@ -42,25 +40,25 @@ func (t *Tmpfile) apply(_ *I) error {
} }
if b, err := os.Stat(t.src); err != nil { if b, err := os.Stat(t.src); err != nil {
return fmsg.WrapErrorSuffix(err, return sys.wrapErrSuffix(err,
fmt.Sprintf("cannot stat %q:", t.src)) fmt.Sprintf("cannot stat %q:", t.src))
} else { } else {
if b.IsDir() { if b.IsDir() {
return fmsg.WrapErrorSuffix(syscall.EISDIR, return sys.wrapErrSuffix(syscall.EISDIR,
fmt.Sprintf("%q is a directory", t.src)) fmt.Sprintf("%q is a directory", t.src))
} }
if s := b.Size(); s > t.n { if s := b.Size(); s > t.n {
return fmsg.WrapErrorSuffix(syscall.ENOMEM, return sys.wrapErrSuffix(syscall.ENOMEM,
fmt.Sprintf("file %q is too long: %d > %d", fmt.Sprintf("file %q is too long: %d > %d",
t.src, s, t.n)) t.src, s, t.n))
} }
} }
if f, err := os.Open(t.src); err != nil { if f, err := os.Open(t.src); err != nil {
return fmsg.WrapErrorSuffix(err, return sys.wrapErrSuffix(err,
fmt.Sprintf("cannot open %q:", t.src)) fmt.Sprintf("cannot open %q:", t.src))
} else if _, err = io.CopyN(t.buf, f, t.n); err != nil { } else if _, err = io.CopyN(t.buf, f, t.n); err != nil {
return fmsg.WrapErrorSuffix(err, return sys.wrapErrSuffix(err,
fmt.Sprintf("cannot read from %q:", t.src)) fmt.Sprintf("cannot read from %q:", t.src))
} }

View File

@ -6,7 +6,6 @@ import (
"os" "os"
"git.gensokyo.uk/security/fortify/acl" "git.gensokyo.uk/security/fortify/acl"
"git.gensokyo.uk/security/fortify/internal/fmsg"
"git.gensokyo.uk/security/fortify/wl" "git.gensokyo.uk/security/fortify/wl"
) )
@ -47,35 +46,35 @@ func (w *Wayland) apply(sys *I) error {
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
err = os.ErrNotExist err = os.ErrNotExist
} }
return fmsg.WrapErrorSuffix(err, return sys.wrapErrSuffix(err,
fmt.Sprintf("cannot attach to wayland on %q:", w.src)) fmt.Sprintf("cannot attach to wayland on %q:", w.src))
} else { } else {
fmsg.Verbosef("wayland attached on %q", w.src) sys.printf("wayland attached on %q", w.src)
} }
if sp, err := w.conn.Bind(w.dst, w.appID, w.instanceID); err != nil { if sp, err := w.conn.Bind(w.dst, w.appID, w.instanceID); err != nil {
return fmsg.WrapErrorSuffix(err, return sys.wrapErrSuffix(err,
fmt.Sprintf("cannot bind to socket on %q:", w.dst)) fmt.Sprintf("cannot bind to socket on %q:", w.dst))
} else { } else {
*w.sync = sp *w.sync = sp
fmsg.Verbosef("wayland listening on %q", w.dst) sys.printf("wayland listening on %q", w.dst)
return fmsg.WrapErrorSuffix(errors.Join(os.Chmod(w.dst, 0), acl.UpdatePerm(w.dst, sys.uid, acl.Read, acl.Write, acl.Execute)), return sys.wrapErrSuffix(errors.Join(os.Chmod(w.dst, 0), acl.UpdatePerm(w.dst, sys.uid, acl.Read, acl.Write, acl.Execute)),
fmt.Sprintf("cannot chmod socket on %q:", w.dst)) fmt.Sprintf("cannot chmod socket on %q:", w.dst))
} }
} }
func (w *Wayland) revert(_ *I, ec *Criteria) error { func (w *Wayland) revert(sys *I, ec *Criteria) error {
if ec.hasType(w) { if ec.hasType(w) {
fmsg.Verbosef("removing wayland socket on %q", w.dst) sys.printf("removing wayland socket on %q", w.dst)
if err := os.Remove(w.dst); err != nil && !errors.Is(err, os.ErrNotExist) { if err := os.Remove(w.dst); err != nil && !errors.Is(err, os.ErrNotExist) {
return err return err
} }
fmsg.Verbosef("detaching from wayland on %q", w.src) sys.printf("detaching from wayland on %q", w.src)
return fmsg.WrapErrorSuffix(w.conn.Close(), return sys.wrapErrSuffix(w.conn.Close(),
fmt.Sprintf("cannot detach from wayland on %q:", w.src)) fmt.Sprintf("cannot detach from wayland on %q:", w.src))
} else { } else {
fmsg.Verbosef("skipping wayland cleanup on %q", w.dst) sys.printf("skipping wayland cleanup on %q", w.dst)
return nil return nil
} }
} }

View File

@ -3,7 +3,6 @@ package system
import ( import (
"fmt" "fmt"
"git.gensokyo.uk/security/fortify/internal/fmsg"
"git.gensokyo.uk/security/fortify/xcb" "git.gensokyo.uk/security/fortify/xcb"
) )
@ -23,19 +22,19 @@ func (x XHost) Type() Enablement {
return EX11 return EX11
} }
func (x XHost) apply(_ *I) error { func (x XHost) apply(sys *I) error {
fmsg.Verbosef("inserting entry %s to X11", x) sys.printf("inserting entry %s to X11", x)
return fmsg.WrapErrorSuffix(xcb.ChangeHosts(xcb.HostModeInsert, xcb.FamilyServerInterpreted, "localuser\x00"+string(x)), return sys.wrapErrSuffix(xcb.ChangeHosts(xcb.HostModeInsert, xcb.FamilyServerInterpreted, "localuser\x00"+string(x)),
fmt.Sprintf("cannot insert entry %s to X11:", x)) fmt.Sprintf("cannot insert entry %s to X11:", x))
} }
func (x XHost) revert(_ *I, ec *Criteria) error { func (x XHost) revert(sys *I, ec *Criteria) error {
if ec.hasType(x) { if ec.hasType(x) {
fmsg.Verbosef("deleting entry %s from X11", x) sys.printf("deleting entry %s from X11", x)
return fmsg.WrapErrorSuffix(xcb.ChangeHosts(xcb.HostModeDelete, xcb.FamilyServerInterpreted, "localuser\x00"+string(x)), return sys.wrapErrSuffix(xcb.ChangeHosts(xcb.HostModeDelete, xcb.FamilyServerInterpreted, "localuser\x00"+string(x)),
fmt.Sprintf("cannot delete entry %s from X11:", x)) fmt.Sprintf("cannot delete entry %s from X11:", x))
} else { } else {
fmsg.Verbosef("skipping entry %s in X11", x) sys.printf("skipping entry %s in X11", x)
return nil return nil
} }
} }