VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gcleanup_unix.cpp
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gcleanup_unix.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gcleanup.h"
23 #include "gprocess.h"
24 #include "groot.h"
25 #include "glog.h"
26 #include <signal.h>
27 
28 extern "C"
29 {
30  void gcleanup_unix_handler_( int signum ) ;
31  typedef void (*Handler)( int ) ;
32 }
33 
34 namespace G
35 {
36  class CleanupImp ;
37 }
38 
39 /// \class G::CleanupImp
40 /// A pimple-pattern implementation class used by G::Cleanup.
41 ///
43 {
44 public:
45  static void add( void (*fn)(SignalSafe,const char*) , const char * ) ;
46  // Adds a cleanup function.
47 
48  static void installDefault( const SignalSafe & , int ) ;
49  // Installs the SIG_DFL signal handler for the given signal.
50 
51  static void installDefault( int ) ;
52  // Installs the SIG_DFL signal handler for the given signal.
53 
54  static void installIgnore( int ) ;
55  // Installs the SIG_IGN signal handler for the given signal.
56 
57  static void callHandlers( SignalSafe ) ;
58  // Calls all the cleanup functions.
59 
60  static void atexit( bool active ) ;
61  // Registers callHandlers() with atexit(3) if the active parameter is true.
62 
63 private:
64  struct Link /// A private linked-list structure used by G::CleanupImp.
65  {
66  void (*fn)(SignalSafe,const char*) ;
67  const char * arg ;
68  Link * next ;
69  } ;
70  struct BlockSignals /// A private implementation class that temporarily blocks signals.
71  {
72  BlockSignals() ;
73  ~BlockSignals() ;
74  } ;
75 
76 private:
77  static void init() ;
78  static void install( int , Handler , bool ) ;
79  static void installHandler( int ) ;
80  static bool ignored( int ) ;
81  static void atexitHandler() ;
82  static Link * new_link_ignore_leak() ;
83 
84 private:
85  static Link * m_head ;
86  static Link * m_tail ;
87  static bool m_atexit_active ;
88  static bool m_atexit_installed ;
89 } ;
90 
91 G::CleanupImp::Link * G::CleanupImp::m_head = nullptr ;
92 G::CleanupImp::Link * G::CleanupImp::m_tail = nullptr ;
93 bool G::CleanupImp::m_atexit_installed = false ;
94 bool G::CleanupImp::m_atexit_active = false ;
95 
96 // ===
97 
99 {
100  CleanupImp::installIgnore( SIGPIPE ) ;
101 }
102 
103 void G::Cleanup::add( void (*fn)(SignalSafe,const char*) , const char * arg )
104 {
105  CleanupImp::add( fn , arg ) ; // was if(arg!=nullptr)
106 }
107 
108 void G::Cleanup::atexit( bool active )
109 {
110  CleanupImp::atexit( active ) ;
111 }
112 
113 // ===
114 
115 void G::CleanupImp::init()
116 {
117  // install our meta-handler for signals that normally terminate the process,
118  // except for sigpipe which we ignore
119  //
120  installIgnore( SIGPIPE ) ;
121  installHandler( SIGTERM ) ;
122  installHandler( SIGINT ) ;
123  installHandler( SIGHUP ) ;
124  installHandler( SIGQUIT ) ;
125  //installHandler( SIGUSR1 ) ;
126  //installHandler( SIGUSR2 ) ;
127 }
128 
129 void G::CleanupImp::add( void (*fn)(SignalSafe,const char*) , const char * arg )
130 {
131  Link * p = new_link_ignore_leak() ;
132  p->fn = fn ;
133  p->arg = arg ;
134  p->next = nullptr ;
135 
136  // (all the signal-handling code treats the m_head/m_tail
137  // list as immutable, so there are no locking issues here)
138 
139  if( m_head == nullptr ) init() ;
140  if( m_tail != nullptr ) m_tail->next = p ;
141  m_tail = p ;
142  if( m_head == nullptr ) m_head = p ;
143 }
144 
145 G::CleanupImp::Link * G::CleanupImp::new_link_ignore_leak()
146 {
147  return new Link ;
148 }
149 
150 void G::CleanupImp::installHandler( int signum )
151 {
152  if( ignored(signum) )
153  G_DEBUG( "G::CleanupImp::installHandler: signal " << signum << " is ignored" ) ;
154  else
155  install( signum , gcleanup_unix_handler_ , true ) ;
156 }
157 
158 bool G::CleanupImp::ignored( int signum )
159 {
160  static struct sigaction zero_action ;
161  struct sigaction action( zero_action ) ;
162  if( ::sigaction( signum , nullptr , &action ) )
163  throw Cleanup::Error( "sigaction" ) ;
164  return action.sa_handler == SIG_IGN ;
165 }
166 
167 void G::CleanupImp::installDefault( int signum )
168 {
169  install( signum , SIG_DFL , true ) ;
170 }
171 
172 void G::CleanupImp::installDefault( const G::SignalSafe & , int signum )
173 {
174  install( signum , SIG_DFL , false ) ;
175 }
176 
177 void G::CleanupImp::installIgnore( int signum )
178 {
179  install( signum , SIG_IGN , true ) ;
180 }
181 
182 void G::CleanupImp::install( int signum , Handler fn , bool do_throw )
183 {
184  // install the given handler, or the system default if null
185  static struct sigaction zero_action ;
186  struct sigaction action( zero_action ) ;
187  action.sa_handler = fn ;
188  if( ::sigaction( signum , &action , nullptr ) && do_throw )
189  throw Cleanup::Error( "sigaction" ) ;
190 }
191 
192 void G::CleanupImp::atexit( bool active )
193 {
194  if( active && !m_atexit_installed )
195  {
196  m_atexit_installed = true ;
197  ::atexit( atexitHandler ) ;
198  }
199  m_atexit_active = active ;
200 }
201 
202 void G::CleanupImp::atexitHandler()
203 {
204  if( m_atexit_active )
205  callHandlers( SignalSafe() ) ;
206 }
207 
208 void G::CleanupImp::callHandlers( SignalSafe )
209 {
210  Identity identity = Root::start( SignalSafe() ) ;
211  for( const Link * p = m_head ; p != nullptr ; p = p->next )
212  {
213  try
214  {
215  (*(p->fn))(SignalSafe(),p->arg) ;
216  }
217  catch(...)
218  {
219  }
220  }
221  Root::stop( SignalSafe() , identity ) ;
222 }
223 
224 extern "C"
225 void gcleanup_unix_handler_( int signum )
226 {
227  // call the registered handler(s) and then do the system default action
228  try
229  {
230  int e = G::Process::errno_( G::SignalSafe() ) ;
231  G::CleanupImp::callHandlers( G::SignalSafe() ) ;
232  G::CleanupImp::installDefault( G::SignalSafe() , signum ) ;
233  G::Process::errno_( G::SignalSafe() , e ) ;
234  ::raise( signum ) ;
235  }
236  catch(...)
237  {
238  }
239 }
240 
static void atexit(bool active=true)
Ensures that the cleanup functions are also called via atexit(), in addition to abnormal-termination ...
An empty structure that is used to indicate a signal-safe, reentrant implementation.
Definition: gsignalsafe.h:36
static Identity start(SignalSafe)
A signal-safe alternative to construction.
Definition: groot.cpp:80
static void stop(SignalSafe, Identity)
A signal-safe alternative to destruction.
Definition: groot.cpp:86
static void init()
An optional early-initialisation function. May be called more than once.
A pimple-pattern implementation class used by G::Cleanup.
static void add(void(*fn)(SignalSafe, const char *), const char *arg)
Adds the given handler to the list of handlers that are to be called when the process terminates abno...