| // 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(); |
| } |