nanomsg.org/go/mangos/v2@v2.0.9-0.20200203084354-8a092611e461/transport/ipc/ipc_unix.go (about)

     1  // +build !windows,!plan9,!js
     2  
     3  // Copyright 2019 The Mangos Authors
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use file except in compliance with the License.
     7  // You may obtain a copy of the license at
     8  //
     9  //    http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  
    17  // Package ipc implements the IPC transport on top of UNIX domain sockets.
    18  // To enable it simply import it.
    19  package ipc
    20  
    21  import (
    22  	"net"
    23  	"os"
    24  	"sync"
    25  	"sync/atomic"
    26  	"syscall"
    27  	"time"
    28  
    29  	"nanomsg.org/go/mangos/v2"
    30  	"nanomsg.org/go/mangos/v2/transport"
    31  )
    32  
    33  const (
    34  	// Transport is a transport.Transport for IPC.
    35  	Transport = ipcTran(0)
    36  )
    37  
    38  func init() {
    39  	transport.RegisterTransport(Transport)
    40  }
    41  
    42  type dialer struct {
    43  	addr        *net.UnixAddr
    44  	proto       transport.ProtocolInfo
    45  	hs          transport.Handshaker
    46  	maxRecvSize int32
    47  }
    48  
    49  // Dial implements the Dialer Dial method
    50  func (d *dialer) Dial() (transport.Pipe, error) {
    51  
    52  	conn, err := net.DialUnix("unix", nil, d.addr)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	p := transport.NewConnPipeIPC(conn, d.proto)
    57  	p.SetMaxRecvSize(int(atomic.LoadInt32(&d.maxRecvSize)))
    58  	d.hs.Start(p)
    59  	return d.hs.Wait()
    60  }
    61  
    62  // SetOption implements Dialer SetOption method.
    63  func (d *dialer) SetOption(n string, v interface{}) error {
    64  	switch n {
    65  	case mangos.OptionMaxRecvSize:
    66  		if b, ok := v.(int); ok {
    67  			atomic.StoreInt32(&d.maxRecvSize, int32(b))
    68  			return nil
    69  		}
    70  		return mangos.ErrBadValue
    71  	}
    72  	return mangos.ErrBadOption
    73  }
    74  
    75  // GetOption implements Dialer GetOption method.
    76  func (d *dialer) GetOption(n string) (interface{}, error) {
    77  	switch n {
    78  	case mangos.OptionMaxRecvSize:
    79  		return int(atomic.LoadInt32(&d.maxRecvSize)), nil
    80  	}
    81  	return nil, mangos.ErrBadOption
    82  }
    83  
    84  type listener struct {
    85  	addr        *net.UnixAddr
    86  	proto       transport.ProtocolInfo
    87  	listener    *net.UnixListener
    88  	hs          transport.Handshaker
    89  	closeq      chan struct{}
    90  	closed      bool
    91  	maxRecvSize int32
    92  	once        sync.Once
    93  	lock        sync.Mutex
    94  }
    95  
    96  // Listen implements the PipeListener Listen method.
    97  func (l *listener) Listen() error {
    98  	l.lock.Lock()
    99  	defer l.lock.Unlock()
   100  	select {
   101  	case <-l.closeq:
   102  		return mangos.ErrClosed
   103  	default:
   104  	}
   105  	listener, err := net.ListenUnix("unix", l.addr)
   106  
   107  	if err != nil && (isSyscallError(err, syscall.EADDRINUSE) || isSyscallError(err, syscall.EEXIST)) {
   108  		l.removeStaleIPC()
   109  		listener, err = net.ListenUnix("unix", l.addr)
   110  		if isSyscallError(err, syscall.EADDRINUSE) || isSyscallError(err, syscall.EEXIST) {
   111  			err = mangos.ErrAddrInUse
   112  		}
   113  	}
   114  	if err != nil {
   115  		return err
   116  	}
   117  	l.listener = listener
   118  	go func() {
   119  		for {
   120  			conn, err := l.listener.AcceptUnix()
   121  			if err != nil {
   122  				select {
   123  				case <-l.closeq:
   124  					return
   125  				default:
   126  					continue
   127  				}
   128  			}
   129  			p := transport.NewConnPipeIPC(conn, l.proto)
   130  			p.SetMaxRecvSize(int(atomic.LoadInt32(&l.maxRecvSize)))
   131  			l.hs.Start(p)
   132  		}
   133  	}()
   134  	return nil
   135  }
   136  
   137  func (l *listener) Address() string {
   138  	return "ipc://" + l.addr.String()
   139  }
   140  
   141  // Accept implements the the PipeListener Accept method.
   142  func (l *listener) Accept() (transport.Pipe, error) {
   143  	l.lock.Lock()
   144  	if l.listener == nil {
   145  		l.lock.Unlock()
   146  		return nil, mangos.ErrClosed
   147  	}
   148  	l.lock.Unlock()
   149  	return l.hs.Wait()
   150  }
   151  
   152  // Close implements the PipeListener Close method.
   153  func (l *listener) Close() error {
   154  	l.once.Do(func() {
   155  		l.lock.Lock()
   156  		l.closed = true
   157  		if l.listener != nil {
   158  			l.listener.Close()
   159  		}
   160  		l.hs.Close()
   161  		close(l.closeq)
   162  		l.lock.Unlock()
   163  	})
   164  	return nil
   165  }
   166  
   167  // SetOption implements a stub PipeListener SetOption method.
   168  func (l *listener) SetOption(n string, v interface{}) error {
   169  	switch n {
   170  	case mangos.OptionMaxRecvSize:
   171  		if b, ok := v.(int); ok {
   172  			atomic.StoreInt32(&l.maxRecvSize, int32(b))
   173  			return nil
   174  		}
   175  		return mangos.ErrBadValue
   176  	}
   177  	return mangos.ErrBadOption
   178  }
   179  
   180  // GetOption implements a stub PipeListener GetOption method.
   181  func (l *listener) GetOption(n string) (interface{}, error) {
   182  	switch n {
   183  	case mangos.OptionMaxRecvSize:
   184  		return int(atomic.LoadInt32(&l.maxRecvSize)), nil
   185  	}
   186  	return nil, mangos.ErrBadOption
   187  }
   188  
   189  func (l *listener) removeStaleIPC() {
   190  	addr := l.addr.String()
   191  	// if it's not a socket, then leave it alone!
   192  	if st, err := os.Stat(addr); err != nil || st.Mode()&os.ModeType != os.ModeSocket {
   193  		return
   194  	}
   195  	conn, err := net.DialTimeout("unix", l.addr.String(), 100*time.Millisecond)
   196  	if err != nil && isSyscallError(err, syscall.ECONNREFUSED) {
   197  		os.Remove(l.addr.String())
   198  		return
   199  	}
   200  	if err == nil {
   201  		conn.Close()
   202  	}
   203  }
   204  
   205  type ipcTran int
   206  
   207  // Scheme implements the Transport Scheme method.
   208  func (ipcTran) Scheme() string {
   209  	return "ipc"
   210  }
   211  
   212  // NewDialer implements the Transport NewDialer method.
   213  func (t ipcTran) NewDialer(addr string, sock mangos.Socket) (transport.Dialer, error) {
   214  	var err error
   215  	d := &dialer{
   216  		proto: sock.Info(),
   217  		hs:    transport.NewConnHandshaker(),
   218  	}
   219  
   220  	if addr, err = transport.StripScheme(t, addr); err != nil {
   221  		return nil, err
   222  	}
   223  
   224  	// ignoring the errors, because this cannot fail on POSIX systems;
   225  	// the only error conditions are if the network is not "unix"
   226  	d.addr, _ = net.ResolveUnixAddr("unix", addr)
   227  	return d, nil
   228  }
   229  
   230  // NewListener implements the Transport NewListener method.
   231  func (t ipcTran) NewListener(addr string, sock mangos.Socket) (transport.Listener, error) {
   232  	var err error
   233  	l := &listener{
   234  		proto:  sock.Info(),
   235  		closeq: make(chan struct{}),
   236  		hs:     transport.NewConnHandshaker(),
   237  	}
   238  
   239  	if addr, err = transport.StripScheme(t, addr); err != nil {
   240  		return nil, err
   241  	}
   242  
   243  	// ignoring the errors, as it cannot fail.
   244  	l.addr, _ = net.ResolveUnixAddr("unix", addr)
   245  	return l, nil
   246  }
   247  
   248  func isSyscallError(err error, code syscall.Errno) bool {
   249  	opErr, ok := err.(*net.OpError)
   250  	if !ok {
   251  		return false
   252  	}
   253  	syscallErr, ok := opErr.Err.(*os.SyscallError)
   254  	if !ok {
   255  		return false
   256  	}
   257  	errno, ok := syscallErr.Err.(syscall.Errno)
   258  	if !ok {
   259  		return false
   260  	}
   261  	if errno == code {
   262  		return true
   263  	}
   264  	return false
   265  }