
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <asm/unistd_32.h>
#include <sys/types.h>
#include <sys/syscall.h>

#include "estructures.h"
#include "utils.h"



#include <sys/reg.h>

#ifdef __x86_64__
#define SC_NUMBER  (8 * ORIG_RAX)
#define SC_RETCODE (8 * RAX)
#else
#define SC_NUMBER  (4 * ORIG_EAX)
#define SC_RETCODE (4 * EAX)
#endif




//Macro per obtenir el numero de limit (resource) a partir del nom
#define REGISTER(x) {RLIMIT_ ## x, #x, 0, {0,0}}

//Arrays de syscalls i de limits
static struct one_call syscalls[400];
static struct one_limit limits[] = {
	REGISTER(AS),
	REGISTER(CORE),
	REGISTER(CPU),
	REGISTER(DATA),
	REGISTER(FSIZE),
	REGISTER(LOCKS),
	REGISTER(MEMLOCK),
	REGISTER(NOFILE),
	REGISTER(NPROC),
	REGISTER(RSS),
	REGISTER(STACK),
	{-1, "", 0, {0,0}}
};

//Numero de crides a la taula de syscalls
int syscalltable_size=0;


void init(){
	//Fitxers obligatoris
	if (!(file_exists(LIMIT_CONFIG_FILE) && file_exists(SYSCALL_CONFIG_FILE) && file_exists(SYSCALL_TABLE_GENERATOR))){
		perror("Error: Missing configuraton file");
		exit(EXIT_FAILURE);
	}

	//Si no existeix, genera la taula de crides a sistema
	if(!file_exists(SYSCALL_TABLE_CONFIG_FILE)){
		generate_syscall_table();
	}

	//Carrega el contingut dels fitxers de configuracio al arrays en memoria
	load_limit_config(limits);
	load_syscall_table(syscalls, &syscalltable_size);
	load_syscall_limits(syscalls, &syscalltable_size);
}

void run_hostile(char *argv[]){
	execve(argv[0], argv, NULL);
}

static void trace_hostile(pid_t pid)
{
    int status;
    long sc_number, sc_retcode;

    //Wait inicial del SIGTRAP de ptrace
    wait(&status);
    ptrace(PTRACE_SYSCALL, pid, NULL, NULL);

    //2 waits per no contabilitzar en els limits la crida original a execve
    wait(&status);
    ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
    wait(&status);
    ptrace(PTRACE_SYSCALL, pid, NULL, NULL);

    while (1)
    {
        wait(&status);


        if (WIFEXITED(status)) {
            exit(0);
        }
        if (WIFSIGNALED(status)) {
	    perror("Child killed by signal");
            exit(0);
        }
        if (!WIFSTOPPED(status)) {
            exit(0);
        }
        if (WSTOPSIG(status) == SIGTRAP) {
    		//Obtenim els registres
    		struct user_regs_struct r;
    		if (ptrace(PTRACE_GETREGS, pid, 0, &r) != 0){
    			perror("Error: ptrace_getregs failed");
    			exit(EXIT_FAILURE);
    		}

    		//Comprobem que el identificador de la syscall sigui correcte
    		int syscall = r.orig_rax;
    		if(syscall < 0 ){
    			perror("Error:process reached resource limit");
    			exit(EXIT_FAILURE);
    		}
    		int i;
    		//Busquem en el array de syscalls la que coincideixi amb el valor que hi havia a eax
    		for(i=0; i < 400; i++){
    			if(syscalls[i].syscall == syscall){
    				if(syscalls[i].in_call == 0){
    					syscalls[i].in_call = 1;
    					if(syscalls[i].maxcalls != -1){
    						syscalls[i].numcalls += 1;
    						if(syscalls[i].numcalls > syscalls[i].maxcalls){
    							ptrace(PTRACE_KILL, pid, 0, 0);
    							perror("Syscall limit reached");
    							exit(EXIT_FAILURE);
    						}
    					}
    				}else{
    					syscalls[i].in_call = 0;
    				}
    				break;
    			}
    		}
        } else {
            printf("Child stopped due to signal %d\n", WSTOPSIG(status));
        }
        fflush(stdout);
		if (ptrace(PTRACE_SYSCALL, pid, 0, 0) != 0){
			perror("Error: ptrace_syscall failed");
			exit(EXIT_FAILURE);
		}
    }
}


static void start_trace(char *argv[])
{
	pid_t pid = fork();
	switch (pid) {
        case -1:
            perror("Error: fork failed when trying to run hostile program");
            exit(EXIT_FAILURE);
        case 0: //Proces fill, executara el programa hostil
        	set_resource_limits(limits);

        	ptrace(PTRACE_TRACEME, 0, 0, 0);
        	kill(getpid(), SIGSTOP);//Aturem el proces per a que al pare li dongui temps de cridar ptrace abans del execve
        	execve(argv[0], argv, NULL);
        	run_hostile(argv);
        	//Mai ha d'arribar aqui
        	perror("Error: could start hostile program");
        	exit(EXIT_FAILURE);
        default: //Proces pare, fara el trace
        	trace_hostile(pid);
        	break;
	}
}

int main(int argc, char *argv[]) {
		init();
		start_trace(&argv[1]);

		return 1;
}

