/*
 *
 * Real-Time and Multimedia Systems Laboratory
 * Copyright (c) 2000 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Real-Time and Multimedia Systems Laboratory
 *  Attn: Prof. Raj Rajkumar
 *  Electrical and Computer Engineering, and Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 *  or via email to raj@ece.cmu.edu
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 *
 *
 */

/* $Author: dionisio $ 
 * $Id: rcl_manager.c,v 1.1 2001/05/14 18:13:46 dionisio Exp $
 * 
 * rcl_manager.c
 * Various resource managers. 
 */

#define __NO_VERSION__

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/wrapper.h> /*for sleeping and waking processes*/

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

#include "rclmodule.h"

/*
#define DEBUG
#define RSDEBUG
*/

/* maybe I should make this an array indexed by uid */
static struct wait_queue *MaxProcessWaitQ            = NULL;
static char               maxprocess_waiting         = 0x0;

struct resource_control *rcl_head, *rcl_default; 


/*
 * RCL configuration: system call and incoming network packets 
 *
 * Temporarily hard coded... ugh.
 *        - user id of -1 means wild card.
 *
 * If you are going to add an entry, make sure to change RCL_LEN too.
 */
struct resource_control rcl_hard_code[RCLVERSION][RCL_LEN] = {
  /**************************************************************************
   * 1st RCL
   **************************************************************************/
  {

  //15% of 100 millisec for gcc and all its progeny spawned by root (user id 0)
  {"RCL-1" ,0, RCL0, 0, "", "/usr/bin/make", 
   INHERIT , RESERV,
   {0, 150*1000*1000}, {0,1000*1000*1000}, {RSV_HARD,RSV_HARD,RSV_HARD},  0, NULL}, 

  //combined 10% of 100 millisec for all gcc for all process other than root (-1=wild card)
  {"RCL-1" ,0, RCL0, -1, "", "/usr/bin/make", 
   INHERIT , RESERV,
   {0, 100*1000*1000}, {0,1000*1000*1000}, {RSV_HARD,RSV_HARD,RSV_HARD},  0, NULL}, 

  //80% of 10 millisec 
  {"RCL-2", 1, RCL0, -1, "", "/usr/home/miyos/W/RK/LinuxRK/LinuxRK/RCL/client-select", 
   INHERIT , RESERV,
   {0, 80*1000*1000}, {0,100*1000*1000}, {RSV_HARD,RSV_HARD,RSV_HARD},  0, NULL}, 

  //19% of 1000 millisec 
  {"RCL-3", 2, RCL0, -1, "", "/usr/home/miyos/W/RK/LinuxRK/LinuxRK/RCL/server-select", 
   NOINHERIT , NORESERV,
   {0, 190*1000*1000}, {0,1000*1000*1000}, {RSV_HARD,RSV_HARD,RSV_HARD},  0, NULL},

  {"RCL-3", 2, RCL0, -1, "128.2.220.93", "", 
   INHERIT , RESERV,
   {0, 3*1000*1000}, {0,30*1000*1000}, {RSV_HARD,RSV_HARD,RSV_HARD},  0, NULL},


  {"RCL-3", 2, RCL0, -1, "128.2.222.155", "", 
   INHERIT , RESERV,
   {0, 3*1000*1000}, {0,30*1000*1000}, {RSV_HARD,RSV_HARD,RSV_HARD},  0, NULL},

  // default
  {"RCL-4", 3, RCL0, -1, "","", NOINHERIT , NORESERV,
   {0, 0}, {0, 0}, {RSV_SOFT,RSV_SOFT,RSV_SOFT},  0, NULL}
  },

