E-MailRelay
gcleanup_unix.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 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/// \file 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 <cstring> // ::strdup()
27#include <csignal> // ::sigaction() etc
28#include <array>
29
30extern "C"
31{
32 void gcleanup_handler( int signum ) ;
33 using Handler = void (*)(int) ;
34}
35
36namespace G
37{
38 class CleanupImp ;
39}
40
41//| \class G::CleanupImp
42/// A static implementation class used by G::Cleanup.
43///
45{
46public:
47 static void add( bool (*fn)(SignalSafe,const char*) , const char * ) ;
48 // Adds a cleanup function.
49
50 static void installDefault( const SignalSafe & , int ) ;
51 // Installs the SIG_DFL signal handler for the given signal.
52
53 static void installDefault( int ) ;
54 // Installs the SIG_DFL signal handler for the given signal.
55
56 static void installIgnore( int ) ;
57 // Installs the SIG_IGN signal handler for the given signal.
58
59 static void callHandlers() ;
60 // Calls all the cleanup functions. Any that fail are automatically
61 // retried with Root::atExit().
62
63 static bool callHandlersOnce( SignalSafe ) ;
64 // Calls all the cleanup functions.
65
66 static void atexit( bool active ) ;
67 // Registers callHandlers() with atexit(3) if the active parameter is true.
68
69 static void block() noexcept ;
70 // Blocks signals until released.
71
72 static void release() noexcept ;
73 // Releases blocked signals.
74
75 static const char * strdup_ignore_leaks( const char * p ) ;
76 // A strdup() function.
77
78private:
79 struct Link /// A private linked-list structure used by G::CleanupImp.
80 {
81 bool (*fn)(SignalSafe,const char*) ;
82 const char * arg ;
83 Link * next ;
84 bool done ;
85 } ;
86
87private:
88 static void init() ;
89 static void install( int , Handler , bool ) ;
90 static void installHandler( int ) ;
91 static bool ignored( int ) ;
92 static void atexitHandler() ;
93 static Link * new_link_ignore_leak() ;
94
95private:
96 static Link * m_head ;
97 static Link * m_tail ;
98 static bool m_atexit_active ;
99 static bool m_atexit_installed ;
100 static std::array<int,4U> m_signals ;
101} ;
102
103std::array<int,4U> G::CleanupImp::m_signals = {{ SIGTERM , SIGINT , SIGHUP , SIGQUIT }} ;
104G::CleanupImp::Link * G::CleanupImp::m_head = nullptr ;
105G::CleanupImp::Link * G::CleanupImp::m_tail = nullptr ;
106bool G::CleanupImp::m_atexit_installed = false ;
107bool G::CleanupImp::m_atexit_active = false ;
108
109// ===
110
112{
113 CleanupImp::installIgnore( SIGPIPE ) ;
114}
115
116void G::Cleanup::add( bool (*fn)(SignalSafe,const char*) , const char * arg )
117{
118 CleanupImp::add( fn , arg ) ;
119}
120
121void G::Cleanup::atexit( bool active )
122{
123 CleanupImp::atexit( active ) ;
124}
125
126void G::Cleanup::block() noexcept
127{
128 CleanupImp::block() ;
129}
130
131void G::Cleanup::release() noexcept
132{
133 CleanupImp::release() ;
134}
135
136const char * G::Cleanup::strdup( const char * p )
137{
138 return CleanupImp::strdup_ignore_leaks( p ) ;
139}
140
141const char * G::Cleanup::strdup( const std::string & s )
142{
143 return CleanupImp::strdup_ignore_leaks( s.c_str() ) ;
144}
145
146// ===
147
148void G::CleanupImp::init()
149{
150 // install our meta-handler for signals that normally terminate the process,
151 // except for sigpipe which we ignore
152 //
153 installIgnore( SIGPIPE ) ;
154 for( int s : m_signals )
155 installHandler( s ) ;
156}
157
158void G::CleanupImp::add( bool (*fn)(SignalSafe,const char*) , const char * arg )
159{
160 Link * p = new_link_ignore_leak() ;
161 p->fn = fn ;
162 p->arg = arg ;
163 p->next = nullptr ;
164 p->done = false ;
165
166 Cleanup::Block block ;
167 if( m_head == nullptr ) init() ;
168 if( m_tail != nullptr ) m_tail->next = p ;
169 m_tail = p ;
170 if( m_head == nullptr ) m_head = p ;
171}
172
173G::CleanupImp::Link * G::CleanupImp::new_link_ignore_leak()
174{
175 return new Link ; // ignore leak
176}
177
178void G::CleanupImp::installHandler( int signum )
179{
180 if( ignored(signum) )
181 G_DEBUG( "G::CleanupImp::installHandler: signal " << signum << " is ignored" ) ;
182 else
183 install( signum , gcleanup_handler , true ) ;
184}
185
186bool G::CleanupImp::ignored( int signum )
187{
188 struct ::sigaction action {} ;
189 if( ::sigaction( signum , nullptr , &action ) != 0 )
190 throw Cleanup::Error( "sigaction" ) ;
191 return action.sa_handler == SIG_IGN ; // NOLINT
192}
193
194void G::CleanupImp::installDefault( int signum )
195{
196 install( signum , SIG_DFL , true ) ;
197}
198
199void G::CleanupImp::installDefault( const G::SignalSafe & , int signum )
200{
201 install( signum , SIG_DFL , false ) ;
202}
203
204void G::CleanupImp::installIgnore( int signum )
205{
206 install( signum , SIG_IGN , true ) ; // NOLINT
207}
208
209void G::CleanupImp::install( int signum , Handler fn , bool do_throw )
210{
211 // install the given handler, or the system default if null
212 struct ::sigaction action {} ;
213 action.sa_handler = fn ;
214 if( ::sigaction( signum , &action , nullptr ) != 0 && do_throw )
215 throw Cleanup::Error( "sigaction" ) ;
216}
217
218void G::CleanupImp::atexit( bool active )
219{
220 if( active && !m_atexit_installed )
221 {
222 m_atexit_installed = true ;
223 ::atexit( atexitHandler ) ;
224 }
225 m_atexit_active = active ;
226}
227
228void G::CleanupImp::atexitHandler()
229{
230 if( m_atexit_active )
231 callHandlers() ;
232}
233
234void G::CleanupImp::callHandlers()
235{
236 if( !callHandlersOnce( SignalSafe() ) )
237 {
238 Root::atExit( SignalSafe() ) ;
239 callHandlersOnce( SignalSafe() ) ;
240 }
241}
242
243bool G::CleanupImp::callHandlersOnce( SignalSafe )
244{
245 bool all_ok = true ;
246 for( Link * p = m_head ; p != nullptr ; p = p->next )
247 {
248 try
249 {
250 if( !p->done && (*(p->fn))(SignalSafe(),p->arg) )
251 p->done = true ;
252 else
253 all_ok = false ;
254 }
255 catch(...)
256 {
257 }
258 }
259 return all_ok ;
260}
261
262extern "C" void gcleanup_handler( int signum )
263{
264 // call the registered handler(s) and exit
265 try
266 {
267 G::CleanupImp::callHandlers() ;
268 std::_Exit( signum + 128 ) ;
269 }
270 catch(...)
271 {
272 }
273}
274
275void G::CleanupImp::block() noexcept
276{
277 sigset_t set ;
278 sigemptyset( &set ) ;
279 for( int s : m_signals )
280 {
281 sigaddset( &set , s ) ;
282 }
283 gdef_pthread_sigmask( SIG_BLOCK , &set , nullptr ) ; // gdef.h
284}
285
286void G::CleanupImp::release() noexcept
287{
288 sigset_t emptyset ;
289 sigemptyset( &emptyset ) ;
290 sigset_t set ;
291 sigemptyset( &set ) ;
292 gdef_pthread_sigmask( SIG_BLOCK , &emptyset , &set ) ;
293 for( int s : m_signals )
294 {
295 sigdelset( &set , s ) ;
296 }
297 gdef_pthread_sigmask( SIG_SETMASK , &set , nullptr ) ;
298}
299
300const char * G::CleanupImp::strdup_ignore_leaks( const char * p )
301{
302 return ::strdup( p ) ; // NOLINT
303}
304
A static implementation class used by G::Cleanup.
static const char * strdup(const char *)
A strdup() function that makes it clear in the stack trace that leaks are expected.
static void atexit(bool active=true)
Ensures that the cleanup functions are also called via atexit(), in addition to abnormal-termination ...
static void release() noexcept
Releases block()ed signals.
static void add(bool(*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...
static void block() noexcept
Temporarily blocks signals until release()d.
static void init()
An optional early-initialisation function. May be called more than once.
static void atExit() noexcept
Re-acquires special privileges just before process exit.
Definition: groot.cpp:68
An empty structure that is used to indicate a signal-safe, reentrant implementation.
Definition: gsignalsafe.h:37
Low-level classes.
Definition: galign.h:28