2
* Copyright (C) 2013 Canonical Ltd
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License version 3 as
6
* published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Authored by: Michi Henning <michi.henning@canonical.com>
19
#include <SignalThread.h>
21
#include <unity/UnityExceptions.h>
32
using namespace unity;
34
namespace scoperegistry
37
SignalThread::SignalThread() :
40
// Block signals in the caller, so they are delivered to the thread we are about to create.
42
sigaddset(&sigs_, SIGCHLD);
43
sigaddset(&sigs_, SIGINT);
44
sigaddset(&sigs_, SIGHUP);
45
sigaddset(&sigs_, SIGTERM);
46
sigaddset(&sigs_, SIGUSR1);
47
int err = pthread_sigmask(SIG_BLOCK, &sigs_, nullptr);
50
throw SyscallException("pthread_sigmask failed", err);
53
// Make ourselves a process group leader.
56
// Run a signal handling thread that waits for any of the above signals.
57
handler_thread_ = thread([this]{ this->wait_for_sigs(); });
60
SignalThread::~SignalThread() noexcept
63
lock_guard<mutex> lock(mutex_);
66
kill(getpid(), SIGUSR1);
69
handler_thread_.join();
72
// Add the pid and supplied command line to the table of children.
74
void SignalThread::add_child(pid_t pid, char const* argv[])
76
lock_guard<mutex> lock(mutex_);
79
return; // We've been told to go away already
82
for (int i = 0; argv[i] != nullptr; ++i)
90
children_[pid] = args;
93
// Unblock the signals we are handling. This allows the child to reset the signals
94
// to their defaults before calling exec().
96
void SignalThread::reset_sigs()
98
int err = pthread_sigmask(SIG_UNBLOCK, &sigs_, nullptr);
101
throw SyscallException("pthread_sigmask failed", err);
105
// Wait for signals. For SIGCHLD, we check exit status and report if a child terminated abnormally.
106
// The normal termination signals (SIGINT, SIGHUP, and SIGTERM) are sent to the children.
107
// SIGUSR1 causes the thread to return without touching any of its children.
109
void SignalThread::wait_for_sigs()
114
int err = sigwait(&sigs_, &signo);
117
cerr << "scoperegistry: sigwait failed: " << strerror(err) << endl;
126
// Multiple children exiting can result in only a single SIGCHLD being received,
127
// so we loop until there are no more exited children.
129
pid_t pid = waitpid(-1, &status, WNOHANG);
134
cerr << "scoperegistry: wait() failed: " << strerror(errno) << endl;
137
continue; // Ignore stray SIGCHLD signals
141
break; // No more exited children remain.
144
lock_guard<mutex> lock(mutex_);
145
auto it = children_.find(pid);
146
if (it == children_.end())
148
cerr << "scoperegistry: ignoring unexpected child termination, pid = " << pid << endl;
151
if (WIFEXITED(status))
153
if (WEXITSTATUS(status) != 0)
155
cerr << "scoperegistry: process " << pid << " exited with status " << WEXITSTATUS(status)
156
<< ", command line: " << it->second << endl;
161
cerr << "scoperegistry: process " << pid << " terminated by signal " << WTERMSIG(status)
162
<< ", command line: " << it->second << endl;
172
// Send the signal to all the children.
173
lock_guard<mutex> lock(mutex_);
174
for (auto pair : children_)
176
kill(pair.first, signo);
179
// TODO: for now, until the callback is added, just exit
185
cerr << "signal thread: Got SIGUSR1" << endl;
186
// TODO: should invoke a callback here that then can shut down the run time.
187
return; // Go away without doing anything to the children.
197
} // namespace scoperegistry