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
30 extern "C"
31{
32 void gcleanup_handler( int signum ) ;
33 using Handler = void (*)(int) ;
34}
35
36 namespace G
37{
38 class CleanupImp ;
39}
40
41//| \class G::CleanupImp
42/// A static implementation class used by G::Cleanup.
43///
44 class G::CleanupImp
45{
46 public:
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
78 private:
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
87 private:
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
95 private:
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 ;
106 bool G::CleanupImp::m_atexit_installed = false ;
107 bool G::CleanupImp::m_atexit_active = false ;
108
109// ===
110
111 void G::Cleanup::init()
112{
113 CleanupImp::installIgnore( SIGPIPE ) ;
114}
115
116 void G::Cleanup::add( bool (*fn)(SignalSafe,const char*) , const char * arg )
117{
118 CleanupImp::add( fn , arg ) ;
119}
120
121 void G::Cleanup::atexit( bool active )
122{
123 CleanupImp::atexit( active ) ;
124}
125
126 void G::Cleanup::block() noexcept
127{
128 CleanupImp::block() ;
129}
130
131 void G::Cleanup::release() noexcept
132{
133 CleanupImp::release() ;
134}
135
136 const char * G::Cleanup::strdup( const char * p )
137{
138 return CleanupImp::strdup_ignore_leaks( p ) ;
139}
140
141 const char * G::Cleanup::strdup( const std::string & s )
142{
143 return CleanupImp::strdup_ignore_leaks( s.c_str() ) ;
144}
145
146// ===
147
148 void 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
158 void 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
178 void 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
186 bool 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
194 void G::CleanupImp::installDefault( int signum )
195{
196 install( signum , SIG_DFL , true ) ;
197}
198
199 void G::CleanupImp::installDefault( const G::SignalSafe & , int signum )
200{
201 install( signum , SIG_DFL , false ) ;
202}
203
204 void G::CleanupImp::installIgnore( int signum )
205{
206 install( signum , SIG_IGN , true ) ; // NOLINT
207}
208
209 void 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
218 void 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
228 void G::CleanupImp::atexitHandler()
229{
230 if( m_atexit_active )
231 callHandlers() ;
232}
233
234 void G::CleanupImp::callHandlers()
235{
236 if( !callHandlersOnce( SignalSafe() ) )
237 {
238 Root::atExit( SignalSafe() ) ;
239 callHandlersOnce( SignalSafe() ) ;
240 }
241}
242
243 bool 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
262 extern "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
275 void 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
286 void 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
300 const char * G::CleanupImp::strdup_ignore_leaks( const char * p )
301{
302 return ::strdup( p ) ; // NOLINT
303}
304
G::CleanupImp
A static implementation class used by G::Cleanup.
Definition: gcleanup_unix.cpp:45
G::Cleanup::strdup
static const char * strdup(const char *)
A strdup() function that makes it clear in the stack trace that leaks are expected.
Definition: gcleanup_unix.cpp:136
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:121
G::Cleanup::release
static void release() noexcept
Releases block()ed signals.
Definition: gcleanup_unix.cpp:131
G::Cleanup::add
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...
Definition: gcleanup_unix.cpp:116
G::Cleanup::block
static void block() noexcept
Temporarily blocks signals until release()d.
Definition: gcleanup_unix.cpp:126
G::Cleanup::init
static void init()
An optional early-initialisation function. May be called more than once.
Definition: gcleanup_unix.cpp:111
G::Root::atExit
static void atExit() noexcept
Re-acquires special privileges just before process exit.
Definition: groot.cpp:68
G::SignalSafe
An empty structure that is used to indicate a signal-safe, reentrant implementation.
Definition: gsignalsafe.h:37
gcleanup.h
glog.h
gprocess.h
groot.h
G
Low-level classes.
Definition: galign.h:28