From a9475f743f3eefad30cc8ff79c03a63226e07c8f Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Fri, 22 Jul 2022 11:37:52 -0700 Subject: [PATCH] Replace github.com/s-urbaniak/uevent.go with github.com/pilebones/go-udev/netlink github.com/s-urbaniak/uevent.go is unable to handle errors from udev netlik listener. It just panics. Issue #155 --- go.mod | 2 +- go.sum | 7 ++-- init/main.go | 2 + init/udev.go | 101 ++++++++++++++++++++++++++------------------------- 4 files changed, 59 insertions(+), 53 deletions(-) diff --git a/go.mod b/go.mod index 7cdd5fe0..74f31e05 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/anatol/luks.go v0.0.0-20211210165108-5d9a15b4f614 github.com/anatol/smart.go v0.0.0-20220218195151-5ee9e8fa73f0 github.com/anatol/tang.go v0.0.0-20220401181244-14a5a12378ca - github.com/anatol/uevent.go v1.0.1-0.20210811163347-3e166d38c549 github.com/anatol/vmtest v0.0.0-20211215032353-afd7b1dd38ef github.com/cavaliergopher/cpio v1.0.1 github.com/google/go-tpm v0.3.3 @@ -16,6 +15,7 @@ require ( github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd github.com/jessevdk/go-flags v1.5.0 github.com/klauspost/compress v1.15.1 + github.com/pilebones/go-udev v0.9.0 github.com/stretchr/testify v1.7.1 github.com/ulikunitz/xz v0.5.10 github.com/vishvananda/netlink v1.1.0 diff --git a/go.sum b/go.sum index c41814db..d0d2eb3f 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,6 @@ github.com/anatol/smart.go v0.0.0-20220218195151-5ee9e8fa73f0 h1:rnURfQKi7StDt+W github.com/anatol/smart.go v0.0.0-20220218195151-5ee9e8fa73f0/go.mod h1:mPog5NmM5znyLbUO9WQPJ4xQdVtuHPptPavY0055POc= github.com/anatol/tang.go v0.0.0-20220401181244-14a5a12378ca h1:60FgDd7iZj00Ggcm2VLkzRAuUSAMZrHqDcUqYApSG5A= github.com/anatol/tang.go v0.0.0-20220401181244-14a5a12378ca/go.mod h1:eehwvW8/vkT20kR75KYthbNZYCXyQVt4T6XGSLplJQ0= -github.com/anatol/uevent.go v1.0.1-0.20210811163347-3e166d38c549 h1:CPuCjtWK5UI9KQPDhylGHfZe7Pp8j58WK7i3wpLemA0= -github.com/anatol/uevent.go v1.0.1-0.20210811163347-3e166d38c549/go.mod h1:yO7hm0VhhOujsh+j0nn8ExPhAqIJh50MZOcKetKhAPA= github.com/anatol/vmtest v0.0.0-20211215032353-afd7b1dd38ef h1:YQzm9r8/ArTsQ6C3/h+w4Dz5dfkYkQbK/3ETLWW2i7Q= github.com/anatol/vmtest v0.0.0-20211215032353-afd7b1dd38ef/go.mod h1:JiDFhD1zjgMx9ONsHhhucGwMvCLrJMl/yu/l5qP4XFw= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -108,8 +106,9 @@ github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583 github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -142,6 +141,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pilebones/go-udev v0.9.0 h1:N1uEO/SxUwtIctc0WLU0t69JeBxIYEYnj8lT/Nabl9Q= +github.com/pilebones/go-udev v0.9.0/go.mod h1:T2eI2tUSK0hA2WS5QLjXJUfQkluZQu+18Cqvem3CaXI= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/init/main.go b/init/main.go index baf18907..c9a07e88 100644 --- a/init/main.go +++ b/init/main.go @@ -637,6 +637,8 @@ func switchRoot() error { // Cleanup the state before handing off the machine to the new init func cleanup() { + close(udevQuitLoop) + udevConn.Close() shutdownNetwork() } diff --git a/init/udev.go b/init/udev.go index 0802ec03..d21176d4 100644 --- a/init/udev.go +++ b/init/udev.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "io" "net" "os" "path/filepath" @@ -11,14 +10,14 @@ import ( "strings" "github.com/anatol/devmapper.go" - "github.com/anatol/uevent.go" + "github.com/pilebones/go-udev/netlink" "golang.org/x/sys/unix" ) // validDmEvent checks whether this udev event has correct flags. // This is similar to checks done by /usr/lib/udev/rules.d/10-dm.rules udev rules. -func validDmEvent(ev *uevent.Uevent) bool { - dmCookie := ev.Vars["DM_COOKIE"] +func validDmEvent(ev netlink.UEvent) bool { + dmCookie := ev.Env["DM_COOKIE"] if dmCookie == "" { info("udev event does not contain DM_COOKIE") return false @@ -63,57 +62,61 @@ func validDmEvent(ev *uevent.Uevent) bool { return true } -var udevReader io.ReadCloser +var ( + udevQuitLoop chan struct{} + udevConn *netlink.UEventConn +) func udevListener() error { - defer func() { - // uevent.NewDecoder uses bufio.ReadString() that is blocking. If we try to close the underlying udev file descriptor - // while bufio tries to read from it then bufio panics. See issues #22, #31 and #153 - // There is no clear way to prevent the panic so we just recover from it here and then safely exit the goroutine. - if r := recover(); r != nil { - warning("recovered udevListener panic: %v", r) - } - }() - - var err error - udevReader, err = uevent.NewReader() - if err != nil { - return err + udevConn = new(netlink.UEventConn) + if err := udevConn.Connect(netlink.KernelEvent); err != nil { + return fmt.Errorf("unable to connect to Netlink Kobject UEvent socket") } - defer udevReader.Close() + defer udevConn.Close() - dec := uevent.NewDecoder(udevReader) + queue := make(chan netlink.UEvent) + errors := make(chan error) + udevQuitLoop = udevConn.Monitor(queue, errors, nil) +exit: for { - ev, err := dec.Decode() - if err == io.EOF { - // EOF is returned if uevent reader is closed concurrently - return nil - } - if err != nil { - return err - } - debug("udev event %+v", *ev) - - // TODO: run each udev in a separate goroutine - if modalias, ok := ev.Vars["MODALIAS"]; ok { - go func() { check(loadModalias(modalias)) }() - } else if ev.Subsystem == "block" { - go func() { check(handleBlockDeviceUevent(ev)) }() - } else if ev.Subsystem == "net" { - go func() { check(handleNetworkUevent(ev)) }() - } else if ev.Subsystem == "hidraw" && ev.Action == "add" { - go func() { hidrawDevices <- ev.Vars["DEVNAME"] }() + select { + case ev, ok := <-queue: + if !ok { + break exit + } + handleUdevEvent(ev) + case err, ok := <-errors: + if !ok { + break exit + } + warning("udev: %+v", err) } } + + return nil +} + +func handleUdevEvent(ev netlink.UEvent) { + debug("udev event: %+v", ev) + + if modalias, ok := ev.Env["MODALIAS"]; ok { + go func() { check(loadModalias(modalias)) }() + } else if ev.Env["SUBSYSTEM"] == "block" { + go func() { check(handleBlockDeviceUevent(ev)) }() + } else if ev.Env["SUBSYSTEM"] == "net" { + go func() { check(handleNetworkUevent(ev)) }() + } else if ev.Env["SUBSYSTEM"] == "hidraw" && ev.Action == "add" { + go func() { hidrawDevices <- ev.Env["DEVNAME"] }() + } } -func handleNetworkUevent(ev *uevent.Uevent) error { +func handleNetworkUevent(ev netlink.UEvent) error { if ev.Action != "add" { return nil } - ifname := ev.Vars["INTERFACE"] + ifname := ev.Env["INTERFACE"] if ifname == "lo" { return nil } @@ -140,8 +143,8 @@ func handleNetworkUevent(ev *uevent.Uevent) error { var dmNameRe = regexp.MustCompile(`dm-\d+`) -func handleBlockDeviceUevent(ev *uevent.Uevent) error { - devName := ev.Vars["DEVNAME"] +func handleBlockDeviceUevent(ev netlink.UEvent) error { + devName := ev.Env["DEVNAME"] if dmNameRe.MatchString(devName) { // mapper devices should not be added on "add" uevent @@ -159,10 +162,10 @@ func handleBlockDeviceUevent(ev *uevent.Uevent) error { devPath := "/dev/" + devName - isPartition := ev.Vars["DEVTYPE"] == "partition" + isPartition := ev.Env["DEVTYPE"] == "partition" if isPartition { // if this device represents a partition inside a table (like GPT) then wait till the table is processed - parts := strings.Split(ev.Devpath, "/") + parts := strings.Split(ev.KObj, "/") tablePath := "/dev/" + parts[len(parts)-2] waitForDeviceToProcess(tablePath) } @@ -173,14 +176,14 @@ func handleBlockDeviceUevent(ev *uevent.Uevent) error { // handleMapperDeviceUevent handles device mapper related uevent // if udev event is valid then it return non-empty string that contains // new mapper device name (e.g. /dev/mapper/name) -func handleMapperDeviceUevent(ev *uevent.Uevent) error { - devName := ev.Vars["DEVNAME"] +func handleMapperDeviceUevent(ev netlink.UEvent) error { + devName := ev.Env["DEVNAME"] - major, err := strconv.Atoi(ev.Vars["MAJOR"]) + major, err := strconv.Atoi(ev.Env["MAJOR"]) if err != nil { return fmt.Errorf("udev['MAJOR']: %v", err) } - minor, err := strconv.Atoi(ev.Vars["MINOR"]) + minor, err := strconv.Atoi(ev.Env["MINOR"]) if err != nil { return fmt.Errorf("udev['MAJOR']: %v", err) }