| /**************************************************************************** |
| * sched/signal/sig_deliver.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <sys/types.h> |
| #include <signal.h> |
| #include <unistd.h> |
| #include <sched.h> |
| #include <string.h> |
| #include <debug.h> |
| |
| #include <nuttx/irq.h> |
| #include <nuttx/arch.h> |
| #include <nuttx/signal.h> |
| |
| #include "signal/signal.h" |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: nxsig_deliver |
| * |
| * Description: |
| * This function is called on the thread of execution of the signal |
| * receiving task. It processes all queued signals then returns. |
| * |
| ****************************************************************************/ |
| |
| void nxsig_deliver(FAR struct tcb_s *stcb) |
| { |
| /* Save the errno. This must be preserved throughout the signal handling |
| * so that the user code final gets the correct errno value (probably |
| * EINTR). |
| */ |
| |
| int saved_errno = get_errno(); |
| int16_t saved_errcode = stcb->errcode; |
| |
| FAR sigq_t *sigq; |
| sigset_t savesigprocmask; |
| sigset_t newsigprocmask; |
| sigset_t tmpset1; |
| sigset_t tmpset2; |
| irqstate_t flags; |
| |
| /* Loop while there are signals to be delivered */ |
| |
| for (; ; ) |
| { |
| /* Test if this task is already handling a signal (we don't permit |
| * nested signals on the same thread). |
| */ |
| |
| flags = enter_critical_section(); |
| if ((stcb->flags & TCB_FLAG_SIGNAL_ACTION) != 0) |
| { |
| /* Yes.. then we must wait for the signal handler to return */ |
| |
| leave_critical_section(flags); |
| break; |
| } |
| |
| /* Remove the signal structure from the head of the sigpendactionq. */ |
| |
| sigq = (FAR sigq_t *)sq_remfirst(&stcb->sigpendactionq); |
| if (sigq == NULL) |
| { |
| /* All queued signal actions have been dispatched */ |
| |
| leave_critical_section(flags); |
| break; |
| } |
| |
| /* Indicate that a signal is being delivered */ |
| |
| stcb->flags |= TCB_FLAG_SIGNAL_ACTION; |
| |
| sinfo("Deliver signal %d to PID %d\n", |
| sigq->info.si_signo, stcb->pid); |
| |
| /* Add the signal structure to the sigpostedq. NOTE: Since signals |
| * are processed one at a time, there should never be more than one |
| * signal in the sigpostedq |
| */ |
| |
| sq_addlast((FAR sq_entry_t *)sigq, &(stcb->sigpostedq)); |
| |
| /* Save a copy of the old sigprocmask and install the new |
| * (temporary) sigprocmask. The new sigprocmask is the union |
| * of the current sigprocmask and the sa_mask for the signal being |
| * delivered plus the signal being delivered. |
| */ |
| |
| savesigprocmask = stcb->sigprocmask; |
| sigorset(&newsigprocmask, &savesigprocmask, &sigq->mask); |
| nxsig_addset(&newsigprocmask, sigq->info.si_signo); |
| stcb->sigprocmask = newsigprocmask; |
| |
| #ifndef CONFIG_BUILD_FLAT |
| /* In the kernel build this has to be handled differently if we are |
| * dispatching to a signal handler in a user-space task or thread; we |
| * have to switch to user-mode before calling the task. |
| */ |
| |
| #ifdef CONFIG_SIG_DEFAULT |
| /* The default signal action handlers, however always reside in the |
| * kernel address space, regardless of configuration. |
| */ |
| |
| if (nxsig_isdefault(stcb, sigq->info.si_signo)) |
| { |
| /* Leave the critical section before calling the handler */ |
| |
| leave_critical_section(flags); |
| (*sigq->action.sighandler)(sigq->info.si_signo, &sigq->info, |
| NULL); |
| } |
| else |
| #endif |
| if ((stcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_KERNEL) |
| { |
| siginfo_t info; |
| |
| /* The sigq_t pointed to by sigq resides in kernel space. So we |
| * cannot pass a reference to sigq->info to the user application. |
| * Instead, we will copy the siginfo_t structure onto the stack. |
| * We are currently executing on the stack of the user thread |
| * (albeit temporarily in kernel mode), so the copy of the |
| * siginfo_t structure will be accessible by the user thread. |
| */ |
| |
| memcpy(&info, &sigq->info, sizeof(siginfo_t)); |
| |
| /* Leave the critical section before calling the handler */ |
| |
| leave_critical_section(flags); |
| up_signal_dispatch(sigq->action.sighandler, sigq->info.si_signo, |
| &info, NULL); |
| } |
| else |
| #endif |
| { |
| /* The kernel thread signal handler is much simpler. */ |
| |
| leave_critical_section(flags); |
| (*sigq->action.sighandler)(sigq->info.si_signo, &sigq->info, |
| NULL); |
| } |
| |
| /* Indicate that a signal has been delivered */ |
| |
| flags = enter_critical_section(); |
| stcb->flags &= ~TCB_FLAG_SIGNAL_ACTION; |
| |
| /* Restore the original sigprocmask. |
| * |
| * What if the signal handler changed the sigprocmask? Try to retain |
| * any such changes here. |
| * |
| * REVISIT: This logic is imperfect. It will fail to detect bits set |
| * in the current sigprocmask that were already set by newsigprocmask. |
| */ |
| |
| nxsig_xorset(&tmpset1, &stcb->sigprocmask, &newsigprocmask); |
| sigandset(&tmpset2, &stcb->sigprocmask, &tmpset1); |
| nxsig_nandset(&tmpset1, &savesigprocmask, &tmpset1); |
| sigorset(&stcb->sigprocmask, &tmpset1, &tmpset2); |
| |
| /* Remove the signal structure from the sigpostedq */ |
| |
| sq_rem((FAR sq_entry_t *)sigq, &(stcb->sigpostedq)); |
| leave_critical_section(flags); |
| |
| /* Now, handle the (rare?) case where (a) a blocked signal was |
| * received while the signal handling executed but (b) restoring the |
| * original sigprocmask will unblock the signal. |
| */ |
| |
| nxsig_unmask_pendingsignal(); |
| |
| /* Then deallocate the signal structure */ |
| |
| nxsig_release_pendingsigaction(sigq); |
| } |
| |
| /* Restore the saved errno value */ |
| |
| set_errno(saved_errno); |
| stcb->errcode = saved_errcode; |
| } |