| | |
| | |
| | |
| |
|
| | |
| |
|
| | package os |
| |
|
| | import ( |
| | "internal/strconv" |
| | "internal/syscall/execenv" |
| | "runtime" |
| | "syscall" |
| | ) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | var ( |
| | Interrupt Signal = syscall.SIGINT |
| | Kill Signal = syscall.SIGKILL |
| | ) |
| |
|
| | func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) { |
| | |
| | |
| | |
| | if attr != nil && attr.Sys == nil && attr.Dir != "" { |
| | if _, err := Stat(attr.Dir); err != nil { |
| | pe := err.(*PathError) |
| | pe.Op = "chdir" |
| | return nil, pe |
| | } |
| | } |
| |
|
| | attrSys, shouldDupPidfd := ensurePidfd(attr.Sys) |
| | sysattr := &syscall.ProcAttr{ |
| | Dir: attr.Dir, |
| | Env: attr.Env, |
| | Sys: attrSys, |
| | } |
| | if sysattr.Env == nil { |
| | sysattr.Env, err = execenv.Default(sysattr.Sys) |
| | if err != nil { |
| | return nil, err |
| | } |
| | } |
| | sysattr.Files = make([]uintptr, 0, len(attr.Files)) |
| | for _, f := range attr.Files { |
| | sysattr.Files = append(sysattr.Files, f.Fd()) |
| | } |
| |
|
| | pid, h, e := syscall.StartProcess(name, argv, sysattr) |
| |
|
| | |
| | runtime.KeepAlive(attr) |
| |
|
| | if e != nil { |
| | return nil, &PathError{Op: "fork/exec", Path: name, Err: e} |
| | } |
| |
|
| | |
| | if runtime.GOOS != "windows" { |
| | var ok bool |
| | h, ok = getPidfd(sysattr.Sys, shouldDupPidfd) |
| | if !ok { |
| | return newPIDProcess(pid), nil |
| | } |
| | } |
| |
|
| | return newHandleProcess(pid, h), nil |
| | } |
| |
|
| | func (p *Process) kill() error { |
| | return p.Signal(Kill) |
| | } |
| |
|
| | func (p *Process) withHandle(f func(handle uintptr)) error { |
| | if p.handle == nil { |
| | return ErrNoHandle |
| | } |
| | handle, status := p.handleTransientAcquire() |
| | switch status { |
| | case statusDone: |
| | return ErrProcessDone |
| | case statusReleased: |
| | return errProcessReleased |
| | } |
| | defer p.handleTransientRelease() |
| | f(handle) |
| |
|
| | return nil |
| | } |
| |
|
| | |
| | type ProcessState struct { |
| | pid int |
| | status syscall.WaitStatus |
| | rusage *syscall.Rusage |
| | } |
| |
|
| | |
| | func (p *ProcessState) Pid() int { |
| | return p.pid |
| | } |
| |
|
| | func (p *ProcessState) exited() bool { |
| | return p.status.Exited() |
| | } |
| |
|
| | func (p *ProcessState) success() bool { |
| | return p.status.ExitStatus() == 0 |
| | } |
| |
|
| | func (p *ProcessState) sys() any { |
| | return p.status |
| | } |
| |
|
| | func (p *ProcessState) sysUsage() any { |
| | return p.rusage |
| | } |
| |
|
| | func (p *ProcessState) String() string { |
| | if p == nil { |
| | return "<nil>" |
| | } |
| | status := p.Sys().(syscall.WaitStatus) |
| | res := "" |
| | switch { |
| | case status.Exited(): |
| | code := status.ExitStatus() |
| | if runtime.GOOS == "windows" && uint(code) >= 1<<16 { |
| | res = "exit status 0x" + strconv.FormatUint(uint64(code), 16) |
| | } else { |
| | res = "exit status " + strconv.Itoa(code) |
| | } |
| | case status.Signaled(): |
| | res = "signal: " + status.Signal().String() |
| | case status.Stopped(): |
| | res = "stop signal: " + status.StopSignal().String() |
| | if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != 0 { |
| | res += " (trap " + strconv.Itoa(status.TrapCause()) + ")" |
| | } |
| | case status.Continued(): |
| | res = "continued" |
| | } |
| | if status.CoreDump() { |
| | res += " (core dumped)" |
| | } |
| | return res |
| | } |
| |
|
| | |
| | |
| | func (p *ProcessState) ExitCode() int { |
| | |
| | if p == nil { |
| | return -1 |
| | } |
| | return p.status.ExitStatus() |
| | } |
| |
|