github.com/sagernet/wireguard-go@v0.0.0-20231215174105-89dec3b2f3e8/ipc/uapi_linux.go (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved. 4 */ 5 6 package ipc 7 8 import ( 9 "net" 10 "os" 11 12 "github.com/sagernet/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 // wrap file in listener 55 56 listener, err := net.FileListener(file) 57 if err != nil { 58 return nil, err 59 } 60 61 if unixListener, ok := listener.(*net.UnixListener); ok { 62 unixListener.SetUnlinkOnClose(true) 63 } 64 65 uapi := &UAPIListener{ 66 listener: listener, 67 connNew: make(chan net.Conn, 1), 68 connErr: make(chan error, 1), 69 } 70 71 // watch for deletion of socket 72 73 socketPath := sockPath(name) 74 75 uapi.inotifyFd, err = unix.InotifyInit() 76 if err != nil { 77 return nil, err 78 } 79 80 _, err = unix.InotifyAddWatch( 81 uapi.inotifyFd, 82 socketPath, 83 unix.IN_ATTRIB| 84 unix.IN_DELETE| 85 unix.IN_DELETE_SELF, 86 ) 87 88 if err != nil { 89 return nil, err 90 } 91 92 uapi.inotifyRWCancel, err = rwcancel.NewRWCancel(uapi.inotifyFd) 93 if err != nil { 94 unix.Close(uapi.inotifyFd) 95 return nil, err 96 } 97 98 go func(l *UAPIListener) { 99 var buf [0]byte 100 for { 101 defer uapi.inotifyRWCancel.Close() 102 // start with lstat to avoid race condition 103 if _, err := os.Lstat(socketPath); os.IsNotExist(err) { 104 l.connErr <- err 105 return 106 } 107 _, err := uapi.inotifyRWCancel.Read(buf[:]) 108 if err != nil { 109 l.connErr <- err 110 return 111 } 112 } 113 }(uapi) 114 115 // watch for new connections 116 117 go func(l *UAPIListener) { 118 for { 119 conn, err := l.listener.Accept() 120 if err != nil { 121 l.connErr <- err 122 break 123 } 124 l.connNew <- conn 125 } 126 }(uapi) 127 128 return uapi, nil 129 }