From 714818c8aa1a26450cdfac0828d1a9528fdb95a4 Mon Sep 17 00:00:00 2001 From: Ophestra Umiker Date: Mon, 4 Nov 2024 23:08:29 +0900 Subject: [PATCH] fortify: implement cleaner argument structure Signed-off-by: Ophestra Umiker --- config.go | 135 ------------------------------------ license.go | 25 ------- main.go | 199 +++++++++++++++++++++++++++++++++++++++++++++++++---- state.go | 35 ---------- version.go | 27 -------- 5 files changed, 187 insertions(+), 234 deletions(-) delete mode 100644 config.go delete mode 100644 license.go delete mode 100644 state.go delete mode 100644 version.go diff --git a/config.go b/config.go deleted file mode 100644 index 261b59c..0000000 --- a/config.go +++ /dev/null @@ -1,135 +0,0 @@ -package main - -import ( - "encoding/json" - "flag" - "fmt" - - "git.ophivana.moe/security/fortify/dbus" - "git.ophivana.moe/security/fortify/internal/app" - "git.ophivana.moe/security/fortify/internal/fmsg" - "git.ophivana.moe/security/fortify/internal/system" -) - -var ( - printTemplate bool - - confPath string - - dbusConfigSession string - dbusConfigSystem string - dbusID string - mpris bool - dbusVerbose bool - - userName string - enablements [system.ELen]bool - - launchMethodText string -) - -func init() { - flag.BoolVar(&printTemplate, "template", false, "Print a full config template and exit") - - // config file, disables every other flag here - flag.StringVar(&confPath, "c", "nil", "Path to full app configuration, or \"nil\" to configure from flags") - - flag.StringVar(&dbusConfigSession, "dbus-config", "builtin", "Path to D-Bus proxy config file, or \"builtin\" for defaults") - flag.StringVar(&dbusConfigSystem, "dbus-system", "nil", "Path to system D-Bus proxy config file, or \"nil\" to disable") - flag.StringVar(&dbusID, "dbus-id", "", "D-Bus ID of application, leave empty to disable own paths, has no effect if custom config is available") - flag.BoolVar(&mpris, "mpris", false, "Allow owning MPRIS D-Bus path, has no effect if custom config is available") - flag.BoolVar(&dbusVerbose, "dbus-log", false, "Force logging in the D-Bus proxy") - - flag.StringVar(&userName, "u", "chronos", "Passwd name of user to run as") - flag.BoolVar(&enablements[system.EWayland], "wayland", false, "Share Wayland socket") - flag.BoolVar(&enablements[system.EX11], "X", false, "Share X11 socket and allow connection") - flag.BoolVar(&enablements[system.EDBus], "dbus", false, "Proxy D-Bus connection") - flag.BoolVar(&enablements[system.EPulse], "pulse", false, "Share PulseAudio socket and cookie") -} - -func init() { - methodHelpString := "Method of launching the child process, can be one of \"sudo\"" - if os.SdBooted() { - methodHelpString += ", \"systemd\"" - } - - flag.StringVar(&launchMethodText, "method", "sudo", methodHelpString) -} - -func tryTemplate() { - if printTemplate { - if s, err := json.MarshalIndent(app.Template(), "", " "); err != nil { - fmsg.Fatalf("cannot generate template: %v", err) - panic("unreachable") - } else { - fmt.Println(string(s)) - } - fmsg.Exit(0) - } -} - -func loadConfig() *app.Config { - if confPath == "nil" { - // config from flags - return configFromFlags() - } else { - // config from file - c := new(app.Config) - if f, err := os.Open(confPath); err != nil { - fmsg.Fatalf("cannot access config file %q: %s", confPath, err) - panic("unreachable") - } else if err = json.NewDecoder(f).Decode(&c); err != nil { - fmsg.Fatalf("cannot parse config file %q: %s", confPath, err) - panic("unreachable") - } else { - return c - } - } -} - -func configFromFlags() (config *app.Config) { - // initialise config from flags - config = &app.Config{ - ID: dbusID, - User: userName, - Command: flag.Args(), - Method: launchMethodText, - } - - // 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 - if enablements[system.EDBus] { - if dbusConfigSession == "builtin" { - config.Confinement.SessionBus = dbus.NewConfig(dbusID, true, mpris) - } else { - if c, err := dbus.NewConfigFromFile(dbusConfigSession); err != nil { - fmsg.Fatalf("cannot load session bus proxy config from %q: %s", dbusConfigSession, err) - } else { - config.Confinement.SessionBus = c - } - } - - // 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) - } else { - config.Confinement.SystemBus = c - } - } - - // override log from configuration - if dbusVerbose { - config.Confinement.SessionBus.Log = true - config.Confinement.SystemBus.Log = true - } - } - - return -} diff --git a/license.go b/license.go deleted file mode 100644 index dbeede0..0000000 --- a/license.go +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import ( - _ "embed" - "flag" - "fmt" -) - -var ( - //go:embed LICENSE - license string - - printLicense bool -) - -func init() { - flag.BoolVar(&printLicense, "license", false, "Print license") -} - -func tryLicense() { - if printLicense { - fmt.Println(license) - os.Exit(0) - } -} diff --git a/main.go b/main.go index 309af76..7c0a218 100644 --- a/main.go +++ b/main.go @@ -1,16 +1,26 @@ package main import ( + _ "embed" + "encoding/json" "flag" + "fmt" + "text/tabwriter" + "git.ophivana.moe/security/fortify/dbus" "git.ophivana.moe/security/fortify/internal" "git.ophivana.moe/security/fortify/internal/app" "git.ophivana.moe/security/fortify/internal/fmsg" "git.ophivana.moe/security/fortify/internal/linux" + "git.ophivana.moe/security/fortify/internal/state" + "git.ophivana.moe/security/fortify/internal/system" ) var ( flagVerbose bool + + //go:embed LICENSE + license string ) func init() { @@ -28,29 +38,193 @@ func main() { flag.Parse() fmsg.SetVerbose(flagVerbose) - if os.SdBooted() { - fmsg.VPrintln("system booted with systemd as init system") - } - // root check if os.Geteuid() == 0 { fmsg.Fatal("this program must not run as root") panic("unreachable") } - // version/license/template command early exit - tryVersion() - tryLicense() - tryTemplate() + printHelp := func() { + fmt.Println() + fmt.Println("Usage:\tfortify [-v] COMMAND [OPTIONS]") + fmt.Println() + fmt.Println("Commands:") + w := tabwriter.NewWriter(os.Stdout(), 0, 1, 4, ' ', 0) + commands := [][2]string{ + {"app", "Launch app defined by the specified config file"}, + {"run", "Configure and start a permissive default sandbox"}, + {"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 { + fmsg.Fatalf("cannot print help: %v", err) + } + fmt.Println() + } - // state query command early exit - tryState() + args := flag.Args() + if len(args) == 0 { + printHelp() + fmsg.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") + } + fmsg.Exit(0) + case "license": // print embedded license + fmt.Println(license) + fmsg.Exit(0) + case "template": // print full template configuration + if s, err := json.MarshalIndent(app.Template(), "", " "); err != nil { + fmsg.Fatalf("cannot generate template: %v", err) + panic("unreachable") + } else { + fmt.Println(string(s)) + } + fmsg.Exit(0) + case "help": // print help message + printHelp() + fmsg.Exit(0) + case "ps": // print all state info + var w *tabwriter.Writer + state.MustPrintLauncherStateSimpleGlobal(&w, os.Paths().RunDirPath) + if w != nil { + if err := w.Flush(); err != nil { + fmsg.Println("cannot format output:", err) + } + } else { + fmt.Println("No information available") + } + + fmsg.Exit(0) + case "app": // launch app from configuration file + if len(args) < 2 { + fmsg.Fatal("app requires at least 1 argument") + } + + config := new(app.Config) + if f, err := os.Open(args[1]); err != nil { + fmsg.Fatalf("cannot access config file %q: %s", args[1], err) + panic("unreachable") + } else if err = json.NewDecoder(f).Decode(&config); err != nil { + fmsg.Fatalf("cannot parse config file %q: %s", args[1], err) + panic("unreachable") + } + + // append extra args + config.Command = append(config.Command, args[2:]...) + + // invoke app + runApp(config) + case "run": // run app in permissive defaults usage pattern + set := flag.NewFlagSet("run", flag.ExitOnError) + + var ( + dbusConfigSession string + dbusConfigSystem string + dbusID string + mpris bool + dbusVerbose bool + + userName string + enablements [system.ELen]bool + + launchMethodText string + ) + + set.StringVar(&dbusConfigSession, "dbus-config", "builtin", "Path to D-Bus proxy config file, or \"builtin\" for defaults") + set.StringVar(&dbusConfigSystem, "dbus-system", "nil", "Path to system D-Bus proxy config file, or \"nil\" to disable") + set.StringVar(&dbusID, "dbus-id", "", "D-Bus ID of application, leave empty to disable own paths, has no effect if custom config is available") + set.BoolVar(&mpris, "mpris", false, "Allow owning MPRIS D-Bus path, has no effect if custom config is available") + set.BoolVar(&dbusVerbose, "dbus-log", false, "Force logging in the D-Bus proxy") + + set.StringVar(&userName, "u", "chronos", "Passwd name of user to run as") + set.BoolVar(&enablements[system.EWayland], "wayland", false, "Share Wayland socket") + 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") + + methodHelpString := "Method of launching the child process, can be one of \"sudo\"" + if os.SdBooted() { + methodHelpString += ", \"systemd\"" + } + set.StringVar(&launchMethodText, "method", "sudo", methodHelpString) + + // Ignore errors; set is set for ExitOnError. + _ = set.Parse(args[1:]) + + // initialise config from flags + config := &app.Config{ + ID: dbusID, + User: userName, + Command: set.Args(), + Method: launchMethodText, + } + + // 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 + if enablements[system.EDBus] { + if dbusConfigSession == "builtin" { + config.Confinement.SessionBus = dbus.NewConfig(dbusID, true, mpris) + } else { + if c, err := dbus.NewConfigFromFile(dbusConfigSession); err != nil { + fmsg.Fatalf("cannot load session bus proxy config from %q: %s", dbusConfigSession, err) + } else { + config.Confinement.SessionBus = c + } + } + + // 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) + } else { + config.Confinement.SystemBus = c + } + } + + // override log from configuration + if dbusVerbose { + config.Confinement.SessionBus.Log = true + config.Confinement.SystemBus.Log = true + } + } + + // invoke app + runApp(config) + default: + fmsg.Fatalf("%q is not a valid command", args[0]) + } + + panic("unreachable") +} + +func runApp(config *app.Config) { + if os.SdBooted() { + fmsg.VPrintln("system booted with systemd as init system") + } - // invoke app a, err := app.New(os) if err != nil { fmsg.Fatalf("cannot create app: %s\n", err) - } else if err = a.Seal(loadConfig()); err != nil { + } else if err = a.Seal(config); err != nil { logBaseError(err, "cannot seal app:") fmsg.Exit(1) } else if err = a.Start(); err != nil { @@ -69,4 +243,5 @@ func main() { fmsg.Println("inner wait failed:", err) } fmsg.Exit(r) + panic("unreachable") } diff --git a/state.go b/state.go deleted file mode 100644 index 3a3f9a6..0000000 --- a/state.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "text/tabwriter" - - "git.ophivana.moe/security/fortify/internal/fmsg" - "git.ophivana.moe/security/fortify/internal/state" -) - -var ( - stateActionEarly bool -) - -func init() { - flag.BoolVar(&stateActionEarly, "state", false, "print state information of active launchers") -} - -// tryState is called after app initialisation -func tryState() { - if stateActionEarly { - var w *tabwriter.Writer - state.MustPrintLauncherStateSimpleGlobal(&w, os.Paths().RunDirPath) - if w != nil { - if err := w.Flush(); err != nil { - fmsg.Println("cannot format output:", err) - } - } else { - fmt.Println("No information available") - } - - fmsg.Exit(0) - } -} diff --git a/version.go b/version.go deleted file mode 100644 index 4028b16..0000000 --- a/version.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -import ( - "flag" - "fmt" - - "git.ophivana.moe/security/fortify/internal" -) - -var ( - printVersion bool -) - -func init() { - flag.BoolVar(&printVersion, "V", false, "Print version") -} - -func tryVersion() { - if printVersion { - if v, ok := internal.Check(internal.Version); ok { - fmt.Println(v) - } else { - fmt.Println("impure") - } - os.Exit(0) - } -}