mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-15 14:56:42 +00:00
GODT-2039: bridge monitors bridge-gui via its PID.
This commit is contained in:
committed by
Romain LE JEUNE
parent
f5148074fd
commit
3b9a3aaad2
@ -220,7 +220,12 @@ void launchBridge(QStringList const &args)
|
||||
else
|
||||
app().log().debug(QString("Bridge executable path: %1").arg(QDir::toNativeSeparators(bridgeExePath)));
|
||||
|
||||
overseer = std::make_unique<Overseer>(new ProcessMonitor(bridgeExePath, QStringList("--grpc") + args, nullptr), nullptr);
|
||||
|
||||
|
||||
qint64 const pid = qApp->applicationPid();
|
||||
QStringList const params = QStringList { "--grpc", "--parent-pid", QString::number(pid) } + args ;
|
||||
app().log().info(QString("Launching bridge process with command \"%1\" %2").arg(bridgeExePath, params.join(" ")));
|
||||
overseer = std::make_unique<Overseer>(new ProcessMonitor(bridgeExePath, params , nullptr), nullptr);
|
||||
overseer->startWorker(true);
|
||||
}
|
||||
|
||||
|
||||
@ -56,6 +56,7 @@ func New(
|
||||
updater types.Updater,
|
||||
restarter types.Restarter,
|
||||
locations *locations.Locations,
|
||||
parentPID int,
|
||||
) Frontend {
|
||||
switch frontendType {
|
||||
case GRPC:
|
||||
@ -66,6 +67,7 @@ func New(
|
||||
updater,
|
||||
restarter,
|
||||
locations,
|
||||
parentPID,
|
||||
)
|
||||
|
||||
case CLI:
|
||||
|
||||
@ -40,6 +40,9 @@ import (
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/keychain"
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/listener"
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
||||
"github.com/bradenaw/juniper/xslices"
|
||||
"github.com/elastic/go-sysinfo"
|
||||
sysinfotypes "github.com/elastic/go-sysinfo/types"
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -85,6 +88,8 @@ type Service struct { // nolint:structcheck
|
||||
locations *locations.Locations
|
||||
token string
|
||||
pemCert string
|
||||
parentPID int
|
||||
parentPIDDoneCh chan struct{}
|
||||
}
|
||||
|
||||
// NewService returns a new instance of the service.
|
||||
@ -95,6 +100,7 @@ func NewService(
|
||||
updater types.Updater,
|
||||
restarter types.Restarter,
|
||||
locations *locations.Locations,
|
||||
parentPID int,
|
||||
) *Service {
|
||||
s := Service{
|
||||
UnimplementedBridgeServer: UnimplementedBridgeServer{},
|
||||
@ -110,6 +116,8 @@ func NewService(
|
||||
firstTimeAutostart: sync.Once{},
|
||||
locations: locations,
|
||||
token: uuid.NewString(),
|
||||
parentPID: parentPID,
|
||||
parentPIDDoneCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
// Initializing.Done is only called sync.Once. Please keep the increment
|
||||
@ -177,6 +185,12 @@ func (s *Service) Loop(b types.Bridger) error {
|
||||
s.initAutostart()
|
||||
s.startGRPCServer()
|
||||
|
||||
if s.parentPID < 0 {
|
||||
s.log.Info("Not monitoring parent PID")
|
||||
} else {
|
||||
go s.monitorParentPID()
|
||||
}
|
||||
|
||||
defer func() {
|
||||
s.bridge.SetBool(settings.FirstStartGUIKey, false)
|
||||
}()
|
||||
@ -520,3 +534,36 @@ func (s *Service) validateStreamServerToken(
|
||||
|
||||
return handler(srv, ss)
|
||||
}
|
||||
|
||||
// monitorParentPID check at regular intervals that the parent process is still alive, and if not shuts down the server
|
||||
// and the applications.
|
||||
func (s *Service) monitorParentPID() {
|
||||
s.log.Infof("Starting to monitor parent PID %v", s.parentPID)
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if s.parentPID < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
processes, err := sysinfo.Processes() // sysinfo.Process(pid) does not seem to work on Windows.
|
||||
if err != nil {
|
||||
s.log.Debug("Could not retrieve process list")
|
||||
continue
|
||||
}
|
||||
|
||||
if !xslices.Any(processes, func(p sysinfotypes.Process) bool { return p != nil && p.PID() == s.parentPID }) {
|
||||
s.log.Info("Parent process does not exist anymore. Initiating shutdown")
|
||||
go s.quit() // quit will write to the parentPIDDoneCh, so we launch a goroutine.
|
||||
} else {
|
||||
s.log.Tracef("Parent process %v is still alive", s.parentPID)
|
||||
}
|
||||
|
||||
case <-s.parentPIDDoneCh:
|
||||
s.log.Infof("Stopping process monitoring for PID %v", s.parentPID)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,15 +95,19 @@ func (s *Service) GuiReady(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empt
|
||||
// Quit implement the Quit gRPC service call.
|
||||
func (s *Service) Quit(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
s.log.Debug("Quit")
|
||||
return &emptypb.Empty{}, s.quit()
|
||||
s.quit()
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *Service) quit() error {
|
||||
func (s *Service) quit() {
|
||||
// Windows is notably slow at Quitting. We do it in a goroutine to speed things up a bit.
|
||||
go func() {
|
||||
var err error
|
||||
if s.parentPID >= 0 {
|
||||
s.parentPIDDoneCh <- struct{}{}
|
||||
}
|
||||
|
||||
if s.isStreamingEvents() {
|
||||
if err = s.stopEventStream(); err != nil {
|
||||
if err := s.stopEventStream(); err != nil {
|
||||
s.log.WithError(err).Error("Quit failed.")
|
||||
}
|
||||
}
|
||||
@ -111,8 +115,6 @@ func (s *Service) quit() error {
|
||||
// The following call is launched as a goroutine, as it will wait for current calls to end, including this one.
|
||||
s.grpcServer.GracefulStop()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Restart implement the Restart gRPC service call.
|
||||
|
||||
@ -71,8 +71,9 @@ func (s *Service) RunEventStream(request *EventStreamRequest, server Bridge_RunE
|
||||
return err
|
||||
}
|
||||
case <-server.Context().Done():
|
||||
s.log.Debug("Client closed the stream, exiting")
|
||||
return s.quit()
|
||||
s.log.Info("Client closed the stream, initiating shutdown")
|
||||
s.quit()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user