wifi_linux_connman.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. package settings
  2. import (
  3. "context"
  4. "strings"
  5. "time"
  6. "github.com/sagernet/sing-box/adapter"
  7. "github.com/godbus/dbus/v5"
  8. )
  9. type connmanMonitor struct {
  10. conn *dbus.Conn
  11. callback func(adapter.WIFIState)
  12. cancel context.CancelFunc
  13. signalChan chan *dbus.Signal
  14. }
  15. func newConnManMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
  16. conn, err := dbus.ConnectSystemBus()
  17. if err != nil {
  18. return nil, err
  19. }
  20. cmObj := conn.Object("net.connman", "/")
  21. ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
  22. defer cancel()
  23. call := cmObj.CallWithContext(ctx, "net.connman.Manager.GetServices", 0)
  24. if call.Err != nil {
  25. conn.Close()
  26. return nil, call.Err
  27. }
  28. return &connmanMonitor{conn: conn, callback: callback}, nil
  29. }
  30. func (m *connmanMonitor) ReadWIFIState() adapter.WIFIState {
  31. ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
  32. defer cancel()
  33. cmObj := m.conn.Object("net.connman", "/")
  34. var services []interface{}
  35. err := cmObj.CallWithContext(ctx, "net.connman.Manager.GetServices", 0).Store(&services)
  36. if err != nil {
  37. return adapter.WIFIState{}
  38. }
  39. for _, service := range services {
  40. servicePair, ok := service.([]interface{})
  41. if !ok || len(servicePair) != 2 {
  42. continue
  43. }
  44. serviceProps, ok := servicePair[1].(map[string]dbus.Variant)
  45. if !ok {
  46. continue
  47. }
  48. typeVariant, hasType := serviceProps["Type"]
  49. if !hasType {
  50. continue
  51. }
  52. serviceType, ok := typeVariant.Value().(string)
  53. if !ok || serviceType != "wifi" {
  54. continue
  55. }
  56. stateVariant, hasState := serviceProps["State"]
  57. if !hasState {
  58. continue
  59. }
  60. state, ok := stateVariant.Value().(string)
  61. if !ok || (state != "online" && state != "ready") {
  62. continue
  63. }
  64. nameVariant, hasName := serviceProps["Name"]
  65. if !hasName {
  66. continue
  67. }
  68. ssid, ok := nameVariant.Value().(string)
  69. if !ok || ssid == "" {
  70. continue
  71. }
  72. bssidVariant, hasBSSID := serviceProps["BSSID"]
  73. if !hasBSSID {
  74. return adapter.WIFIState{SSID: ssid}
  75. }
  76. bssid, ok := bssidVariant.Value().(string)
  77. if !ok {
  78. return adapter.WIFIState{SSID: ssid}
  79. }
  80. return adapter.WIFIState{
  81. SSID: ssid,
  82. BSSID: strings.ToUpper(strings.ReplaceAll(bssid, ":", "")),
  83. }
  84. }
  85. return adapter.WIFIState{}
  86. }
  87. func (m *connmanMonitor) Start() error {
  88. if m.callback == nil {
  89. return nil
  90. }
  91. ctx, cancel := context.WithCancel(context.Background())
  92. m.cancel = cancel
  93. m.signalChan = make(chan *dbus.Signal, 10)
  94. m.conn.Signal(m.signalChan)
  95. err := m.conn.AddMatchSignal(
  96. dbus.WithMatchInterface("net.connman.Service"),
  97. dbus.WithMatchSender("net.connman"),
  98. )
  99. if err != nil {
  100. return err
  101. }
  102. state := m.ReadWIFIState()
  103. go m.monitorSignals(ctx, m.signalChan, state)
  104. m.callback(state)
  105. return nil
  106. }
  107. func (m *connmanMonitor) monitorSignals(ctx context.Context, signalChan chan *dbus.Signal, lastState adapter.WIFIState) {
  108. for {
  109. select {
  110. case <-ctx.Done():
  111. return
  112. case signal, ok := <-signalChan:
  113. if !ok {
  114. return
  115. }
  116. if signal.Name == "PropertyChanged" {
  117. state := m.ReadWIFIState()
  118. if state != lastState {
  119. lastState = state
  120. m.callback(state)
  121. }
  122. }
  123. }
  124. }
  125. }
  126. func (m *connmanMonitor) Close() error {
  127. if m.cancel != nil {
  128. m.cancel()
  129. }
  130. if m.signalChan != nil {
  131. m.conn.RemoveSignal(m.signalChan)
  132. close(m.signalChan)
  133. }
  134. if m.conn != nil {
  135. return m.conn.Close()
  136. }
  137. return nil
  138. }