wifi_linux_nm.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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 networkManagerMonitor struct {
  10. conn *dbus.Conn
  11. callback func(adapter.WIFIState)
  12. cancel context.CancelFunc
  13. signalChan chan *dbus.Signal
  14. }
  15. func newNetworkManagerMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
  16. conn, err := dbus.ConnectSystemBus()
  17. if err != nil {
  18. return nil, err
  19. }
  20. nmObj := conn.Object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")
  21. ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
  22. defer cancel()
  23. var state uint32
  24. err = nmObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager", "State").Store(&state)
  25. if err != nil {
  26. conn.Close()
  27. return nil, err
  28. }
  29. return &networkManagerMonitor{conn: conn, callback: callback}, nil
  30. }
  31. func (m *networkManagerMonitor) ReadWIFIState() adapter.WIFIState {
  32. ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
  33. defer cancel()
  34. nmObj := m.conn.Object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")
  35. var primaryConnectionPath dbus.ObjectPath
  36. err := nmObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager", "PrimaryConnection").Store(&primaryConnectionPath)
  37. if err != nil || primaryConnectionPath == "/" {
  38. return adapter.WIFIState{}
  39. }
  40. connObj := m.conn.Object("org.freedesktop.NetworkManager", primaryConnectionPath)
  41. var devicePaths []dbus.ObjectPath
  42. err = connObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.Connection.Active", "Devices").Store(&devicePaths)
  43. if err != nil || len(devicePaths) == 0 {
  44. return adapter.WIFIState{}
  45. }
  46. for _, devicePath := range devicePaths {
  47. deviceObj := m.conn.Object("org.freedesktop.NetworkManager", devicePath)
  48. var deviceType uint32
  49. err = deviceObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.Device", "DeviceType").Store(&deviceType)
  50. if err != nil || deviceType != 2 {
  51. continue
  52. }
  53. var accessPointPath dbus.ObjectPath
  54. err = deviceObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.Device.Wireless", "ActiveAccessPoint").Store(&accessPointPath)
  55. if err != nil || accessPointPath == "/" {
  56. continue
  57. }
  58. apObj := m.conn.Object("org.freedesktop.NetworkManager", accessPointPath)
  59. var ssidBytes []byte
  60. err = apObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.AccessPoint", "Ssid").Store(&ssidBytes)
  61. if err != nil {
  62. continue
  63. }
  64. var hwAddress string
  65. err = apObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.AccessPoint", "HwAddress").Store(&hwAddress)
  66. if err != nil {
  67. continue
  68. }
  69. ssid := strings.TrimSpace(string(ssidBytes))
  70. if ssid == "" {
  71. continue
  72. }
  73. return adapter.WIFIState{
  74. SSID: ssid,
  75. BSSID: strings.ToUpper(strings.ReplaceAll(hwAddress, ":", "")),
  76. }
  77. }
  78. return adapter.WIFIState{}
  79. }
  80. func (m *networkManagerMonitor) Start() error {
  81. if m.callback == nil {
  82. return nil
  83. }
  84. ctx, cancel := context.WithCancel(context.Background())
  85. m.cancel = cancel
  86. m.signalChan = make(chan *dbus.Signal, 10)
  87. m.conn.Signal(m.signalChan)
  88. err := m.conn.AddMatchSignal(
  89. dbus.WithMatchSender("org.freedesktop.NetworkManager"),
  90. dbus.WithMatchInterface("org.freedesktop.DBus.Properties"),
  91. )
  92. if err != nil {
  93. return err
  94. }
  95. state := m.ReadWIFIState()
  96. go m.monitorSignals(ctx, m.signalChan, state)
  97. m.callback(state)
  98. return nil
  99. }
  100. func (m *networkManagerMonitor) monitorSignals(ctx context.Context, signalChan chan *dbus.Signal, lastState adapter.WIFIState) {
  101. for {
  102. select {
  103. case <-ctx.Done():
  104. return
  105. case signal, ok := <-signalChan:
  106. if !ok {
  107. return
  108. }
  109. if signal.Name == "org.freedesktop.DBus.Properties.PropertiesChanged" {
  110. state := m.ReadWIFIState()
  111. if state != lastState {
  112. lastState = state
  113. m.callback(state)
  114. }
  115. }
  116. }
  117. }
  118. }
  119. func (m *networkManagerMonitor) Close() error {
  120. if m.cancel != nil {
  121. m.cancel()
  122. }
  123. if m.signalChan != nil {
  124. m.conn.RemoveSignal(m.signalChan)
  125. close(m.signalChan)
  126. }
  127. if m.conn != nil {
  128. return m.conn.Close()
  129. }
  130. return nil
  131. }