Skip to content
This repository was archived by the owner on Feb 27, 2018. It is now read-only.

Commit 3afc6e2

Browse files
Sven DowideitSven Dowideit
authored andcommitted
Merge pull request #140 from SvenDowideit/serial-console
Serial console
2 parents 29a1310 + 13d8eb5 commit 3afc6e2

4 files changed

Lines changed: 181 additions & 24 deletions

File tree

cmds.go

Lines changed: 70 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"io/ioutil"
99
"net"
1010
"os"
11-
//"os/exec"
1211
"path/filepath"
1312
"runtime"
1413
"strings"
@@ -80,6 +79,7 @@ func cmdInit() int {
8079
m.OSType = "Linux26_64"
8180
m.CPUs = uint(runtime.NumCPU())
8281
m.Memory = B2D.Memory
82+
m.SerialFile = B2D.SerialFile
8383

8484
m.Flag |= vbx.F_pae
8585
m.Flag |= vbx.F_longmode // important: use x86-64 processor
@@ -232,27 +232,50 @@ func cmdUp() int {
232232
return 1
233233
}
234234

235-
logf("Waiting for SSH server to start...")
236-
addr := fmt.Sprintf("localhost:%d", m.SSHPort)
237-
const n = 10
238-
// Try to connect to the SSH 10 times at 3 sec interval before giving up.
239-
if err := read(addr, n, 3*time.Second); err != nil {
240-
logf("Failed to connect to SSH port at %s after %d attempts. Last error: %v", addr, n, err)
235+
if err := m.Refresh(); err != nil {
236+
logf("Failed to start machine %q: %s", B2D.VM, err)
237+
return 1
238+
}
239+
if m.State != vbx.Running {
240+
logf("Failed to start machine %q (run again with -v for details)", B2D.VM)
241241
return 1
242242
}
243243

244+
logf("Waiting for VM to be started...")
245+
//give the VM a little time to start, so we don't kill the Serial Pipe/Socket
246+
time.Sleep(2)
247+
natSSH := fmt.Sprintf("localhost:%d", B2D.SSHPort)
248+
IP := ""
249+
for i := 1; i < 30; i++ {
250+
if B2D.Serial && runtime.GOOS != "windows" {
251+
if IP = RequestIPFromSerialPort(m.SerialFile); IP != "" {
252+
break
253+
}
254+
}
255+
if err := read(natSSH, 1, 1*time.Second); err == nil {
256+
IP = RequestIPFromSSH(m)
257+
break
258+
}
259+
260+
print(".")
261+
}
262+
print("\n")
263+
244264
logf("Started.")
245265

266+
if IP == "" {
267+
logf("Auto detection of the VM's IP address.")
268+
}
246269
switch runtime.GOOS {
247270
case "windows":
248271
logf("Docker client does not run on Windows for now. Please use")
249272
logf(" %s ssh", os.Args[0])
250273
logf("to SSH into the VM instead.")
251274
default:
252275
// Check if $DOCKER_HOST ENV var is properly configured.
253-
if os.Getenv("DOCKER_HOST") != fmt.Sprintf("tcp://localhost:%d", m.DockerPort) {
276+
if os.Getenv("DOCKER_HOST") != fmt.Sprintf("tcp://%s:%d", IP, m.DockerPort) {
254277
logf("To connect the Docker client to the Docker daemon, please set:")
255-
logf(" export DOCKER_HOST=tcp://localhost:%d", m.DockerPort)
278+
logf(" export DOCKER_HOST=tcp://%s:%d", IP, m.DockerPort)
256279
}
257280
}
258281
return 0
@@ -433,6 +456,33 @@ func cmdIP() int {
433456
return 1
434457
}
435458

459+
IP := ""
460+
if B2D.Serial {
461+
for i := 1; i < 20; i++ {
462+
if runtime.GOOS != "windows" {
463+
if IP = RequestIPFromSerialPort(m.SerialFile); IP != "" {
464+
break
465+
}
466+
}
467+
}
468+
}
469+
470+
if IP == "" {
471+
IP = RequestIPFromSSH(m)
472+
}
473+
if IP != "" {
474+
errf("\nThe VM's Host only interface IP address is: ")
475+
fmt.Printf("%s", IP)
476+
errf("\n\n")
477+
} else {
478+
errf("\nFailed to get VM Host only IP address.\n")
479+
errf("\tWas the VM initilized using boot2docker?\n")
480+
}
481+
return 0
482+
}
483+
484+
func RequestIPFromSSH(m *vbx.Machine) string {
485+
// fall back to using the NAT port forwarded ssh
436486
out, err := cmd(B2D.SSH,
437487
//"-vvv", //TODO: add if its boot2docker -v
438488
"-o", "StrictHostKeyChecking=no",
@@ -442,25 +492,21 @@ func cmdIP() int {
442492
"docker@localhost",
443493
"ip addr show dev eth1",
444494
)
495+
IP := ""
445496
if err != nil {
446497
logf("%s", err)
447-
return 1
448-
}
449-
// parse to find: inet 192.168.59.103/24 brd 192.168.59.255 scope global eth1
450-
lines := strings.Split(out, "\n")
451-
for _, line := range lines {
452-
vals := strings.Split(strings.TrimSpace(line), " ")
453-
if vals[0] == "inet" {
454-
ip := vals[1][:strings.Index(vals[1], "/")]
455-
errf("\nThe VM's Host only interface IP address is: ")
456-
fmt.Printf("%s", ip)
457-
errf("\n\n")
458-
return 0
498+
} else {
499+
// parse to find: inet 192.168.59.103/24 brd 192.168.59.255 scope global eth1
500+
lines := strings.Split(out, "\n")
501+
for _, line := range lines {
502+
vals := strings.Split(strings.TrimSpace(line), " ")
503+
if len(vals) >= 2 && vals[0] == "inet" {
504+
IP = vals[1][:strings.Index(vals[1], "/")]
505+
break
506+
}
459507
}
460508
}
461-
errf("\nFailed to get VM Host only IP address.\n")
462-
errf("\tWas the VM initilized using boot2docker?\n")
463-
return 0
509+
return IP
464510
}
465511

466512
// Download the boot2docker ISO image.

config.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ var B2D struct {
4545
LowerIP net.IP
4646
UpperIP net.IP
4747
DHCPEnabled bool
48+
49+
// Serial console pipe/socket
50+
Serial bool
51+
SerialFile string
4852
}
4953

5054
var (
@@ -138,6 +142,14 @@ func config() (*flag.FlagSet, error) {
138142
flags.IPVar(&B2D.LowerIP, "lowerip", net.ParseIP("192.168.59.103"), "VirtualBox host-only network DHCP lower bound.")
139143
flags.IPVar(&B2D.UpperIP, "upperip", net.ParseIP("192.168.59.254"), "VirtualBox host-only network DHCP upper bound.")
140144

145+
if runtime.GOOS != "windows" {
146+
//SerialFile ~~ filepath.Join(dir, B2D.vm+".sock")
147+
flags.StringVar(&B2D.SerialFile, "serialfile", "", "path to the serial socket/pipe.")
148+
flags.BoolVar(&B2D.Serial, "serial", false, "try serial console to get IP address (experimental)")
149+
} else {
150+
B2D.Serial = false
151+
}
152+
141153
// Set the defaults
142154
if err := flags.Parse([]string{}); err != nil {
143155
return nil, err
@@ -164,6 +176,16 @@ func config() (*flag.FlagSet, error) {
164176

165177
vbx.Verbose = B2D.Verbose
166178
vbx.VBM = B2D.VBM
179+
180+
if B2D.SerialFile == "" {
181+
if runtime.GOOS == "windows" {
182+
//SerialFile ~~ filepath.Join(dir, B2D.vm+".sock")
183+
B2D.SerialFile = `\\.\pipe\` + B2D.VM
184+
} else {
185+
B2D.SerialFile = filepath.Join(dir, B2D.VM+".sock")
186+
}
187+
}
188+
167189
return flags, nil
168190
}
169191

util.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"os"
1111
"os/exec"
1212
"path/filepath"
13+
"regexp"
1314
"strings"
1415
"time"
1516
)
@@ -167,3 +168,72 @@ func CopyFile(src, dst string) (int64, error) {
167168
defer df.Close()
168169
return io.Copy(df, sf)
169170
}
171+
172+
func reader(r io.Reader) {
173+
buf := make([]byte, 1024)
174+
for {
175+
_, err := io.ReadAtLeast(r, buf[:], 20)
176+
if err != nil {
177+
return
178+
}
179+
}
180+
}
181+
182+
// use the serial port socket to ask what the VM's host only IP is
183+
func RequestIPFromSerialPort(socket string) string {
184+
c, err := net.Dial("unix", socket)
185+
186+
if err != nil {
187+
return ""
188+
}
189+
defer c.Close()
190+
c.SetDeadline(time.Now().Add(time.Second))
191+
192+
line := ""
193+
_, err = c.Write([]byte("\r"))
194+
_, err = c.Write([]byte("docker\r"))
195+
196+
IP := ""
197+
fullLog := ""
198+
199+
for IP == "" {
200+
_, err := c.Write([]byte("ip addr show dev eth1\r"))
201+
if err != nil {
202+
println(err)
203+
break
204+
}
205+
time.Sleep(1 * time.Second)
206+
buf := make([]byte, 1024)
207+
for {
208+
n, err := c.Read(buf[:])
209+
if err != nil {
210+
return IP
211+
}
212+
line = line + string(buf[0:n])
213+
fullLog += string(buf[0:n])
214+
if strings.Contains(line, "\n") {
215+
//go looking for the string we want, and chomp line to after the \n
216+
if i := strings.IndexAny(line, "\n"); i != -1 {
217+
// inet 10.180.1.3/16 brd 10.180.255.255 scope global wlan0
218+
inet := regexp.MustCompile(`^[\t ]*inet ([0-9.]*).*$`)
219+
if ip := inet.FindStringSubmatch(line[:i]); ip != nil {
220+
IP = ip[1]
221+
// clean up
222+
break
223+
} else {
224+
line = line[i+1:]
225+
}
226+
}
227+
}
228+
}
229+
230+
}
231+
go reader(c)
232+
//give us time reader clean up
233+
time.Sleep(1)
234+
if IP == "" && B2D.Verbose {
235+
logf(fullLog)
236+
}
237+
238+
return IP
239+
}

virtualbox/machine.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ type Machine struct {
6868
BootOrder []string // max 4 slots, each in {none|floppy|dvd|disk|net}
6969
DockerPort uint
7070
SSHPort uint
71+
SerialFile string
7172
}
7273

7374
// Refresh reloads the machine information.
@@ -92,6 +93,11 @@ func (m *Machine) Start() error {
9293
case Poweroff, Saved, Aborted:
9394
return vbm("startvm", m.Name, "--type", "headless")
9495
}
96+
if err := m.Refresh(); err == nil {
97+
if m.State != Running {
98+
return fmt.Errorf("Failed to start", m.Name)
99+
}
100+
}
95101
return nil
96102
}
97103

@@ -251,6 +257,12 @@ func GetMachine(id string) (*Machine, error) {
251257
return nil, err
252258
}
253259
m.SSHPort = uint(n)
260+
case "uartmode1":
261+
// uartmode1="server,/home/sven/.boot2docker/boot2docker-vm.sock"
262+
vals := strings.Split(val, ",")
263+
if len(vals) >= 2 {
264+
m.SerialFile = vals[1]
265+
}
254266
}
255267
}
256268
if err := s.Err(); err != nil {
@@ -345,6 +357,13 @@ func (m *Machine) Modify() error {
345357
"--accelerate3d", m.Flag.Get(F_accelerate3d),
346358
}
347359

360+
//if runtime.GOOS != "windows" {
361+
args = append(args,
362+
"--uart1", "0x3F8", "4",
363+
"--uartmode1", "server", m.SerialFile,
364+
)
365+
//}
366+
348367
for i, dev := range m.BootOrder {
349368
if i > 3 {
350369
break // Only four slots `--boot{1,2,3,4}`. Ignore the rest.

0 commit comments

Comments
 (0)