Nathan Gieseker 9a429d8d25 Windows: Updates Windows Vendoring
Updates windows dependent libraries for vendoing.
2019-01-23 18:43:18 -08:00

162 lines
5.1 KiB
Go

package lcow
import (
"fmt"
"io"
"strings"
"time"
"github.com/Microsoft/hcsshim/internal/copywithtimeout"
"github.com/Microsoft/hcsshim/internal/hcs"
"github.com/Microsoft/hcsshim/internal/schema2"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
)
// ByteCounts are the number of bytes copied to/from standard handles. Note
// this is int64 rather than uint64 to match the golang io.Copy() signature.
type ByteCounts struct {
In int64
Out int64
Err int64
}
// ProcessOptions are the set of options which are passed to CreateProcessEx() to
// create a utility vm.
type ProcessOptions struct {
HCSSystem *hcs.System
Process *specs.Process
Stdin io.Reader // Optional reader for sending on to the processes stdin stream
Stdout io.Writer // Optional writer for returning the processes stdout stream
Stderr io.Writer // Optional writer for returning the processes stderr stream
CopyTimeout time.Duration // Timeout for the copy
CreateInUtilityVm bool // If the compute system is a utility VM
ByteCounts ByteCounts // How much data to copy on each stream if they are supplied. 0 means to io.EOF.
}
// CreateProcess creates a process either in an LCOW utility VM, or for starting
// the init process. TODO: Potentially extend for exec'd processes.
//
// It's essentially a glorified wrapper around hcs.ComputeSystem CreateProcess used
// for internal purposes.
//
// This is used on LCOW to run processes for remote filesystem commands, utilities,
// and debugging.
//
// It optional performs IO copies with timeout between the pipes provided as input,
// and the pipes in the process.
//
// In the ProcessOptions structure, if byte-counts are non-zero, a maximum of those
// bytes are copied to the appropriate standard IO reader/writer. When zero,
// it copies until EOF. It also returns byte-counts indicating how much data
// was sent/received from the process.
//
// It is the responsibility of the caller to call Close() on the process returned.
func CreateProcess(opts *ProcessOptions) (*hcs.Process, *ByteCounts, error) {
var environment = make(map[string]string)
copiedByteCounts := &ByteCounts{}
if opts == nil {
return nil, nil, fmt.Errorf("no options supplied")
}
if opts.HCSSystem == nil {
return nil, nil, fmt.Errorf("no HCS system supplied")
}
if opts.CreateInUtilityVm && opts.Process == nil {
return nil, nil, fmt.Errorf("process must be supplied for UVM process")
}
// Don't pass a process in if this is an LCOW container. This will start the init process.
if opts.Process != nil {
for _, v := range opts.Process.Env {
s := strings.SplitN(v, "=", 2)
if len(s) == 2 && len(s[1]) > 0 {
environment[s[0]] = s[1]
}
}
if _, ok := environment["PATH"]; !ok {
environment["PATH"] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:"
}
}
processConfig := &ProcessParameters{
ProcessParameters: hcsschema.ProcessParameters{
CreateStdInPipe: (opts.Stdin != nil),
CreateStdOutPipe: (opts.Stdout != nil),
CreateStdErrPipe: (opts.Stderr != nil),
EmulateConsole: false,
},
CreateInUtilityVm: opts.CreateInUtilityVm,
}
if opts.Process != nil {
processConfig.Environment = environment
processConfig.CommandLine = strings.Join(opts.Process.Args, " ")
processConfig.WorkingDirectory = opts.Process.Cwd
if processConfig.WorkingDirectory == "" {
processConfig.WorkingDirectory = `/`
}
}
proc, err := opts.HCSSystem.CreateProcess(processConfig)
if err != nil {
logrus.Debugf("failed to create process: %s", err)
return nil, nil, err
}
processStdin, processStdout, processStderr, err := proc.Stdio()
if err != nil {
proc.Kill() // Should this have a timeout?
proc.Close()
return nil, nil, fmt.Errorf("failed to get stdio pipes for process %+v: %s", processConfig, err)
}
// Send the data into the process's stdin
if opts.Stdin != nil {
if copiedByteCounts.In, err = copywithtimeout.Copy(processStdin,
opts.Stdin,
opts.ByteCounts.In,
"stdin",
opts.CopyTimeout); err != nil {
return nil, nil, err
}
// Don't need stdin now we've sent everything. This signals GCS that we are finished sending data.
if err := proc.CloseStdin(); err != nil && !hcs.IsNotExist(err) && !hcs.IsAlreadyClosed(err) {
// This error will occur if the compute system is currently shutting down
if perr, ok := err.(*hcs.ProcessError); ok && perr.Err != hcs.ErrVmcomputeOperationInvalidState {
return nil, nil, err
}
}
}
// Copy the data back from stdout
if opts.Stdout != nil {
// Copy the data over to the writer.
if copiedByteCounts.Out, err = copywithtimeout.Copy(opts.Stdout,
processStdout,
opts.ByteCounts.Out,
"stdout",
opts.CopyTimeout); err != nil {
return nil, nil, err
}
}
// Copy the data back from stderr
if opts.Stderr != nil {
// Copy the data over to the writer.
if copiedByteCounts.Err, err = copywithtimeout.Copy(opts.Stderr,
processStderr,
opts.ByteCounts.Err,
"stderr",
opts.CopyTimeout); err != nil {
return nil, nil, err
}
}
return proc, copiedByteCounts, nil
}