summaryrefslogtreecommitdiffstats
path: root/src/lib/util/signal_set.h
blob: ab63276202ab321fc9e9e4e6d7067e5985d4e360 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
// Copyright (C) 2014-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef SIGNAL_SET_H
#define SIGNAL_SET_H

#include <exceptions/exceptions.h>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <functional>
#include <set>
#include <list>
#include <pthread.h>
#include <signal.h>

namespace isc {
namespace util {

/// @brief Exception thrown when the @c isc::util::SignalSet class
/// experiences an error.
class SignalSetError : public Exception {
public:
    SignalSetError(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) { };
};


/// @brief Defines a set of integer signal identifiers: SIGHUP, SIGTERM...
typedef std::set<int> SigIntSet;
/// @brief Pointer to a set of signal identifiers
typedef boost::shared_ptr<SigIntSet> SigIntSetPtr;

/// @brief Defines a list of integer signal identifiers: SIGHUP, SIGTERM...
typedef std::list<int> SigIntList;
/// @brief Pointer to a list of signal identifiers
typedef boost::shared_ptr<SigIntList> SigIntListPtr;


/// @brief Forward declaration to the @c isc::util::SignalSet.
class SignalSet;
/// @brief Pointer to the @c isc::util::SignalSet.
typedef boost::shared_ptr<SignalSet> SignalSetPtr;
/// @brief Pointer to the signal handling function.
typedef std::function<void(int signum)> SignalHandler;

/// @brief Pointer to a signal handling function which returns bool result.
///
/// The handler is expected to return true if the signal it was given has
/// been processed (i.e. should not be recorded for deferred processing) or
/// false in which case it will be recorded.
typedef std::function<bool(int signum)> BoolSignalHandler;

/// @brief Represents a collection of signals handled in a customized way.
///
/// Kea processes must handle selected signals in a specialized way. For
/// example: SIGINT and SIGTERM must perform a graceful shut down of the
/// server. The SIGHUP signal is used to trigger server's reconfiguration.
///
/// This class allows the caller to register one or more signals to catch
/// and process.  Signals may be handled either immediately upon arrival and/or
/// recorded and processed later.  To process signals immediately, the caller
/// must register an "on-receipt" handler.  This handler is expected to return
/// a true or false indicating whether or not the signal has been processed.
/// Signal occurrences that are not processed by the on-receipt handler are
/// remembered by SignalSet for deferred processing.  The caller can then query
/// SignalSet at their discretion to determine if any signal occurrences are
/// pending and process them.
///
/// SignalSet uses an internal handler to catch all registered signals. When
/// a signal arrives the internal handler will first attempt to invoke the
/// on-receipt handler.  If one has been registered it is passed the
/// signal value as an argument and if it returns true upon completion, the
/// internal handler will exit without further action.  If the on-receipt
/// handler returned false or one is not registered, then internal handler
/// will record the signal value for deferred processing.  Note that once a
/// particular signal has been recorded, any further occurrences of that signal
/// will be discarded until the original occurrence has been processed.  This
/// guards against rapid-fire occurrences of the same signal.
///
/// @note This class is not thread safe. It uses static variables and
/// functions to track a global state of signal registration and received
/// signals' queue. But the thread library is signal safe as new threads
/// are created with all signals blocked.
class SignalSet : public boost::noncopyable {
public:
    /// @brief Constructor installing one signal.
    ///
    /// @param sig0 First signal.
    /// @throw SignalSetError If attempting to add duplicated signal or
    /// the signal is invalid.
    SignalSet(const int sig0);

    /// @brief Constructor installing two signals.
    ///
    /// @param sig0 First signal.
    /// @param sig1 Second signal.
    /// @throw SignalSetError If attempting to add duplicated signal or
    /// the signal is invalid.
    SignalSet(const int sig0, const int sig1);

    /// @brief Constructor installing three signals.
    ///
    /// @param sig0 First signal.
    /// @param sig1 Second signal.
    /// @param sig2 Third signal.
    /// @throw SignalSetError If attempting to add duplicated signal or
    /// the signal is invalid.
    SignalSet(const int sig0, const int sig1, const int sig2);

    /// @brief Destructor.
    ///
    /// Removes installed handlers.
    ~SignalSet();

