Introduction

Sometimes it's necessary for your program to be able to catch and handle interrupts such as SIGINT, SIGTERM or SIGHUP, and for this we have the Interrupts package. Attaching handlers to interrupts can be done both statically and dynamically and, luckily, both methods are easy to grasp. The following program will show you both methods.

The Program

I've commented the program, so it should be fairly easy to understand what's going on. You can grab the entire program from this Git repository:

git://github.com/ThomasLocke/Catch_Signals.git

Git is available for all Linux distros. The commands to fetch Catch_Signals using Git and then compile it are:

$ git clone git://github.com/ThomasLocke/Catch_Signals.git
$ cd Catch_Signals
$ gnatmake -P catch_signals.gpr

The executable can now be found in the exe/ directory.

If you don't feel like using Git, the full source code can be copied from below. I will of course do my best to make sure that the code on this page matches the code in the Git repository, but since this is a wiki where anybody can make changes, I cannot guarantee the safety of running the code on this page, so use it with caution.

The program attach signal handlers to these interrupts:

  • SIGHUP
  • SIGINT
  • SIGPWR
  • SIGTERM

You can send those interrupts to the running program like this:

kill -SIGHUP pid

Where pid is the Process Id of the program.

catch_signals.gpr

This file sets up compile options. It informs the compiler of the location of the source files and where to put the object and the executable files. You must, of course, create the exe and build directories, if you haven't cloned the Git repository.

-------------------------------------------------------------------------------
--                                                                           --
--                       Catch_Signals Project File                          --
--                                                                           --
--                     Copyright (C) 2010, Thomas Løcke                      --
--                                                                           --
--  Catch_Signals is free software;  you can  redistribute it  and/or modify --
--  it under terms of the  GNU General Public License as published  by the   --
--  Free Software  Foundation;  either version 2,  or (at your option) any   --
--  later version.  Catch_Signals is distributed in the hope that it will be --
--  useful, but WITHOUT ANY WARRANTY;  without even the  implied warranty of --
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        --
--  General Public License for  more details.  You should have  received  a  --
--  copy of the GNU General Public License  distributed with Catch_Signals.  --
--  If not, write  to  the  Free Software Foundation,  51  Franklin  Street, --
--   Fifth  Floor, Boston, MA 02110 - 1301, USA.                             --
--                                                                           --
-------------------------------------------------------------------------------
 
project Catch_Signals is
 
   for Source_Dirs use (".",
                        "src");
 
   for Main use ("catch_signals.adb");
 
   for Exec_Dir use "exe";
 
   for Object_Dir use "build";
 
   package Ide is
 
      for Compiler_Command ("ada") use "gnatmake";
 
   end Ide;
 
   package Compiler is
 
      Common_Options := ("-gnatwa",
                         "-gnaty3abcdefhiklmnoprstux",
                         "-Wall",
                         "-O2",
                         "-gnat05");
 
     for Default_Switches ("Ada") use Common_Options;
 
   end Compiler;
 
end Catch_Signals;

catch_signals.adb

This is the main program file. Not much happens here. We simply wait for an interrupt at the Process_Control.Wait statement.

-------------------------------------------------------------------------------
--                                                                           --
--                              Catch_Signals                                --
--                                                                           --
--                     Copyright (C) 2010, Thomas Løcke                      --
--                                                                           --
--  Catch_Signals is free software;  you can  redistribute it  and/or modify --
--  it under terms of the  GNU General Public License as published  by the   --
--  Free Software  Foundation;  either version 2,  or (at your option) any   --
--  later version.  Catch_Signals is distributed in the hope that it will be --
--  useful, but WITHOUT ANY WARRANTY;  without even the  implied warranty of --
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Gene-  --
--  ral Public License for  more details.  You should have  received  a copy --
--  of the GNU General Public License  distributed with Catch_Signals.  If   --
--  not, write to  the  Free Software Foundation,  51  Franklin  Street,     --
--  Fifth Floor, Boston, MA 02110 - 1301, USA.                               --
--                                                                           --
-------------------------------------------------------------------------------
 
with Ada.Text_IO;
with Process_Control;
 
procedure Catch_Signals 
is
   use Ada.Text_IO; 
