@@ -20,7 +20,7 @@ import (
2020// how often should be cleanup called
2121const cleanupPeriod = 4 * time .Second
2222
23- // timeot for first playlist, when it waits for new data
23+ // timeout for first playlist, when it waits for new data
2424const playlistTimeout = 60 * time .Second
2525
2626// minimum segments available to consider stream as active
@@ -40,7 +40,7 @@ type ManagerCtx struct {
4040 events struct {
4141 onStart func ()
4242 onCmdLog func (message string )
43- onStop func ()
43+ onStop func (err error )
4444 }
4545
4646 cmd * exec.Cmd
@@ -92,7 +92,7 @@ func (m *ManagerCtx) Start() error {
9292 read , write := io .Pipe ()
9393 m .cmd .Stdout = write
9494
95- //create a new process group
95+ // create a new process group
9696 m .cmd .SysProcAttr = & syscall.SysProcAttr {Setpgid : true }
9797
9898 m .active = false
@@ -104,6 +104,7 @@ func (m *ManagerCtx) Start() error {
104104 m .playlistLoad = make (chan string )
105105 m .shutdown = make (chan interface {})
106106
107+ // read playlist on stdout
107108 go func () {
108109 buf := make ([]byte , 1024 )
109110
@@ -125,14 +126,15 @@ func (m *ManagerCtx) Start() error {
125126 }
126127 }
127128
129+ // if stdout pipe has been closed
128130 if err != nil {
129- // When command fails to start, we reach this branch after a while
130131 m .logger .Err (err ).Msg ("cmd read failed" )
131132 return
132133 }
133134 }
134135 }()
135136
137+ // periodic cleanup
136138 go func () {
137139 ticker := time .NewTicker (cleanupPeriod )
138140 defer ticker .Stop ()
@@ -152,21 +154,54 @@ func (m *ManagerCtx) Start() error {
152154 m .events .onStart ()
153155 }
154156
155- return m .cmd .Start ()
157+ // start program
158+ err = m .cmd .Start ()
159+
160+ // wait for program to exit
161+ go func () {
162+ err = m .cmd .Wait ()
163+ if err != nil {
164+ if exiterr , ok := err .(* exec.ExitError ); ok {
165+ // The program has exited with an exit code != 0
166+
167+ // This works on both Unix and Windows. Although package
168+ // syscall is generally platform dependent, WaitStatus is
169+ // defined for both Unix and Windows and in both cases has
170+ // an ExitStatus() method with the same signature.
171+ if status , ok := exiterr .Sys ().(syscall.WaitStatus ); ok {
172+ m .logger .Warn ().Int ("exit-status" , status .ExitStatus ()).Msg ("the program has exited with an exit code != 0" )
173+ }
174+ } else {
175+ m .logger .Err (err ).Msg ("the program has exited with an error" )
176+ }
177+ } else {
178+ m .logger .Info ().Msg ("the program has successfully exited" )
179+ }
180+
181+ close (m .shutdown )
182+
183+ if m .events .onStop != nil {
184+ m .events .onStop (err )
185+ }
186+
187+ err := os .RemoveAll (m .tempdir )
188+ m .logger .Err (err ).Msg ("removing tempdir" )
189+
190+ m .mu .Lock ()
191+ m .cmd = nil
192+ m .mu .Unlock ()
193+ }()
194+
195+ return err
156196}
157197
158198func (m * ManagerCtx ) Stop () {
159199 m .mu .Lock ()
160200 defer m .mu .Unlock ()
161201
162- if m .cmd == nil {
163- return
164- }
165-
166- m .logger .Debug ().Msg ("performing stop" )
167- close (m .shutdown )
202+ if m .cmd != nil && m .cmd .Process != nil {
203+ m .logger .Debug ().Msg ("performing stop" )
168204
169- if m .cmd .Process != nil {
170205 pgid , err := syscall .Getpgid (m .cmd .Process .Pid )
171206 if err == nil {
172207 err := syscall .Kill (- pgid , syscall .SIGKILL )
@@ -176,15 +211,6 @@ func (m *ManagerCtx) Stop() {
176211 err := m .cmd .Process .Kill ()
177212 m .logger .Err (err ).Msg ("killing proccess" )
178213 }
179- _ = m .cmd .Wait ()
180- m .cmd = nil
181- }
182-
183- err := os .RemoveAll (m .tempdir )
184- m .logger .Err (err ).Msg ("removing tempdir" )
185-
186- if m .events .onStop != nil {
187- m .events .onStop ()
188214 }
189215}
190216
@@ -225,14 +251,14 @@ func (m *ManagerCtx) ServePlaylist(w http.ResponseWriter, r *http.Request) {
225251 if ! m .active {
226252 select {
227253 case playlist = <- m .playlistLoad :
254+ // when command exits before providing any playlist
228255 case <- m .shutdown :
229256 m .logger .Warn ().Msg ("playlist load failed because of shutdown" )
230- // When command failed to start and timeout has been increased we reach this branch after a while
231- http .Error (w , "404 playlist not found" , http .StatusNotFound )
257+ http .Error (w , "500 playlist not available" , http .StatusInternalServerError )
232258 return
233259 case <- time .After (playlistTimeout ):
234260 m .logger .Warn ().Msg ("playlist load channel timeouted" )
235- http .Error (w , "500 not available " , http .StatusInternalServerError )
261+ http .Error (w , "504 playlist timeout " , http .StatusGatewayTimeout )
236262 return
237263 }
238264 }
@@ -269,6 +295,6 @@ func (m *ManagerCtx) OnCmdLog(event func(message string)) {
269295 m .events .onCmdLog = event
270296}
271297
272- func (m * ManagerCtx ) OnStop (event func ()) {
298+ func (m * ManagerCtx ) OnStop (event func (err error )) {
273299 m .events .onStop = event
274300}
0 commit comments