1 /**
2 * @file signal_handlers.cc
3 *
4 * @section LICENSE
5 *
6 * The MIT License
7 *
8 * @copyright Copyright (c) 2018-2021 TileDB, Inc.
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 * THE SOFTWARE.
27 *
28 * @section DESCRIPTION
29 *
30 This file defines signal handling functionality.
31 */
32
33 #include <atomic>
34 #include <mutex>
35 #include <string>
36
37 #include <signal.h>
38
39 #ifdef _WIN32
40 #include <io.h>
41 #include <windows.h>
42 #else
43 #include <unistd.h>
44 #endif
45
46 #include "tiledb/common/logger.h"
47 #include "tiledb/sm/global_state/signal_handlers.h"
48
49 using namespace tiledb::common;
50
51 namespace tiledb {
52 namespace sm {
53 namespace global_state {
54
55 /* ********************************* */
56 /* Global variables */
57 /* ********************************* */
58
59 /**
60 * Flag set to true from the installed signal handlers. This is a global
61 * variable in the interest of being paranoid about what can be modified safely
62 * from a signal handler.
63 */
64 std::atomic_bool signal_received(false);
65
66 /** Pointer to signal handler installed before ours, if any. */
67 static void (*old_sigint_handler)(int) = nullptr;
68
69 /* ********************************* */
70 /* Platform-neutral functions */
71 /* ********************************* */
72
GetSignalHandlers()73 SignalHandlers& SignalHandlers::GetSignalHandlers() {
74 // This is thread-safe in C++11.
75 static SignalHandlers signalHandlers;
76 return signalHandlers;
77 }
78
signal_received()79 bool SignalHandlers::signal_received() {
80 bool test = true;
81 return tiledb::sm::global_state::signal_received.compare_exchange_weak(
82 test, false);
83 }
84
85 /**
86 * Signal handler function.
87 * @param signum Signal number being handled.
88 */
tiledb_signal_handler(int signum)89 extern "C" void tiledb_signal_handler(int signum) {
90 switch (signum) {
91 case SIGINT: {
92 if (old_sigint_handler != nullptr) {
93 old_sigint_handler(signum);
94 }
95 signal_received = true;
96 break;
97 }
98 }
99 }
100
101 #ifdef _WIN32
102 /* ********************************* */
103 /* Win32 implementations */
104 /* ********************************* */
105
win_ctrl_handler(DWORD dwCtrlType)106 static BOOL WINAPI win_ctrl_handler(DWORD dwCtrlType) {
107 switch (dwCtrlType) {
108 case CTRL_BREAK_EVENT:
109 tiledb_signal_handler(SIGINT);
110 break;
111 }
112 return false;
113 }
114
initialize()115 Status SignalHandlers::initialize() {
116 if (signal(SIGINT, tiledb_signal_handler) == SIG_ERR) {
117 return Status::Error(
118 std::string("Failed to install Win32 SIGINT handler: ") +
119 strerror(errno));
120 }
121
122 // Win32 applications should also handle Ctrl-Break.
123 if (SetConsoleCtrlHandler(win_ctrl_handler, TRUE) == 0) {
124 return Status::Error(std::string("Failed to install Win32 ctrl handler"));
125 }
126 return Status::Ok();
127 }
128
safe_stderr(const char * msg,size_t msg_len)129 void SignalHandlers::safe_stderr(const char* msg, size_t msg_len) {
130 auto retval = _write(2, msg, (unsigned int)msg_len);
131 // Ignore return value.
132 (void)retval;
133 }
134
135 #else
136 /* ********************************* */
137 /* POSIX implementations */
138 /* ********************************* */
139
initialize()140 Status SignalHandlers::initialize() {
141 struct sigaction action, old_action;
142 memset(&action, 0, sizeof(struct sigaction));
143 memset(&old_action, 0, sizeof(struct sigaction));
144
145 // Remember the previous signal handler so we can call it before ours.
146 if (sigaction(SIGINT, NULL, &old_action) != 0) {
147 return Status::Error(
148 std::string("Failed to get old SIGINT handler: ") + strerror(errno));
149 }
150 old_sigint_handler = old_action.sa_handler;
151
152 // Block additional SIGINTs while in the SIGINT handler:
153 sigemptyset(&action.sa_mask);
154 sigaddset(&action.sa_mask, SIGINT);
155 action.sa_flags = 0;
156 action.sa_handler = tiledb_signal_handler;
157 if (sigaction(SIGINT, &action, &old_action) != 0) {
158 return Status::Error(
159 std::string("Failed to install SIGINT handler: ") + strerror(errno));
160 }
161
162 return Status::Ok();
163 }
164
safe_stderr(const char * msg,size_t msg_len)165 void SignalHandlers::safe_stderr(const char* msg, size_t msg_len) {
166 auto retval = write(2, msg, msg_len);
167 // Ignore return value.
168 (void)retval;
169 }
170
171 #endif
172
173 } // namespace global_state
174 } // namespace sm
175 } // namespace tiledb
176