Files
sing-box-extended-mirror/cmd/sing-box/cmd_api_status.go
T
2026-08-30 17:41:45 +08:00

100 lines
2.8 KiB
Go

package main
import (
"context"
"strings"
"sync"
"time"
"github.com/sagernet/sing-box/daemon"
"github.com/sagernet/sing/common/byteformats"
F "github.com/sagernet/sing/common/format"
"github.com/spf13/cobra"
"google.golang.org/protobuf/types/known/emptypb"
)
var commandAPIStatus = &cobra.Command{
Use: "status",
Short: "Print the service status",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return runAPIStatus()
},
}
func init() {
commandAPIRoot.AddCommand(commandAPIStatus)
}
func runAPIStatus() error {
clientConn, client, err := createAPIClient()
if err != nil {
return err
}
defer clientConn.Close()
ctx, cancel := context.WithCancel(globalCtx)
defer cancel()
statusStream, err := client.SubscribeStatus(ctx, &daemon.SubscribeStatusRequest{Interval: int64(time.Second)})
if err != nil {
return err
}
var (
waitGroup sync.WaitGroup
serviceStatus *daemon.ServiceStatus
startedAt *daemon.StartedAt
)
waitGroup.Go(func() {
serviceStatusStream, statusErr := client.SubscribeServiceStatus(ctx, &emptypb.Empty{})
if statusErr != nil {
return
}
serviceStatus, _ = serviceStatusStream.Recv()
})
waitGroup.Go(func() {
startedAt, _ = client.GetStartedAt(ctx, &emptypb.Empty{})
})
status, err := statusStream.Recv()
if err != nil {
return err
}
rateStatus, err := statusStream.Recv()
if err == nil {
status = rateStatus
}
waitGroup.Wait()
var state string
if serviceStatus != nil {
state = strings.ToLower(serviceStatus.GetStatus().String())
}
var uptime string
if startedAt.GetStartedAt() > 0 {
uptime = time.Since(time.UnixMilli(startedAt.GetStartedAt())).Truncate(time.Second).String()
}
var connections string
if status.GetTrafficAvailable() {
connections = F.ToString(status.GetConnectionsIn(), " in / ", status.GetConnectionsOut(), " out")
} else {
connections = F.ToString("- in / ", status.GetConnectionsOut(), " out")
}
var uplink, downlink string
if status.GetTrafficAvailable() {
uplink = F.ToString(byteformats.FormatBytes(uint64(status.GetUplink())), "/s (", byteformats.FormatBytes(uint64(status.GetUplinkTotal())), " total)")
downlink = F.ToString(byteformats.FormatBytes(uint64(status.GetDownlink())), "/s (", byteformats.FormatBytes(uint64(status.GetDownlinkTotal())), " total)")
}
var block blockWriter
block.addLine("State", state)
block.addLine("Uptime", uptime)
block.addLine("Memory", byteformats.FormatMemoryBytes(status.GetMemory()))
block.addLine("Goroutines", F.ToString(status.GetGoroutines()))
block.addLine("Connections", connections)
block.addLine("Uplink", uplink)
block.addLine("Downlink", downlink)
if serviceStatus.GetStatus() == daemon.ServiceStatus_FATAL {
block.addLine("Error", serviceStatus.GetErrorMessage())
}
block.flush()
return nil
}