mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-21 18:54:01 -04:00
perf(dms): cut daemon idle RAM, threads, and goroutines. Add DMS_PPROF
for performance profiling
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/notify"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -61,8 +62,27 @@ func runNotify(cmd *cobra.Command, args []string) {
|
||||
Timeout: int32(notifyTimeout),
|
||||
}
|
||||
|
||||
if err := notify.Send(n); err != nil {
|
||||
id, err := notify.Send(n)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if n.FilePath == "" {
|
||||
return
|
||||
}
|
||||
watchNotificationAction(id, n.FilePath)
|
||||
}
|
||||
|
||||
func watchNotificationAction(id uint32, path string) {
|
||||
if id == 0 {
|
||||
return
|
||||
}
|
||||
resp, ok := tryServerRequest(models.Request{
|
||||
Method: "notify.watchAction",
|
||||
Params: map[string]any{"id": id, "path": path},
|
||||
})
|
||||
if ok && resp.Error == "" {
|
||||
return
|
||||
}
|
||||
notify.SpawnActionListener(id, path)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/clipboard"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/notify"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/screenshot"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -152,7 +153,7 @@ var notifyActionCmd = &cobra.Command{
|
||||
Use: "notify-action",
|
||||
Hidden: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
screenshot.RunNotifyActionListener(args)
|
||||
notify.RunActionListener(args)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -358,13 +359,14 @@ func runScreenshot(config screenshot.Config) {
|
||||
|
||||
if config.Notify {
|
||||
thumbData, thumbW, thumbH := bufferToRGBThumbnail(result.Buffer, 256, result.Format)
|
||||
screenshot.SendNotification(screenshot.NotifyResult{
|
||||
id := screenshot.SendNotification(screenshot.NotifyResult{
|
||||
FilePath: filePath,
|
||||
Clipboard: config.Clipboard,
|
||||
ImageData: thumbData,
|
||||
Width: thumbW,
|
||||
Height: thumbH,
|
||||
})
|
||||
watchNotificationAction(id, filePath)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ func init() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
disableMemProfilingUnlessRequested()
|
||||
clipboard.MaybeServeAndExit()
|
||||
|
||||
if os.Geteuid() == 0 && !isReadOnlyCommand(os.Args) {
|
||||
|
||||
@@ -22,6 +22,7 @@ func init() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
disableMemProfilingUnlessRequested()
|
||||
clipboard.MaybeServeAndExit()
|
||||
|
||||
if os.Geteuid() == 0 && !isReadOnlyCommand(os.Args) {
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func disableMemProfilingUnlessRequested() {
|
||||
if os.Getenv("DMS_PPROF") != "" {
|
||||
return
|
||||
}
|
||||
runtime.MemProfileRate = 0
|
||||
}
|
||||
|
||||
// isReadOnlyCommand returns true if the CLI args indicate a command that is
|
||||
// safe to run as root (e.g. shell completion, help).
|
||||
func isReadOnlyCommand(args []string) bool {
|
||||
|
||||
@@ -47,7 +47,7 @@ type GeoClueClient struct {
|
||||
}
|
||||
|
||||
func newGeoClueClient() (*GeoClueClient, error) {
|
||||
dbusConn, err := dbus.ConnectSystemBus()
|
||||
dbusConn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("system bus connection failed: %w", err)
|
||||
}
|
||||
@@ -64,7 +64,6 @@ func newGeoClueClient() (*GeoClueClient, error) {
|
||||
}
|
||||
|
||||
if err := c.setupClient(); err != nil {
|
||||
dbusConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -90,10 +89,6 @@ func (c *GeoClueClient) Close() {
|
||||
c.subscribers.Delete(key)
|
||||
return true
|
||||
})
|
||||
|
||||
if c.dbusConn != nil {
|
||||
c.dbusConn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *GeoClueClient) Subscribe(id string) chan Location {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
@@ -19,6 +20,8 @@ const (
|
||||
|
||||
maxSummaryLen = 29
|
||||
maxBodyLen = 80
|
||||
|
||||
listenerMaxLifetime = time.Hour
|
||||
)
|
||||
|
||||
type Notification struct {
|
||||
@@ -30,10 +33,10 @@ type Notification struct {
|
||||
Timeout int32
|
||||
}
|
||||
|
||||
func Send(n Notification) error {
|
||||
func Send(n Notification) (uint32, error) {
|
||||
conn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
return fmt.Errorf("dbus session failed: %w", err)
|
||||
return 0, fmt.Errorf("dbus session failed: %w", err)
|
||||
}
|
||||
|
||||
if n.AppName == "" {
|
||||
@@ -82,22 +85,18 @@ func Send(n Notification) error {
|
||||
)
|
||||
|
||||
if call.Err != nil {
|
||||
return fmt.Errorf("notify call failed: %w", call.Err)
|
||||
return 0, fmt.Errorf("notify call failed: %w", call.Err)
|
||||
}
|
||||
|
||||
var notificationID uint32
|
||||
if err := call.Store(¬ificationID); err != nil {
|
||||
return fmt.Errorf("failed to get notification id: %w", err)
|
||||
return 0, fmt.Errorf("failed to get notification id: %w", err)
|
||||
}
|
||||
|
||||
if len(actions) > 0 && n.FilePath != "" {
|
||||
spawnActionListener(notificationID, n.FilePath)
|
||||
}
|
||||
|
||||
return nil
|
||||
return notificationID, nil
|
||||
}
|
||||
|
||||
func spawnActionListener(notificationID uint32, filePath string) {
|
||||
func SpawnActionListener(notificationID uint32, filePath string) {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return
|
||||
@@ -136,37 +135,45 @@ func RunActionListener(args []string) {
|
||||
|
||||
signals := make(chan *dbus.Signal, 10)
|
||||
conn.Signal(signals)
|
||||
deadline := time.After(listenerMaxLifetime)
|
||||
|
||||
for sig := range signals {
|
||||
switch sig.Name {
|
||||
case notifyInterface + ".ActionInvoked":
|
||||
if len(sig.Body) < 2 {
|
||||
continue
|
||||
}
|
||||
id, ok := sig.Body[0].(uint32)
|
||||
if !ok || id != uint32(notificationID) {
|
||||
continue
|
||||
}
|
||||
action, ok := sig.Body[1].(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
handleAction(action, filePath)
|
||||
for {
|
||||
select {
|
||||
case <-deadline:
|
||||
return
|
||||
|
||||
case notifyInterface + ".NotificationClosed":
|
||||
if len(sig.Body) < 1 {
|
||||
continue
|
||||
case sig := <-signals:
|
||||
if sig == nil || handleSignal(sig, uint32(notificationID), filePath) {
|
||||
return
|
||||
}
|
||||
id, ok := sig.Body[0].(uint32)
|
||||
if !ok || id != uint32(notificationID) {
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleSignal(sig *dbus.Signal, notificationID uint32, filePath string) bool {
|
||||
if len(sig.Body) < 1 {
|
||||
return false
|
||||
}
|
||||
id, ok := sig.Body[0].(uint32)
|
||||
if !ok || id != notificationID {
|
||||
return false
|
||||
}
|
||||
switch sig.Name {
|
||||
case notifyInterface + ".NotificationClosed":
|
||||
return true
|
||||
case notifyInterface + ".ActionInvoked":
|
||||
if len(sig.Body) < 2 {
|
||||
return false
|
||||
}
|
||||
action, ok := sig.Body[1].(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
handleAction(action, filePath)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func handleAction(action, filePath string) {
|
||||
switch action {
|
||||
case "open", "default":
|
||||
|
||||
@@ -2,11 +2,7 @@ package screenshot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
||||
"github.com/godbus/dbus/v5"
|
||||
@@ -26,11 +22,11 @@ type NotifyResult struct {
|
||||
Height int
|
||||
}
|
||||
|
||||
func SendNotification(result NotifyResult) {
|
||||
func SendNotification(result NotifyResult) uint32 {
|
||||
conn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
log.Debug("dbus session failed", "err", err)
|
||||
return
|
||||
return 0
|
||||
}
|
||||
|
||||
var actions []string
|
||||
@@ -89,93 +85,17 @@ func SendNotification(result NotifyResult) {
|
||||
|
||||
if call.Err != nil {
|
||||
log.Debug("notify call failed", "err", call.Err)
|
||||
return
|
||||
return 0
|
||||
}
|
||||
|
||||
var notificationID uint32
|
||||
if err := call.Store(¬ificationID); err != nil {
|
||||
log.Debug("failed to get notification id", "err", err)
|
||||
return
|
||||
return 0
|
||||
}
|
||||
|
||||
if len(actions) == 0 || result.FilePath == "" {
|
||||
return
|
||||
if len(actions) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
spawnActionListener(notificationID, result.FilePath)
|
||||
}
|
||||
|
||||
func spawnActionListener(notificationID uint32, filePath string) {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Debug("failed to get executable", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
cmd := exec.Command(exe, "notify-action", fmt.Sprintf("%d", notificationID), filePath)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setsid: true,
|
||||
}
|
||||
cmd.Start()
|
||||
}
|
||||
|
||||
func RunNotifyActionListener(args []string) {
|
||||
if len(args) < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
notificationID, err := strconv.ParseUint(args[0], 10, 32)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := args[1]
|
||||
|
||||
conn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := conn.AddMatchSignal(
|
||||
dbus.WithMatchObjectPath(notifyPath),
|
||||
dbus.WithMatchInterface(notifyInterface),
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
signals := make(chan *dbus.Signal, 10)
|
||||
conn.Signal(signals)
|
||||
|
||||
for sig := range signals {
|
||||
switch sig.Name {
|
||||
case notifyInterface + ".ActionInvoked":
|
||||
if len(sig.Body) < 2 {
|
||||
continue
|
||||
}
|
||||
id, ok := sig.Body[0].(uint32)
|
||||
if !ok || id != uint32(notificationID) {
|
||||
continue
|
||||
}
|
||||
openFile(filePath)
|
||||
return
|
||||
|
||||
case notifyInterface + ".NotificationClosed":
|
||||
if len(sig.Body) < 1 {
|
||||
continue
|
||||
}
|
||||
id, ok := sig.Body[0].(uint32)
|
||||
if !ok || id != uint32(notificationID) {
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openFile(filePath string) {
|
||||
cmd := exec.Command("xdg-open", filePath)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setsid: true,
|
||||
}
|
||||
cmd.Start()
|
||||
return notificationID
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ type BluezAgent struct {
|
||||
}
|
||||
|
||||
func NewBluezAgent(broker PromptBroker) (*BluezAgent, error) {
|
||||
conn, err := dbus.ConnectSystemBus()
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("system bus connection failed: %w", err)
|
||||
}
|
||||
@@ -80,18 +80,17 @@ func NewBluezAgent(broker PromptBroker) (*BluezAgent, error) {
|
||||
}
|
||||
|
||||
if err := conn.Export(agent, dbus.ObjectPath(agentPath), agent1Iface); err != nil {
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("agent export failed: %w", err)
|
||||
}
|
||||
|
||||
if err := conn.Export(agent, dbus.ObjectPath(agentPath), "org.freedesktop.DBus.Introspectable"); err != nil {
|
||||
conn.Close()
|
||||
agent.unexport()
|
||||
return nil, fmt.Errorf("introspection export failed: %w", err)
|
||||
}
|
||||
|
||||
mgr := conn.Object(bluezService, dbus.ObjectPath(agentManagerPath))
|
||||
if err := mgr.Call(agentManagerIface+".RegisterAgent", 0, dbus.ObjectPath(agentPath), agentCapability).Err; err != nil {
|
||||
conn.Close()
|
||||
agent.unexport()
|
||||
return nil, fmt.Errorf("agent registration failed: %w", err)
|
||||
}
|
||||
|
||||
@@ -109,7 +108,12 @@ func (a *BluezAgent) Close() {
|
||||
}
|
||||
mgr := a.conn.Object(bluezService, dbus.ObjectPath(agentManagerPath))
|
||||
mgr.Call(agentManagerIface+".UnregisterAgent", 0, dbus.ObjectPath(agentPath))
|
||||
a.conn.Close()
|
||||
a.unexport()
|
||||
}
|
||||
|
||||
func (a *BluezAgent) unexport() {
|
||||
_ = a.conn.Export(nil, dbus.ObjectPath(agentPath), agent1Iface)
|
||||
_ = a.conn.Export(nil, dbus.ObjectPath(agentPath), "org.freedesktop.DBus.Introspectable")
|
||||
}
|
||||
|
||||
func (a *BluezAgent) Release() *dbus.Error {
|
||||
|
||||
@@ -18,8 +18,14 @@ const (
|
||||
propertiesIface = "org.freedesktop.DBus.Properties"
|
||||
)
|
||||
|
||||
var bluezMatchRules = [][]dbus.MatchOption{
|
||||
{dbus.WithMatchInterface(propertiesIface), dbus.WithMatchMember("PropertiesChanged")},
|
||||
{dbus.WithMatchInterface(objectMgrIface), dbus.WithMatchMember("InterfacesAdded")},
|
||||
{dbus.WithMatchInterface(objectMgrIface), dbus.WithMatchMember("InterfacesRemoved")},
|
||||
}
|
||||
|
||||
func NewManager() (*Manager, error) {
|
||||
conn, err := dbus.ConnectSystemBus()
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("system bus connection failed: %w", err)
|
||||
}
|
||||
@@ -47,23 +53,19 @@ func NewManager() (*Manager, error) {
|
||||
|
||||
adapters, err := scanAdapters(conn)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("%w: %v", ErrNoAdapter, err)
|
||||
}
|
||||
if len(adapters) == 0 {
|
||||
conn.Close()
|
||||
return nil, ErrNoAdapter
|
||||
}
|
||||
m.adapterPaths = adapters
|
||||
log.Infof("[BluezManager] adapters: %v (default: %s)", adapters, adapters[0])
|
||||
|
||||
if err := m.initialize(); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := m.startAgent(); err != nil {
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("agent start failed: %w", err)
|
||||
}
|
||||
|
||||
@@ -277,25 +279,10 @@ func (m *Manager) startAgent() error {
|
||||
func (m *Manager) startSignalPump() error {
|
||||
m.dbusConn.Signal(m.signals)
|
||||
|
||||
if err := m.dbusConn.AddMatchSignal(
|
||||
dbus.WithMatchInterface(propertiesIface),
|
||||
dbus.WithMatchMember("PropertiesChanged"),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.dbusConn.AddMatchSignal(
|
||||
dbus.WithMatchInterface(objectMgrIface),
|
||||
dbus.WithMatchMember("InterfacesAdded"),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.dbusConn.AddMatchSignal(
|
||||
dbus.WithMatchInterface(objectMgrIface),
|
||||
dbus.WithMatchMember("InterfacesRemoved"),
|
||||
); err != nil {
|
||||
return err
|
||||
for _, rule := range bluezMatchRules {
|
||||
if err := m.dbusConn.AddMatchSignal(rule...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
m.sigWG.Go(func() {
|
||||
@@ -673,6 +660,9 @@ func (m *Manager) Close() {
|
||||
m.dbusConn.RemoveSignal(m.signals)
|
||||
close(m.signals)
|
||||
}
|
||||
for _, rule := range bluezMatchRules {
|
||||
_ = m.dbusConn.RemoveMatchSignal(rule...)
|
||||
}
|
||||
|
||||
if m.agent != nil {
|
||||
m.agent.Close()
|
||||
@@ -689,10 +679,6 @@ func (m *Manager) Close() {
|
||||
m.pairingSubscribers.Delete(key)
|
||||
return true
|
||||
})
|
||||
|
||||
if m.dbusConn != nil {
|
||||
m.dbusConn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func stateChanged(old, new *BluetoothState) bool {
|
||||
|
||||
@@ -10,21 +10,20 @@ import (
|
||||
var ErrNoAdapter = errors.New("no bluetooth adapter found")
|
||||
|
||||
func WaitForAdapter() error {
|
||||
conn, err := dbus.ConnectSystemBus()
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
if err := conn.AddMatchSignal(
|
||||
dbus.WithMatchInterface(objectMgrIface),
|
||||
dbus.WithMatchMember("InterfacesAdded"),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signals := make(chan *dbus.Signal, 64)
|
||||
conn.Signal(signals)
|
||||
defer conn.RemoveSignal(signals)
|
||||
|
||||
adapterAdded := []dbus.MatchOption{dbus.WithMatchInterface(objectMgrIface), dbus.WithMatchMember("InterfacesAdded")}
|
||||
if err := conn.AddMatchSignal(adapterAdded...); err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.RemoveMatchSignal(adapterAdded...)
|
||||
|
||||
if paths, err := scanAdapters(conn); err == nil && len(paths) > 0 {
|
||||
return nil
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
|
||||
type DBusConn interface {
|
||||
Object(dest string, path dbus.ObjectPath) dbus.BusObject
|
||||
Close() error
|
||||
}
|
||||
|
||||
type LogindBackend struct {
|
||||
@@ -17,7 +16,7 @@ type LogindBackend struct {
|
||||
}
|
||||
|
||||
func NewLogindBackend() (*LogindBackend, error) {
|
||||
conn, err := dbus.ConnectSystemBus()
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect to system bus: %w", err)
|
||||
}
|
||||
@@ -25,13 +24,10 @@ func NewLogindBackend() (*LogindBackend, error) {
|
||||
obj := conn.Object("org.freedesktop.login1", "/org/freedesktop/login1/session/auto")
|
||||
call := obj.Call("org.freedesktop.DBus.Peer.Ping", 0)
|
||||
if call.Err != nil {
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("logind not available: %w", call.Err)
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
|
||||
return &LogindBackend{}, nil
|
||||
return &LogindBackend{conn: conn}, nil
|
||||
}
|
||||
|
||||
func NewLogindBackendWithConn(conn DBusConn) *LogindBackend {
|
||||
@@ -42,7 +38,7 @@ func NewLogindBackendWithConn(conn DBusConn) *LogindBackend {
|
||||
|
||||
func (b *LogindBackend) SetBrightness(subsystem, name string, brightness uint32) error {
|
||||
if b.conn == nil {
|
||||
conn, err := dbus.ConnectSystemBus()
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return fmt.Errorf("connect to system bus: %w", err)
|
||||
}
|
||||
@@ -59,8 +55,4 @@ func (b *LogindBackend) SetBrightness(subsystem, name string, brightness uint32)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *LogindBackend) Close() {
|
||||
if b.conn != nil {
|
||||
b.conn.Close()
|
||||
}
|
||||
}
|
||||
func (b *LogindBackend) Close() {}
|
||||
|
||||
@@ -77,18 +77,6 @@ func TestLogindBackend_SetBrightness_LEDDevice(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogindBackend_Close(t *testing.T) {
|
||||
mockConn := mocks_brightness.NewMockDBusConn(t)
|
||||
backend := NewLogindBackendWithConn(mockConn)
|
||||
|
||||
mockConn.EXPECT().
|
||||
Close().
|
||||
Return(nil).
|
||||
Once()
|
||||
|
||||
backend.Close()
|
||||
}
|
||||
|
||||
func TestLogindBackend_Close_NilConn(t *testing.T) {
|
||||
backend := &LogindBackend{conn: nil}
|
||||
backend.Close()
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime/debug"
|
||||
"slices"
|
||||
"strings"
|
||||
"syscall"
|
||||
@@ -347,10 +348,10 @@ func (m *Manager) releaseCurrentSource() {
|
||||
source.Destroy()
|
||||
}
|
||||
|
||||
func readPipeTimeout(r *os.File) []byte {
|
||||
func readPipeTimeout(r *os.File, maxSize int64) []byte {
|
||||
done := make(chan []byte, 1)
|
||||
go func() {
|
||||
data, _ := io.ReadAll(r)
|
||||
data, _ := io.ReadAll(io.LimitReader(r, maxSize+1))
|
||||
done <- data
|
||||
}()
|
||||
|
||||
@@ -374,11 +375,11 @@ func (m *Manager) readAndStore(r *os.File, mimeType string, altR *os.File, altMi
|
||||
default:
|
||||
go func() {
|
||||
defer altR.Close()
|
||||
altCh <- readPipeTimeout(altR)
|
||||
altCh <- readPipeTimeout(altR, cfg.MaxEntrySize)
|
||||
}()
|
||||
}
|
||||
|
||||
data := readPipeTimeout(r)
|
||||
data := readPipeTimeout(r, cfg.MaxEntrySize)
|
||||
altData := <-altCh
|
||||
|
||||
if len(bytes.TrimSpace(altData)) == 0 || int64(len(altData)) > cfg.MaxEntrySize {
|
||||
@@ -398,6 +399,10 @@ func (m *Manager) readAndStore(r *os.File, mimeType string, altR *os.File, altMi
|
||||
|
||||
m.updateState()
|
||||
m.notifySubscribers()
|
||||
|
||||
if len(data)+len(altData) >= largeEntryBytes {
|
||||
debug.FreeOSMemory()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) storeClipboardEntry(data []byte, mimeType string, altData []byte, altMime string) {
|
||||
@@ -1964,12 +1969,11 @@ func (m *Manager) dbusConnForFlatpak() (*dbus.Conn, error) {
|
||||
return m.dbusConn, nil
|
||||
}
|
||||
|
||||
conn, err := dbus.ConnectSessionBus()
|
||||
conn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect session bus: %w", err)
|
||||
}
|
||||
if !conn.SupportsUnixFDs() {
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("D-Bus connection does not support Unix FD passing")
|
||||
}
|
||||
m.dbusConn = conn
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
wlclient "github.com/AvengeMedia/dankgo/wayland/client"
|
||||
)
|
||||
|
||||
const largeEntryBytes = 1 << 20
|
||||
|
||||
type Config struct {
|
||||
MaxHistory int `json:"maxHistory"`
|
||||
MaxEntrySize int64 `json:"maxEntrySize"`
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
var cupsNotifierMatch = dbus.WithMatchInterface("org.cups.cupsd.Notifier")
|
||||
|
||||
type DBusSubscriptionManager struct {
|
||||
client CUPSClientInterface
|
||||
subscriptionID int
|
||||
@@ -42,7 +44,7 @@ func (sm *DBusSubscriptionManager) Start() error {
|
||||
sm.eventChan = make(chan SubscriptionEvent, 100)
|
||||
sm.mu.Unlock()
|
||||
|
||||
conn, err := dbus.ConnectSystemBus()
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
sm.mu.Lock()
|
||||
sm.running = false
|
||||
@@ -53,7 +55,6 @@ func (sm *DBusSubscriptionManager) Start() error {
|
||||
|
||||
subID, err := sm.createDBusSubscription()
|
||||
if err != nil {
|
||||
sm.conn.Close()
|
||||
sm.mu.Lock()
|
||||
sm.running = false
|
||||
sm.mu.Unlock()
|
||||
@@ -63,11 +64,8 @@ func (sm *DBusSubscriptionManager) Start() error {
|
||||
sm.subscriptionID = subID
|
||||
log.Infof("[CUPS] Created D-Bus subscription with ID %d", subID)
|
||||
|
||||
if err := sm.conn.AddMatchSignal(
|
||||
dbus.WithMatchInterface("org.cups.cupsd.Notifier"),
|
||||
); err != nil {
|
||||
if err := sm.conn.AddMatchSignal(cupsNotifierMatch); err != nil {
|
||||
sm.cancelSubscription()
|
||||
sm.conn.Close()
|
||||
sm.mu.Lock()
|
||||
sm.running = false
|
||||
sm.mu.Unlock()
|
||||
@@ -277,7 +275,7 @@ func (sm *DBusSubscriptionManager) Stop() {
|
||||
}
|
||||
|
||||
if sm.conn != nil {
|
||||
sm.conn.Close()
|
||||
_ = sm.conn.RemoveMatchSignal(cupsNotifierMatch)
|
||||
sm.conn = nil
|
||||
}
|
||||
|
||||
|
||||
@@ -12,20 +12,21 @@ import (
|
||||
)
|
||||
|
||||
func NewManager() (*Manager, error) {
|
||||
systemConn, err := dbus.ConnectSystemBus()
|
||||
systemConn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to system bus: %w", err)
|
||||
}
|
||||
|
||||
sessionConn, err := dbus.ConnectSessionBus()
|
||||
sessionConn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
systemConn.Close()
|
||||
return nil, fmt.Errorf("failed to connect to session bus: %w", err)
|
||||
}
|
||||
|
||||
m := &Manager{
|
||||
systemConn: systemConn,
|
||||
sessionConn: sessionConn,
|
||||
systemConn: systemConn,
|
||||
sessionConn: sessionConn,
|
||||
systemSignals: make(chan *dbus.Signal, 256),
|
||||
sessionSignals: make(chan *dbus.Signal, 256),
|
||||
}
|
||||
|
||||
go m.processSystemSignals()
|
||||
@@ -300,10 +301,9 @@ func (m *Manager) processSystemSignals() {
|
||||
if m.systemConn == nil {
|
||||
return
|
||||
}
|
||||
ch := make(chan *dbus.Signal, 256)
|
||||
m.systemConn.Signal(ch)
|
||||
m.systemConn.Signal(m.systemSignals)
|
||||
|
||||
for sig := range ch {
|
||||
for sig := range m.systemSignals {
|
||||
m.dispatchSignal("system", sig)
|
||||
}
|
||||
}
|
||||
@@ -312,10 +312,9 @@ func (m *Manager) processSessionSignals() {
|
||||
if m.sessionConn == nil {
|
||||
return
|
||||
}
|
||||
ch := make(chan *dbus.Signal, 256)
|
||||
m.sessionConn.Signal(ch)
|
||||
m.sessionConn.Signal(m.sessionSignals)
|
||||
|
||||
for sig := range ch {
|
||||
for sig := range m.sessionSignals {
|
||||
m.dispatchSignal("session", sig)
|
||||
}
|
||||
}
|
||||
@@ -376,10 +375,12 @@ func (m *Manager) Close() {
|
||||
})
|
||||
|
||||
if m.systemConn != nil {
|
||||
m.systemConn.Close()
|
||||
m.systemConn.RemoveSignal(m.systemSignals)
|
||||
close(m.systemSignals)
|
||||
}
|
||||
if m.sessionConn != nil {
|
||||
m.sessionConn.Close()
|
||||
m.sessionConn.RemoveSignal(m.sessionSignals)
|
||||
close(m.sessionSignals)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@ import (
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
systemConn *dbus.Conn
|
||||
sessionConn *dbus.Conn
|
||||
systemConn *dbus.Conn
|
||||
sessionConn *dbus.Conn
|
||||
systemSignals chan *dbus.Signal
|
||||
sessionSignals chan *dbus.Signal
|
||||
|
||||
subscriptions syncmap.Map[string, *signalSubscription]
|
||||
signalSubscribers syncmap.Map[string, chan SignalEvent]
|
||||
|
||||
@@ -85,6 +85,16 @@ func NewManager() (*Manager, error) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func armNonBlocking(device EvdevDevice) {
|
||||
nb, ok := device.(interface{ NonBlock() error })
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if err := nb.NonBlock(); err != nil {
|
||||
log.Debugf("evdev: non-blocking mode for %s: %v", device.Path(), err)
|
||||
}
|
||||
}
|
||||
|
||||
func capsLockFromDevices(devices []EvdevDevice) (bool, bool) {
|
||||
for _, device := range devices {
|
||||
if device == nil {
|
||||
@@ -92,6 +102,7 @@ func capsLockFromDevices(devices []EvdevDevice) (bool, bool) {
|
||||
}
|
||||
|
||||
ledStates, err := device.State(evLedType)
|
||||
armNonBlocking(device)
|
||||
if err != nil || len(ledStates) == 0 {
|
||||
continue
|
||||
}
|
||||
@@ -126,6 +137,7 @@ func findKeyboards() ([]EvdevDevice, error) {
|
||||
}
|
||||
|
||||
deviceName, _ := device.Name()
|
||||
armNonBlocking(device)
|
||||
log.Debugf("Found keyboard: %s at %s", deviceName, path)
|
||||
keyboards = append(keyboards, device)
|
||||
}
|
||||
@@ -208,6 +220,7 @@ func (m *Manager) watchForNewKeyboards() {
|
||||
}
|
||||
|
||||
deviceName, _ := device.Name()
|
||||
armNonBlocking(device)
|
||||
log.Debugf("Hotplugged keyboard: %s at %s", deviceName, event.Name)
|
||||
|
||||
m.devices = append(m.devices, device)
|
||||
|
||||
@@ -13,12 +13,12 @@ import (
|
||||
)
|
||||
|
||||
func NewManager() (*Manager, error) {
|
||||
systemConn, err := dbus.ConnectSystemBus()
|
||||
systemConn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to system bus: %w", err)
|
||||
}
|
||||
|
||||
sessionConn, err := dbus.ConnectSessionBus()
|
||||
sessionConn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
sessionConn = nil
|
||||
}
|
||||
@@ -308,9 +308,6 @@ func (m *Manager) Close() {
|
||||
return true
|
||||
})
|
||||
|
||||
if m.systemConn != nil {
|
||||
m.systemConn.Close()
|
||||
}
|
||||
if m.sessionConn != nil {
|
||||
m.sessionConn.RemoveMatchSignal(
|
||||
dbus.WithMatchInterface(dbusPortalSettingsInterface),
|
||||
@@ -324,6 +321,5 @@ func (m *Manager) Close() {
|
||||
m.sessionConn.RemoveSignal(signals)
|
||||
close(signals)
|
||||
}
|
||||
m.sessionConn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func NewManager() (*Manager, error) {
|
||||
conn, err := dbus.ConnectSystemBus()
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to system bus: %w", err)
|
||||
}
|
||||
@@ -29,7 +29,6 @@ func NewManager() (*Manager, error) {
|
||||
m.sleepInhibitorEnabled.Store(true)
|
||||
|
||||
if err := m.initialize(); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -714,8 +713,4 @@ func (m *Manager) Close() {
|
||||
m.subscribers.Delete(key)
|
||||
return true
|
||||
})
|
||||
|
||||
if m.conn != nil {
|
||||
m.conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,10 +92,13 @@ func NewIWDAgent(conn *dbus.Conn, prompts PromptBroker) (*IWDAgent, error) {
|
||||
}
|
||||
|
||||
func (a *IWDAgent) Close() {
|
||||
if a.conn != nil {
|
||||
mgr := a.conn.Object("net.connman.iwd", dbus.ObjectPath(iwdAgentManagerPath))
|
||||
mgr.Call(iwdAgentManagerIface+".UnregisterAgent", 0, a.objPath)
|
||||
if a.conn == nil {
|
||||
return
|
||||
}
|
||||
mgr := a.conn.Object("net.connman.iwd", dbus.ObjectPath(iwdAgentManagerPath))
|
||||
mgr.Call(iwdAgentManagerIface+".UnregisterAgent", 0, a.objPath)
|
||||
_ = a.conn.Export(nil, a.objPath, iwdAgentInterface)
|
||||
_ = a.conn.Export(nil, a.objPath, "org.freedesktop.DBus.Introspectable")
|
||||
}
|
||||
|
||||
func (a *IWDAgent) SetStateChecker(checker ConnectionStateChecker) {
|
||||
|
||||
@@ -75,7 +75,7 @@ const introspectXML = `
|
||||
</node>`
|
||||
|
||||
func NewSecretAgent(prompts PromptBroker, manager *Manager, backend *NetworkManagerBackend) (*SecretAgent, error) {
|
||||
c, err := dbus.ConnectSystemBus()
|
||||
c, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to system bus: %w", err)
|
||||
}
|
||||
@@ -90,19 +90,18 @@ func NewSecretAgent(prompts PromptBroker, manager *Manager, backend *NetworkMana
|
||||
}
|
||||
|
||||
if err := c.Export(sa, sa.objPath, nmSecretAgentIface); err != nil {
|
||||
c.Close()
|
||||
return nil, fmt.Errorf("failed to export secret agent: %w", err)
|
||||
}
|
||||
|
||||
if err := c.Export(sa, sa.objPath, "org.freedesktop.DBus.Introspectable"); err != nil {
|
||||
c.Close()
|
||||
sa.unexport()
|
||||
return nil, fmt.Errorf("failed to export introspection: %w", err)
|
||||
}
|
||||
|
||||
mgr := c.Object("org.freedesktop.NetworkManager", dbus.ObjectPath(nmAgentManagerPath))
|
||||
call := mgr.Call(nmAgentManagerIface+".Register", 0, sa.id)
|
||||
if call.Err != nil {
|
||||
c.Close()
|
||||
sa.unexport()
|
||||
return nil, fmt.Errorf("failed to register agent with NetworkManager: %w", call.Err)
|
||||
}
|
||||
|
||||
@@ -111,11 +110,17 @@ func NewSecretAgent(prompts PromptBroker, manager *Manager, backend *NetworkMana
|
||||
}
|
||||
|
||||
func (a *SecretAgent) Close() {
|
||||
if a.conn != nil {
|
||||
mgr := a.conn.Object("org.freedesktop.NetworkManager", dbus.ObjectPath(nmAgentManagerPath))
|
||||
mgr.Call(nmAgentManagerIface+".Unregister", 0, a.id)
|
||||
a.conn.Close()
|
||||
if a.conn == nil {
|
||||
return
|
||||
}
|
||||
mgr := a.conn.Object("org.freedesktop.NetworkManager", dbus.ObjectPath(nmAgentManagerPath))
|
||||
mgr.Call(nmAgentManagerIface+".Unregister", 0, a.id)
|
||||
a.unexport()
|
||||
}
|
||||
|
||||
func (a *SecretAgent) unexport() {
|
||||
_ = a.conn.Export(nil, a.objPath, nmSecretAgentIface)
|
||||
_ = a.conn.Export(nil, a.objPath, "org.freedesktop.DBus.Introspectable")
|
||||
}
|
||||
|
||||
func (a *SecretAgent) GetSecrets(
|
||||
|
||||
@@ -101,14 +101,13 @@ func NewIWDBackend() (*IWDBackend, error) {
|
||||
}
|
||||
|
||||
func (b *IWDBackend) Initialize() error {
|
||||
conn, err := dbus.ConnectSystemBus()
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to system bus: %w", err)
|
||||
}
|
||||
b.conn = conn
|
||||
|
||||
if err := b.discoverDevices(); err != nil {
|
||||
conn.Close()
|
||||
return fmt.Errorf("failed to discover iwd devices: %w", err)
|
||||
}
|
||||
|
||||
@@ -117,7 +116,6 @@ func (b *IWDBackend) Initialize() error {
|
||||
}
|
||||
|
||||
if err := b.updateState(); err != nil {
|
||||
conn.Close()
|
||||
return fmt.Errorf("failed to get initial state: %w", err)
|
||||
}
|
||||
|
||||
@@ -131,10 +129,6 @@ func (b *IWDBackend) Close() {
|
||||
if b.iwdAgent != nil {
|
||||
b.iwdAgent.Close()
|
||||
}
|
||||
|
||||
if b.conn != nil {
|
||||
b.conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *IWDBackend) discoverDevices() error {
|
||||
|
||||
@@ -85,19 +85,17 @@ func NewSystemdNetworkdBackend() (*SystemdNetworkdBackend, error) {
|
||||
}
|
||||
|
||||
func (b *SystemdNetworkdBackend) Initialize() error {
|
||||
c, err := dbus.ConnectSystemBus()
|
||||
c, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return fmt.Errorf("connect bus: %w", err)
|
||||
}
|
||||
b.conn = c
|
||||
|
||||
if err := b.enumerateLinks(); err != nil {
|
||||
c.Close()
|
||||
return fmt.Errorf("enumerate links: %w", err)
|
||||
}
|
||||
|
||||
if err := b.updateState(); err != nil {
|
||||
c.Close()
|
||||
return fmt.Errorf("update initial state: %w", err)
|
||||
}
|
||||
|
||||
@@ -108,8 +106,13 @@ func (b *SystemdNetworkdBackend) Close() {
|
||||
close(b.stopChan)
|
||||
b.StopMonitoring()
|
||||
|
||||
if b.conn != nil {
|
||||
b.conn.Close()
|
||||
if b.conn == nil || b.signals == nil {
|
||||
return
|
||||
}
|
||||
b.conn.RemoveSignal(b.signals)
|
||||
close(b.signals)
|
||||
for _, rule := range networkdMatchRules {
|
||||
b.conn.BusObject().Call("org.freedesktop.DBus.RemoveMatch", 0, rule)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,18 +7,18 @@ import (
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
var networkdMatchRules = []string{
|
||||
"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path_namespace='/org/freedesktop/network1'",
|
||||
"type='signal',interface='org.freedesktop.network1.Manager'",
|
||||
}
|
||||
|
||||
func (b *SystemdNetworkdBackend) StartMonitoring(onStateChange func()) error {
|
||||
b.onStateChange = onStateChange
|
||||
|
||||
b.signals = make(chan *dbus.Signal, 64)
|
||||
b.conn.Signal(b.signals)
|
||||
|
||||
matchRules := []string{
|
||||
"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path_namespace='/org/freedesktop/network1'",
|
||||
"type='signal',interface='org.freedesktop.network1.Manager'",
|
||||
}
|
||||
|
||||
for _, rule := range matchRules {
|
||||
for _, rule := range networkdMatchRules {
|
||||
if err := b.conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule).Err; err != nil {
|
||||
return fmt.Errorf("add match %q: %w", rule, err)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ const (
|
||||
)
|
||||
|
||||
func (b *NetworkManagerBackend) startSignalPump() error {
|
||||
conn, err := dbus.ConnectSystemBus()
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -28,7 +28,6 @@ func (b *NetworkManagerBackend) startSignalPump() error {
|
||||
dbus.WithMatchMember("PropertiesChanged"),
|
||||
); err != nil {
|
||||
conn.RemoveSignal(signals)
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -43,7 +42,6 @@ func (b *NetworkManagerBackend) startSignalPump() error {
|
||||
dbus.WithMatchMember("PropertiesChanged"),
|
||||
)
|
||||
conn.RemoveSignal(signals)
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -63,7 +61,6 @@ func (b *NetworkManagerBackend) startSignalPump() error {
|
||||
dbus.WithMatchMember("NewConnection"),
|
||||
)
|
||||
conn.RemoveSignal(signals)
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -88,7 +85,6 @@ func (b *NetworkManagerBackend) startSignalPump() error {
|
||||
dbus.WithMatchMember("ConnectionRemoved"),
|
||||
)
|
||||
conn.RemoveSignal(signals)
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -98,7 +94,6 @@ func (b *NetworkManagerBackend) startSignalPump() error {
|
||||
dbus.WithMatchMember("DeviceAdded"),
|
||||
); err != nil {
|
||||
conn.RemoveSignal(signals)
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -108,7 +103,6 @@ func (b *NetworkManagerBackend) startSignalPump() error {
|
||||
dbus.WithMatchMember("DeviceRemoved"),
|
||||
); err != nil {
|
||||
conn.RemoveSignal(signals)
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -119,7 +113,6 @@ func (b *NetworkManagerBackend) startSignalPump() error {
|
||||
dbus.WithMatchMember("PropertiesChanged"),
|
||||
); err != nil {
|
||||
conn.RemoveSignal(signals)
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -131,7 +124,6 @@ func (b *NetworkManagerBackend) startSignalPump() error {
|
||||
dbus.WithMatchMember("PropertiesChanged"),
|
||||
); err != nil {
|
||||
conn.RemoveSignal(signals)
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -145,7 +137,6 @@ func (b *NetworkManagerBackend) startSignalPump() error {
|
||||
dbus.WithMatchMember("VpnStateChanged"),
|
||||
); err != nil {
|
||||
conn.RemoveSignal(signals)
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -155,7 +146,6 @@ func (b *NetworkManagerBackend) startSignalPump() error {
|
||||
dbus.WithMatchMember("StateChanged"),
|
||||
); err != nil {
|
||||
conn.RemoveSignal(signals)
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -248,7 +238,6 @@ func (b *NetworkManagerBackend) stopSignalPump() {
|
||||
|
||||
b.sigWG.Wait()
|
||||
|
||||
b.dbusConn.Close()
|
||||
}
|
||||
|
||||
func (b *NetworkManagerBackend) handleDBusSignal(sig *dbus.Signal) {
|
||||
|
||||
@@ -10,11 +10,12 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
secretServiceBusName = "org.freedesktop.secrets"
|
||||
secretServicePath = "/org/freedesktop/secrets"
|
||||
secretServiceIface = "org.freedesktop.Secret.Service"
|
||||
secretItemIface = "org.freedesktop.Secret.Item"
|
||||
secretPromptIface = "org.freedesktop.Secret.Prompt"
|
||||
secretServiceBusName = "org.freedesktop.secrets"
|
||||
secretServicePath = "/org/freedesktop/secrets"
|
||||
secretServiceIface = "org.freedesktop.Secret.Service"
|
||||
secretServiceSessionIface = "org.freedesktop.Secret.Session"
|
||||
secretItemIface = "org.freedesktop.Secret.Item"
|
||||
secretPromptIface = "org.freedesktop.Secret.Prompt"
|
||||
)
|
||||
|
||||
type secretServiceSession struct {
|
||||
@@ -24,7 +25,7 @@ type secretServiceSession struct {
|
||||
}
|
||||
|
||||
func openSecretService() (*secretServiceSession, error) {
|
||||
c, err := dbus.ConnectSessionBus()
|
||||
c, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -34,11 +35,9 @@ func openSecretService() (*secretServiceSession, error) {
|
||||
var sessionPath dbus.ObjectPath
|
||||
call := svc.Call(secretServiceIface+".OpenSession", 0, "plain", dbus.MakeVariant(""))
|
||||
if call.Err != nil {
|
||||
c.Close()
|
||||
return nil, call.Err
|
||||
}
|
||||
if err := call.Store(new(dbus.Variant), &sessionPath); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -177,7 +176,7 @@ func (s *secretServiceSession) lookup(connUuid, settingName, settingKey string)
|
||||
}
|
||||
|
||||
func (s *secretServiceSession) close() {
|
||||
s.conn.Close()
|
||||
s.conn.Object(secretServiceBusName, s.sessionPath).Call(secretServiceSessionIface+".Close", 0)
|
||||
}
|
||||
|
||||
func (a *SecretAgent) trySecretService(
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package notifyactions
|
||||
|
||||
import (
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
)
|
||||
|
||||
func HandleRequest(conn *models.Conn, req models.Request, manager *Manager) {
|
||||
switch req.Method {
|
||||
case "notify.watchAction":
|
||||
handleWatchAction(conn, req, manager)
|
||||
default:
|
||||
models.RespondError(conn, req.ID, "unknown method")
|
||||
}
|
||||
}
|
||||
|
||||
func handleWatchAction(conn *models.Conn, req models.Request, manager *Manager) {
|
||||
id, ok := models.Get[float64](req, "id")
|
||||
if !ok || id <= 0 {
|
||||
models.RespondError(conn, req.ID, "invalid id parameter")
|
||||
return
|
||||
}
|
||||
path, ok := models.Get[string](req, "path")
|
||||
if !ok || path == "" {
|
||||
models.RespondError(conn, req.ID, "invalid path parameter")
|
||||
return
|
||||
}
|
||||
manager.Watch(uint32(id), path)
|
||||
models.Respond(conn, req.ID, "ok")
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package notifyactions
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
||||
)
|
||||
|
||||
const (
|
||||
notifyPath = "/org/freedesktop/Notifications"
|
||||
notifyInterface = "org.freedesktop.Notifications"
|
||||
actionInvoked = notifyInterface + ".ActionInvoked"
|
||||
closed = notifyInterface + ".NotificationClosed"
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
conn *dbus.Conn
|
||||
signals chan *dbus.Signal
|
||||
mu sync.Mutex
|
||||
watched map[uint32]string
|
||||
}
|
||||
|
||||
func NewManager() (*Manager, error) {
|
||||
conn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := conn.AddMatchSignal(dbus.WithMatchObjectPath(notifyPath), dbus.WithMatchInterface(notifyInterface)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := &Manager{
|
||||
conn: conn,
|
||||
signals: make(chan *dbus.Signal, 32),
|
||||
watched: make(map[uint32]string),
|
||||
}
|
||||
conn.Signal(m.signals)
|
||||
go m.loop()
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *Manager) Watch(id uint32, path string) {
|
||||
m.mu.Lock()
|
||||
m.watched[id] = path
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
func (m *Manager) Close() {
|
||||
m.conn.RemoveSignal(m.signals)
|
||||
_ = m.conn.RemoveMatchSignal(dbus.WithMatchObjectPath(notifyPath), dbus.WithMatchInterface(notifyInterface))
|
||||
close(m.signals)
|
||||
}
|
||||
|
||||
func (m *Manager) loop() {
|
||||
for sig := range m.signals {
|
||||
switch sig.Name {
|
||||
case actionInvoked:
|
||||
m.handleAction(sig)
|
||||
case closed:
|
||||
m.forget(sig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) take(sig *dbus.Signal) (string, bool) {
|
||||
if len(sig.Body) < 1 {
|
||||
return "", false
|
||||
}
|
||||
id, ok := sig.Body[0].(uint32)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
path, ok := m.watched[id]
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
delete(m.watched, id)
|
||||
return path, true
|
||||
}
|
||||
|
||||
func (m *Manager) forget(sig *dbus.Signal) {
|
||||
m.take(sig)
|
||||
}
|
||||
|
||||
func (m *Manager) handleAction(sig *dbus.Signal) {
|
||||
if len(sig.Body) < 2 {
|
||||
return
|
||||
}
|
||||
action, ok := sig.Body[1].(string)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
path, ok := m.take(sig)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if action == "folder" {
|
||||
path = filepath.Dir(path)
|
||||
}
|
||||
openPath(path)
|
||||
}
|
||||
|
||||
func openPath(path string) {
|
||||
cmd := exec.Command("xdg-open", path)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Warnf("notifyactions: xdg-open %s: %v", path, err)
|
||||
return
|
||||
}
|
||||
go func() { _ = cmd.Wait() }()
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/mime"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/network"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/notifyactions"
|
||||
serverPlugins "github.com/AvengeMedia/DankMaterialShell/core/internal/server/plugins"
|
||||
serverRegistries "github.com/AvengeMedia/DankMaterialShell/core/internal/server/registries"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/sysupdate"
|
||||
@@ -209,6 +210,15 @@ func RouteRequest(conn *models.Conn, req models.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasPrefix(req.Method, "notify.") {
|
||||
if notifyActionsManager == nil {
|
||||
models.RespondError(conn, req.ID, "notification action manager not initialized")
|
||||
return
|
||||
}
|
||||
notifyactions.HandleRequest(conn, req, notifyActionsManager)
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasPrefix(req.Method, "sysupdate.") {
|
||||
if sysUpdateManager == nil {
|
||||
models.RespondError(conn, req.ID, "sysupdate manager not initialized")
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
||||
)
|
||||
|
||||
const (
|
||||
serverMaxProcs = 4
|
||||
serverGCPercent = 50
|
||||
)
|
||||
|
||||
func tuneRuntime() {
|
||||
if os.Getenv("GOMAXPROCS") == "" {
|
||||
runtime.GOMAXPROCS(min(runtime.NumCPU(), serverMaxProcs))
|
||||
}
|
||||
if os.Getenv("GOGC") == "" {
|
||||
debug.SetGCPercent(serverGCPercent)
|
||||
}
|
||||
}
|
||||
|
||||
func startPprof() {
|
||||
addr := os.Getenv("DMS_PPROF")
|
||||
if addr == "" {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
log.Infof("pprof listening on http://%s/debug/pprof/", addr)
|
||||
if err := http.ListenAndServe(addr, nil); err != nil {
|
||||
log.Warnf("pprof server: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/loginctl"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/network"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/notifyactions"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/sysupdate"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/tailscale"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/thememode"
|
||||
@@ -75,6 +76,7 @@ var wallpaperManager *wallpaper.Manager
|
||||
var trayRecoveryManager *trayrecovery.Manager
|
||||
var locationManager *location.Manager
|
||||
var sysUpdateManager *sysupdate.Manager
|
||||
var notifyActionsManager *notifyactions.Manager
|
||||
var geoClientInstance geolocation.Client
|
||||
|
||||
const dbusClientID = "dms-dbus-client"
|
||||
@@ -353,6 +355,16 @@ func InitializeLocationManager(geoClient geolocation.Client) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitializeNotifyActionsManager() error {
|
||||
manager, err := notifyactions.NewManager()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
notifyActionsManager = manager
|
||||
log.Info("Notification action manager initialized")
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitializeSysUpdateManager() error {
|
||||
manager, err := sysupdate.NewManager()
|
||||
if err != nil {
|
||||
@@ -1259,6 +1271,9 @@ func cleanupManagers() {
|
||||
if dbusManager != nil {
|
||||
dbusManager.Close()
|
||||
}
|
||||
if notifyActionsManager != nil {
|
||||
notifyActionsManager.Close()
|
||||
}
|
||||
if themeModeManager != nil {
|
||||
themeModeManager.Close()
|
||||
}
|
||||
@@ -1319,6 +1334,9 @@ func (s *Server) Serve(printDocs bool) error {
|
||||
defer s.ipc.Close()
|
||||
defer cleanupManagers()
|
||||
|
||||
tuneRuntime()
|
||||
startPprof()
|
||||
|
||||
// Tailscale manager always starts — reconnects internally via WatchIPNBus.
|
||||
// The capability is only advertised once tailscaled is reachable; the
|
||||
// callback wakes capability subscribers so QML clients see it transition.
|
||||
@@ -1473,6 +1491,8 @@ func (s *Server) Serve(printDocs bool) error {
|
||||
log.Info(" clipboard.getConfig - Get clipboard configuration")
|
||||
log.Info(" clipboard.setConfig - Set configuration (params: maxHistory?, maxEntrySize?, autoClearDays?, clearAtStartup?)")
|
||||
log.Info(" clipboard.subscribe - Subscribe to clipboard state changes (streaming)")
|
||||
log.Info("Notify:")
|
||||
log.Info(" notify.watchAction - Open a file when a notification action fires (params: id, path)")
|
||||
log.Info("Location:")
|
||||
log.Info(" location.getState - Get current location state")
|
||||
log.Info(" location.subscribe - Subscribe to location changes (streaming)")
|
||||
@@ -1696,6 +1716,12 @@ func (s *Server) Serve(printDocs bool) error {
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
if err := InitializeNotifyActionsManager(); err != nil {
|
||||
log.Warnf("Notification action manager unavailable: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := InitializeSysUpdateManager(); err != nil {
|
||||
log.Warnf("Sysupdate manager unavailable: %v", err)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ type Manager struct {
|
||||
}
|
||||
|
||||
func NewManager() (*Manager, error) {
|
||||
conn, err := dbus.ConnectSessionBus()
|
||||
conn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to session bus: %w", err)
|
||||
}
|
||||
@@ -87,8 +87,5 @@ func (m *Manager) Close() {
|
||||
close(m.stopChan)
|
||||
}
|
||||
m.wg.Wait()
|
||||
if m.conn != nil {
|
||||
m.conn.Close()
|
||||
}
|
||||
log.Info("TrayRecovery manager closed")
|
||||
}
|
||||
|
||||
@@ -126,15 +126,15 @@ func (m *Manager) anyOutputReady() bool {
|
||||
return anyReady
|
||||
}
|
||||
|
||||
const prepareForSleepMatchRule = "type='signal',interface='org.freedesktop.login1.Manager',member='PrepareForSleep',path='/org/freedesktop/login1'"
|
||||
|
||||
func (m *Manager) setupDBusMonitor() error {
|
||||
conn, err := dbus.ConnectSystemBus()
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return fmt.Errorf("system bus: %w", err)
|
||||
}
|
||||
|
||||
matchRule := "type='signal',interface='org.freedesktop.login1.Manager',member='PrepareForSleep',path='/org/freedesktop/login1'"
|
||||
if err := conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, matchRule).Err; err != nil {
|
||||
conn.Close()
|
||||
if err := conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, prepareForSleepMatchRule).Err; err != nil {
|
||||
return fmt.Errorf("add match: %w", err)
|
||||
}
|
||||
|
||||
@@ -1281,6 +1281,6 @@ func (m *Manager) Close() {
|
||||
|
||||
if m.dbusConn != nil {
|
||||
m.dbusConn.RemoveSignal(m.dbusSignal)
|
||||
m.dbusConn.Close()
|
||||
m.dbusConn.BusObject().Call("org.freedesktop.DBus.RemoveMatch", 0, prepareForSleepMatchRule)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user