|  | // | 
|  | // 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 | 
|  |  |