github.com/apptainer/singularity@v3.1.1+incompatible/internal/pkg/util/exec/pipe_linux.go (about)

     1  // Copyright (c) 2019, Sylabs Inc. All rights reserved.
     2  // This software is licensed under a 3-clause BSD license. Please consult the
     3  // LICENSE.md file distributed with the sources of this project regarding your
     4  // rights to use or distribute this software.
     5  
     6  package exec
     7  
     8  import (
     9  	"fmt"
    10  	"os/exec"
    11  	"syscall"
    12  
    13  	"github.com/sylabs/singularity/internal/pkg/sylog"
    14  )
    15  
    16  // Pipe execute a command with arguments and pass data over pipe
    17  func Pipe(command string, args []string, env []string, data []byte) error {
    18  	pipeEnv, err := SetPipe(data)
    19  	if err != nil {
    20  		return err
    21  	}
    22  
    23  	env = append(env, pipeEnv)
    24  	err = syscall.Exec(command, args, env)
    25  	if err != nil {
    26  		return fmt.Errorf("command %s execution failed: %s", command, err)
    27  	}
    28  
    29  	return nil
    30  }
    31  
    32  // PipeCommand creates an exec.Command struct which will execute the starter binary
    33  func PipeCommand(command string, args []string, env []string, data []byte) (*exec.Cmd, error) {
    34  	pipeEnv, err := SetPipe(data)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  
    39  	env = append(env, pipeEnv)
    40  
    41  	c := &exec.Cmd{
    42  		Path: command,
    43  		Args: args,
    44  		Env:  env,
    45  	}
    46  	return c, nil
    47  }
    48  
    49  // setPipe sets a pipe communication channel for JSON configuration data and returns
    50  // the file descriptor of the read side
    51  func setPipe(data []byte) (int, error) {
    52  	fd, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
    53  	if err != nil {
    54  		return -1, fmt.Errorf("failed to create communication pipe: %s", err)
    55  	}
    56  
    57  	if curSize, err := syscall.GetsockoptInt(fd[0], syscall.SOL_SOCKET, syscall.SO_SNDBUF); err == nil {
    58  		if curSize <= 65536 {
    59  			sylog.Warningf("current buffer size is %d, you may encounter some issues", curSize)
    60  			sylog.Warningf("the minimum recommended value is 65536, you can adjust this value with:")
    61  			sylog.Warningf("\"echo 65536 > /proc/sys/net/core/wmem_default\"")
    62  		}
    63  	} else {
    64  		return -1, fmt.Errorf("failed to determine current pipe size: %s", err)
    65  	}
    66  
    67  	pipeFd, err := syscall.Dup(fd[1])
    68  	if err != nil {
    69  		return -1, fmt.Errorf("failed to duplicate pipe file descriptor: %s", err)
    70  	}
    71  
    72  	if n, err := syscall.Write(fd[0], data); err != nil || n != len(data) {
    73  		return -1, fmt.Errorf("failed to write data to stdin: %s", err)
    74  	}
    75  
    76  	return pipeFd, err
    77  }
    78  
    79  // SetPipe sets the PIPE_EXEC_FD environment variable containing the JSON configuration data
    80  func SetPipe(data []byte) (string, error) {
    81  	pipeFd, err := setPipe(data)
    82  	if err != nil {
    83  		return "", err
    84  	}
    85  	return fmt.Sprintf("PIPE_EXEC_FD=%d", pipeFd), nil
    86  }