/* SPDX-License-Identifier: BSD-2-Clause */ /** * @file * * @ingroup POSIXAPI * * @brief Send a Signal to a Process */ /* * kill() support routine * * COPYRIGHT (c) 1989-2009. * On-Line Applications Research Corporation (OAR). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include /* * If you enable this, then you get printk() feedback on each path * and the input to the decision that lead to the decision. Hopefully * this will help in debugging the algorithm that distributes process * signals to individual threads. */ /* #define DEBUG_SIGNAL_PROCESSING */ #if defined(DEBUG_SIGNAL_PROCESSING) #include #define DEBUG_STEP(_x) printk(_x) #else #define DEBUG_STEP(_x) #endif /* * 3.3.2 Send a Signal to a Process, P1003.1b-1993, p. 68 * * NOTE: Behavior of kill() depends on _POSIX_SAVED_IDS. */ #define _POSIX_signals_Is_interested( _api, _mask ) \ ( (_api)->signals_unblocked & (_mask) ) int _POSIX_signals_Send( pid_t pid, int sig, const union sigval *value ) { sigset_t mask; POSIX_API_Control *api; uint32_t the_api; uint32_t index; uint32_t maximum; Objects_Information *the_info; Objects_Control **object_table; Thread_Control *the_thread; Thread_Control *interested; Priority_Control interested_priority; Chain_Node *the_node; siginfo_t siginfo_struct; siginfo_t *siginfo; POSIX_signals_Siginfo_node *psiginfo; Thread_queue_Heads *heads; Thread_queue_Context queue_context; Per_CPU_Control *cpu_self; /* * Only supported for the "calling process" (i.e. this node). */ if ( pid != getpid() ) rtems_set_errno_and_return_minus_one( ESRCH ); /* * Validate the signal passed. */ if ( !sig ) rtems_set_errno_and_return_minus_one( EINVAL ); if ( !is_valid_signo(sig) ) rtems_set_errno_and_return_minus_one( EINVAL ); /* * If the signal is being ignored, then we are out of here. */ if ( _POSIX_signals_Vectors[ sig ].sa_handler == SIG_IGN ) return 0; /* * P1003.1c/Draft 10, p. 33 says that certain signals should always * be directed to the executing thread such as those caused by hardware * faults. */ if ( (sig == SIGFPE) || (sig == SIGILL) || (sig == SIGSEGV ) ) return pthread_kill( pthread_self(), sig ); mask = signo_to_mask( sig ); /* * Build up a siginfo structure */ siginfo = &siginfo_struct; siginfo->si_signo = sig; siginfo->si_code = SI_USER; if ( !value ) { siginfo->si_value.sival_int = 0; } else { siginfo->si_value = *value; } /* FIXME: https://devel.rtems.org/ticket/2690 */ cpu_self = _Thread_Dispatch_disable(); /* * Is the currently executing thread interested? If so then it will * get it an execute it as soon as the dispatcher executes. */ the_thread = _Per_CPU_Get_executing( cpu_self ); api = the_thread->API_Extensions[ THREAD_API_POSIX ]; if ( _POSIX_signals_Is_interested( api, mask ) ) { goto process_it; } /* * Is an interested thread waiting for this signal (sigwait())? * * There is no requirement on the order of threads pending on a sigwait(). */ /* XXX violation of visibility -- need to define thread queue support */ heads = _POSIX_signals_Wait_queue.Queue.heads; if ( heads != NULL ) { Chain_Control *the_chain = &heads->Heads.Fifo; for ( the_node = _Chain_First( the_chain ); !_Chain_Is_tail( the_chain, the_node ) ; the_node = the_node->next ) { Scheduler_Node *scheduler_node; scheduler_node = SCHEDULER_NODE_OF_WAIT_PRIORITY_NODE( the_node ); the_thread = _Scheduler_Node_get_owner( scheduler_node ); api = the_thread->API_Extensions[ THREAD_API_POSIX ]; #if defined(DEBUG_SIGNAL_PROCESSING) printk( "Waiting Thread=%p option=0x%08x mask=0x%08x blocked=0x%08x\n", the_thread, the_thread->Wait.option, mask, ~api->signals_unblocked); #endif /* * Is this thread is actually blocked waiting for the signal? */ if (the_thread->Wait.option & mask) goto process_it; /* * Is this thread is blocked waiting for another signal but has * not blocked this one? */ if (api->signals_unblocked & mask) goto process_it; } } /* * Is any other thread interested? The highest priority interested * thread is selected. In the event of a tie, then the following * additional criteria is used: * * + ready thread over blocked * + blocked on call interruptible by signal (can return EINTR) * + blocked on call not interruptible by signal * * This looks at every thread in the system regardless of the creating API. * * NOTES: * * + rtems internal threads do not receive signals. */ interested = NULL; interested_priority = UINT64_MAX; for (the_api = OBJECTS_CLASSIC_API; the_api <= OBJECTS_APIS_LAST; the_api++) { _Assert( _Objects_Information_table[ the_api ] != NULL ); the_info = _Objects_Information_table[ the_api ][ 1 ]; if ( !the_info ) continue; maximum = _Objects_Get_maximum_index( the_info ); object_table = the_info->local_table; for ( index = 0 ; index < maximum ; ++index ) { the_thread = (Thread_Control *) object_table[ index ]; if ( !the_thread ) continue; #if defined(DEBUG_SIGNAL_PROCESSING) printk("\n 0x%08x/0x%08x %d/%d 0x%08x 1", the_thread->Object.id, ((interested) ? interested->Object.id : 0), _Thread_Get_priority( the_thread ), interested_priority, the_thread->current_state ); #endif /* * If this thread is of lower priority than the interested thread, * go on to the next thread. */ if ( _Thread_Get_priority( the_thread ) > interested_priority ) continue; DEBUG_STEP("2"); /* * If this thread is not interested, then go on to the next thread. */ api = the_thread->API_Extensions[ THREAD_API_POSIX ]; #if defined(RTEMS_DEBUG) if ( !api ) continue; #endif if ( !_POSIX_signals_Is_interested( api, mask ) ) continue; DEBUG_STEP("3"); /* * Now we know the thread under consideration is interested. * If the thread under consideration is of higher priority, then * it becomes the interested thread. * * NOTE: We initialized interested_priority to PRIORITY_MAXIMUM + 1 * so we never have to worry about deferencing a NULL * interested thread. */ if ( _Thread_Get_priority( the_thread ) < interested_priority ) { interested = the_thread; interested_priority = _Thread_Get_priority( the_thread ); continue; } DEBUG_STEP("4"); /* * Now the thread and the interested thread have the same priority. * We have to sort through the combinations of blocked/not blocked * and blocking interruptibutable by signal. * * If the interested thread is ready, don't think about changing. */ if ( interested && !_States_Is_ready( interested->current_state ) ) { /* preferred ready over blocked */ DEBUG_STEP("5"); if ( _States_Is_ready( the_thread->current_state ) ) { interested = the_thread; interested_priority = _Thread_Get_priority( the_thread ); continue; } DEBUG_STEP("6"); /* prefer blocked/interruptible over blocked/not interruptible */ if ( !_States_Is_interruptible_by_signal(interested->current_state) ) { DEBUG_STEP("7"); if ( _States_Is_interruptible_by_signal(the_thread->current_state) ) { DEBUG_STEP("8"); interested = the_thread; interested_priority = _Thread_Get_priority( the_thread ); continue; } } } } } if ( interested ) { the_thread = interested; goto process_it; } /* * OK so no threads were interested right now. It will be left on the * global pending until a thread receives it. The global set of threads * can change interest in this signal in one of the following ways: * * + a thread is created with the signal unblocked, * + pthread_sigmask() unblocks the signal, * + sigprocmask() unblocks the signal, OR * + sigaction() which changes the handler to SIG_IGN. */ goto post_process_signal; /* * We found a thread which was interested, so now we mark that this * thread needs to do the post context switch extension so it can * evaluate the signals pending. */ process_it: /* * Returns true if the signal was synchronously given to a thread * blocked waiting for the signal. */ if ( _POSIX_signals_Unblock_thread( the_thread, sig, siginfo ) ) { _Thread_Dispatch_enable( cpu_self ); return 0; } post_process_signal: /* * We may have woken up a thread but we definitely need to post the * signal to the process wide information set. */ _POSIX_signals_Set_process_signals( mask ); _Thread_queue_Context_initialize( &queue_context ); _POSIX_signals_Acquire( &queue_context ); if ( _POSIX_signals_Vectors[ sig ].sa_flags == SA_SIGINFO ) { psiginfo = (POSIX_signals_Siginfo_node *) _Chain_Get_unprotected( &_POSIX_signals_Inactive_siginfo ); if ( !psiginfo ) { _POSIX_signals_Release( &queue_context ); _Thread_Dispatch_enable( cpu_self ); rtems_set_errno_and_return_minus_one( EAGAIN ); } psiginfo->Info = *siginfo; _Chain_Append_unprotected( &_POSIX_signals_Siginfo[ sig ], &psiginfo->Node ); } _POSIX_signals_Release( &queue_context ); DEBUG_STEP("\n"); _Thread_Dispatch_enable( cpu_self ); return 0; }