mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-10 20:56:51 +00:00
112 lines
3.0 KiB
Go
112 lines
3.0 KiB
Go
// Copyright (c) 2020 Proton Technologies AG
|
|
//
|
|
// This file is part of ProtonMail Bridge.
|
|
//
|
|
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/ProtonMail/proton-bridge/internal/frontend"
|
|
"github.com/ProtonMail/proton-bridge/pkg/config"
|
|
"github.com/ProtonMail/proton-bridge/pkg/sentry"
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
const (
|
|
// After how many crashes app gives up starting.
|
|
maxAllowedCrashes = 10
|
|
)
|
|
|
|
var (
|
|
// How many crashes happened so far in a row.
|
|
// It will be filled from args by `filterRestartNumberFromArgs`.
|
|
// Every call of `HandlePanic` will increase this number.
|
|
// Then it will be passed as argument to the next try by `RestartApp`.
|
|
numberOfCrashes = 0 //nolint[gochecknoglobals]
|
|
)
|
|
|
|
// filterRestartNumberFromArgs removes flag with a number how many restart we already did.
|
|
// See restartApp how that number is used.
|
|
func filterRestartNumberFromArgs() {
|
|
tmp := os.Args[:0]
|
|
for i, arg := range os.Args {
|
|
if !strings.HasPrefix(arg, "--restart_") {
|
|
tmp = append(tmp, arg)
|
|
continue
|
|
}
|
|
var err error
|
|
numberOfCrashes, err = strconv.Atoi(os.Args[i][10:])
|
|
if err != nil {
|
|
numberOfCrashes = maxAllowedCrashes
|
|
}
|
|
}
|
|
os.Args = tmp
|
|
}
|
|
|
|
// DisableRestart disables restart once `RestartApp` is called.
|
|
func DisableRestart() {
|
|
numberOfCrashes = maxAllowedCrashes
|
|
}
|
|
|
|
// RestartApp starts a new instance in background.
|
|
func RestartApp() {
|
|
if numberOfCrashes >= maxAllowedCrashes {
|
|
log.Error("Too many crashes")
|
|
return
|
|
}
|
|
if exeFile, err := os.Executable(); err == nil {
|
|
arguments := append(os.Args[1:], fmt.Sprintf("--restart_%d", numberOfCrashes))
|
|
cmd := exec.Command(exeFile, arguments...) //nolint[gosec]
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Stdin = os.Stdin
|
|
if err := cmd.Start(); err != nil {
|
|
log.Error("Restart failed: ", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// PanicHandler defines HandlePanic which can be used anywhere in defer.
|
|
type PanicHandler struct {
|
|
AppName string
|
|
Config *config.Config
|
|
Err *error // Pointer to error of cli action.
|
|
}
|
|
|
|
// HandlePanic should be called in defer to ensure restart of app after error.
|
|
func (ph *PanicHandler) HandlePanic() {
|
|
sentry.SkipDuringUnwind()
|
|
|
|
r := recover()
|
|
if r == nil {
|
|
return
|
|
}
|
|
|
|
config.HandlePanic(ph.Config, fmt.Sprintf("Recover: %v", r))
|
|
frontend.HandlePanic(ph.AppName)
|
|
|
|
*ph.Err = cli.NewExitError("Panic and restart", 255)
|
|
numberOfCrashes++
|
|
log.Error("Restarting after panic")
|
|
RestartApp()
|
|
os.Exit(255)
|
|
}
|