uapi_linux.go raw
1 /* SPDX-License-Identifier: MIT
2 *
3 * Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
4 */
5
6 package ipc
7
8 import (
9 "net"
10 "os"
11
12 "golang.org/x/sys/unix"
13 "golang.zx2c4.com/wireguard/rwcancel"
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 }
130