begin
   Put_Line ("Lets catch some interrupts!");
 
   Process_Control.Wait;
   --  Wait for an interrupt here. The program is "stuck" here until the
   --  Process_State variable is set to Shutdown by one of the registered
   --  interrupt handlers.
 
   Put_Line ("Interrupt caught - shutting down.");
end Catch_Signals;

src/process_control.ads

In this file we define our interrupt handlers. For an example on how to dynamically attach an interrupt handler, take a look at the SIGHUP code.

-------------------------------------------------------------------------------
--                                                                           --
--                              Catch_Signals                                --
--                                                                           --
--                             process_control                               --
--                                                                           --
--                                  SPEC                                     --
--                                                                           --
--                     Copyright (C) 2010, Thomas Løcke                      --
--                                                                           --
--  Catch_Signals is free software;  you can  redistribute it  and/or modify --
--  it under terms of the  GNU General Public License as published  by the   --
--  Free Software  Foundation;  either version 2,  or (at your option) any   --
--  later version.  Catch_Signals is distributed in the hope that it will be --
--  useful, but WITHOUT ANY WARRANTY;  without even the  implied warranty of --
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Gene-  --
--  ral Public License for  more details.  You should have  received  a copy --
--  of the GNU General Public License  distributed with Catch_Signals.  If   --
--  not, write to  the  Free Software Foundation,  51  Franklin  Street,     --
--  Fifth Floor, Boston, MA 02110 - 1301, USA.                               --
--                                                                           --
-------------------------------------------------------------------------------
 
with Ada.Interrupts.Names;
with Ada.Text_IO;
 
package Process_Control is
   pragma Unreserve_All_Interrupts;
   --  Make sure that GNAT does not handle any interrupts automatically.
   --  As per GNAT 2010, this pragma only affects SIGINT.
 
   procedure Wait;
   --  Wait until Process_State is Shutdown.
private
   type State is (Running, Shutdown, Stopped);
   --  Define three different states of the running process.
 
   package State_IO is new Ada.Text_IO.Enumeration_IO (State);
   --  Instantiate a package so we can output the value of Process_State using
   --  the familiar Put procedure.
 
   Wait_Called : Boolean := False;
   --  Set to True when Wait is called the first time.
 
   protected Controller is
      entry Check;
      --  Check the Process_State is Shutdown, and if so, shut down the process
 
      function Get_State return State;
      --  Return the current Process_State.
 
      procedure Handle_SIGINT;
      procedure Handle_SIGPWR;
      procedure Handle_SIGTERM;
      pragma Attach_Handler (Handle_SIGINT, Ada.Interrupts.Names.SIGINT);
      pragma Attach_Handler (Handle_SIGPWR, Ada.Interrupts.Names.SIGPWR);
      pragma Attach_Handler (Handle_SIGTERM, Ada.Interrupts.Names.SIGTERM);
      --  Statically attach handlers to the SIGINT, SIGPWR, SIGHUP and SIGTERM 
      --  interrupts. 
 
      procedure Handle_SIGHUP_Change_Handler;
      procedure Handle_SIGHUP_Shutdown;
      pragma Interrupt_Handler (Handle_SIGHUP_Change_Handler);
      pragma Interrupt_Handler (Handle_SIGHUP_Shutdown);
      --  Handle_SIGHUP_* can now be dynamically assigned using the
      --  Ada.Interrupts.Attach_Handler procedure.
 
      entry Start;
      --  Set Process_State to Running.
   private
      Process_State : State := Stopped;
      --  What state the process is in.
   end Controller;
 
end Process_Control;

src/process_control.adb

Pay special attention to the Wait procedure where the SIGHUP interrupt is attached to the Handle_SIGHUP_Change_Handler procedure, and later re-attached to the Handle_SIGHUP_Shutdown procedure. This means that in order to kill the program using SIGHUP, you have to do kill -SIGHUP pid twice.