  /**************************************************************************
   * 2nd RCL
   **************************************************************************/
  {


  {"RCL-1" ,0, RCL1, 0, "", "/usr/bin/make", 
   INHERIT , RESERV,
   {0, 100*1000*1000}, {0,1000*1000*1000}, {RSV_HARD,RSV_HARD,RSV_HARD},  0, NULL}, 

  {"RCL-1",0, RCL1, -1, "", "/usr/bin/make", 
   INHERIT , RESERV,
   {0, 150*1000*1000}, {0,1000*1000*1000}, {RSV_HARD,RSV_HARD,RSV_HARD},  0, NULL}, 


  {"RCL-2", 1, RCL1, -1, "", "/usr/home/miyos/W/RK/LinuxRK/LinuxRK/RCL/client-select", 
   INHERIT , RESERV,
   {0, 400*1000*1000}, {0,1000*1000*1000}, {RSV_HARD,RSV_HARD,RSV_HARD},  0, NULL}, 


  {"RCL-3",2, RCL1, -1, "", "/usr/home/miyos/W/RK/LinuxRK/LinuxRK/RCL/server-select", 
   INHERIT , RESERV,
   {0, 50*1000*1000}, {0,1000*1000*1000}, {RSV_HARD,RSV_HARD,RSV_HARD},  0, NULL},


  {"RCL-3", 2, RCL0, -1, "128.2.220.93", "", 
   NOINHERIT , RESERV,
   {0, 200*1000*1000}, {0,1000*1000*1000}, {RSV_HARD,RSV_HARD,RSV_HARD},  0, NULL},


  {"RCL-3", 2, RCL0, -1, "128.2.222.94", "", 
   NOINHERIT , RESERV,
   {0, 200*1000*1000}, {0,1000*1000*1000}, {RSV_HARD,RSV_HARD,RSV_HARD},  0, NULL},


  {"RCL-4", 3, RCL0, -1, "", "", NOINHERIT , NORESERV,
   {0, 0}, {0, 0}, {RSV_HARD,RSV_HARD,RSV_HARD},  0, NULL}
  }
};





rk_resource_set_t create_rset_from_rcl(struct resource_control *rcl){
  rk_reserve_t  cpu_rsv  = NULL;
  rk_resource_set_t rset = NULL;
  cpu_reserve_attr_data_t cpu_attr;

  // some rcl may not require reserves.
  if(!rcl->rpal->use_reserv)
    return NULL;
  set_cpuattr(&cpu_attr, rcl);
  rset    = sys_resource_set_create(rcl->name);
  cpu_rsv = sys_cpu_reserve_create(rset, &cpu_attr);
#ifdef RSDEBUG
  printk("create_rset_from_rcl:   \n");
  printk("                   cpu_rsv created c=[%d, %d], t=[%d, %d]\n", 	    
	 (int)rcl->ct.tv_sec, (int)rcl->ct.tv_nsec,
	 (int)rcl->tt.tv_sec, (int)rcl->tt.tv_nsec);
#endif
  return rset;
}


/* 
 * searches for matching rcl from top to bottom
 *    This should only be used only for finding RCL mapping
 *    for newly created members. (to preserve ONCE feature) 
 *    
 */
inline struct resource_control* search_rcl(int uid, int pid, char *progname, char *saddr){
  struct resource_control *rcl;
  
  rcl = rcl_head;
  
  while(rcl != NULL){
    
    if(saddr == NULL ){
      if(strcmp(rcl->saddrip, "") == 0){
	//match: uid, progname
	if(rcl->uid == uid && (strcmp(rcl->progname, progname)==0))
	  return rcl;
	//match: *, progname
	else if(rcl->uid == WILDCARD && (strcmp(rcl->progname, progname)==0))
	  return rcl;
	//match: uid, *
	else if(rcl->uid == uid &&  (strcmp(rcl->progname, "")==0))
	  return rcl;
	//match: uid, **
	else if(rcl->uid == WILDCARD && (strcmp(rcl->progname, "")==0))
	  return rcl;
	//miyos: need to include the ONCE notion;
      }
    }
    else if(progname == NULL){
      if(strcmp(rcl->saddrip, saddr) == 0)
	return rcl;
    }
    rcl = rcl->next;
  }
  return rcl_default; // Should never return NULL. 
}


/* init_manager
 *   rclversion = describes which rcl to use for initialization.
 */
