fortify: integrate command handler
Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
89970f5197
commit
7e52463445
428
main.go
428
main.go
@ -3,19 +3,18 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"flag"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"os/user"
|
"os/user"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"text/tabwriter"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.gensokyo.uk/security/fortify/command"
|
||||||
"git.gensokyo.uk/security/fortify/dbus"
|
"git.gensokyo.uk/security/fortify/dbus"
|
||||||
"git.gensokyo.uk/security/fortify/fst"
|
"git.gensokyo.uk/security/fortify/fst"
|
||||||
"git.gensokyo.uk/security/fortify/helper/seccomp"
|
"git.gensokyo.uk/security/fortify/helper/seccomp"
|
||||||
@ -30,36 +29,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
flagVerbose bool
|
errSuccess = errors.New("success")
|
||||||
flagJSON bool
|
|
||||||
|
|
||||||
//go:embed LICENSE
|
//go:embed LICENSE
|
||||||
license string
|
license string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() { fmsg.Prepare("fortify") }
|
||||||
fmsg.Prepare("fortify")
|
|
||||||
|
|
||||||
flag.BoolVar(&flagVerbose, "v", false, "Verbose output")
|
|
||||||
flag.BoolVar(&flagJSON, "json", false, "Format output in JSON when applicable")
|
|
||||||
}
|
|
||||||
|
|
||||||
var std sys.State = new(sys.Std)
|
var std sys.State = new(sys.Std)
|
||||||
|
|
||||||
type gl []string
|
|
||||||
|
|
||||||
func (g *gl) String() string {
|
|
||||||
if g == nil {
|
|
||||||
return "<nil>"
|
|
||||||
}
|
|
||||||
return strings.Join(*g, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *gl) Set(v string) error {
|
|
||||||
*g = append(*g, v)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// early init argv0 check, skips root check and duplicate PR_SET_DUMPABLE
|
// early init argv0 check, skips root check and duplicate PR_SET_DUMPABLE
|
||||||
init0.TryArgv0()
|
init0.TryArgv0()
|
||||||
@ -73,112 +52,29 @@ func main() {
|
|||||||
log.Fatal("this program must not run as root")
|
log.Fatal("this program must not run as root")
|
||||||
}
|
}
|
||||||
|
|
||||||
flag.CommandLine.Usage = func() {
|
var (
|
||||||
fmt.Println()
|
flagVerbose bool
|
||||||
fmt.Println("Usage:\tfortify [-v] [--json] COMMAND [OPTIONS]")
|
flagJSON bool
|
||||||
fmt.Println()
|
)
|
||||||
fmt.Println("Commands:")
|
c := command.New(os.Stderr, log.Printf, "fortify", func([]string) error { fmsg.Store(flagVerbose); return nil }).
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 1, 4, ' ', 0)
|
Flag(&flagVerbose, "v", command.BoolFlag(false), "Print debug messages to the console").
|
||||||
commands := [][2]string{
|
Flag(&flagJSON, "json", command.BoolFlag(false), "Serialise output as JSON when applicable")
|
||||||
{"app", "Launch app defined by the specified config file"},
|
|
||||||
{"run", "Configure and start a permissive default sandbox"},
|
|
||||||
{"show", "Show the contents of an app configuration"},
|
|
||||||
{"ps", "List active apps and their state"},
|
|
||||||
{"version", "Show fortify version"},
|
|
||||||
{"license", "Show full license text"},
|
|
||||||
{"template", "Produce a config template"},
|
|
||||||
{"help", "Show this help message"},
|
|
||||||
}
|
|
||||||
for _, c := range commands {
|
|
||||||
_, _ = fmt.Fprintf(w, "\t%s\t%s\n", c[0], c[1])
|
|
||||||
}
|
|
||||||
if err := w.Flush(); err != nil {
|
|
||||||
fmt.Printf("fortify: cannot write command list: %v\n", err)
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
flag.Parse()
|
|
||||||
fmsg.Store(flagVerbose)
|
|
||||||
|
|
||||||
args := flag.Args()
|
c.Command("app", "Launch app defined by the specified config file", func(args []string) error {
|
||||||
if len(args) == 0 {
|
if len(args) < 1 {
|
||||||
flag.CommandLine.Usage()
|
|
||||||
internal.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch args[0] {
|
|
||||||
case "version": // print version string
|
|
||||||
if v, ok := internal.Check(internal.Version); ok {
|
|
||||||
fmt.Println(v)
|
|
||||||
} else {
|
|
||||||
fmt.Println("impure")
|
|
||||||
}
|
|
||||||
internal.Exit(0)
|
|
||||||
|
|
||||||
case "license": // print embedded license
|
|
||||||
fmt.Println(license)
|
|
||||||
internal.Exit(0)
|
|
||||||
|
|
||||||
case "template": // print full template configuration
|
|
||||||
printJSON(os.Stdout, false, fst.Template())
|
|
||||||
internal.Exit(0)
|
|
||||||
|
|
||||||
case "help": // print help message
|
|
||||||
flag.CommandLine.Usage()
|
|
||||||
internal.Exit(0)
|
|
||||||
|
|
||||||
case "ps": // print all state info
|
|
||||||
set := flag.NewFlagSet("ps", flag.ExitOnError)
|
|
||||||
var short bool
|
|
||||||
set.BoolVar(&short, "short", false, "Print instance id")
|
|
||||||
|
|
||||||
// Ignore errors; set is set for ExitOnError.
|
|
||||||
_ = set.Parse(args[1:])
|
|
||||||
|
|
||||||
printPs(os.Stdout, time.Now().UTC(), state.NewMulti(std.Paths().RunDirPath), short)
|
|
||||||
internal.Exit(0)
|
|
||||||
|
|
||||||
case "show": // pretty-print app info
|
|
||||||
set := flag.NewFlagSet("show", flag.ExitOnError)
|
|
||||||
var short bool
|
|
||||||
set.BoolVar(&short, "short", false, "Omit filesystem information")
|
|
||||||
|
|
||||||
// Ignore errors; set is set for ExitOnError.
|
|
||||||
_ = set.Parse(args[1:])
|
|
||||||
|
|
||||||
switch len(set.Args()) {
|
|
||||||
case 0: // system
|
|
||||||
printShowSystem(os.Stdout, short)
|
|
||||||
|
|
||||||
case 1: // instance
|
|
||||||
name := set.Args()[0]
|
|
||||||
config, instance := tryShort(name)
|
|
||||||
if config == nil {
|
|
||||||
config = tryPath(name)
|
|
||||||
}
|
|
||||||
printShowInstance(os.Stdout, time.Now().UTC(), instance, config, short)
|
|
||||||
|
|
||||||
default:
|
|
||||||
log.Fatal("show requires 1 argument")
|
|
||||||
}
|
|
||||||
internal.Exit(0)
|
|
||||||
|
|
||||||
case "app": // launch app from configuration file
|
|
||||||
if len(args) < 2 {
|
|
||||||
log.Fatal("app requires at least 1 argument")
|
log.Fatal("app requires at least 1 argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
// config extraArgs...
|
// config extraArgs...
|
||||||
config := tryPath(args[1])
|
config := tryPath(args[0])
|
||||||
config.Command = append(config.Command, args[2:]...)
|
config.Command = append(config.Command, args[1:]...)
|
||||||
|
|
||||||
// invoke app
|
// invoke app
|
||||||
runApp(app.MustNew(std), config)
|
runApp(app.MustNew(std), config)
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
|
})
|
||||||
|
|
||||||
case "run": // run app in permissive defaults usage pattern
|
{
|
||||||
set := flag.NewFlagSet("run", flag.ExitOnError)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dbusConfigSession string
|
dbusConfigSession string
|
||||||
dbusConfigSystem string
|
dbusConfigSystem string
|
||||||
@ -187,135 +83,211 @@ func main() {
|
|||||||
|
|
||||||
fid string
|
fid string
|
||||||
aid int
|
aid int
|
||||||
groups gl
|
groups command.RepeatableFlag
|
||||||
homeDir string
|
homeDir string
|
||||||
userName string
|
userName string
|
||||||
enablements [system.ELen]bool
|
enablements [system.ELen]bool
|
||||||
)
|
)
|
||||||
|
|
||||||
set.StringVar(&dbusConfigSession, "dbus-config", "builtin", "Path to D-Bus proxy config file, or \"builtin\" for defaults")
|
c.NewCommand("run", "Configure and start a permissive default sandbox", func(args []string) error {
|
||||||
set.StringVar(&dbusConfigSystem, "dbus-system", "nil", "Path to system D-Bus proxy config file, or \"nil\" to disable")
|
// initialise config from flags
|
||||||
set.BoolVar(&mpris, "mpris", false, "Allow owning MPRIS D-Bus path, has no effect if custom config is available")
|
config := &fst.Config{
|
||||||
set.BoolVar(&dbusVerbose, "dbus-log", false, "Force logging in the D-Bus proxy")
|
ID: fid,
|
||||||
|
Command: args,
|
||||||
|
}
|
||||||
|
|
||||||
set.StringVar(&fid, "id", "", "App ID, leave empty to disable security context app_id")
|
if aid < 0 || aid > 9999 {
|
||||||
set.IntVar(&aid, "a", 0, "Fortify application ID")
|
log.Fatalf("aid %d out of range", aid)
|
||||||
set.Var(&groups, "g", "Groups inherited by the app process")
|
}
|
||||||
set.StringVar(&homeDir, "d", "os", "Application home directory")
|
|
||||||
set.StringVar(&userName, "u", "chronos", "Passwd name within sandbox")
|
|
||||||
set.BoolVar(&enablements[system.EWayland], "wayland", false, "Allow Wayland connections")
|
|
||||||
set.BoolVar(&enablements[system.EX11], "X", false, "Share X11 socket and allow connection")
|
|
||||||
set.BoolVar(&enablements[system.EDBus], "dbus", false, "Proxy D-Bus connection")
|
|
||||||
set.BoolVar(&enablements[system.EPulse], "pulse", false, "Share PulseAudio socket and cookie")
|
|
||||||
|
|
||||||
// Ignore errors; set is set for ExitOnError.
|
// resolve home/username from os when flag is unset
|
||||||
_ = set.Parse(args[1:])
|
var (
|
||||||
|
passwd *user.User
|
||||||
// initialise config from flags
|
passwdOnce sync.Once
|
||||||
config := &fst.Config{
|
passwdFunc = func() {
|
||||||
ID: fid,
|
var us string
|
||||||
Command: set.Args(),
|
if uid, err := std.Uid(aid); err != nil {
|
||||||
}
|
fmsg.PrintBaseError(err, "cannot obtain uid from fsu:")
|
||||||
|
os.Exit(1)
|
||||||
if aid < 0 || aid > 9999 {
|
} else {
|
||||||
log.Fatalf("aid %d out of range", aid)
|
us = strconv.Itoa(uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve home/username from os when flag is unset
|
if u, err := user.LookupId(us); err != nil {
|
||||||
var (
|
fmsg.Verbosef("cannot look up uid %s", us)
|
||||||
passwd *user.User
|
passwd = &user.User{
|
||||||
passwdOnce sync.Once
|
Uid: us,
|
||||||
passwdFunc = func() {
|
Gid: us,
|
||||||
var us string
|
Username: "chronos",
|
||||||
if uid, err := std.Uid(aid); err != nil {
|
Name: "Fortify",
|
||||||
fmsg.PrintBaseError(err, "cannot obtain uid from fsu:")
|
HomeDir: "/var/empty",
|
||||||
os.Exit(1)
|
}
|
||||||
} else {
|
} else {
|
||||||
us = strconv.Itoa(uid)
|
passwd = u
|
||||||
}
|
|
||||||
|
|
||||||
if u, err := user.LookupId(us); err != nil {
|
|
||||||
fmsg.Verbosef("cannot look up uid %s", us)
|
|
||||||
passwd = &user.User{
|
|
||||||
Uid: us,
|
|
||||||
Gid: us,
|
|
||||||
Username: "chronos",
|
|
||||||
Name: "Fortify",
|
|
||||||
HomeDir: "/var/empty",
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
passwd = u
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if homeDir == "os" {
|
||||||
|
passwdOnce.Do(passwdFunc)
|
||||||
|
homeDir = passwd.HomeDir
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
if homeDir == "os" {
|
if userName == "chronos" {
|
||||||
passwdOnce.Do(passwdFunc)
|
passwdOnce.Do(passwdFunc)
|
||||||
homeDir = passwd.HomeDir
|
userName = passwd.Username
|
||||||
}
|
|
||||||
|
|
||||||
if userName == "chronos" {
|
|
||||||
passwdOnce.Do(passwdFunc)
|
|
||||||
userName = passwd.Username
|
|
||||||
}
|
|
||||||
|
|
||||||
config.Confinement.AppID = aid
|
|
||||||
config.Confinement.Groups = groups
|
|
||||||
config.Confinement.Outer = homeDir
|
|
||||||
config.Confinement.Username = userName
|
|
||||||
|
|
||||||
// enablements from flags
|
|
||||||
for i := system.Enablement(0); i < system.Enablement(system.ELen); i++ {
|
|
||||||
if enablements[i] {
|
|
||||||
config.Confinement.Enablements.Set(i)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// parse D-Bus config file from flags if applicable
|
config.Confinement.AppID = aid
|
||||||
if enablements[system.EDBus] {
|
config.Confinement.Groups = groups
|
||||||
if dbusConfigSession == "builtin" {
|
config.Confinement.Outer = homeDir
|
||||||
config.Confinement.SessionBus = dbus.NewConfig(fid, true, mpris)
|
config.Confinement.Username = userName
|
||||||
} else {
|
|
||||||
if c, err := dbus.NewConfigFromFile(dbusConfigSession); err != nil {
|
// enablements from flags
|
||||||
log.Fatalf("cannot load session bus proxy config from %q: %s", dbusConfigSession, err)
|
for i := system.Enablement(0); i < system.Enablement(system.ELen); i++ {
|
||||||
} else {
|
if enablements[i] {
|
||||||
config.Confinement.SessionBus = c
|
config.Confinement.Enablements.Set(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// system bus proxy is optional
|
// parse D-Bus config file from flags if applicable
|
||||||
if dbusConfigSystem != "nil" {
|
if enablements[system.EDBus] {
|
||||||
if c, err := dbus.NewConfigFromFile(dbusConfigSystem); err != nil {
|
if dbusConfigSession == "builtin" {
|
||||||
log.Fatalf("cannot load system bus proxy config from %q: %s", dbusConfigSystem, err)
|
config.Confinement.SessionBus = dbus.NewConfig(fid, true, mpris)
|
||||||
} else {
|
} else {
|
||||||
config.Confinement.SystemBus = c
|
if conf, err := dbus.NewConfigFromFile(dbusConfigSession); err != nil {
|
||||||
|
log.Fatalf("cannot load session bus proxy config from %q: %s", dbusConfigSession, err)
|
||||||
|
} else {
|
||||||
|
config.Confinement.SessionBus = conf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// system bus proxy is optional
|
||||||
|
if dbusConfigSystem != "nil" {
|
||||||
|
if conf, err := dbus.NewConfigFromFile(dbusConfigSystem); err != nil {
|
||||||
|
log.Fatalf("cannot load system bus proxy config from %q: %s", dbusConfigSystem, err)
|
||||||
|
} else {
|
||||||
|
config.Confinement.SystemBus = conf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// override log from configuration
|
||||||
|
if dbusVerbose {
|
||||||
|
config.Confinement.SessionBus.Log = true
|
||||||
|
config.Confinement.SystemBus.Log = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// override log from configuration
|
// invoke app
|
||||||
if dbusVerbose {
|
runApp(app.MustNew(std), config)
|
||||||
config.Confinement.SessionBus.Log = true
|
panic("unreachable")
|
||||||
config.Confinement.SystemBus.Log = true
|
}).
|
||||||
}
|
Flag(&dbusConfigSession, "dbus-config", command.StringFlag("builtin"),
|
||||||
}
|
"Path to D-Bus proxy config file, or \"builtin\" for defaults").
|
||||||
|
Flag(&dbusConfigSystem, "dbus-system", command.StringFlag("nil"),
|
||||||
// invoke app
|
"Path to system D-Bus proxy config file, or \"nil\" to disable").
|
||||||
runApp(app.MustNew(std), config)
|
Flag(&mpris, "mpris", command.BoolFlag(false),
|
||||||
panic("unreachable")
|
"Allow owning MPRIS D-Bus path, has no effect if custom config is available").
|
||||||
|
Flag(&dbusVerbose, "dbus-log", command.BoolFlag(false),
|
||||||
// internal commands
|
"Force logging in the D-Bus proxy").
|
||||||
case "shim":
|
Flag(&fid, "id", command.StringFlag(""),
|
||||||
shim.Main()
|
"App ID, leave empty to disable security context app_id").
|
||||||
internal.Exit(0)
|
Flag(&aid, "a", command.IntFlag(0),
|
||||||
case "init":
|
"Fortify application ID").
|
||||||
init0.Main()
|
Flag(nil, "g", &groups,
|
||||||
internal.Exit(0)
|
"Groups inherited by the app process").
|
||||||
|
Flag(&homeDir, "d", command.StringFlag("os"),
|
||||||
default:
|
"Application home directory").
|
||||||
log.Fatalf("%q is not a valid command", args[0])
|
Flag(&userName, "u", command.StringFlag("chronos"),
|
||||||
|
"Passwd name within sandbox").
|
||||||
|
Flag(&enablements[system.EWayland], "wayland", command.BoolFlag(false),
|
||||||
|
"Allow Wayland connections").
|
||||||
|
Flag(&enablements[system.EX11], "X", command.BoolFlag(false),
|
||||||
|
"Share X11 socket and allow connection").
|
||||||
|
Flag(&enablements[system.EDBus], "dbus", command.BoolFlag(false),
|
||||||
|
"Proxy D-Bus connection").
|
||||||
|
Flag(&enablements[system.EPulse], "pulse", command.BoolFlag(false),
|
||||||
|
"Share PulseAudio socket and cookie")
|
||||||
}
|
}
|
||||||
|
|
||||||
panic("unreachable")
|
var showFlagShort bool
|
||||||
|
c.NewCommand("show", "Show the contents of an app configuration", func(args []string) error {
|
||||||
|
switch len(args) {
|
||||||
|
case 0: // system
|
||||||
|
printShowSystem(os.Stdout, showFlagShort, flagJSON)
|
||||||
|
|
||||||
|
case 1: // instance
|
||||||
|
name := args[0]
|
||||||
|
config, instance := tryShort(name)
|
||||||
|
if config == nil {
|
||||||
|
config = tryPath(name)
|
||||||
|
}
|
||||||
|
printShowInstance(os.Stdout, time.Now().UTC(), instance, config, showFlagShort, flagJSON)
|
||||||
|
|
||||||
|
default:
|
||||||
|
log.Fatal("show requires 1 argument")
|
||||||
|
}
|
||||||
|
return errSuccess
|
||||||
|
}).Flag(&showFlagShort, "short", command.BoolFlag(false), "Omit filesystem information")
|
||||||
|
|
||||||
|
var psFlagShort bool
|
||||||
|
c.NewCommand("ps", "List active apps and their state", func(args []string) error {
|
||||||
|
printPs(os.Stdout, time.Now().UTC(), state.NewMulti(std.Paths().RunDirPath), psFlagShort, flagJSON)
|
||||||
|
return errSuccess
|
||||||
|
}).Flag(&psFlagShort, "short", command.BoolFlag(false), "Print instance id")
|
||||||
|
|
||||||
|
c.Command("version", "Show fortify version", func(args []string) error {
|
||||||
|
if v, ok := internal.Check(internal.Version); ok {
|
||||||
|
fmt.Println(v)
|
||||||
|
} else {
|
||||||
|
fmt.Println("impure")
|
||||||
|
}
|
||||||
|
return errSuccess
|
||||||
|
})
|
||||||
|
|
||||||
|
c.Command("license", "Show full license text", func(args []string) error {
|
||||||
|
fmt.Println(license)
|
||||||
|
return errSuccess
|
||||||
|
})
|
||||||
|
|
||||||
|
c.Command("template", "Produce a config template", func(args []string) error {
|
||||||
|
printJSON(os.Stdout, false, fst.Template())
|
||||||
|
return errSuccess
|
||||||
|
})
|
||||||
|
|
||||||
|
c.Command("help", "Show this help message", func([]string) error {
|
||||||
|
c.PrintHelp()
|
||||||
|
return errSuccess
|
||||||
|
})
|
||||||
|
|
||||||
|
// internal commands
|
||||||
|
c.Command("shim", command.UsageInternal, func([]string) error { shim.Main(); return errSuccess })
|
||||||
|
c.Command("init", command.UsageInternal, func([]string) error { init0.Main(); return errSuccess })
|
||||||
|
|
||||||
|
err := c.Parse(os.Args[1:])
|
||||||
|
if errors.Is(err, errSuccess) || errors.Is(err, command.ErrHelp) {
|
||||||
|
internal.Exit(0)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
if errors.Is(err, command.ErrNoMatch) || errors.Is(err, command.ErrEmptyTree) {
|
||||||
|
internal.Exit(1)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
log.Fatal("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
var flagError command.FlagError
|
||||||
|
if !errors.As(err, &flagError) {
|
||||||
|
log.Printf("command: %v", err)
|
||||||
|
internal.Exit(1)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
fmsg.Verbose(flagError.Error())
|
||||||
|
if flagError.Success() {
|
||||||
|
internal.Exit(0)
|
||||||
|
}
|
||||||
|
internal.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runApp(a fst.App, config *fst.Config) {
|
func runApp(a fst.App, config *fst.Config) {
|
||||||
|
6
print.go
6
print.go
@ -18,7 +18,7 @@ import (
|
|||||||
"git.gensokyo.uk/security/fortify/internal/state"
|
"git.gensokyo.uk/security/fortify/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
func printShowSystem(output io.Writer, short bool) {
|
func printShowSystem(output io.Writer, short, flagJSON bool) {
|
||||||
t := newPrinter(output)
|
t := newPrinter(output)
|
||||||
defer t.MustFlush()
|
defer t.MustFlush()
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ func printShowSystem(output io.Writer, short bool) {
|
|||||||
func printShowInstance(
|
func printShowInstance(
|
||||||
output io.Writer, now time.Time,
|
output io.Writer, now time.Time,
|
||||||
instance *state.State, config *fst.Config,
|
instance *state.State, config *fst.Config,
|
||||||
short bool) {
|
short, flagJSON bool) {
|
||||||
if flagJSON {
|
if flagJSON {
|
||||||
if instance != nil {
|
if instance != nil {
|
||||||
printJSON(output, short, instance)
|
printJSON(output, short, instance)
|
||||||
@ -190,7 +190,7 @@ func printShowInstance(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printPs(output io.Writer, now time.Time, s state.Store, short bool) {
|
func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON bool) {
|
||||||
var entries state.Entries
|
var entries state.Entries
|
||||||
if e, err := state.Join(s); err != nil {
|
if e, err := state.Join(s); err != nil {
|
||||||
log.Fatalf("cannot join store: %v", err)
|
log.Fatalf("cannot join store: %v", err)
|
||||||
|
@ -448,14 +448,8 @@ App
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
{
|
|
||||||
v := flagJSON
|
|
||||||
t.Cleanup(func() { flagJSON = v })
|
|
||||||
flagJSON = tc.json
|
|
||||||
}
|
|
||||||
|
|
||||||
output := new(strings.Builder)
|
output := new(strings.Builder)
|
||||||
printShowInstance(output, testTime, tc.instance, tc.config, tc.short)
|
printShowInstance(output, testTime, tc.instance, tc.config, tc.short, tc.json)
|
||||||
if got := output.String(); got != tc.want {
|
if got := output.String(); got != tc.want {
|
||||||
t.Errorf("printShowInstance: got\n%s\nwant\n%s",
|
t.Errorf("printShowInstance: got\n%s\nwant\n%s",
|
||||||
got, tc.want)
|
got, tc.want)
|
||||||
@ -645,14 +639,8 @@ func Test_printPs(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
{
|
|
||||||
v := flagJSON
|
|
||||||
t.Cleanup(func() { flagJSON = v })
|
|
||||||
flagJSON = tc.json
|
|
||||||
}
|
|
||||||
|
|
||||||
output := new(strings.Builder)
|
output := new(strings.Builder)
|
||||||
printPs(output, testTime, stubStore(tc.entries), tc.short)
|
printPs(output, testTime, stubStore(tc.entries), tc.short, tc.json)
|
||||||
if got := output.String(); got != tc.want {
|
if got := output.String(); got != tc.want {
|
||||||
t.Errorf("printPs: got\n%s\nwant\n%s",
|
t.Errorf("printPs: got\n%s\nwant\n%s",
|
||||||
got, tc.want)
|
got, tc.want)
|
||||||
|
Loading…
Reference in New Issue
Block a user