Other: SetMainExecutable, ForceLauncher

This commit is contained in:
James Houlahan
2022-10-13 03:04:02 +02:00
parent a4852c1b36
commit cec44be7c3
4 changed files with 41 additions and 38 deletions

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"net/http/cookiejar" "net/http/cookiejar"
"os"
"path/filepath" "path/filepath"
"github.com/Masterminds/semver/v3" "github.com/Masterminds/semver/v3"
@ -123,10 +124,23 @@ func run(c *cli.Context) error {
// Create a new Sentry client that will be used to report crashes etc. // Create a new Sentry client that will be used to report crashes etc.
reporter := sentry.NewReporter(constants.FullAppName, constants.Version, identifier) reporter := sentry.NewReporter(constants.FullAppName, constants.Version, identifier)
// Determine the exe that should be used to restart/autostart the app.
// By default, this is the launcher, if used. Otherwise, we try to get
// the current exe, and fall back to os.Args[0] if that fails.
var exe string
if launcher := c.String(flagLauncher); launcher != "" {
exe = launcher
} else if executable, err := os.Executable(); err == nil {
exe = executable
} else {
exe = os.Args[0]
}
// Run with profiling if requested. // Run with profiling if requested.
return withProfiler(c, func() error { return withProfiler(c, func() error {
// Restart the app if requested. // Restart the app if requested.
return withRestarter(func(restarter *restarter.Restarter) error { return withRestarter(exe, func(restarter *restarter.Restarter) error {
// Handle crashes with various actions. // Handle crashes with various actions.
return withCrashHandler(restarter, reporter, func(crashHandler *crash.Handler) error { return withCrashHandler(restarter, reporter, func(crashHandler *crash.Handler) error {
// Load the locations where we store our files. // Load the locations where we store our files.
@ -140,7 +154,7 @@ func run(c *cli.Context) error {
// Load the cookies from the vault. // Load the cookies from the vault.
return withCookieJar(vault, func(cookieJar http.CookieJar) error { return withCookieJar(vault, func(cookieJar http.CookieJar) error {
// Create a new bridge instance. // Create a new bridge instance.
return withBridge(c, locations, version, identifier, reporter, vault, cookieJar, func(b *bridge.Bridge, eventCh <-chan events.Event) error { return withBridge(c, exe, locations, version, identifier, reporter, vault, cookieJar, func(b *bridge.Bridge, eventCh <-chan events.Event) error {
if insecure { if insecure {
logrus.Warn("The vault key could not be retrieved; the vault will not be encrypted") logrus.Warn("The vault key could not be retrieved; the vault will not be encrypted")
b.PushError(bridge.ErrVaultInsecure) b.PushError(bridge.ErrVaultInsecure)
@ -223,8 +237,8 @@ func withProfiler(c *cli.Context, fn func() error) error {
} }
// Restart the app if necessary. // Restart the app if necessary.
func withRestarter(fn func(*restarter.Restarter) error) error { func withRestarter(exe string, fn func(*restarter.Restarter) error) error {
restarter := restarter.New() restarter := restarter.New(exe)
defer restarter.Restart() defer restarter.Restart()
return fn(restarter) return fn(restarter)

View File

@ -3,7 +3,6 @@ package app
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"os"
"runtime" "runtime"
"github.com/Masterminds/semver/v3" "github.com/Masterminds/semver/v3"
@ -28,6 +27,7 @@ const vaultSecretName = "bridge-vault-key"
// withBridge creates creates and tears down the bridge. // withBridge creates creates and tears down the bridge.
func withBridge( func withBridge(
c *cli.Context, c *cli.Context,
exe string,
locations *locations.Locations, locations *locations.Locations,
version *semver.Version, version *semver.Version,
identifier *useragent.UserAgent, identifier *useragent.UserAgent,
@ -48,7 +48,7 @@ func withBridge(
proxyDialer := dialer.NewProxyTLSDialer(pinningDialer, constants.APIHost) proxyDialer := dialer.NewProxyTLSDialer(pinningDialer, constants.APIHost)
// Create the autostarter. // Create the autostarter.
autostarter, err := newAutostarter() autostarter, err := newAutostarter(exe)
if err != nil { if err != nil {
return fmt.Errorf("could not create autostarter: %w", err) return fmt.Errorf("could not create autostarter: %w", err)
} }
@ -95,12 +95,7 @@ func withBridge(
return fn(bridge, eventCh) return fn(bridge, eventCh)
} }
func newAutostarter() (*autostart.App, error) { func newAutostarter(exe string) (*autostart.App, error) {
exe, err := os.Executable()
if err != nil {
return nil, err
}
return &autostart.App{ return &autostart.App{
Name: constants.FullAppName, Name: constants.FullAppName,
DisplayName: constants.FullAppName, DisplayName: constants.FullAppName,

View File

@ -332,27 +332,21 @@ func (s *Service) ReportBug(ctx context.Context, report *ReportBugRequest) (*emp
return &emptypb.Empty{}, nil return &emptypb.Empty{}, nil
} }
/*
func (s *Service) ForceLauncher(ctx context.Context, launcher *wrapperspb.StringValue) (*emptypb.Empty, error) { func (s *Service) ForceLauncher(ctx context.Context, launcher *wrapperspb.StringValue) (*emptypb.Empty, error) {
s.log.WithField("launcher", launcher.Value).Debug("ForceLauncher") s.log.WithField("launcher", launcher.Value).Debug("ForceLauncher")
go func() { s.restarter.Override(launcher.Value)
defer s.panicHandler.HandlePanic()
s.restarter.ForceLauncher(launcher.Value)
}()
return &emptypb.Empty{}, nil return &emptypb.Empty{}, nil
} }
func (s *Service) SetMainExecutable(ctx context.Context, exe *wrapperspb.StringValue) (*emptypb.Empty, error) { func (s *Service) SetMainExecutable(ctx context.Context, exe *wrapperspb.StringValue) (*emptypb.Empty, error) {
s.log.WithField("executable", exe.Value).Debug("SetMainExecutable") s.log.WithField("executable", exe.Value).Debug("SetMainExecutable")
go func() { s.restarter.AddFlags("--wait", exe.Value)
defer s.panicHandler.HandlePanic()
s.restarter.SetMainExecutable(exe.Value)
}()
return &emptypb.Empty{}, nil return &emptypb.Empty{}, nil
} }
*/
func (s *Service) Login(ctx context.Context, login *LoginRequest) (*emptypb.Empty, error) { func (s *Service) Login(ctx context.Context, login *LoginRequest) (*emptypb.Empty, error) {
s.log.WithField("username", login.Username).Debug("Login") s.log.WithField("username", login.Username).Debug("Login")

View File

@ -5,30 +5,22 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/bradenaw/juniper/xslices"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/sys/execabs" "golang.org/x/sys/execabs"
) )
const ( const BridgeCrashCount = "BRIDGE_CRASH_COUNT"
BridgeCrashCount = "BRIDGE_CRASH_COUNT"
BridgeLauncher = "BRIDGE_LAUNCHER"
)
type Restarter struct { type Restarter struct {
restart bool restart bool
crash bool crash bool
exe string
exe string
flags []string
} }
func New() *Restarter { func New(exe string) *Restarter {
var exe string
if osExe, err := os.Executable(); err == nil {
exe = osExe
} else {
logrus.WithError(err).Error("Failed to get executable path, the app will not be able to restart")
}
return &Restarter{exe: exe} return &Restarter{exe: exe}
} }
@ -37,6 +29,14 @@ func (restarter *Restarter) Set(restart, crash bool) {
restarter.crash = crash restarter.crash = crash
} }
func (restarter *Restarter) Override(exe string) {
restarter.exe = exe
}
func (restarter *Restarter) AddFlags(flags ...string) {
restarter.flags = append(restarter.flags, flags...)
}
func (restarter *Restarter) Restart() { func (restarter *Restarter) Restart() {
if !restarter.restart { if !restarter.restart {
return return
@ -49,12 +49,12 @@ func (restarter *Restarter) Restart() {
env := getEnvMap() env := getEnvMap()
if restarter.crash { if restarter.crash {
env[BridgeCrashCount] = increment(env[BridgeLauncher]) env[BridgeCrashCount] = increment(env[BridgeCrashCount])
} else { } else {
delete(env, BridgeCrashCount) delete(env, BridgeCrashCount)
} }
cmd := execabs.Command(restarter.exe, os.Args[1:]...) cmd := execabs.Command(restarter.exe, xslices.Join(os.Args[1:], restarter.flags)...)
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout