mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-12-02 05:40:13 +00:00
106 lines
2.7 KiB
Go
106 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"proxy/libproxy"
|
|
"strings"
|
|
"syscall"
|
|
)
|
|
|
|
func onePort() {
|
|
host, _, container, localIP := parseHostContainerAddrs()
|
|
|
|
var ipP libproxy.Proxy
|
|
var err error
|
|
|
|
if localIP {
|
|
ipP, err = listenInVM(host, container)
|
|
if err != nil {
|
|
sendError(err)
|
|
}
|
|
}
|
|
|
|
ctl, err := exposePort(host, container)
|
|
if err != nil {
|
|
sendError(err)
|
|
}
|
|
|
|
go handleStopSignals()
|
|
// TODO: avoid this line if we are running in a TTY
|
|
sendOK()
|
|
if ipP != nil {
|
|
ipP.Run()
|
|
} else {
|
|
select {} // sleep forever
|
|
}
|
|
ctl.Close() // ensure ctl remains alive and un-GCed until here
|
|
os.Exit(0)
|
|
}
|
|
|
|
// Best-effort attempt to listen on the address in the VM. This is for
|
|
// backwards compatibility with software that expects to be able to listen on
|
|
// 0.0.0.0 and then connect from within a container to the external port.
|
|
// If the address doesn't exist in the VM (i.e. it exists only on the host)
|
|
// then this is not a hard failure.
|
|
func listenInVM(host net.Addr, container net.Addr) (libproxy.Proxy, error) {
|
|
ipP, err := libproxy.NewIPProxy(host, container)
|
|
if err == nil {
|
|
return ipP, nil
|
|
}
|
|
if opError, ok := err.(*net.OpError); ok {
|
|
if syscallError, ok := opError.Err.(*os.SyscallError); ok {
|
|
if syscallError.Err == syscall.EADDRNOTAVAIL {
|
|
log.Printf("Address %s doesn't exist in the VM: only binding on the host", host)
|
|
return nil, nil // Non-fatal error
|
|
}
|
|
}
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
func exposePort(host net.Addr, container net.Addr) (*os.File, error) {
|
|
name := host.Network() + ":" + host.String() + ":" + container.Network() + ":" + container.String()
|
|
log.Printf("exposePort %s\n", name)
|
|
err := os.Mkdir("/port/"+name, 0)
|
|
if err != nil {
|
|
log.Printf("Failed to mkdir /port/%s: %#v\n", name, err)
|
|
return nil, err
|
|
}
|
|
ctl, err := os.OpenFile("/port/"+name+"/ctl", os.O_RDWR, 0)
|
|
if err != nil {
|
|
log.Printf("Failed to open /port/%s/ctl: %#v\n", name, err)
|
|
return nil, err
|
|
}
|
|
_, err = ctl.WriteString(fmt.Sprintf("%s", name))
|
|
if err != nil {
|
|
log.Printf("Failed to open /port/%s/ctl: %#v\n", name, err)
|
|
return nil, err
|
|
}
|
|
_, err = ctl.Seek(0, 0)
|
|
if err != nil {
|
|
log.Printf("Failed to seek on /port/%s/ctl: %#v\n", name, err)
|
|
return nil, err
|
|
}
|
|
results := make([]byte, 100)
|
|
count, err := ctl.Read(results)
|
|
if err != nil {
|
|
log.Printf("Failed to read from /port/%s/ctl: %#v\n", name, err)
|
|
return nil, err
|
|
}
|
|
// We deliberately keep the control file open since 9P clunk
|
|
// will trigger a shutdown on the host side.
|
|
|
|
response := string(results[0:count])
|
|
if strings.HasPrefix(response, "ERROR ") {
|
|
os.Remove("/port/" + name + "/ctl")
|
|
response = strings.Trim(response[6:], " \t\r\n")
|
|
return nil, errors.New(response)
|
|
}
|
|
// Hold on to a reference to prevent premature GC and close
|
|
return ctl, nil
|
|
}
|