E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gcleanup_unix.cpp
1 //
2 // Copyright (C) 2001-2018 Graeme Walker <graeme_walker@users.sourceforge.net>
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 ///
42 class G::CleanupImp
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
98 void G::Cleanup::init()
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
G::Cleanup::atexit
static void atexit(bool active=true)
Ensures that the cleanup functions are also called via atexit(), in addition to abnormal-termination ...
Definition: gcleanup_unix.cpp:108
G::SignalSafe
An empty structure that is used to indicate a signal-safe, reentrant implementation.
Definition: gsignalsafe.h:36
glog.h
G::Root::start
static Identity start(SignalSafe)
A signal-safe alternative to construction.
Definition: groot.cpp:80
groot.h
G::Root::stop
static void stop(SignalSafe, Identity)
A signal-safe alternative to destruction.
Definition: groot.cpp:86
gcleanup.h
G
Low-level classes.
Definition: garg.h:29
gprocess.h
G::Cleanup::init
static void init()
An optional early-initialisation function. May be called more than once.
Definition: gcleanup_unix.cpp:98
G::CleanupImp
A pimple-pattern implementation class used by G::Cleanup.
Definition: gcleanup_unix.cpp:42
G::Cleanup::add
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...
Definition: gcleanup_unix.cpp:103