1
0
mirror of https://github.com/golang/go synced 2024-10-01 13:18:33 -06:00
go/src/pkg/net/unixsock.go

191 lines
5.0 KiB
Go
Raw Normal View History

// 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.
// Unix domain sockets
package net
import (
"os";
"syscall";
)
func unixSocket(net, laddr, raddr string, mode string) (fd *netFD, err os.Error) {
var proto int;
switch net {
default:
return nil, UnknownNetworkError(net);
case "unix":
proto = syscall.SOCK_STREAM;
case "unix-dgram":
proto = syscall.SOCK_DGRAM;
}
var la, ra syscall.Sockaddr;
switch mode {
default:
panic("unixSocket", mode);
case "dial":
if laddr != "" {
return nil, &OpError{mode, net, raddr, &AddrError{"unexpected local address", laddr}}
}
if raddr == "" {
return nil, &OpError{mode, net, "", errMissingAddress}
}
ra = &syscall.SockaddrUnix{Name: raddr};
case "listen":
if laddr == "" {
return nil, &OpError{mode, net, "", errMissingAddress}
}
la = &syscall.SockaddrUnix{Name: laddr};
if raddr != "" {
return nil, &OpError{mode, net, laddr, &AddrError{"unexpected remote address", raddr}}
}
}
fd, err = socket(net, laddr, raddr, syscall.AF_UNIX, proto, 0, la, ra);
if err != nil {
goto Error;
}
return fd, nil;
Error:
addr := raddr;
if mode == "listen" {
addr = laddr;
}
return nil, &OpError{mode, net, addr, err};
}
// ConnUnix is an implementation of the Conn interface
// for connections to Unix domain sockets.
type ConnUnix struct {
connBase
}
func newConnUnix(fd *netFD, raddr string) *ConnUnix {
c := new(ConnUnix);
c.fd = fd;
c.raddr = raddr;
return c;
}
// DialUnix is like Dial but can only connect to Unix domain sockets
// and returns a ConnUnix structure. The laddr argument must be
// the empty string; it is included only to match the signature of
// the other dial routines.
func DialUnix(net, laddr, raddr string) (c *ConnUnix, err os.Error) {
fd, e := unixSocket(net, laddr, raddr, "dial");
if e != nil {
return nil, e
}
return newConnUnix(fd, raddr), nil;
}
// ListenerUnix is a Unix domain socket listener.
// Clients should typically use variables of type Listener
// instead of assuming Unix domain sockets.
type ListenerUnix struct {
fd *netFD;
laddr string
}
// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener.
// Net can be either "unix" (stream sockets) or "unix-dgram" (datagram sockets).
func ListenUnix(net, laddr string) (l *ListenerUnix, err os.Error) {
fd, e := unixSocket(net, laddr, "", "listen");
if e != nil {
if pe, ok := e.(*os.PathError); ok {
e = pe.Error;
}
// Check for socket ``in use'' but ``refusing connections,''
// which means some program created it and exited
// without unlinking it from the file system.
// Clean up on that program's behalf and try again.
// Don't do this for Linux's ``abstract'' sockets, which begin with @.
if e != os.EADDRINUSE || laddr[0] == '@' {
return nil, e;
}
fd1, e1 := unixSocket(net, "", laddr, "dial");
if e1 == nil {
fd1.Close();
}
if pe, ok := e1.(*os.PathError); ok {
e1 = pe.Error;
}
if e1 != os.ECONNREFUSED {
return nil, e;
}
syscall.Unlink(laddr);
fd1, e1 = unixSocket(net, laddr, "", "listen");
if e1 != nil {
return nil, e;
}
fd = fd1;
}
e1 := syscall.Listen(fd.fd, 8); // listenBacklog());
if e1 != 0 {
syscall.Close(fd.fd);
return nil, &OpError{"listen", "unix", laddr, os.Errno(e1)};
}
return &ListenerUnix{fd, laddr}, nil;
}
// AcceptUnix accepts the next incoming call and returns the new connection
// and the remote address.
func (l *ListenerUnix) AcceptUnix() (c *ConnUnix, raddr string, err os.Error) {
if l == nil || l.fd == nil || l.fd.fd < 0 {
return nil, "", os.EINVAL
}
fd, e := l.fd.accept();
if e != nil {
return nil, "", e
}
return newConnUnix(fd, fd.raddr), raddr, nil
}
// Accept implements the Accept method in the Listener interface;
// it waits for the next call and returns a generic Conn.
func (l *ListenerUnix) Accept() (c Conn, raddr string, err os.Error) {
// TODO(rsc): Should return l.AcceptUnix() be okay here?
// There is a type conversion -- the first return arg of
// l.AcceptUnix() is *ConnUnix and it gets converted to Conn
// in the explicit assignment.
c, raddr, err = l.AcceptUnix();
return;
}
// Close stops listening on the Unix address.
// Already accepted connections are not closed.
func (l *ListenerUnix) Close() os.Error {
if l == nil || l.fd == nil {
return os.EINVAL
}
// 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.
if l.laddr[0] != '@' {
syscall.Unlink(l.laddr);
}
err := l.fd.Close();
l.fd = nil;
return err;
}
// Addr returns the listener's network address.
func (l *ListenerUnix) Addr() string {
return l.fd.addr();
}