Skip to content

Commit 6c1db61

Browse files
committed
feat: Add optional poll-based ready check
1 parent f0a9264 commit 6c1db61

File tree

1 file changed

+86
-33
lines changed

1 file changed

+86
-33
lines changed

pkg/client/nbd.go

Lines changed: 86 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ import (
77
"io"
88
"net"
99
"os"
10+
"path"
11+
"path/filepath"
12+
"strconv"
13+
"strings"
1014
"syscall"
15+
"time"
1116

1217
"github.com/pilebones/go-udev/netlink"
1318
"github.com/pojntfx/go-nbd/pkg/ioctl"
@@ -24,10 +29,12 @@ var (
2429
)
2530

2631
type Options struct {
27-
ExportName string
28-
BlockSize uint32
29-
OnConnected func()
30-
Timeout int
32+
ExportName string
33+
BlockSize uint32
34+
OnConnected func()
35+
ReadyCheckUdev bool
36+
ReadyCheckPollInterval time.Duration
37+
Timeout int
3138
}
3239

3340
func negotiateNewstyle(conn net.Conn) error {
@@ -60,6 +67,10 @@ func Connect(conn net.Conn, device *os.File, options *Options) error {
6067
options.ExportName = "default"
6168
}
6269

70+
if !options.ReadyCheckUdev && options.ReadyCheckPollInterval <= 0 {
71+
options.ReadyCheckPollInterval = time.Millisecond
72+
}
73+
6374
var cfd uintptr
6475
switch c := conn.(type) {
6576
case *net.TCPConn:
@@ -82,41 +93,83 @@ func Connect(conn net.Conn, device *os.File, options *Options) error {
8293

8394
fatal := make(chan error)
8495
if options.OnConnected != nil {
85-
udevConn := new(netlink.UEventConn)
86-
if err := udevConn.Connect(netlink.UdevEvent); err != nil {
87-
return err
88-
}
89-
defer udevConn.Close()
90-
91-
var (
92-
udevReadyCh = make(chan netlink.UEvent)
93-
udevErrCh = make(chan error)
94-
udevQuit = udevConn.Monitor(udevReadyCh, udevErrCh, &netlink.RuleDefinitions{
95-
Rules: []netlink.RuleDefinition{
96-
{
97-
Env: map[string]string{
98-
"DEVNAME": device.Name(),
96+
if options.ReadyCheckUdev {
97+
udevConn := new(netlink.UEventConn)
98+
if err := udevConn.Connect(netlink.UdevEvent); err != nil {
99+
return err
100+
}
101+
defer udevConn.Close()
102+
103+
var (
104+
udevReadyCh = make(chan netlink.UEvent)
105+
udevErrCh = make(chan error)
106+
udevQuit = udevConn.Monitor(udevReadyCh, udevErrCh, &netlink.RuleDefinitions{
107+
Rules: []netlink.RuleDefinition{
108+
{
109+
Env: map[string]string{
110+
"DEVNAME": device.Name(),
111+
},
99112
},
100113
},
101-
},
102-
})
103-
)
104-
defer close(udevQuit)
114+
})
115+
)
116+
defer close(udevQuit)
105117

106-
go func() {
107-
select {
108-
case <-udevReadyCh:
109-
close(udevQuit)
118+
go func() {
119+
select {
120+
case <-udevReadyCh:
121+
close(udevQuit)
110122

111-
options.OnConnected()
123+
options.OnConnected()
112124

113-
return
114-
case err := <-udevErrCh:
115-
fatal <- err
125+
return
126+
case err := <-udevErrCh:
127+
fatal <- err
116128

117-
return
118-
}
119-
}()
129+
return
130+
}
131+
}()
132+
} else {
133+
go func() {
134+
sizeFile, err := os.Open(path.Join("/sys", "block", filepath.Base(device.Name()), "size"))
135+
if err != nil {
136+
fatal <- err
137+
138+
return
139+
}
140+
defer sizeFile.Close()
141+
142+
for {
143+
if _, err := sizeFile.Seek(0, io.SeekStart); err != nil {
144+
fatal <- err
145+
146+
return
147+
}
148+
149+
rsize, err := io.ReadAll(sizeFile)
150+
if err != nil {
151+
fatal <- err
152+
153+
return
154+
}
155+
156+
size, err := strconv.ParseInt(strings.TrimSpace(string(rsize)), 10, 64)
157+
if err != nil {
158+
fatal <- err
159+
160+
return
161+
}
162+
163+
if size > 0 {
164+
options.OnConnected()
165+
166+
return
167+
}
168+
169+
time.Sleep(options.ReadyCheckPollInterval)
170+
}
171+
}()
172+
}
120173
}
121174

122175
if _, _, err := syscall.Syscall(

0 commit comments

Comments
 (0)