Imported Upstream version 4.0
[platform/upstream/ccache.git] / src / SignalHandler.cpp
1 // Copyright (C) 2020 Joel Rosdahl and other contributors
2 //
3 // See doc/AUTHORS.adoc for a complete list of contributors.
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by the Free
7 // Software Foundation; either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but WITHOUT
11 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 // more details.
14 //
15 // You should have received a copy of the GNU General Public License along with
16 // this program; if not, write to the Free Software Foundation, Inc., 51
17 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19 #include "SignalHandler.hpp"
20
21 #include "assertions.hpp"
22
23 #ifndef _WIN32
24
25 #  include "Context.hpp"
26
27 namespace {
28
29 SignalHandler* g_the_signal_handler = nullptr;
30 sigset_t g_fatal_signal_set;
31
32 void
33 register_signal_handler(int signum)
34 {
35   struct sigaction act;
36   memset(&act, 0, sizeof(act));
37   act.sa_handler = SignalHandler::on_signal;
38   act.sa_mask = g_fatal_signal_set;
39 #  ifdef SA_RESTART
40   act.sa_flags = SA_RESTART;
41 #  endif
42   sigaction(signum, &act, nullptr);
43 }
44
45 } // namespace
46
47 SignalHandler::SignalHandler(Context& ctx) : m_ctx(ctx)
48 {
49   ASSERT(!g_the_signal_handler);
50   g_the_signal_handler = this;
51
52   sigemptyset(&g_fatal_signal_set);
53   sigaddset(&g_fatal_signal_set, SIGINT);
54   sigaddset(&g_fatal_signal_set, SIGTERM);
55 #  ifdef SIGHUP
56   sigaddset(&g_fatal_signal_set, SIGHUP);
57 #  endif
58 #  ifdef SIGQUIT
59   sigaddset(&g_fatal_signal_set, SIGQUIT);
60 #  endif
61
62   register_signal_handler(SIGINT);
63   register_signal_handler(SIGTERM);
64 #  ifdef SIGHUP
65   register_signal_handler(SIGHUP);
66 #  endif
67 #  ifdef SIGQUIT
68   register_signal_handler(SIGQUIT);
69 #  endif
70 }
71
72 SignalHandler::~SignalHandler()
73 {
74   ASSERT(g_the_signal_handler);
75   g_the_signal_handler = nullptr;
76 }
77
78 void
79 SignalHandler::on_signal(int signum)
80 {
81   ASSERT(g_the_signal_handler);
82   Context& ctx = g_the_signal_handler->m_ctx;
83
84   // Unregister handler for this signal so that we can send the signal to
85   // ourselves at the end of the handler.
86   signal(signum, SIG_DFL);
87
88   // If ccache was killed explicitly, then bring the compiler subprocess (if
89   // any) with us as well.
90   if (signum == SIGTERM && ctx.compiler_pid != 0
91       && waitpid(ctx.compiler_pid, nullptr, WNOHANG) == 0) {
92     kill(ctx.compiler_pid, signum);
93   }
94
95   ctx.unlink_pending_tmp_files_signal_safe();
96
97   if (ctx.compiler_pid != 0) {
98     // Wait for compiler subprocess to exit before we snuff it.
99     waitpid(ctx.compiler_pid, nullptr, 0);
100   }
101
102   // Resend signal to ourselves to exit properly after returning from the
103   // handler.
104   kill(getpid(), signum);
105 }
106
107 #else // !_WIN32
108
109 SignalHandler::SignalHandler(Context& ctx) : m_ctx(ctx)
110 {
111 }
112
113 SignalHandler::~SignalHandler()
114 {
115 }
116
117 #endif // !_WIN32
118
119 void
120 SignalHandler::block_signals()
121 {
122 #ifndef _WIN32
123   sigprocmask(SIG_BLOCK, &g_fatal_signal_set, nullptr);
124 #endif
125 }
126
127 void
128 SignalHandler::unblock_signals()
129 {
130 #ifndef _WIN32
131   sigset_t empty;
132   sigemptyset(&empty);
133   sigprocmask(SIG_SETMASK, &empty, nullptr);
134 #endif
135 }
136
137 SignalHandlerBlocker::SignalHandlerBlocker()
138 {
139   SignalHandler::block_signals();
140 }
141
142 SignalHandlerBlocker::~SignalHandlerBlocker()
143 {
144   SignalHandler::unblock_signals();
145 }