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