// // Copyright 2005 The Android Open Source Project // #define LOG_TAG "SignalHandler" #include "SignalHandler.h" #include <utils/Atomic.h> #include <utils/Debug.h> #include <utils/Log.h> #include <errno.h> #include <sys/wait.h> #include <unistd.h> namespace android { class SignalHandler::ProcessThread : public Thread { public: ProcessThread(SignalHandler& sh) : Thread(false) , mOwner(sh) { } virtual bool threadLoop() { char buffer[32]; read(mOwner.mAvailMsg[0], buffer, sizeof(buffer)); LOGV("Signal command processing thread woke up!"); if (mOwner.mLostCommands) { LOGE("Lost %d signals!", mOwner.mLostCommands); mOwner.mLostCommands = 0; } int cur; while ((cur=mOwner.mCommandBottom) != mOwner.mCommandTop) { if (mOwner.mCommands[cur].filled == 0) { LOGV("Command at %d is not yet filled", cur); break; } LOGV("Processing command at %d, top is %d", cur, mOwner.mCommandTop); processCommand(mOwner.mCommands[cur]); mOwner.mCommands[cur].filled = 0; int next = mOwner.mCommandBottom+1; if (next >= COMMAND_QUEUE_SIZE) { next = 0; } mOwner.mCommandBottom = next; } return true; } void processCommand(const CommandEntry& entry) { switch (entry.signum) { case SIGCHLD: { mOwner.mLock.lock(); ssize_t i = mOwner.mChildHandlers.indexOfKey(entry.info.si_pid); ChildHandler ch; if (i >= 0) { ch = mOwner.mChildHandlers.valueAt(i); mOwner.mChildHandlers.removeItemsAt(i); } mOwner.mLock.unlock(); LOGD("SIGCHLD: pid=%d, handle index=%d", entry.info.si_pid, i); if (i >= 0) { int res = waitpid(entry.info.si_pid, NULL, WNOHANG); LOGW_IF(res == 0, "Received SIGCHLD, but pid %d is not yet stopped", entry.info.si_pid); if (ch.handler) { ch.handler(entry.info.si_pid, ch.userData); } } else { LOGW("Unhandled SIGCHLD for pid %d", entry.info.si_pid); } } break; } } SignalHandler& mOwner; }; Mutex SignalHandler::mInstanceLock; SignalHandler* SignalHandler::mInstance = NULL; status_t SignalHandler::setChildHandler(pid_t childPid, int tag, child_callback_t handler, void* userData) { SignalHandler* const self = getInstance(); self->mLock.lock(); // First make sure this child hasn't already exited. pid_t res = waitpid(childPid, NULL, WNOHANG); if (res != 0) { if (res < 0) { LOGW("setChildHandler waitpid of %d failed: %d (%s)", childPid, res, strerror(errno)); } else { LOGW("setChildHandler waitpid of %d said %d already dead", childPid, res); } // Some kind of error... just handle the exit now. self->mLock.unlock(); if (handler) { handler(childPid, userData); } // Return an error code -- 0 means it already exited. return (status_t)res; } ChildHandler entry; entry.childPid = childPid; entry.tag = tag; entry.handler = handler; entry.userData = userData; // Note: this replaces an existing entry for this pid, if there already // is one. This is the required behavior. LOGD("setChildHandler adding pid %d, tag %d, handler %p, data %p", childPid, tag, handler, userData); self->mChildHandlers.add(childPid, entry); self->mLock.unlock(); return NO_ERROR; } void SignalHandler::killAllChildren(int tag) { SignalHandler* const self = getInstance(); AutoMutex _l (self->mLock); const size_t N = self->mChildHandlers.size(); for (size_t i=0; i<N; i++) { const ChildHandler& ch(self->mChildHandlers.valueAt(i)); if (tag == 0 || ch.tag == tag) { const pid_t pid = ch.childPid; LOGI("Killing child %d (tag %d)\n", pid, ch.tag); kill(pid, SIGKILL); } } } SignalHandler::SignalHandler() : mCommandTop(0) , mCommandBottom(0) , mLostCommands(0) { memset(mCommands, 0, sizeof(mCommands)); int res = pipe(mAvailMsg); LOGE_IF(res != 0, "Unable to create signal handler pipe: %s", strerror(errno)); mProcessThread = new ProcessThread(*this); mProcessThread->run("SignalHandler", PRIORITY_HIGHEST); struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = sigAction; sa.sa_flags = SA_NOCLDSTOP|SA_SIGINFO; sigaction(SIGCHLD, &sa, NULL); } SignalHandler::~SignalHandler() { } SignalHandler* SignalHandler::getInstance() { AutoMutex _l(mInstanceLock); if (mInstance == NULL) { mInstance = new SignalHandler(); } return mInstance; } void SignalHandler::sigAction(int signum, siginfo_t* info, void*) { static const char wakeupMsg[1] = { 0xff }; // If our signal handler is being called, then we know we have // already initialized the SignalHandler class and thus mInstance // is valid. SignalHandler* const self = mInstance; // XXX This is not safe! #if 0 LOGV("Signal %d: signo=%d, errno=%d, code=%d, pid=%d\n", signum, info->si_signo, info->si_errno, info->si_code, info->si_pid); #endif int32_t oldTop, newTop; // Find the next command slot... do { oldTop = self->mCommandTop; newTop = oldTop + 1; if (newTop >= COMMAND_QUEUE_SIZE) { newTop = 0; } if (newTop == self->mCommandBottom) { // The buffer is filled up! Ouch! // XXX This is not safe! #if 0 LOGE("Command buffer overflow! newTop=%d\n", newTop); #endif android_atomic_add(1, &self->mLostCommands); write(self->mAvailMsg[1], wakeupMsg, sizeof(wakeupMsg)); return; } } while(android_atomic_cmpxchg(oldTop, newTop, &(self->mCommandTop))); // Fill in the command data... self->mCommands[oldTop].signum = signum; self->mCommands[oldTop].info = *info; // And now make this command available. self->mCommands[oldTop].filled = 1; // Wake up the processing thread. write(self->mAvailMsg[1], wakeupMsg, sizeof(wakeupMsg)); } }; // namespace android