uapi_bsd.go raw
1 //go:build darwin || freebsd || openbsd
2
3 /* SPDX-License-Identifier: MIT
4 *
5 * Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
6 */
7
8 package ipc
9
10 import (
11 "errors"
12 "net"
13 "os"
14 "unsafe"
15
16 "golang.org/x/sys/unix"
17 )
18
19 type UAPIListener struct {
20 listener net.Listener // unix socket listener
21 connNew chan net.Conn
22 connErr chan error
23 kqueueFd int
24 keventFd int
25 }
26
27 func (l *UAPIListener) Accept() (net.Conn, error) {
28 for {
29 select {
30 case conn := <-l.connNew:
31 return conn, nil
32
33 case err := <-l.connErr:
34 return nil, err
35 }
36 }
37 }
38
39 func (l *UAPIListener) Close() error {
40 err1 := unix.Close(l.kqueueFd)
41 err2 := unix.Close(l.keventFd)
42 err3 := l.listener.Close()
43 if err1 != nil {
44 return err1
45 }
46 if err2 != nil {
47 return err2
48 }
49 return err3
50 }
51
52 func (l *UAPIListener) Addr() net.Addr {
53 return l.listener.Addr()
54 }
55
56 func UAPIListen(name string, file *os.File) (net.Listener, error) {
57 // wrap file in listener
58
59 listener, err := net.FileListener(file)
60 if err != nil {
61 return nil, err
62 }
63
64 uapi := &UAPIListener{
65 listener: listener,
66 connNew: make(chan net.Conn, 1),
67 connErr: make(chan error, 1),
68 }
69
70 if unixListener, ok := listener.(*net.UnixListener); ok {
71 unixListener.SetUnlinkOnClose(true)
72 }
73
74 socketPath := sockPath(name)
75
76 // watch for deletion of socket
77
78 uapi.kqueueFd, err = unix.Kqueue()
79 if err != nil {
80 return nil, err
81 }
82 uapi.keventFd, err = unix.Open(socketDirectory, unix.O_RDONLY, 0)
83 if err != nil {
84 unix.Close(uapi.kqueueFd)
85 return nil, err
86 }
87
88 go func(l *UAPIListener) {
89 event := unix.Kevent_t{
90 Filter: unix.EVFILT_VNODE,
91 Flags: unix.EV_ADD | unix.EV_ENABLE | unix.EV_ONESHOT,
92 Fflags: unix.NOTE_WRITE,
93 }
94 // Allow this assignment to work with both the 32-bit and 64-bit version
95 // of the above struct. If you know another way, please submit a patch.
96 *(*uintptr)(unsafe.Pointer(&event.Ident)) = uintptr(uapi.keventFd)
97 events := make([]unix.Kevent_t, 1)
98 n := 1
99 var kerr error
100 for {
101 // start with lstat to avoid race condition
102 if _, err := os.Lstat(socketPath); os.IsNotExist(err) {
103 l.connErr <- err
104 return
105 }
106 if (kerr != nil || n != 1) && kerr != unix.EINTR {
107 if kerr != nil {
108 l.connErr <- kerr
109 } else {
110 l.connErr <- errors.New("kqueue returned empty")
111 }
112 return
113 }
114 n, kerr = unix.Kevent(uapi.kqueueFd, []unix.Kevent_t{event}, events, nil)
115 }
116 }(uapi)
117
118 // watch for new connections
119
120 go func(l *UAPIListener) {
121 for {
122 conn, err := l.listener.Accept()
123 if err != nil {
124 l.connErr <- err
125 break
126 }
127 l.connNew <- conn
128 }
129 }(uapi)
130
131 return uapi, nil
132 }
133