inline void
init_manager(int rclversion){
  int i;
  struct resource_control *rcl;

  /*
   * TODO: RCLVERSION should be a variable when read from file
   */
  if(rclversion < 0 || rclversion > RCLVERSION){
    printk("ERROR RK_MANAGER: init_manager\n");
    rclversion = 0; // arbitrarilly 0 for default
  }
  
  rcl_head = &rcl_hard_code[rclversion][0];
  rcl = rcl_head;

  for(i=0; i < RCL_LEN ; i++){
    rcl->rpal                = malloc(sizeof(struct resource_principal));
    rcl->rpal->uid           = rcl->uid;
    rcl->rpal->max_processes = MAXPROC;
    rcl->rpal->inherit       = rcl->inherit;
    rcl->rpal->use_reserv    = rcl->use_reserv;
    INIT_LIST_HEAD(&rcl->rpal->uid_list);
    INIT_LIST_HEAD(&rcl->rpal->rs_proc_list);
    INIT_LIST_HEAD(&rcl->rpal->rs_proc_list_org);
    rcl->rpal->rcl           = rcl->rpal->rclorg  = rcl;
    rcl->rpal->rset          = rcl->rpal->rsetorg = NULL;

    //don't want to create rset here.
    //want to create rset only when members exist.
    if(i != RCL_LEN-1){
      rcl->next                = &rcl_hard_code[rclversion][i+1];
      rcl                      = rcl->next;
    }
  }
  rcl_default = rcl; //assumes last one is default RCL.
  rcl->next = NULL;

  //accept_ip(in_aton("128.2.220.93"));
}




/*
 * Returns 0 on sucess, -EINTR on failed system call due to interrupt.
 * Currently responsible for the following administrative task
 *      1) recognize parent as member of rpal. (execve depends on parent
 *           being a member of a resource principal.)
 *      2) make sure uid does not fork more than MAX allowed.
 */
inline int 
pre_fork_check(int uid, pid_t pid){
  struct resource_principal* rpal;
  struct resource_control*   rcl; 
  struct rs_proc_list*       proc_list;
  int                        numproc, ret;
  
  rpal = search_rpal_from_pid(pid);
  
  // include parent process in rpal
  if(!rpal){
#ifdef DEBUG
    printk("pre_fork_check: current->comm = %s\n", current->comm);
#endif
    rcl  = search_rcl(uid, pid, current->comm, NULL);
    rpal = rcl->rpal;
    proc_list = create_new_proc_entry(pid, current);
    list_add(&proc_list->rs_proc_list, &rpal->rs_proc_list_org);

    proc_list = create_new_proc_entry(pid, current);
    list_add(&proc_list->rs_proc_list, &rpal->rs_proc_list);
  }

  // check parent if it is attached to appropriate reserv
  rcl  = rpal->rcl;
  if(rpal->use_reserv && rpal->rset == NULL){
    rpal->rset = rpal->rsetorg = create_rset_from_rcl(rcl);
    if(current->rk_resource_set && current->rk_resource_set != rpal->rset ){
      __rk_resource_set_detach_process(current->rk_resource_set, current);
    }
    if((ret = sys_resource_set_attach_process(rpal->rset, current->pid)) != RK_SUCCESS){
      printk("register_forked_child: failed at resource set attach\n");
      return ret;
    }
  }
  
  num_proc(&rpal->rs_proc_list, &numproc);

  /* block if number of processes allowed exceeds */
  while(numproc > rpal->max_processes){
    int i, is_sig = 0; 

#ifdef DEBUG
    printk("pre_fork_check: uid=%d OVER MAX PROCESS\n", uid);
#endif

    maxprocess_waiting = 0x1;
    module_interruptible_sleep_on(&MaxProcessWaitQ);
    for(i=0; i<_NSIG_WORDS && !is_sig; i++)
      is_sig = current->signal.sig[i] & ~current->blocked.sig[i];
    if (is_sig)
      return -EINTR;
    num_proc(&rpal->rs_proc_list, &numproc);
  }
  return 0;
}


inline void 
post_fork_check(int uid, pid_t cpid){
  register_forked_child(uid, cpid);
}


/*
 */
