/* $Author: dionisio $ 
 * $Id: rcl_module_main.c,v 1.1 2001/05/14 18:13:47 dionisio Exp $
 *
 * $Log: rcl_module_main.c,v $
 * Revision 1.1  2001/05/14 18:13:47  dionisio
 * PCP code... yet to be fully debug
 *
 * Revision 1.1.1.1  2001/04/19  08:43:54  miyos
 * LinuxRK tree version 03/10/2001
 * 
 * Revision 1.4  2000/08/23 20:20:07  miyos
 * 8.16.00
 *
 * Revision 1.3  2000/08/08 05:19:43  miyos
 * rewriting RCL
 *
 * Revision 1.2  2000/08/03 23:28:58  miyos
 * *** empty log message ***
 *
 *
 * rcl_module_main.c 
 * 
 * Carnegie Mellon University
 * miyos@cs.cmu.edu
 *
 * [v0.1] Nov.99
 * Used part of module template from Ori's book.
 *    (Copyright (C) 1998-99 by Ori Pomerantz)
 *
 * Assumes Linux version greater than 2.2.x 
 * LinuxRK is 2.2.10 anyway...
 *
 * TODO
 *     - check if LinuxRK is running (open /prok/rk?) and do not
 *      load unless it is. If I load on a non-rk linux, something
 *      ugly would surely happen :-)
 *
 *     - special treatment uid=0  
 */


/* 12.9.99 
 * todo: intercept 'kill' system call 
 *   (remove from proc_list if necessary)
 *   (wake up process as necessary in maxprocess_waitq ...)
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/smp_lock.h>

#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif        

#include <sys/syscall.h>
#include <linux/sched.h>  
#include <asm/uaccess.h>

#include <rk/rk_linux.h>
#include <rk/rk_error.h>
#include <rk/rk.h>

#include "rclmodule.h"



/*
 * Can't avoid gcc emitting __udivdi3... 
 *    ugly temporary fix.... miyos
 */ 
void __udivdi3(void){};


extern rk_reserve_t cpu_reserve_create(rk_resource_set_t rs,
 				       cpu_reserve_attr_t cpu_attr);

/*
 * check what is resistered in rs_proc_list
 */
inline void rs_proc_list_pid(struct rs_proc_list* rs_proc){

  if( rs_proc == NULL){
    printk("rs_proc_list_pid: ERROR\n");
    return;
  }
  printk(" (%d, %s)", rs_proc->rs_proc_pid, rs_proc->rs_proc_task->comm);

}


/**************************************************
 * sytem call trapper functions
 **************************************************/
asmlinkage int 
sys_exit_trapper(int value){
  
  pre_exit_check();
  return sys_exit_call(value);
}


/*
 * helper function used by sys_*fork_trappers
 */
static inline int is_child_alive(int cpid){
  if(cpid != current->p_cptr->pid ||
     current->p_cptr->state == TASK_ZOMBIE ||
     current->p_cptr->flags & PF_EXITING){
    return 1;
  }
  else 
    return 0;
}


/*
 *
 */
asmlinkage int 
sys_fork_trapper(struct pt_regs regs){
  int user_id, cpid;
  struct rk_uid_stats *tmp;

  user_id =  getuid_call();
  
  if(pre_fork_check(user_id, current->pid) == -EINTR)
    return -EINTR;

  /* Call original sys_fork */
  cpid = sys_fork_call(regs); 

  /* check if child is still alive */
  if(!is_child_alive(cpid))
    return cpid;
  
  post_fork_check(user_id, cpid); 
#ifdef DEBUG
  tmp = get_uid_stats_pid(user_id, cpid);
  if(tmp->uid != user_id)
    printk("ERROR[sys_fork_trapper] cpid != get_uid_stats\n");
  if(tmp != NULL){
    printk("sys_fork_trapper: \n  current pid=%d", current->pid);
    printk("  new pid=[%d] : (Fork by uid=%d pid=%d parent_name=%s) \n", 
	   cpid, user_id, current->pid, current->comm);
    printk("  current proc list [uid=%d: ", tmp->uid);
    rs_proc_list_apply(&tmp->rs_proc_list, rs_proc_list_pid);
    printk("]\n");
  }
#endif
  /* 
   *    /W/RK/linux/fs/proc/array.c for proc outputs.
   */

  /* todo: make this into an function so modules can use this call */
  /* struct task_struct *tsk; */
  /* tsk = __rk_find_process_by_pid(cpid); */
  
  return cpid;
}






