Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 90 additions & 7 deletions cf-reactor/cf-reactor.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@
#include <writer.h>
#include <config.h>
#include <generic_agent.h>
#include <loading.h> /* LoadPolicy */
#include <bootstrap.h> /* UpdateLastPolicyUpdateTime */
#include <known_dirs.h> /* GetInputDir */
#include <man.h>
#include <cleanup.h>
#include <prototypes3.h>
#include <signal.h> /* signal, kill */
#include <signals.h> /* GetSignalPipe, MakeSignalPipe, IsPendingTermination, HandleSignalsForDaemon */
#include <signals.h> /* GetSignalPipe, MakeSignalPipe, IsPendingTermination, HandleSignalsForDaemon, ReloadConfigRequested, ClearRequestReloadConfig */
#include <exec_tools.h>
#include <reactor_context.h>

Expand All @@ -49,6 +52,7 @@ int NO_FORK = false;
/*****************************************************************************/

#define DEFAULT_POLL_INTERVAL_SECS 30
#define DEFAULT_POLICY_CHECK_INTERVAL_SECS (5 * 60)

/*******************************************************************/
/* Command line options */
Expand All @@ -70,6 +74,7 @@ static const char *const CF_REACTOR_MANPAGE_LONG_DESCRIPTION =
static const struct option OPTIONS[] =
{
{"debug", no_argument, 0, 'd'},
{"file", required_argument, 0, 'f'},
{"no-fork", no_argument, 0, 'F'},
{"log-level", required_argument, 0, 'g'},
{"help", no_argument, 0, 'h'},
Expand All @@ -84,6 +89,7 @@ static const struct option OPTIONS[] =
static const char *const HINTS[] =
{
"Enable debugging output and run in foreground",
"Specify an alternative input file than the default. This option is overridden by FILE if supplied as argument.",
"Run as a foreground process (do not fork)",
"Specify how detailed logs should be. Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug'",
"Print the help message",
Expand All @@ -102,11 +108,15 @@ static GenericAgentConfig *CheckOpts(int argc, char **argv)
GenericAgentConfig *config = GenericAgentConfigNewDefault(AGENT_TYPE_REACTOR, GetTTYInteractive());

int longopt_idx;
while ((c = getopt_long(argc, argv, "dFg:hIlMvV",
while ((c = getopt_long(argc, argv, "df:Fg:hIlMvV",
OPTIONS, &longopt_idx)) != -1)
{
switch (c)
{
case 'f':
GenericAgentConfigSetInputFile(config, GetInputDir(), optarg);
MINUSF = true;
break;
case 'd':
LogSetGlobalLevel(LOG_LEVEL_DEBUG);
NO_FORK = true;
Expand Down Expand Up @@ -190,13 +200,75 @@ static GenericAgentConfig *CheckOpts(int argc, char **argv)

/*****************************************************************************/

static void CheckPolicyUpdates(EvalContext *ctx, Policy **policy, GenericAgentConfig *config)
{
assert(config != NULL);

time_t validated_at = ReadTimestampFromPolicyValidatedFile(config, NULL);

bool reload_policy = false;
if (config->agent_specific.daemon.last_validated_at < validated_at)
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
{
Log(LOG_LEVEL_VERBOSE, "Policy changes detected by cf-reactor, reloading policy");
reload_policy = true;
}
if (ReloadConfigRequested())
{
Log(LOG_LEVEL_VERBOSE, "Policy reload requested, reloading policy");
reload_policy = true;
}

if (!reload_policy)
{
Log(LOG_LEVEL_DEBUG, "No policy changes seen by cf-reactor");
return;
}

ClearRequestReloadConfig();
config->agent_specific.daemon.last_validated_at = validated_at;
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

if (!GenericAgentArePromisesValid(config))
{
Log(LOG_LEVEL_INFO, "Policy file '%s' has errors, cf-reactor keeping the previous policy",
config->input_file);
return;
}

Log(LOG_LEVEL_NOTICE, "Rereading policy file '%s'", config->input_file);
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

EvalContextClear(ctx);
PolicyDestroy(*policy);
*policy = NULL;

UpdateLastPolicyUpdateTime(ctx);
GenericAgentDiscoverContext(ctx, config, NULL);

*policy = LoadPolicy(ctx, config);
}

/*****************************************************************************/


int main(int argc, char *argv[])
{
GenericAgentConfig *config = CheckOpts(argc, argv);
EvalContext *ctx = EvalContextNew();
GenericAgentConfigApply(ctx, config);

const char *program_invocation_name = argv[0];
const char *last_dir_sep = strrchr(program_invocation_name, FILE_SEPARATOR);
const char *program_name = (last_dir_sep != NULL ? last_dir_sep + 1 : program_invocation_name);
GenericAgentDiscoverContext(ctx, config, program_name);

Policy *policy = SelectAndLoadPolicy(config, ctx, false, false);
if (policy == NULL)
{
Log(LOG_LEVEL_ERR, "Error reading CFEngine policy. Exiting...");
DoCleanupAndExit(EXIT_FAILURE);
}

GenericAgentPostLoadInit(ctx);

#ifdef __MINGW32__

if (!NO_FORK)
Expand Down Expand Up @@ -253,15 +325,17 @@ int main(int argc, char *argv[])
/* We need an initial value here for the first iteration of the cycle
* below. */
time_t next_tick = time(NULL) + DEFAULT_POLL_INTERVAL_SECS;
time_t next_policy_check = time(NULL) + DEFAULT_POLICY_CHECK_INTERVAL_SECS;
while (!IsPendingTermination())
{
int max_fd = ReactorContextSetupFileDescriptors(&reactor_ctx);

/* Determine how much time is remaining until the next tick. */
time_t last_tick = time(NULL);
time_t remaining = next_tick > last_tick ? next_tick - last_tick : 0;
time_t remaining_tick = next_tick > last_tick ? next_tick - last_tick : 0;
time_t remaining_policy_check = next_policy_check > last_tick ? next_policy_check - last_tick : 0;

struct timeval timeout = { .tv_sec = remaining };
struct timeval timeout = { .tv_sec = MIN(remaining_tick, remaining_policy_check) };
int ret = select(max_fd, &reactor_ctx.readfds, NULL, NULL, &timeout);

/* Reschedule the backstop tick against the current time (not
Expand Down Expand Up @@ -290,14 +364,23 @@ int main(int argc, char *argv[])
{
/*** timeout ***/
ReactorNovaHandleTimeout(&next_tick);
continue;
}
/* else */
else
{
ReactorContextHandleEvents(&reactor_ctx, &next_tick);
}


ReactorContextHandleEvents(&reactor_ctx, &next_tick);
time_t now = time(NULL);
if (now >= next_policy_check || ReloadConfigRequested())
{
CheckPolicyUpdates(ctx, &policy, config);
next_policy_check = now + DEFAULT_POLICY_CHECK_INTERVAL_SECS;
}
}
ReactorContextFinalize(&reactor_ctx);

PolicyDestroy(policy);
GenericAgentFinalize(ctx, config);
CallCleanupFunctions();

Expand Down
7 changes: 7 additions & 0 deletions libpromises/cf3parse_logic.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,13 @@ static bool RelevantBundle(const char *agent, const char *blocktype)
return true;
}

// cf-reactor should keep agent bundles
if (StringEqual(agent, CF_AGENTTYPES[AGENT_TYPE_REACTOR])
&& StringEqual(blocktype, CF_AGENTTYPES[AGENT_TYPE_AGENT]))
{
return true;
}

/* Here are some additional bundle types handled by cfAgent */

Item *ip = SplitString("edit_line,edit_xml", ',');
Expand Down
5 changes: 4 additions & 1 deletion libpromises/generic_agent.c
Original file line number Diff line number Diff line change
Expand Up @@ -1194,12 +1194,15 @@ static bool IsPolicyPrecheckNeeded(GenericAgentConfig *config, bool force_valida

bool GenericAgentCheckPolicy(GenericAgentConfig *config, bool force_validation, bool write_validated_file)
{
assert(config != NULL);

if (!MissingInputFile(config->input_file))
{
{
if (config->agent_type == AGENT_TYPE_SERVER ||
config->agent_type == AGENT_TYPE_MONITOR ||
config->agent_type == AGENT_TYPE_EXECUTOR)
config->agent_type == AGENT_TYPE_EXECUTOR ||
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
config->agent_type == AGENT_TYPE_REACTOR)
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
{
time_t validated_at = ReadTimestampFromPolicyValidatedFile(config, NULL);
config->agent_specific.daemon.last_validated_at = validated_at;
Expand Down
Loading