package udev import ( "bytes" "errors" "io" "strings" "time" ) // uevent buffer size https://elixir.bootlin.com/linux/latest/source/include/linux/kobject.h#L33 const MaxSize = 4096 // uevent field enums type Event string const ( Action Event = "ACTION" SequenceNumber Event = "SEQNUM" SubSystem Event = "SUBSYSTEM" SysPath Event = "DEVPATH" DevName Event = "DEVNAME" Major Event = "MAJOR" Minor Event = "MINOR" ) // uevent action enums type ActionType string const ( AddAction ActionType = "add" RemoveAction ActionType = "remove" ChangeAction ActionType = "change" BindAction ActionType = "bind" UnbindAction ActionType = "unbind" ) const ( UdevEvent = "UDEV" KernelEvent = "KERNEL" magicNumber = "libudev\x00" ) type EventType string type UEvent struct { Action ActionType SequenceNumber string EventType string SubSystem string SysPath string DevName string Major string Minor string Timestamp time.Time // map of uevent key-value pairs EnvVars map[string]string data *[]byte } // FromBuffer returns uevent from bytes data passed into it func FromBuffer(b *bytes.Buffer) *UEvent { data := b.Bytes() return &UEvent{ EnvVars: map[string]string{}, data: &data, Timestamp: time.Now(), } } func (ue *UEvent) FromBytes(kvBytes []byte) *UEvent { kv := strings.Replace(string(kvBytes), "\u0001", "", -1) // remove start of heading kv = strings.Replace(kv, "\u0010", "", -1) // remove data link escape i := strings.Index(kv, "=") if i < 0 { // drop if key:value unknown return ue } // splits the string into key, pair values k, v := kv[:i], kv[i+1:len(kv)-1] ue.EnvVars[k] = v // map uevent key and value to uevent struct switch Event(k) { case Action: ue.Action = ActionType(v) case SubSystem: ue.SubSystem = v case SysPath: ue.SysPath = v case Major: ue.Major = v case Minor: ue.Minor = v case DevName: ue.DevName = v case SequenceNumber: ue.SequenceNumber = v } return ue } func (ue *UEvent) ToBytes() *[]byte { return ue.data } // Parses the buffer and converts the stream to a UEvent object func Decode(buf []byte) (ue *UEvent, err error) { dataBuffer := bytes.NewBuffer(bytes.Clone(buf)) ue = FromBuffer(dataBuffer) ue.EventType = KernelEvent loop: for { kvBytes, err := dataBuffer.ReadBytes(0x00) if errors.Is(err, io.EOF) || len(kvBytes) == 0 { break loop } if err != nil { return ue, err } // identify udev events as opposed to kernel events if string(kvBytes) == magicNumber { ue.EventType = UdevEvent } // convert the bytes to uevent object ue = ue.FromBytes(kvBytes) } return ue, nil }