// AddProcess is the handler for adding a process to an already running // container. It's called through docker exec. func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendlyName string, procToAdd Process) error { clnt.lock(containerID) defer clnt.unlock(containerID) container, err := clnt.getContainer(containerID) if err != nil { return err } // Note we always tell HCS to // create stdout as it's required regardless of '-i' or '-t' options, so that // docker can always grab the output through logs. We also tell HCS to always // create stdin, even if it's not used - it will be closed shortly. Stderr // is only created if it we're not -t. createProcessParms := hcsshim.ProcessConfig{ EmulateConsole: procToAdd.Terminal, CreateStdInPipe: true, CreateStdOutPipe: true, CreateStdErrPipe: !procToAdd.Terminal, } createProcessParms.ConsoleSize[0] = uint(procToAdd.ConsoleSize.Height) createProcessParms.ConsoleSize[1] = uint(procToAdd.ConsoleSize.Width) // Take working directory from the process to add if it is defined, // otherwise take from the first process. if procToAdd.Cwd != "" { createProcessParms.WorkingDirectory = procToAdd.Cwd } else { createProcessParms.WorkingDirectory = container.ociSpec.Process.Cwd } // Configure the environment for the process createProcessParms.Environment = setupEnvironmentVariables(procToAdd.Env) createProcessParms.CommandLine = strings.Join(procToAdd.Args, " ") logrus.Debugf("libcontainerd: commandLine: %s", createProcessParms.CommandLine) // Start the command running in the container. var stdout, stderr io.ReadCloser var stdin io.WriteCloser newProcess, err := container.hcsContainer.CreateProcess(&createProcessParms) if err != nil { logrus.Errorf("libcontainerd: AddProcess(%s) CreateProcess() failed %s", containerID, err) return err } pid := newProcess.Pid() stdin, stdout, stderr, err = newProcess.Stdio() if err != nil { logrus.Errorf("libcontainerd: %s getting std pipes failed %s", containerID, err) return err } iopipe := &IOPipe{Terminal: procToAdd.Terminal} iopipe.Stdin = createStdInCloser(stdin, newProcess) // Convert io.ReadClosers to io.Readers if stdout != nil { iopipe.Stdout = openReaderFromPipe(stdout) } if stderr != nil { iopipe.Stderr = openReaderFromPipe(stderr) } proc := &process{ processCommon: processCommon{ containerID: containerID, friendlyName: processFriendlyName, client: clnt, systemPid: uint32(pid), }, commandLine: createProcessParms.CommandLine, hcsProcess: newProcess, } // Add the process to the container's list of processes container.processes[processFriendlyName] = proc // Make sure the lock is not held while calling back into the daemon clnt.unlock(containerID) // Tell the engine to attach streams back to the client if err := clnt.backend.AttachStreams(processFriendlyName, *iopipe); err != nil { clnt.lock(containerID) return err } // Lock again so that the defer unlock doesn't fail. (I really don't like this code) clnt.lock(containerID) // Spin up a go routine waiting for exit to handle cleanup go container.waitExit(proc, false) return nil }
// AddProcess is the handler for adding a process to an already running // container. It's called through docker exec. func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendlyName string, procToAdd Process, attachStdio StdioCallback) error { clnt.lock(containerID) defer clnt.unlock(containerID) container, err := clnt.getContainer(containerID) if err != nil { return err } // Note we always tell HCS to // create stdout as it's required regardless of '-i' or '-t' options, so that // docker can always grab the output through logs. We also tell HCS to always // create stdin, even if it's not used - it will be closed shortly. Stderr // is only created if it we're not -t. createProcessParms := hcsshim.ProcessConfig{ EmulateConsole: procToAdd.Terminal, ConsoleSize: procToAdd.InitialConsoleSize, CreateStdInPipe: true, CreateStdOutPipe: true, CreateStdErrPipe: !procToAdd.Terminal, } // Take working directory from the process to add if it is defined, // otherwise take from the first process. if procToAdd.Cwd != "" { createProcessParms.WorkingDirectory = procToAdd.Cwd } else { createProcessParms.WorkingDirectory = container.ociSpec.Process.Cwd } // Configure the environment for the process createProcessParms.Environment = setupEnvironmentVariables(procToAdd.Env) createProcessParms.CommandLine = strings.Join(procToAdd.Args, " ") logrus.Debugf("libcontainerd: commandLine: %s", createProcessParms.CommandLine) // Start the command running in the container. var stdout, stderr io.ReadCloser var stdin io.WriteCloser newProcess, err := container.hcsContainer.CreateProcess(&createProcessParms) if err != nil { logrus.Errorf("libcontainerd: AddProcess(%s) CreateProcess() failed %s", containerID, err) return err } stdin, stdout, stderr, err = newProcess.Stdio() if err != nil { logrus.Errorf("libcontainerd: %s getting std pipes failed %s", containerID, err) return err } iopipe := &IOPipe{Terminal: procToAdd.Terminal} iopipe.Stdin = createStdInCloser(stdin, newProcess) // TEMP: Work around Windows BS/DEL behavior. iopipe.Stdin = fixStdinBackspaceBehavior(iopipe.Stdin, container.ociSpec.Platform.OSVersion, procToAdd.Terminal) // Convert io.ReadClosers to io.Readers if stdout != nil { iopipe.Stdout = ioutil.NopCloser(&autoClosingReader{ReadCloser: stdout}) } if stderr != nil { iopipe.Stderr = ioutil.NopCloser(&autoClosingReader{ReadCloser: stderr}) } pid := newProcess.Pid() proc := &process{ processCommon: processCommon{ containerID: containerID, friendlyName: processFriendlyName, client: clnt, systemPid: uint32(pid), }, commandLine: createProcessParms.CommandLine, hcsProcess: newProcess, } // Add the process to the container's list of processes container.processes[processFriendlyName] = proc // Tell the engine to attach streams back to the client if err := attachStdio(*iopipe); err != nil { return err } // Spin up a go routine waiting for exit to handle cleanup go container.waitExit(proc, false) return nil }