From 4d0f2e9195304e595c706de381d4a59d6f6f72bf Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 14 Jun 2011 10:49:34 -0400 Subject: [PATCH] syscall, os, exec: introduce *syscall.SysProcAttr field in os.ProcAttr and exec.Cmd R=r, bradfitz, alex.brainman, borman, vincent.vanackere CC=golang-dev https://golang.org/cl/4607046 --- src/pkg/debug/proc/proc_linux.go | 8 ++++-- src/pkg/exec/exec.go | 6 ++++ src/pkg/os/exec.go | 6 ++++ src/pkg/os/exec_plan9.go | 1 + src/pkg/os/exec_posix.go | 1 + src/pkg/syscall/exec_plan9.go | 30 ++++++++++--------- src/pkg/syscall/exec_unix.go | 49 +++++++++++++++++++------------- src/pkg/syscall/exec_windows.go | 26 +++++++++++------ 8 files changed, 83 insertions(+), 44 deletions(-) diff --git a/src/pkg/debug/proc/proc_linux.go b/src/pkg/debug/proc/proc_linux.go index 153c3e99b75..5831b0e9795 100644 --- a/src/pkg/debug/proc/proc_linux.go +++ b/src/pkg/debug/proc/proc_linux.go @@ -1284,9 +1284,11 @@ func Attach(pid int) (Process, os.Error) { // details. func StartProcess(argv0 string, argv []string, attr *os.ProcAttr) (Process, os.Error) { sysattr := &syscall.ProcAttr{ - Dir: attr.Dir, - Env: attr.Env, - Ptrace: true, + Dir: attr.Dir, + Env: attr.Env, + Sys: &syscall.SysProcAttr{ + Ptrace: true, + }, } p := newProcess(-1) diff --git a/src/pkg/exec/exec.go b/src/pkg/exec/exec.go index 935f24c2179..5b988d5eb41 100644 --- a/src/pkg/exec/exec.go +++ b/src/pkg/exec/exec.go @@ -12,6 +12,7 @@ import ( "io" "os" "strconv" + "syscall" ) // Error records the name of a binary that failed to be be executed @@ -62,6 +63,10 @@ type Cmd struct { Stdout io.Writer Stderr io.Writer + // SysProcAttr holds optional, operating system-specific attributes. + // Run passes it to os.StartProcess as the os.ProcAttr's Sys field. + SysProcAttr *syscall.SysProcAttr + // Process is the underlying process, once started. Process *os.Process @@ -225,6 +230,7 @@ func (c *Cmd) Start() os.Error { Dir: c.Dir, Files: c.childFiles, Env: c.envv(), + Sys: c.SysProcAttr, }) if err != nil { return err diff --git a/src/pkg/os/exec.go b/src/pkg/os/exec.go index f62caf9a069..e2234f14a06 100644 --- a/src/pkg/os/exec.go +++ b/src/pkg/os/exec.go @@ -37,6 +37,12 @@ type ProcAttr struct { // depending on the underlying operating system. A nil entry corresponds // to that file being closed when the process starts. Files []*File + + // Operating system-specific process creation attributes. + // Note that setting this field means that your program + // may not execute properly or even compile on some + // operating systems. + Sys *syscall.SysProcAttr } // Getpid returns the process id of the caller. diff --git a/src/pkg/os/exec_plan9.go b/src/pkg/os/exec_plan9.go index 11874aba677..299d3fa4db3 100644 --- a/src/pkg/os/exec_plan9.go +++ b/src/pkg/os/exec_plan9.go @@ -15,6 +15,7 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err E sysattr := &syscall.ProcAttr{ Dir: attr.Dir, Env: attr.Env, + Sys: attr.Sys, } // Create array of integer (system) fds. diff --git a/src/pkg/os/exec_posix.go b/src/pkg/os/exec_posix.go index bf992ef42ef..734bf887b39 100644 --- a/src/pkg/os/exec_posix.go +++ b/src/pkg/os/exec_posix.go @@ -30,6 +30,7 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err E sysattr := &syscall.ProcAttr{ Dir: attr.Dir, Env: attr.Env, + Sys: attr.Sys, } if sysattr.Env == nil { sysattr.Env = Environ() diff --git a/src/pkg/syscall/exec_plan9.go b/src/pkg/syscall/exec_plan9.go index 01edb49ecf7..66ab1fced67 100644 --- a/src/pkg/syscall/exec_plan9.go +++ b/src/pkg/syscall/exec_plan9.go @@ -169,7 +169,7 @@ func init() { // no rescheduling, no malloc calls, and no new stack segments. // The calls to RawSyscall are okay because they are assembly // functions that do not grow the stack. -func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, chroot, dir *byte, attr *ProcAttr, fdsToClose []int, pipe int) (pid int, err Error) { +func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, attr *ProcAttr, fdsToClose []int, pipe int, rflag int) (pid int, err Error) { // Declare all variables at top in case any // declarations require heap allocation (e.g., errbuf). var ( @@ -190,7 +190,7 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, chroot, dir * // About to call fork. // No more allocation or calls of non-assembly functions. - r1, _, _ = RawSyscall(SYS_RFORK, uintptr(RFPROC|RFFDG|RFREND|clearenv), 0, 0) + r1, _, _ = RawSyscall(SYS_RFORK, uintptr(RFPROC|RFFDG|RFREND|clearenv|rflag), 0, 0) if r1 != 0 { if int(r1) == -1 { @@ -338,14 +338,18 @@ type envItem struct { } type ProcAttr struct { - Dir string // Current working directory. - Env []string // Environment. - Files []int // File descriptors. - Chroot string // Chroot. + Dir string // Current working directory. + Env []string // Environment. + Files []int // File descriptors. + Sys *SysProcAttr } -var zeroAttributes ProcAttr +type SysProcAttr struct { + Rfork int // additional flags to pass to rfork +} +var zeroProcAttr ProcAttr +var zeroSysProcAttr SysProcAttr func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err Error) { var ( @@ -356,7 +360,11 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err Error) ) if attr == nil { - attr = &zeroAttributes + attr = &zeroProcAttr + } + sys := attr.Sys + if sys == nil { + sys = &zeroSysProcAttr } p[0] = -1 @@ -366,10 +374,6 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err Error) argv0p := StringBytePtr(argv0) argvp := StringSlicePtr(argv) - var chroot *byte - if attr.Chroot != "" { - chroot = StringBytePtr(attr.Chroot) - } var dir *byte if attr.Dir != "" { dir = StringBytePtr(attr.Dir) @@ -439,7 +443,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err Error) fdsToClose = append(fdsToClose, p[0]) // Kick off child. - pid, err = forkAndExecInChild(argv0p, argvp, envvParsed, chroot, dir, attr, fdsToClose, p[1]) + pid, err = forkAndExecInChild(argv0p, argvp, envvParsed, dir, attr, fdsToClose, p[1], sys.Rfork) if err != nil { if p[0] >= 0 { diff --git a/src/pkg/syscall/exec_unix.go b/src/pkg/syscall/exec_unix.go index dee30226886..31bed926a3d 100644 --- a/src/pkg/syscall/exec_unix.go +++ b/src/pkg/syscall/exec_unix.go @@ -96,7 +96,7 @@ func SetNonblock(fd int, nonblocking bool) (errno int) { // no rescheduling, no malloc calls, and no new stack segments. // The calls to RawSyscall are okay because they are assembly // functions that do not grow the stack. -func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, pipe int) (pid int, err int) { +func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err int) { // Declare all variables at top in case any // declarations require heap allocation (e.g., err1). var r1, r2, err1 uintptr @@ -131,7 +131,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // Fork succeeded, now in child. // Enable tracing if requested. - if attr.Ptrace { + if sys.Ptrace { _, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0) if err1 != 0 { goto childerror @@ -139,7 +139,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } // Session ID - if attr.Setsid { + if sys.Setsid { _, _, err1 = RawSyscall(SYS_SETSID, 0, 0, 0) if err1 != 0 { goto childerror @@ -155,21 +155,21 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } // User and groups - if attr.Credential != nil { - ngroups := uintptr(len(attr.Credential.Groups)) + if cred := sys.Credential; cred != nil { + ngroups := uintptr(len(cred.Groups)) groups := uintptr(0) if ngroups > 0 { - groups = uintptr(unsafe.Pointer(&attr.Credential.Groups[0])) + groups = uintptr(unsafe.Pointer(&cred.Groups[0])) } _, _, err1 = RawSyscall(SYS_SETGROUPS, ngroups, groups, 0) if err1 != 0 { goto childerror } - _, _, err1 = RawSyscall(SYS_SETGID, uintptr(attr.Credential.Gid), 0, 0) + _, _, err1 = RawSyscall(SYS_SETGID, uintptr(cred.Gid), 0, 0) if err1 != 0 { goto childerror } - _, _, err1 = RawSyscall(SYS_SETUID, uintptr(attr.Credential.Uid), 0, 0) + _, _, err1 = RawSyscall(SYS_SETUID, uintptr(cred.Uid), 0, 0) if err1 != 0 { goto childerror } @@ -267,16 +267,21 @@ type Credential struct { } type ProcAttr struct { - Setsid bool // Create session. - Ptrace bool // Enable tracing. - Dir string // Current working directory. - Env []string // Environment. - Files []int // File descriptors. - Chroot string // Chroot. - Credential *Credential // Credential. + Dir string // Current working directory. + Env []string // Environment. + Files []int // File descriptors. + Sys *SysProcAttr } -var zeroAttributes ProcAttr +type SysProcAttr struct { + Chroot string // Chroot. + Credential *Credential // Credential. + Ptrace bool // Enable tracing. + Setsid bool // Create session. +} + +var zeroProcAttr ProcAttr +var zeroSysProcAttr SysProcAttr func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { var p [2]int @@ -285,7 +290,11 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { var wstatus WaitStatus if attr == nil { - attr = &zeroAttributes + attr = &zeroProcAttr + } + sys := attr.Sys + if sys == nil { + sys = &zeroSysProcAttr } p[0] = -1 @@ -301,8 +310,8 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { } var chroot *byte - if attr.Chroot != "" { - chroot = StringBytePtr(attr.Chroot) + if sys.Chroot != "" { + chroot = StringBytePtr(sys.Chroot) } var dir *byte if attr.Dir != "" { @@ -326,7 +335,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { } // Kick off child. - pid, err = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, p[1]) + pid, err = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1]) if err != 0 { error: if p[0] >= 0 { diff --git a/src/pkg/syscall/exec_windows.go b/src/pkg/syscall/exec_windows.go index b25f4a65072..96a01e7675f 100644 --- a/src/pkg/syscall/exec_windows.go +++ b/src/pkg/syscall/exec_windows.go @@ -218,22 +218,32 @@ func joinExeDirAndFName(dir, p string) (name string, err int) { } type ProcAttr struct { - Dir string - Env []string - Files []int + Dir string + Env []string + Files []int + Sys *SysProcAttr +} + +type SysProcAttr struct { HideWindow bool CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess } -var zeroAttributes ProcAttr +var zeroProcAttr ProcAttr +var zeroSysProcAttr SysProcAttr func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err int) { if len(argv0) == 0 { return 0, 0, EWINDOWS } if attr == nil { - attr = &zeroAttributes + attr = &zeroProcAttr } + sys := attr.Sys + if sys == nil { + sys = &zeroSysProcAttr + } + if len(attr.Files) > 3 { return 0, 0, EWINDOWS } @@ -257,8 +267,8 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, // Windows CreateProcess takes the command line as a single string: // use attr.CmdLine if set, else build the command line by escaping // and joining each argument with spaces - if attr.CmdLine != "" { - cmdline = attr.CmdLine + if sys.CmdLine != "" { + cmdline = sys.CmdLine } else { cmdline = makeCmdLine(argv) } @@ -293,7 +303,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, si := new(StartupInfo) si.Cb = uint32(unsafe.Sizeof(*si)) si.Flags = STARTF_USESTDHANDLES - if attr.HideWindow { + if sys.HideWindow { si.Flags |= STARTF_USESHOWWINDOW si.ShowWindow = SW_HIDE }