-
Notifications
You must be signed in to change notification settings - Fork 293
Add forked version of os related stdlib packages #1134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # Why | ||
| Package stdlib is a fork of a small set of packages from the go stdlib and specifically the os, syscall, os/exec, and /internal/syscall/execenv packages. | ||
| It exists because the process execution mechanism in the stdlib (os/exec.Cmd and everything in it's call chain all the way down to syscall.StartProcess) | ||
| currently don't expose what's necessary to be able to accomplish some things that we need on Windows. This boils down to three things currently. | ||
|
|
||
| 1. `exec.Cmd.Start()` calls `exec.LookPath` which looks for certain windows extensions to launch an executable at the path provided and will fail if | ||
| one is not found. Although rare, there are cases of binaries with no extension that are perfectly valid and able to be launched by `CreateProcessW`. | ||
| Granted this can be worked around by setting PATHEXT to anything with an empty space entry after the colon separator, so this is more of an added | ||
| annoyance for the other two reasons. | ||
| For example: | ||
| ```go | ||
| os.Setenv("PATHEXT", ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.CPL; ") | ||
| ``` | ||
|
|
||
| 2. For job containers we'd like the ability to launch a process in a job directly as it's created, instead of launching it and assigning it to the | ||
| job slightly afterwards. This introduces a small window where the process is unaccounted for and not in the "container". | ||
| The desired behavior can be accomplished in Windows by calling [UpdateProcThreadAtrribute](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute) | ||
| and using the constant `PROC_THREAD_ATTRIBUTE_JOB_LIST`. For the stdlib to support this it would need a new field off of syscall.SysProcAttr to be | ||
| able to pass in the job object handle that you'd like the process added to. However, there is no way to create a job object in the stdlib itself | ||
| and the syscall package is locked down for the most part. | ||
|
|
||
| 3. Almost same story as 2, but in this case we'd like to support assigning a [pseudo console](https://docs.microsoft.com/en-us/windows/console/createpseudoconsole) | ||
| to a process. There's no exposed way to pass in the pseudo console handle and there's no syscall package support for making one in the first place. | ||
|
|
||
|
|
||
| The stdlib packages have been modified to use the x/sys/windows package where needed, removed some unneccesary functionality that we | ||
| don't need, as well as added the additions described above. | ||
|
|
||
| The fork is from Go 1.17 with HEAD at a6ff433d6a927e8ad8eaa6828127233296d12ce5. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| // Copyright 2020 The Go Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style | ||
| // license that can be found in the LICENSE file. | ||
|
|
||
| //go:build windows | ||
| // +build windows | ||
|
|
||
| package execenv | ||
|
|
||
| import ( | ||
| "unicode/utf16" | ||
| "unsafe" | ||
|
|
||
| "github.com/Microsoft/hcsshim/internal/stdlib/syscall" | ||
| "golang.org/x/sys/windows" | ||
| ) | ||
|
|
||
| // Default will return the default environment | ||
| // variables based on the process attributes | ||
| // provided. | ||
| // | ||
| // If the process attributes contain a token, then | ||
| // the environment variables will be sourced from | ||
| // the defaults for that user token, otherwise they | ||
| // will be sourced from syscall.Environ(). | ||
| func Default(sys *syscall.SysProcAttr) (env []string, err error) { | ||
| if sys == nil || sys.Token == 0 { | ||
| return windows.Environ(), nil | ||
| } | ||
| var block *uint16 | ||
| err = windows.CreateEnvironmentBlock(&block, sys.Token, false) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| defer windows.DestroyEnvironmentBlock(block) // nolint: errcheck | ||
| blockp := uintptr(unsafe.Pointer(block)) | ||
| for { | ||
|
|
||
| // find NUL terminator | ||
| end := unsafe.Pointer(blockp) | ||
| for *(*uint16)(end) != 0 { | ||
| end = unsafe.Pointer(uintptr(end) + 2) | ||
| } | ||
|
|
||
| n := (uintptr(end) - uintptr(unsafe.Pointer(blockp))) / 2 | ||
| if n == 0 { | ||
| // environment block ends with empty string | ||
| break | ||
| } | ||
|
|
||
| entry := (*[(1 << 30) - 1]uint16)(unsafe.Pointer(blockp))[:n:n] | ||
| env = append(env, string(utf16.Decode(entry))) | ||
| blockp += 2 * (uintptr(len(entry)) + 1) | ||
| } | ||
| return | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| // Copyright 2021 The Go Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style | ||
| // license that can be found in the LICENSE file. | ||
|
|
||
| // Simple conversions to avoid depending on strconv. | ||
|
|
||
| package itoa | ||
|
|
||
| // Itoa converts val to a decimal string. | ||
| func Itoa(val int) string { | ||
| if val < 0 { | ||
| return "-" + Uitoa(uint(-val)) | ||
| } | ||
| return Uitoa(uint(val)) | ||
| } | ||
|
|
||
| // Uitoa converts val to a decimal string. | ||
| func Uitoa(val uint) string { | ||
| if val == 0 { // avoid string allocation | ||
| return "0" | ||
| } | ||
| var buf [20]byte // big enough for 64bit value base 10 | ||
| i := len(buf) - 1 | ||
| for val >= 10 { | ||
| q := val / 10 | ||
| buf[i] = byte('0' + val - q*10) | ||
| i-- | ||
| val = q | ||
| } | ||
| // val < 10 | ||
| buf[i] = byte('0' + val) | ||
| return string(buf[i:]) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,175 @@ | ||
| // Copyright 2009 The Go Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style | ||
| // license that can be found in the LICENSE file. | ||
|
|
||
| package os | ||
|
|
||
| import ( | ||
| "errors" | ||
| "os" | ||
| "runtime" | ||
| "sync/atomic" | ||
| "time" | ||
|
|
||
| "github.com/Microsoft/hcsshim/internal/stdlib/syscall" | ||
| "golang.org/x/sys/windows" | ||
| ) | ||
|
|
||
| // ErrProcessDone indicates a Process has finished. | ||
| var ErrProcessDone = errors.New("os: process already finished") | ||
|
|
||
| // Process stores the information about a process created by StartProcess. | ||
| type Process struct { | ||
| Pid int | ||
| handle uintptr // handle is accessed atomically on Windows | ||
| isdone uint32 // process has been successfully waited on, non zero if true | ||
| } | ||
|
|
||
| func newProcess(pid int, handle uintptr) *Process { | ||
| p := &Process{Pid: pid, handle: handle} | ||
| runtime.SetFinalizer(p, (*Process).Release) | ||
| return p | ||
| } | ||
|
|
||
| func (p *Process) setDone() { | ||
| atomic.StoreUint32(&p.isdone, 1) | ||
| } | ||
|
|
||
| func (p *Process) done() bool { | ||
| return atomic.LoadUint32(&p.isdone) > 0 | ||
| } | ||
|
|
||
| // ProcAttr holds the attributes that will be applied to a new process | ||
| // started by StartProcess. | ||
| type ProcAttr struct { | ||
| // If Dir is non-empty, the child changes into the directory before | ||
| // creating the process. | ||
| Dir string | ||
| // If Env is non-nil, it gives the environment variables for the | ||
| // new process in the form returned by Environ. | ||
| // If it is nil, the result of Environ will be used. | ||
| Env []string | ||
| // Files specifies the open files inherited by the new process. The | ||
| // first three entries correspond to standard input, standard output, and | ||
| // standard error. An implementation may support additional entries, | ||
| // depending on the underlying operating system. A nil entry corresponds | ||
| // to that file being closed when the process starts. | ||
| // On Unix systems, StartProcess will change these File values | ||
| // to blocking mode, which means that SetDeadline will stop working | ||
| // and calling Close will not interrupt a Read or Write. | ||
| Files []*os.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 | ||
| } | ||
|
|
||
| // A Signal represents an operating system signal. | ||
| // The usual underlying implementation is operating system-dependent: | ||
| // on Unix it is syscall.Signal. | ||
| type Signal interface { | ||
| String() string | ||
| Signal() // to distinguish from other Stringers | ||
| } | ||
|
|
||
| // Getpid returns the process id of the caller. | ||
| func Getpid() int { return windows.Getpid() } | ||
|
|
||
| // Getppid returns the process id of the caller's parent. | ||
| func Getppid() int { return windows.Getppid() } | ||
|
|
||
| // FindProcess looks for a running process by its pid. | ||
| // | ||
| // The Process it returns can be used to obtain information | ||
| // about the underlying operating system process. | ||
| // | ||
| // On Unix systems, FindProcess always succeeds and returns a Process | ||
| // for the given pid, regardless of whether the process exists. | ||
| func FindProcess(pid int) (*Process, error) { | ||
| return findProcess(pid) | ||
| } | ||
|
|
||
| // StartProcess starts a new process with the program, arguments and attributes | ||
| // specified by name, argv and attr. The argv slice will become os.Args in the | ||
| // new process, so it normally starts with the program name. | ||
| // | ||
| // If the calling goroutine has locked the operating system thread | ||
| // with runtime.LockOSThread and modified any inheritable OS-level | ||
| // thread state (for example, Linux or Plan 9 name spaces), the new | ||
| // process will inherit the caller's thread state. | ||
| // | ||
| // StartProcess is a low-level interface. The os/exec package provides | ||
| // higher-level interfaces. | ||
| // | ||
| // If there is an error, it will be of type *PathError. | ||
| func StartProcess(name string, argv []string, attr *ProcAttr) (*Process, error) { | ||
| return startProcess(name, argv, attr) | ||
| } | ||
|
|
||
| // Release releases any resources associated with the Process p, | ||
| // rendering it unusable in the future. | ||
| // Release only needs to be called if Wait is not. | ||
| func (p *Process) Release() error { | ||
| return p.release() | ||
| } | ||
|
|
||
| // Kill causes the Process to exit immediately. Kill does not wait until | ||
| // the Process has actually exited. This only kills the Process itself, | ||
| // not any other processes it may have started. | ||
| func (p *Process) Kill() error { | ||
| return p.kill() | ||
| } | ||
|
|
||
| // Wait waits for the Process to exit, and then returns a | ||
| // ProcessState describing its status and an error, if any. | ||
| // Wait releases any resources associated with the Process. | ||
| // On most operating systems, the Process must be a child | ||
| // of the current process or an error will be returned. | ||
| func (p *Process) Wait() (*ProcessState, error) { | ||
| return p.wait() | ||
| } | ||
|
|
||
| // Signal sends a signal to the Process. | ||
| // Sending Interrupt on Windows is not implemented. | ||
| func (p *Process) Signal(sig Signal) error { | ||
| return p.signal(sig) | ||
| } | ||
|
|
||
| // UserTime returns the user CPU time of the exited process and its children. | ||
| func (p *ProcessState) UserTime() time.Duration { | ||
| return p.userTime() | ||
| } | ||
|
|
||
| // SystemTime returns the system CPU time of the exited process and its children. | ||
| func (p *ProcessState) SystemTime() time.Duration { | ||
| return p.systemTime() | ||
| } | ||
|
|
||
| // Exited reports whether the program has exited. | ||
| func (p *ProcessState) Exited() bool { | ||
| return p.exited() | ||
| } | ||
|
|
||
| // Success reports whether the program exited successfully, | ||
| // such as with exit status 0 on Unix. | ||
| func (p *ProcessState) Success() bool { | ||
| return p.success() | ||
| } | ||
|
|
||
| // Sys returns system-dependent exit information about | ||
| // the process. Convert it to the appropriate underlying | ||
| // type, such as syscall.WaitStatus on Unix, to access its contents. | ||
| func (p *ProcessState) Sys() interface{} { | ||
| return p.sys() | ||
| } | ||
|
|
||
| // SysUsage returns system-dependent resource usage information about | ||
| // the exited process. Convert it to the appropriate underlying | ||
| // type, such as *syscall.Rusage on Unix, to access its contents. | ||
| // (On Unix, *syscall.Rusage matches struct rusage as defined in the | ||
| // getrusage(2) manual page.) | ||
| func (p *ProcessState) SysUsage() interface{} { | ||
| return p.sysUsage() | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this not build without replacing
syscallwithwindows? Just wondering if we can avoid that so as to keep deviation from upstream as little as possible.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No this would build fine, I swapped to Windows as there's a x/sys/Windows replacement/mirror for everything in syscall and technically the syscall package is locked and some of it is deprecated. I can swap to syscall if preferred. @anmaxvl What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I misread the original code a bit, I thought it was
internal/syscalland we replaced it withwindows, turns out it was originallyinternal/syscall/windowsandsyscallwas imported on its own.I agree with @ambarve that we should probably keep
syscallhere.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will try and add everything thats touched in and update, I'll ping when done!