github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/pkg/client/socket/sockets_unix.go (about)

     1  //go:build !windows
     2  // +build !windows
     3  
     4  package socket
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"net"
    11  	"os"
    12  	"time"
    13  
    14  	"golang.org/x/sys/unix"
    15  	"google.golang.org/grpc"
    16  	"google.golang.org/grpc/credentials/insecure"
    17  
    18  	"github.com/telepresenceio/telepresence/v2/pkg/proc"
    19  )
    20  
    21  // userDaemonPath is the path used when communicating to the user daemon process.
    22  func userDaemonPath(ctx context.Context) string {
    23  	return "/tmp/telepresence-connector.socket"
    24  }
    25  
    26  // rootDaemonPath is the path used when communicating to the root daemon process.
    27  func rootDaemonPath(ctx context.Context) string {
    28  	return "/var/run/telepresence-daemon.socket"
    29  }
    30  
    31  func dial(ctx context.Context, socketName string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
    32  	ctx, cancel := context.WithTimeout(ctx, 5*time.Second) // FIXME(lukeshu): Make this configurable
    33  	defer cancel()
    34  	for firstTry := true; ; firstTry = false {
    35  		conn, err := grpc.DialContext(ctx, "unix:"+socketName, append([]grpc.DialOption{
    36  			grpc.WithTransportCredentials(insecure.NewCredentials()),
    37  			grpc.WithNoProxy(),
    38  			grpc.WithBlock(),
    39  			grpc.FailOnNonTempDialError(true),
    40  		}, opts...)...)
    41  		if err == nil {
    42  			return conn, nil
    43  		}
    44  
    45  		if firstTry && errors.Is(err, unix.ECONNREFUSED) {
    46  			// Socket exists but doesn't accept connections. This usually means that the process
    47  			// terminated ungracefully. To remedy this, we make an attempt to remove the socket
    48  			// and dial again.
    49  			if rmErr := os.Remove(socketName); rmErr != nil {
    50  				err = fmt.Errorf("%w (socket rm failed with %v)", err, rmErr)
    51  			} else {
    52  				continue
    53  			}
    54  		}
    55  
    56  		if err == context.DeadlineExceeded {
    57  			// grpc.DialContext doesn't wrap context.DeadlineExceeded with any useful
    58  			// information at all.  Fix that.
    59  			err = &net.OpError{
    60  				Op:  "dial",
    61  				Net: "unix",
    62  				Addr: &net.UnixAddr{
    63  					Name: socketName,
    64  					Net:  "unix",
    65  				},
    66  				Err: fmt.Errorf("socket exists but is not responding: %w", err),
    67  			}
    68  		}
    69  		// Add some Telepresence-specific commentary on what specific common errors mean.
    70  		switch {
    71  		case errors.Is(err, context.DeadlineExceeded):
    72  			err = fmt.Errorf("%w; this usually means that the process has locked up", err)
    73  		case errors.Is(err, unix.ECONNREFUSED):
    74  			err = fmt.Errorf("%w; this usually means that the process has terminated ungracefully", err)
    75  		case errors.Is(err, os.ErrNotExist):
    76  			err = fmt.Errorf("%w; this usually means that the process is not running", err)
    77  		}
    78  		return nil, err
    79  	}
    80  }
    81  
    82  func listen(_ context.Context, processName, socketName string) (net.Listener, error) {
    83  	if proc.IsAdmin() {
    84  		origUmask := unix.Umask(0)
    85  		defer unix.Umask(origUmask)
    86  	}
    87  	listener, err := net.Listen("unix", socketName)
    88  	if err != nil {
    89  		if errors.Is(err, unix.EADDRINUSE) {
    90  			err = fmt.Errorf("socket %q exists so the %s is either already running or terminated ungracefully", socketName, processName)
    91  		}
    92  		return nil, err
    93  	}
    94  	// Don't have dhttp.ServerConfig.Serve unlink the socket; defer unlinking the socket
    95  	// until the process exits.
    96  	listener.(*net.UnixListener).SetUnlinkOnClose(false)
    97  	return listener, nil
    98  }
    99  
   100  // exists returns true if a socket is found at the given path.
   101  func exists(path string) (bool, error) {
   102  	s, err := os.Stat(path)
   103  	if err != nil {
   104  		if os.IsNotExist(err) {
   105  			err = nil
   106  		}
   107  		return false, err
   108  	}
   109  	if s.Mode()&os.ModeSocket == 0 {
   110  		return false, fmt.Errorf("%q is not a socket", path)
   111  	}
   112  	return true, nil
   113  }