128 lines
2.5 KiB
Go
128 lines
2.5 KiB
Go
package leech
|
|
|
|
import (
|
|
"os"
|
|
"sync"
|
|
)
|
|
|
|
var (
|
|
// cwd is the current working directory of an executable.
|
|
cwd, _ = os.Executable()
|
|
)
|
|
|
|
// The Process struct is a structure
|
|
// defining a basic process that a leech will
|
|
// execute and that may be eventually hooked in to.
|
|
type Process struct {
|
|
// The status of the process, whether it is a daemon or not.
|
|
isDaemon bool
|
|
// The synchronization mutex.
|
|
mu *sync.Mutex
|
|
// The execution working directory. If it is not specified
|
|
// at struct creation, the wd becomes the current working directory (cwd).
|
|
wd string
|
|
// The actual status of the process.
|
|
// TODO: (@bfu4) we need a hook to handle process lifecycle events.
|
|
status Status
|
|
// The data used in the creation of the process.
|
|
data *ProcessData
|
|
// The internal golang process.
|
|
internal *os.Process
|
|
}
|
|
|
|
// ProcessData is a simple structure
|
|
// defining data needed to create
|
|
// the actual process executed.
|
|
type ProcessData struct {
|
|
// The name of the executable.
|
|
Name string
|
|
// The arguments.
|
|
Argv []string
|
|
// Other information.
|
|
Attr *os.ProcAttr
|
|
}
|
|
|
|
func At(wd string) *Leech {
|
|
proc := &Process{
|
|
isDaemon: false,
|
|
mu: &sync.Mutex{},
|
|
wd: wd,
|
|
status: Inactive,
|
|
data: nil,
|
|
}
|
|
meta := &Meta{
|
|
pid: -1,
|
|
process: proc,
|
|
status: Inactive,
|
|
}
|
|
return &Leech{meta: meta, pid: meta.pid}
|
|
}
|
|
|
|
func AtOf(wd string, data *ProcessData) (leech *Leech) {
|
|
leech = At(wd)
|
|
leech.meta.process.data = data
|
|
return
|
|
}
|
|
|
|
func Of(data *ProcessData) (leech *Leech) {
|
|
leech = New()
|
|
leech.meta.process.data = data
|
|
return
|
|
}
|
|
|
|
func New() *Leech {
|
|
return At(cwd)
|
|
}
|
|
|
|
func (p *Process) start() bool {
|
|
if p.status > Zombie {
|
|
return false
|
|
}
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
var err error
|
|
p.internal, err = os.StartProcess(p.data.Name, p.data.Argv, p.data.Attr)
|
|
if err != nil {
|
|
p.status = Inactive
|
|
}
|
|
p.status = Alive
|
|
return p.status > Dead
|
|
}
|
|
|
|
// Daemonize will daemonize the process.
|
|
func (p *Process) Daemonize() bool {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
p.start()
|
|
p.isDaemon = true
|
|
return p.isDaemon
|
|
}
|
|
|
|
func (p *Process) IsDaemon() bool {
|
|
return p.isDaemon
|
|
}
|
|
|
|
func (p *Process) Die() {
|
|
// It's already dead and there may or may
|
|
// not be a pointer to it.
|
|
//
|
|
// We can avoid a null pointer dereference
|
|
// by doing this check as well.
|
|
if p.status < Zombie {
|
|
return
|
|
}
|
|
// Try to gracefully kill the process first.
|
|
p.gracefullyDie()
|
|
defer func() {
|
|
_ = p.internal.Kill()
|
|
}()
|
|
}
|
|
|
|
func (p *Process) gracefullyDie() bool {
|
|
if p.internal != nil {
|
|
_, err := p.internal.Wait()
|
|
return err != nil
|
|
}
|
|
return false
|
|
}
|