diff --git a/cf-reactor/cf-reactor.c b/cf-reactor/cf-reactor.c index a97252aadd..a0baffe28d 100644 --- a/cf-reactor/cf-reactor.c +++ b/cf-reactor/cf-reactor.c @@ -34,6 +34,16 @@ #include #include +/*****************************************************************************/ +/* Globals */ +/*****************************************************************************/ + +int NO_FORK = false; + +/*******************************************************************/ +/* Command line options */ +/*******************************************************************/ + static const Component COMPONENT = { .name = "cf-reactor", @@ -44,7 +54,8 @@ static const Component COMPONENT = static const char *const CF_REACTOR_SHORT_DESCRIPTION = "CFEngine event reaction daemon"; static const char *const CF_REACTOR_MANPAGE_LONG_DESCRIPTION = - "cf-reactor is a daemon reacting on events, currently only NOTIFY events from PostgreSQL."; + "cf-reactor is a daemon reacting on events. It watches specific events defined in the policy " + "and runs policy code on reaction to these events."; static const struct option OPTIONS[] = { @@ -54,6 +65,7 @@ static const struct option OPTIONS[] = {"help", no_argument, 0, 'h'}, {"inform", no_argument, 0, 'I'}, {"timestamp", no_argument, 0, 'l'}, + {"man", no_argument, 0, 'M'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {NULL, 0, 0, '\0'} @@ -72,76 +84,111 @@ static const char *const HINTS[] = NULL }; -int main(int argc, char *argv[]) +static GenericAgentConfig *CheckOpts(int argc, char **argv) { - bool no_fork = false; - extern char *optarg; - int longopt_idx; int c; + GenericAgentConfig *config = GenericAgentConfigNewDefault(AGENT_TYPE_REACTOR, GetTTYInteractive()); + + int longopt_idx; while ((c = getopt_long(argc, argv, "dFg:hIlMvV", - OPTIONS, &longopt_idx)) - != -1) + OPTIONS, &longopt_idx)) != -1) { switch (c) { case 'd': LogSetGlobalLevel(LOG_LEVEL_DEBUG); - no_fork = true; + NO_FORK = true; break; - case 'F': - no_fork = true; + case 'I': + LogSetGlobalLevel(LOG_LEVEL_INFO); + break; + + case 'v': + LogSetGlobalLevel(LOG_LEVEL_VERBOSE); + NO_FORK = true; break; case 'g': LogSetGlobalLevelArgOrExit(optarg); break; + case 'F': + NO_FORK = true; + break; + + case 'V': + { + Writer *w = FileWriter(stdout); + GenericAgentWriteVersion(w); + FileWriterDetach(w); + } + DoCleanupAndExit(EXIT_SUCCESS); + case 'h': - { - Writer *w = FileWriter(stdout); - WriterWriteHelp(w, &COMPONENT, OPTIONS, HINTS, NULL, false, true); - FileWriterDetach(w); - } - DoCleanupAndExit(EXIT_SUCCESS); + { + Writer *w = FileWriter(stdout); + WriterWriteHelp(w, &COMPONENT, OPTIONS, HINTS, NULL, false, true); + FileWriterDetach(w); + } + DoCleanupAndExit(EXIT_SUCCESS); - case 'I': - LogSetGlobalLevel(LOG_LEVEL_INFO); - break; + case 'M': + { + Writer *out = FileWriter(stdout); + ManPageWrite(out, "cf-watchd", time(NULL), + CF_REACTOR_SHORT_DESCRIPTION, + CF_REACTOR_MANPAGE_LONG_DESCRIPTION, + OPTIONS, HINTS, + NULL, false, + true); + FileWriterDetach(out); + DoCleanupAndExit(EXIT_SUCCESS); + } case 'l': LoggingEnableTimestamps(true); break; - case 'M': - { - Writer *out = FileWriter(stdout); - ManPageWrite(out, "cf-reactor", time(NULL), - CF_REACTOR_SHORT_DESCRIPTION, - CF_REACTOR_MANPAGE_LONG_DESCRIPTION, - OPTIONS, HINTS, - NULL, false, - true); - FileWriterDetach(out); - DoCleanupAndExit(EXIT_SUCCESS); - } - - case 'v': - LogSetGlobalLevel(LOG_LEVEL_VERBOSE); - no_fork = true; + /* long options only */ + case 0: + { + // TODO: handle long options if they are added in the future break; + } - case 'V': - { - Writer *w = FileWriter(stdout); - GenericAgentWriteVersion(w); - FileWriterDetach(w); - } - DoCleanupAndExit(EXIT_SUCCESS); - + default: + { + Writer *w = FileWriter(stdout); + WriterWriteHelp(w, &COMPONENT, OPTIONS, HINTS, NULL, false, true); + FileWriterDetach(w); + } + DoCleanupAndExit(EXIT_FAILURE); } } - return ReactorEnterpriseMain(no_fork); + if (!GenericAgentConfigParseArguments(config, argc - optind, argv + optind)) + { + Log(LOG_LEVEL_ERR, "Too many arguments"); + DoCleanupAndExit(EXIT_FAILURE); + } + + return config; +} + +/*****************************************************************************/ + +int main(int argc, char *argv[]) +{ + GenericAgentConfig *config = CheckOpts(argc, argv); + EvalContext *ctx = EvalContextNew(); + GenericAgentConfigApply(ctx, config); + + int ret = ReactorEnterpriseMain(NO_FORK); + + GenericAgentFinalize(ctx, config); + CallCleanupFunctions(); + + return ret; } diff --git a/libpromises/Makefile.am b/libpromises/Makefile.am index 8f60e0f501..48c21a6635 100644 --- a/libpromises/Makefile.am +++ b/libpromises/Makefile.am @@ -135,6 +135,7 @@ libpromises_la_SOURCES = \ mod_knowledge.c mod_knowledge.h \ mod_users.c mod_users.h \ mod_watch.c mod_watch.h \ + mod_reactor.c mod_reactor.h \ modes.c \ monitoring_read.c monitoring_read.h \ ornaments.c ornaments.h \ diff --git a/libpromises/cf3.defs.h b/libpromises/cf3.defs.h index af05798119..0a1d94fabc 100644 --- a/libpromises/cf3.defs.h +++ b/libpromises/cf3.defs.h @@ -399,6 +399,7 @@ typedef enum #define CF_KEYGEN "keygenerator" #define CF_HUBC "hub" #define CF_WATCHC "watch" +#define CF_REACTORC "reactor" typedef enum { @@ -411,6 +412,7 @@ typedef enum AGENT_TYPE_KEYGEN, AGENT_TYPE_HUB, AGENT_TYPE_WATCH, + AGENT_TYPE_REACTOR, AGENT_TYPE_NOAGENT } AgentType; diff --git a/libpromises/constants.c b/libpromises/constants.c index 3844ede6b0..f5abd75432 100644 --- a/libpromises/constants.c +++ b/libpromises/constants.c @@ -73,6 +73,7 @@ const char *const CF_AGENTTYPES[] = /* see enum cfagenttype */ CF_KEYGEN, CF_HUBC, CF_WATCHC, + CF_REACTORC, "", }; diff --git a/libpromises/mod_common.c b/libpromises/mod_common.c index b85ad5b46b..3412877531 100644 --- a/libpromises/mod_common.c +++ b/libpromises/mod_common.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -551,6 +552,7 @@ const PromiseTypeSyntax *const CF_ALL_PROMISE_TYPES[] = CF_KNOWLEDGE_PROMISE_TYPES, /* mod_knowledge.c */ CF_USERS_PROMISE_TYPES, /* mod_users.c */ CF_WATCH_PROMISE_TYPES, /* mod_watch.c */ + CF_REACTOR_PROMISE_TYPES /* mod_reactor.c */ }; const int CF3_MODULES = (sizeof(CF_ALL_PROMISE_TYPES) / sizeof(CF_ALL_PROMISE_TYPES[0])); diff --git a/libpromises/mod_reactor.c b/libpromises/mod_reactor.c new file mode 100644 index 0000000000..c5a1d738ce --- /dev/null +++ b/libpromises/mod_reactor.c @@ -0,0 +1,51 @@ +/* + Copyright 2026 Northern.tech AS + + This file is part of CFEngine 3 - written and maintained by Northern.tech AS. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + + To the extent this program is licensed as part of the Enterprise + versions of CFEngine, the applicable Commercial Open Source License + (COSL) may apply to this file if you as a licensee so wish it. See + included file COSL.txt. +*/ + +#include + +#include + +static const ConstraintSyntax when_constraints[] = +{ + CONSTRAINT_SYNTAX_GLOBAL, + + /* Row models */ + ConstraintSyntaxNewStringList("files_deleted", CF_ANYSTRING, "List of files to react for on deletion", SYNTAX_STATUS_NORMAL), + ConstraintSyntaxNewNull() +}; + +static const BodySyntax when_body = BodySyntaxNew("when", when_constraints, NULL, SYNTAX_STATUS_NORMAL); + +static const ConstraintSyntax CF_EVENT_BODIES[] = +{ + ConstraintSyntaxNewBody("when", &when_body, "Event to react to", SYNTAX_STATUS_NORMAL), + ConstraintSyntaxNewBundle("then", "Bundle to run on event", SYNTAX_STATUS_NORMAL), + ConstraintSyntaxNewNull() +}; + +const PromiseTypeSyntax CF_REACTOR_PROMISE_TYPES[] = +{ + PromiseTypeSyntaxNew("reactor", "events", CF_EVENT_BODIES, NULL, SYNTAX_STATUS_NORMAL), + PromiseTypeSyntaxNewNull() +}; diff --git a/libpromises/mod_reactor.h b/libpromises/mod_reactor.h new file mode 100644 index 0000000000..72842f3504 --- /dev/null +++ b/libpromises/mod_reactor.h @@ -0,0 +1,32 @@ +/* + Copyright 2026 Northern.tech AS + + This file is part of CFEngine 3 - written and maintained by Northern.tech AS. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + + To the extent this program is licensed as part of the Enterprise + versions of CFEngine, the applicable Commercial Open Source License + (COSL) may apply to this file if you as a licensee so wish it. See + included file COSL.txt. +*/ + +#ifndef CFENGINE_MOD_REACTOR_H +#define CFENGINE_MOD_REACTOR_H + +#include + +extern const PromiseTypeSyntax CF_REACTOR_PROMISE_TYPES[]; + +#endif