Skip to content

Instantly share code, notes, and snippets.

@corny
Last active April 16, 2018 13:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save corny/58d8dcdae82fcda47b74891f606dd4e2 to your computer and use it in GitHub Desktop.
Save corny/58d8dcdae82fcda47b74891f606dd4e2 to your computer and use it in GitHub Desktop.
Golang observer for devd.pipe
package devd
import "strings"
const (
NotifyEvent = iota
DeviceAttached
DeviceDetached
Unknown
)
type Event struct {
Type int
Values map[string]string
}
func (event *Event) System() string {
return event.Values["system"]
}
func (event *Event) SubSystem() string {
return event.Values["subsystem"]
}
func (event *Event) TypeValue() string {
return event.Values["type"]
}
func parseLine(line string) *Event {
if len(line) < 1 {
return nil
}
event := Event{
Values: make(map[string]string),
}
switch line[0] {
case '!':
event.Type = NotifyEvent
case '+':
event.Type = DeviceAttached
case '-':
event.Type = DeviceDetached
default:
event.Type = Unknown
}
for _, pair := range strings.Split(line[1:], " ") {
i := strings.IndexByte(pair, '=')
if i > 0 {
event.Values[pair[:i]] = pair[i+1:]
}
}
return &event
}
package devd
import (
"bufio"
"net"
"time"
)
type Listener struct {
Handler func(*Event)
cancel chan struct{}
}
const (
pipePath = "/var/run/devd.seqpacket.pipe"
)
// Listen starts a listener and returns a function to cancel it
func Listen(h func(*Event)) func() {
l := &Listener{
Handler: h,
cancel: make(chan struct{}),
}
go l.run()
return func() {
close(l.cancel)
}
}
func (l *Listener) eventReader() (<-chan *Event, *net.UnixConn, error) {
conn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: pipePath, Net: "unixpacket"})
if err != nil {
return nil, nil, err
}
output := make(chan *Event)
scanner := bufio.NewScanner(conn)
go func() {
for scanner.Scan() {
defer close(output)
defer conn.Close()
if event := parseLine(scanner.Text()); event != nil {
select {
case output <- event:
case <-l.cancel:
return
}
}
}
}()
return output, conn, nil
}
func (l *Listener) run() {
Loop:
for {
events, conn, err := l.eventReader()
if err != nil {
select {
case <-time.After(time.Second):
// try again
continue Loop
case <-l.cancel:
return
}
}
for {
select {
case event := <-events:
if event == nil {
// connection closed
continue Loop
}
l.Handler(event)
case <-l.cancel:
conn.Close()
return
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment