| /**************************************************************************** |
| * sched/signal/sig_action.c |
| * |
| * 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 <stdint.h> |
| #include <stdbool.h> |
| #include <signal.h> |
| #include <sched.h> |
| #include <assert.h> |
| #include <errno.h> |
| |
| #include <nuttx/irq.h> |
| #include <nuttx/queue.h> |
| #include <nuttx/signal.h> |
| #include <nuttx/spinlock.h> |
| |
| #include "sched/sched.h" |
| #include "group/group.h" |
| #include "signal/signal.h" |
| |
| /**************************************************************************** |
| * Preprocessor definitions |
| ****************************************************************************/ |
| |
| /* judges if a sigaction instance is a preallocated one */ |
| |
| #if CONFIG_SIG_PREALLOC_ACTIONS > 0 |
| # define IS_PREALLOC_ACTION(x) ( \ |
| (uintptr_t)(x) >= (uintptr_t)g_sigactions && \ |
| (uintptr_t)(x) < ((uintptr_t)g_sigactions) + sizeof(g_sigactions)) |
| #else |
| # define IS_PREALLOC_ACTION(x) false |
| #endif |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static spinlock_t g_sigaction_spin; |
| |
| #if CONFIG_SIG_PREALLOC_ACTIONS > 0 |
| static sigactq_t g_sigactions[CONFIG_SIG_PREALLOC_ACTIONS]; |
| static bool g_sigactions_used = false; |
| #endif |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: nxsig_alloc_actionblock |
| * |
| * Description: |
| * Allocate a block of signal actions and place them |
| * on the free list. |
| * |
| ****************************************************************************/ |
| |
| static void nxsig_alloc_actionblock(void) |
| { |
| FAR sigactq_t *sigact; |
| irqstate_t flags; |
| int i; |
| |
| /* Use pre-allocated instances only once */ |
| |
| #if CONFIG_SIG_PREALLOC_ACTIONS > 0 |
| flags = spin_lock_irqsave(&g_sigaction_spin); |
| if (!g_sigactions_used) |
| { |
| for (i = 0; i < CONFIG_SIG_PREALLOC_ACTIONS; i++) |
| { |
| sq_addlast((FAR sq_entry_t *)(g_sigactions + i), &g_sigfreeaction); |
| } |
| |
| g_sigactions_used = true; |
| } |
| |
| spin_unlock_irqrestore(&g_sigaction_spin, flags); |
| #endif |
| |
| /* Allocate a block of signal actions */ |
| |
| sigact = kmm_malloc((sizeof(sigactq_t)) * CONFIG_SIG_ALLOC_ACTIONS); |
| if (sigact != NULL) |
| { |
| flags = spin_lock_irqsave(&g_sigaction_spin); |
| |
| for (i = 0; i < CONFIG_SIG_ALLOC_ACTIONS; i++) |
| { |
| sq_addlast((FAR sq_entry_t *)sigact++, &g_sigfreeaction); |
| } |
| |
| spin_unlock_irqrestore(&g_sigaction_spin, flags); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: nxsig_alloc_action |
| * |
| * Description: |
| * Allocate a new element for a sigaction queue |
| * |
| ****************************************************************************/ |
| |
| static FAR sigactq_t *nxsig_alloc_action(void) |
| { |
| FAR sigactq_t *sigact; |
| irqstate_t flags; |
| |
| /* Try to get the signal action structure from the free list */ |
| |
| flags = spin_lock_irqsave(&g_sigaction_spin); |
| sigact = (FAR sigactq_t *)sq_remfirst(&g_sigfreeaction); |
| spin_unlock_irqrestore(&g_sigaction_spin, flags); |
| |
| /* Check if we got one via loop as not in critical section now */ |
| |
| while (!sigact) |
| { |
| /* Add another block of signal actions to the list */ |
| |
| nxsig_alloc_actionblock(); |
| |
| /* And try again */ |
| |
| flags = spin_lock_irqsave(&g_sigaction_spin); |
| sigact = (FAR sigactq_t *)sq_remfirst(&g_sigfreeaction); |
| spin_unlock_irqrestore(&g_sigaction_spin, flags); |
| } |
| |
| return sigact; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: nxsig_action and sigaction |
| * |
| * Description: |
| * This function allows the calling process to examine and/or specify the |
| * action to be associated with a specific signal. |
| * |
| * The structure sigaction, used to describe an action to be taken, is |
| * defined to include the following members: |
| * |
| * - sa_u.sa_handler: Pointer to a signal-catching function |
| * - sa_u.sa_sigaction: Alternative form of the signal-catching function |
| * - sa_mask: An additional set of signals to be blocked during execution |
| * of a signal catching function |
| * - sa_flags. Special flags to affect the behavior of a signal. |
| * |
| * If the argument 'act' is not NULL, it points to a structure specifying |
| * the action to be associated with the specified signal. If the argument |
| * 'oact' is not NULL, the action previously associated with the signal |
| * is stored in the location pointed to by the argument 'oact.' |
| * |
| * When a signal is caught by a signal-catching function installed by |
| * sigaction() function, a new signal mask is calculated and installed for |
| * the duration of the signal-catching function. This mask is formed by |
| * taking the union of the current signal mask and the value of the |
| * sa_mask for the signal being delivered and then including the signal |
| * being delivered. If and when the user's signal handler returns, the |
| * original signal mask is restored. |
| * |
| * Once an action is installed for a specific signal, it remains installed |
| * until another action is explicitly requested by another call to |
| * sigaction(). |
| * |
| * nxsig_action() is an internal version of sigaction that adds an |
| * additional parameter, force, that is used to set default signal actions |
| * (which may not normally be settable). nxsig_action() does not alter the |
| * errno variable. |
| * |
| * Input Parameters: |
| * sig - Signal of interest |
| * act - Location of new handler |
| * oact - Location to store only handler |
| * force - Force setup of the signal handler, even if it cannot normally |
| * be caught or ignored (nxsig_action only) |
| * |
| * Returned Value: |
| * nxsig_action: |
| * Zero (OK) is returned on success; a negated errno value is returned |
| * on failure |
| * sigaction: |
| * Zero (OK) is returned on success; -1 (ERROR) is returned on any |
| * failure if the signal number is invalid with the errno set |
| * appropriately |
| * |
| * Assumptions: |
| * |
| * POSIX Compatibility: |
| * - If CONFIG_SIG_DEFAULT is not defined, then there are no default actions |
| * so the special value SIG_DFL is treated like SIG_IGN. |
| * - All sa_flags in struct sigaction of act input are ignored (all |
| * treated like SA_SIGINFO). The one exception is if |
| * CONFIG_SCHED_CHILD_STATUS is defined; then SA_NOCLDWAIT is supported but |
| * only for SIGCHLD |
| * |
| ****************************************************************************/ |
| |
| int nxsig_action(int signo, FAR const struct sigaction *act, |
| FAR struct sigaction *oact, bool force) |
| { |
| FAR struct tcb_s *rtcb = this_task(); |
| FAR struct task_group_s *group; |
| FAR sigactq_t *sigact; |
| _sa_handler_t handler; |
| |
| /* Since sigactions can only be installed from the running thread of |
| * execution, no special precautions should be necessary. |
| */ |
| |
| DEBUGASSERT(rtcb != NULL && rtcb->group != NULL); |
| group = rtcb->group; |
| |
| /* Verify the signal number */ |
| |
| if (!GOOD_SIGNO(signo)) |
| { |
| return -EINVAL; |
| } |
| |
| #ifdef CONFIG_SIG_DEFAULT |
| /* Check if the user is trying to catch or ignore a signal that cannot be |
| * caught or ignored. |
| */ |
| |
| if (act != NULL && !force && act->sa_handler != SIG_DFL && |
| !nxsig_iscatchable(signo)) |
| { |
| return -EINVAL; |
| } |
| #endif |
| |
| /* Find the signal in the signal action queue */ |
| |
| sigact = nxsig_find_action(group, signo); |
| |
| /* Return the old sigaction value if so requested */ |
| |
| if (oact != NULL) |
| { |
| #ifdef CONFIG_SIG_DEFAULT |
| if (nxsig_isdefault(rtcb, signo)) |
| { |
| /* Return SIG_DFL if the default signal is attached */ |
| |
| oact->sa_handler = SIG_DFL; |
| oact->sa_flags = SA_SIGINFO; |
| sigemptyset(&oact->sa_mask); |
| } |
| else |
| #endif |
| if (sigact) |
| { |
| /* Return the old signal action */ |
| |
| oact->sa_handler = sigact->act.sa_handler; |
| oact->sa_mask = sigact->act.sa_mask; |
| oact->sa_flags = sigact->act.sa_flags; |
| } |
| else |
| { |
| /* There isn't an old value */ |
| |
| oact->sa_handler = NULL; |
| oact->sa_flags = 0; |
| sigemptyset(&oact->sa_mask); |
| } |
| } |
| |
| /* If the argument act is a null pointer, signal handling is unchanged; |
| * thus, the call can be used to inquire about the current handling of |
| * a given signal. |
| */ |
| |
| if (act == NULL) |
| { |
| return OK; |
| } |
| |
| #if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) |
| /* Handle a special case. Retention of child status can be suppressed |
| * if signo == SIGCHLD and sa_flags == SA_NOCLDWAIT. |
| * |
| * POSIX.1 leaves it unspecified whether a SIGCHLD signal is generated |
| * when a child process terminates. In NuttX, a SIGCHLD signal is |
| * generated in this case; but in some other implementations, it may not |
| * be. |
| */ |
| |
| if (signo == SIGCHLD && (act->sa_flags & SA_NOCLDWAIT) != 0) |
| { |
| irqstate_t flags; |
| |
| /* We do require a critical section to muck with the TCB values that |
| * can be modified by the child thread. |
| */ |
| |
| flags = enter_critical_section(); |
| |
| /* Mark that status should be not be retained */ |
| |
| rtcb->group->tg_flags |= GROUP_FLAG_NOCLDWAIT; |
| |
| /* Free all pending exit status */ |
| |
| group_remove_children(rtcb->group); |
| leave_critical_section(flags); |
| } |
| #endif |
| |
| handler = act->sa_handler; |
| |
| #ifdef CONFIG_SIG_DEFAULT |
| /* If the caller is setting the handler to SIG_DFL, then we need to |
| * replace this with the correct, internal default signal action handler. |
| */ |
| |
| if (handler == SIG_DFL) |
| { |
| /* nxsig_default() may returned SIG_IGN */ |
| |
| handler = nxsig_default(rtcb, signo, true); |
| } |
| else |
| { |
| /* We will be replacing the default action (or ignoring it) */ |
| |
| nxsig_default(rtcb, signo, false); |
| } |
| #endif |
| |
| /* Handle the case where no sigaction is supplied (SIG_IGN) */ |
| |
| if (handler == SIG_IGN) |
| { |
| /* Do we still have a sigaction container from the previous setting? */ |
| |
| if (sigact) |
| { |
| /* Yes.. Remove it from signal action queue */ |
| |
| sq_rem((FAR sq_entry_t *)sigact, &group->tg_sigactionq); |
| |
| /* And deallocate it */ |
| |
| nxsig_release_action(sigact); |
| } |
| } |
| |
| /* A sigaction has been supplied */ |
| |
| else |
| { |
| /* Do we still have a sigaction container from the previous setting? |
| * If so, then re-use for the new signal action. |
| */ |
| |
| if (sigact == NULL) |
| { |
| /* No.. Then we need to allocate one for the new action. */ |
| |
| sigact = nxsig_alloc_action(); |
| |
| /* An error has occurred if we could not allocate the sigaction */ |
| |
| if (!sigact) |
| { |
| return -ENOMEM; |
| } |
| |
| /* Put the signal number in the queue entry */ |
| |
| sigact->signo = (uint8_t)signo; |
| |
| /* Add the new sigaction to signal action queue */ |
| |
| sq_addlast((FAR sq_entry_t *)sigact, &group->tg_sigactionq); |
| } |
| |
| /* Set the new sigaction */ |
| |
| sigact->act.sa_handler = handler; |
| sigact->act.sa_mask = act->sa_mask; |
| sigact->act.sa_flags = act->sa_flags; |
| sigact->act.sa_user = act->sa_user; |
| } |
| |
| return OK; |
| } |
| |
| int sigaction(int signo, FAR const struct sigaction *act, |
| FAR struct sigaction *oact) |
| { |
| int ret; |
| |
| /* nxsig_action() does all of the work */ |
| |
| ret = nxsig_action(signo, act, oact, false); |
| if (ret < 0) |
| { |
| set_errno(-ret); |
| return ERROR; |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: nxsig_release_action |
| * |
| * Description: |
| * Deallocate a sigaction Q entry |
| * |
| ****************************************************************************/ |
| |
| void nxsig_release_action(FAR sigactq_t *sigact) |
| { |
| irqstate_t flags; |
| |
| if (CONFIG_SIG_ALLOC_ACTIONS > 1 || IS_PREALLOC_ACTION(sigact)) |
| { |
| /* Non-preallocated instances will never return to heap! */ |
| |
| flags = spin_lock_irqsave(&g_sigaction_spin); |
| sq_addlast((FAR sq_entry_t *)sigact, &g_sigfreeaction); |
| spin_unlock_irqrestore(&g_sigaction_spin, flags); |
| } |
| else |
| { |
| kmm_free(sigact); |
| } |
| } |