github.com/iDigitalFlame/xmt@v0.5.4/cmd/exec_plan9.go (about) 1 //go:build plan9 2 // +build plan9 3 4 // Copyright (C) 2020 - 2023 iDigitalFlame 5 // 6 // This program is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with this program. If not, see <https://www.gnu.org/licenses/>. 18 // 19 20 package cmd 21 22 import ( 23 "context" 24 "io" 25 "os" 26 "os/exec" 27 "sync/atomic" 28 "syscall" 29 30 "github.com/iDigitalFlame/xmt/cmd/filter" 31 "github.com/iDigitalFlame/xmt/util/xerr" 32 ) 33 34 type executable struct { 35 e *exec.Cmd 36 r *os.File 37 closers []io.Closer 38 } 39 40 func (e *executable) close() { 41 if len(e.closers) > 0 { 42 for i := range e.closers { 43 e.closers[i].Close() 44 } 45 } 46 } 47 func (executable) Resume() error { 48 return syscall.EINVAL 49 } 50 func (executable) Suspend() error { 51 return syscall.EINVAL 52 } 53 func (e *executable) Pid() uint32 { 54 if e.e.ProcessState != nil { 55 return uint32(e.e.ProcessState.Pid()) 56 } 57 return uint32(e.e.Process.Pid) 58 } 59 60 // ResumeProcess will attempt to resume the process via its PID. This will 61 // attempt to resume the process using an OS-dependent syscall. 62 // 63 // This will not affect already running processes. 64 func ResumeProcess(_ uint32) error { 65 return syscall.EINVAL 66 } 67 func (executable) Handle() uintptr { 68 return 0 69 } 70 71 // SuspendProcess will attempt to suspend the process via its PID. This will 72 // attempt to suspend the process using an OS-dependent syscall. 73 // 74 // This will not affect already suspended processes. 75 func SuspendProcess(_ uint32) error { 76 return syscall.EINVAL 77 } 78 func (e *executable) isStarted() bool { 79 return e.e != nil && e.e.Process != nil 80 } 81 func (e *executable) isRunning() bool { 82 return e.isStarted() && e.e.ProcessState == nil 83 } 84 func (e *executable) wait(p *Process) { 85 err := e.e.Wait() 86 if _, ok := err.(*exec.ExitError); err != nil && !ok { 87 p.stopWith(exitStopped, err) 88 return 89 } 90 if err2 := p.ctx.Err(); err2 != nil { 91 p.stopWith(exitStopped, err2) 92 return 93 } 94 if atomic.StoreUint32(&p.cookie, atomic.LoadUint32(&p.cookie)|cookieStopped); e.e.ProcessState != nil { 95 p.exit = uint32(e.e.ProcessState.Sys().(*syscall.Waitmsg).ExitStatus()) 96 } 97 if p.exit != 0 { 98 p.stopWith(p.exit, &ExitError{Exit: p.exit}) 99 return 100 } 101 p.stopWith(p.exit, nil) 102 } 103 104 func (executable) SetToken(_ uintptr) {} 105 func (executable) SetFullscreen(_ bool) {} 106 func (executable) SetWindowDisplay(_ int) {} 107 func (executable) SetWindowTitle(_ string) {} 108 func (executable) SetLogin(_, _, _ string) {} 109 func (executable) SetWindowSize(_, _ uint32) {} 110 func (executable) SetUID(_ int32, _ *Process) {} 111 func (executable) SetGID(_ int32, _ *Process) {} 112 func (executable) SetWindowPosition(_, _ uint32) {} 113 func (executable) SetChroot(_ string, _ *Process) {} 114 func (executable) SetNoWindow(_ bool, _ *Process) {} 115 func (executable) SetDetached(_ bool, _ *Process) {} 116 func (executable) SetSuspended(_ bool, _ *Process) {} 117 func (executable) SetNewConsole(_ bool, _ *Process) {} 118 func (e *executable) kill(x uint32, p *Process) error { 119 if p.exit = x; e.e == nil || e.e.Process == nil { 120 return p.err 121 } 122 return e.e.Process.Kill() 123 } 124 func (executable) SetParent(_ *filter.Filter, _ *Process) {} 125 func (e *executable) StdinPipe(p *Process) (io.WriteCloser, error) { 126 var err error 127 if p.Stdin, e.r, err = os.Pipe(); err != nil { 128 return nil, xerr.Wrap("unable to create Pipe", err) 129 } 130 e.closers = append(e.closers, p.Stdin.(io.Closer)) 131 return e.r, nil 132 } 133 func (e *executable) StdoutPipe(p *Process) (io.ReadCloser, error) { 134 r, w, err := os.Pipe() 135 if err != nil { 136 return nil, xerr.Wrap("unable to create Pipe", err) 137 } 138 p.Stdout = w 139 e.closers = append(e.closers, w) 140 return r, nil 141 } 142 func (e *executable) StderrPipe(p *Process) (io.ReadCloser, error) { 143 r, w, err := os.Pipe() 144 if err != nil { 145 return nil, xerr.Wrap("unable to create Pipe", err) 146 } 147 p.Stderr = w 148 e.closers = append(e.closers, w) 149 return r, nil 150 } 151 func (e *executable) start(x context.Context, p *Process, _ bool) error { 152 if e.e != nil { 153 return ErrAlreadyStarted 154 } 155 e.e = exec.CommandContext(x, p.Args[0]) 156 e.e.Dir, e.e.Env = p.Dir, p.Env 157 e.e.Stdin, e.e.Stdout, e.e.Stderr = p.Stdin, p.Stdout, p.Stderr 158 if e.e.Args = p.Args; !p.split { 159 z := os.Environ() 160 if e.e.Env == nil { 161 e.e.Env = make([]string, 0, len(z)) 162 } 163 e.e.Env = append(e.e.Env, z...) 164 } 165 if e.r != nil { 166 e.r.Close() 167 e.r = nil 168 } 169 if err := e.e.Start(); err != nil { 170 return err 171 } 172 go e.wait(p) 173 return nil 174 }