Repository: vmware/govmomi
Pull request #2375 (2021-04-20 04:29:48)

toolbox: move process management to its own package
This allows vcsim to use the process manager without depending on the backdoor RPC package.


Patch:
diff --git a/.goreleaser.yml b/.goreleaser.yml
index 95932e7c..ffe36b35 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -2,8 +2,8 @@
 project_name: govmomi
 before:
   hooks:
-    - go mod vendor
-
+  - go mod vendor
+  
 builds:
   - id: govc
     goos: &goos-defs
@@ -51,25 +51,19 @@ archives:
     format_overrides: &overrides
       - goos: windows
         format: zip
-    files: &extrafiles
-      - CHANGELOG.md
-      - LICENSE.txt
-      - README.md
-
   - id: vcsimbuild
     builds:
       - vcsim
     name_template: "vcsim_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
     replacements: *replacements
     format_overrides: *overrides
-    files: *extrafiles
-
+        
 snapshot:
   name_template: "{{ .Tag }}-next"
-
+  
 checksum:
   name_template: "checksums.txt"
-
+  
 changelog:
   sort: asc
   filters:
@@ -78,7 +72,7 @@ changelog:
       - "^test:"
       - Merge pull request
       - Merge branch
-
+      
 # upload disabled since it is maintained in homebrew-core
 brews:
   - name: govc
@@ -121,7 +115,7 @@ brews:
       system "#{bin}/vcsim -h"
     install: |
       bin.install "vcsim"
-
+      
 dockers:
   - image_templates:
       - "vmware/govc:{{ .Tag }}"
diff --git a/toolbox/command.go b/toolbox/command.go
index 50a6e602..c5e53c85 100644
--- a/toolbox/command.go
+++ b/toolbox/command.go
@@ -31,6 +31,7 @@ import (
 	"time"
 
 	"github.com/vmware/govmomi/toolbox/hgfs"
+	"github.com/vmware/govmomi/toolbox/process"
 	"github.com/vmware/govmomi/toolbox/vix"
 )
 
