github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/osutil/udev/netlink/uevent.go (about) 1 package netlink 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "strings" 8 "unsafe" 9 ) 10 11 // See: http://elixir.free-electrons.com/linux/v3.12/source/lib/kobject_uevent.c#L45 12 13 const ( 14 ADD KObjAction = "add" 15 REMOVE KObjAction = "remove" 16 CHANGE KObjAction = "change" 17 MOVE KObjAction = "move" 18 ONLINE KObjAction = "online" 19 OFFLINE KObjAction = "offline" 20 BIND KObjAction = "bind" 21 UNBIND KObjAction = "unbind" 22 ) 23 24 // The magic value used by udev, see https://github.com/systemd/systemd/blob/v239/src/libudev/libudev-monitor.c#L57 25 const libudevMagic = 0xfeedcafe 26 27 type KObjAction string 28 29 func (a KObjAction) String() string { 30 return string(a) 31 } 32 33 func ParseKObjAction(raw string) (a KObjAction, err error) { 34 a = KObjAction(raw) 35 switch a { 36 case ADD, REMOVE, CHANGE, MOVE, ONLINE, OFFLINE, BIND, UNBIND: 37 default: 38 err = fmt.Errorf("unknow kobject action (got: %s)", raw) 39 } 40 return 41 } 42 43 type UEvent struct { 44 Action KObjAction 45 KObj string 46 Env map[string]string 47 } 48 49 func (e UEvent) String() string { 50 rv := fmt.Sprintf("%s@%s\000", e.Action.String(), e.KObj) 51 for k, v := range e.Env { 52 rv += k + "=" + v + "\000" 53 } 54 return rv 55 } 56 57 func (e UEvent) Bytes() []byte { 58 return []byte(e.String()) 59 } 60 61 func (e UEvent) Equal(e2 UEvent) (bool, error) { 62 if e.Action != e2.Action { 63 return false, fmt.Errorf("Wrong action (got: %s, wanted: %s)", e.Action, e2.Action) 64 } 65 66 if e.KObj != e2.KObj { 67 return false, fmt.Errorf("Wrong kobject (got: %s, wanted: %s)", e.KObj, e2.KObj) 68 } 69 70 if len(e.Env) != len(e2.Env) { 71 return false, fmt.Errorf("Wrong length of env (got: %d, wanted: %d)", len(e.Env), len(e2.Env)) 72 } 73 74 var found bool 75 for k, v := range e.Env { 76 found = false 77 for i, e := range e2.Env { 78 if i == k && v == e { 79 found = true 80 } 81 } 82 if !found { 83 return false, fmt.Errorf("Unable to find %s=%s env var from uevent", k, v) 84 } 85 } 86 return true, nil 87 } 88 89 // Parse udev event created by udevd. 90 // The format of the data header is internal to udev and defined in libudev-monitor.c - see the udev_monitor_netlink_header struct. 91 // go-udev only looks at the "magic" number to filter out possibly invalid packets, and at the payload offset. Other fields of the header 92 // are ignored. 93 // Note, only some of the fields of the header use network byte order, for the rest udev uses native byte order of the platform. 94 func parseUdevEvent(raw []byte) (e *UEvent, err error) { 95 // the magic number is stored in network byte order. 96 magic := binary.BigEndian.Uint32(raw[8:]) 97 if magic != libudevMagic { 98 return nil, fmt.Errorf("cannot parse libudev event: magic number mismatch") 99 } 100 101 // the payload offset int is stored in native byte order. 102 payloadoff := *(*uint32)(unsafe.Pointer(&raw[16])) 103 if payloadoff >= uint32(len(raw)) { 104 return nil, fmt.Errorf("cannot parse libudev event: invalid data offset") 105 } 106 107 fields := bytes.Split(raw[payloadoff:], []byte{0x00}) // 0x00 = end of string 108 if len(fields) == 0 { 109 err = fmt.Errorf("cannot parse libudev event: data missing") 110 return 111 } 112 113 envdata := make(map[string]string, 0) 114 for _, envs := range fields[0 : len(fields)-1] { 115 env := bytes.Split(envs, []byte("=")) 116 if len(env) != 2 { 117 err = fmt.Errorf("cannot parse libudev event: invalid env data") 118 return 119 } 120 envdata[string(env[0])] = string(env[1]) 121 } 122 123 var action KObjAction 124 action, err = ParseKObjAction(strings.ToLower(envdata["ACTION"])) 125 if err != nil { 126 return 127 } 128 129 // XXX: do we need kobj? 130 kobj := envdata["DEVPATH"] 131 132 e = &UEvent{ 133 Action: action, 134 KObj: kobj, 135 Env: envdata, 136 } 137 138 return 139 } 140 141 func ParseUEvent(raw []byte) (e *UEvent, err error) { 142 if len(raw) > 40 && bytes.Compare(raw[:8], []byte("libudev\x00")) == 0 { 143 return parseUdevEvent(raw) 144 } 145 fields := bytes.Split(raw, []byte{0x00}) // 0x00 = end of string 146 147 if len(fields) == 0 { 148 err = fmt.Errorf("Wrong uevent format") 149 return 150 } 151 152 headers := bytes.Split(fields[0], []byte("@")) // 0x40 = @ 153 if len(headers) != 2 { 154 err = fmt.Errorf("Wrong uevent header") 155 return 156 } 157 158 action, err := ParseKObjAction(string(headers[0])) 159 if err != nil { 160 return 161 } 162 163 e = &UEvent{ 164 Action: action, 165 KObj: string(headers[1]), 166 Env: make(map[string]string, 0), 167 } 168 169 for _, envs := range fields[1 : len(fields)-1] { 170 env := bytes.Split(envs, []byte("=")) 171 if len(env) != 2 { 172 err = fmt.Errorf("Wrong uevent env") 173 return 174 } 175 e.Env[string(env[0])] = string(env[1]) 176 } 177 return 178 }