github.com/AbhinandanKurakure/podman/v3@v3.4.10/libpod/oci_conmon_exec_linux.go (about)

     1  package libpod
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strings"
    11  	"syscall"
    12  	"time"
    13  
    14  	"github.com/containers/common/pkg/capabilities"
    15  	"github.com/containers/common/pkg/config"
    16  	"github.com/containers/podman/v3/libpod/define"
    17  	"github.com/containers/podman/v3/pkg/errorhandling"
    18  	"github.com/containers/podman/v3/pkg/lookup"
    19  	"github.com/containers/podman/v3/pkg/util"
    20  	"github.com/containers/podman/v3/utils"
    21  	spec "github.com/opencontainers/runtime-spec/specs-go"
    22  	"github.com/pkg/errors"
    23  	"github.com/sirupsen/logrus"
    24  	"golang.org/x/sys/unix"
    25  )
    26  
    27  // ExecContainer executes a command in a running container
    28  func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams, newSize *define.TerminalSize) (int, chan error, error) {
    29  	if options == nil {
    30  		return -1, nil, errors.Wrapf(define.ErrInvalidArg, "must provide an ExecOptions struct to ExecContainer")
    31  	}
    32  	if len(options.Cmd) == 0 {
    33  		return -1, nil, errors.Wrapf(define.ErrInvalidArg, "must provide a command to execute")
    34  	}
    35  
    36  	if sessionID == "" {
    37  		return -1, nil, errors.Wrapf(define.ErrEmptyID, "must provide a session ID for exec")
    38  	}
    39  
    40  	// TODO: Should we default this to false?
    41  	// Or maybe make streams mandatory?
    42  	attachStdin := true
    43  	if streams != nil {
    44  		attachStdin = streams.AttachInput
    45  	}
    46  
    47  	var ociLog string
    48  	if logrus.GetLevel() != logrus.DebugLevel && r.supportsJSON {
    49  		ociLog = c.execOCILog(sessionID)
    50  	}
    51  
    52  	execCmd, pipes, err := r.startExec(c, sessionID, options, attachStdin, ociLog)
    53  	if err != nil {
    54  		return -1, nil, err
    55  	}
    56  
    57  	// Only close sync pipe. Start and attach are consumed in the attach
    58  	// goroutine.
    59  	defer func() {
    60  		if pipes.syncPipe != nil && !pipes.syncClosed {
    61  			errorhandling.CloseQuiet(pipes.syncPipe)
    62  			pipes.syncClosed = true
    63  		}
    64  	}()
    65  
    66  	// TODO Only create if !detach
    67  	// Attach to the container before starting it
    68  	attachChan := make(chan error)
    69  	go func() {
    70  		// attachToExec is responsible for closing pipes
    71  		attachChan <- c.attachToExec(streams, options.DetachKeys, sessionID, pipes.startPipe, pipes.attachPipe, newSize)
    72  		close(attachChan)
    73  	}()
    74  
    75  	if err := execCmd.Wait(); err != nil {
    76  		return -1, nil, errors.Wrapf(err, "cannot run conmon")
    77  	}
    78  
    79  	pid, err := readConmonPipeData(pipes.syncPipe, ociLog)
    80  
    81  	return pid, attachChan, err
    82  }
    83  
    84  // ExecContainerHTTP executes a new command in an existing container and
    85  // forwards its standard streams over an attach
    86  func (r *ConmonOCIRuntime) ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, req *http.Request, w http.ResponseWriter,
    87  	streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, newSize *define.TerminalSize) (int, chan error, error) {
    88  	if streams != nil {
    89  		if !streams.Stdin && !streams.Stdout && !streams.Stderr {
    90  			return -1, nil, errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to")
    91  		}
    92  	}
    93  
    94  	if options == nil {
    95  		return -1, nil, errors.Wrapf(define.ErrInvalidArg, "must provide exec options to ExecContainerHTTP")
    96  	}
    97  
    98  	detachString := config.DefaultDetachKeys
    99  	if options.DetachKeys != nil {
   100  		detachString = *options.DetachKeys
   101  	}
   102  	detachKeys, err := processDetachKeys(detachString)
   103  	if err != nil {
   104  		return -1, nil, err
   105  	}
   106  
   107  	// TODO: Should we default this to false?
   108  	// Or maybe make streams mandatory?
   109  	attachStdin := true
   110  	if streams != nil {
   111  		attachStdin = streams.Stdin
   112  	}
   113  
   114  	var ociLog string
   115  	if logrus.GetLevel() != logrus.DebugLevel && r.supportsJSON {
   116  		ociLog = ctr.execOCILog(sessionID)
   117  	}
   118  
   119  	execCmd, pipes, err := r.startExec(ctr, sessionID, options, attachStdin, ociLog)
   120  	if err != nil {
   121  		return -1, nil, err
   122  	}
   123  
   124  	// Only close sync pipe. Start and attach are consumed in the attach
   125  	// goroutine.
   126  	defer func() {
   127  		if pipes.syncPipe != nil && !pipes.syncClosed {
   128  			errorhandling.CloseQuiet(pipes.syncPipe)
   129  			pipes.syncClosed = true
   130  		}
   131  	}()
   132  
   133  	attachChan := make(chan error)
   134  	conmonPipeDataChan := make(chan conmonPipeData)
   135  	go func() {
   136  		// attachToExec is responsible for closing pipes
   137  		attachChan <- attachExecHTTP(ctr, sessionID, req, w, streams, pipes, detachKeys, options.Terminal, cancel, hijackDone, holdConnOpen, execCmd, conmonPipeDataChan, ociLog, newSize)
   138  		close(attachChan)
   139  	}()
   140  
   141  	// NOTE: the channel is needed to communicate conmon's data.  In case
   142  	// of an error, the error will be written on the hijacked http
   143  	// connection such that remote clients will receive the error.
   144  	pipeData := <-conmonPipeDataChan
   145  
   146  	return pipeData.pid, attachChan, pipeData.err
   147  }
   148  
   149  // conmonPipeData contains the data when reading from conmon's pipe.
   150  type conmonPipeData struct {
   151  	pid int
   152  	err error
   153  }
   154  
   155  // ExecContainerDetached executes a command in a running container, but does
   156  // not attach to it.
   157  func (r *ConmonOCIRuntime) ExecContainerDetached(ctr *Container, sessionID string, options *ExecOptions, stdin bool) (int, error) {
   158  	if options == nil {
   159  		return -1, errors.Wrapf(define.ErrInvalidArg, "must provide exec options to ExecContainerHTTP")
   160  	}
   161  
   162  	var ociLog string
   163  	if logrus.GetLevel() != logrus.DebugLevel && r.supportsJSON {
   164  		ociLog = ctr.execOCILog(sessionID)
   165  	}
   166  
   167  	execCmd, pipes, err := r.startExec(ctr, sessionID, options, stdin, ociLog)
   168  	if err != nil {
   169  		return -1, err
   170  	}
   171  
   172  	defer func() {
   173  		pipes.cleanup()
   174  	}()
   175  
   176  	// Wait for Conmon to tell us we're ready to attach.
   177  	// We aren't actually *going* to attach, but this means that we're good
   178  	// to proceed.
   179  	if _, err := readConmonPipeData(pipes.attachPipe, ""); err != nil {
   180  		return -1, err
   181  	}
   182  
   183  	// Start the exec session
   184  	if err := writeConmonPipeData(pipes.startPipe); err != nil {
   185  		return -1, err
   186  	}
   187  
   188  	// Wait for conmon to succeed, when return.
   189  	if err := execCmd.Wait(); err != nil {
   190  		return -1, errors.Wrapf(err, "cannot run conmon")
   191  	}
   192  
   193  	pid, err := readConmonPipeData(pipes.syncPipe, ociLog)
   194  
   195  	return pid, err
   196  }
   197  
   198  // ExecAttachResize resizes the TTY of the given exec session.
   199  func (r *ConmonOCIRuntime) ExecAttachResize(ctr *Container, sessionID string, newSize define.TerminalSize) error {
   200  	controlFile, err := openControlFile(ctr, ctr.execBundlePath(sessionID))
   201  	if err != nil {
   202  		return err
   203  	}
   204  	defer controlFile.Close()
   205  
   206  	if _, err = fmt.Fprintf(controlFile, "%d %d %d\n", 1, newSize.Height, newSize.Width); err != nil {
   207  		return errors.Wrapf(err, "failed to write to ctl file to resize terminal")
   208  	}
   209  
   210  	return nil
   211  }
   212  
   213  // ExecStopContainer stops a given exec session in a running container.
   214  func (r *ConmonOCIRuntime) ExecStopContainer(ctr *Container, sessionID string, timeout uint) error {
   215  	pid, err := ctr.getExecSessionPID(sessionID)
   216  	if err != nil {
   217  		return err
   218  	}
   219  
   220  	logrus.Debugf("Going to stop container %s exec session %s", ctr.ID(), sessionID)
   221  
   222  	// Is the session dead?
   223  	// Ping the PID with signal 0 to see if it still exists.
   224  	if err := unix.Kill(pid, 0); err != nil {
   225  		if err == unix.ESRCH {
   226  			return nil
   227  		}
   228  		return errors.Wrapf(err, "error pinging container %s exec session %s PID %d with signal 0", ctr.ID(), sessionID, pid)
   229  	}
   230  
   231  	if timeout > 0 {
   232  		// Use SIGTERM by default, then SIGSTOP after timeout.
   233  		logrus.Debugf("Killing exec session %s (PID %d) of container %s with SIGTERM", sessionID, pid, ctr.ID())
   234  		if err := unix.Kill(pid, unix.SIGTERM); err != nil {
   235  			if err == unix.ESRCH {
   236  				return nil
   237  			}
   238  			return errors.Wrapf(err, "error killing container %s exec session %s PID %d with SIGTERM", ctr.ID(), sessionID, pid)
   239  		}
   240  
   241  		// Wait for the PID to stop
   242  		if err := waitPidStop(pid, time.Duration(timeout)*time.Second); err != nil {
   243  			logrus.Infof("Timed out waiting for container %s exec session %s to stop, resorting to SIGKILL: %v", ctr.ID(), sessionID, err)
   244  		} else {
   245  			// No error, container is dead
   246  			return nil
   247  		}
   248  	}
   249  
   250  	// SIGTERM did not work. On to SIGKILL.
   251  	logrus.Debugf("Killing exec session %s (PID %d) of container %s with SIGKILL", sessionID, pid, ctr.ID())
   252  	if err := unix.Kill(pid, unix.SIGTERM); err != nil {
   253  		if err == unix.ESRCH {
   254  			return nil
   255  		}
   256  		return errors.Wrapf(err, "error killing container %s exec session %s PID %d with SIGKILL", ctr.ID(), sessionID, pid)
   257  	}
   258  
   259  	// Wait for the PID to stop
   260  	if err := waitPidStop(pid, killContainerTimeout*time.Second); err != nil {
   261  		return errors.Wrapf(err, "timed out waiting for container %s exec session %s PID %d to stop after SIGKILL", ctr.ID(), sessionID, pid)
   262  	}
   263  
   264  	return nil
   265  }
   266  
   267  // ExecUpdateStatus checks if the given exec session is still running.
   268  func (r *ConmonOCIRuntime) ExecUpdateStatus(ctr *Container, sessionID string) (bool, error) {
   269  	pid, err := ctr.getExecSessionPID(sessionID)
   270  	if err != nil {
   271  		return false, err
   272  	}
   273  
   274  	logrus.Debugf("Checking status of container %s exec session %s", ctr.ID(), sessionID)
   275  
   276  	// Is the session dead?
   277  	// Ping the PID with signal 0 to see if it still exists.
   278  	if err := unix.Kill(pid, 0); err != nil {
   279  		if err == unix.ESRCH {
   280  			return false, nil
   281  		}
   282  		return false, errors.Wrapf(err, "error pinging container %s exec session %s PID %d with signal 0", ctr.ID(), sessionID, pid)
   283  	}
   284  
   285  	return true, nil
   286  }
   287  
   288  // ExecAttachSocketPath is the path to a container's exec session attach socket.
   289  func (r *ConmonOCIRuntime) ExecAttachSocketPath(ctr *Container, sessionID string) (string, error) {
   290  	// We don't even use container, so don't validity check it
   291  	if sessionID == "" {
   292  		return "", errors.Wrapf(define.ErrInvalidArg, "must provide a valid session ID to get attach socket path")
   293  	}
   294  
   295  	return filepath.Join(ctr.execBundlePath(sessionID), "attach"), nil
   296  }
   297  
   298  // This contains pipes used by the exec API.
   299  type execPipes struct {
   300  	syncPipe     *os.File
   301  	syncClosed   bool
   302  	startPipe    *os.File
   303  	startClosed  bool
   304  	attachPipe   *os.File
   305  	attachClosed bool
   306  }
   307  
   308  func (p *execPipes) cleanup() {
   309  	if p.syncPipe != nil && !p.syncClosed {
   310  		errorhandling.CloseQuiet(p.syncPipe)
   311  		p.syncClosed = true
   312  	}
   313  	if p.startPipe != nil && !p.startClosed {
   314  		errorhandling.CloseQuiet(p.startPipe)
   315  		p.startClosed = true
   316  	}
   317  	if p.attachPipe != nil && !p.attachClosed {
   318  		errorhandling.CloseQuiet(p.attachPipe)
   319  		p.attachClosed = true
   320  	}
   321  }
   322  
   323  // Start an exec session's conmon parent from the given options.
   324  func (r *ConmonOCIRuntime) startExec(c *Container, sessionID string, options *ExecOptions, attachStdin bool, ociLog string) (_ *exec.Cmd, _ *execPipes, deferredErr error) {
   325  	pipes := new(execPipes)
   326  
   327  	if options == nil {
   328  		return nil, nil, errors.Wrapf(define.ErrInvalidArg, "must provide an ExecOptions struct to ExecContainer")
   329  	}
   330  	if len(options.Cmd) == 0 {
   331  		return nil, nil, errors.Wrapf(define.ErrInvalidArg, "must provide a command to execute")
   332  	}
   333  
   334  	if sessionID == "" {
   335  		return nil, nil, errors.Wrapf(define.ErrEmptyID, "must provide a session ID for exec")
   336  	}
   337  
   338  	// create sync pipe to receive the pid
   339  	parentSyncPipe, childSyncPipe, err := newPipe()
   340  	if err != nil {
   341  		return nil, nil, errors.Wrapf(err, "error creating socket pair")
   342  	}
   343  	pipes.syncPipe = parentSyncPipe
   344  
   345  	defer func() {
   346  		if deferredErr != nil {
   347  			pipes.cleanup()
   348  		}
   349  	}()
   350  
   351  	// create start pipe to set the cgroup before running
   352  	// attachToExec is responsible for closing parentStartPipe
   353  	childStartPipe, parentStartPipe, err := newPipe()
   354  	if err != nil {
   355  		return nil, nil, errors.Wrapf(err, "error creating socket pair")
   356  	}
   357  	pipes.startPipe = parentStartPipe
   358  
   359  	// create the attach pipe to allow attach socket to be created before
   360  	// $RUNTIME exec starts running. This is to make sure we can capture all output
   361  	// from the process through that socket, rather than half reading the log, half attaching to the socket
   362  	// attachToExec is responsible for closing parentAttachPipe
   363  	parentAttachPipe, childAttachPipe, err := newPipe()
   364  	if err != nil {
   365  		return nil, nil, errors.Wrapf(err, "error creating socket pair")
   366  	}
   367  	pipes.attachPipe = parentAttachPipe
   368  
   369  	childrenClosed := false
   370  	defer func() {
   371  		if !childrenClosed {
   372  			errorhandling.CloseQuiet(childSyncPipe)
   373  			errorhandling.CloseQuiet(childAttachPipe)
   374  			errorhandling.CloseQuiet(childStartPipe)
   375  		}
   376  	}()
   377  
   378  	runtimeDir, err := util.GetRuntimeDir()
   379  	if err != nil {
   380  		return nil, nil, err
   381  	}
   382  
   383  	finalEnv := make([]string, 0, len(options.Env))
   384  	for k, v := range options.Env {
   385  		finalEnv = append(finalEnv, fmt.Sprintf("%s=%s", k, v))
   386  	}
   387  
   388  	processFile, err := prepareProcessExec(c, options, finalEnv, sessionID)
   389  	if err != nil {
   390  		return nil, nil, err
   391  	}
   392  
   393  	args := r.sharedConmonArgs(c, sessionID, c.execBundlePath(sessionID), c.execPidPath(sessionID), c.execLogPath(sessionID), c.execExitFileDir(sessionID), ociLog, define.NoLogging, "")
   394  
   395  	if options.PreserveFDs > 0 {
   396  		args = append(args, formatRuntimeOpts("--preserve-fds", fmt.Sprintf("%d", options.PreserveFDs))...)
   397  	}
   398  
   399  	if options.Terminal {
   400  		args = append(args, "-t")
   401  	}
   402  
   403  	if attachStdin {
   404  		args = append(args, "-i")
   405  	}
   406  
   407  	// Append container ID and command
   408  	args = append(args, "-e")
   409  	// TODO make this optional when we can detach
   410  	args = append(args, "--exec-attach")
   411  	args = append(args, "--exec-process-spec", processFile.Name())
   412  
   413  	if len(options.ExitCommand) > 0 {
   414  		args = append(args, "--exit-command", options.ExitCommand[0])
   415  		for _, arg := range options.ExitCommand[1:] {
   416  			args = append(args, []string{"--exit-command-arg", arg}...)
   417  		}
   418  		if options.ExitCommandDelay > 0 {
   419  			args = append(args, []string{"--exit-delay", fmt.Sprintf("%d", options.ExitCommandDelay)}...)
   420  		}
   421  	}
   422  
   423  	logrus.WithFields(logrus.Fields{
   424  		"args": args,
   425  	}).Debugf("running conmon: %s", r.conmonPath)
   426  	execCmd := exec.Command(r.conmonPath, args...)
   427  
   428  	// TODO: This is commented because it doesn't make much sense in HTTP
   429  	// attach, and I'm not certain it does for non-HTTP attach as well.
   430  	// if streams != nil {
   431  	// 	// Don't add the InputStream to the execCmd. Instead, the data should be passed
   432  	// 	// through CopyDetachable
   433  	// 	if streams.AttachOutput {
   434  	// 		execCmd.Stdout = options.Streams.OutputStream
   435  	// 	}
   436  	// 	if streams.AttachError {
   437  	// 		execCmd.Stderr = options.Streams.ErrorStream
   438  	// 	}
   439  	// }
   440  
   441  	conmonEnv := r.configureConmonEnv(c, runtimeDir)
   442  
   443  	var filesToClose []*os.File
   444  	if options.PreserveFDs > 0 {
   445  		for fd := 3; fd < int(3+options.PreserveFDs); fd++ {
   446  			f := os.NewFile(uintptr(fd), fmt.Sprintf("fd-%d", fd))
   447  			filesToClose = append(filesToClose, f)
   448  			execCmd.ExtraFiles = append(execCmd.ExtraFiles, f)
   449  		}
   450  	}
   451  
   452  	// we don't want to step on users fds they asked to preserve
   453  	// Since 0-2 are used for stdio, start the fds we pass in at preserveFDs+3
   454  	execCmd.Env = r.conmonEnv
   455  	execCmd.Env = append(execCmd.Env, fmt.Sprintf("_OCI_SYNCPIPE=%d", options.PreserveFDs+3), fmt.Sprintf("_OCI_STARTPIPE=%d", options.PreserveFDs+4), fmt.Sprintf("_OCI_ATTACHPIPE=%d", options.PreserveFDs+5))
   456  	execCmd.Env = append(execCmd.Env, conmonEnv...)
   457  
   458  	execCmd.ExtraFiles = append(execCmd.ExtraFiles, childSyncPipe, childStartPipe, childAttachPipe)
   459  	execCmd.Dir = c.execBundlePath(sessionID)
   460  	execCmd.SysProcAttr = &syscall.SysProcAttr{
   461  		Setpgid: true,
   462  	}
   463  
   464  	err = startCommandGivenSelinux(execCmd, c)
   465  
   466  	// We don't need children pipes  on the parent side
   467  	errorhandling.CloseQuiet(childSyncPipe)
   468  	errorhandling.CloseQuiet(childAttachPipe)
   469  	errorhandling.CloseQuiet(childStartPipe)
   470  	childrenClosed = true
   471  
   472  	if err != nil {
   473  		return nil, nil, errors.Wrapf(err, "cannot start container %s", c.ID())
   474  	}
   475  	if err := r.moveConmonToCgroupAndSignal(c, execCmd, parentStartPipe); err != nil {
   476  		return nil, nil, err
   477  	}
   478  
   479  	// These fds were passed down to the runtime.  Close them
   480  	// and not interfere
   481  	for _, f := range filesToClose {
   482  		errorhandling.CloseQuiet(f)
   483  	}
   484  
   485  	return execCmd, pipes, nil
   486  }
   487  
   488  // Attach to a container over HTTP
   489  func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, pipes *execPipes, detachKeys []byte, isTerminal bool, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, execCmd *exec.Cmd, conmonPipeDataChan chan<- conmonPipeData, ociLog string, newSize *define.TerminalSize) (deferredErr error) {
   490  	// NOTE: As you may notice, the attach code is quite complex.
   491  	// Many things happen concurrently and yet are interdependent.
   492  	// If you ever change this function, make sure to write to the
   493  	// conmonPipeDataChan in case of an error.
   494  
   495  	if pipes == nil || pipes.startPipe == nil || pipes.attachPipe == nil {
   496  		err := errors.Wrapf(define.ErrInvalidArg, "must provide a start and attach pipe to finish an exec attach")
   497  		conmonPipeDataChan <- conmonPipeData{-1, err}
   498  		return err
   499  	}
   500  
   501  	defer func() {
   502  		if !pipes.startClosed {
   503  			errorhandling.CloseQuiet(pipes.startPipe)
   504  			pipes.startClosed = true
   505  		}
   506  		if !pipes.attachClosed {
   507  			errorhandling.CloseQuiet(pipes.attachPipe)
   508  			pipes.attachClosed = true
   509  		}
   510  	}()
   511  
   512  	logrus.Debugf("Attaching to container %s exec session %s", c.ID(), sessionID)
   513  
   514  	// set up the socket path, such that it is the correct length and location for exec
   515  	sockPath, err := c.execAttachSocketPath(sessionID)
   516  	if err != nil {
   517  		conmonPipeDataChan <- conmonPipeData{-1, err}
   518  		return err
   519  	}
   520  
   521  	// 2: read from attachFd that the parent process has set up the console socket
   522  	if _, err := readConmonPipeData(pipes.attachPipe, ""); err != nil {
   523  		conmonPipeDataChan <- conmonPipeData{-1, err}
   524  		return err
   525  	}
   526  
   527  	// resize before we start the container process
   528  	if newSize != nil {
   529  		err = c.ociRuntime.ExecAttachResize(c, sessionID, *newSize)
   530  		if err != nil {
   531  			logrus.Warn("resize failed", err)
   532  		}
   533  	}
   534  
   535  	// 2: then attach
   536  	conn, err := openUnixSocket(sockPath)
   537  	if err != nil {
   538  		conmonPipeDataChan <- conmonPipeData{-1, err}
   539  		return errors.Wrapf(err, "failed to connect to container's attach socket: %v", sockPath)
   540  	}
   541  	defer func() {
   542  		if err := conn.Close(); err != nil {
   543  			logrus.Errorf("unable to close socket: %q", err)
   544  		}
   545  	}()
   546  
   547  	attachStdout := true
   548  	attachStderr := true
   549  	attachStdin := true
   550  	if streams != nil {
   551  		attachStdout = streams.Stdout
   552  		attachStderr = streams.Stderr
   553  		attachStdin = streams.Stdin
   554  	}
   555  
   556  	// Perform hijack
   557  	hijacker, ok := w.(http.Hijacker)
   558  	if !ok {
   559  		conmonPipeDataChan <- conmonPipeData{-1, err}
   560  		return errors.Errorf("unable to hijack connection")
   561  	}
   562  
   563  	httpCon, httpBuf, err := hijacker.Hijack()
   564  	if err != nil {
   565  		conmonPipeDataChan <- conmonPipeData{-1, err}
   566  		return errors.Wrapf(err, "error hijacking connection")
   567  	}
   568  
   569  	hijackDone <- true
   570  
   571  	// Write a header to let the client know what happened
   572  	writeHijackHeader(r, httpBuf)
   573  
   574  	// Force a flush after the header is written.
   575  	if err := httpBuf.Flush(); err != nil {
   576  		conmonPipeDataChan <- conmonPipeData{-1, err}
   577  		return errors.Wrapf(err, "error flushing HTTP hijack header")
   578  	}
   579  
   580  	go func() {
   581  		// Wait for conmon to succeed, when return.
   582  		if err := execCmd.Wait(); err != nil {
   583  			conmonPipeDataChan <- conmonPipeData{-1, err}
   584  		} else {
   585  			pid, err := readConmonPipeData(pipes.syncPipe, ociLog)
   586  			if err != nil {
   587  				hijackWriteError(err, c.ID(), isTerminal, httpBuf)
   588  				conmonPipeDataChan <- conmonPipeData{pid, err}
   589  			} else {
   590  				conmonPipeDataChan <- conmonPipeData{pid, err}
   591  			}
   592  		}
   593  		// We need to hold the connection open until the complete exec
   594  		// function has finished. This channel will be closed in a defer
   595  		// in that function, so we can wait for it here.
   596  		// Can't be a defer, because this would block the function from
   597  		// returning.
   598  		<-holdConnOpen
   599  		hijackWriteErrorAndClose(deferredErr, c.ID(), isTerminal, httpCon, httpBuf)
   600  	}()
   601  
   602  	stdoutChan := make(chan error)
   603  	stdinChan := make(chan error)
   604  
   605  	// Next, STDIN. Avoid entirely if attachStdin unset.
   606  	if attachStdin {
   607  		go func() {
   608  			logrus.Debugf("Beginning STDIN copy")
   609  			_, err := utils.CopyDetachable(conn, httpBuf, detachKeys)
   610  			logrus.Debugf("STDIN copy completed")
   611  			stdinChan <- err
   612  		}()
   613  	}
   614  
   615  	// 4: send start message to child
   616  	if err := writeConmonPipeData(pipes.startPipe); err != nil {
   617  		return err
   618  	}
   619  
   620  	// Handle STDOUT/STDERR *after* start message is sent
   621  	go func() {
   622  		var err error
   623  		if isTerminal {
   624  			// Hack: return immediately if attachStdout not set to
   625  			// emulate Docker.
   626  			// Basically, when terminal is set, STDERR goes nowhere.
   627  			// Everything does over STDOUT.
   628  			// Therefore, if not attaching STDOUT - we'll never copy
   629  			// anything from here.
   630  			logrus.Debugf("Performing terminal HTTP attach for container %s", c.ID())
   631  			if attachStdout {
   632  				err = httpAttachTerminalCopy(conn, httpBuf, c.ID())
   633  			}
   634  		} else {
   635  			logrus.Debugf("Performing non-terminal HTTP attach for container %s", c.ID())
   636  			err = httpAttachNonTerminalCopy(conn, httpBuf, c.ID(), attachStdin, attachStdout, attachStderr)
   637  		}
   638  		stdoutChan <- err
   639  		logrus.Debugf("STDOUT/ERR copy completed")
   640  	}()
   641  
   642  	for {
   643  		select {
   644  		case err := <-stdoutChan:
   645  			if err != nil {
   646  				return err
   647  			}
   648  
   649  			return nil
   650  		case err := <-stdinChan:
   651  			if err != nil {
   652  				return err
   653  			}
   654  			// copy stdin is done, close it
   655  			if connErr := conn.CloseWrite(); connErr != nil {
   656  				logrus.Errorf("Unable to close conn: %v", connErr)
   657  			}
   658  		case <-cancel:
   659  			return nil
   660  		}
   661  	}
   662  }
   663  
   664  // prepareProcessExec returns the path of the process.json used in runc exec -p
   665  // caller is responsible to close the returned *os.File if needed.
   666  func prepareProcessExec(c *Container, options *ExecOptions, env []string, sessionID string) (*os.File, error) {
   667  	f, err := ioutil.TempFile(c.execBundlePath(sessionID), "exec-process-")
   668  	if err != nil {
   669  		return nil, err
   670  	}
   671  	pspec := new(spec.Process)
   672  	if err := JSONDeepCopy(c.config.Spec.Process, pspec); err != nil {
   673  		return nil, err
   674  	}
   675  	pspec.SelinuxLabel = c.config.ProcessLabel
   676  	pspec.Args = options.Cmd
   677  
   678  	// We need to default this to false else it will inherit terminal as true
   679  	// from the container.
   680  	pspec.Terminal = false
   681  	if options.Terminal {
   682  		pspec.Terminal = true
   683  	}
   684  	if len(env) > 0 {
   685  		pspec.Env = append(pspec.Env, env...)
   686  	}
   687  
   688  	// Add secret envs if they exist
   689  	manager, err := c.runtime.SecretsManager()
   690  	if err != nil {
   691  		return nil, err
   692  	}
   693  	for name, secr := range c.config.EnvSecrets {
   694  		_, data, err := manager.LookupSecretData(secr.Name)
   695  		if err != nil {
   696  			return nil, err
   697  		}
   698  		pspec.Env = append(pspec.Env, fmt.Sprintf("%s=%s", name, string(data)))
   699  	}
   700  
   701  	if options.Cwd != "" {
   702  		pspec.Cwd = options.Cwd
   703  	}
   704  
   705  	var addGroups []string
   706  	var sgids []uint32
   707  
   708  	// if the user is empty, we should inherit the user that the container is currently running with
   709  	user := options.User
   710  	if user == "" {
   711  		logrus.Debugf("Set user to %s", c.config.User)
   712  		user = c.config.User
   713  		addGroups = c.config.Groups
   714  	}
   715  
   716  	overrides := c.getUserOverrides()
   717  	execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, user, overrides)
   718  	if err != nil {
   719  		return nil, err
   720  	}
   721  
   722  	if len(addGroups) > 0 {
   723  		sgids, err = lookup.GetContainerGroups(addGroups, c.state.Mountpoint, overrides)
   724  		if err != nil {
   725  			return nil, errors.Wrapf(err, "error looking up supplemental groups for container %s exec session %s", c.ID(), sessionID)
   726  		}
   727  	}
   728  
   729  	// If user was set, look it up in the container to get a UID to use on
   730  	// the host
   731  	if user != "" || len(sgids) > 0 {
   732  		if user != "" {
   733  			for _, sgid := range execUser.Sgids {
   734  				sgids = append(sgids, uint32(sgid))
   735  			}
   736  		}
   737  		processUser := spec.User{
   738  			UID:            uint32(execUser.Uid),
   739  			GID:            uint32(execUser.Gid),
   740  			AdditionalGids: sgids,
   741  		}
   742  
   743  		pspec.User = processUser
   744  	}
   745  
   746  	ctrSpec, err := c.specFromState()
   747  	if err != nil {
   748  		return nil, err
   749  	}
   750  
   751  	allCaps, err := capabilities.BoundingSet()
   752  	if err != nil {
   753  		return nil, err
   754  	}
   755  	if options.Privileged {
   756  		pspec.Capabilities.Bounding = allCaps
   757  	} else {
   758  		pspec.Capabilities.Bounding = ctrSpec.Process.Capabilities.Bounding
   759  	}
   760  
   761  	// Always unset the inheritable capabilities similarly to what the Linux kernel does
   762  	// They are used only when using capabilities with uid != 0.
   763  	pspec.Capabilities.Inheritable = []string{}
   764  
   765  	if execUser.Uid == 0 {
   766  		pspec.Capabilities.Effective = pspec.Capabilities.Bounding
   767  		pspec.Capabilities.Permitted = pspec.Capabilities.Bounding
   768  	} else {
   769  		if user == c.config.User {
   770  			pspec.Capabilities.Effective = ctrSpec.Process.Capabilities.Effective
   771  			pspec.Capabilities.Inheritable = ctrSpec.Process.Capabilities.Effective
   772  			pspec.Capabilities.Permitted = ctrSpec.Process.Capabilities.Effective
   773  			pspec.Capabilities.Ambient = ctrSpec.Process.Capabilities.Effective
   774  		}
   775  	}
   776  
   777  	hasHomeSet := false
   778  	for _, s := range pspec.Env {
   779  		if strings.HasPrefix(s, "HOME=") {
   780  			hasHomeSet = true
   781  			break
   782  		}
   783  	}
   784  	if !hasHomeSet {
   785  		pspec.Env = append(pspec.Env, fmt.Sprintf("HOME=%s", execUser.Home))
   786  	}
   787  
   788  	processJSON, err := json.Marshal(pspec)
   789  	if err != nil {
   790  		return nil, err
   791  	}
   792  
   793  	if err := ioutil.WriteFile(f.Name(), processJSON, 0644); err != nil {
   794  		return nil, err
   795  	}
   796  	return f, nil
   797  }