// deployer BashModule implementation
// Copyright (C) 2024 Jean-Cloud
// GNU General Public License v3

#include <filesystem>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pwd.h>
#include "BashModule.h"
#include "BashManager.h"

using namespace std;

//constructor
BashModule::BashModule()
{
	name="Bash";
}
//destructor inline 

//private methods
int BashModule::executeScript(string serviceUsername, string script)
{
    cout << "debut execute script" <<endl;
    //this method is called in deploy() and remove(), it executes the script if it exists
    if(!filesystem::exists(script)){
        cout << "No deploy.sh script for this service.";
        return 0;
    } else {
        //check that the file is of type regular
        if (filesystem::status(script).type()!=filesystem::file_type::regular){
            cerr << "Error. The file deploy_user.sh is not regular." << endl;
            return -1;
        } else {
            //check if the owner has the execute permission
            if ((filesystem::status(script).permissions() & filesystem::perms::owner_exec)==filesystem::perms::none){
                cerr << "Error. The owner of file deploy_user.sh does not have permission to execute it. Please change the permission or remove/rename the file." << endl;
                return -1;
            }else{
                //executing the script in a separate process
                pid_t pid = fork(); 
                if (pid == -1) { 
                    cerr << "Error when forking." << endl; 
                    return -1; 
                } else if (pid > 0) { 
                    //parent process 
                    int status;
                    waitpid(-1,&status,0);
                    if(status==-1){
                        cerr << "Error when executing " << script << endl;
                    }
                    return status;
                } else { 
                    //child process
                    execl("/bin/bash", "/bin/bash", "--noediting", "--noprofile", "--norc", "--", script.c_str(), (char *)0);
                } 
            }
        }
        return 0;
    }
}

int BashModule::executeScriptAs(string serviceUsername, string script)
{
    //this method is called in deploy(), it executes the script if it exists, as the user associated with the service
    //check that the file exists
    cout << "debut ex script as"<<endl;
    if(!filesystem::exists(script)){
        cout << "No deploy_user.sh script for this service.";
        return 0;
    } else {
        //check that the file is of type regular
        if (filesystem::status(script).type()!=filesystem::file_type::regular){
            cerr << "Error. The file deploy_user.sh is not regular." << endl;
            return -1;
        } else {
            //check if the owner has the execute permission
            if ((filesystem::status(script).permissions() & filesystem::perms::owner_exec)==filesystem::perms::none){
                cerr << "Error. The owner of file deploy_user.sh does not have permission to execute it. Please change the permission or remove/rename the file." << endl;
                return -1;
            }
        }
        //getting the connection information for the user
        struct passwd * p=getpwnam(serviceUsername.c_str());
        cout << "script " << script.c_str() << endl;
        if (p==NULL){
            cerr << "Error. The user dedicated to the service is not found when trying to execute the bash script." << endl;
            return -1;
        }else{          
            //executing the script in a separate process
            cout << "avant le fork "<<endl;
            pid_t pid = fork(); 
            if (pid == -1) { 
                cerr << "Error when forking." << endl; 
                return -1; 
            } else if (pid > 0) { 
                //parent process 
                int status;
                waitpid(-1,&status,0);
                if(status==-1){
                    cerr << "Error when executing " << script << endl;
                }
                return status;
            } else { 
                //child process
                //executing as the user corresponding to the service
                setgid(p->pw_gid);
                setuid(p->pw_uid);
                execl("/bin/bash", "/bin/bash", "--noediting", "--noprofile", "--norc", "--", script.c_str(), " ", serviceUsername, (char *)0);
            } 
        }       
    }
    return 0;
}

//public methods
int BashModule::Prepare()
{
    return 0;
}

int BashModule::Deploy(string serviceUsername)
{
    string deployScript="./services/"+serviceUsername+"/deploy.sh"; //to be executed as current user
    string deployAsScript="./services/"+serviceUsername+"/deploy_user.sh"; //to be executed as the user corresponding to the service
    if (executeScript(serviceUsername,deployScript)!=0){
        cerr << "Error in BashModule::deploy." << endl;
        return -1;
    }else if (executeScriptAs(serviceUsername,deployAsScript)!=0){
        cerr << "Error in BashModule::deploy." << endl;
        return -1;
    }   
	return 0;		
}


int BashModule::Remove (string serviceUsername)
{
    string removeScript="./services/"+serviceUsername+"/undeploy_user.sh";
    if (executeScript(serviceUsername,removeScript)!=0){
        cerr << "Error in BashModule::deploy." << endl;
        return -1;
    }
	return 0;
}

int BashModule::Clean()
{
	return 0;
} 
