-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprocman.go
151 lines (139 loc) · 3.71 KB
/
procman.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package procman
import (
"errors"
"log"
"os"
"os/exec"
"runtime"
)
// Action represents an action to be performed on the process manager.
// The possible values are:
// - ActionStart
// - ActionStop
// - ActionRestart
type Action byte
const (
ActionStart Action = iota
ActionStop
ActionRestart
)
// Request represents a request to be sent to the process manager.
// It contains the action to be performed and a channel to send the response
// back.
type Request struct {
Action Action
Response chan<- error
}
// ProcMan represents a process manager.
// It contains the command to be executed and the arguments to be passed to it.
// It also contains a channel to receive requests.
type ProcMan struct {
Cmd string
Args []string
ChReq chan Request
}
// Run starts the process manager.
// It listens for requests and performs the actions accordingly.
// It also listens for the process to finish and restarts it when it does.
// If the process finishes with an error, it logs the error.
// If the process finishes successfully, it logs a message.
func (pm *ProcMan) Run() {
// Create and start the initial process.
proc := &proc{
Cmd: exec.Command(pm.Cmd, pm.Args...),
Done: make(chan error),
}
go proc.run()
for {
// Wait for one of the following events:
// 1. A request is received
// 2. The process finishes
// If a request is received, perform the action accordingly.
// If the process finishes, restart it.
select {
case req := <-pm.ChReq:
switch req.Action {
case ActionStart:
// If the process is already running, return an error.
if proc.Cmd != nil {
req.Response <- errors.New("process running")
continue
}
// Start the process.
proc.Cmd = exec.Command(pm.Cmd, pm.Args...)
proc.Done = make(chan error)
go proc.run()
req.Response <- nil
log.Println("process started")
case ActionStop:
// If the process is not running, return an error.
if proc.Cmd == nil {
req.Response <- errors.New("process not running")
continue
}
// Stop the process.
proc.kill()
<-proc.Done
proc.Cmd = nil
req.Response <- nil
log.Println("process stopped")
case ActionRestart:
// If the process is not running, return an error.
if proc.Cmd == nil {
req.Response <- errors.New("process not running")
continue
}
// Restart the process.
proc.kill()
<-proc.Done
proc.Cmd = exec.Command(pm.Cmd, pm.Args...)
proc.Done = make(chan error)
go proc.run()
req.Response <- nil
log.Println("process restarted")
}
case err := <-proc.Done:
// If the process finishes, restart it.
if err != nil {
log.Println("process finished with error:", err)
} else {
log.Println("process finished successfully")
}
proc.Cmd = exec.Command(pm.Cmd, pm.Args...)
proc.Done = make(chan error)
go proc.run()
}
}
}
// proc represents a process.
// It contains the command to be executed and a channel to send the status of
// the process back.
type proc struct {
Cmd *exec.Cmd
Done chan error
}
// run starts the process.
// It sets the standard input, output, and error of the process to the standard
// input, output, and error of the current process.
// It starts the process and sends the status back when it finishes.
func (p *proc) run() {
p.Cmd.Stdin = os.Stdin
p.Cmd.Stdout = os.Stdout
p.Cmd.Stderr = os.Stderr
err := p.Cmd.Start()
if err != nil {
p.Done <- err
return
}
p.Done <- p.Cmd.Wait()
}
// kill sends a signal to the process to stop it.
// On Windows, it sends a kill signal.
// On other platforms, it sends an interrupt signal.
func (p *proc) kill() {
if runtime.GOOS == "windows" {
p.Cmd.Process.Signal(os.Kill)
} else {
p.Cmd.Process.Signal(os.Interrupt)
}
}