@@ -288,16 +288,31 @@ func (rc *ReportingCommand) Run() error {
288288 return nil
289289}
290290
291+ func signalProcessOnTarget (t target.Target , pidStr string , sigStr string ) error {
292+ var cmd * exec.Cmd
293+ // prepend "-" to the signal string if not already present
294+ if ! strings .HasPrefix (sigStr , "-" ) {
295+ sigStr = "-" + sigStr
296+ }
297+ if ! t .IsSuperUser () && t .CanElevatePrivileges () {
298+ cmd = exec .Command ("sudo" , "kill" , sigStr , pidStr )
299+ } else {
300+ cmd = exec .Command ("kill" , sigStr , pidStr )
301+ }
302+ _ , _ , _ , err := t .RunCommandEx (cmd , 5 , false , true ) // #nosec G204
303+ return err
304+ }
305+
291306// configureSignalHandler sets up a signal handler to catch SIGINT and SIGTERM
292307//
293308// When perfspect receives ctrl-c while in the shell, the shell propagates the
294309// signal to all our children. But when perfspect is run in the background or disowned and
295310// then receives SIGINT, e.g., from a script, we need to send the signal to our children
296311//
297- // Also, when running scripts in parallel using the parallel_master .sh script, we need to
298- // send the signal to the parallel_master .sh script on each target so that it can clean up
299- // its child processes. This is because the parallel_master .sh script is run in its own process group
300- // and does not receive the signal when perfspect receives it.
312+ // When running scripts using the controller .sh script, we need to send the signal to the
313+ // controller .sh script on each target so that it can clean up its child processes. This is
314+ // because the controller .sh script is run in its own process group and does not receive the
315+ // signal when perfspect receives it.
301316//
302317// Parameters:
303318// - myTargets: The list of targets to send the signal to.
@@ -308,48 +323,38 @@ func configureSignalHandler(myTargets []target.Target, statusFunc progress.Multi
308323 go func () {
309324 sig := <- sigChannel
310325 slog .Debug ("received signal" , slog .String ("signal" , sig .String ()))
311- // Scripts that are run in parallel using the parallel_master.sh script and a few other sequential scripts need to be handled specially
312- // because they are run in their own process group, we need to send the signal directly to the PID of the script.
313- // For every target, look for the primary_collection_script PID file and send SIGINT to it.
326+ // The controller.sh script is run in its own process group, so we need to send the signal
327+ // directly to the PID of the controller. For every target, look for the primary_collection_script
328+ // PID file and send SIGINT to it.
329+ // The controller script is run in its own process group, so we need to send the signal
330+ // directly to the PID of the controller. For every target, look for the controller
331+ // PID file and send SIGINT to it.
314332 for _ , t := range myTargets {
315333 if statusFunc != nil {
316334 _ = statusFunc (t .GetName (), "Signal received, cleaning up..." )
317335 }
318- pidFilePath := filepath .Join (t .GetTempDirectory (), "primary_collection_script.pid" )
336+ pidFilePath := filepath .Join (t .GetTempDirectory (), script . ControllerPIDFileName )
319337 stdout , _ , exitcode , err := t .RunCommandEx (exec .Command ("cat" , pidFilePath ), 5 , false , true ) // #nosec G204
320338 if err != nil {
321- slog .Error ("error retrieving target primary_collection_script PID" , slog .String ("target" , t .GetName ()), slog .String ("error" , err .Error ()))
339+ slog .Error ("error retrieving target controller PID" , slog .String ("target" , t .GetName ()), slog .String ("error" , err .Error ()))
322340 }
323341 if exitcode == 0 {
324342 pidStr := strings .TrimSpace (stdout )
325- _ , _ , _ , err := t . RunCommandEx ( exec . Command ( "sudo" , "kill" , "- SIGINT" , pidStr ), 5 , false , true ) // #nosec G204
343+ err = signalProcessOnTarget ( t , pidStr , "SIGINT" )
326344 if err != nil {
327- slog .Error ("error sending signal to target primary_collection_script " , slog .String ("target" , t .GetName ()), slog .String ("error" , err .Error ()))
345+ slog .Error ("error sending SIGINT signal to target controller " , slog .String ("target" , t .GetName ()), slog .String ("error" , err .Error ()))
328346 }
329347 }
330348 }
331- // now wait until all primary collection scripts have exited
332- slog .Debug ("waiting for primary_collection_script scripts to exit" )
349+ // now wait until all controller scripts have exited
350+ slog .Debug ("waiting for controller scripts to exit" )
333351 for _ , t := range myTargets {
334352 // create a per-target timeout context
335353 targetTimeout := 10 * time .Second
336354 ctx , cancel := context .WithTimeout (context .Background (), targetTimeout )
337355 timedOut := false
338- pidFilePath := filepath .Join (t .GetTempDirectory (), "primary_collection_script.pid" )
356+ pidFilePath := filepath .Join (t .GetTempDirectory (), script . ControllerPIDFileName )
339357 for {
340- // check for timeout
341- select {
342- case <- ctx .Done ():
343- if statusFunc != nil {
344- _ = statusFunc (t .GetName (), "cleanup timeout exceeded" )
345- }
346- slog .Warn ("signal handler cleanup timeout exceeded for target" , slog .String ("target" , t .GetName ()))
347- timedOut = true
348- default :
349- }
350- if timedOut {
351- break
352- }
353358 // read the pid file
354359 stdout , _ , exitcode , err := t .RunCommandEx (exec .Command ("cat" , pidFilePath ), 5 , false , true ) // #nosec G204
355360 if err != nil || exitcode != 0 {
@@ -362,6 +367,23 @@ func configureSignalHandler(myTargets []target.Target, statusFunc progress.Multi
362367 if err != nil || exitcode != 0 {
363368 break // process no longer exists, script has exited
364369 }
370+ // check for timeout
371+ select {
372+ case <- ctx .Done ():
373+ timedOut = true
374+ default :
375+ }
376+ if timedOut {
377+ if statusFunc != nil {
378+ _ = statusFunc (t .GetName (), "cleanup timeout exceeded, sending kill signal" )
379+ }
380+ slog .Warn ("signal handler cleanup timeout exceeded for target, sending SIGKILL" , slog .String ("target" , t .GetName ()))
381+ err = signalProcessOnTarget (t , pidStr , "SIGKILL" )
382+ if err != nil {
383+ slog .Error ("error sending SIGKILL signal to target controller" , slog .String ("target" , t .GetName ()), slog .String ("error" , err .Error ()))
384+ }
385+ break
386+ }
365387 // sleep for a short time before checking again
366388 time .Sleep (500 * time .Millisecond )
367389 }
0 commit comments