From 8697344f98bd0eec14a975d0298c435dacbe3e0f Mon Sep 17 00:00:00 2001 From: sammyette <38820196+TorchedSammy@users.noreply.github.com> Date: Tue, 24 May 2022 19:15:02 -0400 Subject: [PATCH] feat: implement job foregrounding/backgrounding (#155) --- go.mod | 12 ++++++------ go.sum | 10 ++++++++++ job.go | 39 +++++++++++++++++++++++++++++++++++++++ job_unix.go | 38 ++++++++++++++++++++++++++++++++++++++ job_windows.go | 15 +++++++++++++++ nature/commands/bg.lua | 15 +++++++++++++++ nature/commands/fg.lua | 15 +++++++++++++++ nature/commands/init.lua | 1 + signal_unix.go | 1 + 9 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 job_unix.go create mode 100644 job_windows.go create mode 100644 nature/commands/bg.lua create mode 100644 nature/commands/fg.lua diff --git a/go.mod b/go.mod index 6974c6b..825dae0 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,9 @@ require ( github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 github.com/maxlandon/readline v0.1.0-beta.0.20211027085530-2b76cabb8036 github.com/pborman/getopt v1.1.0 - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 - mvdan.cc/sh/v3 v3.4.3 + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a + golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 + mvdan.cc/sh/v3 v3.5.1 ) require ( @@ -18,12 +19,11 @@ require ( github.com/evilsocket/islazy v1.10.6 // indirect github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 // indirect github.com/rivo/uniseg v0.2.0 // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect - golang.org/x/text v0.3.6 // indirect + golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect + golang.org/x/text v0.3.7 // indirect ) -replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e +replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b replace github.com/maxlandon/readline => ./readline diff --git a/go.sum b/go.sum index c81fd1e..c313c19 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3 h1:I/wWr40FFLFF9pbT github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE= github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e h1:P2XupP8SaylWaudD1DqbWtZ3mIa8OsE9635LmR+Q+lg= github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs= +github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b h1:s5eDMhBk6H1BgipgLub/gv9qeyBaTuiHM0k3h2/9TSE= +github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/arnodel/edit v0.0.0-20220202110212-dfc8d7a13890/go.mod h1:AcpttpuZBaL9xl8/CX+Em4fBTUbwIkJ66RiAsJlNrBk= @@ -45,6 +47,8 @@ github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4Rx github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4= +golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -52,13 +56,19 @@ golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210916214954-140adaaadfaf/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8= +golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= diff --git a/job.go b/job.go index a0bdcb5..5ffb61f 100644 --- a/job.go +++ b/job.go @@ -111,6 +111,8 @@ func (j *job) lua() rt.Value { jobFuncs := map[string]util.LuaExport{ "stop": {j.luaStop, 0, false}, "start": {j.luaStart, 0, false}, + "foreground": {j.luaForeground, 0, false}, + "background": {j.luaBackground, 0, false}, } luaJob := rt.NewTable() util.SetExports(l, luaJob, jobFuncs) @@ -146,9 +148,46 @@ func (j *job) luaStop(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.Next(), nil } +func (j *job) luaForeground(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + if !j.running { + return nil, errors.New("job not running") + } + + // lua code can run in other threads and goroutines, so this exists + jobs.foreground = true + // this is kinda funny + // background continues the process incase it got suspended + err := j.background() + if err != nil { + return nil, err + } + + err = j.foreground() + if err != nil { + return nil, err + } + jobs.foreground = false + + return c.Next(), nil +} + +func (j *job) luaBackground(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + if !j.running { + return nil, errors.New("job not running") + } + + err := j.background() + if err != nil { + return nil, err + } + + return c.Next(), nil +} + type jobHandler struct { jobs map[int]*job latestID int + foreground bool // if job currently in the foreground mu *sync.RWMutex } diff --git a/job_unix.go b/job_unix.go new file mode 100644 index 0000000..5029012 --- /dev/null +++ b/job_unix.go @@ -0,0 +1,38 @@ +// +build darwin linux + +package main + +import ( + "errors" + "os" + "syscall" + + "golang.org/x/sys/unix" +) + +func (j *job) foreground() error { + if jobs.foreground { + return errors.New("(another) job already foregrounded") + } + + pgid, _ := syscall.Getpgid(j.pid) + // tcsetpgrp + unix.IoctlSetPointerInt(0, unix.TIOCSPGRP, pgid) + proc, _ := os.FindProcess(j.pid) + proc.Wait() + + hshPgid, _ := syscall.Getpgid(os.Getpid()) + unix.IoctlSetPointerInt(0, unix.TIOCSPGRP, hshPgid) + + return nil +} + +func (j *job) background() error { + proc := j.handle.Process + if proc == nil { + return nil + } + + proc.Signal(syscall.SIGCONT) + return nil +} diff --git a/job_windows.go b/job_windows.go new file mode 100644 index 0000000..140a5d1 --- /dev/null +++ b/job_windows.go @@ -0,0 +1,15 @@ +// +build windows + +package main + +import ( + "errors" +) + +func (j *job) foreground() error { + return errors.New("not supported on windows") +} + +func (j *job) background() error { + return errors.New("not supported on windows") +} diff --git a/nature/commands/bg.lua b/nature/commands/bg.lua new file mode 100644 index 0000000..f0aa462 --- /dev/null +++ b/nature/commands/bg.lua @@ -0,0 +1,15 @@ +local commander = require 'commander' + +commander.register('bg', function() + local job = hilbish.jobs.last() + if not job then + print 'bg: no last job' + return 1 + end + + local err = job.background() + if err then + print('bg: ' .. err) + return 2 + end +end) diff --git a/nature/commands/fg.lua b/nature/commands/fg.lua new file mode 100644 index 0000000..a3f1451 --- /dev/null +++ b/nature/commands/fg.lua @@ -0,0 +1,15 @@ +local commander = require 'commander' + +commander.register('fg', function() + local job = hilbish.jobs.last() + if not job then + print 'fg: no last job' + return 1 + end + + local err = job.foreground() -- waits for job; blocks + if err then + print('fg: ' .. err) + return 2 + end +end) diff --git a/nature/commands/init.lua b/nature/commands/init.lua index 589cbd5..bba2bb6 100644 --- a/nature/commands/init.lua +++ b/nature/commands/init.lua @@ -5,3 +5,4 @@ require 'nature.commands.doc' require 'nature.commands.exit' require 'nature.commands.guide' require 'nature.commands.disown' +require 'nature.commands.fg' diff --git a/signal_unix.go b/signal_unix.go index c186512..bd5984c 100644 --- a/signal_unix.go +++ b/signal_unix.go @@ -10,6 +10,7 @@ import ( func handleSignals() { c := make(chan os.Signal) + signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN, syscall.SIGTSTP) signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGWINCH, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGQUIT) for s := range c {