@@ -39,11 +40,11 @@ type CommandHandler func(vix.CommandRequestHeader, []byte) ([]byte, error)
 type CommandServer struct {
 	Out *ChannelOut
 
-	ProcessManager *ProcessManager
+	ProcessManager *process.Manager
 
 	Authenticate func(vix.CommandRequestHeader, []byte) error
 
-	ProcessStartCommand func(*ProcessManager, *vix.StartProgramRequest) (int64, error)
+	ProcessStartCommand func(*process.Manager, *vix.StartProgramRequest) (int64, error)
 
 	handlers map[uint32]CommandHandler
 
@@ -53,7 +54,7 @@ type CommandServer struct {
 func registerCommandServer(service *Service) *CommandServer {
 	server := &CommandServer{
 		Out:            service.out,
-		ProcessManager: NewProcessManager(),
+		ProcessManager: process.NewManager(),
 	}
 
 	server.handlers = map[uint32]CommandHandler{
@@ -231,12 +232,12 @@ func (c *CommandServer) StartCommand(header vix.CommandRequestHeader, data []byt
 	return append([]byte(fmt.Sprintf("%d", pid)), 0), nil
 }
 
-func DefaultStartCommand(m *ProcessManager, r *vix.StartProgramRequest) (int64, error) {
-	p := NewProcess()
+func DefaultStartCommand(m *process.Manager, r *vix.StartProgramRequest) (int64, error) {
+	p := process.New()
 
 	switch r.ProgramPath {
 	case "http.RoundTrip":
-		p = NewProcessRoundTrip()
+		p = process.NewRoundTrip()
 	default:
 		// Standard vmware-tools requires an absolute path,
 		// we'll enable IO redirection by default without an absolute path.
@@ -296,7 +297,7 @@ func (c *CommandServer) ReadEnvironmentVariables(header vix.CommandRequestHeader
 
 	if len(r.Names) == 0 {
 		for _, e := range os.Environ() {
-			_, _ = buf.WriteString(fmt.Sprintf("<ev>%s</ev>", xmlEscape.Replace(e)))
+			_, _ = buf.WriteString(fmt.Sprintf("<ev>%s</ev>", process.EscapeXML.Replace(e)))
 		}
 	} else {
 		for _, key := range r.Names {
@@ -304,7 +305,7 @@ func (c *CommandServer) ReadEnvironmentVariables(header vix.CommandRequestHeader
 			if val == "" {
 				continue
 			}
-			_, _ = buf.WriteString(fmt.Sprintf("<ev>%s=%s</ev>", xmlEscape.Replace(key), xmlEscape.Replace(val)))
+			_, _ = buf.WriteString(fmt.Sprintf("<ev>%s=%s</ev>", process.EscapeXML.Replace(key), process.EscapeXML.Replace(val)))
 		}
 	}
 
diff --git a/toolbox/process.go b/toolbox/process/process.go
similarity index 83%
rename from toolbox/process.go
rename to toolbox/process/process.go
index 7b4334d0..4346176c 100644
--- a/toolbox/process.go
+++ b/toolbox/process/process.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2017 VMware, Inc. All Rights Reserved.
+Copyright (c) 2017-2021 VMware, Inc. All Rights Reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package toolbox
+package process
 
 import (
 	"bytes"
@@ -40,7 +40,7 @@ import (
 )
 
 var (
-	xmlEscape *strings.Replacer
+	EscapeXML *strings.Replacer
 
 	shell = "/bin/sh"
 
@@ -65,7 +65,7 @@ func init() {
 		replace = append(replace, url.QueryEscape(c))
 	}
 
-	xmlEscape = strings.NewReplacer(replace...)
+	EscapeXML = strings.NewReplacer(replace...)
 
 	// See procMgrPosix.c:ProcMgrStartProcess:
 	// Prefer bash -c as is uses exec() to replace itself,
@@ -79,9 +79,9 @@ func init() {
 	}
 }
 
-// ProcessIO encapsulates IO for Go functions and OS commands such that they can interact via the OperationsManager
+// IO encapsulates IO for Go functions and OS commands such that they can interact via the OperationsManager
 // without file system disk IO.
-type ProcessIO struct {
+type IO struct {
 	In struct {
 		io.Writer
 		io.Reader
@@ -92,8 +92,8 @@ type ProcessIO struct {
 	Err *bytes.Buffer
 }
 
-// ProcessState is the toolbox representation of the GuestProcessInfo type
-type ProcessState struct {
+// State is the toolbox representation of the GuestProcessInfo type
+type State struct {
 	StartTime int64 // (keep first to ensure 64-bit alignment)
 	EndTime   int64 // (keep first to ensure 64-bit alignment)
 
@@ -103,12 +103,12 @@ type ProcessState struct {
 	Pid      int64
 	ExitCode int32
 
-	IO *ProcessIO
+	IO *IO
 }
 
 // WithIO enables toolbox Process IO without file system disk IO.
 func (p *Process) WithIO() *Process {
-	p.IO = &ProcessIO{
+	p.IO = &IO{
 		Out: new(bytes.Buffer),
 		Err: new(bytes.Buffer),
 	}
@@ -116,8 +116,8 @@ func (p *Process) WithIO() *Process {
 	return p
 }
 
-// ProcessFile implements the os.FileInfo interface to enable toolbox interaction with virtual files.
-type ProcessFile struct {
+// File implements the os.FileInfo interface to enable toolbox interaction with virtual files.
+type File struct {
 	io.Reader
 	io.Writer
 	io.Closer
@@ -127,17 +127,17 @@ type ProcessFile struct {
 }
 
 // Name implementation of the os.FileInfo interface method.
-func (a *ProcessFile) Name() string {
+func (a *File) Name() string {
 	return a.name
 }
 
 // Size implementation of the os.FileInfo interface method.
-func (a *ProcessFile) Size() int64 {
+func (a *File) Size() int64 {
 	return int64(a.size)
 }
 
 // Mode implementation of the os.FileInfo interface method.
-func (a *ProcessFile) Mode() os.FileMode {
+func (a *File) Mode() os.FileMode {
 	if strings.HasSuffix(a.name, "stdin") {
 		return 0200
 	}
@@ -145,21 +145,21 @@ func (a *ProcessFile) Mode() os.FileMode {
 }
 
 // ModTime implementation of the os.FileInfo interface method.
-func (a *ProcessFile) ModTime() time.Time {
+func (a *File) ModTime() time.Time {
 	return time.Now()
 }
 
 // IsDir implementation of the os.FileInfo interface method.
-func (a *ProcessFile) IsDir() bool {
+func (a *File) IsDir() bool {
 	return false
 }
 
 // Sys implementation of the os.FileInfo interface method.
-func (a *ProcessFile) Sys() interface{} {
+func (a *File) Sys() interface{} {
 	return nil
 }
 
-func (s *ProcessState) toXML() string {
+func (s *State) toXML() string {
 	const format = "<proc>" +
 		"<cmd>%s</cmd>" +
 		"<name>%s</name>" +
@@ -175,7 +175,7 @@ func (s *ProcessState) toXML() string {
 	argv := []string{s.Name}
 
 	if len(s.Args) != 0 {
-		argv = append(argv, xmlEscape.Replace(s.Args))
+		argv = append(argv, EscapeXML.Replace(s.Args))
 	}
 
 	args := strings.Join(argv, " ")
@@ -188,7 +188,7 @@ func (s *ProcessState) toXML() string {
 
 // Process managed by the ProcessManager.
 type Process struct {
-	ProcessState
+	State
 
 	Start func(*Process, *vix.StartProgramRequest) (int64, error)
 	Wait  func() error
@@ -197,19 +197,19 @@ type Process struct {
 	ctx context.Context
 }
 
-// ProcessError can be returned by the Process.Wait function to propagate ExitCode to ProcessState.
-type ProcessError struct {
+// Error can be returned by the Process.Wait function to propagate ExitCode to ProcessState.
+type Error struct {
 	Err      error
 	ExitCode int32
 }
 
-func (e *ProcessError) Error() string {
+func (e *Error) Error() string {
 	return e.Err.Error()
 }
 
-// ProcessManager manages processes within the guest.
-// See: http://pubs.vmware.com/vsphere-60/topic/com.vmware.wssdk.apiref.doc/vim.vm.guest.ProcessManager.html
-type ProcessManager struct {
+// Manager manages processes within the guest.
+// See: http://pubs.vmware.com/vsphere-60/topic/com.vmware.wssdk.apiref.doc/vim.vm.guest.Manager.html
+type Manager struct {
 	wg      sync.WaitGroup
 	mu      sync.Mutex
 	expire  time.Duration
@@ -217,14 +217,14 @@ type ProcessManager struct {
 	pids    sync.Pool
 }
 
-// NewProcessManager creates a new ProcessManager instance.
-func NewProcessManager() *ProcessManager {
+// NewManager creates a new ProcessManager instance.
+func NewManager() *Manager {
 	// We use pseudo PIDs that don't conflict with OS PIDs, so they can live in the same table.
 	// For the pseudo PIDs, we use a sync.Pool rather than a plain old counter to avoid the unlikely,
 	// but possible wrapping should such a counter exceed MaxInt64.
 	pid := int64(32768) // TODO: /proc/sys/kernel/pid_max
 
-	return &ProcessManager{
+	return &Manager{
 		expire:  time.Minute * 5,
 		entries: make(map[int64]*Process),
 		pids: sync.Pool{
@@ -239,7 +239,7 @@ func NewProcessManager() *ProcessManager {
 // A goroutine is started that calls the Process.Wait function.  After Process.Wait has
 // returned, the ProcessState EndTime and ExitCode fields are set.  The process state can be
 // queried via ListProcessesInGuest until it is removed, 5 minutes after Wait returns.
-func (m *ProcessManager) Start(r *vix.StartProgramRequest, p *Process) (int64, error) {
+func (m *Manager) Start(r *vix.StartProgramRequest, p *Process) (int64, error) {
 	p.Name = r.ProgramPath
 	p.Args = r.Arguments
 
@@ -275,7 +275,7 @@ func (m *ProcessManager) Start(r *vix.StartProgramRequest, p *Process) (int64, e
 
 		if werr != nil {
 			rc := int32(1)
-			if xerr, ok := werr.(*ProcessError); ok {
+			if xerr, ok := werr.(*Error); ok {
 				rc = xerr.ExitCode
 			}
 
@@ -304,7 +304,7 @@ func (m *ProcessManager) Start(r *vix.StartProgramRequest, p *Process) (int64, e
 
 // Kill cancels the Process Context.
 // Returns true if pid exists in the process table, false otherwise.
-func (m *ProcessManager) Kill(pid int64) bool {
+func (m *Manager) Kill(pid int64) bool {
 	m.mu.Lock()
 	entry, ok := m.entries[pid]
 	m.mu.Unlock()
@@ -320,7 +320,7 @@ func (m *ProcessManager) Kill(pid int64) bool {
 // ListProcesses marshals the ProcessState for the given pids.
 // If no pids are specified, all current processes are included.
 // The return value can be used for responding to a VixMsgListProcessesExRequest.
-func (m *ProcessManager) ListProcesses(pids []int64) []byte {
+func (m *Manager) ListProcesses(pids []int64) []byte {
 	w := new(bytes.Buffer)
 
 	m.mu.Lock()
@@ -356,7 +356,7 @@ func (p procFileInfo) Size() int64 {
 }
 
 // Stat implements hgfs.FileHandler.Stat
-func (m *ProcessManager) Stat(u *url.URL) (os.FileInfo, error) {
+func (m *Manager) Stat(u *url.URL) (os.FileInfo, error) {
 	name := path.Join("/proc", u.Path)
 
 	info, err := os.Stat(name)
@@ -380,7 +380,7 @@ func (m *ProcessManager) Stat(u *url.URL) (os.FileInfo, error) {
 		return nil, os.ErrNotExist
 	}
 
-	pf := &ProcessFile{
+	pf := &File{
 		name:   name,
 		Closer: ioutil.NopCloser(nil), // via hgfs, nop for stdout and stderr
 	}
@@ -424,13 +424,13 @@ func (m *ProcessManager) Stat(u *url.URL) (os.FileInfo, error) {
 }
 
 // Open implements hgfs.FileHandler.Open
-func (m *ProcessManager) Open(u *url.URL, mode int32) (hgfs.File, error) {
+func (m *Manager) Open(u *url.URL, mode int32) (hgfs.File, error) {
 	info, err := m.Stat(u)
 	if err != nil {
 		return nil, err
 	}
 
-	pinfo, ok := info.(*ProcessFile)
+	pinfo, ok := info.(*File)
 
 	if !ok {
 		return nil, os.ErrNotExist // fall through to default os.Open
@@ -458,11 +458,11 @@ type processFunc struct {
 	err error
 }
 
-// NewProcessFunc creates a new Process, where the Start function calls the given run function within a goroutine.
+// NewFunc creates a new Process, where the Start function calls the given run function within a goroutine.
 // The Wait function waits for the goroutine to finish and returns the error returned by run.
 // The run ctx param may be used to return early via the ProcessManager.Kill method.
 // The run args command is that of the VixMsgStartProgramRequest.Arguments field.
-func NewProcessFunc(run func(ctx context.Context, args string) error) *Process {
+func NewFunc(run func(ctx context.Context, args string) error) *Process {
 	f := &processFunc{run: run}
 
 	return &Process{
@@ -471,8 +471,8 @@ func NewProcessFunc(run func(ctx context.Context, args string) error) *Process {
 	}
 }
 
-// ProcessFuncIO is the Context key to access optional ProcessIO
-var ProcessFuncIO = struct {
+// FuncIO is the Context key to access optional ProcessIO
+var FuncIO = struct {
 	key int64
 }{vix.CommandMagicWord}
 
@@ -487,7 +487,7 @@ func (f *processFunc) start(p *Process, r *vix.StartProgramRequest) (int64, erro
 		p.IO.In.Reader, p.IO.In.Writer = pr, pw
 		c, p.IO.In.Closer = pr, pw
 
-		p.ctx = context.WithValue(p.ctx, ProcessFuncIO, p.IO)
+		p.ctx = context.WithValue(p.ctx, FuncIO, p.IO)
 	}
 
 	go func() {
@@ -516,13 +516,13 @@ type processCmd struct {
 	cmd *exec.Cmd
 }
 
-// NewProcess creates a new Process, where the Start function use exec.CommandContext to create and start the process.
+// New creates a new Process, where the Start function use exec.CommandContext to create and start the process.
 // The Wait function waits for the process to finish and returns the error returned by exec.Cmd.Wait().
 // Prior to Wait returning, the exec.Cmd.Wait() error is used to set the ProcessState.ExitCode, if error is of type exec.ExitError.
 // The ctx param may be used to kill the process via the ProcessManager.Kill method.
 // The VixMsgStartProgramRequest param fields are mapped to the exec.Cmd counterpart fields.
 // Processes are started within a sub-shell, allowing for i/o redirection, just as with the C version of vmware-tools.
-func NewProcess() *Process {
+func New() *Process {
 	c := new(processCmd)
 
 	return &Process{
@@ -572,7 +572,7 @@ func (c *processCmd) start(p *Process, r *vix.StartProgramRequest) (int64, error
 func (c *processCmd) wait() error {
 	err := c.cmd.Wait()
 	if err != nil {
-		xerr := &ProcessError{
+		xerr := &Error{
 			Err:      err,
 			ExitCode: 1,
 		}
@@ -589,10 +589,10 @@ func (c *processCmd) wait() error {
 	return nil
 }
 
-// NewProcessRoundTrip starts a Go function to implement a toolbox backed http.RoundTripper
-func NewProcessRoundTrip() *Process {
-	return NewProcessFunc(func(ctx context.Context, host string) error {
-		p, _ := ctx.Value(ProcessFuncIO).(*ProcessIO)
+// NewRoundTrip starts a Go function to implement a toolbox backed http.RoundTripper
+func NewRoundTrip() *Process {
+	return NewFunc(func(ctx context.Context, host string) error {
+		p, _ := ctx.Value(FuncIO).(*IO)
 
 		closers := []io.Closer{p.In.Closer}
 


Test patch:
diff --git a/toolbox/command_test.go b/toolbox/command_test.go
index 773fdfe0..5697ba15 100644
--- a/toolbox/command_test.go
+++ b/toolbox/command_test.go
@@ -33,6 +33,7 @@ import (
 	"time"
 
 	"github.com/vmware/govmomi/toolbox/hgfs"
+	"github.com/vmware/govmomi/toolbox/process"
 	"github.com/vmware/govmomi/toolbox/vix"
 )
 
@@ -188,7 +189,7 @@ func TestVixRelayedCommandHandler(t *testing.T) {
 		t.Fatalf("%q", reply)
 	}
 
-	cmd.ProcessStartCommand = func(pm *ProcessManager, r *vix.StartProgramRequest) (int64, error) {
+	cmd.ProcessStartCommand = func(pm *process.Manager, r *vix.StartProgramRequest) (int64, error) {
 		return -1, nil
 	}
 
@@ -443,13 +444,12 @@ func TestVixProcessHgfsPacket(t *testing.T) {
 
 func TestVixListProcessesEx(t *testing.T) {
 	c := NewCommandClient()
-	pm := c.Service.Command.ProcessManager
 
-	c.Service.Command.ProcessStartCommand = func(pm *ProcessManager, r *vix.StartProgramRequest) (int64, error) {
-		var p *Process
+	c.Service.Command.ProcessStartCommand = func(pm *process.Manager, r *vix.StartProgramRequest) (int64, error) {
+		var p *process.Process
 		switch r.ProgramPath {
 		case "foo":
-			p = NewProcessFunc(func(ctx context.Context, arg string) error {
+			p = process.NewFunc(func(ctx context.Context, arg string) error {
 				return nil
 			})
 		default:
@@ -483,7 +483,7 @@ func TestVixListProcessesEx(t *testing.T) {
 		t.Fatalf("rc: %d", rc)
 	}
 
-	pm.wg.Wait()
+	<-time.Tick(time.Millisecond * 100)
 
 	ps := new(vix.ListProcessesRequest)
 
diff --git a/toolbox/process_test.go b/toolbox/process/process_test.go
similarity index 85%
rename from toolbox/process_test.go
rename to toolbox/process/process_test.go
index b846666b..e4a0e270 100644
--- a/toolbox/process_test.go
+++ b/toolbox/process/process_test.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2017 VMware, Inc. All Rights Reserved.
+Copyright (c) 2017-2021 VMware, Inc. All Rights Reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package toolbox
+package process
 
 import (
 	"bufio"
@@ -34,7 +34,7 @@ import (
 )
 
 func TestProcessFunction(t *testing.T) {
-	m := NewProcessManager()
+	m := NewManager()
 	var pids []int64
 
 	for i := 0; i <= 2; i++ {
@@ -43,13 +43,13 @@ func TestProcessFunction(t *testing.T) {
 			Arguments:   strconv.Itoa(i),
 		}
 
-		pid, _ := m.Start(r, NewProcessFunc(func(_ context.Context, arg string) error {
+		pid, _ := m.Start(r, NewFunc(func(_ context.Context, arg string) error {
 			rc, _ := strconv.Atoi(arg)
 			if rc == 0 {
 				return nil
 
 			}
-			return &ProcessError{Err: errors.New("fail"), ExitCode: int32(rc)}
+			return &Error{Err: errors.New("fail"), ExitCode: int32(rc)}
 		}))
 
 		if pid == 0 {
@@ -72,7 +72,7 @@ func TestProcessFunction(t *testing.T) {
 }
 
 func TestProcessCommand(t *testing.T) {
-	m := NewProcessManager()
+	m := NewManager()
 	var pids []int64
 
 	for i := 0; i <= 2; i++ {
@@ -81,7 +81,7 @@ func TestProcessCommand(t *testing.T) {
 			Arguments:   fmt.Sprintf(`-c "exit %d"`, i),
 		}
 
-		pid, _ := m.Start(r, NewProcess())
+		pid, _ := m.Start(r, New())
 		pids = append(pids, pid)
 	}
 
@@ -101,21 +101,21 @@ func TestProcessCommand(t *testing.T) {
 	}
 
 	shell = "/enoent/enoent"
-	_, err := m.Start(r, NewProcess())
+	_, err := m.Start(r, New())
 	if err == nil {
 		t.Error("expected error")
 	}
 	shell = r.ProgramPath
 
 	r.ProgramPath = "/enoent/enoent"
-	_, err = m.Start(r, NewProcess())
+	_, err = m.Start(r, New())
 	if err == nil {
 		t.Error("expected error")
 	}
 }
 
 func TestProcessKill(t *testing.T) {
-	m := NewProcessManager()
+	m := NewManager()
 	var pids []int64
 
 	procs := []struct {
@@ -127,10 +127,10 @@ func TestProcessKill(t *testing.T) {
 				ProgramPath: "test",
 				Arguments:   "none",
 			},
-			NewProcessFunc(func(ctx context.Context, _ string) error {
+			NewFunc(func(ctx context.Context, _ string) error {
 				select {
 				case <-ctx.Done():
-					return &ProcessError{Err: ctx.Err(), ExitCode: 42}
+					return &Error{Err: ctx.Err(), ExitCode: 42}
 				case <-time.After(time.Minute):
 				}
 
@@ -142,7 +142,7 @@ func TestProcessKill(t *testing.T) {
 				ProgramPath: "/bin/bash",
 				Arguments:   fmt.Sprintf(`-c "while true; do sleep 1; done"`),
 			},
-			NewProcess(),
+			New(),
 		},
 	}
 
@@ -186,7 +186,7 @@ func TestProcessKill(t *testing.T) {
 }
 
 func TestProcessRemove(t *testing.T) {
-	m := NewProcessManager()
+	m := NewManager()
 
 	m.expire = time.Millisecond
 
@@ -194,7 +194,7 @@ func TestProcessRemove(t *testing.T) {
 		ProgramPath: "test",
 	}
 
-	pid, _ := m.Start(r, NewProcessFunc(func(_ context.Context, arg string) error {
+	pid, _ := m.Start(r, NewFunc(func(_ context.Context, arg string) error {
 		return nil
 	}))
 
@@ -217,7 +217,7 @@ func TestEscapeXML(t *testing.T) {
 	}
 
 	for i, test := range tests {
-		e := xmlEscape.Replace(test.in)
+		e := EscapeXML.Replace(test.in)
 		if e != test.out {
 			t.Errorf("%d: %s != %s", i, e, test.out)
 		}
@@ -226,7 +226,7 @@ func TestEscapeXML(t *testing.T) {
 
 func TestProcessError(t *testing.T) {
 	fault := errors.New("fail")
-	var err error = &ProcessError{Err: fault}
+	var err error = &Error{Err: fault}
 
 	if err.Error() != fault.Error() {
 		t.Fatal()
@@ -234,13 +234,13 @@ func TestProcessError(t *testing.T) {
 }
 
 func TestProcessIO(t *testing.T) {
-	m := NewProcessManager()
+	m := NewManager()
 
 	r := &vix.StartProgramRequest{
 		ProgramPath: "/bin/date",
 	}
 
-	p := NewProcess().WithIO()
+	p := New().WithIO()
 
 	_, err := m.Start(r, p)
 	if err != nil {
@@ -284,14 +284,14 @@ func TestProcessRoundTripper(t *testing.T) {
 
 	u, _ := url.Parse(echo.URL)
 
-	m := NewProcessManager()
+	m := NewManager()
 
 	r := &vix.StartProgramRequest{
 		ProgramPath: "http.RoundTrip",
 		Arguments:   u.Host,
 	}
 
-	p := NewProcessRoundTrip()
+	p := NewRoundTrip()
 
 	_, err := m.Start(r, p)
 	if err != nil {
diff --git a/toolbox/service_test.go b/toolbox/service_test.go
index 121eb37e..a0b2744b 100644
--- a/toolbox/service_test.go
+++ b/toolbox/service_test.go
@@ -34,6 +34,7 @@ import (
 	"time"
 
 	"github.com/vmware/govmomi/toolbox/hgfs"
+	"github.com/vmware/govmomi/toolbox/process"
 	"github.com/vmware/govmomi/toolbox/vix"
 	"github.com/vmware/govmomi/vim25/types"
 )
@@ -413,7 +414,7 @@ func TestServiceRunESX(t *testing.T) {
 	}
 
 	if *testPID != 0 {
-		service.Command.ProcessStartCommand = func(m *ProcessManager, r *vix.StartProgramRequest) (int64, error) {
+		service.Command.ProcessStartCommand = func(m *process.Manager, r *vix.StartProgramRequest) (int64, error) {
 			wg.Add(1)
 			defer wg.Done()
 
@@ -421,7 +422,7 @@ func TestServiceRunESX(t *testing.T) {
 			case "/bin/date":
 				return *testPID, nil
 			case "sleep":
-				p := NewProcessFunc(func(ctx context.Context, arg string) error {
+				p := process.NewFunc(func(ctx context.Context, arg string) error {
 					d, err := time.ParseDuration(arg)
 					if err != nil {
 						return err
@@ -429,7 +430,7 @@ func TestServiceRunESX(t *testing.T) {
 
 					select {
 					case <-ctx.Done():
-						return &ProcessError{Err: ctx.Err(), ExitCode: 42}
+						return &process.Error{Err: ctx.Err(), ExitCode: 42}
 					case <-time.After(d):
 					}