leech/process.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
}