/* todo
 * probably should merge some of sys_clone_trapper and sys_fork_trapper...
 */
asmlinkage int 
sys_clone_trapper(struct pt_regs regs){
  int user_id, cpid;
  struct rk_uid_stats *tmp;

  user_id =  getuid_call();

  if(pre_fork_check(user_id, current->pid) == -EINTR)
    return -EINTR;
  
  /* Call original sys_fork */
  cpid = sys_clone_call(regs); 

  /* check if child is still alive */
  if(!is_child_alive(cpid))
    return cpid;
  
  post_fork_check(user_id, cpid);
#ifdef DEBUG
  tmp = get_uid_stats_pid(user_id, cpid);
  if(tmp->uid != user_id)
    printk("ERROR[sys_clone_trapper] cpid != get_uid_stats\n");
  if(tmp != NULL){
    printk("sys_clone_trapper: \n  current pid=%d", current->pid);
    printk("  new pid=[%d] : (Fork by uid=%d pid=%d parent_name=%s) \n", 
	   cpid, user_id, current->pid, current->comm);
    printk("  current proc list [uid=%d: ", tmp->uid);
    rs_proc_list_apply(&tmp->rs_proc_list, rs_proc_list_pid);
    printk("]\n");
  }
#endif
  /* 
   *    /W/RK/linux/fs/proc/array.c for proc outputs.
   */

  /* todo: make this into an function so modules can use this call */
  /* struct task_struct *tsk; */
  /* tsk = __rk_find_process_by_pid(cpid); */
  
  return cpid;
}





/* todo
 * probably should merge some of sys_vfork_trapper and sys_fork_trapper...
 */
asmlinkage int 
sys_vfork_trapper(struct pt_regs regs){
  int user_id, cpid;
  struct rk_uid_stats *tmp;

  user_id =  getuid_call();

  if(pre_fork_check(user_id, current->pid) == -EINTR)
    return -EINTR;
  
  /* Call original sys_fork */
  cpid = sys_vfork_call(regs); 

  /* check if child is still alive */
  if(!is_child_alive(cpid))
    return cpid;

  post_fork_check(user_id, cpid);

#ifdef DEBUG
  tmp = get_uid_stats_pid(user_id, cpid);
  if(tmp->uid != user_id)
    printk("WARHING: sys_vfork_trapper: ustat->uid != user_id\n");
  if(tmp != NULL){
    printk("sys_vfork_trapper: \n  current pid=%d", current->pid);
    printk("  new pid=[%d] : (Fork by uid=%d pid=%d parent_name=%s) \n", 
	   cpid, user_id, current->pid, current->comm);
    printk("  current proc list [uid=%d: ", tmp->uid);
    rs_proc_list_apply(&tmp->rs_proc_list, rs_proc_list_pid);
    printk("]\n");
  }
#endif
  /* 
   *    /W/RK/linux/fs/proc/array.c for proc outputs.
   */

  /* todo: make this into an function so modules can use this call */
  /* struct task_struct *tsk; */
  /* tsk = __rk_find_process_by_pid(cpid); */
  
  return cpid;
}






/*
 * Couldn't forward to sys_execve like other system calls, 
 * so I had to re-implement sys_execve here. (copied from process.c)
 * I think the problem is that sys_execve changes 
 * the memory/register contents before returning....
 */
