added group and user set[uid|gid] capability at unit definition level
parent
3517b9cc11
commit
f4a38de0c0
10
examplar.cpp
10
examplar.cpp
|
@ -137,15 +137,22 @@ int main( int argc, char * argv[] )
|
||||||
std::string definitions_file = configuration.get_units_path();
|
std::string definitions_file = configuration.get_units_path();
|
||||||
std::string plan_file = configuration.get_plan_path();
|
std::string plan_file = configuration.get_plan_path();
|
||||||
|
|
||||||
|
slog.log( E_DEBUG, "* Initialising suite (definition library).");
|
||||||
Suite available_definitions = Suite( L_LEVEL );
|
Suite available_definitions = Suite( L_LEVEL );
|
||||||
|
|
||||||
|
slog.log( E_INFO, "* Loading all actionable units into suite." );
|
||||||
available_definitions.load_units_file( definitions_file );
|
available_definitions.load_units_file( definitions_file );
|
||||||
|
|
||||||
|
slog.log( E_DEBUG, "* Initialising plan." );
|
||||||
Plan plan = Plan( &configuration, L_LEVEL );
|
Plan plan = Plan( &configuration, L_LEVEL );
|
||||||
|
|
||||||
|
slog.log( E_INFO, "* Loading plan outline.");
|
||||||
plan.load_plan_file( plan_file );
|
plan.load_plan_file( plan_file );
|
||||||
|
|
||||||
|
slog.log( E_INFO, "* Loading planned tasks from suite to plan." );
|
||||||
plan.load_definitions( available_definitions );
|
plan.load_definitions( available_definitions );
|
||||||
|
|
||||||
slog.log( E_DEBUG, "Ready to execute all tasks in Plan." );
|
slog.log( E_INFO, "* Ready to execute all actionable tasks in plan." );
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -154,6 +161,7 @@ int main( int argc, char * argv[] )
|
||||||
|
|
||||||
catch ( std::exception& e)
|
catch ( std::exception& e)
|
||||||
{
|
{
|
||||||
|
slog.log( E_FATAL, "Caught exception.");
|
||||||
slog.log( E_FATAL, e.what() );
|
slog.log( E_FATAL, e.what() );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,108 @@
|
||||||
#include "Sproc.h"
|
#include "Sproc.h"
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <cstring>
|
#include <sys/wait.h>
|
||||||
#include <wait.h>
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include "../Logger/Logger.h"
|
||||||
|
|
||||||
|
int username_to_uid( std::string username, int & uid )
|
||||||
|
{
|
||||||
|
// assume failure unless proven otherwise
|
||||||
|
int r_code = false;
|
||||||
|
|
||||||
|
struct passwd * pw;
|
||||||
|
if ( ( pw = getpwnam( username.c_str() ) ) != NULL )
|
||||||
|
{
|
||||||
|
// successful user lookup
|
||||||
|
r_code = true;
|
||||||
|
uid = pw->pw_uid;
|
||||||
|
} else {
|
||||||
|
// failed lookup, do nothing, assumed failure
|
||||||
|
}
|
||||||
|
return r_code;
|
||||||
|
};
|
||||||
|
|
||||||
|
int groupname_to_gid( std::string groupname, int & gid )
|
||||||
|
{
|
||||||
|
int r_code = false;
|
||||||
|
|
||||||
|
struct group * gp;
|
||||||
|
if ( ( gp = getgrnam( groupname.c_str() ) ) != NULL )
|
||||||
|
{
|
||||||
|
r_code = true;
|
||||||
|
gid = gp->gr_gid;
|
||||||
|
} else {
|
||||||
|
// failed lookup, do nothing, assumed failure
|
||||||
|
}
|
||||||
|
return r_code;
|
||||||
|
}
|
||||||
|
|
||||||
/// Sproc::execute
|
/// Sproc::execute
|
||||||
///
|
///
|
||||||
/// \param input - The commandline input to execute.
|
/// \param input - The commandline input to execute.
|
||||||
/// \return - The return code of the execution of input in the calling shell.
|
/// \return - The return code of the execution of input in the calling shell.
|
||||||
int Sproc::execute(std::string input) {
|
int Sproc::execute(std::string run_as, std::string group, std::string command )
|
||||||
int child_exit_code = -666;
|
{
|
||||||
child_exit_code = system( input.c_str() );
|
Logger slog = Logger( E_INFO, "_sproc" );
|
||||||
child_exit_code = WEXITSTATUS( child_exit_code );
|
|
||||||
return child_exit_code;
|
// the run_as_uid to capture the run_as_uid to run as
|
||||||
|
int run_as_uid;
|
||||||
|
int run_as_gid;
|
||||||
|
|
||||||
|
slog.log( E_DEBUG, "Attempt: Running as user '" + run_as + "'.");
|
||||||
|
slog.log( E_DEBUG, "Attempt: Running as group '" + group + "'.");
|
||||||
|
|
||||||
|
if ( username_to_uid( run_as, run_as_uid ) )
|
||||||
|
{
|
||||||
|
slog.log( E_DEBUG, "UID of '" + run_as + "' is '" + std::to_string( run_as_uid ) + "'." );
|
||||||
|
} else {
|
||||||
|
slog.log( E_FATAL, "Failed to look up UID for '" + run_as + "'.");
|
||||||
|
return -404;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( groupname_to_gid( group, run_as_gid ) )
|
||||||
|
{
|
||||||
|
slog.log( E_DEBUG, "GID of '" + group + "' is '" + std::to_string( run_as_gid ) + "'." );
|
||||||
|
} else {
|
||||||
|
slog.log( E_FATAL, "Failed to look up DID for '" + group + "'.");
|
||||||
|
return -404;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if you get this return value, it's an issue with this method and not your
|
||||||
|
// called executable.
|
||||||
|
int exit_code_raw = -666;
|
||||||
|
|
||||||
|
// fork a process
|
||||||
|
int pid = fork();
|
||||||
|
|
||||||
|
if ( pid == 0 )
|
||||||
|
{
|
||||||
|
// child process
|
||||||
|
if ( seteuid( run_as_uid ) == 0 )
|
||||||
|
{
|
||||||
|
slog.log( E_DEBUG, "Successfully set UID to '" + std::to_string(run_as_uid) + "'." );
|
||||||
|
} else {
|
||||||
|
slog.log( E_FATAL, "Failed to set UID. Panicking." );
|
||||||
|
return -401;
|
||||||
|
}
|
||||||
|
if ( setegid( run_as_gid ) == 0 )
|
||||||
|
{
|
||||||
|
slog.log( E_DEBUG, "Successfully set GID to '" + std::to_string(run_as_gid) + "'." );
|
||||||
|
} else {
|
||||||
|
slog.log( E_FATAL, "Failed to set GID. Panicking." );
|
||||||
|
return -401;
|
||||||
|
}
|
||||||
|
exit_code_raw = system( command.c_str() );
|
||||||
|
exit( WEXITSTATUS( exit_code_raw ) );
|
||||||
|
} else if ( pid > 0 )
|
||||||
|
{
|
||||||
|
// parent process
|
||||||
|
while ( ( pid = waitpid( pid, &exit_code_raw, 0 ) ) == -1 ) {}
|
||||||
|
} else {
|
||||||
|
// fork failed
|
||||||
|
slog.log( E_FATAL, "Fork Failed");
|
||||||
|
}
|
||||||
|
return WEXITSTATUS( exit_code_raw );
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -23,13 +23,15 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "../Logger/Logger.h"
|
||||||
|
|
||||||
// executes a subprocess and captures STDOUT, STDERR, and return code.
|
// executes a subprocess and captures STDOUT, STDERR, and return code.
|
||||||
// should be able to recieve path of binary to be executed as well as any parameters
|
// should be able to recieve path of binary to be executed as well as any parameters
|
||||||
class Sproc {
|
class Sproc {
|
||||||
public:
|
public:
|
||||||
// call the object. returnvalue is enum representing external execution attempt not binary exit code
|
// call the object. returnvalue is enum representing external execution attempt not binary exit code
|
||||||
static int execute( std::string input );
|
static int execute(std::string run_as, std::string group, std::string command );
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FTESTS_SPROC_H
|
#endif //FTESTS_SPROC_H
|
||||||
|
|
|
@ -132,7 +132,7 @@ std::string Task::get_name()
|
||||||
void Task::load_definition( Unit selected_unit )
|
void Task::load_definition( Unit selected_unit )
|
||||||
{
|
{
|
||||||
this->definition = selected_unit;
|
this->definition = selected_unit;
|
||||||
this->slog.log( E_INFO, "Loaded definition \"" + selected_unit.get_name() + "\" for task \"" + this->get_name() + "\".");
|
this->slog.log( E_INFO, "Loaded definition \"" + selected_unit.get_name() + "\" as task in configured plan.");
|
||||||
this->defined = true;
|
this->defined = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ void Task::execute( Conf * configuration )
|
||||||
// END PREWORK
|
// END PREWORK
|
||||||
|
|
||||||
// get the target execution command
|
// get the target execution command
|
||||||
std::string target_command = configuration->get_execution_context() + + "/" + this->definition.get_target();
|
std::string target_command = this->definition.get_target();
|
||||||
|
|
||||||
// check if context override
|
// check if context override
|
||||||
if ( configuration->has_context_override() )
|
if ( configuration->has_context_override() )
|
||||||
|
@ -197,10 +197,18 @@ void Task::execute( Conf * configuration )
|
||||||
|
|
||||||
// a[0] execute target
|
// a[0] execute target
|
||||||
// TODO revise variable sourcing strategy
|
// TODO revise variable sourcing strategy
|
||||||
|
// ....sourcing on the shell for variables and environment population doesn't have a good smell.
|
||||||
|
|
||||||
this->slog.log( E_INFO, "Executing target: \"" + target_command + "\"." );
|
this->slog.log( E_INFO, "Executing target: \"" + target_command + "\"." );
|
||||||
|
if ( exists( target_command ) )
|
||||||
|
{
|
||||||
|
this->slog.log( E_DEBUG, "Executable exists.");
|
||||||
|
} else {
|
||||||
|
this->slog.log( E_FATAL, "Executable does not exist." );
|
||||||
|
throw Task_NotReady();
|
||||||
|
}
|
||||||
this->slog.log( E_DEBUG, "Vars file: " + configuration->get_env_vars_file() );
|
this->slog.log( E_DEBUG, "Vars file: " + configuration->get_env_vars_file() );
|
||||||
int return_code = Sproc::execute( ". " + configuration->get_env_vars_file() + " && " + target_command );
|
int return_code = Sproc::execute( this->definition.get_user(), this->definition.get_group(), ". " + configuration->get_env_vars_file() + " && " + target_command );
|
||||||
|
|
||||||
// **********************************************
|
// **********************************************
|
||||||
// d[0] Error Code Check
|
// d[0] Error Code Check
|
||||||
|
@ -259,7 +267,7 @@ void Task::execute( Conf * configuration )
|
||||||
|
|
||||||
this->slog.log( E_INFO, "Executing rectification: " + rectifier_command + "." );
|
this->slog.log( E_INFO, "Executing rectification: " + rectifier_command + "." );
|
||||||
|
|
||||||
int rectifier_error = Sproc::execute( "source " + configuration->get_env_vars_file() + " && " + rectifier_command );
|
int rectifier_error = Sproc::execute( this->definition.get_user(), this->definition.get_group(), ". " + configuration->get_env_vars_file() + " && " + rectifier_command );
|
||||||
|
|
||||||
// **********************************************
|
// **********************************************
|
||||||
// d[3] Error Code Check for Rectifier
|
// d[3] Error Code Check for Rectifier
|
||||||
|
@ -300,7 +308,7 @@ void Task::execute( Conf * configuration )
|
||||||
// a[7] Re-execute Target
|
// a[7] Re-execute Target
|
||||||
this->slog.log( E_INFO, "Re-Executing target \"" + this->definition.get_target() + "\"." );
|
this->slog.log( E_INFO, "Re-Executing target \"" + this->definition.get_target() + "\"." );
|
||||||
|
|
||||||
int retry_code = Sproc::execute( "source " + configuration->get_env_vars_file() + " && " + target_command );
|
int retry_code = Sproc::execute( this->definition.get_user(), this->definition.get_group(), ". " + configuration->get_env_vars_file() + " && " + target_command );
|
||||||
|
|
||||||
// **********************************************
|
// **********************************************
|
||||||
// d[5] Error Code Check
|
// d[5] Error Code Check
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
|
||||||
/// Unit_NotPopulated - Meant to be thrown when a Unit type is not populated before being used.
|
/// Unit_NotPopulated - Meant to be thrown when a Unit type is not populated before being used.
|
||||||
/// Signaled by use of the 'populated' boolean member of the Unit class.
|
/// Signaled by use of the 'populated' boolean member of the Unit class.
|
||||||
|
@ -30,6 +32,11 @@ class Unit_NotPopulated: public std::runtime_error { public:
|
||||||
Unit_NotPopulated(): std::runtime_error("Unit: Attempted to access a member before loading values.") {}
|
Unit_NotPopulated(): std::runtime_error("Unit: Attempted to access a member before loading values.") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// EnvironmentErrorFatal - Meant to be thrown when the environment is too broken for Examplar to do its job.
|
||||||
|
class EnvironmentErrorFatal: public std::runtime_error { public:
|
||||||
|
EnvironmentErrorFatal(): std::runtime_error("Unit: Environment is too broken to continue.") {}
|
||||||
|
};
|
||||||
|
|
||||||
/// Unit_DataStructureException - Meant to be thrown when a Unit type is accessing a member that does not exist.
|
/// Unit_DataStructureException - Meant to be thrown when a Unit type is accessing a member that does not exist.
|
||||||
class Unit_DataStructureException: public std::runtime_error { public:
|
class Unit_DataStructureException: public std::runtime_error { public:
|
||||||
// TODO rework this to accept the key name being fetched
|
// TODO rework this to accept the key name being fetched
|
||||||
|
@ -83,6 +90,44 @@ int Unit::load_root(Json::Value loader_root)
|
||||||
if ( loader_root.isMember("rectify") )
|
if ( loader_root.isMember("rectify") )
|
||||||
{ this->rectify = loader_root.get("rectify", errmsg).asBool(); } else throw Unit_DataStructureException();
|
{ this->rectify = loader_root.get("rectify", errmsg).asBool(); } else throw Unit_DataStructureException();
|
||||||
|
|
||||||
|
// TODO functionize this
|
||||||
|
char * lgn;
|
||||||
|
std::string errmsg_user;
|
||||||
|
|
||||||
|
// if no user field is specified then default to the currently executing user
|
||||||
|
if ( ( lgn = getlogin() ) == NULL )
|
||||||
|
{
|
||||||
|
throw EnvironmentErrorFatal();
|
||||||
|
} else {
|
||||||
|
errmsg_user = lgn;
|
||||||
|
}
|
||||||
|
// -TODO
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if ( loader_root.isMember( "user" ) )
|
||||||
|
{ this->user = loader_root.get( "user", errmsg_user ).asString(); } else this->user = lgn;
|
||||||
|
|
||||||
|
|
||||||
|
// TODO functionalize this
|
||||||
|
// get the current context gid as a backup value
|
||||||
|
int gid = getgid();
|
||||||
|
// declare the grp object to pull the name from once populated
|
||||||
|
struct group * grp;
|
||||||
|
// storage for backup value once retrieved
|
||||||
|
std::string errmsg_group;
|
||||||
|
|
||||||
|
// get the backup value and store it to errmsg_group
|
||||||
|
if ( ( grp = getgrgid( gid ) ) == NULL )
|
||||||
|
{
|
||||||
|
throw EnvironmentErrorFatal();
|
||||||
|
} else {
|
||||||
|
errmsg_group = grp->gr_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( loader_root.isMember( "group" ) )
|
||||||
|
{ this->group = loader_root.get( "group", errmsg_group ).asString(); } else this->group = grp->gr_name;
|
||||||
|
|
||||||
this->populated = true;
|
this->populated = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -164,4 +209,21 @@ bool Unit::get_rectify()
|
||||||
{
|
{
|
||||||
if ( ! this->populated ) { throw Unit_NotPopulated(); }
|
if ( ! this->populated ) { throw Unit_NotPopulated(); }
|
||||||
return this->rectify;
|
return this->rectify;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unit::get_user - retrieves the user context for the unit.
|
||||||
|
///
|
||||||
|
/// \return the string value of the user name.
|
||||||
|
std::string Unit::get_user()
|
||||||
|
{
|
||||||
|
if ( ! this->populated ) { throw Unit_NotPopulated(); }
|
||||||
|
return this->user;
|
||||||
|
}
|
||||||
|
/// Unit::get_group - retrieves the group context for the unit.
|
||||||
|
///
|
||||||
|
/// \return the string value of the group name.
|
||||||
|
std::string Unit::get_group()
|
||||||
|
{
|
||||||
|
if ( ! this->populated ) { throw Unit_NotPopulated(); }
|
||||||
|
return this->group;
|
||||||
}
|
}
|
|
@ -59,6 +59,14 @@ private:
|
||||||
// if rectifier exits on non-zero return code, it should be trigger the behaviour indicated by required
|
// if rectifier exits on non-zero return code, it should be trigger the behaviour indicated by required
|
||||||
bool rectify;
|
bool rectify;
|
||||||
|
|
||||||
|
// user to run process as.
|
||||||
|
// not intended for protected accounts, handle your own security
|
||||||
|
std::string user;
|
||||||
|
|
||||||
|
// group to run process as.
|
||||||
|
// not intended for protected accounts, handle your own security
|
||||||
|
std::string group;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Unit( int LOG_LEVEL );
|
Unit( int LOG_LEVEL );
|
||||||
|
|
||||||
|
@ -76,6 +84,8 @@ public:
|
||||||
bool get_active();
|
bool get_active();
|
||||||
bool get_required();
|
bool get_required();
|
||||||
bool get_rectify();
|
bool get_rectify();
|
||||||
|
std::string get_user();
|
||||||
|
std::string get_group();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int LOG_LEVEL;
|
int LOG_LEVEL;
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
echo "dependent test"
|
||||||
|
exit $?
|
|
@ -0,0 +1,11 @@
|
||||||
|
whoami
|
||||||
|
id
|
||||||
|
|
||||||
|
touch /home/bagira/testfile
|
||||||
|
|
||||||
|
stat /home/bagira/testfile
|
||||||
|
|
||||||
|
#dialog --stdout --title "Interact with me!" \
|
||||||
|
# --backtitle "This is user interaction." \
|
||||||
|
# --yesno "Yes: pass, No: fail" 7 60
|
||||||
|
exit $?
|
|
@ -0,0 +1,2 @@
|
||||||
|
echo "independent test 2 output"
|
||||||
|
exit $?
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"execution_context_override": true,
|
"execution_context_override": true,
|
||||||
"execution_context": "/home/bagira/development/internal/examplar/test",
|
"execution_context": "/home/bagira/development/internal/examplar/test",
|
||||||
"units_path": "units/all_test.units",
|
"units_path": "units/",
|
||||||
"plan_path": "plans/test.plan",
|
"plan_path": "plans/atomic.plan",
|
||||||
"config_version": "3",
|
"config_version": "3",
|
||||||
"env_vars_file": "examplar.variables"
|
"env_vars_file": "examplar.variables"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"plan": [
|
||||||
|
{ "name": "independent test 1", "dependencies": [ null ] }
|
||||||
|
]
|
||||||
|
}
|
|
@ -2,35 +2,23 @@
|
||||||
"units": [
|
"units": [
|
||||||
{
|
{
|
||||||
"name": "independent test 1",
|
"name": "independent test 1",
|
||||||
"target": "/usr/bin/true",
|
"target": "components/independent_test_1.bash",
|
||||||
"rectifier": "/usr/bin/true",
|
"rectifier": "",
|
||||||
"active": true,
|
"active": true,
|
||||||
"required": true,
|
"required": true,
|
||||||
"rectify": true
|
"user": "root",
|
||||||
},
|
"group": "root",
|
||||||
{
|
|
||||||
"name": "independent test 2",
|
|
||||||
"target": "/usr/bin/true",
|
|
||||||
"rectifier": "/usr/bin/true",
|
|
||||||
"active": true,
|
|
||||||
"required": false,
|
|
||||||
"rectify": false
|
"rectify": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "A DEFINITION THAT IS NOT USED",
|
"name": "independent test 2",
|
||||||
"target": "/usr/bin/dialog --yesno test 50 50",
|
"target": "components/independent_test_2.bash",
|
||||||
"rectifier": "/usr/bin/false",
|
"rectifier": "",
|
||||||
"active": false,
|
|
||||||
"required": false,
|
|
||||||
"rectify": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "dependent test",
|
|
||||||
"target": "/usr/bin/false",
|
|
||||||
"rectifier": "/usr/bin/true",
|
|
||||||
"active": true,
|
"active": true,
|
||||||
"required": true,
|
"required": false,
|
||||||
"rectify": true
|
"user": "bagira",
|
||||||
|
"group": "bagira",
|
||||||
|
"rectify": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,17 +4,19 @@
|
||||||
"name": "A DEFINITION THAT IS NOT USED",
|
"name": "A DEFINITION THAT IS NOT USED",
|
||||||
"target": "/usr/bin/dialog --yesno test 50 50",
|
"target": "/usr/bin/dialog --yesno test 50 50",
|
||||||
"rectifier": "/usr/bin/false",
|
"rectifier": "/usr/bin/false",
|
||||||
"active": true,
|
"active": false,
|
||||||
"required": true,
|
"required": true,
|
||||||
"rectify": true
|
"rectify": true,
|
||||||
|
"user": "root"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dependent test",
|
"name": "dependent test",
|
||||||
"target": "/usr/bin/false",
|
"target": "/usr/bin/false",
|
||||||
"rectifier": "/usr/bin/true",
|
"rectifier": "/usr/bin/true",
|
||||||
"active": true,
|
"active": false,
|
||||||
"required": true,
|
"required": true,
|
||||||
"rectify": true
|
"rectify": true,
|
||||||
|
"user": "root"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue