diff --git a/display.go b/display.go index 68723b59..bf802e05 100644 --- a/display.go +++ b/display.go @@ -232,6 +232,14 @@ func updateStaticContents() { // nativeInstance.UpdateLabelAndChangeVisibility("boot_screen_device_id", GetDeviceID()) } +// configureDisplayOnNativeRestart is called when the native process restarts +// it ensures the display is configured correctly after the restart +func configureDisplayOnNativeRestart() { + displayLogger.Info().Msg("native restarted, configuring display") + updateStaticContents() + requestDisplayUpdate(true, "native_restart") +} + // setDisplayBrightness sets /sys/class/backlight/backlight/brightness to alter // the backlight brightness of the JetKVM hardware's display. func setDisplayBrightness(brightness int, reason string) error { diff --git a/internal/native/grpc_server.go b/internal/native/grpc_server.go index 4e162833..f1a1e301 100644 --- a/internal/native/grpc_server.go +++ b/internal/native/grpc_server.go @@ -6,7 +6,6 @@ import ( "net" "sync" - "github.com/erikdubbelboer/gspt" "github.com/rs/zerolog" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -160,6 +159,9 @@ func (s *grpcServer) VideoLogStatus(ctx context.Context, req *pb.Empty) (*pb.Vid } func (s *grpcServer) VideoStop(ctx context.Context, req *pb.Empty) (*pb.Empty, error) { + procPrefix = "jetkvm: [native]" + setProcTitle(lastProcTitle) + if err := s.native.VideoStop(); err != nil { return nil, status.Error(codes.Internal, err.Error()) } @@ -167,6 +169,9 @@ func (s *grpcServer) VideoStop(ctx context.Context, req *pb.Empty) (*pb.Empty, e } func (s *grpcServer) VideoStart(ctx context.Context, req *pb.Empty) (*pb.Empty, error) { + procPrefix = "jetkvm: [native+video]" + setProcTitle(lastProcTitle) + if err := s.native.VideoStart(); err != nil { return nil, status.Error(codes.Internal, err.Error()) } @@ -315,7 +320,8 @@ func (s *grpcServer) DoNotUseThisIsForCrashTestingOnly(ctx context.Context, req // StreamEvents streams events from the native process func (s *grpcServer) StreamEvents(req *pb.Empty, stream pb.NativeService_StreamEventsServer) error { - gspt.SetProcTitle("jetkvm: [native] connected") + setProcTitle("connected") + defer setProcTitle("waiting") eventCh := make(chan *pb.Event, 100) diff --git a/internal/native/native.go b/internal/native/native.go index 5d126f32..131283a4 100644 --- a/internal/native/native.go +++ b/internal/native/native.go @@ -37,6 +37,7 @@ type NativeOptions struct { OnVideoFrameReceived func(frame []byte, duration time.Duration) OnIndevEvent func(event string) OnRpcEvent func(event string) + OnNativeRestart func() } func NewNative(opts NativeOptions) *Native { diff --git a/internal/native/proxy.go b/internal/native/proxy.go index 51644eb9..d4433b26 100644 --- a/internal/native/proxy.go +++ b/internal/native/proxy.go @@ -33,11 +33,13 @@ type nativeProxyOptions struct { BinaryPath string `env:"JETKVM_NATIVE_BINARY_PATH"` LoggerLevel zerolog.Level `env:"JETKVM_NATIVE_LOGGER_LEVEL"` HandshakeMessage string `env:"JETKVM_NATIVE_HANDSHAKE_MESSAGE"` + MaxRestartAttempts uint OnVideoFrameReceived func(frame []byte, duration time.Duration) OnIndevEvent func(event string) OnRpcEvent func(event string) OnVideoStateChange func(state VideoState) + OnNativeRestart func() } func randomId(binaryLength int) string { @@ -63,6 +65,7 @@ func (n *NativeOptions) toProxyOptions() *nativeProxyOptions { OnIndevEvent: n.OnIndevEvent, OnRpcEvent: n.OnRpcEvent, OnVideoStateChange: n.OnVideoStateChange, + OnNativeRestart: n.OnNativeRestart, HandshakeMessage: handshakeMessage, } } @@ -118,6 +121,7 @@ type NativeProxy struct { ready chan struct{} options *nativeProxyOptions restartM sync.Mutex + restarts uint stopped bool processWait chan error } @@ -142,10 +146,7 @@ func NewNativeProxy(opts NativeOptions) (*NativeProxy, error) { ready: make(chan struct{}), options: proxyOptions, processWait: make(chan error, 1), - } - proxy.cmd, err = proxy.toProcessCommand() - if err != nil { - return nil, fmt.Errorf("failed to create process: %w", err) + restarts: 0, } return proxy, nil @@ -195,6 +196,7 @@ func (w *nativeProxyStdoutHandler) Write(p []byte) (n int, err error) { if !w.handshakeDone && strings.Contains(string(p), w.handshakeMessage) { w.handshakeDone = true w.handshakeCh <- true + return len(p), nil } os.Stdout.Write(p) @@ -287,6 +289,11 @@ func (p *NativeProxy) setUpGRPCClient() error { return fmt.Errorf("failed to wait for ready: %w", err) } + // Call on native restart callback if it exists and restarts are greater than 0 + if p.options.OnNativeRestart != nil && p.restarts > 0 { + p.options.OnNativeRestart() + } + // Start monitoring process for crashes go p.monitorProcess() return nil @@ -298,6 +305,13 @@ func (p *NativeProxy) start() error { runtime.LockOSThread() defer runtime.UnlockOSThread() + cmd, err := p.toProcessCommand() + if err != nil { + return fmt.Errorf("failed to create process: %w", err) + } + + p.cmd = cmd + if err := p.cmd.Start(); err != nil { return fmt.Errorf("failed to start native process: %w", err) } @@ -390,6 +404,8 @@ func (p *NativeProxy) restartProcess() error { _ = p.client.Close() } + p.restarts++ + if err := p.start(); err != nil { return fmt.Errorf("failed to start native process: %w", err) } diff --git a/internal/native/server.go b/internal/native/server.go index 3f6266c2..dd660e73 100644 --- a/internal/native/server.go +++ b/internal/native/server.go @@ -16,24 +16,37 @@ import ( // stdout - exchange messages with the parent process // stderr - logging and error messages +var ( + procPrefix string = "jetkvm: [native]" + lastProcTitle string +) + +func setProcTitle(status string) { + lastProcTitle = status + if status != "" { + status = " " + status + } + title := fmt.Sprintf("%s%s", procPrefix, status) + gspt.SetProcTitle(title) +} + // RunNativeProcess runs the native process mode func RunNativeProcess(binaryName string) { - logger := *nativeLogger - // Initialize logger - - gspt.SetProcTitle("jetkvm: [native] starting") + logger := nativeLogger.With().Int("pid", os.Getpid()).Logger() + setProcTitle("starting") + // Parse native options var proxyOptions nativeProxyOptions if err := env.Parse(&proxyOptions); err != nil { - logger.Fatal().Err(err).Msg("failed to parse native options") + logger.Fatal().Err(err).Msg("failed to parse native proxy options") } - // connect to video stream socket + // Connect to video stream socket conn, err := net.Dial("unixpacket", proxyOptions.VideoStreamUnixSocket) if err != nil { logger.Fatal().Err(err).Msg("failed to connect to video stream socket") } - logger.Info().Str("video_stream_socket_path", proxyOptions.VideoStreamUnixSocket).Msg("connected to video stream socket") + logger.Info().Str("videoStreamSocketPath", proxyOptions.VideoStreamUnixSocket).Msg("connected to video stream socket") nativeOptions := proxyOptions.toNativeOptions() nativeOptions.OnVideoFrameReceived = func(frame []byte, duration time.Duration) { @@ -52,9 +65,10 @@ func RunNativeProcess(binaryName string) { logger.Fatal().Err(err).Msg("failed to start native instance") } - gspt.SetProcTitle("jetkvm: [native] starting gRPC server") + grpcLogger := logger.With().Str("socketPath", fmt.Sprintf("@%v", proxyOptions.CtrlUnixSocket)).Logger() + setProcTitle("starting gRPC server") // Create gRPC server - grpcServer := NewGRPCServer(nativeInstance, &logger) + grpcServer := NewGRPCServer(nativeInstance, &grpcLogger) logger.Info().Msg("starting gRPC server") // Start gRPC server @@ -62,7 +76,7 @@ func RunNativeProcess(binaryName string) { if err != nil { logger.Fatal().Err(err).Msg("failed to start gRPC server") } - gspt.SetProcTitle("jetkvm: [native] ready") + setProcTitle("ready") // Signal that we're ready by writing handshake message to stdout (for parent to read) // Stdout.Write is used to avoid buffering the message @@ -76,8 +90,10 @@ func RunNativeProcess(binaryName string) { signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT) // Wait for signal - <-sigChan - logger.Info().Msg("received termination signal") + sig := <-sigChan + logger.Info(). + Str("signal", sig.String()). + Msg("received termination signal") // Graceful shutdown server.GracefulStop() diff --git a/native.go b/native.go index 77ba4a8f..0b1e3569 100644 --- a/native.go +++ b/native.go @@ -24,6 +24,9 @@ func initNative(systemVersion *semver.Version, appVersion *semver.Version) { AppVersion: appVersion, DisplayRotation: config.GetDisplayRotation(), DefaultQualityFactor: config.VideoQualityFactor, + OnNativeRestart: func() { + configureDisplayOnNativeRestart() + }, OnVideoStateChange: func(state native.VideoState) { lastVideoState = state triggerVideoStateUpdate()