/*
 * sys_kill.c: Send a signal to a given process.
 * 
 * Author    Alain Greiner (2016,2017)
 *
 * Copyright (c)  UPMC Sorbonne Universites
 *
 * This file is part of ALMOS-MKH.
 *
 * ALMOS-MKH is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-MKH is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <kernel_config.h>
#include <hal_types.h>
#include <errno.h>
#include <thread.h>
#include <printk.h>
#include <process.h>
#include <signal.h>
#include <cluster.h>
#include <rpc.h>

///////////////////////////
int sys_kill( pid_t    pid,
              uint32_t sig_id )
{
    thread_t  * this    = CURRENT_THREAD;
    process_t * process = this->process;

    // check signal index
	if( (sig_id == 0) || (sig_id >= SIG_NR) )
	{
        printk("\n[ERROR] in %s : illegal signal = %d for thread %x in process %x\n",
               __FUNCTION__ , sig_id , this->trdid , process->pid );
		this->errno = EINVAL;
        return -1;
	}

    // get local pointer on local cluster manager
    cluster_t * cluster = LOCAL_CLUSTER;

    // get owner process cluster and lpid
    cxy_t   owner_cxy  = CXY_FROM_PID( pid );
    lpid_t  lpid       = LPID_FROM_PID( pid );

    // check PID
    if( (lpid >= CONFIG_MAX_PROCESS_PER_CLUSTER) || cluster_is_undefined( owner_cxy ) )
    {
        printk("\n[ERROR] in %s : illegal target PID = %d for thread %x in process %x\n",
               __FUNCTION__ , pid , this->trdid , process->pid );
		this->errno = EINVAL;
        return -1;
    }

    // get extended pointers on copies root and lock
    xptr_t root_xp = XPTR( owner_cxy , &cluster->pmgr.copies_root[lpid] );
    xptr_t lock_xp = XPTR( owner_cxy , &cluster->pmgr.copies_lock[lpid] );

    // take the lock protecting the copies
    remote_spinlock_lock( lock_xp );

    // TODO the loop below sequencialize the RPCs
    // they could be pipelined using a non-blocking RPC ...
 
    // loop on the process decriptor copies 
    xptr_t  iter_xp;
    XLIST_FOREACH( root_xp , iter_xp )
    {
        xptr_t      process_xp  = XLIST_ELEMENT( iter_xp , process_t , copies_list );
        cxy_t       process_cxy = GET_CXY( process_xp );
        process_t * process_ptr = (process_t *)GET_PTR( process_xp );

        if( process_cxy == local_cxy )   // process copy is local
        {
            signal_rise( process_ptr , sig_id );
        }
        else                           // process copy is remote
        {
            rpc_signal_rise_client( process_cxy , process_ptr , sig_id );
        }
    }

    // release the lock
    remote_spinlock_unlock( lock_xp );

    hal_fence();

	return 0;

}  // end sys_kill()