asmlinkage int 
sys_execve_trapper(struct pt_regs regs){
	int error; 
	int uid;
	char * filename;
	
	lock_kernel();
	filename = getname((char *) regs.ebx);

	uid = getuid_call();
#ifdef DEBUG
	printk("sys_execve_trapper enter: exec'ing %s pid=%d uid=%d\n", 
	       filename, current->pid, uid);
#endif
#ifdef FINDEXEC
	printk("FINDEXEC      %s\n", filename);
#endif
	pre_execve_check(uid, filename);

	error = PTR_ERR(filename);
	if (IS_ERR(filename))
		goto out;
	error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &regs);
	


	if (error == 0)
		current->flags &= ~PF_DTRACE;
	putname(filename);
out:
	unlock_kernel();
	return error;
}




asmlinkage rk_reserve_t cpu_reserve_create_trapper(rk_resource_set_t rs,
 						   cpu_reserve_attr_t cpu_attr)
{
  return sys_cpu_reserve_create(rs, cpu_attr);
}



/**************************************************
 * kernel module functions
 **************************************************/

/* Initialize the module - replace the system call */
int init_module(){

  /* Warning - too late for it now, but maybe for next time... */
  printk("Please do a sync before insmod\n and sync before rmmmod.\n");
  printk("eg. sync; sync; sync; insmod rkmodule\n");
  printk("eg. sync; sync; sync; rmmod rmmodule\n");
  
  
  /* TODO
   * should check if /proc/rk exists... 
   *    miyos
   */
  
  /* 
   * intercept/replace system calls 
   */
  sys_fork_call                    = sys_call_table[__NR_fork];
  sys_call_table[__NR_fork]        = sys_fork_trapper;
  sys_vfork_call                   = sys_call_table[__NR_vfork];
  sys_call_table[__NR_vfork]       = sys_vfork_trapper;
  sys_clone_call                   = sys_call_table[__NR_clone];
  sys_call_table[__NR_clone]       = sys_clone_trapper;
  sys_execve_call                  = sys_call_table[__NR_execve];
  sys_call_table[__NR_execve]      = sys_execve_trapper;
  sys_exit_call                    = sys_call_table[__NR_exit];
  sys_call_table[__NR_exit]        = sys_exit_trapper;


  /*
   * system calls that we need.
   */ 
  sys_resource_set_attach_process  = sys_call_table[__NR_rk_resource_set_attach_process];
  sys_resource_set_create          = sys_call_table[__NR_rk_resource_set_create];
  /*sys_cpu_reserve_create           = sys_call_table[__NR_rk_cpu_reserve_create];*/
  sys_cpu_reserve_create           = cpu_reserve_create;
  sys_resource_set_destroy         = sys_call_table[__NR_rk_resource_set_destroy];
  getuid_call                      = sys_call_table[__NR_getuid];
  
  init_manager();
  return 0;
}


/* Return the system call back to normal */
void cleanup_module(){

  if (sys_call_table[__NR_fork]   != sys_fork_trapper   ||
      sys_call_table[__NR_vfork]   != sys_vfork_trapper ||
      sys_call_table[__NR_clone]  != sys_clone_trapper  ||
      sys_call_table[__NR_exit]   != sys_exit_trapper   ||
      sys_call_table[__NR_execve] != sys_execve_trapper ){
      printk("Warning: The system may be left in an unstable state.\n");
  }

  /*
   * todo: should free rk_uid_stats (and all dyanmically allocated memory)
   */
  
  sys_call_table[__NR_fork]                  = sys_fork_call;
  sys_call_table[__NR_vfork]                 = sys_vfork_call;
  sys_call_table[__NR_clone]                 = sys_clone_call;
  sys_call_table[__NR_exit]                  = sys_exit_call;
  sys_call_table[__NR_execve]                = sys_execve_call;
  
  
  printk("rkmodule unloaded\n");
}  


char *get_name_from_pid(int pid){
  printk("rkmodule unloaded\n");
  return "foobar";
}


