├── .gitignore ├── LICENSE ├── README.md ├── modalias.go ├── monitor.go ├── udevmon └── udevmon.go └── uevent.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 deniswernert 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | udev 2 | ==== 3 | 4 | Aims to provide a standalone udev replacement 5 | -------------------------------------------------------------------------------- /modalias.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package udev 4 | 5 | import ( 6 | "errors" 7 | "strings" 8 | ) 9 | 10 | // ModAlias abstracts a MODALIAS identifier string 11 | type ModAlias struct { 12 | Type string 13 | Value string 14 | } 15 | 16 | // ParseModAlias parses a MODALIAS identifier 17 | func ParseModAlias(input string) (*ModAlias, error) { 18 | bits := strings.SplitN(input, ":", 2) 19 | if len(bits) != 2 { 20 | return nil, errors.New("Invalid string prefix") 21 | } 22 | 23 | modalias := &ModAlias{ 24 | Type: bits[0], 25 | Value: bits[1], 26 | } 27 | 28 | return modalias, nil 29 | } 30 | -------------------------------------------------------------------------------- /monitor.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package udev 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | ) 9 | 10 | // UDevMonitor is a UDev netlink socket 11 | type UDevMonitor struct { 12 | Fd int 13 | Address syscall.SockaddrNetlink 14 | } 15 | 16 | // NewMonitor creates and connects a new monitor 17 | func NewMonitor() (mon *UDevMonitor, err error) { 18 | mon = new(UDevMonitor) 19 | if err = mon.Connect(); err != nil { 20 | mon = nil 21 | } 22 | return 23 | } 24 | 25 | // Connect connects the monitoring socket 26 | func (mon *UDevMonitor) Connect() (err error) { 27 | mon.Fd, err = syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM, syscall.NETLINK_KOBJECT_UEVENT) 28 | if nil != err { 29 | return 30 | } 31 | 32 | mon.Address = syscall.SockaddrNetlink{ 33 | Family: syscall.AF_NETLINK, 34 | Groups: 4294967295, 35 | Pid: uint32(os.Getpid()), 36 | } 37 | 38 | err = syscall.Bind(mon.Fd, &mon.Address) 39 | if nil != err { 40 | syscall.Close(mon.Fd) 41 | } 42 | 43 | return 44 | } 45 | 46 | // Close closes the monitor socket 47 | func (mon *UDevMonitor) Close() error { 48 | return syscall.Close(mon.Fd) 49 | } 50 | 51 | func (mon *UDevMonitor) Read(b []byte) (n int, err error) { 52 | return syscall.Read(mon.Fd, b) 53 | } 54 | 55 | // Process processes one packet from the socket, and sends the event on the notify channel 56 | func (mon *UDevMonitor) Process(notify chan *UEvent) { 57 | buffer := make([]byte, syscall.Getpagesize()) 58 | bytesRead, from, err := syscall.Recvfrom(mon.Fd, buffer, 0) 59 | switch v := from.(type) { 60 | case *syscall.SockaddrNetlink: 61 | if v.Pid != 0 { 62 | // Filter out non-kernel messages. 63 | // TODO: this should probably be configurable 64 | return 65 | } 66 | 67 | default: 68 | // Shouldn't happen, we might want to panic here actually 69 | return 70 | } 71 | 72 | if bytesRead <= 0 { 73 | return 74 | } 75 | 76 | if nil == err { 77 | buffer = buffer[:bytesRead] 78 | if parsed, err := ParseUEvent(buffer); err == nil { 79 | notify <- parsed 80 | } 81 | } 82 | } 83 | 84 | // Monitor starts udev event monitoring. Events are sent on the notify channel, and the watch can be 85 | // terminated by sending true on the returned shutdown channel 86 | func (mon *UDevMonitor) Monitor(notify chan *UEvent) (shutdown chan bool) { 87 | shutdown = make(chan bool) 88 | go func() { 89 | done: 90 | for { 91 | select { 92 | case <-shutdown: 93 | break done 94 | default: 95 | mon.Process(notify) 96 | } 97 | } 98 | }() 99 | return shutdown 100 | } 101 | -------------------------------------------------------------------------------- /udevmon/udevmon.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package main 4 | 5 | import ( 6 | "log" 7 | 8 | "github.com/deniswernert/udev" 9 | ) 10 | 11 | func main() { 12 | monitor, err := udev.NewMonitor() 13 | if nil != err { 14 | log.Fatalln(err) 15 | } 16 | 17 | defer monitor.Close() 18 | events := make(chan *udev.UEvent) 19 | monitor.Monitor(events) 20 | for { 21 | event := <-events 22 | log.Println(event.String()) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /uevent.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package udev 4 | 5 | import ( 6 | "errors" 7 | "strings" 8 | ) 9 | 10 | // UEvent is a kernel UDev event 11 | type UEvent struct { 12 | Action string 13 | Devpath string 14 | Env map[string]string 15 | } 16 | 17 | func (event *UEvent) String() (str string) { 18 | str = event.Action + "@" + event.Devpath 19 | for k, v := range event.Env { 20 | str += "\n" + k + "=" + v 21 | } 22 | return 23 | } 24 | 25 | // GetEnv returns the variable for Key 26 | func (event *UEvent) GetEnv(Key string) string { 27 | return event.Env[strings.ToUpper(Key)] 28 | } 29 | 30 | // Modalias returns the module alias information, if present 31 | func (event *UEvent) Modalias() (*ModAlias, error) { 32 | return ParseModAlias(event.GetEnv("MODALIAS")) 33 | } 34 | 35 | // ParseUEvent parses a byte sequence into a UEvent object 36 | func ParseUEvent(packet []byte) (event *UEvent, err error) { 37 | lines := strings.Split(string(packet), "\000") 38 | header := strings.Split(lines[0], "@") 39 | if 2 != len(header) { 40 | return nil, errors.New("Invalid action/devpath line") 41 | } 42 | 43 | event = &UEvent{ 44 | Action: header[0], 45 | Devpath: header[1], 46 | Env: make(map[string]string), 47 | } 48 | 49 | for _, line := range lines[1 : len(lines)-2] { 50 | bits := strings.SplitN(line, "=", 2) 51 | // Check well-formedness of lines. 52 | if len(bits) == 2 { 53 | event.Env[bits[0]] = bits[1] 54 | } else { 55 | return nil, errors.New("Invalid environment line") 56 | } 57 | } 58 | 59 | return 60 | } 61 | --------------------------------------------------------------------------------