fmsg: support temporarily withholding output
test / test (push) Successful in 31s
Details
test / test (push) Successful in 31s
Details
Trying to print to a shared stdout is a terrible idea. This change makes it possible to withhold output for the lifetime of the sandbox. Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
This commit is contained in:
parent
093e99d062
commit
ae1a102882
|
@ -65,7 +65,7 @@ func tryTemplate() {
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(string(s))
|
fmt.Println(string(s))
|
||||||
}
|
}
|
||||||
os.Exit(0)
|
fmsg.Exit(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
3
error.go
3
error.go
|
@ -2,7 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.ophivana.moe/security/fortify/internal/app"
|
"git.ophivana.moe/security/fortify/internal/app"
|
||||||
"git.ophivana.moe/security/fortify/internal/fmsg"
|
"git.ophivana.moe/security/fortify/internal/fmsg"
|
||||||
|
@ -51,6 +50,6 @@ func logBaseError(err error, message string) {
|
||||||
if fmsg.AsBaseError(err, &e) {
|
if fmsg.AsBaseError(err, &e) {
|
||||||
fmsg.Print(e.Message())
|
fmsg.Print(e.Message())
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(message, err)
|
fmsg.Println(message, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.ophivana.moe/security/fortify/helper/bwrap"
|
"git.ophivana.moe/security/fortify/helper/bwrap"
|
||||||
|
"git.ophivana.moe/security/fortify/internal/fmsg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InternalChildStub is an internal function but exported because it is cross-package;
|
// InternalChildStub is an internal function but exported because it is cross-package;
|
||||||
|
@ -33,7 +34,7 @@ func InternalChildStub() {
|
||||||
genericStub(argsFD, statFD)
|
genericStub(argsFD, statFD)
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Exit(0)
|
fmsg.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InternalReplaceExecCommand is an internal function but exported because it is cross-package;
|
// InternalReplaceExecCommand is an internal function but exported because it is cross-package;
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
package fmsg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
wstate atomic.Bool
|
||||||
|
withhold = make(chan struct{}, 1)
|
||||||
|
msgbuf = make(chan dOp, 64) // these ops are tiny so a large buffer is allocated for withholding output
|
||||||
|
|
||||||
|
dequeueOnce sync.Once
|
||||||
|
queueSync sync.WaitGroup
|
||||||
|
)
|
||||||
|
|
||||||
|
func dequeue() {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case op := <-msgbuf:
|
||||||
|
op.Do()
|
||||||
|
queueSync.Done()
|
||||||
|
case <-withhold:
|
||||||
|
<-withhold
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
type dOp interface{ Do() }
|
||||||
|
|
||||||
|
func Exit(code int) {
|
||||||
|
queueSync.Wait()
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Withhold() {
|
||||||
|
if wstate.CompareAndSwap(false, true) {
|
||||||
|
withhold <- struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Resume() {
|
||||||
|
if wstate.CompareAndSwap(true, false) {
|
||||||
|
withhold <- struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type dPrint []any
|
||||||
|
|
||||||
|
func (v dPrint) Do() {
|
||||||
|
std.Print(v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type dPrintf struct {
|
||||||
|
format string
|
||||||
|
v []any
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dPrintf) Do() {
|
||||||
|
std.Printf(d.format, d.v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type dPrintln []any
|
||||||
|
|
||||||
|
func (v dPrintln) Do() {
|
||||||
|
std.Println(v...)
|
||||||
|
}
|
|
@ -4,38 +4,40 @@ package fmsg
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"sync/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var std = log.New(os.Stderr, "fortify: ", 0)
|
||||||
std = log.New(os.Stdout, "fortify: ", 0)
|
|
||||||
warn = log.New(os.Stderr, "fortify: ", 0)
|
|
||||||
|
|
||||||
verbose = new(atomic.Bool)
|
|
||||||
)
|
|
||||||
|
|
||||||
func SetPrefix(prefix string) {
|
func SetPrefix(prefix string) {
|
||||||
prefix += ": "
|
prefix += ": "
|
||||||
std.SetPrefix(prefix)
|
std.SetPrefix(prefix)
|
||||||
warn.SetPrefix(prefix)
|
std.SetPrefix(prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Print(v ...any) {
|
func Print(v ...any) {
|
||||||
warn.Print(v...)
|
dequeueOnce.Do(dequeue)
|
||||||
|
queueSync.Add(1)
|
||||||
|
msgbuf <- dPrint(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Printf(format string, v ...any) {
|
func Printf(format string, v ...any) {
|
||||||
warn.Printf(format, v...)
|
dequeueOnce.Do(dequeue)
|
||||||
|
queueSync.Add(1)
|
||||||
|
msgbuf <- &dPrintf{format, v}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Println(v ...any) {
|
func Println(v ...any) {
|
||||||
warn.Println(v...)
|
dequeueOnce.Do(dequeue)
|
||||||
|
queueSync.Add(1)
|
||||||
|
msgbuf <- dPrintln(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Fatal(v ...any) {
|
func Fatal(v ...any) {
|
||||||
warn.Fatal(v...)
|
Print(v...)
|
||||||
|
Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Fatalf(format string, v ...any) {
|
func Fatalf(format string, v ...any) {
|
||||||
warn.Fatalf(format, v...)
|
Printf(format, v...)
|
||||||
|
Exit(1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package fmsg
|
package fmsg
|
||||||
|
|
||||||
|
import "sync/atomic"
|
||||||
|
|
||||||
|
var verbose = new(atomic.Bool)
|
||||||
|
|
||||||
func Verbose() bool {
|
func Verbose() bool {
|
||||||
return verbose.Load()
|
return verbose.Load()
|
||||||
}
|
}
|
||||||
|
@ -10,12 +14,12 @@ func SetVerbose(v bool) {
|
||||||
|
|
||||||
func VPrintf(format string, v ...any) {
|
func VPrintf(format string, v ...any) {
|
||||||
if verbose.Load() {
|
if verbose.Load() {
|
||||||
std.Printf(format, v...)
|
Printf(format, v...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func VPrintln(v ...any) {
|
func VPrintln(v ...any) {
|
||||||
if verbose.Load() {
|
if verbose.Load() {
|
||||||
std.Println(v...)
|
Println(v...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ func doInit(fd uintptr) {
|
||||||
select {
|
select {
|
||||||
case s := <-sig:
|
case s := <-sig:
|
||||||
fmsg.VPrintln("received", s.String())
|
fmsg.VPrintln("received", s.String())
|
||||||
os.Exit(0)
|
fmsg.Exit(0)
|
||||||
case w := <-info:
|
case w := <-info:
|
||||||
if w.wpid == cmd.Process.Pid {
|
if w.wpid == cmd.Process.Pid {
|
||||||
switch {
|
switch {
|
||||||
|
@ -147,10 +147,10 @@ func doInit(fd uintptr) {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
case <-done:
|
case <-done:
|
||||||
os.Exit(r)
|
fmsg.Exit(r)
|
||||||
case <-timeout:
|
case <-timeout:
|
||||||
fmsg.Println("timeout exceeded waiting for lingering processes")
|
fmsg.Println("timeout exceeded waiting for lingering processes")
|
||||||
os.Exit(r)
|
fmsg.Exit(r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,9 +134,9 @@ func doShim(socket string) {
|
||||||
fmsg.VPrintln("wait:", err)
|
fmsg.VPrintln("wait:", err)
|
||||||
}
|
}
|
||||||
if b.Unwrap().ProcessState != nil {
|
if b.Unwrap().ProcessState != nil {
|
||||||
os.Exit(b.Unwrap().ProcessState.ExitCode())
|
fmsg.Exit(b.Unwrap().ProcessState.ExitCode())
|
||||||
} else {
|
} else {
|
||||||
os.Exit(127)
|
fmsg.Exit(127)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,7 @@ func MustPrintLauncherStateSimpleGlobal(w **tabwriter.Writer, runDir string) {
|
||||||
|
|
||||||
// read runtime directory to get all UIDs
|
// read runtime directory to get all UIDs
|
||||||
if dirs, err := os.ReadDir(path.Join(runDir, "state")); err != nil && !errors.Is(err, os.ErrNotExist) {
|
if dirs, err := os.ReadDir(path.Join(runDir, "state")); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
fmsg.Println("cannot read runtime directory:", err)
|
fmsg.Fatal("cannot read runtime directory:", err)
|
||||||
os.Exit(1)
|
|
||||||
} else {
|
} else {
|
||||||
for _, e := range dirs {
|
for _, e := range dirs {
|
||||||
// skip non-directories
|
// skip non-directories
|
||||||
|
@ -112,13 +111,11 @@ func (s *simpleStore) mustPrintLauncherState(w **tabwriter.Writer, now time.Time
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
fmsg.Printf("cannot perform action on store %q: %s", path.Join(s.path...), err)
|
fmsg.Printf("cannot perform action on store %q: %s", path.Join(s.path...), err)
|
||||||
if !ok {
|
if !ok {
|
||||||
fmsg.Println("store faulted before printing")
|
fmsg.Fatal("store faulted before printing")
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if innerErr != nil {
|
if innerErr != nil {
|
||||||
fmsg.Printf("cannot print launcher state for store %q: %s", path.Join(s.path...), innerErr)
|
fmsg.Fatalf("cannot print launcher state for store %q: %s", path.Join(s.path...), innerErr)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,7 @@ func (s *Std) Open(name string) (fs.File, error) {
|
||||||
return os.Open(name)
|
return os.Open(name)
|
||||||
}
|
}
|
||||||
func (s *Std) Exit(code int) {
|
func (s *Std) Exit(code int) {
|
||||||
os.Exit(code)
|
fmsg.Exit(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
const xdgRuntimeDir = "XDG_RUNTIME_DIR"
|
const xdgRuntimeDir = "XDG_RUNTIME_DIR"
|
||||||
|
|
Loading…
Reference in New Issue