init: Combine two global sigchld_fd variables into one
Remove the Service::SetSigchldFd() method. Make the Service::GetSigchldFd() create a signalfd for SIGCHLD. This makes it possible to use a SIGCHLD signalfd in unit tests. Change-Id: I0b41caa8f46c79f4d400e49aaba5227fad53c251 Signed-off-by: Bart Van Assche <bvanassche@google.com>
This commit is contained in:
parent
ced62e53ed
commit
97047b54e9
5 changed files with 51 additions and 33 deletions
|
@ -117,7 +117,6 @@ namespace init {
|
|||
|
||||
static int property_triggers_enabled = 0;
|
||||
|
||||
int sigchld_fd = -1;
|
||||
static int sigterm_fd = -1;
|
||||
static int property_fd = -1;
|
||||
|
||||
|
@ -717,7 +716,7 @@ static void HandleSigtermSignal(const signalfd_siginfo& siginfo) {
|
|||
|
||||
static void HandleSignalFd(int signal) {
|
||||
signalfd_siginfo siginfo;
|
||||
const int signal_fd = signal == SIGCHLD ? sigchld_fd : sigterm_fd;
|
||||
const int signal_fd = signal == SIGCHLD ? Service::GetSigchldFd() : sigterm_fd;
|
||||
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
|
||||
if (bytes_read != sizeof(siginfo)) {
|
||||
PLOG(ERROR) << "Failed to read siginfo from signal_fd";
|
||||
|
@ -751,20 +750,24 @@ static void UnblockSignals() {
|
|||
}
|
||||
}
|
||||
|
||||
static Result<void> RegisterSignalFd(Epoll* epoll, int signal, int fd) {
|
||||
return epoll->RegisterHandler(
|
||||
fd, [signal]() { HandleSignalFd(signal); }, EPOLLIN | EPOLLPRI);
|
||||
}
|
||||
|
||||
static Result<int> CreateAndRegisterSignalFd(Epoll* epoll, int signal) {
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, signal);
|
||||
unique_fd signal_fd(signalfd(-1, &mask, SFD_CLOEXEC));
|
||||
if (signal_fd == -1) {
|
||||
return ErrnoError() << "failed to create signalfd for signal " << signal;
|
||||
if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
|
||||
return ErrnoError() << "failed to block signal " << signal;
|
||||
}
|
||||
|
||||
auto result = epoll->RegisterHandler(
|
||||
signal_fd.get(), [signal]() { HandleSignalFd(signal); }, EPOLLIN | EPOLLPRI);
|
||||
if (!result.ok()) {
|
||||
return result.error();
|
||||
unique_fd signal_fd(signalfd(-1, &mask, SFD_CLOEXEC));
|
||||
if (signal_fd.get() < 0) {
|
||||
return ErrnoError() << "failed to create signalfd for signal " << signal;
|
||||
}
|
||||
OR_RETURN(RegisterSignalFd(epoll, signal, signal_fd.get()));
|
||||
|
||||
return signal_fd.release();
|
||||
}
|
||||
|
@ -775,34 +778,18 @@ static void InstallSignalFdHandler(Epoll* epoll) {
|
|||
const struct sigaction act { .sa_flags = SA_NOCLDSTOP, .sa_handler = SIG_DFL };
|
||||
sigaction(SIGCHLD, &act, nullptr);
|
||||
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGCHLD);
|
||||
|
||||
if (!IsRebootCapable()) {
|
||||
// If init does not have the CAP_SYS_BOOT capability, it is running in a container.
|
||||
// In that case, receiving SIGTERM will cause the system to shut down.
|
||||
sigaddset(&mask, SIGTERM);
|
||||
}
|
||||
|
||||
if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
|
||||
PLOG(FATAL) << "failed to block signals";
|
||||
}
|
||||
|
||||
// Register a handler to unblock signals in the child processes.
|
||||
const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);
|
||||
if (result != 0) {
|
||||
LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
|
||||
}
|
||||
|
||||
Result<int> cs_result = CreateAndRegisterSignalFd(epoll, SIGCHLD);
|
||||
Result<void> cs_result = RegisterSignalFd(epoll, SIGCHLD, Service::GetSigchldFd());
|
||||
if (!cs_result.ok()) {
|
||||
PLOG(FATAL) << cs_result.error();
|
||||
}
|
||||
sigchld_fd = cs_result.value();
|
||||
Service::SetSigchldFd(sigchld_fd);
|
||||
|
||||
if (sigismember(&mask, SIGTERM)) {
|
||||
if (!IsRebootCapable()) {
|
||||
Result<int> cs_result = CreateAndRegisterSignalFd(epoll, SIGTERM);
|
||||
if (!cs_result.ok()) {
|
||||
PLOG(FATAL) << cs_result.error();
|
||||
|
|
|
@ -28,8 +28,6 @@
|
|||
namespace android {
|
||||
namespace init {
|
||||
|
||||
extern int sigchld_fd;
|
||||
|
||||
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
|
||||
Parser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list);
|
||||
|
||||
|
|
|
@ -563,7 +563,7 @@ static void StopServices(const std::set<std::string>& services, std::chrono::mil
|
|||
}
|
||||
}
|
||||
if (timeout > 0ms) {
|
||||
WaitToBeReaped(sigchld_fd, pids, timeout);
|
||||
WaitToBeReaped(Service::GetSigchldFd(), pids, timeout);
|
||||
} else {
|
||||
// Even if we don't to wait for services to stop, we still optimistically reap zombies.
|
||||
ReapAnyOutstandingChildren();
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <cutils/sockets.h>
|
||||
#include <processgroup/processgroup.h>
|
||||
#include <selinux/selinux.h>
|
||||
#include <sys/signalfd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -68,6 +69,7 @@ using android::base::make_scope_guard;
|
|||
using android::base::SetProperty;
|
||||
using android::base::StartsWith;
|
||||
using android::base::StringPrintf;
|
||||
using android::base::unique_fd;
|
||||
using android::base::WriteStringToFile;
|
||||
|
||||
namespace android {
|
||||
|
@ -136,7 +138,6 @@ static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigsto
|
|||
|
||||
unsigned long Service::next_start_order_ = 1;
|
||||
bool Service::is_exec_service_running_ = false;
|
||||
int Service::sigchld_fd_ = -1;
|
||||
|
||||
Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
|
||||
const std::string& filename, const std::vector<std::string>& args)
|
||||
|
@ -792,6 +793,35 @@ void Service::SetMountNamespace() {
|
|||
mount_namespace_ = IsDefaultMountNamespaceReady() ? NS_DEFAULT : NS_BOOTSTRAP;
|
||||
}
|
||||
|
||||
static int ThreadCount() {
|
||||
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/proc/self/task"), closedir);
|
||||
if (!dir) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
dirent* entry;
|
||||
while ((entry = readdir(dir.get())) != nullptr) {
|
||||
if (entry->d_name[0] != '.') {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Must be called BEFORE any threads are created. See also the sigprocmask() man page.
|
||||
unique_fd Service::CreateSigchldFd() {
|
||||
CHECK_EQ(ThreadCount(), 1);
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGCHLD);
|
||||
if (sigprocmask(SIG_BLOCK, &mask, nullptr) < 0) {
|
||||
PLOG(FATAL) << "Failed to block SIGCHLD";
|
||||
}
|
||||
|
||||
return unique_fd(signalfd(-1, &mask, SFD_CLOEXEC));
|
||||
}
|
||||
|
||||
void Service::SetStartedInFirstStage(pid_t pid) {
|
||||
LOG(INFO) << "adding first-stage service '" << name_ << "'...";
|
||||
|
||||
|
|
|
@ -156,7 +156,10 @@ class Service {
|
|||
const Subcontext* subcontext() const { return subcontext_; }
|
||||
const std::string& filename() const { return filename_; }
|
||||
void set_filename(const std::string& name) { filename_ = name; }
|
||||
static void SetSigchldFd(int sigchld_fd) { sigchld_fd_ = sigchld_fd; }
|
||||
static int GetSigchldFd() {
|
||||
static int sigchld_fd = CreateSigchldFd().release();
|
||||
return sigchld_fd;
|
||||
}
|
||||
|
||||
private:
|
||||
void NotifyStateChange(const std::string& new_state) const;
|
||||
|
@ -169,10 +172,10 @@ class Service {
|
|||
void RunService(const std::vector<Descriptor>& descriptors, InterprocessFifo cgroups_activated,
|
||||
InterprocessFifo setsid_finished);
|
||||
void SetMountNamespace();
|
||||
static ::android::base::unique_fd CreateSigchldFd();
|
||||
|
||||
static unsigned long next_start_order_;
|
||||
static bool is_exec_service_running_;
|
||||
static int sigchld_fd_;
|
||||
|
||||
const std::string name_;
|
||||
std::set<std::string> classnames_;
|
||||
|
|
Loading…
Reference in a new issue