Skip to content

Commit 83d8df8

Browse files
add dbus notifier - support notifications via dbus SessionBus (#69)
1 parent 32216e4 commit 83d8df8

File tree

4 files changed

+145
-1
lines changed

4 files changed

+145
-1
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ The app supports the following environment variables and CLI arguments (CLI args
8484
| `YUBIKEY_TOUCH_DETECTOR_LIBNOTIFY` | `--libnotify` |
8585
| `YUBIKEY_TOUCH_DETECTOR_STDOUT` | `--stdout` |
8686
| `YUBIKEY_TOUCH_DETECTOR_NOSOCKET` | `--no-socket` |
87+
| `YUBIKEY_TOUCH_DETECTOR_DBUS` | `--dbus` |
8788

8889
You can configure the systemd service by defining any of these environment variables in `$XDG_CONFIG_HOME/yubikey-touch-detector/service.conf` - see `service.conf.example` for a configuration example.
8990

@@ -108,6 +109,12 @@ Next, in order to integrate the app with other UI components to display a visibl
108109

109110
All messages have a fixed length of 5 bytes to simplify the code on the receiving side.
110111

112+
##### notifier/dbus
113+
114+
`dbus` notifier registers a dbus server at the interface name `com.github.maximbaz.YubikeyTouchDetector` and path `/com/github/maximbaz/YubikeyTouchDetector`.
115+
116+
Properties on this dbus interface are discoverable through introspection. Properties also emit PropertiesChanged signals to indicate updates and support gobject binding.
117+
111118
## How it works
112119

113120
Your YubiKey may require a physical touch to confirm these operations:

flake.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
# remember to bump this hash when your dependencies change.
4949
# vendorHash = pkgs.lib.fakeHash;
5050

51-
vendorHash = "sha256-x8Fmhsk6MtgAtLxgH/V3KusM0BXAOaSU+2HULR5boJQ=";
51+
vendorHash = "sha256-oHEcpu3QvcVC/YCtGtP7nNT9++BSU8BPT5pf8NdLrOo=";
5252

5353
nativeBuildInputs = with pkgs; [ pkg-config scdoc ];
5454

main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,21 @@ func main() {
2828
envLibnotify := truthyValues[strings.ToLower(os.Getenv("YUBIKEY_TOUCH_DETECTOR_LIBNOTIFY"))]
2929
envStdout := truthyValues[strings.ToLower(os.Getenv("YUBIKEY_TOUCH_DETECTOR_STDOUT"))]
3030
envNosocket := truthyValues[strings.ToLower(os.Getenv("YUBIKEY_TOUCH_DETECTOR_NOSOCKET"))]
31+
envDbus := truthyValues[strings.ToLower(os.Getenv("YUBIKEY_TOUCH_DETECTOR_DBUS"))]
3132

3233
var version bool
3334
var verbose bool
3435
var libnotify bool
3536
var stdout bool
3637
var nosocket bool
38+
var dbus bool
3739

3840
flag.BoolVar(&version, "version", false, "print version and exit")
3941
flag.BoolVar(&verbose, "v", envVerbose, "enable debug logging")
4042
flag.BoolVar(&libnotify, "libnotify", envLibnotify, "show desktop notifications using libnotify")
4143
flag.BoolVar(&stdout, "stdout", envStdout, "print notifications to stdout")
4244
flag.BoolVar(&nosocket, "no-socket", envNosocket, "disable unix socket notifier")
45+
flag.BoolVar(&dbus, "dbus", envDbus, "enable dbus server for IPC")
4346
flag.Parse()
4447

4548
if version {
@@ -71,6 +74,9 @@ func main() {
7174
if stdout {
7275
go notifier.SetupStdoutNotifier(notifiers)
7376
}
77+
if dbus {
78+
go notifier.SetupDbusNotifier(notifiers)
79+
}
7480

7581
go detector.WatchU2F(notifiers)
7682
go detector.WatchHMAC(notifiers)

notifier/dbus.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package notifier
2+
3+
import (
4+
"sync"
5+
6+
"github.com/godbus/dbus/v5"
7+
"github.com/godbus/dbus/v5/introspect"
8+
"github.com/godbus/dbus/v5/prop"
9+
log "github.com/sirupsen/logrus"
10+
)
11+
12+
const DBUS_IFACE string = "com.github.maximbaz.YubikeyTouchDetector"
13+
const DBUS_PATH dbus.ObjectPath = "/com/github/maximbaz/YubikeyTouchDetector"
14+
15+
const PROP_GPG_STATE string = "GPGState"
16+
const PROP_U2F_STATE string = "U2FState"
17+
const PROP_HMAC_STATE string = "HMACState"
18+
19+
var messagePropMap = map[Message]string{
20+
GPG_ON: PROP_GPG_STATE,
21+
GPG_OFF: PROP_GPG_STATE,
22+
U2F_ON: PROP_U2F_STATE,
23+
U2F_OFF: PROP_U2F_STATE,
24+
HMAC_ON: PROP_HMAC_STATE,
25+
HMAC_OFF: PROP_HMAC_STATE,
26+
}
27+
28+
var messageValueMap = map[Message]dbus.Variant{
29+
GPG_ON: dbus.MakeVariant(uint32(1)),
30+
GPG_OFF: dbus.MakeVariant(uint32(0)),
31+
U2F_ON: dbus.MakeVariant(uint32(1)),
32+
U2F_OFF: dbus.MakeVariant(uint32(0)),
33+
HMAC_ON: dbus.MakeVariant(uint32(1)),
34+
HMAC_OFF: dbus.MakeVariant(uint32(0)),
35+
}
36+
37+
type server struct{}
38+
39+
func SetupDbusNotifier(notifiers *sync.Map) {
40+
conn, err := dbus.ConnectSessionBus()
41+
if err != nil {
42+
log.Error("Cannot establish dbus SessionBus connection ", err)
43+
return
44+
}
45+
defer conn.Close()
46+
47+
reply, err := conn.RequestName(DBUS_IFACE,
48+
dbus.NameFlagDoNotQueue)
49+
if err != nil {
50+
log.Error("Cannot request dbus interface name ", err)
51+
return
52+
}
53+
if reply != dbus.RequestNameReplyPrimaryOwner {
54+
log.Error("dbus interface name already taken")
55+
return
56+
}
57+
58+
propsSpec := map[string]map[string]*prop.Prop{
59+
DBUS_IFACE: {
60+
PROP_GPG_STATE: {
61+
Value: uint32(0),
62+
Writable: true,
63+
Emit: prop.EmitTrue,
64+
Callback: func(c *prop.Change) *dbus.Error {
65+
log.Debug(DBUS_IFACE, ".", c.Name, " changed to ", c.Value)
66+
return nil
67+
},
68+
},
69+
PROP_U2F_STATE: {
70+
Value: uint32(0),
71+
Writable: true,
72+
Emit: prop.EmitTrue,
73+
Callback: func(c *prop.Change) *dbus.Error {
74+
log.Debug(DBUS_IFACE, ".", c.Name, " changed to ", c.Value)
75+
return nil
76+
},
77+
},
78+
PROP_HMAC_STATE: {
79+
Value: uint32(0),
80+
Writable: true,
81+
Emit: prop.EmitTrue,
82+
Callback: func(c *prop.Change) *dbus.Error {
83+
log.Debug(DBUS_IFACE, ".", c.Name, " changed to ", c.Value)
84+
return nil
85+
},
86+
},
87+
},
88+
}
89+
90+
s := server{}
91+
err = conn.Export(s, DBUS_PATH, DBUS_IFACE)
92+
if err != nil {
93+
log.Error("dbus export server failed ", err)
94+
return
95+
}
96+
97+
props, err := prop.Export(conn, DBUS_PATH, propsSpec)
98+
if err != nil {
99+
log.Error("dbus export propSpec failed ", err)
100+
return
101+
}
102+
n := &introspect.Node{
103+
Name: string(DBUS_PATH),
104+
Interfaces: []introspect.Interface{
105+
introspect.IntrospectData,
106+
prop.IntrospectData,
107+
{
108+
Name: DBUS_IFACE,
109+
Methods: introspect.Methods(s),
110+
Properties: props.Introspection(DBUS_IFACE),
111+
},
112+
},
113+
}
114+
err = conn.Export(introspect.NewIntrospectable(n), DBUS_PATH, "org.freedesktop.DBus.Introspectable")
115+
if err != nil {
116+
log.Error("dbus export introspect failed ", err)
117+
return
118+
}
119+
log.Debug("Connected to dbus session interface ", DBUS_IFACE)
120+
121+
touch := make(chan Message, 10)
122+
notifiers.Store("notifier/dbus", touch)
123+
124+
for {
125+
message := <-touch
126+
err := props.Set(DBUS_IFACE, messagePropMap[message], messageValueMap[message])
127+
if err != nil {
128+
log.Warn("dbus failed to update property ", messagePropMap[message], ", ", err)
129+
}
130+
}
131+
}

0 commit comments

Comments
 (0)