99#include < csignal>
1010#include < cstring>
1111#include < iostream>
12- #include < optional>
1312#include < variant>
1413#include < vector>
1514
1615#include < sys/wait.h>
1716
1817// no need to block these signals
19- constexpr std::array<int , 11 > unblock_signals{ SIGABRT , SIGBUS , SIGFPE , SIGILL , SIGSEGV , SIGSYS ,
20- SIGTRAP , SIGXCPU , SIGXFSZ , SIGTTIN , SIGTTOU };
18+ constexpr std::array<int , 13 > unblock_signals{ SIGABRT , SIGBUS , SIGFPE , SIGILL , SIGSEGV ,
19+ SIGSYS , SIGTRAP , SIGXCPU , SIGXFSZ , SIGTTIN ,
20+ SIGTTOU , SIGINT , SIGTERM };
2121
2222struct sig_conf
2323{
2424 sigset_t cur_set{};
2525 sigset_t old_set{};
2626 struct sigaction ttin_action{};
2727 struct sigaction ttou_action{};
28+ struct sigaction int_action{};
29+ struct sigaction term_action{};
2830};
2931
32+ sig_atomic_t received_signal{ 0 };
33+
3034namespace {
3135
3236void print_sys_error (std::string_view msg) noexcept
@@ -36,7 +40,14 @@ void print_sys_error(std::string_view msg) noexcept
3640
3741void print_info (std::string_view msg) noexcept
3842{
39- std::cout << msg << std::endl;
43+ std::cerr << msg << std::endl;
44+ }
45+
46+ // ATTENTION: there is a potential risk that the signal will be overridden if multiple signals are
47+ // received at the same time.
48+ void sig_forward (int signo) noexcept
49+ {
50+ received_signal = signo;
4051}
4152
4253using sig_result = std::variant<int , sig_conf>;
@@ -49,6 +60,7 @@ sig_result handle_signals() noexcept
4960 ::sigdelset (&conf.cur_set, signal);
5061 }
5162
63+ // ignore the rest of the signals
5264 auto ret = ::sigprocmask (SIG_SETMASK , &conf.cur_set , &conf.old_set );
5365 if (ret == -1 ) {
5466 print_sys_error (" Failed to set signal mask" );
@@ -71,6 +83,21 @@ sig_result handle_signals() noexcept
7183 return -1 ;
7284 }
7385
86+ // we only forward SIGINT, SIGTERM to the child process
87+ struct sigaction forward_action{};
88+ forward_action.sa_handler = sig_forward;
89+ forward_action.sa_flags = 0 ;
90+
91+ if (::sigaction (SIGINT , &forward_action, &conf.int_action ) == -1 ) {
92+ print_sys_error (" Failed to forward SIGINT" );
93+ return -1 ;
94+ }
95+
96+ if (::sigaction (SIGTERM , &forward_action, &conf.term_action ) == -1 ) {
97+ print_sys_error (" Failed to forward SIGTERM" );
98+ return -1 ;
99+ }
100+
74101 return conf;
75102}
76103
@@ -124,32 +151,28 @@ pid_t run(std::vector<const char *> args, const sig_conf &conf) noexcept
124151 return -1 ;
125152 }
126153
154+ ret = ::sigaction (SIGINT , &conf.int_action , nullptr );
155+ if (ret == -1 ) {
156+ print_sys_error (" Failed to restore SIGINT action" );
157+ return -1 ;
158+ }
159+
160+ ret = ::sigaction (SIGTERM , &conf.term_action , nullptr );
161+ if (ret == -1 ) {
162+ print_sys_error (" Failed to restore SIGTERM action" );
163+ return -1 ;
164+ }
165+
127166 args.emplace_back (nullptr );
128167
129168 ::execv (args[0 ], const_cast <char *const *>(args.data()));
130169 print_sys_error (" Failed to exec" );
131- return - 1 ;
170+ ::_exit ( EXIT_FAILURE ) ;
132171 }
133172
134173 return pid;
135174}
136175
137- std::optional<int > handle_exited_child (pid_t child) noexcept
138- {
139- int status{};
140- while (true ) {
141- auto ret = ::waitpid (-1 , &status, WNOHANG );
142- if (ret == 0 ) {
143- break ;
144- }
145-
146- if (ret == child) {
147- return status;
148- }
149- }
150-
151- return std::nullopt ;
152- }
153176} // namespace
154177
155178int main (int argc, char *argv[])
@@ -175,48 +198,42 @@ int main(int argc, char *argv[])
175198
176199 auto child = run (args, conf);
177200 if (child == -1 ) {
178- return -1 ;
179- }
180-
181- auto sigfd = ::signalfd (-1 , &conf.cur_set , 0 );
182- if (sigfd == -1 ) {
183- print_sys_error (" Failed to create signalfd" );
201+ print_info (" Failed to run child process" );
184202 return -1 ;
185203 }
186204
187205 int child_status{};
188- signalfd_siginfo info{};
189206 while (true ) {
190- std::memset (&info, 0 , sizeof (info));
191- auto ret = ::read (sigfd, &info, sizeof (info));
192- if (ret == -1 ) {
193- print_sys_error (" Failed to read from signalfd" );
194- return -1 ;
207+ ret = waitpid (-1 , &child_status, 0 );
208+ if (ret != -1 ) {
209+ if (ret == child) {
210+ // if child process already exited, we will send the signal to all processes in this
211+ // pid namespace
212+ child = -1 ;
213+ }
214+
215+ continue ;
195216 }
196217
197- if (info. ssi_signo != SIGCHLD ) {
198- ret = ::kill (child, info. ssi_signo );
218+ if (errno == EINTR ) {
219+ ret = ::kill (child, received_signal );
199220 if (ret == -1 ) {
200- auto msg = std::string (" Failed to forward signal " ) + ::strsignal (info. ssi_signo );
221+ auto msg = std::string (" Failed to forward signal " ) + ::strsignal (received_signal );
201222 print_sys_error (msg);
202223 }
203224 }
204225
205- auto child_exited = handle_exited_child (child);
206- if (child_exited) {
207- child_status = *child_exited;
226+ if (errno == ECHILD ) {
208227 break ;
209228 }
210229 }
211230
212- ::close (sigfd);
213-
214231 if (WIFEXITED (child_status)) {
215- print_info (" Child exited with status " + std::to_string (WEXITSTATUS (child_status)));
232+ print_info (" Last child exited with status " + std::to_string (WEXITSTATUS (child_status)));
216233 } else if (WIFSIGNALED (child_status)) {
217- print_info (" Child exited with signal " + std::to_string (WTERMSIG (child_status)));
234+ print_info (" Last child exited with signal " + std::to_string (WTERMSIG (child_status)));
218235 } else {
219- print_info (" Child exited with unknown status" );
236+ print_info (" Last child exited with unknown status" );
220237 }
221238
222239 return 0 ;
0 commit comments