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