]> Cypherpunks repositories - gostls13.git/commitdiff
net: plan9 changes for default net directory
authorJeff Sickel <jas@corpus-callosum.com>
Wed, 22 Jan 2014 21:21:53 +0000 (22:21 +0100)
committerDavid du Colombier <0intro@gmail.com>
Wed, 22 Jan 2014 21:21:53 +0000 (22:21 +0100)
This change include updates to the probeIPv4Stack
and probeIPv6Stack to ensure that one or both
protocols are supported by ip(3).
The addition of fdMutex to netFD fixes the
TestTCPConcurrentAccept failures.
Additional changes add support for keepalive.

R=golang-codereviews, 0intro
CC=golang-codereviews, rsc
https://golang.org/cl/49920048

src/pkg/net/fd_plan9.go
src/pkg/net/file_plan9.go
src/pkg/net/ipsock_plan9.go
src/pkg/net/lookup_plan9.go
src/pkg/net/sockopt_plan9.go [new file with mode: 0644]
src/pkg/net/tcpsock_plan9.go
src/pkg/net/tcpsockopt_plan9.go [new file with mode: 0644]
src/pkg/net/udpsock_plan9.go

index acc8294021716f9742f1576a20ce96851c209bcb..4309a87c3a4a69e397286b453bc0e1830b9163f0 100644 (file)
@@ -13,12 +13,23 @@ import (
 
 // Network file descritor.
 type netFD struct {
-       proto, name, dir string
-       ctl, data        *os.File
-       laddr, raddr     Addr
+       // locking/lifetime of sysfd + serialize access to Read and Write methods
+       fdmu fdMutex
+
+       // immutable until Close
+       proto        string
+       n            string
+       dir          string
+       ctl, data    *os.File
+       laddr, raddr Addr
 }
 
+var (
+       netdir string // default network
+)
+
 func sysInit() {
+       netdir = "/net"
 }
 
 func dial(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) {
@@ -27,16 +38,99 @@ func dial(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline ti
        return dialChannel(net, ra, dialer, deadline)
 }
 
-func newFD(proto, name string, ctl, data *os.File, laddr, raddr Addr) *netFD {
-       return &netFD{proto, name, "/net/" + proto + "/" + name, ctl, data, laddr, raddr}
+func newFD(proto, name string, ctl, data *os.File, laddr, raddr Addr) (*netFD, error) {
+       return &netFD{proto: proto, n: name, dir: netdir + "/" + proto + "/" + name, ctl: ctl, data: data, laddr: laddr, raddr: raddr}, nil
+}
+
+func (fd *netFD) init() error {
+       // stub for future fd.pd.Init(fd)
+       return nil
+}
+
+func (fd *netFD) name() string {
+       var ls, rs string
+       if fd.laddr != nil {
+               ls = fd.laddr.String()
+       }
+       if fd.raddr != nil {
+               rs = fd.raddr.String()
+       }
+       return fd.proto + ":" + ls + "->" + rs
 }
 
 func (fd *netFD) ok() bool { return fd != nil && fd.ctl != nil }
 
+func (fd *netFD) destroy() {
+       if !fd.ok() {
+               return
+       }
+       err := fd.ctl.Close()
+       if fd.data != nil {
+               if err1 := fd.data.Close(); err1 != nil && err == nil {
+                       err = err1
+               }
+       }
+       fd.ctl = nil
+       fd.data = nil
+}
+
+// Add a reference to this fd.
+// Returns an error if the fd cannot be used.
+func (fd *netFD) incref() error {
+       if !fd.fdmu.Incref() {
+               return errClosing
+       }
+       return nil
+}
+
+// Remove a reference to this FD and close if we've been asked to do so
+// (and there are no references left).
+func (fd *netFD) decref() {
+       if fd.fdmu.Decref() {
+               fd.destroy()
+       }
+}
+
+// Add a reference to this fd and lock for reading.
+// Returns an error if the fd cannot be used.
+func (fd *netFD) readLock() error {
+       if !fd.fdmu.RWLock(true) {
+               return errClosing
+       }
+       return nil
+}
+
+// Unlock for reading and remove a reference to this FD.
+func (fd *netFD) readUnlock() {
+       if fd.fdmu.RWUnlock(true) {
+               fd.destroy()
+       }
+}
+
+// Add a reference to this fd and lock for writing.
+// Returns an error if the fd cannot be used.
+func (fd *netFD) writeLock() error {
+       if !fd.fdmu.RWLock(false) {
+               return errClosing
+       }
+       return nil
+}
+
+// Unlock for writing and remove a reference to this FD.
+func (fd *netFD) writeUnlock() {
+       if fd.fdmu.RWUnlock(false) {
+               fd.destroy()
+       }
+}
+
 func (fd *netFD) Read(b []byte) (n int, err error) {
        if !fd.ok() || fd.data == nil {
                return 0, syscall.EINVAL
        }
+       if err := fd.readLock(); err != nil {
+               return 0, err
+       }
+       defer fd.readUnlock()
        n, err = fd.data.Read(b)
        if fd.proto == "udp" && err == io.EOF {
                n = 0
@@ -49,6 +143,10 @@ func (fd *netFD) Write(b []byte) (n int, err error) {
        if !fd.ok() || fd.data == nil {
                return 0, syscall.EINVAL
        }
+       if err := fd.writeLock(); err != nil {
+               return 0, err
+       }
+       defer fd.writeUnlock()
        return fd.data.Write(b)
 }
 
@@ -67,6 +165,9 @@ func (fd *netFD) CloseWrite() error {
 }
 
 func (fd *netFD) Close() error {
+       if !fd.fdmu.IncrefAndClose() {
+               return errClosing
+       }
        if !fd.ok() {
                return syscall.EINVAL
        }
index f6ee1c29e0f2a90a7a22695836a423fb4f03debb..068f0881dd36dd3f7740d7f24960167a0f16df39 100644 (file)
@@ -43,7 +43,7 @@ func newFileFD(f *os.File) (net *netFD, err error) {
        }
        comp := splitAtBytes(path, "/")
        n := len(comp)
-       if n < 3 || comp[0] != "net" {
+       if n < 3 || comp[0][0:3] != "net" {
                return nil, syscall.EPLAN9
        }
 
@@ -58,7 +58,7 @@ func newFileFD(f *os.File) (net *netFD, err error) {
                }
                defer close(fd)
 
-               dir := "/net/" + comp[n-2]
+               dir := netdir + "/" + comp[n-2]
                ctl = os.NewFile(uintptr(fd), dir+"/"+file)
                ctl.Seek(0, 0)
                var buf [16]byte
@@ -71,19 +71,19 @@ func newFileFD(f *os.File) (net *netFD, err error) {
                if len(comp) < 4 {
                        return nil, errors.New("could not find control file for connection")
                }
-               dir := "/net/" + comp[1] + "/" + name
+               dir := netdir + "/" + comp[1] + "/" + name
                ctl, err = os.OpenFile(dir+"/ctl", os.O_RDWR, 0)
                if err != nil {
                        return nil, err
                }
                defer close(int(ctl.Fd()))
        }
-       dir := "/net/" + comp[1] + "/" + name
+       dir := netdir + "/" + comp[1] + "/" + name
        laddr, err := readPlan9Addr(comp[1], dir+"/local")
        if err != nil {
                return nil, err
        }
-       return newFD(comp[1], name, ctl, nil, laddr, nil), nil
+       return newFD(comp[1], name, ctl, nil, laddr, nil)
 }
 
 func newFileConn(f *os.File) (c Conn, err error) {
index fcec4164f4c74c9ff57845b79dfdb19d91118483..c2225002ee142bae31686e79d64cda2c26892fad 100644 (file)
@@ -12,19 +12,45 @@ import (
        "syscall"
 )
 
+func probe(filename, query string, bufSize int) bool {
+       var file *file
+       var err error
+       if file, err = open(filename); err != nil {
+               return false
+       }
+
+       r := false
+       for line, ok := file.readLine(); ok && !r; line, ok = file.readLine() {
+               f := getFields(line)
+               if len(f) < 3 {
+                       continue
+               }
+               for i := 0; i < len(f); i++ {
+                       if query == f[i] {
+                               r = true
+                               break
+                       }
+               }
+       }
+       file.close()
+       return r
+}
+
 func probeIPv4Stack() bool {
-       // TODO(mikio): implement this when Plan 9 supports IPv6-only
-       // kernel.
-       return true
+       return probe(netdir+"/ipselftab", "127.0.0.1", 128)
 }
 
 // probeIPv6Stack returns two boolean values.  If the first boolean
 // value is true, kernel supports basic IPv6 functionality.  If the
 // second boolean value is true, kernel supports IPv6 IPv4-mapping.
 func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
-       // TODO(mikio): implement this once Plan 9 gets an IPv6
-       // protocol stack implementation.
-       return false, false
+       // Plan 9 uses IPv6 natively, see ip(3).
+       r := probe(netdir+"/iproute", "6i", 128)
+       v := false
+       if r {
+               v = probe(netdir+"/iproute", "4b", 128)
+       }
+       return r, v
 }
 
 // parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80).
@@ -133,18 +159,18 @@ func dialPlan9(net string, laddr, raddr Addr) (fd *netFD, err error) {
                f.Close()
                return nil, &OpError{"dial", f.Name(), raddr, err}
        }
-       data, err := os.OpenFile("/net/"+proto+"/"+name+"/data", os.O_RDWR, 0)
+       data, err := os.OpenFile(netdir+"/"+proto+"/"+name+"/data", os.O_RDWR, 0)
        if err != nil {
                f.Close()
                return nil, &OpError{"dial", net, raddr, err}
        }
-       laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local")
+       laddr, err = readPlan9Addr(proto, netdir+"/"+proto+"/"+name+"/local")
        if err != nil {
                data.Close()
                f.Close()
                return nil, &OpError{"dial", proto, raddr, err}
        }
-       return newFD(proto, name, f, data, laddr, raddr), nil
+       return newFD(proto, name, f, data, laddr, raddr)
 }
 
 func listenPlan9(net string, laddr Addr) (fd *netFD, err error) {
@@ -158,20 +184,24 @@ func listenPlan9(net string, laddr Addr) (fd *netFD, err error) {
                f.Close()
                return nil, &OpError{"announce", proto, laddr, err}
        }
-       laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local")
+       laddr, err = readPlan9Addr(proto, netdir+"/"+proto+"/"+name+"/local")
        if err != nil {
                f.Close()
                return nil, &OpError{Op: "listen", Net: net, Err: err}
        }
-       return newFD(proto, name, f, nil, laddr, nil), nil
+       return newFD(proto, name, f, nil, laddr, nil)
 }
 
-func (l *netFD) netFD() *netFD {
-       return newFD(l.proto, l.name, l.ctl, l.data, l.laddr, l.raddr)
+func (l *netFD) netFD() (*netFD, error) {
+       return newFD(l.proto, l.n, l.ctl, l.data, l.laddr, l.raddr)
 }
 
 func (l *netFD) acceptPlan9() (fd *netFD, err error) {
        defer func() { netErr(err) }()
+       if err := l.readLock(); err != nil {
+               return nil, err
+       }
+       defer l.readUnlock()
        f, err := os.Open(l.dir + "/listen")
        if err != nil {
                return nil, &OpError{"accept", l.dir + "/listen", l.laddr, err}
@@ -183,16 +213,16 @@ func (l *netFD) acceptPlan9() (fd *netFD, err error) {
                return nil, &OpError{"accept", l.dir + "/listen", l.laddr, err}
        }
        name := string(buf[:n])
-       data, err := os.OpenFile("/net/"+l.proto+"/"+name+"/data", os.O_RDWR, 0)
+       data, err := os.OpenFile(netdir+"/"+l.proto+"/"+name+"/data", os.O_RDWR, 0)
        if err != nil {
                f.Close()
                return nil, &OpError{"accept", l.proto, l.laddr, err}
        }
-       raddr, err := readPlan9Addr(l.proto, "/net/"+l.proto+"/"+name+"/remote")
+       raddr, err := readPlan9Addr(l.proto, netdir+"/"+l.proto+"/"+name+"/remote")
        if err != nil {
                data.Close()
                f.Close()
                return nil, &OpError{"accept", l.proto, l.laddr, err}
        }
-       return newFD(l.proto, name, f, data, l.laddr, raddr), nil
+       return newFD(l.proto, name, f, data, l.laddr, raddr)
 }
index 95c0f0b0681e454d8728f26dec724d14139e3763..723f29ffce19ca3f9189ec86c2da5229548475a0 100644 (file)
@@ -49,7 +49,7 @@ func queryCS(net, host, service string) (res []string, err error) {
        if host == "" {
                host = "*"
        }
-       return query("/net/cs", net+"!"+host+"!"+service, 128)
+       return query(netdir+"/cs", net+"!"+host+"!"+service, 128)
 }
 
 func queryCS1(net string, ip IP, port int) (clone, dest string, err error) {
@@ -70,7 +70,7 @@ func queryCS1(net string, ip IP, port int) (clone, dest string, err error) {
 }
 
 func queryDNS(addr string, typ string) (res []string, err error) {
-       return query("/net/dns", addr+" "+typ, 1024)
+       return query(netdir+"/dns", addr+" "+typ, 1024)
 }
 
 // toLower returns a lower-case version of in. Restricting us to
@@ -97,7 +97,7 @@ func toLower(in string) string {
 // lookupProtocol looks up IP protocol name and returns
 // the corresponding protocol number.
 func lookupProtocol(name string) (proto int, err error) {
-       lines, err := query("/net/cs", "!protocol="+toLower(name), 128)
+       lines, err := query(netdir+"/cs", "!protocol="+toLower(name), 128)
        if err != nil {
                return 0, err
        }
@@ -117,7 +117,7 @@ func lookupProtocol(name string) (proto int, err error) {
 }
 
 func lookupHost(host string) (addrs []string, err error) {
-       // Use /net/cs instead of /net/dns because cs knows about
+       // Use netdir/cs instead of netdir/dns because cs knows about
        // host names in local network (e.g. from /lib/ndb/local)
        lines, err := queryCS("net", host, "1")
        if err != nil {
diff --git a/src/pkg/net/sockopt_plan9.go b/src/pkg/net/sockopt_plan9.go
new file mode 100644 (file)
index 0000000..8bc689b
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+func setKeepAlive(fd *netFD, keepalive bool) error {
+       if keepalive {
+               _, e := fd.ctl.WriteAt([]byte("keepalive"), 0)
+               return e
+       }
+       return nil
+}
index cf9c0f8904764bafb1e37d9cdb296713b57d40d6..6e1a8b9a1928f1168364ba43890365af425b87ae 100644 (file)
@@ -62,12 +62,18 @@ func (c *TCPConn) SetLinger(sec int) error {
 // SetKeepAlive sets whether the operating system should send
 // keepalive messages on the connection.
 func (c *TCPConn) SetKeepAlive(keepalive bool) error {
-       return syscall.EPLAN9
+       if !c.ok() {
+               return syscall.EPLAN9
+       }
+       return setKeepAlive(c.fd, keepalive)
 }
 
 // SetKeepAlivePeriod sets period between keep alives.
 func (c *TCPConn) SetKeepAlivePeriod(d time.Duration) error {
-       return syscall.EPLAN9
+       if !c.ok() {
+               return syscall.EPLAN9
+       }
+       return setKeepAlivePeriod(c.fd, d)
 }
 
 // SetNoDelay controls whether the operating system should delay
diff --git a/src/pkg/net/tcpsockopt_plan9.go b/src/pkg/net/tcpsockopt_plan9.go
new file mode 100644 (file)
index 0000000..0e7a664
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// TCP socket options for plan9
+
+package net
+
+import (
+       "time"
+)
+
+// Set keep alive period.
+func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
+       cmd := "keepalive " + string(int64(d/time.Millisecond))
+       _, e := fd.ctl.WriteAt([]byte(cmd), 0)
+       return e
+}
index 73621706d5c814213c915700e9078ccfb8863cc8..510ac5e4aaa6037b91c32fc084064307680a1fa6 100644 (file)
@@ -190,7 +190,8 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
        if err != nil {
                return nil, err
        }
-       return newUDPConn(l.netFD()), nil
+       fd, err := l.netFD()
+       return newUDPConn(fd), err
 }
 
 // ListenMulticastUDP listens for incoming multicast UDP packets