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 }