// Package unix contains utilities to work with Unix sockets. package unix import ( "fmt" "net" "net/url" "os" "sync" "time" ) type unixConn struct { l net.Listener c net.Conn mutex sync.Mutex closed bool deadline time.Time } func (r *unixConn) Close() error { r.mutex.Lock() defer r.mutex.Unlock() r.closed = true r.l.Close() if r.c != nil { r.c.Close() } return nil } func (r *unixConn) acceptWithDeadline() (net.Conn, error) { done := make(chan struct{}) defer func() { <-done }() terminate := make(chan struct{}) defer close(terminate) go func() { defer close(done) select { case <-time.After(time.Until(r.deadline)): r.l.Close() case <-terminate: return } }() c, err := r.l.Accept() if err != nil { if time.Now().After(r.deadline) { return nil, fmt.Errorf("deadline exceeded") } return nil, err } return c, nil } func (r *unixConn) setConn(c net.Conn) error { r.mutex.Lock() defer r.mutex.Unlock() if r.closed { return fmt.Errorf("closed") } r.c = c return nil } func (r *unixConn) Read(p []byte) (int, error) { if r.c == nil { c, err := r.acceptWithDeadline() if err != nil { return 0, err } err = r.setConn(c) if err != nil { return 0, err } } r.c.SetReadDeadline(r.deadline) return r.c.Read(p) } func (r *unixConn) Write(_ []byte) (int, error) { panic("unimplemented") } func (r *unixConn) LocalAddr() net.Addr { panic("unimplemented") } func (r *unixConn) RemoteAddr() net.Addr { panic("unimplemented") } func (r *unixConn) SetDeadline(_ time.Time) error { panic("unimplemented") } func (r *unixConn) SetReadDeadline(t time.Time) error { r.deadline = t return nil } func (r *unixConn) SetWriteDeadline(_ time.Time) error { panic("unimplemented") } // CreateConn creates a Unix socket connection. func CreateConn(u *url.URL) (net.Conn, error) { var pa string if u.Path != "" { pa = u.Path } else { pa = u.Host } if pa == "" { return nil, fmt.Errorf("invalid unix path") } os.Remove(pa) socket, err := net.Listen("unix", pa) if err != nil { return nil, err } return &unixConn{l: socket}, nil }