mirror of https://github.com/Hilbis/Hilbish
feat: suspend jobs via ctrl z
this was a bit of a pain to add and it still doesnt work properly on this commit, but this is how it would be donejob-suspend
parent
26c8f28034
commit
f239363a60
22
api.go
22
api.go
|
@ -47,6 +47,7 @@ var exports = map[string]util.LuaExport{
|
||||||
"interval": {hlinterval, 2, false},
|
"interval": {hlinterval, 2, false},
|
||||||
"read": {hlread, 1, false},
|
"read": {hlread, 1, false},
|
||||||
"run": {hlrun, 1, true},
|
"run": {hlrun, 1, true},
|
||||||
|
"suspend": {hlsuspend, 0, false},
|
||||||
"timeout": {hltimeout, 2, false},
|
"timeout": {hltimeout, 2, false},
|
||||||
"which": {hlwhich, 1, false},
|
"which": {hlwhich, 1, false},
|
||||||
}
|
}
|
||||||
|
@ -88,6 +89,12 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("hilbish.hinter has to be a function")
|
return nil, errors.New("hilbish.hinter has to be a function")
|
||||||
}
|
}
|
||||||
|
} else if k == "suspend" {
|
||||||
|
var err error
|
||||||
|
_, err = c.ClosureArg(2)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("hilbish.suspend has to be a function")
|
||||||
|
}
|
||||||
} else if modVal := mod.Get(rt.StringValue(k)); modVal != rt.NilValue {
|
} else if modVal := mod.Get(rt.StringValue(k)); modVal != rt.NilValue {
|
||||||
return nil, errors.New("not allowed to override in hilbish table")
|
return nil, errors.New("not allowed to override in hilbish table")
|
||||||
}
|
}
|
||||||
|
@ -213,7 +220,7 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var exitcode uint8
|
var exitcode uint8
|
||||||
stdout, stderr, err := execCommand(cmd, terminalOut)
|
stdout, stderr, err := execCommand(cmd, terminalOut, false)
|
||||||
|
|
||||||
if code, ok := interp.IsExitStatus(err); ok {
|
if code, ok := interp.IsExitStatus(err); ok {
|
||||||
exitcode = code
|
exitcode = code
|
||||||
|
@ -643,3 +650,16 @@ func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// suspend()
|
||||||
|
// Suspends the currently running process. This can (and should be) overwritten if a
|
||||||
|
// a different runner is being used.
|
||||||
|
// --- @param line string
|
||||||
|
func hlsuspend(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
if currentHandle != nil || currentHandle.Process != nil {
|
||||||
|
j := jobs.addFromHandler(currentCmd, currentHandle)
|
||||||
|
j.suspend()
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Next(), nil
|
||||||
|
}
|
||||||
|
|
52
exec.go
52
exec.go
|
@ -27,6 +27,8 @@ import (
|
||||||
var errNotExec = errors.New("not executable")
|
var errNotExec = errors.New("not executable")
|
||||||
var errNotFound = errors.New("not found")
|
var errNotFound = errors.New("not found")
|
||||||
var runnerMode rt.Value = rt.StringValue("hybrid")
|
var runnerMode rt.Value = rt.StringValue("hybrid")
|
||||||
|
var currentHandle *exec.Cmd
|
||||||
|
var currentCmd string
|
||||||
|
|
||||||
type execError struct{
|
type execError struct{
|
||||||
typ string
|
typ string
|
||||||
|
@ -233,7 +235,7 @@ func handleSh(cmdString string) (input string, exitCode uint8, cont bool, runErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func execSh(cmdString string) (string, uint8, bool, error) {
|
func execSh(cmdString string) (string, uint8, bool, error) {
|
||||||
_, _, err := execCommand(cmdString, true)
|
_, _, err := execCommand(cmdString, true, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If input is incomplete, start multiline prompting
|
// If input is incomplete, start multiline prompting
|
||||||
if syntax.IsIncomplete(err) {
|
if syntax.IsIncomplete(err) {
|
||||||
|
@ -254,7 +256,7 @@ func execSh(cmdString string) (string, uint8, bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run command in sh interpreter
|
// Run command in sh interpreter
|
||||||
func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) {
|
func execCommand(cmd string, terminalOut, interactiveCall bool) (io.Writer, io.Writer, error) {
|
||||||
file, err := syntax.NewParser().Parse(strings.NewReader(cmd), "")
|
file, err := syntax.NewParser().Parse(strings.NewReader(cmd), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -277,16 +279,21 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) {
|
||||||
var bg bool
|
var bg bool
|
||||||
for _, stmt := range file.Stmts {
|
for _, stmt := range file.Stmts {
|
||||||
bg = false
|
bg = false
|
||||||
|
printer.Print(buf, stmt.Cmd)
|
||||||
|
|
||||||
|
stmtStr := buf.String()
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
if interactiveCall {
|
||||||
|
currentCmd = stmtStr
|
||||||
|
}
|
||||||
|
|
||||||
if stmt.Background {
|
if stmt.Background {
|
||||||
bg = true
|
bg = true
|
||||||
printer.Print(buf, stmt.Cmd)
|
|
||||||
|
|
||||||
stmtStr := buf.String()
|
|
||||||
buf.Reset()
|
|
||||||
jobs.add(stmtStr, []string{}, "")
|
jobs.add(stmtStr, []string{}, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
interp.ExecHandler(execHandle(bg))(runner)
|
interp.ExecHandler(execHandle(bg, interactiveCall))(runner)
|
||||||
err = runner.Run(context.TODO(), stmt)
|
err = runner.Run(context.TODO(), stmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return stdout, stderr, err
|
return stdout, stderr, err
|
||||||
|
@ -296,7 +303,7 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) {
|
||||||
return stdout, stderr, nil
|
return stdout, stderr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func execHandle(bg bool) interp.ExecHandlerFunc {
|
func execHandle(bg, interactiveCall bool) interp.ExecHandlerFunc {
|
||||||
return func(ctx context.Context, args []string) error {
|
return func(ctx context.Context, args []string) error {
|
||||||
_, argstring := splitInput(strings.Join(args, " "))
|
_, argstring := splitInput(strings.Join(args, " "))
|
||||||
// i dont really like this but it works
|
// i dont really like this but it works
|
||||||
|
@ -410,6 +417,10 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
Stderr: hc.Stderr,
|
Stderr: hc.Stderr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if interactiveCall {
|
||||||
|
currentHandle = &cmd
|
||||||
|
}
|
||||||
|
|
||||||
var j *job
|
var j *job
|
||||||
if bg {
|
if bg {
|
||||||
j = jobs.getLatest()
|
j = jobs.getLatest()
|
||||||
|
@ -419,6 +430,8 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
err = cmd.Start()
|
err = cmd.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
execDone := make(chan struct{}, 1)
|
||||||
|
var suspended bool
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if done := ctx.Done(); done != nil {
|
if done := ctx.Done(); done != nil {
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -440,12 +453,31 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cmd.Wait()
|
go func() {
|
||||||
|
hooks.On("job.suspend", func(args ...interface{}) {
|
||||||
|
fmt.Println("suspended")
|
||||||
|
val := args[0].(rt.Value)
|
||||||
|
|
||||||
|
_, ok := valueToJob(val)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
suspended = true
|
||||||
|
execDone <- struct{}{}
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
err = cmd.Wait()
|
||||||
|
execDone <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-execDone
|
||||||
}
|
}
|
||||||
|
|
||||||
exit := handleExecErr(err)
|
exit := handleExecErr(err)
|
||||||
|
|
||||||
if bg {
|
if bg && !suspended {
|
||||||
j.exitCode = int(exit)
|
j.exitCode = int(exit)
|
||||||
j.finish()
|
j.finish()
|
||||||
}
|
}
|
||||||
|
|
12
job.go
12
job.go
|
@ -268,6 +268,18 @@ func (j *jobHandler) add(cmd string, args []string, path string) *job {
|
||||||
return jb
|
return jb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (j *jobHandler) addFromHandler(cmd string, handler *exec.Cmd) *job {
|
||||||
|
jb := j.add(cmd, handler.Args, handler.Path)
|
||||||
|
if ps := handler.ProcessState; ps != nil {
|
||||||
|
if !ps.Exited() && ps.Pid() != 0 {
|
||||||
|
jb.running = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jb.setHandle(handler)
|
||||||
|
|
||||||
|
return jb
|
||||||
|
}
|
||||||
|
|
||||||
func (j *jobHandler) getLatest() *job {
|
func (j *jobHandler) getLatest() *job {
|
||||||
j.mu.RLock()
|
j.mu.RLock()
|
||||||
defer j.mu.RUnlock()
|
defer j.mu.RUnlock()
|
||||||
|
|
12
job_unix.go
12
job_unix.go
|
@ -8,6 +8,7 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
rt "github.com/arnodel/golua/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (j *job) foreground() error {
|
func (j *job) foreground() error {
|
||||||
|
@ -36,3 +37,14 @@ func (j *job) background() error {
|
||||||
proc.Signal(syscall.SIGCONT)
|
proc.Signal(syscall.SIGCONT)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (j *job) suspend() error {
|
||||||
|
proc := j.handle.Process
|
||||||
|
if proc == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
proc.Signal(syscall.SIGSTOP)
|
||||||
|
hooks.Emit("job.suspend", rt.UserDataValue(j.ud))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -13,3 +13,7 @@ func (j *job) foreground() error {
|
||||||
func (j *job) background() error {
|
func (j *job) background() error {
|
||||||
return errors.New("not supported on windows")
|
return errors.New("not supported on windows")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (j *job) suspend() error {
|
||||||
|
return errors.New("not supported on windows")
|
||||||
|
}
|
||||||
|
|
|
@ -3,23 +3,32 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"syscall"
|
"syscall"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
|
||||||
|
rt "github.com/arnodel/golua/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func handleSignals() {
|
func handleSignals() {
|
||||||
c := make(chan os.Signal)
|
c := make(chan os.Signal)
|
||||||
signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN, syscall.SIGTSTP)
|
signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN)
|
||||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGWINCH, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGQUIT)
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGWINCH, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGQUIT, syscall.SIGTSTP)
|
||||||
|
|
||||||
for s := range c {
|
for s := range c {
|
||||||
switch s {
|
switch s {
|
||||||
case os.Interrupt: hooks.Emit("signal.sigint")
|
case os.Interrupt: hooks.Emit("signal.sigint")
|
||||||
case syscall.SIGTERM: exit(0)
|
case syscall.SIGTERM: exit(0)
|
||||||
case syscall.SIGWINCH: hooks.Emit("signal.resize")
|
case syscall.SIGWINCH: hooks.Emit("signal.resize")
|
||||||
case syscall.SIGUSR1: hooks.Emit("signal.sigusr1")
|
case syscall.SIGUSR1: hooks.Emit("signal.sigusr1")
|
||||||
case syscall.SIGUSR2: hooks.Emit("signal.sigusr2")
|
case syscall.SIGUSR2: hooks.Emit("signal.sigusr2")
|
||||||
|
case syscall.SIGTSTP:
|
||||||
|
suspendHandler := hshMod.Get(rt.StringValue("suspend"))
|
||||||
|
_, err := rt.Call1(l.MainThread(), suspendHandler)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue