app: migrate to new shim implementation
Both machinectl and sudo launch methods launch shim as shim is now responsible for setting up the sandbox. Various app structures are adapted to accommodate bwrap configuration and mediated wayland access. Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
This commit is contained in:
parent
b86fa6b4c9
commit
6220f7e197
|
@ -1,6 +1,7 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
@ -18,6 +19,8 @@ type app struct {
|
||||||
seal *appSeal
|
seal *appSeal
|
||||||
// underlying fortified child process
|
// underlying fortified child process
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
|
// wayland connection if wayland mediation is enabled
|
||||||
|
wayland *net.UnixConn
|
||||||
// error returned waiting for process
|
// error returned waiting for process
|
||||||
wait error
|
wait error
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.ophivana.moe/cat/fortify/dbus"
|
"git.ophivana.moe/cat/fortify/dbus"
|
||||||
|
"git.ophivana.moe/cat/fortify/helper/bwrap"
|
||||||
"git.ophivana.moe/cat/fortify/internal/state"
|
"git.ophivana.moe/cat/fortify/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,6 +23,11 @@ type Config struct {
|
||||||
|
|
||||||
// ConfinementConfig defines fortified child's confinement
|
// ConfinementConfig defines fortified child's confinement
|
||||||
type ConfinementConfig struct {
|
type ConfinementConfig struct {
|
||||||
|
// bwrap sandbox confinement configuration
|
||||||
|
Sandbox *bwrap.Config `json:"sandbox"`
|
||||||
|
// mediated access to wayland socket
|
||||||
|
Wayland bool `json:"wayland"`
|
||||||
|
|
||||||
// reference to a system D-Bus proxy configuration,
|
// reference to a system D-Bus proxy configuration,
|
||||||
// nil value disables system bus proxy
|
// nil value disables system bus proxy
|
||||||
SystemBus *dbus.Config `json:"system_bus,omitempty"`
|
SystemBus *dbus.Config `json:"system_bus,omitempty"`
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"git.ophivana.moe/cat/fortify/internal/verbose"
|
"git.ophivana.moe/cat/fortify/internal/verbose"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *app) commandBuilderMachineCtl() (args []string) {
|
func (a *app) commandBuilderMachineCtl(shimEnv string) (args []string) {
|
||||||
args = make([]string, 0, 9+len(a.seal.env))
|
args = make([]string, 0, 9+len(a.seal.env))
|
||||||
|
|
||||||
// shell --uid=$USER
|
// shell --uid=$USER
|
||||||
|
@ -25,7 +25,7 @@ func (a *app) commandBuilderMachineCtl() (args []string) {
|
||||||
envQ[i] = "-E" + e
|
envQ[i] = "-E" + e
|
||||||
}
|
}
|
||||||
// add shim payload to environment for shim path
|
// add shim payload to environment for shim path
|
||||||
envQ[len(a.seal.env)] = "-E" + a.shimPayloadEnv()
|
envQ[len(a.seal.env)] = "-E" + shimEnv
|
||||||
args = append(args, envQ...)
|
args = append(args, envQ...)
|
||||||
|
|
||||||
// -- .host
|
// -- .host
|
||||||
|
|
|
@ -10,8 +10,8 @@ const (
|
||||||
sudoAskPass = "SUDO_ASKPASS"
|
sudoAskPass = "SUDO_ASKPASS"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *app) commandBuilderSudo() (args []string) {
|
func (a *app) commandBuilderSudo(shimEnv string) (args []string) {
|
||||||
args = make([]string, 0, 4+len(a.seal.env)+len(a.seal.command))
|
args = make([]string, 0, 8)
|
||||||
|
|
||||||
// -Hiu $USER
|
// -Hiu $USER
|
||||||
args = append(args, "-Hiu", a.seal.sys.Username)
|
args = append(args, "-Hiu", a.seal.sys.Username)
|
||||||
|
@ -22,12 +22,11 @@ func (a *app) commandBuilderSudo() (args []string) {
|
||||||
args = append(args, "-A")
|
args = append(args, "-A")
|
||||||
}
|
}
|
||||||
|
|
||||||
// environ
|
// shim payload
|
||||||
args = append(args, a.seal.env...)
|
args = append(args, shimEnv)
|
||||||
|
|
||||||
// -- $@
|
// -- $@
|
||||||
args = append(args, "--")
|
args = append(args, "--", a.seal.sys.executable, "-V", "--license") // magic for shim.Try()
|
||||||
args = append(args, a.seal.command...)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,12 @@ func (a *app) Seal(config *Config) error {
|
||||||
// pass through config values
|
// pass through config values
|
||||||
seal.fid = config.ID
|
seal.fid = config.ID
|
||||||
seal.command = config.Command
|
seal.command = config.Command
|
||||||
|
seal.bwrap = config.Confinement.Sandbox
|
||||||
|
|
||||||
|
// create wayland client wait channel
|
||||||
|
if config.Confinement.Wayland {
|
||||||
|
seal.wlDone = make(chan struct{})
|
||||||
|
}
|
||||||
|
|
||||||
// parses launch method text and looks up tool path
|
// parses launch method text and looks up tool path
|
||||||
switch config.Method {
|
switch config.Method {
|
||||||
|
|
|
@ -34,7 +34,7 @@ func (seal *appSeal) shareDisplay() error {
|
||||||
if seal.et.Has(state.EnableWayland) {
|
if seal.et.Has(state.EnableWayland) {
|
||||||
if wd, ok := os.LookupEnv(waylandDisplay); !ok {
|
if wd, ok := os.LookupEnv(waylandDisplay); !ok {
|
||||||
return (*ErrDisplayEnv)(wrapError(ErrWayland, "WAYLAND_DISPLAY is not set"))
|
return (*ErrDisplayEnv)(wrapError(ErrWayland, "WAYLAND_DISPLAY is not set"))
|
||||||
} else {
|
} else if seal.wlDone == nil {
|
||||||
// hardlink wayland socket
|
// hardlink wayland socket
|
||||||
wp := path.Join(seal.RuntimePath, wd)
|
wp := path.Join(seal.RuntimePath, wd)
|
||||||
wpi := path.Join(seal.shareLocal, "wayland")
|
wpi := path.Join(seal.shareLocal, "wayland")
|
||||||
|
@ -43,6 +43,9 @@ func (seal *appSeal) shareDisplay() error {
|
||||||
|
|
||||||
// ensure Wayland socket ACL (e.g. `/run/user/%d/wayland-%d`)
|
// ensure Wayland socket ACL (e.g. `/run/user/%d/wayland-%d`)
|
||||||
seal.sys.updatePermTag(state.EnableWayland, wp, acl.Read, acl.Write, acl.Execute)
|
seal.sys.updatePermTag(state.EnableWayland, wp, acl.Read, acl.Write, acl.Execute)
|
||||||
|
} else {
|
||||||
|
// set wayland socket path (e.g. `/run/user/%d/wayland-%d`)
|
||||||
|
seal.wl = path.Join(seal.RuntimePath, wd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"git.ophivana.moe/cat/fortify/acl"
|
"git.ophivana.moe/cat/fortify/acl"
|
||||||
|
@ -11,10 +12,17 @@ const (
|
||||||
xdgRuntimeDir = "XDG_RUNTIME_DIR"
|
xdgRuntimeDir = "XDG_RUNTIME_DIR"
|
||||||
xdgSessionClass = "XDG_SESSION_CLASS"
|
xdgSessionClass = "XDG_SESSION_CLASS"
|
||||||
xdgSessionType = "XDG_SESSION_TYPE"
|
xdgSessionType = "XDG_SESSION_TYPE"
|
||||||
|
|
||||||
|
shell = "SHELL"
|
||||||
)
|
)
|
||||||
|
|
||||||
// shareRuntime queues actions for sharing/ensuring the runtime and share directories
|
// shareRuntime queues actions for sharing/ensuring the runtime and share directories
|
||||||
func (seal *appSeal) shareRuntime() {
|
func (seal *appSeal) shareRuntime() {
|
||||||
|
// look up shell
|
||||||
|
if s, ok := os.LookupEnv(shell); ok {
|
||||||
|
seal.appendEnv(shell, s)
|
||||||
|
}
|
||||||
|
|
||||||
// ensure RunDir (e.g. `/run/user/%d/fortify`)
|
// ensure RunDir (e.g. `/run/user/%d/fortify`)
|
||||||
seal.sys.ensure(seal.RunDirPath, 0700)
|
seal.sys.ensure(seal.RunDirPath, 0700)
|
||||||
seal.sys.updatePermTag(state.EnableLength, seal.RunDirPath, acl.Execute)
|
seal.sys.updatePermTag(state.EnableLength, seal.RunDirPath, acl.Execute)
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/gob"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
const shimPayload = "FORTIFY_SHIM_PAYLOAD"
|
|
||||||
|
|
||||||
func (a *app) shimPayloadEnv() string {
|
|
||||||
r := &bytes.Buffer{}
|
|
||||||
enc := base64.NewEncoder(base64.StdEncoding, r)
|
|
||||||
|
|
||||||
if err := gob.NewEncoder(enc).Encode(a.seal.command); err != nil {
|
|
||||||
// should be unreachable
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = enc.Close()
|
|
||||||
return shimPayload + "=" + r.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TryShim attempts the early hidden launcher shim path
|
|
||||||
func TryShim() {
|
|
||||||
// environment variable contains encoded argv
|
|
||||||
if r, ok := os.LookupEnv(shimPayload); ok {
|
|
||||||
// everything beyond this point runs as target user
|
|
||||||
// proceed with caution!
|
|
||||||
|
|
||||||
// parse base64 revealing underlying gob stream
|
|
||||||
dec := base64.NewDecoder(base64.StdEncoding, strings.NewReader(r))
|
|
||||||
|
|
||||||
// decode argv gob stream
|
|
||||||
var argv []string
|
|
||||||
if err := gob.NewDecoder(dec).Decode(&argv); err != nil {
|
|
||||||
fmt.Println("fortify-shim: cannot decode shim payload:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove payload variable since the child does not need to see it
|
|
||||||
if err := os.Unsetenv(shimPayload); err != nil {
|
|
||||||
fmt.Println("fortify-shim: cannot unset shim payload:", err)
|
|
||||||
// not fatal, do not fail
|
|
||||||
}
|
|
||||||
|
|
||||||
// look up argv0
|
|
||||||
var argv0 string
|
|
||||||
|
|
||||||
if len(argv) > 0 {
|
|
||||||
// look up program from $PATH
|
|
||||||
if p, err := exec.LookPath(argv[0]); err != nil {
|
|
||||||
fmt.Printf("%s not found: %s\n", argv[0], err)
|
|
||||||
os.Exit(1)
|
|
||||||
} else {
|
|
||||||
argv0 = p
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// no argv, look up shell instead
|
|
||||||
if argv0, ok = os.LookupEnv("SHELL"); !ok {
|
|
||||||
fmt.Println("fortify-shim: no command was specified and $SHELL was unset")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
argv = []string{argv0}
|
|
||||||
}
|
|
||||||
|
|
||||||
// exec target process
|
|
||||||
if err := syscall.Exec(argv0, argv, os.Environ()); err != nil {
|
|
||||||
fmt.Println("fortify-shim: cannot execute shim payload:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// unreachable
|
|
||||||
os.Exit(1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,11 +2,16 @@ package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.ophivana.moe/cat/fortify/helper"
|
||||||
|
"git.ophivana.moe/cat/fortify/internal/shim"
|
||||||
"git.ophivana.moe/cat/fortify/internal/state"
|
"git.ophivana.moe/cat/fortify/internal/state"
|
||||||
"git.ophivana.moe/cat/fortify/internal/verbose"
|
"git.ophivana.moe/cat/fortify/internal/verbose"
|
||||||
)
|
)
|
||||||
|
@ -14,6 +19,8 @@ import (
|
||||||
type (
|
type (
|
||||||
// ProcessError encapsulates errors returned by starting *exec.Cmd
|
// ProcessError encapsulates errors returned by starting *exec.Cmd
|
||||||
ProcessError BaseError
|
ProcessError BaseError
|
||||||
|
// ShimError encapsulates errors returned by shim.ServeConfig.
|
||||||
|
ShimError BaseError
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start starts the fortified child
|
// Start starts the fortified child
|
||||||
|
@ -21,12 +28,30 @@ func (a *app) Start() error {
|
||||||
a.lock.Lock()
|
a.lock.Lock()
|
||||||
defer a.lock.Unlock()
|
defer a.lock.Unlock()
|
||||||
|
|
||||||
|
// resolve exec paths
|
||||||
|
e := [2]string{helper.BubblewrapName}
|
||||||
|
if len(a.seal.command) > 0 {
|
||||||
|
e[1] = a.seal.command[0]
|
||||||
|
}
|
||||||
|
for i, n := range e {
|
||||||
|
if len(n) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if filepath.Base(n) == n {
|
||||||
|
if s, err := exec.LookPath(n); err == nil {
|
||||||
|
e[i] = s
|
||||||
|
} else {
|
||||||
|
return (*ProcessError)(wrapError(err, fmt.Sprintf("cannot find %q in PATH: %v", n, err)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := a.seal.sys.commit(); err != nil {
|
if err := a.seal.sys.commit(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// select command builder
|
// select command builder
|
||||||
var commandBuilder func() (args []string)
|
var commandBuilder func(shimEnv string) (args []string)
|
||||||
switch a.seal.launchOption {
|
switch a.seal.launchOption {
|
||||||
case LaunchMethodSudo:
|
case LaunchMethodSudo:
|
||||||
commandBuilder = a.commandBuilderSudo
|
commandBuilder = a.commandBuilderSudo
|
||||||
|
@ -37,15 +62,30 @@ func (a *app) Start() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure child process
|
// configure child process
|
||||||
a.cmd = exec.Command(a.seal.toolPath, commandBuilder()...)
|
confSockPath := path.Join(a.seal.share, "shim")
|
||||||
|
a.cmd = exec.Command(a.seal.toolPath, commandBuilder(shim.EnvShim+"="+confSockPath)...)
|
||||||
a.cmd.Env = []string{}
|
a.cmd.Env = []string{}
|
||||||
a.cmd.Stdin = os.Stdin
|
a.cmd.Stdin = os.Stdin
|
||||||
a.cmd.Stdout = os.Stdout
|
a.cmd.Stdout = os.Stdout
|
||||||
a.cmd.Stderr = os.Stderr
|
a.cmd.Stderr = os.Stderr
|
||||||
a.cmd.Dir = a.seal.RunDirPath
|
a.cmd.Dir = a.seal.RunDirPath
|
||||||
|
|
||||||
// start child process
|
if wls, err := shim.ServeConfig(confSockPath, &shim.Payload{
|
||||||
verbose.Println("starting main process:", a.cmd)
|
Argv: a.seal.command,
|
||||||
|
Env: a.seal.env,
|
||||||
|
Exec: e,
|
||||||
|
Bwrap: a.seal.bwrap,
|
||||||
|
WL: a.seal.wlDone != nil,
|
||||||
|
|
||||||
|
Verbose: verbose.Get(),
|
||||||
|
}, a.seal.wl, a.seal.wlDone); err != nil {
|
||||||
|
return (*ShimError)(wrapError(err, "cannot listen on shim socket:", err))
|
||||||
|
} else {
|
||||||
|
a.wayland = wls
|
||||||
|
}
|
||||||
|
|
||||||
|
// start shim
|
||||||
|
verbose.Println("starting shim as target user:", a.cmd)
|
||||||
if err := a.cmd.Start(); err != nil {
|
if err := a.cmd.Start(); err != nil {
|
||||||
return (*ProcessError)(wrapError(err, "cannot start process:", err))
|
return (*ProcessError)(wrapError(err, "cannot start process:", err))
|
||||||
}
|
}
|
||||||
|
@ -62,11 +102,11 @@ func (a *app) Start() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// register process state
|
// register process state
|
||||||
var e = new(StateStoreError)
|
var err = new(StateStoreError)
|
||||||
e.Inner, e.DoErr = a.seal.store.Do(func(b state.Backend) {
|
err.Inner, err.DoErr = a.seal.store.Do(func(b state.Backend) {
|
||||||
e.InnerErr = b.Save(&sd)
|
err.InnerErr = b.Save(&sd)
|
||||||
})
|
})
|
||||||
return e.equiv("cannot save process state:", e)
|
return err.equiv("cannot save process state:", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateStoreError is returned for a failed state save
|
// StateStoreError is returned for a failed state save
|
||||||
|
@ -146,6 +186,14 @@ func (a *app) Wait() (int, error) {
|
||||||
|
|
||||||
verbose.Println("process", strconv.Itoa(a.cmd.Process.Pid), "exited with exit code", r)
|
verbose.Println("process", strconv.Itoa(a.cmd.Process.Pid), "exited with exit code", r)
|
||||||
|
|
||||||
|
// close wayland connection
|
||||||
|
if a.wayland != nil {
|
||||||
|
close(a.seal.wlDone)
|
||||||
|
if err := a.wayland.Close(); err != nil {
|
||||||
|
fmt.Println("fortify: cannot close wayland connection:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// update store and revert app setup transaction
|
// update store and revert app setup transaction
|
||||||
e := new(StateStoreError)
|
e := new(StateStoreError)
|
||||||
e.Inner, e.DoErr = a.seal.store.Do(func(b state.Backend) {
|
e.Inner, e.DoErr = a.seal.store.Do(func(b state.Backend) {
|
||||||
|
@ -187,8 +235,10 @@ func (a *app) Wait() (int, error) {
|
||||||
ct = append(ct, i)
|
ct = append(ct, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(ct) > 0 {
|
||||||
verbose.Println("will revert operations tagged", ct, "as no remaining launchers hold these enablements")
|
verbose.Println("will revert operations tagged", ct, "as no remaining launchers hold these enablements")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := a.seal.sys.revert(tags); err != nil {
|
if err := a.seal.sys.revert(tags); err != nil {
|
||||||
return err.(RevertCompoundError)
|
return err.(RevertCompoundError)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"git.ophivana.moe/cat/fortify/acl"
|
"git.ophivana.moe/cat/fortify/acl"
|
||||||
"git.ophivana.moe/cat/fortify/dbus"
|
"git.ophivana.moe/cat/fortify/dbus"
|
||||||
|
"git.ophivana.moe/cat/fortify/helper/bwrap"
|
||||||
"git.ophivana.moe/cat/fortify/internal"
|
"git.ophivana.moe/cat/fortify/internal"
|
||||||
"git.ophivana.moe/cat/fortify/internal/state"
|
"git.ophivana.moe/cat/fortify/internal/state"
|
||||||
"git.ophivana.moe/cat/fortify/internal/verbose"
|
"git.ophivana.moe/cat/fortify/internal/verbose"
|
||||||
|
@ -19,6 +20,12 @@ import (
|
||||||
type appSeal struct {
|
type appSeal struct {
|
||||||
// application unique identifier
|
// application unique identifier
|
||||||
id *appID
|
id *appID
|
||||||
|
// bwrap config
|
||||||
|
bwrap *bwrap.Config
|
||||||
|
// wayland socket path if mediated wayland is enabled
|
||||||
|
wl string
|
||||||
|
// wait for wayland client to exit if mediated wayland is enabled
|
||||||
|
wlDone chan struct{}
|
||||||
|
|
||||||
// freedesktop application ID
|
// freedesktop application ID
|
||||||
fid string
|
fid string
|
||||||
|
@ -187,7 +194,7 @@ func (tx *appSealTx) commit() error {
|
||||||
}
|
}
|
||||||
tx.complete = true
|
tx.complete = true
|
||||||
|
|
||||||
txp := &appSealTx{}
|
txp := &appSealTx{User: tx.User}
|
||||||
defer func() {
|
defer func() {
|
||||||
// rollback partial commit
|
// rollback partial commit
|
||||||
if txp != nil {
|
if txp != nil {
|
||||||
|
@ -371,6 +378,8 @@ func (seal *appSeal) shareAll(bus [2]*dbus.Config) error {
|
||||||
seal.shared = true
|
seal.shared = true
|
||||||
|
|
||||||
seal.shareRuntime()
|
seal.shareRuntime()
|
||||||
|
targetRuntime := seal.shareRuntimeChild()
|
||||||
|
verbose.Printf("child runtime data dir '%s' configured\n", targetRuntime)
|
||||||
if err := seal.shareDisplay(); err != nil {
|
if err := seal.shareDisplay(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -393,11 +402,5 @@ func (seal *appSeal) shareAll(bus [2]*dbus.Config) error {
|
||||||
verbose.Println("message bus proxy final args:", seal.sys.dbus)
|
verbose.Println("message bus proxy final args:", seal.sys.dbus)
|
||||||
}
|
}
|
||||||
|
|
||||||
// workaround for launch method sudo
|
|
||||||
if seal.launchOption == LaunchMethodSudo {
|
|
||||||
targetRuntime := seal.shareRuntimeChild()
|
|
||||||
verbose.Printf("child runtime data dir '%s' configured\n", targetRuntime)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
3
main.go
3
main.go
|
@ -10,6 +10,7 @@ import (
|
||||||
"git.ophivana.moe/cat/fortify/dbus"
|
"git.ophivana.moe/cat/fortify/dbus"
|
||||||
"git.ophivana.moe/cat/fortify/internal"
|
"git.ophivana.moe/cat/fortify/internal"
|
||||||
"git.ophivana.moe/cat/fortify/internal/app"
|
"git.ophivana.moe/cat/fortify/internal/app"
|
||||||
|
"git.ophivana.moe/cat/fortify/internal/shim"
|
||||||
"git.ophivana.moe/cat/fortify/internal/state"
|
"git.ophivana.moe/cat/fortify/internal/state"
|
||||||
"git.ophivana.moe/cat/fortify/internal/verbose"
|
"git.ophivana.moe/cat/fortify/internal/verbose"
|
||||||
)
|
)
|
||||||
|
@ -35,7 +36,7 @@ func main() {
|
||||||
|
|
||||||
// launcher payload early exit
|
// launcher payload early exit
|
||||||
if printVersion && printLicense {
|
if printVersion && printLicense {
|
||||||
app.TryShim()
|
shim.Try()
|
||||||
}
|
}
|
||||||
|
|
||||||
// version/license command early exit
|
// version/license command early exit
|
||||||
|
|
Loading…
Reference in New Issue