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  }