Table of Contents
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;