package uevent import ( "bytes" "strconv" "strings" ) // A Message represents a kernel message to userspace. type Message struct { // alloc_uevent_skb: action_string Action KobjectAction `json:"action"` // alloc_uevent_skb: devpath DevPath string `json:"devpath"` // add_uevent_var: key value strings Env []string `json:"env"` } // String returns a multiline user-facing string representation of [Message]. func (msg *Message) String() string { var buf strings.Builder buf.WriteString(msg.Action.String() + " event") if msg.DevPath != "" { buf.WriteString(" on " + msg.DevPath) } buf.WriteString(":") for _, s := range msg.Env { buf.WriteString("\n" + s) } return buf.String() } var ( // zero is a single pre-allocated NUL character. zero = []byte{0} // sepHeader is the separator in a [Message] header. sepHeader = []byte{'@'} ) func (msg *Message) AppendBinary(b []byte) (_ []byte, err error) { if b, err = msg.Action.AppendText(b); err != nil { return } b = append(b, sepHeader...) b = append(b, msg.DevPath...) b = append(b, zero...) for _, s := range msg.Env { b = append(b, s...) b = append(b, zero...) } return b, nil } func (msg *Message) MarshalBinary() ([]byte, error) { return msg.AppendBinary(nil) } // MissingHeaderError is an invalid representation of [Message] which is missing // its header added by alloc_uevent_skb. type MissingHeaderError string var _ Recoverable = MissingHeaderError("") func (MissingHeaderError) recoverable() {} func (e MissingHeaderError) Error() string { return "message " + strconv.Quote(string(e)) + " has no header" } // MessageError describes a malformed representation of [Message]. type MessageError struct { // Full offending data. Data string `json:"data"` // Offending section. Section string `json:"section"` // Part of header in Section. Kind int `json:"kind"` } var _ Recoverable = new(MessageError) var _ Nontrivial = new(MessageError) const ( // MErrorKindHeaderSep denotes a message header missing its separator. MErrorKindHeaderSep = iota // MErrorKindFinalNUL denotes a message body missing its final NUL terminator. MErrorKindFinalNUL ) func (*MessageError) recoverable() {} func (*MessageError) nontrivial() {} func (e *MessageError) Error() string { switch e.Kind { case MErrorKindHeaderSep: return "header " + strconv.Quote(e.Section) + " missing separator" case MErrorKindFinalNUL: return "entry " + strconv.Quote(e.Section) + " missing NUL" default: return "section " + strconv.Quote(e.Section) + " is invalid" } } func (msg *Message) UnmarshalBinary(data []byte) error { header, body, ok := bytes.Cut(data, zero) if !ok { return MissingHeaderError(data) } action_string, devpath, ok := bytes.Cut(header, sepHeader) if !ok { return &MessageError{string(data), string(header), MErrorKindHeaderSep} } if err := msg.Action.UnmarshalText(action_string); err != nil { return err } msg.DevPath = string(devpath) if len(body) == 0 { msg.Env = nil return nil } msg.Env = make([]string, 0, bytes.Count(body, zero)) var s []byte for len(body) != 0 { var r []byte s, r, ok = bytes.Cut(body, zero) if !ok { return &MessageError{string(data), string(body), MErrorKindFinalNUL} } body = r msg.Env = append(msg.Env, string(s)) } return nil }