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 plan_file = configuration.get_plan_path();
|
||||
|
||||
slog.log( E_DEBUG, "* Initialising suite (definition library).");
|
||||
Suite available_definitions = Suite( L_LEVEL );
|
||||
|
||||
slog.log( E_INFO, "* Loading all actionable units into suite." );
|
||||
available_definitions.load_units_file( definitions_file );
|
||||
|
||||
slog.log( E_DEBUG, "* Initialising plan." );
|
||||
Plan plan = Plan( &configuration, L_LEVEL );
|
||||
|
||||
slog.log( E_INFO, "* Loading plan outline.");
|
||||
plan.load_plan_file( plan_file );
|
||||
|
||||
slog.log( E_INFO, "* Loading planned tasks from suite to plan." );
|
||||
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
|
||||
{
|
||||
|
@ -154,6 +161,7 @@ int main( int argc, char * argv[] )
|
|||
|
||||
catch ( std::exception& e)
|
||||
{
|
||||
slog.log( E_FATAL, "Caught exception.");
|
||||
slog.log( E_FATAL, e.what() );
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,108 @@
|
|||
#include "Sproc.h"
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <wait.h>
|
||||
#include <sys/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
|
||||
///
|
||||
/// \param input - The commandline input to execute.
|
||||
/// \return - The return code of the execution of input in the calling shell.
|
||||
int Sproc::execute(std::string input) {
|
||||
int child_exit_code = -666;
|
||||
child_exit_code = system( input.c_str() );
|
||||
child_exit_code = WEXITSTATUS( child_exit_code );
|
||||
return child_exit_code;
|
||||
int Sproc::execute(std::string run_as, std::string group, std::string command )
|
||||
{
|
||||
Logger slog = Logger( E_INFO, "_sproc" );
|
||||
|
||||
// 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 <iostream>
|
||||
#include <stdio.h>
|
||||
#include "../Logger/Logger.h"
|
||||
|
||||
// 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
|
||||
class Sproc {
|
||||
public:
|
||||
// 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
|
||||
|
|
|
@ -132,7 +132,7 @@ std::string Task::get_name()
|
|||
void Task::load_definition( Unit 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;
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,7 @@ void Task::execute( Conf * configuration )
|
|||
// END PREWORK
|
||||
|
||||
// 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
|
||||
if ( configuration->has_context_override() )
|
||||
|
@ -197,10 +197,18 @@ void Task::execute( Conf * configuration )
|
|||
|
||||
// a[0] execute target
|
||||
// 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 + "\"." );
|
||||
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() );
|
||||
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
|
||||
|
@ -259,7 +267,7 @@ void Task::execute( Conf * configuration )
|
|||
|
||||
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
|
||||
|
@ -300,7 +308,7 @@ void Task::execute( Conf * configuration )
|
|||
// a[7] Re-execute 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
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
||||
/// 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.
|
||||
|
@ -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.") {}
|
||||
};
|
||||
|
||||
/// 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.
|
||||
class Unit_DataStructureException: public std::runtime_error { public:
|
||||
// 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") )
|
||||
{ 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;
|
||||
|
||||
return 0;
|
||||
|
@ -164,4 +209,21 @@ bool Unit::get_rectify()
|
|||
{
|
||||
if ( ! this->populated ) { throw Unit_NotPopulated(); }
|
||||
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
|
||||
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:
|
||||
Unit( int LOG_LEVEL );
|
||||
|
||||
|
@ -76,6 +84,8 @@ public:
|
|||
bool get_active();
|
||||
bool get_required();
|
||||
bool get_rectify();
|
||||
std::string get_user();
|
||||
std::string get_group();
|
||||
|
||||
private:
|
||||
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": "/home/bagira/development/internal/examplar/test",
|
||||
"units_path": "units/all_test.units",
|
||||
"plan_path": "plans/test.plan",
|
||||
"units_path": "units/",
|
||||
"plan_path": "plans/atomic.plan",
|
||||
"config_version": "3",
|
||||
"env_vars_file": "examplar.variables"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"plan": [
|
||||
{ "name": "independent test 1", "dependencies": [ null ] }
|
||||
]
|
||||
}
|
|
@ -2,35 +2,23 @@
|
|||
"units": [
|
||||
{
|
||||
"name": "independent test 1",
|
||||
"target": "/usr/bin/true",
|
||||
"rectifier": "/usr/bin/true",
|
||||
"target": "components/independent_test_1.bash",
|
||||
"rectifier": "",
|
||||
"active": true,
|
||||
"required": true,
|
||||
"rectify": true
|
||||
},
|
||||
{
|
||||
"name": "independent test 2",
|
||||
"target": "/usr/bin/true",
|
||||
"rectifier": "/usr/bin/true",
|
||||
"active": true,
|
||||
"required": false,
|
||||
"user": "root",
|
||||
"group": "root",
|
||||
"rectify": false
|
||||
},
|
||||
{
|
||||
"name": "A DEFINITION THAT IS NOT USED",
|
||||
"target": "/usr/bin/dialog --yesno test 50 50",
|
||||
"rectifier": "/usr/bin/false",
|
||||
"active": false,
|
||||
"required": false,
|
||||
"rectify": true
|
||||
},
|
||||
{
|
||||
"name": "dependent test",
|
||||
"target": "/usr/bin/false",
|
||||
"rectifier": "/usr/bin/true",
|
||||
"name": "independent test 2",
|
||||
"target": "components/independent_test_2.bash",
|
||||
"rectifier": "",
|
||||
"active": true,
|
||||
"required": true,
|
||||
"rectify": true
|
||||
"required": false,
|
||||
"user": "bagira",
|
||||
"group": "bagira",
|
||||
"rectify": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -4,17 +4,19 @@
|
|||
"name": "A DEFINITION THAT IS NOT USED",
|
||||
"target": "/usr/bin/dialog --yesno test 50 50",
|
||||
"rectifier": "/usr/bin/false",
|
||||
"active": true,
|
||||
"active": false,
|
||||
"required": true,
|
||||
"rectify": true
|
||||
"rectify": true,
|
||||
"user": "root"
|
||||
},
|
||||
{
|
||||
"name": "dependent test",
|
||||
"target": "/usr/bin/false",
|
||||
"rectifier": "/usr/bin/true",
|
||||
"active": true,
|
||||
"active": false,
|
||||
"required": true,
|
||||
"rectify": true
|
||||
"rectify": true,
|
||||
"user": "root"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue