-
Notifications
You must be signed in to change notification settings - Fork 2
/
main.go
143 lines (117 loc) · 2.46 KB
/
main.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
package main
import (
"fmt"
"io"
"log"
"os"
"os/exec"
"time"
"strings"
)
var (
command *exec.Cmd
startTime time.Time
modTime time.Time
writer io.Writer = os.Stdout
done = make(chan error)
)
func checkError(err error) {
if err != nil {
panic(err)
}
}
func runBin(bin string, args []string) error {
command = exec.Command(bin, args...)
stdout, err := command.StdoutPipe()
if err != nil {
return err
}
stderr, err := command.StderrPipe()
if err != nil {
return err
}
err = command.Start()
if err != nil {
return err
}
startTime = time.Now()
go io.Copy(writer, stdout)
go io.Copy(writer, stderr)
go func() {
done <- command.Wait()
}()
return nil
}
func kill() error {
if command == nil {
return nil
}
err := command.Process.Kill()
if err != nil {
return err
}
return <-done // allow goroutine to exit
}
func main() {
bin := ""
args := []string{}
if len(os.Args) < 2 {
fmt.Println("Missing args, require at least one usage: [program] binary arg1 arg2 arg3")
os.Exit(1)
}
for idx, arg := range os.Args {
if idx == 0 {
// Skip the first arg as its our program name.
continue
} else if idx == 1 {
bin = arg
} else {
args = append(args, arg)
}
}
_, err := os.Stat(bin)
checkError(err)
if os.IsNotExist(err) {
fmt.Printf("Path: %s does not exist\n", bin)
os.Exit(1)
}
log.Println("Initially starting the app...")
err = runBin(bin, args)
checkError(err)
nbusy := 0
for {
time.Sleep(500 * time.Millisecond)
stat, err := os.Stat(bin)
if err != nil {
// Not found
continue
}
modTime = stat.ModTime()
if modTime.After(startTime) {
log.Println("Reloading the app...")
// Deliberately ignoring errors here.
kill()
// Need sleeping before starting the app or it will somewtimes fail.
// Need to investigate why exactly, some timing issue.
// Might be caused by https://github.com/golang/go/issues/22220
time.Sleep(500 * time.Millisecond)
log.Println("App killed, starting it again...")
stat, err = os.Stat(bin)
if err != nil {
// Not found
continue
}
err = runBin(bin, args)
// https://github.com/golang/go/issues/22220
if err != nil && nbusy < 3 && strings.Contains(err.Error(), "text file busy") {
log.Println("Text file busy - retry in a bit")
time.Sleep(100 * time.Millisecond << uint(nbusy))
nbusy++
continue
}
checkError(err)
nbusy = 0
log.Println("Started and up and running...")
}
}
}