loop/cmd/loop/stop.go
2026-05-20 03:12:03 -05:00

87 lines
1.9 KiB
Go

package main
import (
"context"
"fmt"
"github.com/lightninglabs/loop/looprpc"
"github.com/urfave/cli/v3"
"google.golang.org/grpc/connectivity"
)
var stopCommand = &cli.Command{
Name: "stop",
Usage: "stop the loop daemon",
Description: "Requests loopd to perform a graceful shutdown.",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "wait",
Usage: "wait until loopd fully shuts down",
},
},
Action: stopDaemon,
}
// stopDaemon requests the daemon to shut down gracefully and optionally waits
// for the gRPC connection to terminate.
func stopDaemon(ctx context.Context, cmd *cli.Command) error {
waitForShutdown := cmd.Bool("wait")
// Establish a client connection to loopd.
client, conn, cleanup, err := getClientWithConn(cmd)
if err != nil {
return err
}
defer cleanup()
// Request loopd to shut down.
_, err = client.StopDaemon(ctx, &looprpc.StopDaemonRequest{})
if err != nil {
return err
}
fmt.Println("Shutting down loopd")
// Optionally wait for the gRPC connection to terminate.
if !waitForShutdown {
return nil
}
fmt.Println("Waiting for loopd to exit...")
err = waitForDaemonShutdown(ctx, conn)
if err != nil {
return err
}
fmt.Println("Loopd shut down")
return nil
}
// waitForDaemonShutdown monitors the gRPC connectivity state until the daemon
// disappears. To avoid getting stuck in idle mode we nudge the connection to
// reconnect when needed.
func waitForDaemonShutdown(ctx context.Context, conn daemonConn) error {
for {
state := conn.GetState()
switch state {
case connectivity.Shutdown:
return nil
// Connection attempts now fail which means loopd is offline.
case connectivity.TransientFailure:
return nil
// Force the channel out of Idle so we'll see failure states
// once loopd stops serving.
case connectivity.Idle:
conn.Connect()
}
if !conn.WaitForStateChange(ctx, state) {
return ctx.Err()
}
}
}