/
exec.go
130 lines (113 loc) · 2.97 KB
/
exec.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package main
import (
"fmt"
"os"
"os/exec"
"os/signal"
"strings"
"syscall"
)
func execBuiltinCmd(cmd []string) {
builtin := map[string]func([]string) error{
"kill": kill,
"jobs": lsJobs,
"exit": exit,
}
err := builtin[cmd[0]](cmd)
if err != nil {
fmt.Println(err)
}
}
func execFgCmd(cmd []string, sigStateChanged chan string) {
cmdStr := strings.Join(cmd, " ")
// TODO: Extract start process into common function.
argv0, err := exec.LookPath(cmd[0])
if err != nil {
if cmd[0] != "" {
fmt.Printf("Unknown command: %s\n", cmd[0])
}
// Don't execute new process with empty return. Will cause panic.
sigPrompt <- struct{}{}
return
}
var procAttr os.ProcAttr
procAttr.Files = []*os.File{os.Stdin, os.Stdout, os.Stderr}
p, err := os.StartProcess(argv0, cmd, &procAttr)
if err != nil {
fmt.Printf("Start process %s, %s failed: %v", err, argv0, cmd)
}
for {
sigChild := make(chan os.Signal)
defer close(sigChild)
// SIGCONT not receivable: https://github.com/golang/go/issues/8953
// This causes some bugs. Eg. CONT signal not captured by handler means subsequent KILL or STOP signals will be ignored by this handler.
signal.Notify(sigChild, syscall.SIGTSTP, syscall.SIGINT, syscall.SIGCONT, syscall.SIGKILL)
defer signal.Stop(sigChild)
var ws syscall.WaitStatus
// Ignoring error. May return "no child processes" error. Eg. Sending Ctrl-c on `cat` command.
wpid, _ := syscall.Wait4(p.Pid, &ws, syscall.WUNTRACED, nil)
if ws.Exited() {
break
}
if ws.Stopped() {
jobHandler(wpid, runningState, cmdStr)
jobHandler(wpid, suspendedState, cmdStr)
// Return prompt when fg has become bg
sigPrompt <- struct{}{}
}
//if ws.Continued() {
// state = contState
//}
if ws == 9 {
jobHandler(wpid, killedState, cmdStr)
break
}
}
p.Wait()
sigPrompt <- struct{}{}
}
func execBgCmd(cmd []string, sigStateChanged chan string) {
cmdStr := strings.Join(cmd, " ")
argv0, err := exec.LookPath(cmd[0])
if err != nil {
if cmd[0] != "" {
fmt.Printf("Unknown command: %s\n", cmd[0])
}
sigPrompt <- struct{}{}
return
}
var procAttr os.ProcAttr
procAttr.Files = []*os.File{os.Stdin, os.Stdout, os.Stderr}
p, err := os.StartProcess(argv0, cmd, &procAttr)
if err != nil {
fmt.Printf("Start process %s, %s failed: %v", err, argv0, cmd)
}
jobHandler(p.Pid, runningState, cmdStr)
sigPrompt <- struct{}{}
//FIXME: Bg processes should not receive keyboard signals sent to fg process.
for {
sigChild := make(chan os.Signal)
defer close(sigChild)
signal.Notify(sigChild, syscall.SIGCHLD)
defer signal.Stop(sigChild)
var ws syscall.WaitStatus
wpid, _ := syscall.Wait4(p.Pid, &ws, syscall.WUNTRACED, nil)
if ws.Exited() {
jobHandler(wpid, doneState, cmdStr)
break
}
if ws.Stopped() {
jobHandler(wpid, suspendedState, cmdStr)
sigPrompt <- struct{}{}
}
//if ws.Continued() {
// state = contState
//}
if ws == 9 {
jobHandler(wpid, killedState, cmdStr)
break
}
}
p.Wait()
sigPrompt <- struct{}{}
}