summaryrefslogblamecommitdiffstats
path: root/cpukit/posix/src/killinfo.c
blob: bde511504a5965fa0e863ffd07be604f07bf0a4d (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                                           


        
                    

                                    

   


                          
                            
                                                    
  



















                                                                              

   
                    

                   



                    
 
                                    
                                    
                            

                                   
 















                                                                       





                                                          
                                         
 
                        






                                    


                                       


                                            
                                          
                                                   



                                              
                                     
                                             
                                        
 


                                                                
                        
                                                  

    
                                 
     



                                                   
                                                   



                                                              
                                                            


             
                                                                      


                                                                          
                                                                
                                                 





                                  








                                    

                                                  




                                                                       
                                                  







                                                                  

                                                                             



                                                                          






                                                  
                                     
 
                                                                        
                                                               



                                                                               
                                                                              











                                                                     
                                        

                        
















                                                                              
                    
                                   
 
                                                                                
                                                             
                                                          

                    
 
                                                     

                                         
                                                   




                                                            

                                                 

                                                     
                                                                  



                                   



                                                                         
                                                                     
                 
                      



                                                                          

                                                           



                              

                                                       
                 
                      


                                                                   

                                                                       



                                                                          
         
                                                                       
                                  
                                                                 

                 
                      


                                                                          



                                                                         
         
 
                                                                           



                                                              
                                                                   

                   
 






                                                                                
                                                                     


                     



       

                            










                                                                           
                                                           
     


                           

                                                                      

                                   
           
 
    
                                                                    

                                     
                                                                    
                                        








                                                                      

                                             

                                                     
 


                                                               
                                                                 
                      
                                               
                                          
                                                     
     


                              



                                     

   
                                           
                   
                                      

           
/* 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 <pthread.h>
#include <signal.h>
#include <errno.h>

#include <rtems/posix/pthreadimpl.h>
#include <rtems/posix/psignalimpl.h>
#include <rtems/score/isr.h>
#include <rtems/score/statesimpl.h>
#include <rtems/seterr.h>

/*
 *  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 <rtems/bspIo.h>
  #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;
}