os_result os_signalHandlerEnableExceptionSignals ( void) { os_signalHandler _this = signalHandlerObj; unsigned i, iException; int r; if (isSignallingSafe(0) && _this) { for (i = 0; i < lengthof(exceptions); i++) { const int sig = exceptions[i]; r = os_sigsetDel(&_this->action.sa_mask, sig); if (r < 0) { OS_REPORT(OS_ERROR, "os_signalHandlerInit", 0, "os_sigsetDel(0x%"PA_PRIxADDR", %d) failed, result = %d", (os_address)&_this->action, sig, r); goto err_exceptionSigSetDel; } } for (iException = 0; iException < lengthof(exceptions); iException++) { const int sig = exceptions[iException]; /* For exceptions we always set our own signal handler, since * applications that cause an exception are not in a position * to ignore it. However, we will chain the old handler to our * own. */ r = os_sigactionSet(sig, &_this->action, &old_signalHandler[sig]); if (r < 0) { OS_REPORT(OS_ERROR, "os_signalHandlerInit", 0, "os_sigactionSet(%d, 0x%"PA_PRIxADDR", 0x%"PA_PRIxADDR") failed, result = %d", sig, (os_address)&_this->action, (os_address)&old_signalHandler[sig], r); goto err_exceptionSigSet; } } _this->handleExceptions = OS_TRUE; } return os_resultSuccess; err_exceptionSigSet: while(iException) { const int sig = exceptions[--iException]; r = os_sigactionSet(sig, &old_signalHandler[sig], NULL); if (r < 0) { OS_REPORT(OS_ERROR, "os_signalHandlerInit", 0, "Failed to restore original handler: os_sigactionSet(%d, 0x%"PA_PRIxADDR", NULL) failed, result = %d", sig, (os_address)&old_signalHandler[sig], r); } } err_exceptionSigSetDel: /* No undo needed for os_sigsetDel(...) */ return os_resultFail; }
void os_signalHandlerFree( void) { #if !defined INTEGRITY && !defined VXWORKS_RTP int i; os_ssize_t r; os_signalHandler _this = signalHandlerObj; if (isSignallingSafe(0) && _this) { if (_this->handleExceptions) { for (i=0; i<lengthof(exceptions); i++) { const int sig = exceptions[i]; r = os_sigactionSet(sig, &old_signalHandler[sig], NULL); if (r<0) { OS_REPORT(OS_ERROR, "os_signalHandlerFree", 0, "os_sigactionSet(%d, 0x%"PA_PRIxADDR", NULL) failed, result = %"PA_PRIdSIZE, sig, (os_address)&old_signalHandler[sig], r); assert(OS_FALSE); } } } for (i=0; i<lengthof(quits); i++) { const int sig = quits[i]; r = os_sigactionSet(sig, &old_signalHandler[sig], NULL); if (r<0) { OS_REPORT(OS_ERROR, "os_signalHandlerFree", 0, "os_sigactionSet(%d, 0x%"PA_PRIxADDR", NULL) failed, result = %"PA_PRIdSIZE, sig, (os_address)&old_signalHandler[sig], r); assert(OS_FALSE); } } os__signalHandlerThreadStop(_this); close(_this->pipeIn[0]); close(_this->pipeIn[1]); close(_this->pipeOut[0]); close(_this->pipeOut[1]); os__signalHandlerCallbackDeinit(&_this->callbackInfo); os_free(_this); signalHandlerObj = NULL; } #endif }
os_result os_signalHandlerSetHandler( os_signal signal, os_actionHandler handler) { os_sigset mask; os_result result; os_sigaction action; int r; /* block all signals during handling of a signal */ result = os_sigsetFill(&mask); if (result != os_resultSuccess) { OS_REPORT_2(OS_ERROR, "os_signalHandlerInit", 0, "os_sigsetFill(0x%x) failed, result = %d", &action.sa_mask, result); } else { action = os_sigactionNew(handler, &mask, SA_SIGINFO); } if (result == os_resultSuccess) { r = os_sigsetDel(&action.sa_mask, signal); if (r < 0) { OS_REPORT_3(OS_ERROR, "os_signalHandlerInit", 0, "os_sigsetDel(0x%x, %d) failed, result = %d", &action, signal, r); result = os_resultFail; assert(OS_FALSE); } } if (result == os_resultSuccess) { r = os_sigactionSet(signal, &action, &old_signalHandler[signal]); if (r < 0) { OS_REPORT_4(OS_ERROR, "os_signalHandlerInit", 0, "os_sigactionSet(%d, 0x%x, 0x%x) failed, result = %d", signal, &action, &old_signalHandler[signal], r); result = os_resultFail; assert(OS_FALSE); } } return result; }
void os_signalHandlerFree( void) { #if !defined INTEGRITY && !defined VXWORKS_RTP void *thread_result; int i, r; os_signalHandler _this = signalHandlerObj; struct sig_context info; if (isSignallingSafe(0) && _this) { for (i=0; i<lengthof(exceptions); i++) { const int sig = exceptions[i]; r = os_sigactionSet(sig, &old_signalHandler[sig], NULL); if (r<0) { OS_REPORT_3(OS_ERROR, "os_signalHandlerFree", 0, "os_sigactionSet(%d, 0x%x, NULL) failed, result = %d", sig, &old_signalHandler[sig], r); assert(OS_FALSE); } } memset (&info, 0, sizeof(info)); info.info.si_signo = SIGNAL_THREAD_STOP; r = write(_this->pipeIn[1], &info, sizeof(info)); /* when signalhandler is the exiting thread itself, this can happen when an exit call is done in the signalhandler of the customer * do not call os_threadWaitExit but just let this thread run out of its main function */ if (os_threadIdSelf() != _this->threadId ) { os_threadWaitExit(_this->threadId, &thread_result); } close(_this->pipeIn[0]); close(_this->pipeIn[1]); close(_this->pipeOut[0]); close(_this->pipeOut[1]); os_free(_this); signalHandlerObj = NULL; } #endif }
/** * This is the signal-handler routine that is performed in case of a signal. It * distinguishes: * - synchronous: * - exceptions * - asynchronous: * - exceptions * - quits (termination requests). * * @remarks Do not perform any signal-handling context unsafe operations in this * function. */ static void signalHandler( int sig, siginfo_t *info, void* uap) { struct sig_context sync; struct sig_context sigInfo; sigInfo.info = *info; #ifdef OS_HAS_UCONTEXT_T sigInfo.uc = *(ucontext_t *)uap; #endif /* WARNING: Don't do any async/signalling-unsafe calls here (so refrain from * using OS_REPORT_X and the like). */ if (sigismember(&exceptionsMask, sig) == 1 && sigInfo.info.si_code != SI_USER) { os_sigaction *xo; /* We have an exception (synchronous) here. The assumption is * that exception don't occur in middleware-code, so we can * synchronously call the signalHandlerThread in order to detach user- * layer from all Domains (kernels). */ signalHandlerThreadNotify(sigInfo, &sync); /* BEWARE: This may be an interleaved handling of an exception, so use * sync from now on instead of sigInfo.*/ /* Reset the original signal-handler. Since the exception was * synchronous, running out of this handler will regenerate the signal, * which will then be handled by the original signal-handler. */ xo = &old_signalHandler[sync.info.si_signo]; os_sigactionSet(sync.info.si_signo, xo, NULL); } else { /* Pass signal to signal-handler thread for asynchronous handling */ signalHandlerThreadNotify(sigInfo, NULL); } }
static os_result os_signalHandlerEnableExitSignals ( void) { os_signalHandler _this = signalHandlerObj; unsigned iExit; int r; if (isSignallingSafe(0) && _this) { for (iExit = 0; iExit < quits_len; iExit++) { const int sig = quits[iExit]; /* By passing NULL we only retrieve the currently set handler. If * the signal should be ignored, we don't do anything. Otherwise we * chain the old handler to our own. * man-page of sigaction only states behaviour when new * action is non-NULL, but all known implementations act as * expected. That is: return the currently set signal-hand- * ler (and not the previous, as the man-pages state). * NOTE: Not MT-safe */ r = os_sigactionSet(sig, NULL, &old_signalHandler[sig]); if (r < 0) { OS_REPORT(OS_ERROR, "os_signalHandlerEnableQuitSignals", 0, "Could not retrieve currently set signal-handler for signal %d", sig); goto err_exitSigSet; } if(old_signalHandler[sig].sa_handler != SIG_IGN){ /* We don't know if the oldHandler has been modified in the mean * time, since there is no way to make the signal handler reentrant. * It doesn't make sense to look for modifications now, since a * new modification could be on its way while we are processing * the current modification. * For that reason we will ignore any intermediate modifications * and continue setting our own handler. Processes should therefore * refrain from modifying the signal handler in multiple threads. */ r = os_sigactionSet(sig, &_this->action, NULL); if (r != 0) { OS_REPORT(OS_ERROR, "os_signalHandlerEnableQuitSignals", 0, "os_sigactionSet(%d, 0x%"PA_PRIxADDR", 0x%"PA_PRIxADDR") failed, result = %d", sig, (os_address)&_this->action, (os_address)&old_signalHandler[sig], r); goto err_exitSigSet; } } } } return os_resultSuccess; err_exitSigSet: while(iExit) { const int sig = quits[--iExit]; r = os_sigactionSet(sig, &old_signalHandler[sig], NULL); if (r < 0) { OS_REPORT(OS_ERROR, "os_signalHandlerInit", 0, "Failed to restore original handler: os_sigactionSet(%d, 0x%"PA_PRIxADDR", NULL) failed, result = %d", sig, (os_address)&old_signalHandler[sig], r); } } return os_resultFail; }
static void * signalHandlerThread( void *arg) { ssize_t r; size_t nread; int sig, pid; struct sig_context info; int cont = 1; os_signalHandler _this = (os_signalHandler)arg; if (_this == NULL) return NULL; while (cont) { os_report_stack_open(NULL, 0, NULL, NULL); nread = 0; r = 0; while(nread < sizeof(info) && ((r = read(_this->pipeIn[0], &info + nread, sizeof(info) - nread)) > 0)){ nread += (size_t) r; } if (r < 0) { int errorNr = os_getErrno(); OS_REPORT(OS_ERROR, "os_signalHandlerThread", 0, "read(_this->pipeIn[0]) failed, error %d: %s", errorNr, os_strError(errorNr)); assert(OS_FALSE); sig = SIGNAL_THREAD_STOP; } else { sig = info.info.si_signo; } if(sig != SIGNAL_THREAD_STOP){ if (sig < 1 || sig >= OS_NSIG) { OS_REPORT_NOW(OS_WARNING, "os_signalHandlerThread", 0, info.domainId, "Unexpected signal, value out of range 1-%d: signal = %d", OS_NSIG, sig); } else { if(sigismember(&exceptionsMask, sig) == 1){ if(info.info.si_code == SI_USER || info.info.si_code == SI_QUEUE){ /* Sent by kill or sigqueue, so we can report the origin * of the asynchronous delivery. */ OS_REPORT_NOW(OS_INFO, "signalHandlerThread", 0, info.domainId, "Synchronous exception (signal %d) asynchronously received from PID %d%s, UID %d", sig, info.info.si_pid, info.info.si_pid == getpid() ? " (self)" : "", info.info.si_uid); } else { OS_REPORT_WID(OS_ERROR, "signalHandlerThread", 0, info.domainId, "Exception (signal %d; 0x%" PA_PRIxADDR "; 0x%" PA_PRIxADDR ") %s in process", sig, (sig == SIGSEGV) ? (os_address)info.info.si_addr : (os_address)0, (os_address)pc_from_ucontext(&info.uc), /* Positive values for si_code are reserved for kernel- * generated signals. This report allows to distinguish * an actual kernel-signal and signals within the process * like from pthread_kill(...) for example. */ info.info.si_code > 0 ? "occurred" : "generated"); } /* If an exceptionCallback was set, this should always be invoked. The main * goal is to protect SHM in case of an exception, even if a customer * installed a handler as well. */ os__signalHandlerExceptionCallbackInvoke(&_this->callbackInfo); if(info.info.si_code == SI_USER){ /* Mimic an exception by re-raising it. A random thread received the signal * asynchronously, so when we raise it again another random thread will * receive the signal. */ os_sigaction *xo; /* Reset the original signal-handler. */ xo = &old_signalHandler[info.info.si_signo]; os_sigactionSet(info.info.si_signo, xo, NULL); /* Since the exception was actually asynchronous (issued by an external/soft * source), the original signal-handler will not be called by running * out of the handler. We chain by re-raising, since SIG_DFL or SIG_IGN * cannot be called directly. */ /* OSPL-2762: Instead of re-raising we use kill to make sure the signal isn't delivered to this thread as that would cause a dead lock. */ pid = getpid(); /* Don't think it's possible to not send the signal */ OS_REPORT_NOW (OS_DEBUG, "os_signalHandlerThread", 0, info.domainId, "Invoking kill (signal %d) on PID %d (self)", info.info.si_signo, pid); (void)kill (pid, info.info.si_signo); } else { /* Notify waiting signal handler to unblock. */ r = write(_this->pipeOut[1], &info, sizeof(info)); if (r<0) { int errorNr = os_getErrno(); OS_REPORT_WID(OS_ERROR, "os_signalHandlerThread", 0, info.domainId, "write(_this->pipeOut[1]) failed, error %d: %s", errorNr, os_strError(errorNr)); assert(OS_FALSE); } } } else if (sigismember(&quitsMask, sig) == 1){ os_callbackArg cbarg; OS_REPORT_NOW(OS_INFO, "signalHandlerThread", 0, info.domainId, "Termination request (signal %d) received from PID %d%s, UID %d", sig, (info.info.si_code == SI_USER) ? info.info.si_pid : getpid(), (info.info.si_code != SI_USER || info.info.si_pid == getpid()) ? " (self)" : "", (info.info.si_code == SI_USER) ? info.info.si_uid : getuid()); cbarg.sig = (void*)(os_address)sig; /* Quit-signals which were set to SIG_IGN shouldn't have our signal-handler * set at all. */ assert(old_signalHandler[sig].sa_handler != SIG_IGN); if(old_signalHandler[sig].sa_handler == SIG_DFL){ (void) os__signalHandlerExitRequestCallbackInvoke(&_this->callbackInfo, cbarg); } else { /* Execute the original signal-handler within our safe context. For quit-signals our * own exitRequestCallback is not executed. We don't chain exit-request-handlers. */ if ((old_signalHandler[sig].sa_flags & SA_SIGINFO) == SA_SIGINFO) { #ifdef OS_HAS_UCONTEXT_T old_signalHandler[sig].sa_sigaction(sig, &info.info, &info.uc); #else old_signalHandler[sig].sa_sigaction(sig, &info.info, NULL); #endif } else { old_signalHandler[sig].sa_handler(sig); } } os_signalHandlerDeleteDeregisteredExitRequestCallbacks(&_this->callbackInfo); } /* Else do nothing */ } } else { cont = 0; } os_report_flush((os_report_stack_size() > 0), "os_signalHandler", __FILE__, __LINE__, -1); } return NULL; }
os_result os_signalHandlerFinishExitRequest( os_callbackArg arg) { os_result r = os_resultSuccess; int sig = (int)(os_address)arg.sig; os_sigaction * xo; /* This is a request from the application to round up an (asynchronous) * exit-request. */ if (sig < 1 || sig >= OS_NSIG){ OS_REPORT(OS_WARNING, "os_signalHandlerFinishExitRequest", 0, "Callback-arg contains invalid signal, value out of range 1-%d: arg = %d", OS_NSIG, sig); r = os_resultInvalid; } else if (sigismember(&quitsMask, sig) == 0){ #if OS_NSIG >= 100 #error "Worst-case allocation assumes max. signal of 99, which apparently is not correct" #endif /* We know which signal-number exist, all take at most 2 digits + ", ", * so allocate worst-case 4 * quits_len */ char *expected = os_malloc(quits_len * 4 + 1); if(expected){ unsigned i; int pos; assert(quits_len > 0); pos = sprintf(expected, "%d", quits[0]); for(i = 1; i < quits_len; i++){ pos += sprintf(expected + pos, ", %d", quits[i]); } } OS_REPORT(OS_WARNING, "os_signalHandlerFinishExitRequest", 0, "Unexpected Signal, value out of range: signal = %d. Expected one of %s.", sig, expected ? expected : "the asynchronous exit request signals"); os_free(expected); r = os_resultInvalid; } if(r == os_resultSuccess){ /* We need to restore the original signal-handler and than re-raise the * original signal. */ xo = &old_signalHandler[sig]; if(os_sigactionSet(sig, xo, NULL) != 0){ OS_REPORT(OS_WARNING, "os_signalHandlerFinishExitRequest", 0, "Resetting original signal handler for signal %d failed, sigaction did not return 0.",sig); abort(); /* We were unable to reset the original handler, which is pretty serious. */ } else { os_sigset current_sigset, old_sigset; /* Determine the current mask, and make sure that the signal to be * raised is not blocked (this code is typically executed in the * signalHandlerThread (if callback is implemented synchronously), * which blocks all signals). */ os_sigThreadSetMask(NULL, ¤t_sigset); os_sigsetDel(¤t_sigset, sig); raise(sig); /* Set mask temporarily, this should raise the pending signal set above. */ os_sigThreadSetMask(¤t_sigset, &old_sigset); /* Reset the mask to the original state. If this is the signal- * HandlerThread this is essential (if sig is handled), otherwise just * the proper thing to do. */ os_sigThreadSetMask(&old_sigset, NULL); } } return r; }
/** * This is the signal-handler routine that is performed in case of a signal. It * distinguishes: * - synchronous: * - exceptions * - asynchronous: * - exceptions * - quits (termination requests). * * @remarks Do not perform any signal-handling context unsafe operations in this * function. */ static void signalHandler( int sig, siginfo_t *info, void* uap) { struct sig_context sync; struct sig_context sigInfo; os_signalHandlerCallbackInfo *cbInfo = os__signalHandlerGetCallbackInfo(); /* info can be NULL on Solaris */ if (info == NULL) { /* Pretend that it was an SI_USER signal. */ memset(&sigInfo.info, 0, sizeof(siginfo_t)); sigInfo.info.si_signo = sig; sigInfo.info.si_code = SI_USER; sigInfo.info.si_pid = getpid(); sigInfo.info.si_uid = getuid(); } else { sigInfo.info = *info; } sigInfo.ThreadIdSignalRaised = os_threadIdToInteger(os_threadIdSelf()); sigInfo.domainId = os_reportGetDomain(); #ifdef OS_HAS_UCONTEXT_T sigInfo.uc = *(ucontext_t *)uap; #endif /* WARNING: Don't do any async/signalling-unsafe calls here (so refrain from * using OS_REPORT_X and the like). */ if (sigismember(&exceptionsMask, sig) == 1 && sigInfo.info.si_code != SI_USER){ os_sigaction *xo; if (inSignalHandlerThread()) { /* The signalHandlerThread caught an exception (synchronous) * itself. The fact that the signalHandlerThread caught an * exception means there is a bug in the error handling code. */ const char panicmsg[] = "FATAL ERROR: Synchronously trapped signal in signalHandlerThread\n"; panic(panicmsg, sizeof(panicmsg) - 1); /* This line will not be reached anymore */ } /* Grab the Mutex for the ExceptionHandler stack and hold it during * the processing of the exception. This way you avoid handlers * being added or removed right in between writing the exception * context into the pipe and the signal handler thread reading it * and handling it from the pipe. Also you avoid another signal from * another thread overwriting our context. * To visualize the the duration for this mutex claim, the resulting * handler code has been placed in its own (indented) scope. */ { os_mutexLock(&cbInfo->exceptionMtx); /* Obtain the context of the thread that called the signalHandler. * This information will be needed by the callback invoked by the * signalHandlerThread, to decide what kind of action needs to * be taken. */ os__signalHandlerExceptionGetThreadContextCallbackInvoke(cbInfo); /* We have an exception (synchronous) here. The assumption is * that exception don't occur in middleware-code, so we can * synchronously call the signalHandlerThread in order to detach user- * layer from all Domains (kernels). */ signalHandlerThreadNotify(sigInfo, &sync); os_mutexUnlock(&cbInfo->exceptionMtx); } /* BEWARE: This may be an interleaved handling of an exception, so use * sync from now on instead of sigInfo.*/ /* Reset the original signal-handler. If the exception was synchronous, * running out of this handler will regenerate the signal, which will * then be handled by the original signal-handler. Otherwise it needs * to be re-raised. */ xo = &old_signalHandler[sync.info.si_signo]; os_sigactionSet(sync.info.si_signo, xo, NULL); /* Positive values are reserved for kernel-generated signals, i.e., * actual synchronous hard errors. The rest are 'soft' errors and thus * need to be re-raised. */ if(sigInfo.info.si_code <= 0){ raise(sig); } } else { /* Pass signal to signal-handler thread for asynchronous handling */ os_uint32 index = pa_inc32_nv(&cbInfo->exitRequestInsertionIndex) % EXIT_REQUEST_BUFFER_SIZE; os__signalHandlerExitRequestGetThreadContextCallbackInvoke(cbInfo, index); signalHandlerThreadNotify(sigInfo, NULL); } }
static os_result os_signalHandlerInit( os_signalHandler _this) { os_result result = os_resultSuccess; os_sigaction action; os_sigset block_all_sigset, old_sigset; int i, r; if (_this == NULL) { result = os_resultFail; } _this->exitRequestCallback = (os_signalHandlerExitRequestCallback)0; _this->exceptionCallback = (os_signalHandlerExceptionCallback)0; if(isSignallingSafe(1)) { /* Initialise the exceptionsMask */ sigemptyset(&exceptionsMask); for(i = 0; i < lengthof(exceptions); i++) { sigaddset(&exceptionsMask, exceptions[i]); } /* Initialise the quitsMask */ sigemptyset(&quitsMask); for(i = 0; i < lengthof(quits); i++) { sigaddset(&quitsMask, quits[i]); } /* create signal handling pipes */ if (result == os_resultSuccess) { r = pipe(_this->pipeIn); if (r<0) { OS_REPORT_1(OS_ERROR, "os_signalHandlerInit", 0, "pipe(_this->pipeIn) failed, result = %d", r); result = os_resultFail; } else { r = fcntl(_this->pipeIn[0], F_SETFD, 1); if (r<0) { OS_REPORT_1(OS_WARNING, "os_signalHandlerInit", 0, "fcntl(_this->pipeIn[0]) failed, result = %d", r); assert(OS_FALSE); } r = fcntl(_this->pipeIn[1], F_SETFD, 1); if (r<0) { OS_REPORT_1(OS_WARNING, "os_signalHandlerInit", 0, "fcntl(_this->pipeIn[1]) failed, result = %d", r); assert(OS_FALSE); } } } if (result == os_resultSuccess) { r = pipe(_this->pipeOut); if (r<0) { OS_REPORT_1(OS_ERROR, "os_signalHandlerInit", 1, "pipe(_this->pipeOut) failed, result = %d", r); result = os_resultFail; } else { r = fcntl(_this->pipeOut[0], F_SETFD, 1); if (r<0) { OS_REPORT_1(OS_WARNING, "os_signalHandlerInit", 0, "fcntl(_this->pipeOut[0]) failed, result = %d", r); assert(OS_FALSE); } r = fcntl(_this->pipeOut[1], F_SETFD, 1); if (r<0) { OS_REPORT_1(OS_WARNING, "os_signalHandlerInit", 0, "fcntl(_this->pipeOut[1]) failed, result = %d", r); assert(OS_FALSE); } } } /* Block all signals */ if (result == os_resultSuccess) { result = os_sigsetFill(&block_all_sigset); if (result != os_resultSuccess) { OS_REPORT_1(OS_ERROR, "os_signalHandlerInit", 0, "os_sigsetFill(&block_all_sigset) failed, result = %d", r); assert(OS_FALSE); } else { result = os_sigThreadSetMask(&block_all_sigset, &old_sigset); if (result != os_resultSuccess) { OS_REPORT_3(OS_ERROR, "os_signalHandlerInit", 0, "os_sigThreadSetMask(0x%x, 0x%x) failed, result = %d", &block_all_sigset, &old_sigset, r); assert(OS_FALSE); } } } /* Setup signal handler thread. */ if (result == os_resultSuccess) { os_threadAttr thrAttr; result = os_threadAttrInit(&thrAttr); if (result != os_resultSuccess) { OS_REPORT_2(OS_ERROR, "os_signalHandlerInit", 0, "pthread_attr_init(0x%x) failed, result = %d", &thrAttr, r); assert(OS_FALSE); } else { thrAttr.stackSize = 4*1024*1024; /* 4 MB */ result = os_threadCreate(&_this->threadId, "signalHandler", &thrAttr, signalHandlerThread, (void*)_this); if (result != os_resultSuccess) { OS_REPORT_4(OS_ERROR, "os_signalHandlerInit", 0, "os_threadCreate(0x%x, 0x%x,0x%x,0) failed, result = %d", &_this->threadId, &thrAttr, signalHandlerThread, result); assert(OS_FALSE); } } } /* Reset signal mask to original value. */ if (result == os_resultSuccess) { result = os_sigThreadSetMask(&old_sigset, NULL); if (result != os_resultSuccess) { OS_REPORT_2(OS_ERROR, "os_signalHandlerInit", 0, "os_sigThreadSetMask(0x%x, NULL) failed, result = %d", &old_sigset, r); result = os_resultFail; assert(OS_FALSE); } } /* install signal handlers */ if (result == os_resultSuccess) { os_sigset mask; /* block all signals during handling of a signal */ result = os_sigsetFill(&mask); if (result != os_resultSuccess) { OS_REPORT_2(OS_ERROR, "os_signalHandlerInit", 0, "os_sigsetFill(0x%x) failed, result = %d", &action.sa_mask, result); } else { action = os_sigactionNew(signalHandler, &mask, SA_SIGINFO); } if (result == os_resultSuccess) { for (i=0; i<lengthof(exceptions); i++) { const int sig = exceptions[i]; r = os_sigsetDel(&action.sa_mask, sig); if (r<0) { OS_REPORT_3(OS_ERROR, "os_signalHandlerInit", 0, "os_sigsetDel(0x%x, %d) failed, result = %d", &action, sig, r); result = os_resultFail; assert(OS_FALSE); } } } if (result == os_resultSuccess) { for (i=0; i<lengthof(exceptions); i++) { const int sig = exceptions[i]; /* For exceptions we always set our own signal handler, since * applications that cause an exception are not in a position * to ignore it. However, we will chain the old handler to our * own. */ r = os_sigactionSet(sig, &action, &old_signalHandler[sig]); if (r < 0) { OS_REPORT_4(OS_ERROR, "os_signalHandlerInit", 0, "os_sigactionSet(%d, 0x%x, 0x%x) failed, result = %d", sig, &action, &old_signalHandler[sig], r); result = os_resultFail; assert(OS_FALSE); } } } if (result == os_resultSuccess) { for (i=0; i<lengthof(quits); i++) { const int sig = quits[i]; /* By passing NULL we only retrieve the currently set handler. If * the signal should be ignored, we don't do anything. Otherwise we * chain the old handler to our own. * man-page of sigaction only states behaviour when new * action is non-NULL, but all known implementations act as * expected. That is: return the currently set signal-hand- * ler (and not the previous, as the man-pages state). * NOTE: Not MT-safe */ r = os_sigactionSet(sig, NULL, &old_signalHandler[sig]); if (r == 0) { if(old_signalHandler[sig].sa_handler != SIG_IGN) { /* We don't know if the oldHandler has been modified in the mean * time, since there is no way to make the signal handler reentrant. * It doesn't make sense to look for modifications now, since a * new modification could be on its way while we are processing * the current modification. * For that reason we will ignore any intermediate modifications * and continue setting our own handler. Processes should therefore * refrain from modifying the signal handler in multiple threads. */ r = os_sigactionSet(sig, &action, NULL); if (r != 0) { OS_REPORT_4(OS_ERROR, "os_signalHandlerInit", 0, "os_sigactionSet(%d, 0x%x, 0x%x) failed, result = %d", sig, &action, &old_signalHandler[sig], r); result = os_resultFail; assert(OS_FALSE); } } else { OS_REPORT_1(OS_INFO, "os_signalHandlerThread", 0, "Not installing a signal handler for the already ignored signal %d.", sig); } } else { OS_REPORT_1(OS_ERROR, "os_signalHandlerInit", 0, "Could not retrieve currently set signal-handler for signal %d", sig); result = os_resultFail; assert(OS_FALSE); } } } } } else { result = os_resultSuccess; } return result; }
static void * signalHandlerThread( void *arg) { ssize_t r; size_t nread; int sig, pid; struct sig_context info; int cont = 1; os_signalHandler _this = (os_signalHandler)arg; if (_this == NULL) return NULL; while (cont) { nread = 0; while(nread < sizeof(info) && ((r = read(_this->pipeIn[0], &info + nread, sizeof(info) - nread)) > 0)) { nread += r; } if (r < 0) { int errorNr = errno; OS_REPORT_2(OS_ERROR, "os_signalHandlerThread", 0, "read(_this->pipeIn[0]) failed, error %d: %s", errorNr, strerror(errorNr)); assert(OS_FALSE); sig = SIGNAL_THREAD_STOP; } else { sig = info.info.si_signo; } if(sig != SIGNAL_THREAD_STOP) { if (sig < 1 || sig >= OS_NSIG) { OS_REPORT_2(OS_WARNING, "os_signalHandlerThread", 0, "Unexpected signal, value out of range 1-%d: signal = %d", OS_NSIG, sig); } else { if(sigismember(&exceptionsMask, sig) == 1) { if(info.info.si_code == SI_USER) { OS_REPORT_4(OS_INFO, "signalHandlerThread", 0, "Synchronous exception (signal %d) asynchronously received from PID %d%s, UID %d", sig, info.info.si_pid, info.info.si_pid == getpid() ? " (self)" : "", info.info.si_uid); } else { OS_REPORT_1(OS_INFO, "signalHandlerThread", 0, "Exception (signal %d) occurred in process", sig); } /* If an exceptionCallback was set, this should always be invoked. The main * goal is to protect SHM in case of an exception, even if a customer * installed a handler as well. */ if (_this->exceptionCallback && (_this->exceptionCallback() != os_resultSuccess)) { OS_REPORT(OS_ERROR, "os_signalHandlerThread", 0, "Exception-callback failed"); } if(info.info.si_code == SI_USER) { /* Mimic an exception by re-raising it. A random thread received the signal * asynchronously, so when we raise it again another random thread will * receive the signal. */ os_sigaction *xo; os_sigset ss; /* Reset the original signal-handler. */ xo = &old_signalHandler[info.info.si_signo]; os_sigactionSet(info.info.si_signo, xo, NULL); /* Since the exception was actually asynchronous (issued by an external * source), the original signal-handler will not be called by running * out of the handler. We chain by re-raising, since SIG_DFL or SIG_IGN * cannot be called directly. */ /* OSPL-2762: Instead of re-raising we use kill to make sure the signal isn't delivered to this thread as that would cause a dead lock. */ pid = getpid(); /* Don't think it's possible to not send the signal */ OS_REPORT_2 (OS_DEBUG, "os_signalHandlerThread", 0, "Invoking kill (signal %d) on PID %d (self)", info.info.si_signo, pid); (void)kill (pid, info.info.si_signo); } else { /* Notify waiting signal handler to unblock. */ r = write(_this->pipeOut[1], &info, sizeof(info)); if (r<0) { int errorNr = errno; OS_REPORT_2(OS_ERROR, "os_signalHandlerThread", 0, "write(_this->pipeOut[1]) failed, error %d: %s", errorNr, strerror(errorNr)); assert(OS_FALSE); } } } else if (sigismember(&quitsMask, sig) == 1) { os_callbackArg arg; OS_REPORT_4(OS_INFO, "signalHandlerThread", 0, "Termination request (signal %d) received from PID %d%s, UID %d", sig, (info.info.si_code == SI_USER) ? info.info.si_pid : getpid(), (info.info.si_code != SI_USER || info.info.si_pid == getpid()) ? " (self)" : "", (info.info.si_code == SI_USER) ? info.info.si_uid : getuid()); arg.sig = (void*)(os_address)sig; /* Quit-signals which were set to SIG_IGN shouldn't have our signal-handler * set at all. */ assert(old_signalHandler[sig].sa_handler != SIG_IGN); if(old_signalHandler[sig].sa_handler == SIG_DFL) { if (_this->exitRequestCallback && (_this->exitRequestCallback(arg) != os_resultSuccess)) { OS_REPORT(OS_ERROR, "os_signalHandlerThread", 0, "Exit request-callback failed"); } } else { /* Execute the original signal-handler within our safe context. For quit-signals our * own exitRequestCallback is not executed. We don't chain exit-request-handlers. */ if ((old_signalHandler[sig].sa_flags & SA_SIGINFO) == SA_SIGINFO) { #ifdef OS_HAS_UCONTEXT_T old_signalHandler[sig].sa_sigaction(sig, &info.info, &info.uc); #else old_signalHandler[sig].sa_sigaction(sig, &info.info, NULL); #endif } else { old_signalHandler[sig].sa_handler(sig); } } } /* Else do nothing */ } } else { cont = 0; } } return NULL; }