    /// @brief Installs the handler for the specified signal.
    ///
    /// This function adds a signal to the set. When the signal is received
    /// by the process, it will be recorded and a signal can be later handled
    /// by the process.
    ///
    /// @param sig Signal code.
    /// @throw SignalSetError if signal being added duplicates an existing
    /// signal.
    void add(const int sig);

    /// @brief Uninstalls all signals.
    ///
    /// This function calls @c isc::util::SignalSet::remove for each
    /// installed signal.
    void clear();

    /// @brief Returns a code of the next received signal.
    ///
    /// @return A code of the next received signal or -1 if there are no
    /// more signals received.
    int getNext();

    /// @brief Calls a handler for the next received signal.
    ///
    /// This function handles the next received signal and removes it from the
    /// queue of received signals. While the function is executed, all custom
    /// signal handlers are blocked to prevent race condition.
    ///
    /// @param signal_handler A pointer to the signal handler function to
    /// be used to handle the signal.
    void handleNext(SignalHandler signal_handler);

    /// @brief Uninstalls signal handler for a specified signal.
    ///
    /// @param sig A code of the signal to be removed.
    void remove(const int sig);

    /// @brief Registers a handler as the onreceipt signal handler
    ///
    /// Sets the given handler as the handler to invoke immediately
    /// upon receipt of a a registered signal.
    ///
    /// @note Currently, the on-receipt handler is stored as a static
    /// value and hence there may only be one such handler at a time
    /// for a given process.
    ///
    /// @param handler the signal handler to register
    static void setOnReceiptHandler(BoolSignalHandler handler);

    /// @brief Unregisters the onreceipt signal handler
    static void clearOnReceiptHandler();

    /// @brief Invokes the onreceipt handler if it exists
    ///
    /// This static method is used by @c isc::util::SignalSet class to
    /// invoke the registered handler (if one) immediately upon receipt of
    /// a registered signal.
    ///
    /// Prior to invoking the handler, it sets signal action for the
    /// given signal to SIG_IGN which prevents any repeat signal
    /// occurrences from queuing while the handler is executing.  Upon
    /// completion of the handler, the signal action is restored which
    /// re-enables receipt and handling of the signal.
    ///
    /// @param sig Signal number.
    /// @return Boolean false if no on-receipt handler was registered,
    /// otherwise it is the value returned by the on-receipt handler.
    static bool invokeOnReceiptHandler(int sig);

private:

    /// @brief Blocks signals in the set.
    ///
    /// This function blocks the signals in a set to prevent race condition
    /// between the signal handler and the new signal coming in.
    void block();

    /// @brief Removes the signal from the set.
    ///
    /// This function removes only a signal which is owned by this signal set.
    ///
    /// @param sig Signal to be removed.
    /// @throw SignalSetError if the signal being removed is not owned by this
    /// signal set.
    void erase(const int sig);

    /// @brief Insert a signal to the set.
    ///
    /// @param sig Signal to be inserted.
    /// @throw SignalSetError if a signal being inserted has already been
    /// registered in this or other signal set.
    void insert(const int sig);

    /// @brief Applies a mask to all signals in the set.
    ///
    /// This function is used by @c SignalSet::block and @c SignalSet::unblock
    /// to apply the SIG_BLOCK and SIG_UNBLOCK mask to signals.
    ///
    /// @param mask A mask to be applied to all signals.
    /// @param sig Optional signal to be masked. If this value is negative all
    /// signals are masked.
    void maskSignals(const int mask, const int sig = -1) const;

    /// @brief Pops a next signal number from the static collection of signals.
    ///
    /// The static collection of signals is updated by the internal signal
    /// handler being invoked when one of the installed signals is received by
    /// the process. This function removes the first element of the collection.
    void popNext();

    /// @brief Unblocks signals in the set.
    ///
    /// This function unblocks the signals in a set.
    ///
    /// @param sig Optional signal to be unblocked. If this is negative, all
    /// registered signals are unblocked.
    void unblock(int sig = -1);

    /// @brief Indicates if receiving signals is blocked.
    bool blocked_;

    /// @brief Stores the set of signals registered in this signal set.
    std::set<int> local_signals_;

    /// @brief Shared pointer to static set of registered signals
    /// Set during construction to ensure static set does not lose scope
    /// before we do.
    SigIntSetPtr registered_signals_;

    /// @brief Shared pointer to static list of pending signals
    /// Set during construction to ensure static list does not lose scope
    /// before we do.
    SigIntListPtr signal_states_;
};

}
}

#endif // SIGNAL_SET_H