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 }