@@ -169,9 +169,15 @@ func Run(options ...RunOption) {
169
169
}
170
170
apiServices = append (apiServices , additionalServices ... )
171
171
172
+ // The reaper can be turned into a terminating reaper by writing true to this channel.
173
+ // When in terminating mode, the reaper will send SIGTERM to each child that gets reparented
174
+ // to us and is still running. We use this mechanism to send SIGTERM to a shell child processes
175
+ // that get reparented once their parent shell terminates during shutdown.
176
+ terminatingReaper := make (chan bool , 1 )
177
+
172
178
var wg sync.WaitGroup
173
179
wg .Add (6 )
174
- go reaper (ctx , & wg )
180
+ go reaper (ctx , & wg , terminatingReaper )
175
181
go startAndWatchIDE (ctx , cfg , & wg , ideReady )
176
182
go startContentInit (ctx , cfg , & wg , cstate )
177
183
go startAPIEndpoint (ctx , cfg , & wg , apiServices , apiEndpointOpts ... )
@@ -203,6 +209,7 @@ func Run(options ...RunOption) {
203
209
}
204
210
205
211
log .Info ("received SIGTERM - tearing down" )
212
+ terminatingReaper <- true
206
213
err = termMux .Close ()
207
214
if err != nil {
208
215
log .WithError (err ).Error ("terminal closure failed" )
@@ -305,16 +312,19 @@ func hasMetadataAccess() bool {
305
312
return false
306
313
}
307
314
308
- func reaper (ctx context.Context , wg * sync.WaitGroup ) {
315
+ func reaper (ctx context.Context , wg * sync.WaitGroup , terminatingReaper <- chan bool ) {
309
316
defer wg .Done ()
310
317
318
+ var terminating bool
311
319
sigs := make (chan os.Signal , 128 )
312
320
signal .Notify (sigs , syscall .SIGCHLD )
313
321
for {
314
322
select {
315
323
case <- ctx .Done ():
316
324
return
317
325
case <- sigs :
326
+ case terminating = <- terminatingReaper :
327
+ continue
318
328
}
319
329
320
330
pid , err := unix .Wait4 (- 1 , nil , 0 , nil )
@@ -325,6 +335,21 @@ func reaper(ctx context.Context, wg *sync.WaitGroup) {
325
335
}
326
336
if err != nil {
327
337
log .WithField ("pid" , pid ).WithError (err ).Debug ("cannot call waitpid() for re-parented child" )
338
+ continue
339
+ }
340
+
341
+ if ! terminating {
342
+ continue
343
+ }
344
+ proc , err := os .FindProcess (pid )
345
+ if err != nil {
346
+ log .WithField ("pid" , pid ).WithError (err ).Debug ("cannot find re-parented process" )
347
+ continue
348
+ }
349
+ err = proc .Signal (syscall .SIGTERM )
350
+ if err != nil {
351
+ log .WithField ("pid" , pid ).WithError (err ).Debug ("cannot send SIGTERM to re-parented process" )
352
+ continue
328
353
}
329
354
}
330
355
}
0 commit comments