From f9814324163c6019849569b7456d3ae0c4af4040 Mon Sep 17 00:00:00 2001 From: Victor Moene Date: Tue, 4 Aug 2026 12:49:32 +0200 Subject: [PATCH 1/2] Moved cf-reactor binary to core Changelog: Title Ticket: ENT-14275 Signed-off-by: Victor Moene --- .gitignore | 3 + Makefile.am | 1 + cf-reactor/Makefile.am | 54 ++++++++++++ cf-reactor/cf-reactor.c | 147 +++++++++++++++++++++++++++++++++ configure.ac | 1 + libpromises/enterprise_stubs.c | 7 ++ libpromises/prototypes3.h | 2 + 7 files changed, 215 insertions(+) create mode 100644 cf-reactor/Makefile.am create mode 100644 cf-reactor/cf-reactor.c diff --git a/.gitignore b/.gitignore index b305318e13..ad8b3a2111 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ stamp-h1 /cf-testd/Makefile /cf-upgrade/Makefile /cf-watchd/Makefile +/cf-reactor/Makefile /contrib/vagrant-ci/centos-9s-x64/Makefile /examples/Makefile /ext/Makefile @@ -115,6 +116,8 @@ xml_tmp_suite /cf-upgrade/cf-upgrade.exe /cf-watchd/cf-watchd /cf-watchd/cf-watchd.exe +/cf-reactor/cf-reactor +/cf-reactor/cf-reactor.exe /ext/rpmvercmp /ext/rpmvercmp.exe diff --git a/Makefile.am b/Makefile.am index 920855f10c..b7c62cbebc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -46,6 +46,7 @@ SUBDIRS = \ cf-net \ cf-secret \ cf-watchd \ + cf-reactor \ misc \ python \ ext \ diff --git a/cf-reactor/Makefile.am b/cf-reactor/Makefile.am new file mode 100644 index 0000000000..c6cd6f2fa8 --- /dev/null +++ b/cf-reactor/Makefile.am @@ -0,0 +1,54 @@ +# +# 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. +# +noinst_LTLIBRARIES = libcf-reactor.la + +AM_CPPFLAGS = -I$(srcdir)/../libpromises -I$(srcdir)/../libntech/libutils \ + -I$(srcdir)/../libcfecompat \ + -I$(srcdir)/../libcfnet \ + $(OPENSSL_CPPFLAGS) \ + $(PCRE2_CPPFLAGS) \ + $(ENTERPRISE_CPPFLAGS) + +AM_CFLAGS = $(CF3_CFLAGS) \ + $(DEBUG_CFLAGS) \ + $(OPENSSL_CFLAGS) \ + $(ENTERPRISE_CFLAGS) + +libcf_reactor_la_LIBADD = ../libpromises/libpromises.la + +libcf_reactor_la_SOURCES = \ + cf-reactor.c + +if !BUILTIN_EXTENSIONS + bin_PROGRAMS = cf-reactor + cf_reactor_LDADD = libcf-reactor.la + cf_reactor_SOURCES = +endif + +CLEANFILES = *.gcno *.gcda + +# +# Some basic clean ups +# +MOSTLYCLEANFILES = *~ *.orig *.rej diff --git a/cf-reactor/cf-reactor.c b/cf-reactor/cf-reactor.c new file mode 100644 index 0000000000..a97252aadd --- /dev/null +++ b/cf-reactor/cf-reactor.c @@ -0,0 +1,147 @@ +/* + 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 +#include +#include +#include +#include +#include +#include +#include +#include + +static const Component COMPONENT = +{ + .name = "cf-reactor", + .website = CF_WEBSITE, + .copyright = CF_COPYRIGHT +}; + +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."; + +static const struct option OPTIONS[] = +{ + {"debug", no_argument, 0, 'd'}, + {"no-fork", no_argument, 0, 'F'}, + {"log-level", required_argument, 0, 'g'}, + {"help", no_argument, 0, 'h'}, + {"inform", no_argument, 0, 'I'}, + {"timestamp", no_argument, 0, 'l'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {NULL, 0, 0, '\0'} +}; + +static const char *const HINTS[] = +{ + "Enable debugging output and run in foreground", + "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", + "Print basic information about actions being taken", + "Log timestamps on each line of log output", + "Output verbose information about the behaviour of the agent", + "Output the version of the software", + NULL +}; + +int main(int argc, char *argv[]) +{ + bool no_fork = false; + + extern char *optarg; + int longopt_idx; + int c; + while ((c = getopt_long(argc, argv, "dFg:hIlMvV", + OPTIONS, &longopt_idx)) + != -1) + { + switch (c) + { + case 'd': + LogSetGlobalLevel(LOG_LEVEL_DEBUG); + no_fork = true; + break; + + case 'F': + no_fork = true; + break; + + case 'g': + LogSetGlobalLevelArgOrExit(optarg); + break; + + case 'h': + { + 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 '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; + break; + + case 'V': + { + Writer *w = FileWriter(stdout); + GenericAgentWriteVersion(w); + FileWriterDetach(w); + } + DoCleanupAndExit(EXIT_SUCCESS); + + } + } + + return ReactorEnterpriseMain(no_fork); +} diff --git a/configure.ac b/configure.ac index 9dad073aa9..85947d5bef 100644 --- a/configure.ac +++ b/configure.ac @@ -2015,6 +2015,7 @@ AC_CONFIG_FILES([Makefile cf-net/Makefile cf-secret/Makefile cf-watchd/Makefile + cf-reactor/Makefile config.post.h contrib/vagrant-ci/centos-9s-x64/Makefile misc/Makefile diff --git a/libpromises/enterprise_stubs.c b/libpromises/enterprise_stubs.c index aed0bd4bfe..afcbec2ede 100644 --- a/libpromises/enterprise_stubs.c +++ b/libpromises/enterprise_stubs.c @@ -231,3 +231,10 @@ ENTERPRISE_VOID_FUNC_2ARG_DEFINE_STUB(void, Nova_ClassHistoryEnable, ARG_UNUSED bool, enable) { } + +ENTERPRISE_FUNC_1ARG_DEFINE_STUB(int, ReactorEnterpriseMain, ARG_UNUSED bool, no_fork) +{ + Log(LOG_LEVEL_VERBOSE, "Nova extension library is not available."); + Log(LOG_LEVEL_VERBOSE, "Running open-source cf-reactor."); + return 0; +} diff --git a/libpromises/prototypes3.h b/libpromises/prototypes3.h index 20e8e0f420..7ba1d9f051 100644 --- a/libpromises/prototypes3.h +++ b/libpromises/prototypes3.h @@ -85,6 +85,8 @@ ENTERPRISE_VOID_FUNC_0ARG_DECLARE(void, ReloadHAConfig); ENTERPRISE_VOID_FUNC_2ARG_DECLARE(void, Nova_ClassHistoryAddContextName, const StringSet *, list, const char *, context_name); ENTERPRISE_VOID_FUNC_2ARG_DECLARE(void, Nova_ClassHistoryEnable, StringSet **, list, bool, enable); +ENTERPRISE_FUNC_1ARG_DECLARE(int, ReactorEnterpriseMain, bool, no_fork); + /* manual.c */ void TexinfoManual(EvalContext *ctx, const char *source_dir, const char *output_file); From 22ff75c6551ed92a6a9e04f95da5dae07cea26ee Mon Sep 17 00:00:00 2001 From: Victor Moene Date: Fri, 14 Aug 2026 12:21:44 +0200 Subject: [PATCH 2/2] Added events promise type to cf-reactor component Ticket: ENT-14398 Signed-off-by: Victor Moene --- cf-reactor/cf-reactor.c | 136 ++++++++++++++++++++++++++------------ libpromises/Makefile.am | 1 + libpromises/cf3.defs.h | 2 + libpromises/constants.c | 1 + libpromises/mod_common.c | 2 + libpromises/mod_reactor.c | 51 ++++++++++++++ libpromises/mod_reactor.h | 32 +++++++++ 7 files changed, 181 insertions(+), 44 deletions(-) create mode 100644 libpromises/mod_reactor.c create mode 100644 libpromises/mod_reactor.h diff --git a/cf-reactor/cf-reactor.c b/cf-reactor/cf-reactor.c index a97252aadd..bd8c98a7ae 100644 --- a/cf-reactor/cf-reactor.c +++ b/cf-reactor/cf-reactor.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -34,6 +35,16 @@ #include #include +/*****************************************************************************/ +/* Globals */ +/*****************************************************************************/ + +int NO_FORK = false; + +/*******************************************************************/ +/* Command line options */ +/*******************************************************************/ + static const Component COMPONENT = { .name = "cf-reactor", @@ -44,7 +55,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 +66,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 +85,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: + { + // const char *const option_name = OPTIONS[longopt_idx].name; 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 4cef498d15..aceee843d1 100644 --- a/libpromises/Makefile.am +++ b/libpromises/Makefile.am @@ -136,6 +136,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 8ade1ced25..e2d68ee19c 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