added autostart for windows and autostart for devices
This commit is contained in:
+110
-7
@@ -4,8 +4,10 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/duffy/usb-server/internal/config"
|
||||
"github.com/duffy/usb-server/internal/protocol"
|
||||
"github.com/duffy/usb-server/internal/usbip"
|
||||
"github.com/google/uuid"
|
||||
@@ -28,6 +30,8 @@ type AttachedDevice struct {
|
||||
// UseManager handles receiving/using remote USB devices
|
||||
type UseManager struct {
|
||||
client *Client
|
||||
cfg *config.Config
|
||||
cfgPath string
|
||||
mu sync.RWMutex
|
||||
available map[string][]RemoteDevice // clientID -> devices
|
||||
attached map[string]*AttachedDevice // busID@clientID -> attached info
|
||||
@@ -44,9 +48,11 @@ type useTunnel struct {
|
||||
}
|
||||
|
||||
// NewUseManager creates a use manager
|
||||
func NewUseManager(client *Client) *UseManager {
|
||||
func NewUseManager(client *Client, cfg *config.Config, cfgPath string) *UseManager {
|
||||
um := &UseManager{
|
||||
client: client,
|
||||
cfg: cfg,
|
||||
cfgPath: cfgPath,
|
||||
available: make(map[string][]RemoteDevice),
|
||||
attached: make(map[string]*AttachedDevice),
|
||||
tunnels: make(map[string]*useTunnel),
|
||||
@@ -210,15 +216,20 @@ func (um *UseManager) setupVHCI(clientID, busID string, granted *protocol.Device
|
||||
}
|
||||
|
||||
key := busID + "@" + clientID
|
||||
remDev := RemoteDevice{
|
||||
USBDevice: protocol.USBDevice{BusID: busID},
|
||||
ClientID: clientID,
|
||||
}
|
||||
if devInfo != nil {
|
||||
remDev = *devInfo
|
||||
}
|
||||
|
||||
um.mu.Lock()
|
||||
um.tunnels[granted.TunnelID] = tunnel
|
||||
um.attached[key] = &AttachedDevice{
|
||||
RemoteDevice: RemoteDevice{
|
||||
USBDevice: protocol.USBDevice{BusID: busID},
|
||||
ClientID: clientID,
|
||||
},
|
||||
TunnelID: granted.TunnelID,
|
||||
VHCIPort: vhciPort,
|
||||
RemoteDevice: remDev,
|
||||
TunnelID: granted.TunnelID,
|
||||
VHCIPort: vhciPort,
|
||||
}
|
||||
um.mu.Unlock()
|
||||
|
||||
@@ -273,10 +284,102 @@ func (um *UseManager) handleDeviceList(msg *protocol.DeviceList) {
|
||||
})
|
||||
}
|
||||
um.available[msg.ClientID] = remoteDevs
|
||||
|
||||
// Collect devices to auto-connect (while holding the lock to check attached map)
|
||||
var toAutoConnect []RemoteDevice
|
||||
for _, dev := range remoteDevs {
|
||||
if dev.Status != protocol.StatusAvailable {
|
||||
continue
|
||||
}
|
||||
key := dev.BusID + "@" + msg.ClientID
|
||||
if _, attached := um.attached[key]; attached {
|
||||
continue
|
||||
}
|
||||
if um.matchesAutoConnect(dev) {
|
||||
toAutoConnect = append(toAutoConnect, dev)
|
||||
}
|
||||
}
|
||||
um.mu.Unlock()
|
||||
|
||||
log.Printf("[use] received device list from %s (%s): %d devices",
|
||||
msg.ClientName, msg.ClientID[:8], len(msg.Devices))
|
||||
|
||||
// Auto-connect matching devices (outside lock, each in its own goroutine)
|
||||
for _, dev := range toAutoConnect {
|
||||
log.Printf("[use] auto-connecting %s (%s:%s) from %s",
|
||||
dev.Name, dev.VendorID, dev.ProductID, msg.ClientName)
|
||||
go um.AttachDevice(msg.ClientID, dev.BusID)
|
||||
}
|
||||
}
|
||||
|
||||
// matchesAutoConnect checks if a device matches any auto-connect rule.
|
||||
// Must be called with um.mu held (at least RLock).
|
||||
func (um *UseManager) matchesAutoConnect(dev RemoteDevice) bool {
|
||||
for _, rule := range um.cfg.AutoConnect {
|
||||
if rule.VendorID != "" && !strings.EqualFold(rule.VendorID, dev.VendorID) {
|
||||
continue
|
||||
}
|
||||
if rule.ProductID != "" && !strings.EqualFold(rule.ProductID, dev.ProductID) {
|
||||
continue
|
||||
}
|
||||
if rule.BusID != "" && rule.BusID != dev.BusID {
|
||||
continue
|
||||
}
|
||||
if rule.ClientName != "" && rule.ClientName != dev.ClientName {
|
||||
continue
|
||||
}
|
||||
return true // all specified fields match
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetAutoConnect adds or removes an auto-connect rule for a VendorID:ProductID pair.
|
||||
func (um *UseManager) SetAutoConnect(vendorID, productID string, enabled bool) error {
|
||||
um.mu.Lock()
|
||||
defer um.mu.Unlock()
|
||||
|
||||
if enabled {
|
||||
// Check if rule already exists
|
||||
for _, rule := range um.cfg.AutoConnect {
|
||||
if strings.EqualFold(rule.VendorID, vendorID) && strings.EqualFold(rule.ProductID, productID) {
|
||||
return nil // already exists
|
||||
}
|
||||
}
|
||||
um.cfg.AutoConnect = append(um.cfg.AutoConnect, config.AutoConnectRule{
|
||||
VendorID: vendorID,
|
||||
ProductID: productID,
|
||||
})
|
||||
} else {
|
||||
// Remove matching rule
|
||||
filtered := um.cfg.AutoConnect[:0]
|
||||
for _, rule := range um.cfg.AutoConnect {
|
||||
if strings.EqualFold(rule.VendorID, vendorID) && strings.EqualFold(rule.ProductID, productID) {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, rule)
|
||||
}
|
||||
um.cfg.AutoConnect = filtered
|
||||
}
|
||||
|
||||
if err := um.cfg.Save(um.cfgPath); err != nil {
|
||||
return fmt.Errorf("saving config: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("[use] auto-connect %s:%s = %v", vendorID, productID, enabled)
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsAutoConnect checks if there is an auto-connect rule for this VendorID:ProductID.
|
||||
func (um *UseManager) IsAutoConnect(vendorID, productID string) bool {
|
||||
um.mu.RLock()
|
||||
defer um.mu.RUnlock()
|
||||
|
||||
for _, rule := range um.cfg.AutoConnect {
|
||||
if strings.EqualFold(rule.VendorID, vendorID) && strings.EqualFold(rule.ProductID, productID) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (um *UseManager) handleDeviceGranted(msg *protocol.DeviceGranted) {
|
||||
|
||||
Reference in New Issue
Block a user