Files
Cursor AgentandHoshinoAya a032cbf8a8 fix: 修复 modem 包的健壮性问题
- modem.go: DBus 属性的裸类型断言改为安全的 comma-ok 模式,避免 panic
- messaging.go: Subscribe 中增加 sig 的 nil 检查,防止 channel 关闭时 panic
- sms.go: 时区解析改用正则表达式,正确处理 +05:30 等半小时偏移时区
- manager.go: Modems() 中 NewModem 失败时记录警告日志

Co-authored-by: HoshinoAya <HoshinoDesu@users.noreply.github.com>
2026-03-06 06:38:52 +00:00

158 lines
3.4 KiB
Go

package modem
import (
"fmt"
"log/slog"
"github.com/godbus/dbus/v5"
)
const (
ModemManagerInterface = "org.freedesktop.ModemManager1"
ModemManagerPath = "/org/freedesktop/ModemManager1"
ObjectManagerInterface = "org.freedesktop.DBus.ObjectManager"
)
// Manager 调制解调器管理器
type Manager struct {
conn *dbus.Conn
dbusObject dbus.BusObject
}
// NewManager 创建管理器
func NewManager() (*Manager, error) {
conn, err := dbus.SystemBus()
if err != nil {
return nil, fmt.Errorf("连接系统总线: %w", err)
}
obj := conn.Object(ModemManagerInterface, ModemManagerPath)
return &Manager{
conn: conn,
dbusObject: obj,
}, nil
}
// Modems 获取所有调制解调器
func (m *Manager) Modems() (map[dbus.ObjectPath]*Modem, error) {
// 使用 ObjectManager 接口获取所有管理的对象
managedObjects := make(map[dbus.ObjectPath]map[string]map[string]dbus.Variant)
err := m.dbusObject.Call(ObjectManagerInterface+".GetManagedObjects", 0).Store(&managedObjects)
if err != nil {
return nil, fmt.Errorf("获取调制解调器列表: %w", err)
}
modems := make(map[dbus.ObjectPath]*Modem)
for objectPath, interfaces := range managedObjects {
// 检查对象是否包含 Modem 接口
if _, hasModem := interfaces[ModemInterface]; !hasModem {
continue
}
modem, err := NewModem(m.conn, objectPath)
if err != nil {
slog.Warn("初始化调制解调器失败", "path", objectPath, "error", err)
continue
}
modems[objectPath] = modem
}
return modems, nil
}
// Subscribe 订阅调制解调器事件
func (m *Manager) Subscribe(handler func(event ModemEvent) error) (func(), error) {
if err := m.conn.AddMatchSignal(
dbus.WithMatchInterface(ObjectManagerInterface),
dbus.WithMatchMember("InterfacesAdded"),
dbus.WithMatchPathNamespace(ModemManagerPath),
); err != nil {
return nil, err
}
if err := m.conn.AddMatchSignal(
dbus.WithMatchInterface(ObjectManagerInterface),
dbus.WithMatchMember("InterfacesRemoved"),
dbus.WithMatchPathNamespace(ModemManagerPath),
); err != nil {
return nil, err
}
signalChan := make(chan *dbus.Signal, 10)
m.conn.Signal(signalChan)
done := make(chan struct{})
go func() {
for {
select {
case <-done:
return
case sig := <-signalChan:
if sig == nil || len(sig.Body) == 0 {
continue
}
path, ok := sig.Body[0].(dbus.ObjectPath)
if !ok {
continue
}
var event ModemEvent
event.Path = path
switch sig.Name {
case ObjectManagerInterface + ".InterfacesAdded":
event.Type = ModemEventAdded
modem, err := NewModem(m.conn, event.Path)
if err == nil {
event.Modem = modem
}
case ObjectManagerInterface + ".InterfacesRemoved":
event.Type = ModemEventRemoved
default:
continue
}
if err := handler(event); err != nil {
slog.Debug("处理调制解调器事件失败", "error", err)
}
}
}
}()
unsubscribe := func() {
close(done)
m.conn.RemoveSignal(signalChan)
}
return unsubscribe, nil
}
// Conn 返回 D-Bus 连接
func (m *Manager) Conn() *dbus.Conn {
return m.conn
}
// Close 关闭 D-Bus 连接
func (m *Manager) Close() error {
if m.conn != nil {
return m.conn.Close()
}
return nil
}
// ModemEventType 调制解调器事件类型
type ModemEventType int
const (
ModemEventAdded ModemEventType = iota
ModemEventRemoved
)
// ModemEvent 调制解调器事件
type ModemEvent struct {
Type ModemEventType
Path dbus.ObjectPath
Modem *Modem
}