fmsg: implement suspend in writer
This removes the requirement to call fmsg.Exit on every exit path, and enables direct use of the "log" package. However, fmsg.BeforeExit is still encouraged when possible to catch exit on suspended output. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
33a4ab11c2
commit
e599b5583d
@ -2,10 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/dbus"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
"git.gensokyo.uk/security/fortify/internal/system"
|
||||
)
|
||||
|
||||
@ -63,18 +63,18 @@ func loadBundleInfo(name string, beforeFail func()) *bundleInfo {
|
||||
bundle := new(bundleInfo)
|
||||
if f, err := os.Open(name); err != nil {
|
||||
beforeFail()
|
||||
fmsg.Fatalf("cannot open bundle: %v", err)
|
||||
log.Fatalf("cannot open bundle: %v", err)
|
||||
} else if err = json.NewDecoder(f).Decode(&bundle); err != nil {
|
||||
beforeFail()
|
||||
fmsg.Fatalf("cannot parse bundle metadata: %v", err)
|
||||
log.Fatalf("cannot parse bundle metadata: %v", err)
|
||||
} else if err = f.Close(); err != nil {
|
||||
fmsg.Printf("cannot close bundle metadata: %v", err)
|
||||
log.Printf("cannot close bundle metadata: %v", err)
|
||||
// not fatal
|
||||
}
|
||||
|
||||
if bundle.ID == "" {
|
||||
beforeFail()
|
||||
fmsg.Fatal("application identifier must not be empty")
|
||||
log.Fatal("application identifier must not be empty")
|
||||
}
|
||||
|
||||
return bundle
|
||||
@ -82,7 +82,7 @@ func loadBundleInfo(name string, beforeFail func()) *bundleInfo {
|
||||
|
||||
func formatHostname(name string) string {
|
||||
if h, err := os.Hostname(); err != nil {
|
||||
fmsg.Printf("cannot get hostname: %v", err)
|
||||
log.Printf("cannot get hostname: %v", err)
|
||||
return "fortify-" + name
|
||||
} else {
|
||||
return h + "-" + name
|
||||
|
@ -3,10 +3,12 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/fst"
|
||||
"git.gensokyo.uk/security/fortify/internal"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
)
|
||||
|
||||
@ -25,12 +27,12 @@ func actionInstall(args []string) {
|
||||
args = set.Args()
|
||||
|
||||
if len(args) != 1 {
|
||||
fmsg.Fatal("invalid argument")
|
||||
log.Fatal("invalid argument")
|
||||
}
|
||||
pkgPath := args[0]
|
||||
if !path.IsAbs(pkgPath) {
|
||||
if dir, err := os.Getwd(); err != nil {
|
||||
fmsg.Fatalf("cannot get current directory: %v", err)
|
||||
log.Fatalf("cannot get current directory: %v", err)
|
||||
} else {
|
||||
pkgPath = path.Join(dir, pkgPath)
|
||||
}
|
||||
@ -54,7 +56,7 @@ func actionInstall(args []string) {
|
||||
|
||||
var workDir string
|
||||
if p, err := os.MkdirTemp("", "fpkg.*"); err != nil {
|
||||
fmsg.Fatalf("cannot create temporary directory: %v", err)
|
||||
log.Fatalf("cannot create temporary directory: %v", err)
|
||||
} else {
|
||||
workDir = p
|
||||
}
|
||||
@ -78,19 +80,17 @@ func actionInstall(args []string) {
|
||||
if s, err := os.Stat(pathSet.metaPath); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
cleanup()
|
||||
fmsg.Fatalf("cannot access %q: %v", pathSet.metaPath, err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot access %q: %v", pathSet.metaPath, err)
|
||||
}
|
||||
// did not modify app, clean installation condition met later
|
||||
} else if s.IsDir() {
|
||||
cleanup()
|
||||
fmsg.Fatalf("metadata path %q is not a file", pathSet.metaPath)
|
||||
panic("unreachable")
|
||||
log.Fatalf("metadata path %q is not a file", pathSet.metaPath)
|
||||
} else {
|
||||
app = loadBundleInfo(pathSet.metaPath, cleanup)
|
||||
if app.ID != bundle.ID {
|
||||
cleanup()
|
||||
fmsg.Fatalf("app %q claims to have identifier %q", bundle.ID, app.ID)
|
||||
log.Fatalf("app %q claims to have identifier %q", bundle.ID, app.ID)
|
||||
}
|
||||
// sec: should verify credentials
|
||||
}
|
||||
@ -102,21 +102,20 @@ func actionInstall(args []string) {
|
||||
app.Launcher == bundle.Launcher &&
|
||||
app.ActivationPackage == bundle.ActivationPackage {
|
||||
cleanup()
|
||||
fmsg.Printf("package %q is identical to local application %q", pkgPath, app.ID)
|
||||
fmsg.Exit(0)
|
||||
log.Printf("package %q is identical to local application %q", pkgPath, app.ID)
|
||||
internal.Exit(0)
|
||||
}
|
||||
|
||||
// AppID determines uid
|
||||
if app.AppID != bundle.AppID {
|
||||
cleanup()
|
||||
fmsg.Fatalf("package %q app id %d differs from installed %d", pkgPath, bundle.AppID, app.AppID)
|
||||
panic("unreachable")
|
||||
log.Fatalf("package %q app id %d differs from installed %d", pkgPath, bundle.AppID, app.AppID)
|
||||
}
|
||||
|
||||
// sec: should compare version string
|
||||
fmsg.VPrintf("installing application %q version %q over local %q", bundle.ID, bundle.Version, app.Version)
|
||||
fmsg.Verbosef("installing application %q version %q over local %q", bundle.ID, bundle.Version, app.Version)
|
||||
} else {
|
||||
fmsg.VPrintf("application %q clean installation", bundle.ID)
|
||||
fmsg.Verbosef("application %q clean installation", bundle.ID)
|
||||
// sec: should install credentials
|
||||
}
|
||||
|
||||
@ -174,21 +173,18 @@ func actionInstall(args []string) {
|
||||
// serialise metadata to ensure consistency
|
||||
if f, err := os.OpenFile(pathSet.metaPath+"~", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644); err != nil {
|
||||
cleanup()
|
||||
fmsg.Fatalf("cannot create metadata file: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot create metadata file: %v", err)
|
||||
} else if err = json.NewEncoder(f).Encode(bundle); err != nil {
|
||||
cleanup()
|
||||
fmsg.Fatalf("cannot write metadata: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot write metadata: %v", err)
|
||||
} else if err = f.Close(); err != nil {
|
||||
fmsg.Printf("cannot close metadata file: %v", err)
|
||||
log.Printf("cannot close metadata file: %v", err)
|
||||
// not fatal
|
||||
}
|
||||
|
||||
if err := os.Rename(pathSet.metaPath+"~", pathSet.metaPath); err != nil {
|
||||
cleanup()
|
||||
fmsg.Fatalf("cannot rename metadata file: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot rename metadata file: %v", err)
|
||||
}
|
||||
|
||||
cleanup()
|
||||
|
@ -2,8 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/internal"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
)
|
||||
|
||||
@ -11,7 +13,7 @@ const shell = "/run/current-system/sw/bin/bash"
|
||||
|
||||
func init() {
|
||||
if err := os.Setenv("SHELL", shell); err != nil {
|
||||
fmsg.Fatalf("cannot set $SHELL: %v", err)
|
||||
log.Fatalf("cannot set $SHELL: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,14 +26,14 @@ func init() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmsg.SetPrefix("fpkg")
|
||||
fmsg.Prepare("fpkg")
|
||||
|
||||
flag.Parse()
|
||||
fmsg.SetVerbose(flagVerbose)
|
||||
fmsg.Store(flagVerbose)
|
||||
|
||||
args := flag.Args()
|
||||
if len(args) < 1 {
|
||||
fmsg.Fatal("invalid argument")
|
||||
log.Fatal("invalid argument")
|
||||
}
|
||||
|
||||
switch args[0] {
|
||||
@ -41,8 +43,8 @@ func main() {
|
||||
actionStart(args[1:])
|
||||
|
||||
default:
|
||||
fmsg.Fatal("invalid argument")
|
||||
log.Fatal("invalid argument")
|
||||
}
|
||||
|
||||
fmsg.Exit(0)
|
||||
internal.Exit(0)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@ -25,8 +26,8 @@ func init() {
|
||||
|
||||
func lookPath(file string) string {
|
||||
if p, err := exec.LookPath(file); err != nil {
|
||||
fmsg.Fatalf("%s: command not found", file)
|
||||
panic("unreachable")
|
||||
log.Fatalf("%s: command not found", file)
|
||||
return ""
|
||||
} else {
|
||||
return p
|
||||
}
|
||||
@ -35,15 +36,14 @@ func lookPath(file string) string {
|
||||
var beforeRunFail = new(atomic.Pointer[func()])
|
||||
|
||||
func mustRun(name string, arg ...string) {
|
||||
fmsg.VPrintf("spawning process: %q %q", name, arg)
|
||||
fmsg.Verbosef("spawning process: %q %q", name, arg)
|
||||
cmd := exec.Command(name, arg...)
|
||||
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
if f := beforeRunFail.Swap(nil); f != nil {
|
||||
(*f)()
|
||||
}
|
||||
fmsg.Fatalf("%s: %v", name, err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("%s: %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
@ -25,14 +26,12 @@ func fortifyApp(config *fst.Config, beforeFail func()) {
|
||||
)
|
||||
if p, ok := internal.Path(Fmain); !ok {
|
||||
beforeFail()
|
||||
fmsg.Fatal("invalid fortify path, this copy of fpkg is not compiled correctly")
|
||||
panic("unreachable")
|
||||
log.Fatal("invalid fortify path, this copy of fpkg is not compiled correctly")
|
||||
} else if r, w, err := os.Pipe(); err != nil {
|
||||
beforeFail()
|
||||
fmsg.Fatalf("cannot pipe: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot pipe: %v", err)
|
||||
} else {
|
||||
if fmsg.Verbose() {
|
||||
if fmsg.Load() {
|
||||
cmd = exec.Command(p, "-v", "app", "3")
|
||||
} else {
|
||||
cmd = exec.Command(p, "app", "3")
|
||||
@ -45,26 +44,22 @@ func fortifyApp(config *fst.Config, beforeFail func()) {
|
||||
go func() {
|
||||
if err := json.NewEncoder(st).Encode(config); err != nil {
|
||||
beforeFail()
|
||||
fmsg.Fatalf("cannot send configuration: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot send configuration: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
beforeFail()
|
||||
fmsg.Fatalf("cannot start fortify: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot start fortify: %v", err)
|
||||
}
|
||||
if err := cmd.Wait(); err != nil {
|
||||
var exitError *exec.ExitError
|
||||
if errors.As(err, &exitError) {
|
||||
beforeFail()
|
||||
fmsg.Exit(exitError.ExitCode())
|
||||
panic("unreachable")
|
||||
internal.Exit(exitError.ExitCode())
|
||||
} else {
|
||||
beforeFail()
|
||||
fmsg.Fatalf("cannot wait: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot wait: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,12 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"path"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/fst"
|
||||
"git.gensokyo.uk/security/fortify/helper/bwrap"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
"git.gensokyo.uk/security/fortify/internal"
|
||||
)
|
||||
|
||||
func actionStart(args []string) {
|
||||
@ -26,7 +27,7 @@ func actionStart(args []string) {
|
||||
args = set.Args()
|
||||
|
||||
if len(args) < 1 {
|
||||
fmsg.Fatal("invalid argument")
|
||||
log.Fatal("invalid argument")
|
||||
}
|
||||
|
||||
/*
|
||||
@ -37,7 +38,7 @@ func actionStart(args []string) {
|
||||
pathSet := pathSetByApp(id)
|
||||
app := loadBundleInfo(pathSet.metaPath, func() {})
|
||||
if app.ID != id {
|
||||
fmsg.Fatalf("app %q claims to have identifier %q", id, app.ID)
|
||||
log.Fatalf("app %q claims to have identifier %q", id, app.ID)
|
||||
}
|
||||
|
||||
/*
|
||||
@ -144,7 +145,7 @@ func actionStart(args []string) {
|
||||
*/
|
||||
|
||||
fortifyApp(config, func() {})
|
||||
fmsg.Exit(0)
|
||||
internal.Exit(0)
|
||||
}
|
||||
|
||||
func appendGPUFilesystem(config *fst.Config) {
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
|
||||
"git.gensokyo.uk/security/fortify/fst"
|
||||
"git.gensokyo.uk/security/fortify/helper/bwrap"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
"git.gensokyo.uk/security/fortify/internal"
|
||||
)
|
||||
|
||||
func withNixDaemon(
|
||||
@ -95,7 +95,7 @@ func fortifyAppDropShell(config *fst.Config, dropShell bool, beforeFail func())
|
||||
config.Command = []string{shell, "-l"}
|
||||
fortifyApp(config, beforeFail)
|
||||
beforeFail()
|
||||
fmsg.Exit(0)
|
||||
internal.Exit(0)
|
||||
}
|
||||
fortifyApp(config, beforeFail)
|
||||
}
|
||||
|
15
error.go
15
error.go
@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/internal/app"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
@ -10,13 +11,13 @@ import (
|
||||
func logWaitError(err error) {
|
||||
var e *fmsg.BaseError
|
||||
if !fmsg.AsBaseError(err, &e) {
|
||||
fmsg.Println("wait failed:", err)
|
||||
log.Println("wait failed:", err)
|
||||
} else {
|
||||
// Wait only returns either *app.ProcessError or *app.StateStoreError wrapped in a *app.BaseError
|
||||
var se *app.StateStoreError
|
||||
if !errors.As(err, &se) {
|
||||
// does not need special handling
|
||||
fmsg.Print(e.Message())
|
||||
log.Print(e.Message())
|
||||
} else {
|
||||
// inner error are either unwrapped store errors
|
||||
// or joined errors returned by *appSealTx revert
|
||||
@ -24,7 +25,7 @@ func logWaitError(err error) {
|
||||
var ej app.RevertCompoundError
|
||||
if !errors.As(se.InnerErr, &ej) {
|
||||
// does not require special handling
|
||||
fmsg.Print(e.Message())
|
||||
log.Print(e.Message())
|
||||
} else {
|
||||
errs := ej.Unwrap()
|
||||
|
||||
@ -33,10 +34,10 @@ func logWaitError(err error) {
|
||||
var eb *fmsg.BaseError
|
||||
if !errors.As(ei, &eb) {
|
||||
// unreachable
|
||||
fmsg.Println("invalid error type returned by revert:", ei)
|
||||
log.Println("invalid error type returned by revert:", ei)
|
||||
} else {
|
||||
// print inner *app.BaseError message
|
||||
fmsg.Print(eb.Message())
|
||||
log.Print(eb.Message())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -48,8 +49,8 @@ func logBaseError(err error, message string) {
|
||||
var e *fmsg.BaseError
|
||||
|
||||
if fmsg.AsBaseError(err, &e) {
|
||||
fmsg.Print(e.Message())
|
||||
log.Print(e.Message())
|
||||
} else {
|
||||
fmsg.Println(message, err)
|
||||
log.Println(message, err)
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ func (s *SandboxConfig) Bwrap(os linux.System) (*bwrap.Config, error) {
|
||||
}
|
||||
|
||||
if s.Syscall == nil {
|
||||
fmsg.VPrintln("syscall filter not configured, PROCEED WITH CAUTION")
|
||||
fmsg.Verbose("syscall filter not configured, PROCEED WITH CAUTION")
|
||||
}
|
||||
|
||||
var uid int
|
||||
@ -121,11 +121,11 @@ func (s *SandboxConfig) Bwrap(os linux.System) (*bwrap.Config, error) {
|
||||
// get parent dir of socket
|
||||
dir := path.Dir(pair[1])
|
||||
if dir == "." || dir == "/" {
|
||||
fmsg.VPrintf("dbus socket %q is in an unusual location", pair[1])
|
||||
fmsg.Verbosef("dbus socket %q is in an unusual location", pair[1])
|
||||
}
|
||||
hidePaths = append(hidePaths, dir)
|
||||
} else {
|
||||
fmsg.VPrintf("dbus socket %q is not absolute", pair[1])
|
||||
fmsg.Verbosef("dbus socket %q is not absolute", pair[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -169,7 +169,7 @@ func (s *SandboxConfig) Bwrap(os linux.System) (*bwrap.Config, error) {
|
||||
return nil, err
|
||||
} else if ok {
|
||||
hidePathMatch[i] = true
|
||||
fmsg.VPrintf("hiding paths from %q", c.Src)
|
||||
fmsg.Verbosef("hiding paths from %q", c.Src)
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,7 +221,7 @@ func evalSymlinks(os linux.System, v *string) error {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
fmsg.VPrintf("path %q does not yet exist", *v)
|
||||
fmsg.Verbosef("path %q does not yet exist", *v)
|
||||
} else {
|
||||
*v = p
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package bwrap_test
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"slices"
|
||||
"testing"
|
||||
@ -8,11 +9,10 @@ import (
|
||||
"git.gensokyo.uk/security/fortify/helper/bwrap"
|
||||
"git.gensokyo.uk/security/fortify/helper/proc"
|
||||
"git.gensokyo.uk/security/fortify/helper/seccomp"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
)
|
||||
|
||||
func TestConfig_Args(t *testing.T) {
|
||||
seccomp.CPrintln = fmsg.Println
|
||||
seccomp.CPrintln = log.Println
|
||||
t.Cleanup(func() { seccomp.CPrintln = nil })
|
||||
|
||||
testCases := []struct {
|
||||
|
@ -4,12 +4,12 @@ import (
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"slices"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/helper/seccomp"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
)
|
||||
|
||||
func TestExport(t *testing.T) {
|
||||
@ -79,7 +79,7 @@ func TestExport(t *testing.T) {
|
||||
buf := make([]byte, 8)
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
seccomp.CPrintln = fmsg.Println
|
||||
seccomp.CPrintln = log.Println
|
||||
t.Cleanup(func() { seccomp.CPrintln = nil })
|
||||
|
||||
e := seccomp.New(tc.opts)
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
|
||||
"git.gensokyo.uk/security/fortify/helper/bwrap"
|
||||
"git.gensokyo.uk/security/fortify/helper/proc"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
"git.gensokyo.uk/security/fortify/internal"
|
||||
)
|
||||
|
||||
// InternalChildStub is an internal function but exported because it is cross-package;
|
||||
@ -40,7 +40,7 @@ func InternalChildStub() {
|
||||
genericStub(flagRestoreFiles(4, ap, sp))
|
||||
}
|
||||
|
||||
fmsg.Exit(0)
|
||||
internal.Exit(0)
|
||||
}
|
||||
|
||||
// InternalReplaceExecCommand is an internal function but exported because it is cross-package;
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
"git.gensokyo.uk/security/fortify/internal"
|
||||
)
|
||||
|
||||
// used by the parent process
|
||||
@ -13,6 +13,6 @@ import (
|
||||
func TryArgv0() {
|
||||
if len(os.Args) > 0 && path.Base(os.Args[0]) == "init" {
|
||||
Main()
|
||||
fmsg.Exit(0)
|
||||
internal.Exit(0)
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package init0
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
@ -24,17 +25,15 @@ const (
|
||||
func Main() {
|
||||
// sharing stdout with shim
|
||||
// USE WITH CAUTION
|
||||
fmsg.SetPrefix("init")
|
||||
fmsg.Prepare("init")
|
||||
|
||||
// setting this prevents ptrace
|
||||
if err := internal.PR_SET_DUMPABLE__SUID_DUMP_DISABLE(); err != nil {
|
||||
fmsg.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
|
||||
}
|
||||
|
||||
if os.Getpid() != 1 {
|
||||
fmsg.Fatal("this process must run as pid 1")
|
||||
panic("unreachable")
|
||||
log.Fatal("this process must run as pid 1")
|
||||
}
|
||||
|
||||
// receive setup payload
|
||||
@ -44,30 +43,29 @@ func Main() {
|
||||
)
|
||||
if f, err := proc.Receive(Env, &payload); err != nil {
|
||||
if errors.Is(err, proc.ErrInvalid) {
|
||||
fmsg.Fatal("invalid config descriptor")
|
||||
log.Fatal("invalid config descriptor")
|
||||
}
|
||||
if errors.Is(err, proc.ErrNotSet) {
|
||||
fmsg.Fatal("FORTIFY_INIT not set")
|
||||
log.Fatal("FORTIFY_INIT not set")
|
||||
}
|
||||
|
||||
fmsg.Fatalf("cannot decode init setup payload: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot decode init setup payload: %v", err)
|
||||
} else {
|
||||
fmsg.SetVerbose(payload.Verbose)
|
||||
fmsg.Store(payload.Verbose)
|
||||
closeSetup = f
|
||||
|
||||
// child does not need to see this
|
||||
if err = os.Unsetenv(Env); err != nil {
|
||||
fmsg.Printf("cannot unset %s: %v", Env, err)
|
||||
log.Printf("cannot unset %s: %v", Env, err)
|
||||
// not fatal
|
||||
} else {
|
||||
fmsg.VPrintln("received configuration")
|
||||
fmsg.Verbose("received configuration")
|
||||
}
|
||||
}
|
||||
|
||||
// die with parent
|
||||
if err := internal.PR_SET_PDEATHSIG__SIGKILL(); err != nil {
|
||||
fmsg.Fatalf("prctl(PR_SET_PDEATHSIG, SIGKILL): %v", err)
|
||||
log.Fatalf("prctl(PR_SET_PDEATHSIG, SIGKILL): %v", err)
|
||||
}
|
||||
|
||||
cmd := exec.Command(payload.Argv0)
|
||||
@ -76,13 +74,13 @@ func Main() {
|
||||
cmd.Env = os.Environ()
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
fmsg.Fatalf("cannot start %q: %v", payload.Argv0, err)
|
||||
log.Fatalf("cannot start %q: %v", payload.Argv0, err)
|
||||
}
|
||||
fmsg.Suspend()
|
||||
|
||||
// close setup pipe as setup is now complete
|
||||
if err := closeSetup(); err != nil {
|
||||
fmsg.Println("cannot close setup pipe:", err)
|
||||
log.Println("cannot close setup pipe:", err)
|
||||
// not fatal
|
||||
}
|
||||
|
||||
@ -119,7 +117,7 @@ func Main() {
|
||||
}
|
||||
}
|
||||
if !errors.Is(err, syscall.ECHILD) {
|
||||
fmsg.Println("unexpected wait4 response:", err)
|
||||
log.Println("unexpected wait4 response:", err)
|
||||
}
|
||||
|
||||
close(done)
|
||||
@ -132,9 +130,12 @@ func Main() {
|
||||
for {
|
||||
select {
|
||||
case s := <-sig:
|
||||
fmsg.VPrintln("received", s.String())
|
||||
fmsg.Resume() // output could still be withheld at this point, so resume is called
|
||||
fmsg.Exit(0)
|
||||
if fmsg.Resume() {
|
||||
fmsg.Verbosef("terminating on %s after process start", s.String())
|
||||
} else {
|
||||
fmsg.Verbosef("terminating on %s", s.String())
|
||||
}
|
||||
internal.Exit(0)
|
||||
case w := <-info:
|
||||
if w.wpid == cmd.Process.Pid {
|
||||
// initial process exited, output is most likely available again
|
||||
@ -155,10 +156,10 @@ func Main() {
|
||||
}()
|
||||
}
|
||||
case <-done:
|
||||
fmsg.Exit(r)
|
||||
internal.Exit(r)
|
||||
case <-timeout:
|
||||
fmsg.Println("timeout exceeded waiting for lingering processes")
|
||||
fmsg.Exit(r)
|
||||
log.Println("timeout exceeded waiting for lingering processes")
|
||||
internal.Exit(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -191,7 +191,7 @@ func (a *app) Seal(config *fst.Config) error {
|
||||
|
||||
// map sandbox config to bwrap
|
||||
if config.Confinement.Sandbox == nil {
|
||||
fmsg.VPrintln("sandbox configuration not supplied, PROCEED WITH CAUTION")
|
||||
fmsg.Verbose("sandbox configuration not supplied, PROCEED WITH CAUTION")
|
||||
|
||||
// permissive defaults
|
||||
conf := &fst.SandboxConfig{
|
||||
@ -264,7 +264,7 @@ func (a *app) Seal(config *fst.Config) error {
|
||||
}
|
||||
|
||||
// verbose log seal information
|
||||
fmsg.VPrintf("created application seal for uid %s (%s) groups: %v, command: %s",
|
||||
fmsg.Verbosef("created application seal for uid %s (%s) groups: %v, command: %s",
|
||||
seal.sys.user.us, seal.sys.user.username, config.Confinement.Groups, config.Command)
|
||||
|
||||
// seal app and release lock
|
||||
|
@ -143,7 +143,7 @@ func (seal *appSeal) setupShares(bus [2]*dbus.Config, os linux.System) error {
|
||||
if seal.et.Has(system.EWayland) {
|
||||
var socketPath string
|
||||
if name, ok := os.LookupEnv(wl.WaylandDisplay); !ok {
|
||||
fmsg.VPrintln(wl.WaylandDisplay + " is not set, assuming " + wl.FallbackName)
|
||||
fmsg.Verbose(wl.WaylandDisplay + " is not set, assuming " + wl.FallbackName)
|
||||
socketPath = path.Join(seal.RuntimePath, wl.FallbackName)
|
||||
} else if !path.IsAbs(name) {
|
||||
socketPath = path.Join(seal.RuntimePath, name)
|
||||
@ -166,7 +166,7 @@ func (seal *appSeal) setupShares(bus [2]*dbus.Config, os linux.System) error {
|
||||
seal.sys.Wayland(outerPath, socketPath, appID, seal.id)
|
||||
seal.sys.bwrap.Bind(outerPath, innerPath)
|
||||
} else { // bind mount wayland socket (insecure)
|
||||
fmsg.VPrintln("direct wayland access, PROCEED WITH CAUTION")
|
||||
fmsg.Verbose("direct wayland access, PROCEED WITH CAUTION")
|
||||
seal.sys.bwrap.Bind(socketPath, innerPath)
|
||||
|
||||
// ensure Wayland socket ACL (e.g. `/run/user/%d/wayland-%d`)
|
||||
@ -229,7 +229,7 @@ func (seal *appSeal) setupShares(bus [2]*dbus.Config, os linux.System) error {
|
||||
// publish current user's pulse cookie for target user
|
||||
if src, err := discoverPulseCookie(os); err != nil {
|
||||
// not fatal
|
||||
fmsg.VPrintln(strings.TrimSpace(err.(*fmsg.BaseError).Message()))
|
||||
fmsg.Verbose(strings.TrimSpace(err.(*fmsg.BaseError).Message()))
|
||||
} else {
|
||||
dst := path.Join(seal.share, "pulse-cookie")
|
||||
innerDst := fst.Tmp + "/pulse-cookie"
|
||||
|
@ -3,6 +3,7 @@ package shim
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
@ -25,12 +26,11 @@ import (
|
||||
func Main() {
|
||||
// sharing stdout with fortify
|
||||
// USE WITH CAUTION
|
||||
fmsg.SetPrefix("shim")
|
||||
fmsg.Prepare("shim")
|
||||
|
||||
// setting this prevents ptrace
|
||||
if err := internal.PR_SET_DUMPABLE__SUID_DUMP_DISABLE(); err != nil {
|
||||
fmsg.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
|
||||
}
|
||||
|
||||
// receive setup payload
|
||||
@ -40,21 +40,20 @@ func Main() {
|
||||
)
|
||||
if f, err := proc.Receive(Env, &payload); err != nil {
|
||||
if errors.Is(err, proc.ErrInvalid) {
|
||||
fmsg.Fatal("invalid config descriptor")
|
||||
log.Fatal("invalid config descriptor")
|
||||
}
|
||||
if errors.Is(err, proc.ErrNotSet) {
|
||||
fmsg.Fatal("FORTIFY_SHIM not set")
|
||||
log.Fatal("FORTIFY_SHIM not set")
|
||||
}
|
||||
|
||||
fmsg.Fatalf("cannot decode shim setup payload: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot decode shim setup payload: %v", err)
|
||||
} else {
|
||||
fmsg.SetVerbose(payload.Verbose)
|
||||
fmsg.Store(payload.Verbose)
|
||||
closeSetup = f
|
||||
}
|
||||
|
||||
if payload.Bwrap == nil {
|
||||
fmsg.Fatal("bwrap config not supplied")
|
||||
log.Fatal("bwrap config not supplied")
|
||||
}
|
||||
|
||||
// restore bwrap sync fd
|
||||
@ -65,7 +64,7 @@ func Main() {
|
||||
|
||||
// close setup socket
|
||||
if err := closeSetup(); err != nil {
|
||||
fmsg.Println("cannot close setup pipe:", err)
|
||||
log.Println("cannot close setup pipe:", err)
|
||||
// not fatal
|
||||
}
|
||||
|
||||
@ -73,15 +72,15 @@ func Main() {
|
||||
if s, err := os.Stat(payload.Home); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err = os.Mkdir(payload.Home, 0700); err != nil {
|
||||
fmsg.Fatalf("cannot create home directory: %v", err)
|
||||
log.Fatalf("cannot create home directory: %v", err)
|
||||
}
|
||||
} else {
|
||||
fmsg.Fatalf("cannot access home directory: %v", err)
|
||||
log.Fatalf("cannot access home directory: %v", err)
|
||||
}
|
||||
|
||||
// home directory is created, proceed
|
||||
} else if !s.IsDir() {
|
||||
fmsg.Fatalf("data path %q is not a directory", payload.Home)
|
||||
log.Fatalf("data path %q is not a directory", payload.Home)
|
||||
}
|
||||
|
||||
var ic init0.Payload
|
||||
@ -95,10 +94,10 @@ func Main() {
|
||||
// no argv, look up shell instead
|
||||
var ok bool
|
||||
if payload.Bwrap.SetEnv == nil {
|
||||
fmsg.Fatal("no command was specified and environment is unset")
|
||||
log.Fatal("no command was specified and environment is unset")
|
||||
}
|
||||
if ic.Argv0, ok = payload.Bwrap.SetEnv["SHELL"]; !ok {
|
||||
fmsg.Fatal("no command was specified and $SHELL was unset")
|
||||
log.Fatal("no command was specified and $SHELL was unset")
|
||||
}
|
||||
|
||||
ic.Argv = []string{ic.Argv0}
|
||||
@ -110,20 +109,20 @@ func Main() {
|
||||
|
||||
// serve setup payload
|
||||
if fd, encoder, err := proc.Setup(&extraFiles); err != nil {
|
||||
fmsg.Fatalf("cannot pipe: %v", err)
|
||||
log.Fatalf("cannot pipe: %v", err)
|
||||
} else {
|
||||
conf.SetEnv[init0.Env] = strconv.Itoa(fd)
|
||||
go func() {
|
||||
fmsg.VPrintln("transmitting config to init")
|
||||
fmsg.Verbose("transmitting config to init")
|
||||
if err = encoder.Encode(&ic); err != nil {
|
||||
fmsg.Fatalf("cannot transmit init config: %v", err)
|
||||
log.Fatalf("cannot transmit init config: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
helper.BubblewrapName = payload.Exec[0] // resolved bwrap path by parent
|
||||
if fmsg.Verbose() {
|
||||
seccomp.CPrintln = fmsg.Println
|
||||
if fmsg.Load() {
|
||||
seccomp.CPrintln = log.Println
|
||||
}
|
||||
if b, err := helper.NewBwrap(
|
||||
conf, path.Join(fst.Tmp, "sbin/init"),
|
||||
@ -131,7 +130,7 @@ func Main() {
|
||||
extraFiles,
|
||||
syncFd,
|
||||
); err != nil {
|
||||
fmsg.Fatalf("malformed sandbox config: %v", err)
|
||||
log.Fatalf("malformed sandbox config: %v", err)
|
||||
} else {
|
||||
b.Stdin(os.Stdin).Stdout(os.Stdout).Stderr(os.Stderr)
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
@ -139,15 +138,15 @@ func Main() {
|
||||
|
||||
// run and pass through exit code
|
||||
if err = b.Start(ctx, false); err != nil {
|
||||
fmsg.Fatalf("cannot start target process: %v", err)
|
||||
log.Fatalf("cannot start target process: %v", err)
|
||||
} else if err = b.Wait(); err != nil {
|
||||
var exitError *exec.ExitError
|
||||
if !errors.As(err, &exitError) {
|
||||
fmsg.Println("wait:", err)
|
||||
fmsg.Exit(127)
|
||||
log.Printf("wait: %v", err)
|
||||
internal.Exit(127)
|
||||
panic("unreachable")
|
||||
}
|
||||
fmsg.Exit(exitError.ExitCode())
|
||||
internal.Exit(exitError.ExitCode())
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
@ -54,8 +55,7 @@ func (s *Shim) Start(
|
||||
// prepare user switcher invocation
|
||||
var fsu string
|
||||
if p, ok := internal.Path(internal.Fsu); !ok {
|
||||
fmsg.Fatal("invalid fsu path, this copy of fortify is not compiled correctly")
|
||||
panic("unreachable")
|
||||
log.Fatal("invalid fsu path, this copy of fortify is not compiled correctly")
|
||||
} else {
|
||||
fsu = p
|
||||
}
|
||||
@ -75,7 +75,7 @@ func (s *Shim) Start(
|
||||
|
||||
// format fsu supplementary groups
|
||||
if len(supp) > 0 {
|
||||
fmsg.VPrintf("attaching supplementary group ids %s", supp)
|
||||
fmsg.Verbosef("attaching supplementary group ids %s", supp)
|
||||
s.cmd.Env = append(s.cmd.Env, "FORTIFY_GROUPS="+strings.Join(supp, " "))
|
||||
}
|
||||
s.cmd.Stdin, s.cmd.Stdout, s.cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||
@ -87,7 +87,7 @@ func (s *Shim) Start(
|
||||
s.sync = &fd
|
||||
}
|
||||
|
||||
fmsg.VPrintln("starting shim via fsu:", s.cmd)
|
||||
fmsg.Verbose("starting shim via fsu:", s.cmd)
|
||||
// withhold messages to stderr
|
||||
fmsg.Suspend()
|
||||
if err := s.cmd.Start(); err != nil {
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -81,7 +82,7 @@ func (a *app) Run(ctx context.Context, rs *RunState) error {
|
||||
Bwrap: a.seal.sys.bwrap,
|
||||
Home: a.seal.sys.user.data,
|
||||
|
||||
Verbose: fmsg.Verbose(),
|
||||
Verbose: fmsg.Load(),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -119,8 +120,8 @@ func (a *app) Run(ctx context.Context, rs *RunState) error {
|
||||
} else {
|
||||
rs.ExitCode = a.shim.Unwrap().ProcessState.ExitCode()
|
||||
}
|
||||
if fmsg.Verbose() {
|
||||
fmsg.VPrintf("process %d exited with exit code %d", a.shim.Unwrap().Process.Pid, rs.ExitCode)
|
||||
if fmsg.Load() {
|
||||
fmsg.Verbosef("process %d exited with exit code %d", a.shim.Unwrap().Process.Pid, rs.ExitCode)
|
||||
}
|
||||
|
||||
// this is reached when a fault makes an already running shim impossible to continue execution
|
||||
@ -128,11 +129,11 @@ func (a *app) Run(ctx context.Context, rs *RunState) error {
|
||||
// the effects of this is similar to the alternative exit path and ensures shim death
|
||||
case err := <-a.shim.WaitFallback():
|
||||
rs.ExitCode = 255
|
||||
fmsg.Printf("cannot terminate shim on faulted setup: %v", err)
|
||||
log.Printf("cannot terminate shim on faulted setup: %v", err)
|
||||
|
||||
// alternative exit path relying on shim behaviour on monitor process exit
|
||||
case <-ctx.Done():
|
||||
fmsg.VPrintln("alternative exit path selected")
|
||||
fmsg.Verbose("alternative exit path selected")
|
||||
}
|
||||
|
||||
// child process exited, resume output
|
||||
@ -163,10 +164,10 @@ func (a *app) Run(ctx context.Context, rs *RunState) error {
|
||||
} else {
|
||||
if l := len(states); l == 0 {
|
||||
// cleanup globals as the final launcher
|
||||
fmsg.VPrintln("no other launchers active, will clean up globals")
|
||||
fmsg.Verbose("no other launchers active, will clean up globals")
|
||||
ec.Set(system.User)
|
||||
} else {
|
||||
fmsg.VPrintf("found %d active launchers, cleaning up without globals", l)
|
||||
fmsg.Verbosef("found %d active launchers, cleaning up without globals", l)
|
||||
}
|
||||
|
||||
// accumulate capabilities of other launchers
|
||||
@ -174,7 +175,7 @@ func (a *app) Run(ctx context.Context, rs *RunState) error {
|
||||
if s.Config != nil {
|
||||
*rt |= s.Config.Confinement.Enablements
|
||||
} else {
|
||||
fmsg.Printf("state entry %d does not contain config", i)
|
||||
log.Printf("state entry %d does not contain config", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -184,7 +185,7 @@ func (a *app) Run(ctx context.Context, rs *RunState) error {
|
||||
ec.Set(i)
|
||||
}
|
||||
}
|
||||
if fmsg.Verbose() {
|
||||
if fmsg.Load() {
|
||||
labels := make([]string, 0, system.ELen+1)
|
||||
for i := system.Enablement(0); i < system.Enablement(system.ELen+2); i++ {
|
||||
if ec.Has(i) {
|
||||
@ -192,7 +193,7 @@ func (a *app) Run(ctx context.Context, rs *RunState) error {
|
||||
}
|
||||
}
|
||||
if len(labels) > 0 {
|
||||
fmsg.VPrintln("reverting operations labelled", strings.Join(labels, ", "))
|
||||
fmsg.Verbose("reverting operations labelled", strings.Join(labels, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
package proc
|
||||
package internal
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -14,7 +13,7 @@ var (
|
||||
|
||||
func copyExecutable() {
|
||||
if name, err := os.Executable(); err != nil {
|
||||
fmsg.Fatalf("cannot read executable path: %v", err)
|
||||
log.Fatalf("cannot read executable path: %v", err)
|
||||
} else {
|
||||
executable = name
|
||||
}
|
9
internal/exit.go
Normal file
9
internal/exit.go
Normal file
@ -0,0 +1,9 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
)
|
||||
|
||||
func Exit(code int) { fmsg.BeforeExit(); os.Exit(code) }
|
@ -1,98 +0,0 @@
|
||||
package fmsg
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
wstate atomic.Bool
|
||||
dropped atomic.Uint64
|
||||
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
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// queue submits ops to msgbuf but drops messages
|
||||
// when the buffer is full and dequeue is withholding
|
||||
func queue(op dOp) {
|
||||
queueSync.Add(1)
|
||||
|
||||
select {
|
||||
case msgbuf <- op:
|
||||
default:
|
||||
// send the op anyway if not withholding
|
||||
// as dequeue will get to it eventually
|
||||
if !wstate.Load() {
|
||||
msgbuf <- op
|
||||
} else {
|
||||
queueSync.Done()
|
||||
// increment dropped message count
|
||||
dropped.Add(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type dOp interface{ Do() }
|
||||
|
||||
func Exit(code int) {
|
||||
Resume() // resume here to avoid deadlock
|
||||
queueSync.Wait()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func Suspend() {
|
||||
dequeueOnce.Do(dequeue)
|
||||
if wstate.CompareAndSwap(false, true) {
|
||||
queueSync.Wait()
|
||||
withhold <- struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func Resume() {
|
||||
dequeueOnce.Do(dequeue)
|
||||
if wstate.CompareAndSwap(true, false) {
|
||||
withhold <- struct{}{}
|
||||
if d := dropped.Swap(0); d != 0 {
|
||||
Printf("dropped %d messages during withhold", d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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...)
|
||||
}
|
@ -2,39 +2,85 @@
|
||||
package fmsg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var std = log.New(os.Stderr, "fortify: ", 0)
|
||||
const (
|
||||
bufSize = 4 * 1024
|
||||
bufSizeMax = 16 * 1024 * 1024
|
||||
)
|
||||
|
||||
func SetPrefix(prefix string) {
|
||||
prefix += ": "
|
||||
std.SetPrefix(prefix)
|
||||
std.SetPrefix(prefix)
|
||||
var o = &suspendable{w: os.Stderr}
|
||||
|
||||
// Prepare configures the system logger for [Suspend] and [Resume] to take effect.
|
||||
func Prepare(prefix string) { log.SetPrefix(prefix + ": "); log.SetFlags(0); log.SetOutput(o) }
|
||||
|
||||
type suspendable struct {
|
||||
w io.Writer
|
||||
s atomic.Bool
|
||||
|
||||
buf bytes.Buffer
|
||||
bufOnce sync.Once
|
||||
bufMu sync.Mutex
|
||||
dropped int
|
||||
}
|
||||
|
||||
func Print(v ...any) {
|
||||
dequeueOnce.Do(dequeue)
|
||||
queue(dPrint(v))
|
||||
func (s *suspendable) Write(p []byte) (n int, err error) {
|
||||
if !s.s.Load() {
|
||||
return s.w.Write(p)
|
||||
}
|
||||
s.bufOnce.Do(func() { s.prepareBuf() })
|
||||
|
||||
s.bufMu.Lock()
|
||||
defer s.bufMu.Unlock()
|
||||
|
||||
if l := len(p); s.buf.Len()+l > bufSizeMax {
|
||||
s.dropped += l
|
||||
return 0, syscall.ENOMEM
|
||||
}
|
||||
return s.buf.Write(p)
|
||||
}
|
||||
|
||||
func Printf(format string, v ...any) {
|
||||
dequeueOnce.Do(dequeue)
|
||||
queue(&dPrintf{format, v})
|
||||
func (s *suspendable) prepareBuf() { s.buf.Grow(bufSize) }
|
||||
func (s *suspendable) Suspend() bool { return o.s.CompareAndSwap(false, true) }
|
||||
func (s *suspendable) Resume() (resumed bool, dropped uintptr, n int64, err error) {
|
||||
if o.s.CompareAndSwap(true, false) {
|
||||
o.bufMu.Lock()
|
||||
defer o.bufMu.Unlock()
|
||||
|
||||
resumed = true
|
||||
dropped = uintptr(o.dropped)
|
||||
|
||||
o.dropped = 0
|
||||
n, err = io.Copy(s.w, &s.buf)
|
||||
s.buf = bytes.Buffer{}
|
||||
s.prepareBuf()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Println(v ...any) {
|
||||
dequeueOnce.Do(dequeue)
|
||||
queue(dPrintln(v))
|
||||
func Suspend() bool { return o.Suspend() }
|
||||
func Resume() bool {
|
||||
resumed, dropped, _, err := o.Resume()
|
||||
if err != nil {
|
||||
// probably going to result in an error as well,
|
||||
// so this call is as good as unreachable
|
||||
log.Printf("cannot dump buffer on resume: %v", err)
|
||||
}
|
||||
if resumed && dropped > 0 {
|
||||
log.Fatalf("dropped %d bytes while output is suspended", dropped)
|
||||
}
|
||||
return resumed
|
||||
}
|
||||
|
||||
func Fatal(v ...any) {
|
||||
Print(v...)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func Fatalf(format string, v ...any) {
|
||||
Printf(format, v...)
|
||||
Exit(1)
|
||||
func BeforeExit() {
|
||||
if Resume() {
|
||||
log.Printf("beforeExit reached on suspended output")
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,23 @@
|
||||
package fmsg
|
||||
|
||||
import "sync/atomic"
|
||||
import (
|
||||
"log"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var verbose = new(atomic.Bool)
|
||||
|
||||
func Verbose() bool {
|
||||
return verbose.Load()
|
||||
}
|
||||
func Load() bool { return verbose.Load() }
|
||||
func Store(v bool) { verbose.Store(v) }
|
||||
|
||||
func SetVerbose(v bool) {
|
||||
verbose.Store(v)
|
||||
}
|
||||
|
||||
func VPrintf(format string, v ...any) {
|
||||
func Verbosef(format string, v ...any) {
|
||||
if verbose.Load() {
|
||||
Printf(format, v...)
|
||||
log.Printf(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func VPrintln(v ...any) {
|
||||
func Verbose(v ...any) {
|
||||
if verbose.Load() {
|
||||
Println(v...)
|
||||
log.Println(v...)
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ type Paths struct {
|
||||
func CopyPaths(os System, v *Paths) {
|
||||
v.SharePath = path.Join(os.TempDir(), "fortify."+strconv.Itoa(os.Geteuid()))
|
||||
|
||||
fmsg.VPrintf("process share directory at %q", v.SharePath)
|
||||
fmsg.Verbosef("process share directory at %q", v.SharePath)
|
||||
|
||||
if r, ok := os.LookupEnv(xdgRuntimeDir); !ok || r == "" || !path.IsAbs(r) {
|
||||
// fall back to path in share since fortify has no hard XDG dependency
|
||||
@ -65,5 +65,5 @@ func CopyPaths(os System, v *Paths) {
|
||||
v.RunDirPath = path.Join(v.RuntimePath, "fortify")
|
||||
}
|
||||
|
||||
fmsg.VPrintf("runtime directory at %q", v.RunDirPath)
|
||||
fmsg.Verbosef("runtime directory at %q", v.RunDirPath)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package linux
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
@ -11,9 +12,7 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/helper/proc"
|
||||
"git.gensokyo.uk/security/fortify/internal"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
)
|
||||
|
||||
// Std implements System using the standard library.
|
||||
@ -33,13 +32,13 @@ func (s *Std) Geteuid() int { return os.Geteuid(
|
||||
func (s *Std) LookupEnv(key string) (string, bool) { return os.LookupEnv(key) }
|
||||
func (s *Std) TempDir() string { return os.TempDir() }
|
||||
func (s *Std) LookPath(file string) (string, error) { return exec.LookPath(file) }
|
||||
func (s *Std) MustExecutable() string { return proc.MustExecutable() }
|
||||
func (s *Std) MustExecutable() string { return internal.MustExecutable() }
|
||||
func (s *Std) LookupGroup(name string) (*user.Group, error) { return user.LookupGroup(name) }
|
||||
func (s *Std) ReadDir(name string) ([]os.DirEntry, error) { return os.ReadDir(name) }
|
||||
func (s *Std) Stat(name string) (fs.FileInfo, error) { return os.Stat(name) }
|
||||
func (s *Std) Open(name string) (fs.File, error) { return os.Open(name) }
|
||||
func (s *Std) EvalSymlinks(path string) (string, error) { return filepath.EvalSymlinks(path) }
|
||||
func (s *Std) Exit(code int) { fmsg.Exit(code) }
|
||||
func (s *Std) Exit(code int) { internal.Exit(code) }
|
||||
|
||||
const xdgRuntimeDir = "XDG_RUNTIME_DIR"
|
||||
|
||||
@ -74,8 +73,9 @@ func (s *Std) Uid(aid int) (int, error) {
|
||||
|
||||
u.uid = -1
|
||||
if fsu, ok := internal.Check(internal.Fsu); !ok {
|
||||
fmsg.Fatal("invalid fsu path, this copy of fortify is not compiled correctly")
|
||||
panic("unreachable")
|
||||
log.Fatal("invalid fsu path, this copy of fortify is not compiled correctly")
|
||||
// unreachable
|
||||
return 0, syscall.EBADE
|
||||
} else {
|
||||
cmd := exec.Command(fsu)
|
||||
cmd.Path = fsu
|
||||
|
@ -85,17 +85,17 @@ func (s *multiStore) List() ([]int, error) {
|
||||
for _, e := range entries {
|
||||
// skip non-directories
|
||||
if !e.IsDir() {
|
||||
fmsg.VPrintf("skipped non-directory entry %q", e.Name())
|
||||
fmsg.Verbosef("skipped non-directory entry %q", e.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
// skip non-numerical names
|
||||
if v, err := strconv.Atoi(e.Name()); err != nil {
|
||||
fmsg.VPrintf("skipped non-aid entry %q", e.Name())
|
||||
fmsg.Verbosef("skipped non-aid entry %q", e.Name())
|
||||
continue
|
||||
} else {
|
||||
if v < 0 || v > 9999 {
|
||||
fmsg.VPrintf("skipped out of bounds entry %q", e.Name())
|
||||
fmsg.Verbosef("skipped out of bounds entry %q", e.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -36,18 +36,18 @@ func (a *ACL) Type() Enablement {
|
||||
}
|
||||
|
||||
func (a *ACL) apply(sys *I) error {
|
||||
fmsg.VPrintln("applying ACL", a)
|
||||
fmsg.Verbose("applying ACL", a)
|
||||
return fmsg.WrapErrorSuffix(acl.UpdatePerm(a.path, sys.uid, a.perms...),
|
||||
fmt.Sprintf("cannot apply ACL entry to %q:", a.path))
|
||||
}
|
||||
|
||||
func (a *ACL) revert(sys *I, ec *Criteria) error {
|
||||
if ec.hasType(a) {
|
||||
fmsg.VPrintln("stripping ACL", a)
|
||||
fmsg.Verbose("stripping ACL", a)
|
||||
return fmsg.WrapErrorSuffix(acl.UpdatePerm(a.path, sys.uid),
|
||||
fmt.Sprintf("cannot strip ACL entry from %q:", a.path))
|
||||
} else {
|
||||
fmsg.VPrintln("skipping ACL", a)
|
||||
fmsg.Verbose("skipping ACL", a)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package system
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@ -47,12 +48,12 @@ func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath st
|
||||
d.proxy = dbus.New(sessionBus, systemBus)
|
||||
|
||||
defer func() {
|
||||
if fmsg.Verbose() && d.proxy.Sealed() {
|
||||
fmsg.VPrintln("sealed session proxy", session.Args(sessionBus))
|
||||
if fmsg.Load() && d.proxy.Sealed() {
|
||||
fmsg.Verbose("sealed session proxy", session.Args(sessionBus))
|
||||
if system != nil {
|
||||
fmsg.VPrintln("sealed system proxy", system.Args(systemBus))
|
||||
fmsg.Verbose("sealed system proxy", system.Args(systemBus))
|
||||
}
|
||||
fmsg.VPrintln("message bus proxy final args:", d.proxy)
|
||||
fmsg.Verbose("message bus proxy final args:", d.proxy)
|
||||
}
|
||||
}()
|
||||
|
||||
@ -78,9 +79,9 @@ func (d *DBus) Type() Enablement {
|
||||
}
|
||||
|
||||
func (d *DBus) apply(sys *I) error {
|
||||
fmsg.VPrintf("session bus proxy on %q for upstream %q", d.proxy.Session()[1], d.proxy.Session()[0])
|
||||
fmsg.Verbosef("session bus proxy on %q for upstream %q", d.proxy.Session()[1], d.proxy.Session()[0])
|
||||
if d.system {
|
||||
fmsg.VPrintf("system bus proxy on %q for upstream %q", d.proxy.System()[1], d.proxy.System()[0])
|
||||
fmsg.Verbosef("system bus proxy on %q for upstream %q", d.proxy.System()[1], d.proxy.System()[0])
|
||||
}
|
||||
|
||||
// this starts the process and blocks until ready
|
||||
@ -89,15 +90,15 @@ func (d *DBus) apply(sys *I) error {
|
||||
return fmsg.WrapErrorSuffix(err,
|
||||
"cannot start message bus proxy:")
|
||||
}
|
||||
fmsg.VPrintln("starting message bus proxy:", d.proxy)
|
||||
fmsg.Verbose("starting message bus proxy:", d.proxy)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DBus) revert(_ *I, _ *Criteria) error {
|
||||
// criteria ignored here since dbus is always process-scoped
|
||||
fmsg.VPrintln("terminating message bus proxy")
|
||||
fmsg.Verbose("terminating message bus proxy")
|
||||
d.proxy.Close()
|
||||
defer fmsg.VPrintln("message bus proxy exit")
|
||||
defer fmsg.Verbose("message bus proxy exit")
|
||||
return fmsg.WrapErrorSuffix(d.proxy.Wait(), "message bus proxy error:")
|
||||
}
|
||||
|
||||
@ -144,7 +145,7 @@ func (s *scanToFmsg) write(p []byte, a int) (int, error) {
|
||||
func (s *scanToFmsg) Dump() {
|
||||
s.mu.RLock()
|
||||
for _, msg := range s.msgbuf {
|
||||
fmsg.Println(msg)
|
||||
log.Println(msg)
|
||||
}
|
||||
s.mu.RUnlock()
|
||||
}
|
||||
|
@ -28,18 +28,18 @@ type Hardlink struct {
|
||||
func (l *Hardlink) Type() Enablement { return l.et }
|
||||
|
||||
func (l *Hardlink) apply(_ *I) error {
|
||||
fmsg.VPrintln("linking ", l)
|
||||
fmsg.Verbose("linking ", l)
|
||||
return fmsg.WrapErrorSuffix(os.Link(l.src, l.dst),
|
||||
fmt.Sprintf("cannot link %q:", l.dst))
|
||||
}
|
||||
|
||||
func (l *Hardlink) revert(_ *I, ec *Criteria) error {
|
||||
if ec.hasType(l) {
|
||||
fmsg.VPrintf("removing hard link %q", l.dst)
|
||||
fmsg.Verbosef("removing hard link %q", l.dst)
|
||||
return fmsg.WrapErrorSuffix(os.Remove(l.dst),
|
||||
fmt.Sprintf("cannot remove hard link %q:", l.dst))
|
||||
} else {
|
||||
fmsg.VPrintf("skipping hard link %q", l.dst)
|
||||
fmsg.Verbosef("skipping hard link %q", l.dst)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ func (m *Mkdir) Type() Enablement {
|
||||
}
|
||||
|
||||
func (m *Mkdir) apply(_ *I) error {
|
||||
fmsg.VPrintln("ensuring directory", m)
|
||||
fmsg.Verbose("ensuring directory", m)
|
||||
|
||||
// create directory
|
||||
err := os.Mkdir(m.path, m.perm)
|
||||
@ -61,11 +61,11 @@ func (m *Mkdir) revert(_ *I, ec *Criteria) error {
|
||||
}
|
||||
|
||||
if ec.hasType(m) {
|
||||
fmsg.VPrintln("destroying ephemeral directory", m)
|
||||
fmsg.Verbose("destroying ephemeral directory", m)
|
||||
return fmsg.WrapErrorSuffix(os.Remove(m.path),
|
||||
fmt.Sprintf("cannot remove ephemeral directory %q:", m.path))
|
||||
} else {
|
||||
fmsg.VPrintln("skipping ephemeral directory", m)
|
||||
fmsg.Verbose("skipping ephemeral directory", m)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
@ -105,9 +106,9 @@ func (sys *I) Commit(ctx context.Context) error {
|
||||
// sp is set to nil when all ops are applied
|
||||
if sp != nil {
|
||||
// rollback partial commit
|
||||
fmsg.VPrintf("commit faulted after %d ops, rolling back partial commit", len(sp.ops))
|
||||
fmsg.Verbosef("commit faulted after %d ops, rolling back partial commit", len(sp.ops))
|
||||
if err := sp.Revert(&Criteria{nil}); err != nil {
|
||||
fmsg.Println("errors returned reverting partial commit:", err)
|
||||
log.Println("errors returned reverting partial commit:", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -44,7 +44,7 @@ func (t *Tmpfile) Type() Enablement {
|
||||
func (t *Tmpfile) apply(_ *I) error {
|
||||
switch t.method {
|
||||
case tmpfileCopy:
|
||||
fmsg.VPrintln("publishing tmpfile", t)
|
||||
fmsg.Verbose("publishing tmpfile", t)
|
||||
return fmsg.WrapErrorSuffix(copyFile(t.dst, t.src),
|
||||
fmt.Sprintf("cannot copy tmpfile %q:", t.dst))
|
||||
default:
|
||||
@ -54,11 +54,11 @@ func (t *Tmpfile) apply(_ *I) error {
|
||||
|
||||
func (t *Tmpfile) revert(_ *I, ec *Criteria) error {
|
||||
if ec.hasType(t) {
|
||||
fmsg.VPrintf("removing tmpfile %q", t.dst)
|
||||
fmsg.Verbosef("removing tmpfile %q", t.dst)
|
||||
return fmsg.WrapErrorSuffix(os.Remove(t.dst),
|
||||
fmt.Sprintf("cannot remove tmpfile %q:", t.dst))
|
||||
} else {
|
||||
fmsg.VPrintf("skipping tmpfile %q", t.dst)
|
||||
fmsg.Verbosef("skipping tmpfile %q", t.dst)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ func (w Wayland) apply(sys *I) error {
|
||||
return fmsg.WrapErrorSuffix(err,
|
||||
fmt.Sprintf("cannot attach to wayland on %q:", w.pair[1]))
|
||||
} else {
|
||||
fmsg.VPrintf("wayland attached on %q", w.pair[1])
|
||||
fmsg.Verbosef("wayland attached on %q", w.pair[1])
|
||||
}
|
||||
|
||||
if sp, err := w.conn.Bind(w.pair[0], w.appID, w.instanceID); err != nil {
|
||||
@ -53,7 +53,7 @@ func (w Wayland) apply(sys *I) error {
|
||||
fmt.Sprintf("cannot bind to socket on %q:", w.pair[0]))
|
||||
} else {
|
||||
sys.sp = sp
|
||||
fmsg.VPrintf("wayland listening on %q", w.pair[0])
|
||||
fmsg.Verbosef("wayland listening on %q", w.pair[0])
|
||||
return fmsg.WrapErrorSuffix(errors.Join(os.Chmod(w.pair[0], 0), acl.UpdatePerm(w.pair[0], sys.uid, acl.Read, acl.Write, acl.Execute)),
|
||||
fmt.Sprintf("cannot chmod socket on %q:", w.pair[0]))
|
||||
}
|
||||
@ -61,16 +61,16 @@ func (w Wayland) apply(sys *I) error {
|
||||
|
||||
func (w Wayland) revert(_ *I, ec *Criteria) error {
|
||||
if ec.hasType(w) {
|
||||
fmsg.VPrintf("removing wayland socket on %q", w.pair[0])
|
||||
fmsg.Verbosef("removing wayland socket on %q", w.pair[0])
|
||||
if err := os.Remove(w.pair[0]); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
fmsg.VPrintf("detaching from wayland on %q", w.pair[1])
|
||||
fmsg.Verbosef("detaching from wayland on %q", w.pair[1])
|
||||
return fmsg.WrapErrorSuffix(w.conn.Close(),
|
||||
fmt.Sprintf("cannot detach from wayland on %q:", w.pair[1]))
|
||||
} else {
|
||||
fmsg.VPrintf("skipping wayland cleanup on %q", w.pair[0])
|
||||
fmsg.Verbosef("skipping wayland cleanup on %q", w.pair[0])
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -24,18 +24,18 @@ func (x XHost) Type() Enablement {
|
||||
}
|
||||
|
||||
func (x XHost) apply(_ *I) error {
|
||||
fmsg.VPrintf("inserting entry %s to X11", x)
|
||||
fmsg.Verbosef("inserting entry %s to X11", x)
|
||||
return fmsg.WrapErrorSuffix(xcb.ChangeHosts(xcb.HostModeInsert, xcb.FamilyServerInterpreted, "localuser\x00"+string(x)),
|
||||
fmt.Sprintf("cannot insert entry %s to X11:", x))
|
||||
}
|
||||
|
||||
func (x XHost) revert(_ *I, ec *Criteria) error {
|
||||
if ec.hasType(x) {
|
||||
fmsg.VPrintf("deleting entry %s from X11", x)
|
||||
fmsg.Verbosef("deleting entry %s from X11", x)
|
||||
return fmsg.WrapErrorSuffix(xcb.ChangeHosts(xcb.HostModeDelete, xcb.FamilyServerInterpreted, "localuser\x00"+string(x)),
|
||||
fmt.Sprintf("cannot delete entry %s from X11:", x))
|
||||
} else {
|
||||
fmsg.VPrintf("skipping entry %s in X11", x)
|
||||
fmsg.Verbosef("skipping entry %s in X11", x)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
71
main.go
71
main.go
@ -5,6 +5,7 @@ import (
|
||||
_ "embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"os/user"
|
||||
@ -37,6 +38,8 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
fmsg.Prepare("fortify")
|
||||
|
||||
flag.BoolVar(&flagVerbose, "v", false, "Verbose output")
|
||||
flag.BoolVar(&flagJSON, "json", false, "Format output in JSON when applicable")
|
||||
}
|
||||
@ -62,13 +65,12 @@ func main() {
|
||||
init0.TryArgv0()
|
||||
|
||||
if err := internal.PR_SET_DUMPABLE__SUID_DUMP_DISABLE(); err != nil {
|
||||
fmsg.Printf("cannot set SUID_DUMP_DISABLE: %s", err)
|
||||
log.Printf("cannot set SUID_DUMP_DISABLE: %s", err)
|
||||
// not fatal: this program runs as the privileged user
|
||||
}
|
||||
|
||||
if os.Geteuid() == 0 {
|
||||
fmsg.Fatal("this program must not run as root")
|
||||
panic("unreachable")
|
||||
log.Fatal("this program must not run as root")
|
||||
}
|
||||
|
||||
flag.CommandLine.Usage = func() {
|
||||
@ -96,12 +98,12 @@ func main() {
|
||||
fmt.Println()
|
||||
}
|
||||
flag.Parse()
|
||||
fmsg.SetVerbose(flagVerbose)
|
||||
fmsg.Store(flagVerbose)
|
||||
|
||||
args := flag.Args()
|
||||
if len(args) == 0 {
|
||||
flag.CommandLine.Usage()
|
||||
fmsg.Exit(0)
|
||||
internal.Exit(0)
|
||||
}
|
||||
|
||||
switch args[0] {
|
||||
@ -111,16 +113,20 @@ func main() {
|
||||
} else {
|
||||
fmt.Println("impure")
|
||||
}
|
||||
fmsg.Exit(0)
|
||||
internal.Exit(0)
|
||||
|
||||
case "license": // print embedded license
|
||||
fmt.Println(license)
|
||||
fmsg.Exit(0)
|
||||
internal.Exit(0)
|
||||
|
||||
case "template": // print full template configuration
|
||||
printJSON(os.Stdout, false, fst.Template())
|
||||
fmsg.Exit(0)
|
||||
internal.Exit(0)
|
||||
|
||||
case "help": // print help message
|
||||
flag.CommandLine.Usage()
|
||||
fmsg.Exit(0)
|
||||
internal.Exit(0)
|
||||
|
||||
case "ps": // print all state info
|
||||
set := flag.NewFlagSet("ps", flag.ExitOnError)
|
||||
var short bool
|
||||
@ -130,7 +136,8 @@ func main() {
|
||||
_ = set.Parse(args[1:])
|
||||
|
||||
printPs(os.Stdout, time.Now().UTC(), state.NewMulti(sys.Paths().RunDirPath), short)
|
||||
fmsg.Exit(0)
|
||||
internal.Exit(0)
|
||||
|
||||
case "show": // pretty-print app info
|
||||
set := flag.NewFlagSet("show", flag.ExitOnError)
|
||||
var short bool
|
||||
@ -142,6 +149,7 @@ func main() {
|
||||
switch len(set.Args()) {
|
||||
case 0: // system
|
||||
printShowSystem(os.Stdout, short)
|
||||
|
||||
case 1: // instance
|
||||
name := set.Args()[0]
|
||||
config, instance := tryShort(name)
|
||||
@ -149,14 +157,15 @@ func main() {
|
||||
config = tryPath(name)
|
||||
}
|
||||
printShowInstance(os.Stdout, time.Now().UTC(), instance, config, short)
|
||||
default:
|
||||
fmsg.Fatal("show requires 1 argument")
|
||||
}
|
||||
|
||||
fmsg.Exit(0)
|
||||
default:
|
||||
log.Fatal("show requires 1 argument")
|
||||
}
|
||||
internal.Exit(0)
|
||||
|
||||
case "app": // launch app from configuration file
|
||||
if len(args) < 2 {
|
||||
fmsg.Fatal("app requires at least 1 argument")
|
||||
log.Fatal("app requires at least 1 argument")
|
||||
}
|
||||
|
||||
// config extraArgs...
|
||||
@ -166,6 +175,7 @@ func main() {
|
||||
// invoke app
|
||||
runApp(config)
|
||||
panic("unreachable")
|
||||
|
||||
case "run": // run app in permissive defaults usage pattern
|
||||
set := flag.NewFlagSet("run", flag.ExitOnError)
|
||||
|
||||
@ -208,8 +218,7 @@ func main() {
|
||||
}
|
||||
|
||||
if aid < 0 || aid > 9999 {
|
||||
fmsg.Fatalf("aid %d out of range", aid)
|
||||
panic("unreachable")
|
||||
log.Fatalf("aid %d out of range", aid)
|
||||
}
|
||||
|
||||
// resolve home/username from os when flag is unset
|
||||
@ -219,13 +228,13 @@ func main() {
|
||||
passwdFunc = func() {
|
||||
var us string
|
||||
if uid, err := sys.Uid(aid); err != nil {
|
||||
fmsg.Fatalf("cannot obtain uid from fsu: %v", err)
|
||||
log.Fatalf("cannot obtain uid from fsu: %v", err)
|
||||
} else {
|
||||
us = strconv.Itoa(uid)
|
||||
}
|
||||
|
||||
if u, err := user.LookupId(us); err != nil {
|
||||
fmsg.VPrintf("cannot look up uid %s", us)
|
||||
fmsg.Verbosef("cannot look up uid %s", us)
|
||||
passwd = &user.User{
|
||||
Uid: us,
|
||||
Gid: us,
|
||||
@ -267,7 +276,7 @@ func main() {
|
||||
config.Confinement.SessionBus = dbus.NewConfig(fid, true, mpris)
|
||||
} else {
|
||||
if c, err := dbus.NewConfigFromFile(dbusConfigSession); err != nil {
|
||||
fmsg.Fatalf("cannot load session bus proxy config from %q: %s", dbusConfigSession, err)
|
||||
log.Fatalf("cannot load session bus proxy config from %q: %s", dbusConfigSession, err)
|
||||
} else {
|
||||
config.Confinement.SessionBus = c
|
||||
}
|
||||
@ -276,7 +285,7 @@ func main() {
|
||||
// system bus proxy is optional
|
||||
if dbusConfigSystem != "nil" {
|
||||
if c, err := dbus.NewConfigFromFile(dbusConfigSystem); err != nil {
|
||||
fmsg.Fatalf("cannot load system bus proxy config from %q: %s", dbusConfigSystem, err)
|
||||
log.Fatalf("cannot load system bus proxy config from %q: %s", dbusConfigSystem, err)
|
||||
} else {
|
||||
config.Confinement.SystemBus = c
|
||||
}
|
||||
@ -291,17 +300,18 @@ func main() {
|
||||
|
||||
// invoke app
|
||||
runApp(config)
|
||||
panic("unreachable")
|
||||
|
||||
// internal commands
|
||||
case "shim":
|
||||
shim.Main()
|
||||
fmsg.Exit(0)
|
||||
internal.Exit(0)
|
||||
case "init":
|
||||
init0.Main()
|
||||
fmsg.Exit(0)
|
||||
internal.Exit(0)
|
||||
|
||||
default:
|
||||
fmsg.Fatalf("%q is not a valid command", args[0])
|
||||
log.Fatalf("%q is not a valid command", args[0])
|
||||
}
|
||||
|
||||
panic("unreachable")
|
||||
@ -313,15 +323,15 @@ func runApp(config *fst.Config) {
|
||||
syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop() // unreachable
|
||||
|
||||
if fmsg.Verbose() {
|
||||
seccomp.CPrintln = fmsg.Println
|
||||
if fmsg.Load() {
|
||||
seccomp.CPrintln = log.Println
|
||||
}
|
||||
|
||||
if a, err := app.New(sys); err != nil {
|
||||
fmsg.Fatalf("cannot create app: %s\n", err)
|
||||
log.Fatalf("cannot create app: %s", err)
|
||||
} else if err = a.Seal(config); err != nil {
|
||||
logBaseError(err, "cannot seal app:")
|
||||
fmsg.Exit(1)
|
||||
internal.Exit(1)
|
||||
} else if err = a.Run(ctx, rs); err != nil {
|
||||
if !rs.Start {
|
||||
logBaseError(err, "cannot start app:")
|
||||
@ -334,8 +344,7 @@ func runApp(config *fst.Config) {
|
||||
}
|
||||
}
|
||||
if rs.WaitErr != nil {
|
||||
fmsg.Println("inner wait failed:", rs.WaitErr)
|
||||
log.Println("inner wait failed:", rs.WaitErr)
|
||||
}
|
||||
fmsg.Exit(rs.ExitCode)
|
||||
panic("unreachable")
|
||||
internal.Exit(rs.ExitCode)
|
||||
}
|
||||
|
21
parse.go
21
parse.go
@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -21,11 +22,10 @@ func tryPath(name string) (config *fst.Config) {
|
||||
if name != "-" {
|
||||
r = tryFd(name)
|
||||
if r == nil {
|
||||
fmsg.VPrintln("load configuration from file")
|
||||
fmsg.Verbose("load configuration from file")
|
||||
|
||||
if f, err := os.Open(name); err != nil {
|
||||
fmsg.Fatalf("cannot access configuration file %q: %s", name, err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot access configuration file %q: %s", name, err)
|
||||
} else {
|
||||
// finalizer closes f
|
||||
r = f
|
||||
@ -33,7 +33,7 @@ func tryPath(name string) (config *fst.Config) {
|
||||
} else {
|
||||
defer func() {
|
||||
if err := r.(io.ReadCloser).Close(); err != nil {
|
||||
fmsg.Printf("cannot close config fd: %v", err)
|
||||
log.Printf("cannot close config fd: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@ -42,8 +42,7 @@ func tryPath(name string) (config *fst.Config) {
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r).Decode(&config); err != nil {
|
||||
fmsg.Fatalf("cannot load configuration: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot load configuration: %v", err)
|
||||
}
|
||||
|
||||
return
|
||||
@ -51,7 +50,7 @@ func tryPath(name string) (config *fst.Config) {
|
||||
|
||||
func tryFd(name string) io.ReadCloser {
|
||||
if v, err := strconv.Atoi(name); err != nil {
|
||||
fmsg.VPrintf("name cannot be interpreted as int64: %v", err)
|
||||
fmsg.Verbosef("name cannot be interpreted as int64: %v", err)
|
||||
return nil
|
||||
} else {
|
||||
fd := uintptr(v)
|
||||
@ -59,7 +58,7 @@ func tryFd(name string) io.ReadCloser {
|
||||
if errors.Is(errno, syscall.EBADF) {
|
||||
return nil
|
||||
}
|
||||
fmsg.Fatalf("cannot get fd %d: %v", fd, errno)
|
||||
log.Fatalf("cannot get fd %d: %v", fd, errno)
|
||||
}
|
||||
return os.NewFile(fd, strconv.Itoa(v))
|
||||
}
|
||||
@ -83,11 +82,11 @@ func tryShort(name string) (config *fst.Config, instance *state.State) {
|
||||
|
||||
// try to match from state store
|
||||
if likePrefix && len(name) >= 8 {
|
||||
fmsg.VPrintln("argument looks like prefix")
|
||||
fmsg.Verbose("argument looks like prefix")
|
||||
|
||||
s := state.NewMulti(sys.Paths().RunDirPath)
|
||||
if entries, err := state.Join(s); err != nil {
|
||||
fmsg.Printf("cannot join store: %v", err)
|
||||
log.Printf("cannot join store: %v", err)
|
||||
// drop to fetch from file
|
||||
} else {
|
||||
for id := range entries {
|
||||
@ -99,7 +98,7 @@ func tryShort(name string) (config *fst.Config, instance *state.State) {
|
||||
break
|
||||
}
|
||||
|
||||
fmsg.VPrintf("instance %s skipped", v)
|
||||
fmsg.Verbosef("instance %s skipped", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
30
print.go
30
print.go
@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -12,7 +13,6 @@ import (
|
||||
|
||||
"git.gensokyo.uk/security/fortify/dbus"
|
||||
"git.gensokyo.uk/security/fortify/fst"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
"git.gensokyo.uk/security/fortify/internal/state"
|
||||
)
|
||||
|
||||
@ -24,7 +24,7 @@ func printShowSystem(output io.Writer, short bool) {
|
||||
|
||||
// get fid by querying uid of aid 0
|
||||
if uid, err := sys.Uid(0); err != nil {
|
||||
fmsg.Fatalf("cannot obtain uid from fsu: %v", err)
|
||||
log.Fatalf("cannot obtain uid from fsu: %v", err)
|
||||
} else {
|
||||
info.User = (uid / 10000) - 100
|
||||
}
|
||||
@ -190,12 +190,12 @@ func printShowInstance(
|
||||
func printPs(output io.Writer, now time.Time, s state.Store, short bool) {
|
||||
var entries state.Entries
|
||||
if e, err := state.Join(s); err != nil {
|
||||
fmsg.Fatalf("cannot join store: %v", err)
|
||||
log.Fatalf("cannot join store: %v", err)
|
||||
} else {
|
||||
entries = e
|
||||
}
|
||||
if err := s.Close(); err != nil {
|
||||
fmsg.Printf("cannot close store: %v", err)
|
||||
log.Printf("cannot close store: %v", err)
|
||||
}
|
||||
|
||||
if !short && flagJSON {
|
||||
@ -212,13 +212,13 @@ func printPs(output io.Writer, now time.Time, s state.Store, short bool) {
|
||||
for id, instance := range entries {
|
||||
// gracefully skip nil states
|
||||
if instance == nil {
|
||||
fmsg.Printf("got invalid state entry %s", id.String())
|
||||
log.Printf("got invalid state entry %s", id.String())
|
||||
continue
|
||||
}
|
||||
|
||||
// gracefully skip inconsistent states
|
||||
if id != instance.ID {
|
||||
fmsg.Printf("possible store corruption: entry %s has id %s",
|
||||
log.Printf("possible store corruption: entry %s has id %s",
|
||||
id.String(), instance.ID.String())
|
||||
continue
|
||||
}
|
||||
@ -273,8 +273,7 @@ func printJSON(output io.Writer, short bool, v any) {
|
||||
encoder.SetIndent("", " ")
|
||||
}
|
||||
if err := encoder.Encode(v); err != nil {
|
||||
fmsg.Fatalf("cannot serialise: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot serialise: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -284,31 +283,26 @@ type tp struct{ *tabwriter.Writer }
|
||||
|
||||
func (p *tp) Printf(format string, a ...any) {
|
||||
if _, err := fmt.Fprintf(p, format, a...); err != nil {
|
||||
fmsg.Fatalf("cannot write to tabwriter: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot write to tabwriter: %v", err)
|
||||
}
|
||||
}
|
||||
func (p *tp) Println(a ...any) {
|
||||
if _, err := fmt.Fprintln(p, a...); err != nil {
|
||||
fmsg.Fatalf("cannot write to tabwriter: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot write to tabwriter: %v", err)
|
||||
}
|
||||
}
|
||||
func (p *tp) MustFlush() {
|
||||
if err := p.Writer.Flush(); err != nil {
|
||||
fmsg.Fatalf("cannot flush tabwriter: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot flush tabwriter: %v", err)
|
||||
}
|
||||
}
|
||||
func mustPrint(output io.Writer, a ...any) {
|
||||
if _, err := fmt.Fprint(output, a...); err != nil {
|
||||
fmsg.Fatalf("cannot print: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot print: %v", err)
|
||||
}
|
||||
}
|
||||
func mustPrintln(output io.Writer, a ...any) {
|
||||
if _, err := fmt.Fprintln(output, a...); err != nil {
|
||||
fmsg.Fatalf("cannot print: %v", err)
|
||||
panic("unreachable")
|
||||
log.Fatalf("cannot print: %v", err)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user