github.com/tailscale/wireguard-go@v0.0.20201119-0.20210522003738-46b531feb08a/ipc/uapi_linux.go (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved. 4 */ 5 6 package ipc 7 8 import ( 9 "net" 10 "os" 11 12 "github.com/tailscale/wireguard-go/rwcancel" 13 "golang.org/x/sys/unix" 14 ) 15 16 type UAPIListener struct { 17 listener net.Listener // unix socket listener 18 connNew chan net.Conn 19 connErr chan error 20 inotifyFd int 21 inotifyRWCancel *rwcancel.RWCancel 22 } 23 24 func (l *UAPIListener) Accept() (net.Conn, error) { 25 for { 26 select { 27 case conn := <-l.connNew: 28 return conn, nil 29 30 case err := <-l.connErr: 31 return nil, err 32 } 33 } 34 } 35 36 func (l *UAPIListener) Close() error { 37 err1 := unix.Close(l.inotifyFd) 38 err2 := l.inotifyRWCancel.Cancel() 39 err3 := l.listener.Close() 40 if err1 != nil { 41 return err1 42 } 43 if err2 != nil { 44 return err2 45 } 46 return err3 47 } 48 49 func (l *UAPIListener) Addr() net.Addr { 50 return l.listener.Addr() 51 } 52 53 func UAPIListen(name string, file *os.File) (net.Listener, error) { 54 55 // wrap file in listener 56 57 listener, err := net.FileListener(file) 58 if err != nil { 59 return nil, err 60 } 61 62 if unixListener, ok := listener.(*net.UnixListener); ok { 63 unixListener.SetUnlinkOnClose(true) 64 } 65 66 uapi := &UAPIListener{ 67 listener: listener, 68 connNew: make(chan net.Conn, 1), 69 connErr: make(chan error, 1), 70 } 71 72 // watch for deletion of socket 73 74 socketPath := sockPath(name) 75 76 uapi.inotifyFd, err = unix.InotifyInit() 77 if err != nil { 78 return nil, err 79 } 80 81 _, err = unix.InotifyAddWatch( 82 uapi.inotifyFd, 83 socketPath, 84 unix.IN_ATTRIB| 85 unix.IN_DELETE| 86 unix.IN_DELETE_SELF, 87 ) 88 89 if err != nil { 90 return nil, err 91 } 92 93 uapi.inotifyRWCancel, err = rwcancel.NewRWCancel(uapi.inotifyFd) 94 if err != nil { 95 unix.Close(uapi.inotifyFd) 96 return nil, err 97 } 98 99 go func(l *UAPIListener) { 100 var buff [0]byte 101 for { 102 defer uapi.inotifyRWCancel.Close() 103 // start with lstat to avoid race condition 104 if _, err := os.Lstat(socketPath); os.IsNotExist(err) { 105 l.connErr <- err 106 return 107 } 108 _, err := uapi.inotifyRWCancel.Read(buff[:]) 109 if err != nil { 110 l.connErr <- err 111 return 112 } 113 } 114 }(uapi) 115 116 // watch for new connections 117 118 go func(l *UAPIListener) { 119 for { 120 conn, err := l.listener.Accept() 121 if err != nil { 122 l.connErr <- err 123 break 124 } 125 l.connNew <- conn 126 } 127 }(uapi) 128 129 return uapi, nil 130 }