mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-09-15 21:00:27 +00:00
125 lines
3.4 KiB
Go
125 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/sagernet/sing-box/daemon"
|
|
"github.com/sagernet/sing/common"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
|
|
"github.com/spf13/cobra"
|
|
"google.golang.org/protobuf/types/known/emptypb"
|
|
)
|
|
|
|
var commandAPIUsbipStatus = &cobra.Command{
|
|
Use: "status",
|
|
Short: "Print the shared USB devices",
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return runAPIUsbipStatus()
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
commandAPIUsbipStatus.Flags().StringVar(&commandAPIUsbipStatusFlagService, "service", "", "Restrict to one usbip-server tag")
|
|
commandAPIUsbip.AddCommand(commandAPIUsbipStatus)
|
|
}
|
|
|
|
func runAPIUsbipStatus() error {
|
|
clientConn, client, err := createAPIClient()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer clientConn.Close()
|
|
ctx, cancel := context.WithCancel(globalCtx)
|
|
defer cancel()
|
|
servers, err := fetchUsbipServers(ctx, client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if commandAPIUsbipStatusFlagService != "" {
|
|
server, findErr := resolveUsbipServer(servers, commandAPIUsbipStatusFlagService)
|
|
if findErr != nil {
|
|
return findErr
|
|
}
|
|
servers = []*daemon.USBIPServerStatus{server}
|
|
}
|
|
for index, server := range servers {
|
|
if len(servers) > 1 {
|
|
if index > 0 {
|
|
os.Stdout.WriteString("\n")
|
|
}
|
|
os.Stdout.WriteString(server.GetServerTag() + "\n")
|
|
}
|
|
table := tableWriter{
|
|
header: []string{"BUSID", "VID:PID", "PRODUCT", "STATE"},
|
|
emptyMessage: "no shared devices",
|
|
}
|
|
for _, device := range server.GetDevices() {
|
|
table.addRow(
|
|
device.GetBusId(),
|
|
usbipVendorProduct(uint16(device.GetDescriptor_().GetVendorId()), uint16(device.GetDescriptor_().GetProductId())),
|
|
device.GetDescriptor_().GetProduct(),
|
|
usbipDeviceStateString(device.GetState()),
|
|
)
|
|
}
|
|
table.flush()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SubscribeUSBIPServerStatus never completes: with no dynamic usbip-server it sends
|
|
// a single empty update and then blocks until the client cancels.
|
|
func fetchUsbipServers(ctx context.Context, client daemon.StartedServiceClient) ([]*daemon.USBIPServerStatus, error) {
|
|
stream, err := client.SubscribeUSBIPServerStatus(ctx, &emptypb.Empty{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
update, err := stream.Recv()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return update.GetServers(), nil
|
|
}
|
|
|
|
func resolveUsbipServer(servers []*daemon.USBIPServerStatus, tag string) (*daemon.USBIPServerStatus, error) {
|
|
if tag != "" {
|
|
index := slices.IndexFunc(servers, func(it *daemon.USBIPServerStatus) bool {
|
|
return it.GetServerTag() == tag
|
|
})
|
|
if index == -1 {
|
|
return nil, E.New("usbip-server not found: ", tag, usbipServerTagHint(servers))
|
|
}
|
|
return servers[index], nil
|
|
}
|
|
switch len(servers) {
|
|
case 0:
|
|
return nil, E.New("no usbip-server found")
|
|
case 1:
|
|
return servers[0], nil
|
|
default:
|
|
return nil, E.New("multiple usbip-servers found, select one with --service", usbipServerTagHint(servers))
|
|
}
|
|
}
|
|
|
|
func usbipServerTagHint(servers []*daemon.USBIPServerStatus) string {
|
|
if len(servers) == 0 {
|
|
return " (no usbip-server with dynamic provider found)"
|
|
}
|
|
return " (known tags: " + strings.Join(common.Map(servers, func(it *daemon.USBIPServerStatus) string {
|
|
return it.GetServerTag()
|
|
}), ", ") + ")"
|
|
}
|
|
|
|
func usbipVendorProduct(vendorID uint16, productID uint16) string {
|
|
return fmt.Sprintf("%04x:%04x", vendorID, productID)
|
|
}
|
|
|
|
func usbipDeviceStateString(state daemon.USBDeviceState) string {
|
|
return strings.ToLower(strings.TrimPrefix(state.String(), "USB_DEVICE_STATE_"))
|
|
}
|