Signal Handling with sigactionhandler_t - Structures are not initialized lazarus / 32-Linux ---------------------------------------------------------------------- domibay_hugo · 2020-02-26 15:34 I try to write a Signal Handling Procedure that would react to the extenal Signals from the Operating System as described at: https://www.freepascal.org/docs-html/rtl/baseunix/fpsigaction.html I declared and registered a Procedure as required at: https://www.freepascal.org/docs- html/rtl/baseunix/sigactionhandler_t.html Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TMyService.OnTerminate(isignal: LongInt; pinfo: psiginfo; pcontext: PSigContext); cdecl;begin  WriteLn('TMyService.OnTerminate: go ...');   WriteLn('got signal: ', chr(39), isignal, chr(39));   if pinfo <> Nil then  begin    WriteLn('signal details: no: ', chr(39), pinfo^.si_signo, chr(39)      , '; cd: ', chr(39), pinfo^.si_code, chr(39)      , '; err: ', chr(39), pinfo^.si_errno, chr(39));   end;   if pcontext <> Nil then  begin    WriteLn('signal context: set.');  end;end;  This Procedure is correctly called at the SigTerm Signal but I find that isignal is not initialized with any value. Supposedly the Structures tsiginfo and TSigContext would give info about the received Signal, but they don't seem to have any content. Sending from another Terminal a Signal with kill: $ ps axuf|grep -i dbg usr    14045  0.0  0.0   9792  1196 pts/0    S+   15:29   0:00  |         \_ ./my_service.dbg.run $ kill 14045 I got this output: $ ./my_service.dbg.run TMyService.DoRun: new message event waiting ... TMyService.OnTerminate: go ... got signal: '1612258544' signal details: no: '0'; cd: '0'; err: '0' signal context: set. Where could I find the actually Information about the received Signal ? ---------------------------------------------------------------------- domibay_hugo · 2020-03-31 11:09 Additional I need to remark even if the OnTerminate Function is defined within the Class it is invoked statically by the System. So even if Self is not Nil any access to it will result into an Access Violation Exception. ---------------------------------------------------------------------- Martin_fr · 2020-03-31 11:24 Without looking at any detail.... The docs refer to a procedure. You have a method (procedure of object). That is a big difference. A method expects an additional parameter for self. But that would not be passed by the caller.... Also (and I understand that may be for testing only): write/ln is not safe to be called in a signal/interrupt handler. ---------------------------------------------------------------------- domibay_hugo · 2020-03-31 11:42 Studying further I found that SigActionRec https://www.freepascal.org/docs-html/rtl/baseunix/sigactionrec.html Is not POSIX compliant as it should according to: http://man7.org/linux/man-pages/man2/sigaction.2.html QuoteThe sigaction structure is defined as something like:            struct sigaction {                void     (*sa_handler)(int);                void     (*sa_sigaction)(int, siginfo_t *, void *);                sigset_t   sa_mask;                int        sa_flags;                void     (*sa_restorer)(void);            }; The sigaction.sa_sigaction field is missing in /rtl/linux/signal.inc So the POSIX documentation states on that: QuoteOn some architectures a union is involved: do not assign to both        sa_handler and sa_sigaction. in /rtl/linux/signal.inc: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     signalhandler_t = procedure(signal: longint); cdecl;  sigactionhandler_t = procedure(signal: longint; info: psiginfo; context: psigcontext); cdecl;   sigactionrec = record    sa_handler: sigactionhandler_t;    sa_flags: culong;    sa_restorer: sigrestorerhandler_t;    sa_mask: sigset_t;  end;  sigactionrec.sa_handler is defined as sigactionhandler_t but should rather be a signalhandler_t Pointer. I understand that is why I find all Parameters uninitialized because the memory mapping is wrong. ---------------------------------------------------------------------- domibay_hugo · 2020-03-31 11:47 Quote from: Martin_fr on March 31, 2020, 01:24:48 pmWithout looking at any detail.... The docs refer to a procedure. You have a method (procedure of object). That is a big difference. A method expects an additional parameter for self. But that would not be passed by the caller.... Also (and I understand that may be for testing only): write/ln is not safe to be called in a signal/interrupt handler. Additionally the Documentation is not conform with the actual FPC Source as defined in /rtl/unix/signal.inc As the added Examples are as well, which reference the signalhandler_t Signal Handler style. https://www.freepascal.org/docs-html/rtl/baseunix/fpsignal.html ---------------------------------------------------------------------- PascalDragon · 2020-03-31 14:15 Quote from: domibay_hugo on March 31, 2020, 01:42:38 pmI understand that is why I find all Parameters uninitialized because the memory mapping is wrong. The existance of both sa_handler and sa_sigaction would be wrong, because - as the POSIX documentation states and as you have quoted - the two fields might be a union. Also for the kernel it does not matter whether you pass in a signalhandler_t or a sigactionhandler_t. For the kernel it's only a pointer. And it will happily call it with the correct parameters depending on whether you set up the signal using signal or sigaction. What is your problem is that you're passing in a method. You need to either use a global function or a class function with static modifier, because otherwise the function signature will be ($Self: Pointer; signal: longint; info: psiginfo; context: psigcontext) and that results in bogus fields, because the parameters will be mixed up. Also the sigactionrec is not declared in rtl/unix/signal.inc, but in rtl//signal.inc and as such is declared fitting for the corresponding OS. So if there would be one that explicitly has both sa_handler and sa_sigaction as separate fields then it would be declared accordingly there. If you still have problems then please provide a full reproducible example. ---------------------------------------------------------------------- domibay_hugo · 2020-03-31 15:00 Thank you for looking into this issue. I modified my code according to the hints and suggestions I received. I built a Protype to demonstrate the observed behaviour. A Prototype from what I try to achieve: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program signal_handler; uses  SysUtils, BaseUnix;  type  TMyService = class  protected    ierr: Integer;    brun: Boolean;    procedure ShutDown;  procedure InstallSignalHandler;  public    function DoRun: Integer;  end;    TServiceTerminateException = class(Exception)  end;       procedure RaiseTerminateException(isignal: LongInt; pinfo: PSigInfo; pcontext: PSigContext); cdecl;  const    SMETHOD = 'RaiseTerminateException';  begin    WriteLn(SMETHOD, ': go ...');   WriteLn('got signal: ', chr(39), isignal, chr(39));     if pinfo <> Nil then    begin      WriteLn('signal info: no: ', chr(39), pinfo^.si_signo, chr(39)        , '; cd: ', chr(39), pinfo^.si_code, chr(39)        , '; err: ', chr(39), pinfo^.si_errno, chr(39));    end;     if pcontext <> Nil then    begin      WriteLn('signal context: set.');    end;     raise TServiceTerminateException.CreateHelp('Terminate Signal received', 4);   end;    function TMyService.DoRun: Integer;  const    SMETHOD = 'TMyService.DoRun';  begin    Self.ierr := 0;    Self.brun := True;   InstallSignalHandler;     Result := Self.ierr;     if Self.ierr <> 0 then Self.brun := False;     if Self.brun then    begin      try  //except        while Self.brun do        begin          WriteLn(SMETHOD, ': sleeping ...');           Sleep(15000);           WriteLn(SMETHOD, ': sleeping done.');        end;  //while Self.brun do      except        on te: TServiceTerminateException do        begin          WriteLn(SMETHOD, ': TServiceTerminateException received.');          WriteLn('Message [', te.HelpContext, ']: ', te.Message);        end;      end;  //except       WriteLn(SMETHOD, ': done.');       ShutDown;       Result := Self.ierr;     end;  //if Self.brun then  end; procedure TMyService.ShutDown;const  SMETHOD = 'TMyService.ShutDown';begin  WriteLn(SMETHOD, ': go ...');    WriteLn(SMETHOD, ': done.');end;   procedure TMyService.InstallSignalHandler;const  SMETHOD = 'TMyService.InstallSignalHandler';var  narec, oarec: SigActionRec;begin  narec.sa_handler := sigactionhandler(@RaiseTerminateException);  FillChar(narec.sa_mask, SizeOf(narec.sa_mask), #0);  narec.sa_flags := 0 or SA_SIGINFO;  {$ifdef Linux}               // Linux specific    narec.sa_restorer := Nil;  {$endif}   if FPSigAction(SigTerm, @narec, @oarec) <> 0 then  begin    WriteLn(SMETHOD, ': Error: ', chr(39), fpgeterrno, chr(39), '.');    //Self.brun := False;     Self.ierr := 1;  end;end;   var  service: TMyService;begin  service := TMyService.Create;   try    try      service.DoRun;    except      on e: Exception do      begin      WriteLn('Main: Exception caught.');        WriteLn('Message [', e.HelpContext, ']: ', e.Message);      end;    end;  finally    service.Free;  end; end.   I run it with the sudo command [2020-03-31 15:51:34 - root@dev-lan27 cacti]# sudo -u upload1 ./signal_handler TMyService.DoRun: sleeping ... TMyService.DoRun: sleeping done. TMyService.DoRun: sleeping ... TMyService.DoRun: sleeping done. TMyService.DoRun: sleeping ... RaiseTerminateException: go ... got signal: '15' signal info: no: '15'; cd: '0'; err: '0' signal context: set. TMyService.DoRun: TServiceTerminateException received. Message [4]: Terminate Signal received TMyService.DoRun: done. TMyService.ShutDown: go ... TMyService.ShutDown: done. Heap dump by heaptrc unit 10 memory blocks allocated : 927/936 10 memory blocks freed     : 927/936 0 unfreed memory blocks : 0 True heap size : 360448 True free heap : 360448 [2020-03-31 15:52:06 - root@dev-lan27 cacti]# strace -p 7172 > sig- hdl_strace_7172_2020-03-31-1.log 2>&1 [2020-03-31 15:51:34 - root@dev-lan27 cacti]# kill 7171 so this produces this dump: Process 7172 attached restart_syscall(<... resuming interrupted call ...>) = 0 write(1, "TMyService.DoRun: sleeping done."..., 33) = 33 write(1, "TMyService.DoRun: sleeping ...\n", 31) = 31 nanosleep({15, 0}, {7, 736493484})      = ? ERESTART_RESTARTBLOCK (Interrupted by signal) --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=7171, si_uid=0} --- write(1, "RaiseTerminateException: go ...\n", 32) = 32 write(1, "got signal: '15'\n", 17)      = 17 write(1, "signal info: no: '15'; cd: '0'; "..., 41) = 41 write(1, "signal context: set.\n", 21)  = 21 write(1, "TMyService.DoRun: TServiceTermin"..., 55) = 55 write(1, "Message [4]: Terminate Signal re"..., 39) = 39 write(1, "TMyService.DoRun: done.\n", 24) = 24 write(1, "TMyService.ShutDown: go ...\n", 28) = 28 write(1, "TMyService.ShutDown: done.\n", 27) = 27 write(2, "Heap dump by heaptrc unit\n", 26) = 26 write(2, "10 memory blocks allocated : 927"..., 37) = 37 write(2, "10 memory blocks freed     : 927"..., 37) = 37 write(2, "0 unfreed memory blocks : 0\n", 28) = 28 write(2, "True heap size : 360448", 23) = 23 write(2, "\n", 1)                       = 1 write(2, "True free heap : 360448\n", 24) = 24 munmap(0x7f1f17ecd000, 32768)           = 0 munmap(0x7f1f17ed5000, 32768)           = 0 munmap(0x7f1f17edd000, 262144)          = 0 munmap(0x7f1f17ec5000, 32768)           = 0 exit_group(0)                           = ? +++ exited with 0 +++ the information about where the signal came from is missing --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=7171, si_uid=0} --- only If I knew the si_code I could find that the signal came from the parent. this is relevant according to the information: https://www.mkssoftware.com/docs/man5/siginfo_t.5.asp#Signal_Codes ---------------------------------------------------------------------- domibay_hugo · 2020-04-01 08:27 There is another Ceveat to take into account as the POSIX documentation explains: http://man7.org/linux/man-pages/man2/sigaction.2.html QuoteThe siginfo_t argument to a SA_SIGINFO handler        When the SA_SIGINFO flag is specified in act.sa_flags, the signal        handler address is passed via the act.sa_sigaction field. So I modified the TMyService.InstallSignalHandler method to set the SA_SIGINFO Flag in SigActionRec.sa_flags: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     narec.sa_flags := 0 or SA_SIGINFO;  Setting this Flag I can observe in the strace log that the values are correctly passed to the rt_sigaction Call: rt_sigaction(SIGTERM, {sa_handler=0x401080, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x402d60}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8 ) = 0 Now when I debug the example signal_handler Application I can see in the Debugger that the Fields TSigInfo._sifields._kill._pid and TSigInfo._sifields._kill._uid are set. But as the POSIX Documentation states: Quote  si_signo, si_errno and si_code are defined for all signals. (si_errno is generally unused on Linux.)  The rest of the struct may be a union, so that one should read only the fields that are meaningful for the given signal I need TSigInfo.si_signo and TSigInfo.si_code to faithfully parse the TSigInfo structure as commented above. ---------------------------------------------------------------------- domibay_hugo · 2020-04-01 08:44 I looked into the Linux Kernel and found the si_code Definitions at: https://github.com/torvalds/linux/blob/master/include/uapi/asm- generic/siginfo.h include/uapi/asm-generic/siginfo.h: 168:#define SI_USER      0      /* sent by kill, sigsend, raise */ But not all si_code Definitions are included in FPC If I try to write a clean code like: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     if pinfo^.si_code = SI_USER then    begin      ipppid := LongInt(pinfo^._sifields._kill._pid);      ippuid := LongInt(pinfo^._sifields._kill._uid);       WriteLn('signal info: pid: ', chr(39), ipppid, chr(39)      , '; uid: ', chr(39), ippuid, chr(39));    end;  //if pinfo^.si_code = SI_USER then  I get a Compiler Error: Error: Identifier not found "SI_USER" ---------------------------------------------------------------------- domibay_hugo · 2020-04-01 10:35 I found another Use Case that demonstrate the vital Necessity to handle System Signals correctly in FreePascal Applications: The TProcess.Execute generates a SIGCHLD Signal with si_code=CLD_EXITED when TProcess.Executable does not have the correct PermissionsWriting to the Pipe of the Exited Child Process generates a SIGPIPE Signal which is fatal to the Main Process So it is vital to convert these Signals into Exceptions to be able to handle them in the correct context. My Prototype to demonstrate this behaviour is: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program command_launcher; usesClasses, sysutils, process, BaseUnix;  type  TChildException = class(Exception)  end;   TPipeException = class(Exception)  end;  procedure RaiseChildException(isignal: LongInt; pinfo: PSigInfo; pcontext: PSigContext); cdecl;const  SMETHOD = 'RaiseChildException';  CLD_EXITED = 1;var  ichldpid, ichlduid, ichldstt: LongInt;begin  WriteLn(SMETHOD, ': go ...');   ichldpid := -1;  ichlduid := -1;  ichldstt := -1;   WriteLn('got signal: ', chr(39), isignal, chr(39));   if pinfo <> Nil then  begin    WriteLn('signal info: no: ', chr(39), pinfo^.si_signo, chr(39)      , '; cd: ', chr(39), pinfo^.si_code, chr(39)      , '; err: ', chr(39), pinfo^.si_errno, chr(39));     if pinfo^.si_code = CLD_EXITED then    begin      ichldpid := LongInt(pinfo^._sifields._sigchld._pid);      ichlduid := LongInt(pinfo^._sifields._sigchld._uid);      ichldstt := pinfo^._sifields._sigchld._status;       WriteLn('signal info: pid: ', chr(39), ichldpid, chr(39)      , '; uid: ', chr(39), ichlduid, chr(39)      , '; stt: ', chr(39), ichldstt, chr(39));    end;  //if pinfo^.si_code = CLD_EXITED then  end;  //if pinfo <> Nil then   if pcontext <> Nil then  begin    WriteLn('signal context: set.');  end;   raise TChildException.CreateHelp('Child [' + IntToStr(ichldpid)    + ']: User (uid: ' + chr(39) + IntToStr(ichlduid) + chr(39)    + '): Launch failed with [' + IntToStr(ichldstt) + ']', ichldstt); end; procedure RaisePipeException(isignal: LongInt; pinfo: PSigInfo; pcontext: PSigContext); cdecl;const  SMETHOD = 'RaisePipeException';  SI_USER = 0;var  ipppid, ippuid: LongInt;begin  WriteLn(SMETHOD, ': go ...');   ipppid := -1;  ippuid := -1;   WriteLn('got signal: ', chr(39), isignal, chr(39));   if pinfo <> Nil then  begin    WriteLn('signal info: no: ', chr(39), pinfo^.si_signo, chr(39)      , '; cd: ', chr(39), pinfo^.si_code, chr(39)      , '; err: ', chr(39), pinfo^.si_errno, chr(39));     if pinfo^.si_code = SI_USER then    begin      ipppid := LongInt(pinfo^._sifields._kill._pid);      ippuid := LongInt(pinfo^._sifields._kill._uid);       WriteLn('signal info: pid: ', chr(39), ipppid, chr(39)      , '; uid: ', chr(39), ippuid, chr(39));    end;  //if pinfo^.si_code = SI_USER then  end;  //if pinfo <> Nil then   if pcontext <> Nil then  begin    WriteLn('signal context: set.');  end;   raise TPipeException.CreateHelp('Process [' + IntToStr(ipppid)    + ']: User (uid: ' + chr(39) + IntToStr(ippuid) + chr(39)    + '): Pipe failed', isignal); end;  function InstallSignalHandler: Integer;const  SMETHOD = 'InstallSignalHandler';var  nchldrec, ochldrec, npprec, opprec: SigActionRec;begin  Result := 0;   nchldrec.sa_handler := sigactionhandler(@RaiseChildException);  FillChar(nchldrec.sa_mask, SizeOf(nchldrec.sa_mask), #0);  nchldrec.sa_flags := 0 or SA_SIGINFO;  {$ifdef Linux}               // Linux specific    nchldrec.sa_restorer := Nil;  {$endif}   if FPSigAction(SIGCHLD, @nchldrec, @ochldrec) <> 0 then  begin    WriteLn(SMETHOD, ': FPSigAction(SIGCHLD): failed with [', fpgeterrno, '].');    //Self.brun := False;     Result := 1;  end;   npprec.sa_handler := sigactionhandler(@RaisePipeException);  FillChar(npprec.sa_mask, SizeOf(npprec.sa_mask), #0);  npprec.sa_flags := 0 or SA_SIGINFO;  {$ifdef Linux}               // Linux specific  npprec.sa_restorer := Nil;  {$endif}   if FPSigAction(SIGPIPE, @npprec, @opprec) <> 0 then  begin    WriteLn(SMETHOD, ': FPSigAction(SIGPIPE): failed with [', fpgeterrno, '].');    //Self.brun := False;     Result := 1;  end;end;  var  command: TProcess;  messagestream: TStringStream;  smessage: String;  sbuffer: String;  ReadSize: Integer;  bwait: Boolean;begin  command := TProcess.Create(nil);  messagestream := TStringStream.Create('');   InstallSignalHandler;   try    try      smessage := 'test input';     messagestream.WriteString(smessage);       command.Options    := [poUsePipes, poStderrToOutPut];      command.Executable := 'noexec_script.pl';       command.Execute;       WriteLn('Command Input: Writing ...');       WriteLn('Command Input (Length ', chr(39), messagestream.Size, chr(39), '):');      WriteLn(chr(39), messagestream.DataString, chr(39));      command.Input.Write(messagestream.DataString[1], messagestream.Size);       // Close the input on the SecondProcess    // so it finishes processing it's data      command.CloseInput;     // and wait for it to complete       bwait := command.WaitOnExit;     WriteLn('Command WaitOnExit: ', chr(39), bwait, chr(39));       // that's it! the rest of the program is just so the example      // is a little 'useful'       WriteLn('Command Output: Reading ...');       sbuffer := '';       ReadSize := command.Output.NumBytesAvailable;     WriteLn('Command Report (Length ', chr(39), ReadSize, chr(39), '):');       SetLength(sbuffer, ReadSize);       if ReadSize > 0 then      begin        command.Output.Read(sbuffer[1], ReadSize);         WriteLn(chr(39), sbuffer, chr(39));      end;       WriteLn('Command finished with [', command.ExitStatus, ']');    except      //------------------------      //Report Exception       on e : Exception do      begin        WriteLn('Command - failed with Exception [', e.HelpContext, ']: '          , chr(39), e.Message, chr(39));      end //on E : Exception do      else      begin        WriteLn('Command - failed with Unknown Exception: '          , chr(39), 'unknown error', chr(39));      end;  //on e : Exception do  end;   finally    // free our process objects    messagestream.Free;    command.Free;  end; end.                                             With Uncaptured Signals the Output of the command_launcher Application is: $ ./command_launcher RaiseChildException: go ... got signal: '17' signal info: no: '17'; cd: '1'; err: '0' signal info: pid: '3057'; cd: '1000'; stt: '127' signal context: set. Command Input: Writing ... Command Input (Length '10'): 'test input' The strace log documents: strace: Process 3048 attached restart_syscall(<... resuming interrupted nanosleep ...>) = 0 mmap(NULL, 32768, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc83b586000 rt_sigaction(SIGCHLD, {sa_handler=0x401080, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x402710}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8 ) = 0 pipe([3, 4])                            = 0 pipe([5, 6])                            = 0 access("noexec_script.pl", F_OK)        = 0 fork()                                  = 3057 close(4)                                = 0 close(5)                                = 0 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3057, si_uid=1000, si_status=127, si_utime=0, si_stime=0} --- write(1, "RaiseChildException: go ...\n", 28) = 28 write(1, "got signal: '17'\n", 17)      = 17 write(1, "signal info: no: '17'; cd: '1'; "..., 41) = 41 write(1, "signal info: pid: '3057'; cd: '1"..., 49) = 49 write(1, "signal context: set.\n", 21)  = 21 rt_sigreturn({mask=[]})                 = 0 write(1, "Command Input: Writing ...\n", 27) = 27 write(1, "Command Input (Length '10'):\n", 29) = 29 write(1, "'test input'\n", 13)          = 13 write(6, "test input", 10)              = -1 EPIPE (Tubería rota) --- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=3048, si_uid=1000} --- +++ killed by SIGPIPE +++ To recognize the SIGCHLD Signal correctly the POSIX Documentation explains: http://man7.org/linux/man-pages/man2/sigaction.2.html Quote* SIGCHLD fills in si_pid, si_uid, si_status, si_utime, and si_stime,          providing information about the child.  The si_pid field is the          process ID of the child; si_uid is the child's real user ID.  The          si_status field contains the exit status of the child (if si_code is CLD_EXITED), or the signal number that caused the process to change state. So I need to match si_code against  CLD_EXITED. Unfortunately this Symbol is not defined in the FPC either. I looked it up in the Linux Kernel and found it in: include/uapi/asm-generic/siginfo.h: 264:#define CLD_EXITED   1   /* child has exited */ So I added those Definitions as local Constants for the Signal Handler Procedures. Now with the correct Signal Handler Procedure the Behaviour of the command_launcher Prototype Application changed to: $ ./command_launcher RaiseChildException: go ... got signal: '17' signal info: no: '17'; cd: '1'; err: '0' signal info: pid: '3432'; uid: '1000'; stt: '127' signal context: set. Command - failed with Exception [127]: 'Child [3432]: User (uid: '1000'): Launch failed with [127]' Heap dump by heaptrc unit 42 memory blocks allocated : 2780/2832 42 memory blocks freed     : 2780/2832 0 unfreed memory blocks : 0 True heap size : 131072 True free heap : 131072 And the strace log documents the correct Signal Handling: strace: Process 3418 attached restart_syscall(<... resuming interrupted nanosleep ...>) = 0 mmap(NULL, 32768, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f757ed26000 rt_sigaction(SIGCHLD, {sa_handler=0x401080, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x402d60}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8 ) = 0 rt_sigaction(SIGPIPE, {sa_handler=0x401620, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x402d60}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8 ) = 0 pipe([3, 4])                            = 0 pipe([5, 6])                            = 0 mmap(NULL, 32768, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f757ed1e000 access("noexec_script.pl", F_OK)        = 0 fork()                                  = 3432 close(4)                                = 0 close(5)                                = 0 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3432, si_uid=1000, si_status=127, si_utime=0, si_stime=0} --- write(1, "RaiseChildException: go ...\n", 28) = 28 write(1, "got signal: '17'\n", 17)      = 17 write(1, "signal info: no: '17'; cd: '1'; "..., 41) = 41 write(1, "signal info: pid: '3432'; uid: '"..., 50) = 50 write(1, "signal context: set.\n", 21)  = 21 write(1, "Command - failed with Exception "..., 100) = 100 munmap(0x7f757ed4e000, 262144)          = 0 munmap(0x7f757ed46000, 32768)           = 0 munmap(0x7f757ed3e000, 32768)           = 0 write(2, "Heap dump by heaptrc unit\n", 26) = 26 write(2, "42 memory blocks allocated : 278"..., 39) = 39 write(2, "42 memory blocks freed     : 278"..., 39) = 39 write(2, "0 unfreed memory blocks : 0\n", 28) = 28 write(2, "True heap size : 131072", 23) = 23 write(2, "\n", 1)                       = 1 write(2, "True free heap : 131072\n", 24) = 24 munmap(0x7f757ed36000, 32768)           = 0 munmap(0x7f757ed2e000, 32768)           = 0 munmap(0x7f757ed26000, 32768)           = 0 munmap(0x7f757ed1e000, 32768)           = 0 exit_group(0)                           = ? +++ exited with 0 +++ ---------------------------------------------------------------------- trev · 2020-04-01 11:16 It would be helpful to log a bug for the missing defines or they may never be fixed. See How do I create a bug report. ---------------------------------------------------------------------- marcov · 2020-04-01 11:45 Afaik the siginfo is a more BSD solution. Iirc Linux only switched later on it when it got more architectures, so it might only do something on non-x86.