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