diff --git a/exec.go b/exec.go index 3c41d5f..2e0e0b1 100644 --- a/exec.go +++ b/exec.go @@ -239,7 +239,7 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) { stmtStr := buf.String() buf.Reset() - jobs.add(stmtStr) + jobs.add(stmtStr, []string{}, "") } interp.ExecHandler(execHandle(bg))(runner) @@ -357,13 +357,15 @@ func execHandle(bg bool) interp.ExecHandlerFunc { Stderr: hc.Stderr, } - err = cmd.Start() var j *job if bg { j = jobs.getLatest() - j.setHandle(cmd.Process) - j.start(cmd.Process.Pid) + j.setHandle(&cmd) + err = j.start() + } else { + err = cmd.Start() } + if err == nil { if done := ctx.Done(); done != nil { go func() { @@ -388,35 +390,8 @@ func execHandle(bg bool) interp.ExecHandlerFunc { err = cmd.Wait() } - var exit uint8 - switch x := err.(type) { - case *exec.ExitError: - // started, but errored - default to 1 if OS - // doesn't have exit statuses - if status, ok := x.Sys().(syscall.WaitStatus); ok { - if status.Signaled() { - if ctx.Err() != nil { - return ctx.Err() - } - exit = uint8(128 + status.Signal()) - goto end - } - exit = uint8(status.ExitStatus()) - goto end - } - exit = 1 - goto end - case *exec.Error: - // did not start - fmt.Fprintf(hc.Stderr, "%v\n", err) - exit = 127 - goto end - case nil: - goto end - default: - return err - } - end: + exit := handleExecErr(err) + if bg { j.exitCode = int(exit) j.finish() @@ -425,6 +400,35 @@ func execHandle(bg bool) interp.ExecHandlerFunc { } } +func handleExecErr(err error) (exit uint8) { + ctx := context.TODO() + + switch x := err.(type) { + case *exec.ExitError: + // started, but errored - default to 1 if OS + // doesn't have exit statuses + if status, ok := x.Sys().(syscall.WaitStatus); ok { + if status.Signaled() { + if ctx.Err() != nil { + return + } + exit = uint8(128 + status.Signal()) + return + } + exit = uint8(status.ExitStatus()) + return + } + exit = 1 + return + case *exec.Error: + // did not start + //fmt.Fprintf(hc.Stderr, "%v\n", err) + exit = 127 + default: return + } + + return +} func lookpath(file string) error { // custom lookpath function so we know if a command is found *and* is executable var skip []string if runtime.GOOS == "windows" { diff --git a/job.go b/job.go index d20666b..8f63f95 100644 --- a/job.go +++ b/job.go @@ -3,10 +3,12 @@ package main import ( "sync" "os" + "os/exec" "hilbish/util" rt "github.com/arnodel/golua/runtime" + "github.com/arnodel/golua/lib/iolib" ) var jobs *jobHandler @@ -17,18 +19,51 @@ type job struct { id int pid int exitCode int - proc *os.Process + once bool + args []string + // save path for a few reasons, one being security (lmao) while the other + // would just be so itll be the same binary command always (path changes) + path string + handle *exec.Cmd + stdin *iolib.File + stdout *iolib.File + stderr *iolib.File } -func (j *job) start(pid int) { - j.pid = pid +func (j *job) start() error { + if j.handle == nil || j.once { + // cmd cant be reused so make a new one + cmd := exec.Cmd{ + Path: j.path, + Args: j.args, + Stdin: j.getStdio("in"), + Stdout: j.getStdio("out"), + Stderr: j.getStdio("err"), + } + j.setHandle(&cmd) + } + + if !j.once { + j.once = true + } + + err := j.handle.Start() + proc := j.getProc() + + j.pid = proc.Pid j.running = true + hooks.Em.Emit("job.start", j.lua()) + + return err } func (j *job) stop() { // finish will be called in exec handle - j.proc.Kill() + proc := j.getProc() + if proc != nil { + proc.Kill() + } } func (j *job) finish() { @@ -36,13 +71,38 @@ func (j *job) finish() { hooks.Em.Emit("job.done", j.lua()) } -func (j *job) setHandle(handle *os.Process) { - j.proc = handle +func (j *job) setHandle(handle *exec.Cmd) { + j.handle = handle + j.args = handle.Args + j.path = handle.Path +} + +func (j *job) getProc() *os.Process { + handle := j.handle + if handle != nil { + return handle.Process + } + + return nil +} + +func (j *job) getStdio(typ string) *os.File { + // TODO: make this use std io/out/err values from job struct, + // which are lua files + var stdio *os.File + switch typ { + case "in": stdio = os.Stdin + case "out": stdio = os.Stdout + case "err": stdio = os.Stderr + } + + return stdio } func (j *job) lua() rt.Value { jobFuncs := map[string]util.LuaExport{ "stop": {j.luaStop, 0, false}, + "start": {j.luaStart, 0, false}, } luaJob := rt.NewTable() util.SetExports(l, luaJob, jobFuncs) @@ -56,6 +116,17 @@ func (j *job) lua() rt.Value { return rt.TableValue(luaJob) } +func (j *job) luaStart(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + if !j.running { + err := j.start() + exit := handleExecErr(err) + j.exitCode = int(exit) + j.finish() + } + + return c.Next(), nil +} + func (j *job) luaStop(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if j.running { j.stop() @@ -79,7 +150,7 @@ func newJobHandler() *jobHandler { } } -func (j *jobHandler) add(cmd string) *job { +func (j *jobHandler) add(cmd string, args []string, path string) *job { j.mu.Lock() defer j.mu.Unlock() @@ -88,6 +159,7 @@ func (j *jobHandler) add(cmd string) *job { cmd: cmd, running: false, id: j.latestID, + args: args, } j.jobs[j.latestID] = jb @@ -145,8 +217,19 @@ func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err != nil { return nil, err } + largs, err := c.TableArg(1) + if err != nil { + return nil, err + } - jb := j.add(cmd) + var args []string + util.ForEach(largs, func(k rt.Value, v rt.Value) { + if v.Type() == rt.StringType { + args = append(args, v.AsString()) + } + }) + // TODO: change to lookpath for args[0] + jb := j.add(cmd, args, args[0]) return c.PushingNext1(t.Runtime, jb.lua()), nil }