inline int pre_execve_check(int uid, char *prog){

  struct resource_control     *rcl = NULL;
  struct resource_principal   *rpal, *cur_rpal, *parent_rpal;
  struct rs_proc_list         *proc_list = NULL;  
  struct rs_proc_list         *proc_list_org = NULL;  
  int    ret;

#ifdef DEBUG
  printk("enter: pre_execve_check prog = %s\n", prog);
#endif
  
  rpal = NULL;
  rcl  = search_rcl(uid, current->pid, prog, NULL);

  // If we should inherit from parent rpal, rcl = NULL.
  // When forked, a process should INHERIT by default and already be included
  // in parents rpal. So, should do nothing here. 
  
  //could search for rcl first...?
  if((parent_rpal = search_rpal_from_pid(current->p_pptr->pid))){
    if(parent_rpal->inherit == INHERIT){
      rpal = parent_rpal;
      rcl  = parent_rpal->rcl; 
    }
  }

  //Process may already be in appropriate rpal.
  //If there is a more strict rcl, must change
  //to appropriate resource set.
  if((cur_rpal = search_rpal_from_pid(current->pid))){
    // if not in appropriate rpal, remove process from current rpal
    if(cur_rpal->rcl != rcl){
      proc_list =     remove_proc_from_rpal(cur_rpal, current->pid);
      proc_list_org = remove_proc_from_rpal_org(cur_rpal, current->pid);
    }
  }
  
  if(rcl == NULL) //sanity
    return RK_ERROR; 
  rpal = rcl->rpal;
      
  if(rpal->use_reserv && rpal->rset == NULL){
    if((rpal->rset = rpal->rsetorg = create_rset_from_rcl(rcl)) == NULL)
      printk("create rset from rcl returns ERROR\n");
  }
  /*
   * make sure that current process is not attached to any reserves before
   * attempting to attach.
   */
  if(rpal->rset){
    if(current->rk_resource_set != rpal->rset){
      if(current->rk_resource_set)
	__rk_resource_set_detach_process(current->rk_resource_set, current);
      
      if((ret = sys_resource_set_attach_process(rpal->rset, current->pid)) != RK_SUCCESS){
	printk("pre_execve_check: failed at resource set attach\n");
	return ret;
      }
    }
  }
    
  // if not in proc_list for rpal, include current proccess
  if(proc_list == NULL){
    if((proc_list = rs_proc_list_search_pid(&rpal->rs_proc_list, current->pid)))
      return RK_SUCCESS;
    else{
      proc_list = create_new_proc_entry(current->pid, current);
      proc_list_org = create_new_proc_entry(current->pid, current);
    }
  }
  list_add(&proc_list_org->rs_proc_list, &rpal->rs_proc_list_org);
  list_add(&proc_list->rs_proc_list, &rpal->rs_proc_list);
  
  return RK_SUCCESS;
}


struct resource_control *update_rcl(int version, struct resource_control *oldrcl){
  struct resource_control *newrcl;
  printk("oldrcl->{version=%d, uid=%d, prgname=%s}\n", 
	 oldrcl->version, oldrcl->uid, oldrcl->progname);
  newrcl = (struct resource_control *)
    ((char *)oldrcl + (version - oldrcl->version)*sizeof(struct resource_control)*RCL_LEN);
  
  printk("newrcl->{version=%d, uid=%d, prgname=%s}\n", 
	 newrcl->version, newrcl->uid, newrcl->progname);
  return newrcl;
}




/*
 *
 */
inline void pre_exit_check(){
  struct resource_principal *rpal;
  struct rs_proc_list       *proc_list;
  int                        user_id, pid, num;

  num = 0;
  user_id = getuid_call();
  pid     = current->pid;

#ifdef DEBUG
  printk("pre_exit_check: deleting uid = %d, pid=%d, name=%s\n", 
	 user_id, pid, current->comm);
#endif

  if((rpal = search_rpal_org_from_pid(pid)) != NULL){
    proc_list = remove_proc_from_rpal_org(rpal, pid);
    free(proc_list);
  }

  if((rpal = search_rpal_from_pid(pid)) != NULL){
    proc_list = remove_proc_from_rpal(rpal, pid);
    free(proc_list);
  }
  else{ // rpal == NULL
#ifdef DEBUG
    /*
     * this happens when
     * Parent| -> forks-> |                 | post_fork_check[this is where ustat is alloced] 
     * Child |            | exec -> exit -> |
     */
    printk("WARNING: sys_exit_trapper\n");
#endif
  }

  if(maxprocess_waiting){
    module_wake_up(&MaxProcessWaitQ);
    maxprocess_waiting = 0x0;
  }

}