-------------------------------------------------------------------------------
--                                                                           --
--                               Catch_Signals                               --
--                                                                           --
--                              process_control                              --
--                                                                           --
--                                  BODY                                     --
--                                                                           --
--                     Copyright (C) 2010, Thomas Løcke                      --
--                                                                           --
--  Catch_Signals is free software;  you can  redistribute it  and/or modify --
--  it under terms of the  GNU General Public License as published  by the   --
--  Free Software  Foundation;  either version 2,  or (at your option) any   --
--  later version.  Catch_Signals is distributed in the hope that it will be --
--  useful, but WITHOUT ANY WARRANTY;  without even the  implied warranty of --
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Gene-  --
--  ral Public License for  more details.  You should have  received  a copy --
--  of the GNU General Public License  distributed with Catch_Signals.  If   --
--  not, write to  the  Free Software Foundation,  51  Franklin  Street,     --
--  Fifth Floor, Boston, MA 02110 - 1301, USA.                               --
--                                                                           --
-------------------------------------------------------------------------------
 
with Ada.Interrupts;
 
package body Process_Control is
 
   ------------
   --  Wait  --
   ------------
 
   procedure Wait
   is
      use Ada.Interrupts;
      use Ada.Text_IO;
      use State_IO;
   begin 
      if not Wait_Called then
         Wait_Called := True;
 
         Attach_Handler 
           (New_Handler => Controller.Handle_SIGHUP_Change_Handler'Access,
            Interrupt   => Ada.Interrupts.Names.SIGHUP);
         --  Dynamically attach a handler to the SIGHUP interrupt.
 
         Put ("Wait called. Process_State is ");
         Put (Controller.Get_State);
         New_Line;
 
         Controller.Start;
         Controller.Check;
      end if;
   end Wait;
 
   ------------------
   --  Controller  --
   ------------------
 
   protected body Controller is
 
      -------------
      --  Check  --
      -------------
 
      entry Check when Process_State = Shutdown
      is
         use Ada.Text_IO;
         use State_IO;
      begin
         Put ("Check called. Process_State is ");
         Put (Controller.Get_State);
         New_Line;
      end Check;
 
      -----------------
      --  Get_State  --
      -----------------
 
      function Get_State return State
      is
      begin
         return Process_State;
      end Get_State;
 
      ------------------------------------
      --  Handle_SIGHUP_Change_Handler  --
      ------------------------------------
 
      procedure Handle_SIGHUP_Change_Handler 
      is
         use Ada.Interrupts;
         use Ada.Text_IO;
      begin
         Put_Line ("Handle_SIGHUP_Change_Handler called.");
         Put_Line 
           ("Dynamically changing handler to Handle_SIGHUP_Shutdown.");
         Put_Line ("Next SIGHUP will stop the program.");
 
         Attach_Handler (New_Handler => Handle_SIGHUP_Shutdown'Access,
                         Interrupt   => Ada.Interrupts.Names.SIGHUP);
         --  Dynamically assign a new handler to the SIGHUP interrupt.
      end Handle_SIGHUP_Change_Handler;
 
      ------------------------------
      --  Handle_SIGHUP_Shutdown  --
      ------------------------------
 
      procedure Handle_SIGHUP_Shutdown
      is
         use Ada.Text_IO;
      begin
         Put_Line ("Handle_SIGHUP_Shutdown called.");
         Process_State := Shutdown;
      end Handle_SIGHUP_Shutdown;
 
      ---------------------
      --  Handle_SIGINT  --
      ---------------------
 
      procedure Handle_SIGINT 
      is
         use Ada.Text_IO;
      begin
         Put_Line ("Handle_SIGINT called.");
         Process_State := Shutdown;
      end Handle_SIGINT;
 
      ---------------------
      --  Handle_SIGPWR  --
      ---------------------
 
      procedure Handle_SIGPWR 
      is
         use Ada.Text_IO;
      begin
         Put_Line ("Handle_SIGPWR called.");
         Process_State := Shutdown;
      end Handle_SIGPWR;
 
      ----------------------
      --  Handle_SIGTERM  --
      ----------------------
 
      procedure Handle_SIGTERM 
      is 
         use Ada.Text_IO;
      begin
         Put_Line ("Handle_SIGTERM called.");
         Process_State := Shutdown;
      end Handle_SIGTERM;
 
      -------------
      --  Start  --
      -------------
 
      entry Start when Process_State = Stopped
      is
         use Ada.Text_IO;
         use State_IO;
      begin
         Process_State := Running;
         Put ("Start called. Process_State is ");
         Put (Controller.Get_State);
         New_Line;
 
         Put_Line ("I will react on SIGHUP, SIGINT, SIGPWR and SIGTERM.");
 
      end Start;
   end Controller;
end Process_Control;

Navigation