...
Run Format

Source file src/net/unixsock_posix.go

Documentation: net

  // Copyright 2009 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.
  
  // +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
  
  package net
  
  import (
  	"context"
  	"errors"
  	"os"
  	"syscall"
  )
  
  func unixSocket(ctx context.Context, net string, laddr, raddr sockaddr, mode string) (*netFD, error) {
  	var sotype int
  	switch net {
  	case "unix":
  		sotype = syscall.SOCK_STREAM
  	case "unixgram":
  		sotype = syscall.SOCK_DGRAM
  	case "unixpacket":
  		sotype = syscall.SOCK_SEQPACKET
  	default:
  		return nil, UnknownNetworkError(net)
  	}
  
  	switch mode {
  	case "dial":
  		if laddr != nil && laddr.isWildcard() {
  			laddr = nil
  		}
  		if raddr != nil && raddr.isWildcard() {
  			raddr = nil
  		}
  		if raddr == nil && (sotype != syscall.SOCK_DGRAM || laddr == nil) {
  			return nil, errMissingAddress
  		}
  	case "listen":
  	default:
  		return nil, errors.New("unknown mode: " + mode)
  	}
  
  	fd, err := socket(ctx, net, syscall.AF_UNIX, sotype, 0, false, laddr, raddr)
  	if err != nil {
  		return nil, err
  	}
  	return fd, nil
  }
  
  func sockaddrToUnix(sa syscall.Sockaddr) Addr {
  	if s, ok := sa.(*syscall.SockaddrUnix); ok {
  		return &UnixAddr{Name: s.Name, Net: "unix"}
  	}
  	return nil
  }
  
  func sockaddrToUnixgram(sa syscall.Sockaddr) Addr {
  	if s, ok := sa.(*syscall.SockaddrUnix); ok {
  		return &UnixAddr{Name: s.Name, Net: "unixgram"}
  	}
  	return nil
  }
  
  func sockaddrToUnixpacket(sa syscall.Sockaddr) Addr {
  	if s, ok := sa.(*syscall.SockaddrUnix); ok {
  		return &UnixAddr{Name: s.Name, Net: "unixpacket"}
  	}
  	return nil
  }
  
  func sotypeToNet(sotype int) string {
  	switch sotype {
  	case syscall.SOCK_STREAM:
  		return "unix"
  	case syscall.SOCK_DGRAM:
  		return "unixgram"
  	case syscall.SOCK_SEQPACKET:
  		return "unixpacket"
  	default:
  		panic("sotypeToNet unknown socket type")
  	}
  }
  
  func (a *UnixAddr) family() int {
  	return syscall.AF_UNIX
  }
  
  func (a *UnixAddr) sockaddr(family int) (syscall.Sockaddr, error) {
  	if a == nil {
  		return nil, nil
  	}
  	return &syscall.SockaddrUnix{Name: a.Name}, nil
  }
  
  func (a *UnixAddr) toLocal(net string) sockaddr {
  	return a
  }
  
  func (c *UnixConn) readFrom(b []byte) (int, *UnixAddr, error) {
  	var addr *UnixAddr
  	n, sa, err := c.fd.readFrom(b)
  	switch sa := sa.(type) {
  	case *syscall.SockaddrUnix:
  		if sa.Name != "" {
  			addr = &UnixAddr{Name: sa.Name, Net: sotypeToNet(c.fd.sotype)}
  		}
  	}
  	return n, addr, err
  }
  
  func (c *UnixConn) readMsg(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
  	var sa syscall.Sockaddr
  	n, oobn, flags, sa, err = c.fd.readMsg(b, oob)
  	switch sa := sa.(type) {
  	case *syscall.SockaddrUnix:
  		if sa.Name != "" {
  			addr = &UnixAddr{Name: sa.Name, Net: sotypeToNet(c.fd.sotype)}
  		}
  	}
  	return
  }
  
  func (c *UnixConn) writeTo(b []byte, addr *UnixAddr) (int, error) {
  	if c.fd.isConnected {
  		return 0, ErrWriteToConnected
  	}
  	if addr == nil {
  		return 0, errMissingAddress
  	}
  	if addr.Net != sotypeToNet(c.fd.sotype) {
  		return 0, syscall.EAFNOSUPPORT
  	}
  	sa := &syscall.SockaddrUnix{Name: addr.Name}
  	return c.fd.writeTo(b, sa)
  }
  
  func (c *UnixConn) writeMsg(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) {
  	if c.fd.sotype == syscall.SOCK_DGRAM && c.fd.isConnected {
  		return 0, 0, ErrWriteToConnected
  	}
  	var sa syscall.Sockaddr
  	if addr != nil {
  		if addr.Net != sotypeToNet(c.fd.sotype) {
  			return 0, 0, syscall.EAFNOSUPPORT
  		}
  		sa = &syscall.SockaddrUnix{Name: addr.Name}
  	}
  	return c.fd.writeMsg(b, oob, sa)
  }
  
  func dialUnix(ctx context.Context, net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
  	fd, err := unixSocket(ctx, net, laddr, raddr, "dial")
  	if err != nil {
  		return nil, err
  	}
  	return newUnixConn(fd), nil
  }
  
  func (ln *UnixListener) accept() (*UnixConn, error) {
  	fd, err := ln.fd.accept()
  	if err != nil {
  		return nil, err
  	}
  	return newUnixConn(fd), nil
  }
  
  func (ln *UnixListener) close() error {
  	// The operating system doesn't clean up
  	// the file that announcing created, so
  	// we have to clean it up ourselves.
  	// There's a race here--we can't know for
  	// sure whether someone else has come along
  	// and replaced our socket name already--
  	// but this sequence (remove then close)
  	// is at least compatible with the auto-remove
  	// sequence in ListenUnix. It's only non-Go
  	// programs that can mess us up.
  	// Even if there are racy calls to Close, we want to unlink only for the first one.
  	ln.unlinkOnce.Do(func() {
  		if ln.path[0] != '@' && ln.unlink {
  			syscall.Unlink(ln.path)
  		}
  	})
  	return ln.fd.Close()
  }
  
  func (ln *UnixListener) file() (*os.File, error) {
  	f, err := ln.fd.dup()
  	if err != nil {
  		return nil, err
  	}
  	return f, nil
  }
  
  // SetUnlinkOnClose sets whether the underlying socket file should be removed
  // from the file system when the listener is closed.
  //
  // The default behavior is to unlink the socket file only when package net created it.
  // That is, when the listener and the underlying socket file were created by a call to
  // Listen or ListenUnix, then by default closing the listener will remove the socket file.
  // but if the listener was created by a call to FileListener to use an already existing
  // socket file, then by default closing the listener will not remove the socket file.
  func (l *UnixListener) SetUnlinkOnClose(unlink bool) {
  	l.unlink = unlink
  }
  
  func listenUnix(ctx context.Context, network string, laddr *UnixAddr) (*UnixListener, error) {
  	fd, err := unixSocket(ctx, network, laddr, nil, "listen")
  	if err != nil {
  		return nil, err
  	}
  	return &UnixListener{fd: fd, path: fd.laddr.String(), unlink: true}, nil
  }
  
  func listenUnixgram(ctx context.Context, network string, laddr *UnixAddr) (*UnixConn, error) {
  	fd, err := unixSocket(ctx, network, laddr, nil, "listen")
  	if err != nil {
  		return nil, err
  	}
  	return newUnixConn(fd), nil
  }
  

View as plain text